On Sun, May 24, 2009 at 7:38 PM, Jon Smirl <jonsmirl@xxxxxxxxx> wrote: > Rewrite the mpc5200 audio DMA code to support both I2S and AC97. > > Signed-off-by: Jon Smirl <jonsmirl@xxxxxxxxx> > --- > sound/soc/fsl/Kconfig | 1 > sound/soc/fsl/mpc5200_dma.c | 504 ++++++++++++++++++++++++++------------- > sound/soc/fsl/mpc5200_dma.h | 33 +-- > sound/soc/fsl/mpc5200_psc_i2s.c | 247 +++---------------- > sound/soc/fsl/mpc5200_psc_i2s.h | 12 + > 5 files changed, 408 insertions(+), 389 deletions(-) > create mode 100644 sound/soc/fsl/mpc5200_psc_i2s.h > > diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig > index dc79bdf..1918c78 100644 > --- a/sound/soc/fsl/Kconfig > +++ b/sound/soc/fsl/Kconfig > @@ -25,7 +25,6 @@ config SND_SOC_MPC8610_HPCD > config SND_SOC_MPC5200_I2S > tristate "Freescale MPC5200 PSC in I2S mode driver" > depends on PPC_MPC52xx && PPC_BESTCOMM > - select SND_SOC_OF_SIMPLE > select SND_MPC52xx_DMA > select PPC_BESTCOMM_GEN_BD > help > diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c > index 6850392..4e1f1f8 100644 > --- a/sound/soc/fsl/mpc5200_dma.c > +++ b/sound/soc/fsl/mpc5200_dma.c > @@ -3,23 +3,13 @@ > * ALSA SoC Platform driver > * > * Copyright (C) 2008 Secret Lab Technologies Ltd. > + * Copyright (C) 2009 Jon Smirl, Digispeaker > */ > > -#include <linux/init.h> > #include <linux/module.h> > -#include <linux/interrupt.h> > -#include <linux/device.h> > -#include <linux/delay.h> > #include <linux/of_device.h> > -#include <linux/of_platform.h> > -#include <linux/dma-mapping.h> > > -#include <sound/core.h> > -#include <sound/pcm.h> > -#include <sound/pcm_params.h> > -#include <sound/initval.h> > #include <sound/soc.h> > -#include <sound/soc-of-simple.h> > > #include <sysdev/bestcomm/bestcomm.h> > #include <sysdev/bestcomm/gen_bd.h> > @@ -27,10 +17,6 @@ > > #include "mpc5200_dma.h" > > -MODULE_AUTHOR("Grant Likely <grant.likely@xxxxxxxxxxxx>"); > -MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); > -MODULE_LICENSE("GPL"); > - > /* > * Interrupt handlers > */ > @@ -50,7 +36,7 @@ static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) > if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) > psc_dma->stats.overrun_count++; > > - out_8(®s->command, 4 << 4); /* reset the error status */ > + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); > > return IRQ_HANDLED; > } > @@ -81,8 +67,21 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) > s->period_next_pt = s->period_start; > } > > +static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) > +{ > + while (s->appl_ptr < s->runtime->control->appl_ptr) { > + > + if (bcom_queue_full(s->bcom_task)) > + return; > + > + s->appl_ptr += s->period_size; > + > + psc_dma_bcom_enqueue_next_buffer(s); > + } > +} > + > /* Bestcomm DMA irq handler */ > -static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) > +static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) > { > struct psc_dma_stream *s = _psc_dma_stream; > > @@ -90,12 +89,12 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) > * 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_dma_bcom_enqueue_next_buffer(s); > - bcom_enable(s->bcom_task); > } > + psc_dma_bcom_enqueue_tx(s); > > /* If the stream is active, then also inform the PCM middle layer > * of the period finished event. */ > @@ -105,49 +104,31 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) > return IRQ_HANDLED; > } > > -/** > - * psc_dma_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. > - */ > -int psc_dma_startup(struct snd_pcm_substream *substream, > - struct snd_soc_dai *dai) > +static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) > { > - struct snd_soc_pcm_runtime *rtd = substream->private_data; > - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; > - int rc; > + struct psc_dma_stream *s = _psc_dma_stream; > > - dev_dbg(psc_dma->dev, "psc_dma_startup(substream=%p)\n", substream); > + /* 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); > > - if (!psc_dma->playback.active && > - !psc_dma->capture.active) { > - /* Setup the IRQs */ > - rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, > - "psc-dma-status", psc_dma); > - rc |= request_irq(psc_dma->capture.irq, > - &psc_dma_bcom_irq, IRQF_SHARED, > - "psc-dma-capture", &psc_dma->capture); > - rc |= request_irq(psc_dma->playback.irq, > - &psc_dma_bcom_irq, IRQF_SHARED, > - "psc-dma-playback", &psc_dma->playback); > - if (rc) { > - free_irq(psc_dma->irq, psc_dma); > - free_irq(psc_dma->capture.irq, > - &psc_dma->capture); > - free_irq(psc_dma->playback.irq, > - &psc_dma->playback); > - return -ENODEV; > - } > + s->period_current_pt += s->period_bytes; > + if (s->period_current_pt >= s->period_end) > + s->period_current_pt = s->period_start; > + > + psc_dma_bcom_enqueue_next_buffer(s); > } > > - return 0; > + /* 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; > } > > -int psc_dma_hw_free(struct snd_pcm_substream *substream, > - struct snd_soc_dai *dai) > +static int psc_dma_hw_free(struct snd_pcm_substream *substream) > { > snd_pcm_set_runtime_buffer(substream, NULL); > return 0; > @@ -159,8 +140,7 @@ int psc_dma_hw_free(struct snd_pcm_substream *substream, > * This function is called by ALSA to start, stop, pause, and resume the DMA > * transfer of data. > */ > -int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, > - struct snd_soc_dai *dai) > +static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) > { > struct snd_soc_pcm_runtime *rtd = substream->private_data; > struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; > @@ -168,8 +148,8 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, > struct psc_dma_stream *s; > struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; > u16 imr; > - u8 psc_cmd; > unsigned long flags; > + int i; > > if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) > s = &psc_dma->capture; > @@ -189,68 +169,45 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, > (s->period_bytes * runtime->periods); > s->period_next_pt = s->period_start; > s->period_current_pt = s->period_start; > + s->period_size = runtime->period_size; > s->active = 1; > > - /* First; reset everything */ > + /* track appl_ptr so that we have a better chance of detecting > + * end of stream and not over running it. > + */ > + s->runtime = runtime; > + s->appl_ptr = s->runtime->control->appl_ptr - > + (runtime->period_size * runtime->periods); > + > + /* 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) { > - out_8(®s->command, MPC52xx_PSC_RST_RX); > - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); > + bcom_gen_bd_rx_reset(s->bcom_task); > + for (i = 0; i < runtime->periods; i++) > + if (!bcom_queue_full(s->bcom_task)) > + psc_dma_bcom_enqueue_next_buffer(s); > } else { > - out_8(®s->command, MPC52xx_PSC_RST_TX); > - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); > + bcom_gen_bd_tx_reset(s->bcom_task); > + psc_dma_bcom_enqueue_tx(s); > } > > - /* 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_dma_bcom_enqueue_next_buffer(s); > bcom_enable(s->bcom_task); > > - /* Due to errata in the dma mode; need to line up enabling > - * the transmitter with a transition on the frame sync > - * line */ > - > spin_lock_irqsave(&psc_dma->lock, flags); > - /* first make sure it is low */ > - while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) > - ; > - /* then wait for the transition to high */ > - while ((in_8(®s->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(®s->command, psc_cmd); > + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); > spin_unlock_irqrestore(&psc_dma->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_dma->playback.active) { > - out_8(®s->command, 2 << 4); /* reset rx */ > - out_8(®s->command, 3 << 4); /* reset tx */ > - out_8(®s->command, 4 << 4); /* reset err */ > - } > - } else { > - out_8(®s->command, 3 << 4); /* reset tx */ > - out_8(®s->command, 4 << 4); /* reset err */ > - if (!psc_dma->capture.active) > - out_8(®s->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); > + 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); > > break; > > @@ -265,44 +222,11 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, > imr |= MPC52xx_PSC_IMR_TXEMP; > if (psc_dma->capture.active) > imr |= MPC52xx_PSC_IMR_ORERR; > - out_be16(®s->isr_imr.imr, imr); > + out_be16(®s->isr_imr.imr, psc_dma->imr | imr); > > return 0; > } > > -/** > - * psc_dma_shutdown: shutdown the data transfer on a stream > - * > - * Shutdown the PSC if there are no other substreams open. > - */ > -void psc_dma_shutdown(struct snd_pcm_substream *substream, > - struct snd_soc_dai *dai) > -{ > - struct snd_soc_pcm_runtime *rtd = substream->private_data; > - struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; > - > - dev_dbg(psc_dma->dev, "psc_dma_shutdown(substream=%p)\n", substream); > - > - /* > - * If this is the last active substream, disable the PSC and release > - * the IRQ. > - */ > - if (!psc_dma->playback.active && > - !psc_dma->capture.active) { > - > - /* Disable all interrupts and reset the PSC */ > - out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); > - out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset tx */ > - out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset rx */ > - out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ > - out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ > - > - /* Release irqs */ > - free_irq(psc_dma->irq, psc_dma); > - free_irq(psc_dma->capture.irq, &psc_dma->capture); > - free_irq(psc_dma->playback.irq, &psc_dma->playback); > - } > -} > > /* --------------------------------------------------------------------- > * The PSC DMA 'ASoC platform' driver > @@ -312,62 +236,78 @@ void psc_dma_shutdown(struct snd_pcm_substream *substream, > * interaction with the attached codec > */ > > -static const struct snd_pcm_hardware psc_dma_pcm_hardware = { > +static const struct snd_pcm_hardware psc_dma_hardware = { > .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | > SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | > SNDRV_PCM_INFO_BATCH, > .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | > - SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, > + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, Unrelated whitespace changes? > +/* --------------------------------------------------------------------- > + * Sysfs attributes for error monitoring > + */ All this sysfs stuff should be dropped from this patch. It is an abuse of sysfs and I never should have written it this way. Feel free to put it in a separate patch so others can use it if they really need it, but I'd like it to not be kept in mainline. g. -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel