[PATCH 2/3] ALSA SoC: Add mpc5200-psc I2S driver

new to sound world dinesh.dua at coraltele.com
Wed Jul 9 18:22:14 EST 2008


i am using your mpc5200 psc soc sound driver as a model for my task but for
that what i am missing is "soc-of.h" and "dts" file for the same. please
provide me with these files and if possible mail me at
dinesh.dua at coraltele.com.
thanks for your help. 

Grant Likely-2 wrote:
> 
> From: Grant Likely 
> 
> This is an I2S bus driver for the MPC5200 PSC device.  It is probably
> will not be merged as-is because it uses v1 of the ASoC API, but I want
> to get it out there for comments.
> ---
> 
>  sound/soc/fsl/Kconfig           |    6 
>  sound/soc/fsl/Makefile          |    2 
>  sound/soc/fsl/mpc5200_psc_i2s.c |  899
> +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 907 insertions(+), 0 deletions(-)
> 
> diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
> index 257101f..5daa8d3 100644
> --- a/sound/soc/fsl/Kconfig
> +++ b/sound/soc/fsl/Kconfig
> @@ -17,4 +17,10 @@ config SND_SOC_MPC8610_HPCD
>  	help
>  	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
>  
> +config SND_SOC_MPC5200_I2S
> +	bool "Freescale MPC5200 PSC in I2S mode driver"
> +	depends on SND_SOC && PPC_MPC52xx
> +	help
> +	  Say Y here to support the MPC5200 PSCs in I2S mode.
> +
>  endmenu
> diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
> index 62f680a..98729a1 100644
> --- a/sound/soc/fsl/Makefile
> +++ b/sound/soc/fsl/Makefile
> @@ -4,3 +4,5 @@ obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
>  # MPC8610 Platform Support
>  obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
>  
> +obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
> +
> diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c
> b/sound/soc/fsl/mpc5200_psc_i2s.c
> new file mode 100644
> index 0000000..81d0933
> --- /dev/null
> +++ b/sound/soc/fsl/mpc5200_psc_i2s.c
> @@ -0,0 +1,899 @@
> +/*
> + * Freescale MPC5200 PSC in I2S mode
> + * ALSA SoC Digital Audio Interface (DAI) driver
> + *
> + * Copyright (C) 2008 Secret Lab Technologies Ltd.
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +
> +MODULE_AUTHOR("Grant Likely ");
> +MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
> +MODULE_LICENSE("GPL");
> +
> +/**
> + * PSC_I2S_RATES: sample rates supported by the I2S
> + *
> + * This driver currently only supports the PSC running in I2S slave mode,
> + * which means the codec determines the sample rate.  Therefore, we tell
> + * ALSA that we support all rates and let the codec driver decide what
> rates
> + * are really supported.
> + */
> +#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 |
> \
> +			SNDRV_PCM_RATE_CONTINUOUS)
> +
> +/**
> + * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
> + */
> +#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
> \
> +			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
> +			 SNDRV_PCM_FMTBIT_S32_BE)
> +
> +/**
> + * psc_i2s_stream - Data specific to a single stream (playback or
> capture)
> + * @active:		flag indicating if the stream is active
> + * @psc_i2s:		pointer back to parent psc_i2s data structure
> + * @bcom_task:		bestcomm task structure
> + * @irq:		irq number for bestcomm task
> + * @period_start:	physical address of start of DMA region
> + * @period_end:		physical address of end of DMA region
> + * @period_next_pt:	physical address of next DMA buffer to enqueue
> + * @period_bytes:	size of DMA period in bytes
> + */
> +struct psc_i2s_stream {
> +	int active;
> +	struct psc_i2s *psc_i2s;
> +	struct bcom_task *bcom_task;
> +	int irq;
> +	struct snd_pcm_substream *stream;
> +	dma_addr_t period_start;
> +	dma_addr_t period_end;
> +	dma_addr_t period_next_pt;
> +	dma_addr_t period_current_pt;
> +	int period_bytes;
> +};
> +
> +/**
> + * psc_i2s - Private driver data
> + * @name: short name for this device ("PSC0", "PSC1", etc)
> + * @psc_regs: pointer to the PSC's registers
> + * @fifo_regs: pointer to the PSC's FIFO registers
> + * @irq: IRQ of this PSC
> + * @dev: struct device pointer
> + * @playback: the number of playback streams opened
> + * @capture: the number of capture streams opened
> + * @dai: the CPU DAI for this device
> + * @playback_stream: Playback stream context data
> + * @capture_stream: Capture stream context data
> + */
> +struct psc_i2s {
> +	char name[32];
> +	struct mpc52xx_psc __iomem *psc_regs;
> +	struct mpc52xx_psc_fifo __iomem *fifo_regs;
> +	unsigned int irq;
> +	struct device *dev;
> +	struct snd_soc_cpu_dai dai;
> +	spinlock_t lock;
> +
> +	/* per-stream data */
> +	struct psc_i2s_stream playback_stream;
> +	struct psc_i2s_stream capture_stream;
> +
> +	/* Statistics */
> +	struct {
> +		int overrun_count;
> +		int underrun_count;
> +	} stats;
> +};
> +
> +/*
> + * Interrupt handlers
> + */
> +static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
> +{
> +	struct psc_i2s *psc_i2s = _psc_i2s;
> +	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
> +	u16 imr;
> +	u16 isr;
> +
> +	isr = in_be16(&regs->mpc52xx_psc_isr);
> +	imr = in_be16(&regs->mpc52xx_psc_imr);
> +
> +	/* Playback underrun error */
> +	if (isr & imr & MPC52xx_PSC_IMR_TXEMP)
> +		psc_i2s->stats.underrun_count++;
> +
> +	/* Capture overrun error */
> +	if (isr & imr & MPC52xx_PSC_IMR_ORERR)
> +		psc_i2s->stats.overrun_count++;
> +
> +	out_8(&regs->command, 4 << 4);	/* reset the error status */
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
> + * @s: pointer to stream private data structure
> + *
> + * Enqueues another audio period buffer into the bestcomm queue.
> + *
> + * Note: The routine must only be called when there is space available in
> + * the queue.  Otherwise the enqueue will fail and the audio ring buffer
> + * will get out of sync
> + */
> +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
> +{
> +	struct bcom_bd *bd;
> +
> +	/* Prepare and enqueue the next buffer descriptor */
> +	bd = bcom_prepare_next_buffer(s->bcom_task);
> +	bd->status = s->period_bytes;
> +	bd->data[0] = s->period_next_pt;
> +	bcom_submit_next_buffer(s->bcom_task, NULL);
> +
> +	/* Update for next period */
> +	s->period_next_pt += s->period_bytes;
> +	if (s->period_next_pt >= s->period_end)
> +		s->period_next_pt = s->period_start;
> +}
> +
> +/* Bestcomm DMA irq handler */
> +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
> +{
> +	struct psc_i2s_stream *s = _psc_i2s_stream;
> +
> +	//spin_lock(&s->psc_i2s->lock);
> +
> +	/* For each finished period, dequeue the completed period buffer
> +	 * and enqueue a new one in it's place. */
> +	while (bcom_buffer_done(s->bcom_task)) {
> +		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
> +		s->period_current_pt += s->period_bytes;
> +		if (s->period_current_pt >= s->period_end)
> +			s->period_current_pt = s->period_start;
> +		psc_i2s_bcom_enqueue_next_buffer(s);
> +		bcom_enable(s->bcom_task);
> +	}
> +
> +	//spin_unlock(&s->psc_i2s->lock);
> +
> +	/* If the stream is active, then also inform the PCM middle layer
> +	 * of the period finished event. */
> +	if (s->active)
> +		snd_pcm_period_elapsed(s->stream);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * psc_i2s_startup: create a new substream
> + *
> + * This is the first function called when a stream is opened.
> + *
> + * If this is the first stream open, then grab the IRQ and program most
> of
> + * the PSC registers.
> + */
> +static int psc_i2s_startup(struct snd_pcm_substream *substream)
> +{
> +	int playback_irq, capture_irq, rc;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
> +	struct mpc52xx_psc_fifo __iomem *fiforegs = psc_i2s->fifo_regs;
> +
> +	dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
> +
> +	/* Disable all interrupts and reset the PSC */
> +	out_be16(&regs->mpc52xx_psc_imr, 0);
> +	out_8(&regs->command, 3 << 4); /* reset transmitter */
> +	out_8(&regs->command, 2 << 4); /* reset receiver */
> +	out_8(&regs->command, 1 << 4); /* reset mode */
> +	out_8(&regs->command, 4 << 4); /* reset error */
> +
> +	/* Default to CODEC8 mode */
> +	out_be32(&regs->sicr,
> +		 MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
> +		 MPC52xx_PSC_SICR_CLKPOL | MPC52xx_PSC_SICR_SIM_CODEC_8);
> +
> +	/* First write: RxRdy (FIFO Alarm) generates receive FIFO interrupt */
> +	/* Second write to mode: register Normal mode for non loopback */
> +	out_8(&regs->mode, 0);
> +	out_8(&regs->mode, 0);
> +
> +	/* Set the TX and RX fifo alarm thresholds */
> +	out_be16(&fiforegs->rfalarm, 0x100);	/* set RFALARM level */
> +	out_8(&fiforegs->rfcntl, 0x4);		/* set RFGRAN level (bytes) */
> +	out_be16(&fiforegs->tfalarm, 0x100);	/* set TFALARM level */
> +	out_8(&fiforegs->tfcntl, 0x7);		/* set TFGRAN level (bytes*4) */
> +
> +	/* Setup the IRQs */
> +	playback_irq = bcom_get_task_irq(psc_i2s->playback_stream.bcom_task);
> +	capture_irq = bcom_get_task_irq(psc_i2s->capture_stream.bcom_task);
> +	rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
> +			 "psc-i2s-status", psc_i2s);
> +	rc |= request_irq(capture_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
> +			  "psc-i2s-capture", &psc_i2s->capture_stream);
> +	rc |= request_irq(playback_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
> +			  "psc-i2s-playback", &psc_i2s->playback_stream);
> +	if (rc) {
> +		free_irq(psc_i2s->irq, psc_i2s);
> +		free_irq(capture_irq, &psc_i2s->capture_stream);
> +		free_irq(playback_irq, &psc_i2s->playback_stream);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *params)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	u32 sicr;
> +
> +	dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
> +		" periods=%i buffer_size=%i  buffer_bytes=%i\n",
> +		__FUNCTION__, substream, params_period_size(params),
> +		params_period_bytes(params), params_periods(params),
> +		params_buffer_size(params), params_buffer_bytes(params));
> +
> +	sicr = MPC52xx_PSC_SICR_DTS1 |
> +	       MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL;
> +	switch (params_format(params)) {
> +	 case SNDRV_PCM_FORMAT_S8:
> +		sicr |= MPC52xx_PSC_SICR_SIM_CODEC_8;
> +		break;
> +	 case SNDRV_PCM_FORMAT_S16_BE:
> +		sicr |= MPC52xx_PSC_SICR_SIM_CODEC_16;
> +		break;
> +	 case SNDRV_PCM_FORMAT_S24_BE:
> +		sicr |= MPC52xx_PSC_SICR_SIM_CODEC_24;
> +		break;
> +	 case SNDRV_PCM_FORMAT_S32_BE:
> +		sicr |= MPC52xx_PSC_SICR_SIM_CODEC_32;
> +		break;
> +	 default:
> +		dev_dbg(psc_i2s->dev, "invalid format\n");
> +		return -EINVAL;
> +	}
> +	out_be32(&psc_i2s->psc_regs->sicr, sicr);
> +
> +	//rc = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
> +	//if (rc) {
> +	//	dev_err(psc_i2s->dev, "could not allocate dma buffer\n");
> +	//	return rc;
> +	//}
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
> +
> +	return 0;
> +}
> +
> +static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
> +{
> +	//return snd_pcm_lib_free_pages(substream);
> +	snd_pcm_set_runtime_buffer(substream, NULL);
> +	return 0;
> +}
> +
> +/**
> + * psc_i2s_trigger: start and stop the DMA transfer.
> + *
> + * This function is called by ALSA to start, stop, pause, and resume the
> DMA
> + * transfer of data.
> + */
> +static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct psc_i2s_stream *s;
> +	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
> +	u16 imr;
> +	u8 psc_cmd;
> +	long flags;
> +
> +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		s = &psc_i2s->capture_stream;
> +	else
> +		s = &psc_i2s->playback_stream;
> +
> +	dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
> +		" stream_id=%i\n",
> +		substream, cmd, substream->pstr->stream);
> +
> +	switch (cmd) {
> +	 case SNDRV_PCM_TRIGGER_START:
> +		s->period_bytes = frames_to_bytes(runtime,
> +						  runtime->period_size);
> +		s->period_start = virt_to_phys(runtime->dma_area);
> +		s->period_end = s->period_start +
> +				(s->period_bytes * runtime->periods);
> +		s->period_next_pt = s->period_start;
> +		s->period_current_pt = s->period_start;
> +		s->active = 1;
> +
> +		/* First; reset everything */
> +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +			out_8(&regs->command, MPC52xx_PSC_RST_RX);
> +			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
> +		} else {
> +			out_8(&regs->command, MPC52xx_PSC_RST_TX);
> +			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
> +		}
> +
> +		/* Next, fill up the bestcomm bd queue and enable DMA.
> +		 * This will begin filling the PSC's fifo. */
> +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
> +			bcom_gen_bd_rx_reset(s->bcom_task);
> +		else
> +			bcom_gen_bd_tx_reset(s->bcom_task);
> +		while (!bcom_queue_full(s->bcom_task))
> +			psc_i2s_bcom_enqueue_next_buffer(s);
> +		bcom_enable(s->bcom_task);
> +
> +		/* Update interrupt enable settings.  This must be done
> +		 * before the PSC is enabled so that TX underrun events
> +		 * are not missed. */
> +		imr = 0;
> +		if (psc_i2s->playback_stream.active)
> +			imr |= MPC52xx_PSC_IMR_TXEMP;
> +		if (psc_i2s->capture_stream.active)
> +			imr |= MPC52xx_PSC_IMR_ORERR;
> +		out_be16(&regs->isr_imr.imr, imr);
> +
> +		/* Due to errata in the i2s mode; need to line up enabling
> +		 * the transmitter with a transition on the frame sync
> +		 * line */
> +
> +		spin_lock_irqsave(&psc_i2s->lock, flags);
> +		/* first make sure it is low */
> +		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0);
> +		/* then wait for the transition to high */
> +		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0);
> +		/* Finally, enable the PSC.
> +		 * Receiver must always be enabled; even when we only want
> +		 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
> +		psc_cmd = MPC52xx_PSC_RX_ENABLE;
> +		if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +			psc_cmd |= MPC52xx_PSC_TX_ENABLE;
> +		out_8(&regs->command, psc_cmd);
> +		spin_unlock_irqrestore(&psc_i2s->lock, flags);
> +
> +		break;
> +
> +	 case SNDRV_PCM_TRIGGER_STOP:
> +		/* Turn off the PSC */
> +		s->active = 0;
> +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +			if (!psc_i2s->playback_stream.active) {
> +				out_8(&regs->command, 2 << 4);	/* reset rx */
> +				out_8(&regs->command, 3 << 4);	/* reset tx */
> +				out_8(&regs->command, 4 << 4);	/* reset err */
> +			}
> +		} else {
> +			out_8(&regs->command, 3 << 4);	/* reset tx */
> +			out_8(&regs->command, 4 << 4);	/* reset err */
> +			if (!psc_i2s->capture_stream.active)
> +				out_8(&regs->command, 2 << 4);	/* reset rx */
> +		}
> +
> +		bcom_disable(s->bcom_task);
> +		while (!bcom_queue_empty(s->bcom_task))
> +			bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
> +
> +		break;
> +
> +	 default:
> +		dev_dbg(psc_i2s->dev, "invalid command\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Update interrupt enable settings */
> +	imr = 0;
> +	if (psc_i2s->playback_stream.active) imr |= MPC52xx_PSC_IMR_TXEMP;
> +	if (psc_i2s->capture_stream.active) imr |= MPC52xx_PSC_IMR_ORERR;
> +	out_be16(&regs->isr_imr.imr, imr);
> +
> +	return 0;
> +}
> +
> +/**
> + * psc_i2s_shutdown: shutdown the data transfer on a stream
> + *
> + * Shutdown the PSC if there are no other substreams open.
> + */
> +static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +
> +	dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
> +
> +	/*
> +	 * If this is the last active substream, disable the PSC and release
> +	 * the IRQ.
> +	 */
> +	if (!psc_i2s->playback_stream.active &&
> +	    !psc_i2s->capture_stream.active) {
> +		/* TODO: shut off channels */
> +		free_irq(psc_i2s->irq, psc_i2s);
> +		free_irq(bcom_get_task_irq(psc_i2s->capture_stream.bcom_task),
> +			 &psc_i2s->capture_stream);
> +		free_irq(bcom_get_task_irq(psc_i2s->playback_stream.bcom_task),
> +			 &psc_i2s->playback_stream);
> +	}
> +}
> +
> +/**
> + * psc_i2s_set_sysclk: set the clock frequency and direction
> + *
> + * This function is called by the machine driver to tell us what the
> clock
> + * frequency and direction are.
> + *
> + * Currently, we only support operating as a clock slave
> (SND_SOC_CLOCK_IN),
> + * and we don't care about the frequency.  Return an error if the
> direction
> + * is not SND_SOC_CLOCK_IN.
> + *
> + * @clk_id: reserved, should be zero
> + * @freq: the frequency of the given clock ID, currently ignored
> + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock
> master)
> + */
> +static int psc_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
> +			      int clk_id, unsigned int freq, int dir)
> +{
> +	struct psc_i2s *psc_i2s = cpu_dai->private_data;
> +	dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
> +				cpu_dai, dir);
> +	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
> +}
> +
> +/**
> + * psc_i2s_set_fmt: set the serial format.
> + *
> + * This function is called by the machine driver to tell us what serial
> + * format to use.
> + *
> + * This driver only supports I2S mode.  Return an error if the format is
> + * not SND_SOC_DAIFMT_I2S.
> + *
> + * @format: one of SND_SOC_DAIFMT_xxx
> + */
> +static int psc_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int
> format)
> +{
> +	struct psc_i2s *psc_i2s = cpu_dai->private_data;
> +	dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
> +				cpu_dai, format);
> +	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
> +}
> +
> +/* ---------------------------------------------------------------------
> + * ALSA SoC Bindings
> + *
> + * - Digital Audio Interface (DAI) template
> + * - create/destroy dai hooks
> + */
> +
> +/**
> + * psc_i2s_dai_template: template CPU Digital Audio Interface
> + */
> +static struct snd_soc_cpu_dai psc_i2s_dai_template = {
> +	.type = SND_SOC_DAI_I2S,
> +	.playback = {
> +		.channels_min = 2,
> +		.channels_max = 2,
> +		.rates = PSC_I2S_RATES,
> +		.formats = PSC_I2S_FORMATS,
> +	},
> +	.capture = {
> +		.channels_min = 2,
> +		.channels_max = 2,
> +		.rates = PSC_I2S_RATES,
> +		.formats = PSC_I2S_FORMATS,
> +	},
> +	.ops = {
> +		.startup = psc_i2s_startup,
> +		.hw_params = psc_i2s_hw_params,
> +		.hw_free = psc_i2s_hw_free,
> +		.shutdown = psc_i2s_shutdown,
> +		.trigger = psc_i2s_trigger,
> +	},
> +	.dai_ops = {
> +		.set_sysclk = psc_i2s_set_sysclk,
> +		.set_fmt = psc_i2s_set_fmt,
> +	},
> +};
> +
> +/* ---------------------------------------------------------------------
> + * The PSC I2S 'ASoC platform' driver
> + *
> + * Can be referenced by an 'ASoC machine' driver
> + * This driver only deals with the audio bus; it doesn't have any
> + * interaction with the attached codec
> + */
> +
> +static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
> +	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> +		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
> +	.formats = SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_BE |
> +		   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
> +	.rate_min = 8000,
> +	.rate_max = 48000,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.period_bytes_max	= 1024 * 1024,
> +	.period_bytes_min	= 32,
> +	.period_bytes_max	= 1024 * 1024,
> +	.periods_min		= 2,
> +	.periods_max		= 256,
> +	.buffer_bytes_max	= 2 * 1024 * 1024,
> +	.fifo_size		= 0,
> +};
> +
> +static unsigned int psc_i2s_fixed_rates[] = {
> +	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
> +};
> +
> +static struct snd_pcm_hw_constraint_list psc_i2s_constraints_rates = {
> +	.count = ARRAY_SIZE(psc_i2s_fixed_rates),
> +	.list = psc_i2s_fixed_rates,
> +	.mask = 0,
> +};
> +
> +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	struct psc_i2s_stream *s;
> +	int rc;
> +
> +	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
> +
> +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		s = &psc_i2s->capture_stream;
> +	else
> +		s = &psc_i2s->playback_stream;
> +
> +	snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
> +
> +	rc = snd_pcm_hw_constraint_integer(substream->runtime,
> +					   SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (rc < 0) {
> +		dev_err(psc_i2s->dev, "invalid buffer size\n");
> +		return rc;
> +	}
> +	rc = snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					SNDRV_PCM_HW_PARAM_RATE,
> +					&psc_i2s_constraints_rates);
> +	if (rc < 0) {
> +		dev_err(psc_i2s->dev, "invalid rate\n");
> +		return rc;
> +	}
> +
> +	s->stream = substream;
> +	return 0;
> +}
> +
> +static int psc_i2s_pcm_close(struct snd_pcm_substream * substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	struct psc_i2s_stream *s;
> +
> +	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
> +
> +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		s = &psc_i2s->capture_stream;
> +	else
> +		s = &psc_i2s->playback_stream;
> +
> +	s->stream = NULL;
> +	return 0;
> +}
> +
> +static snd_pcm_uframes_t
> +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
> +	struct psc_i2s_stream *s;
> +	dma_addr_t count;
> +
> +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		s = &psc_i2s->capture_stream;
> +	else
> +		s = &psc_i2s->playback_stream;
> +
> +	/*FIXME: count = s->sdma->bd[s->sdma->outdex].data - s->period_start;*/
> +	count = s->period_current_pt - s->period_start;
> +
> +	return bytes_to_frames(substream->runtime, count);
> +}
> +
> +static struct snd_pcm_ops psc_i2s_pcm_ops = {
> +	.open		= psc_i2s_pcm_open,
> +	.close		= psc_i2s_pcm_close,
> +	.ioctl		= snd_pcm_lib_ioctl,
> +	.pointer	= psc_i2s_pcm_pointer,
> +};
> +
> +static u64 psc_i2s_pcm_dmamask = 0xffffffff;
> +static int psc_i2s_pcm_new(struct snd_card *card, struct
> snd_soc_codec_dai *dai,
> +			   struct snd_pcm *pcm)
> +{
> +	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
> +	size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
> +	int rc = 0;
> +
> +	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
> +		card, dai, pcm);
> +
> +	if (!card->dev->dma_mask)
> +		card->dev->dma_mask = &psc_i2s_pcm_dmamask;
> +	if (!card->dev->coherent_dma_mask)
> +		card->dev->coherent_dma_mask = 0xffffffff;
> +
> +	rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
> +				 &pcm->streams[0].substream->dma_buffer);
> +	if (rc) {
> +		dev_err(card->dev, "Cannot alloc playback DMA buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
> +				 &pcm->streams[1].substream->dma_buffer);
> +	if (rc) {
> +		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
> +		dev_err(card->dev, "Can't allocate capture DMA buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void psc_i2s_pcm_free(struct snd_pcm *pcm)
> +{
> +	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
> +	struct snd_pcm_substream *substream;
> +	int stream;
> +
> +	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
> +
> +	for (stream = 0; stream < 2; stream++) {
> +		substream = pcm->streams[stream].substream;
> +		if (substream) {
> +			snd_dma_free_pages(&substream->dma_buffer);
> +			substream->dma_buffer.area = NULL;
> +			substream->dma_buffer.addr = 0;
> +		}
> +	}
> +}
> +
> +struct snd_soc_platform psc_i2s_pcm_soc_platform = {
> +	.name		= "mpc5200-psc-audio",
> +	.pcm_ops	= &psc_i2s_pcm_ops,
> +	.pcm_new	= &psc_i2s_pcm_new,
> +	.pcm_free	= &psc_i2s_pcm_free,
> +};
> +
> +/* ---------------------------------------------------------------------
> + * Sysfs attributes for debugging
> + */
> +
> +static ssize_t psc_i2s_status_show(struct device *dev,
> +			   struct device_attribute *attr, char *buf)
> +{
> +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x
> tfnum=%i tfstat=0x%.4x\n",
> +			in_be16(&psc_i2s->psc_regs->sr_csr.status),
> +			in_be32(&psc_i2s->psc_regs->sicr),
> +			in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
> +			in_be16(&psc_i2s->fifo_regs->rfstat),
> +			in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
> +			in_be16(&psc_i2s->fifo_regs->tfstat));
> +}
> +
> +static int * psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s,
> +				   const char *name)
> +{
> +	if (strcmp(name, "playback_underrun") == 0)
> +		return &psc_i2s->stats.underrun_count;
> +	if (strcmp(name, "capture_overrun") == 0)
> +		return &psc_i2s->stats.overrun_count;
> +
> +	return NULL;
> +}
> +
> +static ssize_t psc_i2s_stat_show(struct device *dev,
> +				 struct device_attribute *attr, char *buf)
> +{
> +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
> +	int *attrib;
> +
> +	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
> +	if (!attrib)
> +		return 0;
> +
> +	return sprintf(buf, "%i\n", *attrib);
> +}
> +
> +static ssize_t psc_i2s_stat_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf,
> +				  size_t count)
> +{
> +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
> +	int *attrib;
> +
> +	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
> +	if (!attrib)
> +		return 0;
> +
> +	*attrib = simple_strtoul(buf, NULL, 0);
> +	return count;
> +}
> +
> +DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
> +DEVICE_ATTR(playback_underrun, 0644,
> psc_i2s_stat_show,psc_i2s_stat_store);
> +DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show,
> psc_i2s_stat_store);
> +
> +/* ---------------------------------------------------------------------
> + * OF platform bus binding code:
> + * - Probe/remove operations
> + * - OF device match table
> + */
> +static int __devinit psc_i2s_of_probe(struct of_device *op,
> +				      const struct of_device_id *match)
> +{
> +	phys_addr_t fifo;
> +	struct psc_i2s *psc_i2s;
> +	struct resource res;
> +	int size, psc_id, irq, rc;
> +	const __be32 *prop;
> +	void __iomem *regs;
> +
> +	dev_dbg(&op->dev, "probing psc i2s device\n");
> +
> +	/* Get the PSC ID */
> +	prop = of_get_property(op->node, "cell-index", &size);
> +	if (!prop || size < sizeof *prop)
> +		return -ENODEV;
> +	psc_id = be32_to_cpu(*prop);
> +
> +	/* Fetch the registers and IRQ of the PSC */
> +	irq = irq_of_parse_and_map(op->node, 0);
> +	if (of_address_to_resource(op->node, 0, &res)) {
> +		dev_err(&op->dev, "Missing reg property\n");
> +		return -ENODEV;
> +	}
> +	regs = ioremap(res.start, 1 + res.end - res.start);
> +	if (!regs) {
> +		dev_err(&op->dev, "Could not map registers\n");
> +		return -ENODEV;
> +	}
> +
> +	/* Allocate and initialize the driver private data */
> +	psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
> +	if (!psc_i2s) {
> +		iounmap(regs);
> +		return -ENOMEM;
> +	}
> +	spin_lock_init(&psc_i2s->lock);
> +	psc_i2s->irq = irq;
> +	psc_i2s->psc_regs = regs;
> +	psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
> +	psc_i2s->dev = &op->dev;
> +	psc_i2s->playback_stream.psc_i2s = psc_i2s;
> +	psc_i2s->capture_stream.psc_i2s = psc_i2s;
> +	snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
> +
> +	/* Fill out the CPU DAI structure */
> +	memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
> +	psc_i2s->dai.private_data = psc_i2s;
> +	psc_i2s->dai.name = psc_i2s->name;
> +	psc_i2s->dai.id = psc_id;
> +
> +	/* Find the address of the fifo data registers and setup the
> +	 * DMA tasks */
> +	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
> +	psc_i2s->capture_stream.bcom_task =
> +		bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
> +	psc_i2s->playback_stream.bcom_task =
> +		bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
> +	if (!psc_i2s->capture_stream.bcom_task ||
> +	    !psc_i2s->playback_stream.bcom_task) {
> +		dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
> +		iounmap(regs);
> +		kfree(psc_i2s);
> +		return -ENODEV;
> +	}
> +
> +	/* Save what we've done so it can be found again later */
> +	dev_set_drvdata(&op->dev, psc_i2s);
> +
> +	/* Register the SYSFS files */
> +	rc = device_create_file(psc_i2s->dev, &dev_attr_status);
> +	rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
> +	rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
> +	if (rc)
> +		dev_info(psc_i2s->dev, "error creating sysfs files\n");
> +
> +	/* Tell the ASoC OF helpers about it */
> +	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
> +				     &psc_i2s->dai);
> +
> +	return 0;
> +}
> +
> +static int __devexit psc_i2s_of_remove(struct of_device *op)
> +{
> +	struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
> +
> +	dev_dbg(&op->dev, "psc_i2s_remove()\n");
> +
> +	bcom_gen_bd_rx_release(psc_i2s->capture_stream.bcom_task);
> +	bcom_gen_bd_tx_release(psc_i2s->playback_stream.bcom_task);
> +
> +	iounmap(psc_i2s->psc_regs);
> +	iounmap(psc_i2s->fifo_regs);
> +	kfree(psc_i2s);
> +	dev_set_drvdata(&op->dev, NULL);
> +
> +	return 0;
> +}
> +
> +/* Match table for of_platform binding */
> +static struct of_device_id psc_i2s_match[] __devinitdata = {
> +	{ .compatible = "fsl,mpc5200-psc-i2s", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, psc_i2s_match);
> +
> +static struct of_platform_driver psc_i2s_driver = {
> +	.match_table = psc_i2s_match,
> +	.probe = psc_i2s_of_probe,
> +	.remove = __devexit_p(psc_i2s_of_remove),
> +	.driver = {
> +		.name = "mpc5200-psc-i2s",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +/* ---------------------------------------------------------------------
> + * Module setup and teardown; simply register the of_platform driver
> + * for the PSC in I2S mode.
> + */
> +static int __init psc_i2s_init(void)
> +{
> +	return of_register_platform_driver(&psc_i2s_driver);
> +}
> +module_init(psc_i2s_init);
> +
> +static void __exit psc_i2s_exit(void)
> +{
> +	of_unregister_platform_driver(&psc_i2s_driver);
> +}
> +module_exit(psc_i2s_exit);
> +
> +
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
> 
> 

-- 
View this message in context: http://www.nabble.com/-PATCH-1-3--ALSA-SoC%3A-Add-OpenFirmware-helper-for-matching-bus-and-codec-drivers-tp18227666p18356669.html
Sent from the linuxppc-dev mailing list archive at Nabble.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linuxppc-dev/attachments/20080709/24b9267c/attachment.htm>


More information about the Linuxppc-dev mailing list