[alsa-devel] [PATCH V1 00/13] mpc5200 audio rework for AC97
The following series implements audio support for the mpc5200. It adds an AC97 driver and STAC9766 codec driver. Board support for the Efika and Phytec pcm030 are also included. I'm sure everyone will have some edits on this first version.
---
Jon Smirl (13): Support for AC97 on Phytec pmc030 base board. Fabric bindings for STAC9766 on the Efika AC97 driver for mpc5200 Codec for STAC9766 used on the Efika Main rewite of the mpc5200 audio DMA code Have the WM9712 driver self register itself Add SNDRV_PCM_FMTBIT_S32_BE as a valid AC97 format Allow a custom ASOC fabric driver with soc-of-simple Add a few more mpc5200 PSC defines redo 2008f137e92220b98120c4803499cdddb2b0fb06 Rename the PSC functions to DMA Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Temporarily undo 2008f137e92220b98120c4803499cdddb2b0fb06
arch/powerpc/boot/dts/pcm030.dts | 8 arch/powerpc/include/asm/mpc52xx_psc.h | 10 arch/powerpc/platforms/52xx/efika.c | 8 include/sound/soc-dai.h | 3 include/sound/soc-of-simple.h | 16 + sound/last.c | 6 sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/stac9766.c | 491 ++++++++++++++++++++++ sound/soc/codecs/stac9766.h | 21 + sound/soc/codecs/tlv320aic26.c | 2 sound/soc/codecs/wm9712.c | 34 ++ sound/soc/fsl/Kconfig | 30 + sound/soc/fsl/Makefile | 6 sound/soc/fsl/efika-audio-fabric.c | 95 ++++ sound/soc/fsl/mpc5200_dma.c | 693 +++++++++++++++++++++++++++++++ sound/soc/fsl/mpc5200_dma.h | 83 ++++ sound/soc/fsl/mpc5200_psc_ac97.c | 379 +++++++++++++++++ sound/soc/fsl/mpc5200_psc_ac97.h | 13 + sound/soc/fsl/mpc5200_psc_i2s.c | 707 +------------------------------- sound/soc/fsl/soc-of-simple.c | 160 ++++++- 21 files changed, 2053 insertions(+), 718 deletions(-) create mode 100644 sound/soc/codecs/stac9766.c create mode 100644 sound/soc/codecs/stac9766.h create mode 100644 sound/soc/fsl/efika-audio-fabric.c create mode 100644 sound/soc/fsl/mpc5200_dma.c create mode 100644 sound/soc/fsl/mpc5200_dma.h create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.c create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.h
Temporarily undo 2008f137e92220b98120c4803499cdddb2b0fb06 in sound/soc/fsl/mpc5200_psc_i2s.c so that it won't interfere with following patches that split mpc5200_psc_i2s.c.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/mpc5200_psc_i2s.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 1111c71..3aa729d 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -504,8 +504,7 @@ static struct snd_soc_dai psc_i2s_dai_template = {
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 | - SNDRV_PCM_INFO_BATCH, + 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,
On Wed, May 13, 2009 at 09:59:01PM -0400, Jon Smirl wrote:
Temporarily undo 2008f137e92220b98120c4803499cdddb2b0fb06 in sound/soc/fsl/mpc5200_psc_i2s.c so that it won't interfere with following patches that split mpc5200_psc_i2s.c.
I'm fairly sure it's possible to squash these in with the other two patches but I guess it's not the end of the world :(
On Thu, May 14, 2009 at 6:13 AM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Wed, May 13, 2009 at 09:59:01PM -0400, Jon Smirl wrote:
Temporarily undo 2008f137e92220b98120c4803499cdddb2b0fb06 in sound/soc/fsl/mpc5200_psc_i2s.c so that it won't interfere with following patches that split mpc5200_psc_i2s.c.
I'm fairly sure it's possible to squash these in with the other two patches but I guess it's not the end of the world :(
You and I can easily see how this patch can be folded into the next one. Git is broken in this situation. I've reported the issue to the git people and they acknowledge it, but no one has fixed it. Git's merge strategy does not handle the case of a file splitting. When git tries to merge this one-liner into the next patch that splits the files, it does the merge wrong which then messes up the entire patch stack.
On Thu, 2009-05-14 at 10:37 -0400, Jon Smirl wrote:
You and I can easily see how this patch can be folded into the next one. Git is broken in this situation. I've reported the issue to the git people and they acknowledge it, but no one has fixed it. Git's merge strategy does not handle the case of a file splitting. When git tries to merge this one-liner into the next patch that splits the files, it does the merge wrong which then messes up the entire patch stack.
It should be possible to do the relevant thing by hand - it's only a one line change you're staging out. Like I say, not the end of the world anyway.
On Thu, May 14, 2009 at 12:25 PM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Thu, 2009-05-14 at 10:37 -0400, Jon Smirl wrote:
You and I can easily see how this patch can be folded into the next one. Git is broken in this situation. I've reported the issue to the git people and they acknowledge it, but no one has fixed it. Git's merge strategy does not handle the case of a file splitting. When git tries to merge this one-liner into the next patch that splits the files, it does the merge wrong which then messes up the entire patch stack.
It should be possible to do the relevant thing by hand - it's only a one line change you're staging out. Like I say, not the end of the world anyway.
I manually edited the diff files to fix this. That not something I like to do because it is too easy to make mistakes. Once they were edited I could feed them back into git. This is the second time I've hit this problem in git.
Basic split of mpc5200 DMA code out from i2s into a standalone file.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/Kconfig | 4 sound/soc/fsl/Makefile | 2 sound/soc/fsl/mpc5200_dma.c | 457 +++++++++++++++++++++++++++++++++++++ sound/soc/fsl/mpc5200_dma.h | 81 +++++++ sound/soc/fsl/mpc5200_psc_i2s.c | 484 --------------------------------------- 5 files changed, 546 insertions(+), 482 deletions(-) create mode 100644 sound/soc/fsl/mpc5200_dma.c create mode 100644 sound/soc/fsl/mpc5200_dma.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 9fc9082..dc79bdf 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,5 +1,8 @@ config SND_SOC_OF_SIMPLE tristate + +config SND_MPC52xx_DMA + tristate
# ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers # for the SSI and the Elo DMA controller. You will still need to select @@ -23,6 +26,7 @@ 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 Say Y here to support the MPC5200 PSCs in I2S mode. diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index f85134c..7731ef2 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,5 +10,7 @@ snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
+# MPC5200 Platform Support +obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c new file mode 100644 index 0000000..cccaff4 --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.c @@ -0,0 +1,457 @@ +/* + * Freescale MPC5200 PSC DMA + * ALSA SoC Platform driver + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#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> +#include <asm/mpc52xx_psc.h> + +#include "mpc5200_dma.h" + +MODULE_AUTHOR("Grant Likely grant.likely@secretlab.ca"); +MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); +MODULE_LICENSE("GPL"); + +/* + * 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 isr; + + isr = in_be16(®s->mpc52xx_psc_isr); + + /* Playback underrun error */ + if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) + psc_i2s->stats.underrun_count++; + + /* Capture overrun error */ + if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) + psc_i2s->stats.overrun_count++; + + out_8(®s->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; + + /* 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); + } + + /* 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. + */ +int psc_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + int rc; + + dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); + + if (!psc_i2s->playback.active && + !psc_i2s->capture.active) { + /* Setup the IRQs */ + rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, + "psc-i2s-status", psc_i2s); + rc |= request_irq(psc_i2s->capture.irq, + &psc_i2s_bcom_irq, IRQF_SHARED, + "psc-i2s-capture", &psc_i2s->capture); + rc |= request_irq(psc_i2s->playback.irq, + &psc_i2s_bcom_irq, IRQF_SHARED, + "psc-i2s-playback", &psc_i2s->playback); + if (rc) { + free_irq(psc_i2s->irq, psc_i2s); + free_irq(psc_i2s->capture.irq, + &psc_i2s->capture); + free_irq(psc_i2s->playback.irq, + &psc_i2s->playback); + return -ENODEV; + } + } + + return 0; +} + +int psc_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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. + */ +int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + 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; + unsigned long flags; + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + s = &psc_i2s->capture; + else + s = &psc_i2s->playback; + + 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(®s->command, MPC52xx_PSC_RST_RX); + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); + } else { + out_8(®s->command, MPC52xx_PSC_RST_TX); + out_8(®s->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); + + /* 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(®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); + 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.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_i2s->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); + + break; + + default: + dev_dbg(psc_i2s->dev, "invalid command\n"); + return -EINVAL; + } + + /* Update interrupt enable settings */ + imr = 0; + if (psc_i2s->playback.active) + imr |= MPC52xx_PSC_IMR_TXEMP; + if (psc_i2s->capture.active) + imr |= MPC52xx_PSC_IMR_ORERR; + out_be16(®s->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. + */ +void psc_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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.active && + !psc_i2s->capture.active) { + + /* Disable all interrupts and reset the PSC */ + out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); + out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ + out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ + out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ + out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + + /* Release irqs */ + free_irq(psc_i2s->irq, psc_i2s); + free_irq(psc_i2s->capture.irq, &psc_i2s->capture); + free_irq(psc_i2s->playback.irq, &psc_i2s->playback); + } +} + +/* --------------------------------------------------------------------- + * The PSC DMA '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, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 2 * 1024 * 1024, + .fifo_size = 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; + + 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; + else + s = &psc_i2s->playback; + + snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); + + 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; + else + s = &psc_i2s->playback; + + 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; + else + s = &psc_i2s->playback; + + 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_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; + + if (pcm->streams[0].substream) { + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + &pcm->streams[0].substream->dma_buffer); + if (rc) + goto playback_alloc_err; + } + + if (pcm->streams[1].substream) { + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + &pcm->streams[1].substream->dma_buffer); + if (rc) + goto capture_alloc_err; + } + + return 0; + + capture_alloc_err: + if (pcm->streams[0].substream) + snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); + playback_alloc_err: + dev_err(card->dev, "Cannot allocate buffer(s)\n"); + return -ENOMEM; +} + +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, +}; + diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h new file mode 100644 index 0000000..9a19e8a --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.h @@ -0,0 +1,81 @@ +/* + * Freescale MPC5200 Audio DMA driver + */ + +#ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ +#define __SOUND_SOC_FSL_MPC5200_DMA_H__ + +/** + * 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 + * @dai: the CPU DAI for this device + * @sicr: Base value used in serial interface control register; mode is ORed + * with this value. + * @playback: Playback stream context data + * @capture: 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_dai dai; + spinlock_t lock; + u32 sicr; + + /* per-stream data */ + struct psc_i2s_stream playback; + struct psc_i2s_stream capture; + + /* Statistics */ + struct { + int overrun_count; + int underrun_count; + } stats; +}; + + +int psc_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +int psc_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +void psc_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai); + +extern struct snd_soc_platform psc_i2s_pcm_soc_platform; + +#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 3aa729d..8974b53 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -25,6 +25,8 @@ #include <sysdev/bestcomm/gen_bd.h> #include <asm/mpc52xx_psc.h>
+#include "mpc5200_dma.h" + MODULE_AUTHOR("Grant Likely grant.likely@secretlab.ca"); MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); MODULE_LICENSE("GPL"); @@ -47,179 +49,6 @@ MODULE_LICENSE("GPL"); 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 - * @dai: the CPU DAI for this device - * @sicr: Base value used in serial interface control register; mode is ORed - * with this value. - * @playback: Playback stream context data - * @capture: 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_dai dai; - spinlock_t lock; - u32 sicr; - - /* per-stream data */ - struct psc_i2s_stream playback; - struct psc_i2s_stream capture; - - /* 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 isr; - - isr = in_be16(®s->mpc52xx_psc_isr); - - /* Playback underrun error */ - if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) - psc_i2s->stats.underrun_count++; - - /* Capture overrun error */ - if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) - psc_i2s->stats.overrun_count++; - - out_8(®s->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; - - /* 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); - } - - /* 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, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; - int rc; - - dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); - - if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { - /* Setup the IRQs */ - rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, - "psc-i2s-status", psc_i2s); - rc |= request_irq(psc_i2s->capture.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-capture", &psc_i2s->capture); - rc |= request_irq(psc_i2s->playback.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-playback", &psc_i2s->playback); - if (rc) { - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, - &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, - &psc_i2s->playback); - return -ENODEV; - } - } - - return 0; -} - static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -258,164 +87,6 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, return 0; }
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - 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_dai *dai) -{ - 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; - unsigned long flags; - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; - else - s = &psc_i2s->playback; - - 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(®s->command, MPC52xx_PSC_RST_RX); - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - } else { - out_8(®s->command, MPC52xx_PSC_RST_TX); - out_8(®s->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); - - /* 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(®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); - 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.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_i2s->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); - - break; - - default: - dev_dbg(psc_i2s->dev, "invalid command\n"); - return -EINVAL; - } - - /* Update interrupt enable settings */ - imr = 0; - if (psc_i2s->playback.active) - imr |= MPC52xx_PSC_IMR_TXEMP; - if (psc_i2s->capture.active) - imr |= MPC52xx_PSC_IMR_ORERR; - out_be16(®s->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_dai *dai) -{ - 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.active && - !psc_i2s->capture.active) { - - /* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ - - /* Release irqs */ - free_irq(psc_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, &psc_i2s->playback); - } -} - /** * psc_i2s_set_sysclk: set the clock frequency and direction * @@ -495,157 +166,6 @@ static struct snd_soc_dai psc_i2s_dai_template = { };
/* --------------------------------------------------------------------- - * 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, - .periods_min = 2, - .periods_max = 256, - .buffer_bytes_max = 2 * 1024 * 1024, - .fifo_size = 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; - - 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; - else - s = &psc_i2s->playback; - - snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); - - 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; - else - s = &psc_i2s->playback; - - 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; - else - s = &psc_i2s->playback; - - 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_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; - - if (pcm->streams[0].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, - &pcm->streams[0].substream->dma_buffer); - if (rc) - goto playback_alloc_err; - } - - if (pcm->streams[1].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, - &pcm->streams[1].substream->dma_buffer); - if (rc) - goto capture_alloc_err; - } - - return 0; - - capture_alloc_err: - if (pcm->streams[0].substream) - snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); - playback_alloc_err: - dev_err(card->dev, "Cannot allocate buffer(s)\n"); - return -ENOMEM; -} - -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 */
Rename the functions in the mpc5200 DMA file from i2s based names to dma ones to reflect the file they are in.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/mpc5200_dma.c | 194 ++++++++++++++++++++------------------- sound/soc/fsl/mpc5200_dma.h | 26 +++-- sound/soc/fsl/mpc5200_psc_i2s.c | 160 ++++++++++++++++---------------- 3 files changed, 190 insertions(+), 190 deletions(-)
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index cccaff4..c82ef75 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -34,21 +34,21 @@ MODULE_LICENSE("GPL"); /* * Interrupt handlers */ -static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) +static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) { - struct psc_i2s *psc_i2s = _psc_i2s; - struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; + struct psc_dma *psc_dma = _psc_dma; + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 isr;
isr = in_be16(®s->mpc52xx_psc_isr);
/* Playback underrun error */ - if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) - psc_i2s->stats.underrun_count++; + if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) + psc_dma->stats.underrun_count++;
/* Capture overrun error */ - if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) - psc_i2s->stats.overrun_count++; + 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 */
@@ -56,7 +56,7 @@ static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) }
/** - * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer + * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer * @s: pointer to stream private data structure * * Enqueues another audio period buffer into the bestcomm queue. @@ -65,7 +65,7 @@ static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) * 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) +static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) { struct bcom_bd *bd;
@@ -82,9 +82,9 @@ static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) }
/* Bestcomm DMA irq handler */ -static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) +static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { - struct psc_i2s_stream *s = _psc_i2s_stream; + struct psc_dma_stream *s = _psc_dma_stream;
/* For each finished period, dequeue the completed period buffer * and enqueue a new one in it's place. */ @@ -93,7 +93,7 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) 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); + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task); }
@@ -106,39 +106,39 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) }
/** - * psc_i2s_startup: create a new substream + * 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_i2s_startup(struct snd_pcm_substream *substream, +int psc_dma_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; int rc;
- dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_startup(substream=%p)\n", substream);
- if (!psc_i2s->playback.active && - !psc_i2s->capture.active) { + if (!psc_dma->playback.active && + !psc_dma->capture.active) { /* Setup the IRQs */ - rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, - "psc-i2s-status", psc_i2s); - rc |= request_irq(psc_i2s->capture.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-capture", &psc_i2s->capture); - rc |= request_irq(psc_i2s->playback.irq, - &psc_i2s_bcom_irq, IRQF_SHARED, - "psc-i2s-playback", &psc_i2s->playback); + 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_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, - &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, - &psc_i2s->playback); + 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; } } @@ -146,7 +146,7 @@ int psc_i2s_startup(struct snd_pcm_substream *substream, return 0; }
-int psc_i2s_hw_free(struct snd_pcm_substream *substream, +int psc_dma_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { snd_pcm_set_runtime_buffer(substream, NULL); @@ -154,29 +154,29 @@ int psc_i2s_hw_free(struct snd_pcm_substream *substream, }
/** - * psc_i2s_trigger: start and stop the DMA transfer. + * psc_dma_trigger: start and stop the DMA transfer. * * This function is called by ALSA to start, stop, pause, and resume the DMA * transfer of data. */ -int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, +int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = 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; + struct psc_dma_stream *s; + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; u8 psc_cmd; unsigned long flags;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
- dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" + dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" " stream_id=%i\n", substream, cmd, substream->pstr->stream);
@@ -207,14 +207,14 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, else bcom_gen_bd_tx_reset(s->bcom_task); while (!bcom_queue_full(s->bcom_task)) - psc_i2s_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task);
- /* Due to errata in the i2s mode; need to line up enabling + /* 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_i2s->lock, flags); + spin_lock_irqsave(&psc_dma->lock, flags); /* first make sure it is low */ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) ; @@ -228,7 +228,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) psc_cmd |= MPC52xx_PSC_TX_ENABLE; out_8(®s->command, psc_cmd); - spin_unlock_irqrestore(&psc_i2s->lock, flags); + spin_unlock_irqrestore(&psc_dma->lock, flags);
break;
@@ -236,7 +236,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, /* Turn off the PSC */ s->active = 0; if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (!psc_i2s->playback.active) { + 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 */ @@ -244,7 +244,7 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, } else { out_8(®s->command, 3 << 4); /* reset tx */ out_8(®s->command, 4 << 4); /* reset err */ - if (!psc_i2s->capture.active) + if (!psc_dma->capture.active) out_8(®s->command, 2 << 4); /* reset rx */ }
@@ -255,15 +255,15 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, break;
default: - dev_dbg(psc_i2s->dev, "invalid command\n"); + dev_dbg(psc_dma->dev, "invalid command\n"); return -EINVAL; }
/* Update interrupt enable settings */ imr = 0; - if (psc_i2s->playback.active) + if (psc_dma->playback.active) imr |= MPC52xx_PSC_IMR_TXEMP; - if (psc_i2s->capture.active) + if (psc_dma->capture.active) imr |= MPC52xx_PSC_IMR_ORERR; out_be16(®s->isr_imr.imr, imr);
@@ -271,36 +271,36 @@ int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, }
/** - * psc_i2s_shutdown: shutdown the data transfer on a stream + * psc_dma_shutdown: shutdown the data transfer on a stream * * Shutdown the PSC if there are no other substreams open. */ -void psc_i2s_shutdown(struct snd_pcm_substream *substream, +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_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
- dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); + 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_i2s->playback.active && - !psc_i2s->capture.active) { + if (!psc_dma->playback.active && + !psc_dma->capture.active) {
/* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + 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_i2s->irq, psc_i2s); - free_irq(psc_i2s->capture.irq, &psc_i2s->capture); - free_irq(psc_i2s->playback.irq, &psc_i2s->playback); + 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); } }
@@ -312,7 +312,7 @@ void psc_i2s_shutdown(struct snd_pcm_substream *substream, * interaction with the attached codec */
-static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { +static const struct snd_pcm_hardware psc_dma_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 | @@ -329,80 +329,80 @@ static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { .fifo_size = 0, };
-static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) +static int psc_dma_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; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s;
- dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_pcm_open(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
- snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &psc_dma_pcm_hardware);
s->stream = substream; return 0; }
-static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) +static int psc_dma_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; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s;
- dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_pcm_close(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
s->stream = NULL; return 0; }
static snd_pcm_uframes_t -psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) +psc_dma_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; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s; dma_addr_t count;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_i2s->capture; + s = &psc_dma->capture; else - s = &psc_i2s->playback; + s = &psc_dma->playback;
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, +static struct snd_pcm_ops psc_dma_pcm_ops = { + .open = psc_dma_pcm_open, + .close = psc_dma_pcm_close, .ioctl = snd_pcm_lib_ioctl, - .pointer = psc_i2s_pcm_pointer, + .pointer = psc_dma_pcm_pointer, };
-static u64 psc_i2s_pcm_dmamask = 0xffffffff; -static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +static u64 psc_dma_pcm_dmamask = 0xffffffff; +static int psc_dma_pcm_new(struct snd_card *card, struct snd_soc_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; + size_t size = psc_dma_pcm_hardware.buffer_bytes_max; int rc = 0;
- dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", + dev_dbg(rtd->socdev->dev, "psc_dma_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; + card->dev->dma_mask = &psc_dma_pcm_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff;
@@ -430,13 +430,13 @@ static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, return -ENOMEM; }
-static void psc_i2s_pcm_free(struct snd_pcm *pcm) +static void psc_dma_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); + dev_dbg(rtd->socdev->dev, "psc_dma_pcm_free(pcm=%p)\n", pcm);
for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -448,10 +448,10 @@ static void psc_i2s_pcm_free(struct snd_pcm *pcm) } }
-struct snd_soc_platform psc_i2s_pcm_soc_platform = { +struct snd_soc_platform psc_dma_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, + .pcm_ops = &psc_dma_pcm_ops, + .pcm_new = &psc_dma_pcm_new, + .pcm_free = &psc_dma_pcm_free, };
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 9a19e8a..a33232c 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -6,9 +6,9 @@ #define __SOUND_SOC_FSL_MPC5200_DMA_H__
/** - * psc_i2s_stream - Data specific to a single stream (playback or capture) + * psc_dma_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 + * @psc_dma: pointer back to parent psc_dma data structure * @bcom_task: bestcomm task structure * @irq: irq number for bestcomm task * @period_start: physical address of start of DMA region @@ -16,9 +16,9 @@ * @period_next_pt: physical address of next DMA buffer to enqueue * @period_bytes: size of DMA period in bytes */ -struct psc_i2s_stream { +struct psc_dma_stream { int active; - struct psc_i2s *psc_i2s; + struct psc_dma *psc_dma; struct bcom_task *bcom_task; int irq; struct snd_pcm_substream *stream; @@ -30,7 +30,7 @@ struct psc_i2s_stream { };
/** - * psc_i2s - Private driver data + * psc_dma - 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 @@ -42,7 +42,7 @@ struct psc_i2s_stream { * @playback: Playback stream context data * @capture: Capture stream context data */ -struct psc_i2s { +struct psc_dma { char name[32]; struct mpc52xx_psc __iomem *psc_regs; struct mpc52xx_psc_fifo __iomem *fifo_regs; @@ -53,8 +53,8 @@ struct psc_i2s { u32 sicr;
/* per-stream data */ - struct psc_i2s_stream playback; - struct psc_i2s_stream capture; + struct psc_dma_stream playback; + struct psc_dma_stream capture;
/* Statistics */ struct { @@ -64,18 +64,18 @@ struct psc_i2s { };
-int psc_i2s_startup(struct snd_pcm_substream *substream, +int psc_dma_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-int psc_i2s_hw_free(struct snd_pcm_substream *substream, +int psc_dma_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-void psc_i2s_shutdown(struct snd_pcm_substream *substream, +void psc_dma_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
-int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, +int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai);
-extern struct snd_soc_platform psc_i2s_pcm_soc_platform; +extern struct snd_soc_platform psc_dma_pcm_soc_platform;
#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 8974b53..12a7917 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -54,10 +54,10 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; u32 mode;
- dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i" + dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" " periods=%i buffer_size=%i buffer_bytes=%i\n", __func__, substream, params_period_size(params), params_period_bytes(params), params_periods(params), @@ -77,10 +77,10 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, mode = MPC52xx_PSC_SICR_SIM_CODEC_32; break; default: - dev_dbg(psc_i2s->dev, "invalid format\n"); + dev_dbg(psc_dma->dev, "invalid format\n"); return -EINVAL; } - out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode); + out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -104,8 +104,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, static int psc_i2s_set_sysclk(struct snd_soc_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", + struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", cpu_dai, dir); return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; } @@ -123,8 +123,8 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, */ static int psc_i2s_set_fmt(struct snd_soc_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", + struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", cpu_dai, format); return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; } @@ -140,11 +140,11 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) * psc_i2s_dai_template: template CPU Digital Audio Interface */ static struct snd_soc_dai_ops psc_i2s_dai_ops = { - .startup = psc_i2s_startup, + .startup = psc_dma_startup, .hw_params = psc_i2s_hw_params, - .hw_free = psc_i2s_hw_free, - .shutdown = psc_i2s_shutdown, - .trigger = psc_i2s_trigger, + .hw_free = psc_dma_hw_free, + .shutdown = psc_dma_shutdown, + .trigger = psc_dma_trigger, .set_sysclk = psc_i2s_set_sysclk, .set_fmt = psc_i2s_set_fmt, }; @@ -172,24 +172,24 @@ static struct snd_soc_dai psc_i2s_dai_template = { 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); + struct psc_dma *psc_dma = 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)); + in_be16(&psc_dma->psc_regs->sr_csr.status), + in_be32(&psc_dma->psc_regs->sicr), + in_be16(&psc_dma->fifo_regs->rfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->rfstat), + in_be16(&psc_dma->fifo_regs->tfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->tfstat)); }
-static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) +static int *psc_i2s_get_stat_attr(struct psc_dma *psc_dma, const char *name) { if (strcmp(name, "playback_underrun") == 0) - return &psc_i2s->stats.underrun_count; + return &psc_dma->stats.underrun_count; if (strcmp(name, "capture_overrun") == 0) - return &psc_i2s->stats.overrun_count; + return &psc_dma->stats.overrun_count;
return NULL; } @@ -197,10 +197,10 @@ static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) 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); + struct psc_dma *psc_dma = dev_get_drvdata(dev); int *attrib;
- attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); + attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); if (!attrib) return 0;
@@ -212,10 +212,10 @@ static ssize_t psc_i2s_stat_store(struct device *dev, const char *buf, size_t count) { - struct psc_i2s *psc_i2s = dev_get_drvdata(dev); + struct psc_dma *psc_dma = dev_get_drvdata(dev); int *attrib;
- attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); + attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); if (!attrib) return 0;
@@ -238,7 +238,7 @@ 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 psc_dma *psc_dma; struct resource res; int size, psc_id, irq, rc; const __be32 *prop; @@ -265,56 +265,56 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, }
/* Allocate and initialize the driver private data */ - psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL); - if (!psc_i2s) { + psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); + if (!psc_dma) { 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.psc_i2s = psc_i2s; - psc_i2s->capture.psc_i2s = psc_i2s; - snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1); + spin_lock_init(&psc_dma->lock); + psc_dma->irq = irq; + psc_dma->psc_regs = regs; + psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; + psc_dma->dev = &op->dev; + psc_dma->playback.psc_dma = psc_dma; + psc_dma->capture.psc_dma = psc_dma; + snprintf(psc_dma->name, sizeof psc_dma->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; + memcpy(&psc_dma->dai, &psc_i2s_dai_template, sizeof psc_dma->dai); + psc_dma->dai.private_data = psc_dma; + psc_dma->dai.name = psc_dma->name; + psc_dma->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.bcom_task = + psc_dma->capture.bcom_task = bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); - psc_i2s->playback.bcom_task = + psc_dma->playback.bcom_task = bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); - if (!psc_i2s->capture.bcom_task || - !psc_i2s->playback.bcom_task) { + if (!psc_dma->capture.bcom_task || + !psc_dma->playback.bcom_task) { dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); iounmap(regs); - kfree(psc_i2s); + kfree(psc_dma); return -ENODEV; }
/* Disable all interrupts and reset the PSC */ - out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); - out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */ - out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */ - out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); + out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset transmitter */ + out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset receiver */ + out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ + out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
/* Configure the serial interface mode; defaulting to CODEC8 mode */ - psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | + psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL; if (of_get_property(op->node, "fsl,cellslave", NULL)) - psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | + psc_dma->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | MPC52xx_PSC_SICR_GENCLK; - out_be32(&psc_i2s->psc_regs->sicr, - psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); + out_be32(&psc_dma->psc_regs->sicr, + psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
/* Check for the codec handle. If it is not present then we * are done */ @@ -325,54 +325,54 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, * First write: RxRdy (FIFO Alarm) generates rx FIFO irq * Second write: register Normal mode for non loopback */ - out_8(&psc_i2s->psc_regs->mode, 0); - out_8(&psc_i2s->psc_regs->mode, 0); + out_8(&psc_dma->psc_regs->mode, 0); + out_8(&psc_dma->psc_regs->mode, 0);
/* Set the TX and RX fifo alarm thresholds */ - out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100); - out_8(&psc_i2s->fifo_regs->rfcntl, 0x4); - out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100); - out_8(&psc_i2s->fifo_regs->tfcntl, 0x7); + out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); + out_8(&psc_dma->fifo_regs->rfcntl, 0x4); + out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); + out_8(&psc_dma->fifo_regs->tfcntl, 0x7);
/* Lookup the IRQ numbers */ - psc_i2s->playback.irq = - bcom_get_task_irq(psc_i2s->playback.bcom_task); - psc_i2s->capture.irq = - bcom_get_task_irq(psc_i2s->capture.bcom_task); + psc_dma->playback.irq = + bcom_get_task_irq(psc_dma->playback.bcom_task); + psc_dma->capture.irq = + bcom_get_task_irq(psc_dma->capture.bcom_task);
/* Save what we've done so it can be found again later */ - dev_set_drvdata(&op->dev, psc_i2s); + dev_set_drvdata(&op->dev, psc_dma);
/* 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); + rc = device_create_file(psc_dma->dev, &dev_attr_status); + rc |= device_create_file(psc_dma->dev, &dev_attr_capture_overrun); + rc |= device_create_file(psc_dma->dev, &dev_attr_playback_underrun); if (rc) - dev_info(psc_i2s->dev, "error creating sysfs files\n"); + dev_info(psc_dma->dev, "error creating sysfs files\n");
- snd_soc_register_platform(&psc_i2s_pcm_soc_platform); + snd_soc_register_platform(&psc_dma_pcm_soc_platform);
/* Tell the ASoC OF helpers about it */ - of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node, - &psc_i2s->dai); + of_snd_soc_register_platform(&psc_dma_pcm_soc_platform, op->node, + &psc_dma->dai);
return 0; }
static int __devexit psc_i2s_of_remove(struct of_device *op) { - struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev); + struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);
dev_dbg(&op->dev, "psc_i2s_remove()\n");
- snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform); + snd_soc_unregister_platform(&psc_dma_pcm_soc_platform);
- bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task); - bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task); + bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); + bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);
- iounmap(psc_i2s->psc_regs); - iounmap(psc_i2s->fifo_regs); - kfree(psc_i2s); + iounmap(psc_dma->psc_regs); + iounmap(psc_dma->fifo_regs); + kfree(psc_dma); dev_set_drvdata(&op->dev, NULL);
return 0;
On Wed, May 13, 2009 at 09:59:05PM -0400, Jon Smirl wrote:
Rename the functions in the mpc5200 DMA file from i2s based names to dma ones to reflect the file they are in.
Signed-off-by: Jon Smirl jonsmirl@gmail.com
I'm OK with these if Grant is?
Reapply the changes from 2008f137e92220b98120c4803499cdddb2b0fb06 to mpc5200_dma.c since the code effected by this change moved to a different file.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/mpc5200_dma.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index c82ef75..6850392 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -314,7 +314,8 @@ void psc_dma_shutdown(struct snd_pcm_substream *substream,
static const struct snd_pcm_hardware psc_dma_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, + 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, .rate_min = 8000,
Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 PSC registers
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- arch/powerpc/include/asm/mpc52xx_psc.h | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/asm/mpc52xx_psc.h index a218da6..c323f4f 100644 --- a/arch/powerpc/include/asm/mpc52xx_psc.h +++ b/arch/powerpc/include/asm/mpc52xx_psc.h @@ -28,6 +28,10 @@ #define MPC52xx_PSC_MAXNUM 6
/* Programmable Serial Controller (PSC) status register bits */ +#define MPC52xx_PSC_SR_UNEX_RX 0x0001 +#define MPC52xx_PSC_SR_DATA_VAL 0x0002 +#define MPC52xx_PSC_SR_DATA_OVR 0x0004 +#define MPC52xx_PSC_SR_CMDSEND 0x0008 #define MPC52xx_PSC_SR_CDE 0x0080 #define MPC52xx_PSC_SR_RXRDY 0x0100 #define MPC52xx_PSC_SR_RXFULL 0x0200 @@ -61,6 +65,12 @@ #define MPC52xx_PSC_RXTX_FIFO_EMPTY 0x0001
/* PSC interrupt status/mask bits */ +#define MPC52xx_PSC_IMR_UNEX_RX_SLOT 0x0001 +#define MPC52xx_PSC_IMR_DATA_VALID 0x0002 +#define MPC52xx_PSC_IMR_DATA_OVR 0x0004 +#define MPC52xx_PSC_IMR_CMD_SEND 0x0008 +#define MPC52xx_PSC_IMR_ERROR 0x0040 +#define MPC52xx_PSC_IMR_DEOF 0x0080 #define MPC52xx_PSC_IMR_TXRDY 0x0100 #define MPC52xx_PSC_IMR_RXRDY 0x0200 #define MPC52xx_PSC_IMR_DB 0x0400
On Wed, May 13, 2009 at 09:59:10PM -0400, Jon Smirl wrote:
Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 PSC registers
Signed-off-by: Jon Smirl jonsmirl@gmail.com
This is OK too, providing the PowerPC guys are happy with merging it via ALSA.
Allow a custom ASOC fabric driver with soc-of-simple. Register multiple DAIs. If no custom fabric driver, supply a default one so that ASOC binding will complete.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- include/sound/soc-of-simple.h | 16 +++- sound/last.c | 6 + sound/soc/codecs/tlv320aic26.c | 2 sound/soc/fsl/Kconfig | 2 sound/soc/fsl/mpc5200_psc_i2s.c | 4 - sound/soc/fsl/soc-of-simple.c | 160 ++++++++++++++++++++++++++++++++------- 6 files changed, 154 insertions(+), 36 deletions(-)
diff --git a/include/sound/soc-of-simple.h b/include/sound/soc-of-simple.h index a064e19..97d04af 100644 --- a/include/sound/soc-of-simple.h +++ b/include/sound/soc-of-simple.h @@ -12,13 +12,21 @@ #include <linux/of.h> #include <sound/soc.h>
+#define SOC_OF_SIMPLE_MAX_DAI 2 + int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, void *codec_data, struct snd_soc_dai *dai, - struct device_node *node); + int count, struct device_node *node); + +int of_snd_soc_register_cpu_dai(struct device_node *node, + struct snd_soc_dai *cpu_dai, int count); + +int of_snd_soc_register_platform(struct snd_soc_platform *platform); + +int of_snd_soc_register_fabric(const char *name, struct snd_soc_ops *ops, + int (*init)(struct snd_soc_codec *codec));
-int of_snd_soc_register_platform(struct snd_soc_platform *platform, - struct device_node *node, - struct snd_soc_dai *cpu_dai); +int of_snd_soc_register_default_fabric(void);
#endif
diff --git a/sound/last.c b/sound/last.c index bdd0857..b6efece 100644 --- a/sound/last.c +++ b/sound/last.c @@ -22,11 +22,15 @@ #define SNDRV_MAIN_OBJECT_FILE #include <linux/init.h> #include <sound/core.h> +#include <sound/soc-of-simple.h>
static int __init alsa_sound_last_init(void) { int idx, ok = 0; - + +#if defined(CONFIG_SND_SOC_OF_SIMPLE) + of_snd_soc_register_default_fabric(); +#endif printk(KERN_INFO "ALSA device list:\n"); for (idx = 0; idx < SNDRV_CARDS; idx++) if (snd_cards[idx] != NULL) { diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 3387d9e..95a7ac9 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -487,7 +487,7 @@ static int aic26_spi_probe(struct spi_device *spi)
#if defined(CONFIG_SND_SOC_OF_SIMPLE) /* Tell the of_soc helper about this codec */ - of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, + of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, 1, spi->dev.archdata.of_node); #endif
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index dc79bdf..8060b95 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,5 +1,5 @@ config SND_SOC_OF_SIMPLE - tristate + bool config SND_MPC52xx_DMA tristate diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 12a7917..5dbf1bc 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -353,8 +353,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, snd_soc_register_platform(&psc_dma_pcm_soc_platform);
/* Tell the ASoC OF helpers about it */ - of_snd_soc_register_platform(&psc_dma_pcm_soc_platform, op->node, - &psc_dma->dai); + of_snd_soc_register_platform(&mpc5200_audio_dma_platform); + of_snd_soc_register_cpu_dai(op->node, &psc_dma->dai, 1);
return 0; } diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c index 8bc5cd9..bf307aa 100644 --- a/sound/soc/fsl/soc-of-simple.c +++ b/sound/soc/fsl/soc-of-simple.c @@ -32,18 +32,23 @@ struct of_snd_soc_device { struct list_head list; struct snd_soc_device device; struct snd_soc_card card; - struct snd_soc_dai_link dai_link; + struct snd_soc_dai_link dai_link[SOC_OF_SIMPLE_MAX_DAI]; struct platform_device *pdev; - struct device_node *platform_node; + struct device_node *cpu_dai_node; struct device_node *codec_node; };
-static struct snd_soc_ops of_snd_soc_ops = { -}; +/* template values */ +struct snd_soc_platform *template_platform; +const char *template_name = NULL; +struct snd_soc_ops *template_ops; +int (*template_init)(struct snd_soc_codec *codec);
static struct of_snd_soc_device * of_snd_soc_get_device(struct device_node *codec_node) { + int i; + struct of_snd_soc_device *of_soc;
list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { @@ -58,10 +63,16 @@ of_snd_soc_get_device(struct device_node *codec_node) /* Initialize the structure and add it to the global list */ of_soc->codec_node = codec_node; of_soc->id = of_snd_soc_next_index++; - of_soc->card.dai_link = &of_soc->dai_link; - of_soc->card.num_links = 1; + of_soc->card.dai_link = of_soc->dai_link; of_soc->device.card = &of_soc->card; - of_soc->dai_link.ops = &of_snd_soc_ops; + of_soc->card.num_links = SOC_OF_SIMPLE_MAX_DAI; + for (i = 0; i < SOC_OF_SIMPLE_MAX_DAI; i++) { + of_soc->dai_link[i].ops = template_ops; + of_soc->dai_link[i].init = template_init; + } + of_soc->card.name = (char *)template_name; + of_soc->card.platform = template_platform; + list_add(&of_soc->list, &of_snd_soc_device_list);
return of_soc; @@ -74,10 +85,11 @@ static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
/* Only register the device if both the codec and platform have * been registered */ - if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) + if ((!of_soc->device.codec_data) || (!of_soc->cpu_dai_node) || + !of_soc->card.platform || !of_soc->card.name) return;
- pr_info("platform<-->codec match achieved; registering machine\n"); + pr_info("platform<-->codec match achieved; registering fabric\n");
pdev = platform_device_alloc("soc-audio", of_soc->id); if (!pdev) { @@ -100,10 +112,10 @@ static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, void *codec_data, struct snd_soc_dai *dai, - struct device_node *node) + int count, struct device_node *node) { struct of_snd_soc_device *of_soc; - int rc = 0; + int i, rc = 0;
pr_info("registering ASoC codec driver: %s\n", node->full_name);
@@ -117,10 +129,11 @@ int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, /* Store the codec data */ of_soc->device.codec_data = codec_data; of_soc->device.codec_dev = codec_dev; - of_soc->dai_link.name = (char *)node->name; - of_soc->dai_link.stream_name = (char *)node->name; - of_soc->dai_link.codec_dai = dai; - + of_soc->card.num_links = min(count, of_soc->card.num_links); + for (i = 0; i < of_soc->card.num_links; i++) { + of_soc->dai_link[i].name = dai[i].name; + of_soc->dai_link[i].codec_dai = &dai[i]; + } /* Now try to register the SoC device */ of_snd_soc_register_device(of_soc);
@@ -130,21 +143,51 @@ int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, } EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
-int of_snd_soc_register_platform(struct snd_soc_platform *platform, - struct device_node *node, - struct snd_soc_dai *cpu_dai) +int of_snd_soc_register_cpu_dai(struct device_node *node, + struct snd_soc_dai *cpu_dai, int count) { struct of_snd_soc_device *of_soc; struct device_node *codec_node; const phandle *handle; - int len, rc = 0; + int i, len, rc = 0;
- pr_info("registering ASoC platform driver: %s\n", node->full_name); + pr_info("registering ASoC CPU DAI driver: %s\n", node->full_name);
handle = of_get_property(node, "codec-handle", &len); - if (!handle || len < sizeof(handle)) - return -ENODEV; - codec_node = of_find_node_by_phandle(*handle); + if (handle && len >= sizeof(handle)) + codec_node = of_find_node_by_phandle(*handle); + else { + /* Check for the codec child nodes */ + for_each_child_of_node(node, codec_node) { + struct platform_device *pdev; + struct dev_archdata dev_ad = {}; + char name[MODULE_NAME_LEN]; + const u32 *addr; + int len; + + if (of_modalias_node(codec_node, name, sizeof(name)) < 0) + continue; + + addr = of_get_property(codec_node, "reg", &len); + if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) { + pr_err("invalid codec reg in device tree\n"); + continue; + } + request_module("%s", name); + + pdev = platform_device_alloc(name, 0); + + dev_archdata_set_node(&dev_ad, codec_node); + pdev->dev.archdata = dev_ad; + + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + return rc; + } + break; + } + } if (!codec_node) return -ENODEV; pr_info("looking for codec: %s\n", codec_node->full_name); @@ -156,10 +199,12 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform, goto out; }
- of_soc->platform_node = node; - of_soc->dai_link.cpu_dai = cpu_dai; - of_soc->card.platform = platform; - of_soc->card.name = of_soc->dai_link.cpu_dai->name; + of_soc->cpu_dai_node = node; + of_soc->card.num_links = min(count, of_soc->card.num_links); + for (i = 0; i < of_soc->card.num_links; i++) { + of_soc->dai_link[i].stream_name = cpu_dai[i].name; + of_soc->dai_link[i].cpu_dai = &cpu_dai[i]; + }
/* Now try to register the SoC device */ of_snd_soc_register_device(of_soc); @@ -168,4 +213,65 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform, mutex_unlock(&of_snd_soc_mutex); return rc; } +EXPORT_SYMBOL_GPL(of_snd_soc_register_cpu_dai); + +int of_snd_soc_register_platform(struct snd_soc_platform *platform) +{ + struct of_snd_soc_device *of_soc; + int rc = 0; + + pr_info("registering ASoC platform driver: %s\n", platform->name); + template_platform = platform; + + mutex_lock(&of_snd_soc_mutex); + list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { + of_soc->card.platform = platform; + of_snd_soc_register_device(of_soc); + } + mutex_unlock(&of_snd_soc_mutex); + return rc; +} EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); + +int of_snd_soc_register_fabric(const char *name, struct snd_soc_ops *ops, + int (*init)(struct snd_soc_codec *codec)) +{ + int i; + struct of_snd_soc_device *of_soc; + + pr_info("registering ASoC fabric driver: %s\n", name); + template_name = name; + template_ops = ops; + template_init = init; + + mutex_lock(&of_snd_soc_mutex); + list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { + for (i = 0; i < SOC_OF_SIMPLE_MAX_DAI; i++) { + of_soc->dai_link[i].ops = ops; + of_soc->dai_link[i].init = init; + } + of_soc->card.name = (char *)name; + of_snd_soc_register_device(of_soc); + } + mutex_unlock(&of_snd_soc_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_fabric); + +/* If no board specific fabric driver has been registered, register a default one */ +int of_snd_soc_register_default_fabric(void) +{ + struct device_node *root; + const char *model = ""; + + if (template_name != NULL) + return 0; + + root = of_find_node_by_path("/"); + if (root) + model = of_get_property(root, "model", NULL); + + return of_snd_soc_register_fabric(model, NULL, NULL); +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_default_fabric); +
On Wed, May 13, 2009 at 09:59:12PM -0400, Jon Smirl wrote:
Allow a custom ASOC fabric driver with soc-of-simple. Register multiple DAIs. If no custom fabric driver, supply a default one so that ASOC binding will complete.
Hrm. I'm not sure the way you're providing a default behaviour here is going to work (see comments below). Would it not be better to have something in the OF tree that specifies non-default behaviour? Obviously that opens the whole can of worms with OpenFirmware not being able to cope with machine drivers well, though that's probably going to have to be dealt with at some point.
Also, note that it's spelt 'ASoC'. I'm fairly sure I've mentioned this to you before.
+#define SOC_OF_SIMPLE_MAX_DAI 2
Why not make this dynamic? It's also surprising to see this in the header, it should be an implementation detail.
+int of_snd_soc_register_cpu_dai(struct device_node *node,
struct snd_soc_dai *cpu_dai, int count);
ASoC is moving towards having all DAIs register equivalently. Might be as well to consider supporting this here too, though the core isn't quite there yet.
+int of_snd_soc_register_default_fabric(void);
+#include <sound/soc-of-simple.h>
static int __init alsa_sound_last_init(void) { int idx, ok = 0;
+#if defined(CONFIG_SND_SOC_OF_SIMPLE)
- of_snd_soc_register_default_fabric();
+#endif
This approach relies on having things built in or being able to load the modules before the alsa_sound_last_init() is called. That's not something that's going to be reliably doable for all systems - netbooted development systems are a common example here.
#if defined(CONFIG_SND_SOC_OF_SIMPLE) /* Tell the of_soc helper about this codec */
- of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
- of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, 1, spi->dev.archdata.of_node);
#endif
If, as previously discussed, this were merged with the core you could pick most of this data out of the snd_soc_codec.
- of_soc->card.name = (char *)template_name;
This cast is suspect..
- if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
- if ((!of_soc->device.codec_data) || (!of_soc->cpu_dai_node) ||
!of_soc->card.platform || !of_soc->card.name)
Looks like something went very wrong with the indentation here.
- pr_info("registering ASoC platform driver: %s\n", platform->name);
pr_debug().
Add SNDRV_PCM_FMTBIT_S32_BE as a valid AC97 format
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- include/sound/soc-dai.h | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 496dc30..352d7ee 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -79,7 +79,8 @@ struct snd_pcm_substream; #define SND_SOC_CLOCK_OUT 1
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_S32_BE)
struct snd_soc_dai_ops; struct snd_soc_dai;
On Wed, May 13, 2009 at 09:59:14PM -0400, Jon Smirl wrote:
Add SNDRV_PCM_FMTBIT_S32_BE as a valid AC97 format
Signed-off-by: Jon Smirl jonsmirl@gmail.com
Applied, thanks.
Have the WM9712 driver self register itself. This is needed to support device tree driver binding via the soc_of_simple code.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/codecs/wm9712.c | 34 ++++++++++++++++++++++++++++++++++ 1 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 550c903..cb0e679 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -20,6 +20,7 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/soc-of-simple.h> #include "wm9712.h"
#define WM9712_VERSION "0.4" @@ -740,6 +741,39 @@ struct snd_soc_codec_device soc_codec_dev_wm9712 = { }; EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
+static int __init wm9712_probe(struct platform_device *pdev) +{ + snd_soc_register_dais(wm9712_dai, ARRAY_SIZE(wm9712_dai)); +#if defined(CONFIG_SND_SOC_OF_SIMPLE) + /* Tell the of_soc helper about this codec */ + of_snd_soc_register_codec(&soc_codec_dev_wm9712, pdev->dev.archdata.of_node, + wm9712_dai, ARRAY_SIZE(wm9712_dai), + pdev->dev.archdata.of_node); +#endif + return 0; +} + +static struct platform_driver wm9712_driver = +{ + .probe = wm9712_probe, + .driver = { + .name = "wm9712", + }, +}; + +static __init int wm9712_driver_init(void) +{ + return platform_driver_register(&wm9712_driver); +} + +static __exit void wm9712_driver_exit(void) +{ +} + +module_init(wm9712_driver_init); +module_exit(wm9712_driver_exit); + + MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver"); MODULE_AUTHOR("Liam Girdwood"); MODULE_LICENSE("GPL");
On Wed, May 13, 2009 at 09:59:16PM -0400, Jon Smirl wrote:
Have the WM9712 driver self register itself. This is needed to support device tree driver binding via the soc_of_simple code.
Signed-off-by: Jon Smirl jonsmirl@gmail.com
All the comments I made when you posted this earlier yesterday continue to apply.
Rewrite the mpc5200 audio DMA code to support both I2S and AC97. Make it more robust.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/mpc5200_dma.c | 503 +++++++++++++++++++++++++++++---------- sound/soc/fsl/mpc5200_dma.h | 32 +- sound/soc/fsl/mpc5200_psc_i2s.c | 198 +-------------- 3 files changed, 396 insertions(+), 337 deletions(-)
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 6850392..5ac2693 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -23,6 +23,8 @@
#include <sysdev/bestcomm/bestcomm.h> #include <sysdev/bestcomm/gen_bd.h> +#include <asm/time.h> +#include <asm/mpc52xx.h> #include <asm/mpc52xx_psc.h>
#include "mpc5200_dma.h" @@ -50,7 +52,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 +83,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 +105,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 +120,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 +156,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; @@ -170,6 +166,7 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, u16 imr; u8 psc_cmd; unsigned long flags; + int i;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) s = &psc_dma->capture; @@ -189,25 +186,24 @@ 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 */ - 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); - } else { - out_8(®s->command, MPC52xx_PSC_RST_TX); - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - } + s->runtime = runtime; + s->appl_ptr = s->runtime->control->appl_ptr - (runtime->period_size * runtime->periods);
- /* Next, fill up the bestcomm bd queue and enable DMA. + /* 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) + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { bcom_gen_bd_rx_reset(s->bcom_task); - else + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); + } else { bcom_gen_bd_tx_reset(s->bcom_task); - while (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); + psc_dma_bcom_enqueue_tx(s); + } + bcom_enable(s->bcom_task);
/* Due to errata in the dma mode; need to line up enabling @@ -227,30 +223,22 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, 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, 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 +253,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,67 +267,93 @@ 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, + .formats = SNDRV_PCM_FMTBIT_S32_BE, .rate_min = 8000, .rate_max = 48000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .period_bytes_max = 1024 * 1024, .period_bytes_min = 32, .periods_min = 2, .periods_max = 256, .buffer_bytes_max = 2 * 1024 * 1024, - .fifo_size = 0, + .fifo_size = 512, };
-static int psc_dma_pcm_open(struct snd_pcm_substream *substream) +static int psc_dma_open(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct psc_dma_stream *s; + int rc;
- dev_dbg(psc_dma->dev, "psc_dma_pcm_open(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) s = &psc_dma->capture; else s = &psc_dma->playback;
- snd_soc_set_runtime_hwparams(substream, &psc_dma_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); + + rc = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (rc < 0) { + dev_err(substream->pcm->card->dev, "invalid buffer size\n"); + return rc; + } + + if (!psc_dma->playback.active && + !psc_dma->capture.active) { + /* Setup the IRQs */ + }
s->stream = substream; return 0; }
-static int psc_dma_pcm_close(struct snd_pcm_substream *substream) +static int psc_dma_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct psc_dma_stream *s;
- dev_dbg(psc_dma->dev, "psc_dma_pcm_close(substream=%p)\n", substream); + dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) s = &psc_dma->capture; else s = &psc_dma->playback;
+ 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, psc_dma->imr); + //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 */ + + } + s->stream = NULL; return 0; }
static snd_pcm_uframes_t -psc_dma_pcm_pointer(struct snd_pcm_substream *substream) +psc_dma_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct psc_dma_stream *s; dma_addr_t count; + snd_pcm_uframes_t frames;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) s = &psc_dma->capture; @@ -381,46 +362,63 @@ psc_dma_pcm_pointer(struct snd_pcm_substream *substream)
count = s->period_current_pt - s->period_start;
- return bytes_to_frames(substream->runtime, count); + frames = bytes_to_frames(substream->runtime, count); + return frames; +} + +static int +psc_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; }
-static struct snd_pcm_ops psc_dma_pcm_ops = { - .open = psc_dma_pcm_open, - .close = psc_dma_pcm_close, +static struct snd_pcm_ops psc_dma_ops = { + .open = psc_dma_open, + .close = psc_dma_close, + .hw_free = psc_dma_hw_free, .ioctl = snd_pcm_lib_ioctl, - .pointer = psc_dma_pcm_pointer, + .pointer = psc_dma_pointer, + .trigger = psc_dma_trigger, + .hw_params = psc_dma_hw_params, };
-static u64 psc_dma_pcm_dmamask = 0xffffffff; -static int psc_dma_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +static u64 psc_dma_dmamask = 0xffffffff; +static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; - size_t size = psc_dma_pcm_hardware.buffer_bytes_max; + struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + size_t size = psc_dma_hardware.buffer_bytes_max; int rc = 0;
- dev_dbg(rtd->socdev->dev, "psc_dma_pcm_new(card=%p, dai=%p, pcm=%p)\n", + dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", card, dai, pcm);
if (!card->dev->dma_mask) - card->dev->dma_mask = &psc_dma_pcm_dmamask; + card->dev->dma_mask = &psc_dma_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff;
if (pcm->streams[0].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, size, &pcm->streams[0].substream->dma_buffer); if (rc) goto playback_alloc_err; }
if (pcm->streams[1].substream) { - rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, + rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, size, &pcm->streams[1].substream->dma_buffer); if (rc) goto capture_alloc_err; }
+ if (rtd->socdev->card->codec->ac97) + rtd->socdev->card->codec->ac97->private_data = psc_dma; + return 0;
capture_alloc_err: @@ -431,13 +429,13 @@ static int psc_dma_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, return -ENOMEM; }
-static void psc_dma_pcm_free(struct snd_pcm *pcm) +static void psc_dma_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_dma_pcm_free(pcm=%p)\n", pcm); + dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm);
for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -449,10 +447,247 @@ static void psc_dma_pcm_free(struct snd_pcm *pcm) } }
-struct snd_soc_platform psc_dma_pcm_soc_platform = { +struct snd_soc_platform mpc5200_audio_dma_platform = { .name = "mpc5200-psc-audio", - .pcm_ops = &psc_dma_pcm_ops, - .pcm_new = &psc_dma_pcm_new, - .pcm_free = &psc_dma_pcm_free, + .pcm_ops = &psc_dma_ops, + .pcm_new = &psc_dma_new, + .pcm_free = &psc_dma_free, }; +EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); + +/* --------------------------------------------------------------------- + * Sysfs attributes for debugging + */ + +static ssize_t psc_dma_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct psc_dma *psc_dma = 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_dma->psc_regs->sr_csr.status), + in_be32(&psc_dma->psc_regs->sicr), + in_be16(&psc_dma->fifo_regs->rfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->rfstat), + in_be16(&psc_dma->fifo_regs->tfnum) & 0x1ff, + in_be16(&psc_dma->fifo_regs->tfstat)); +} + +static int *psc_dma_get_stat_attr(struct psc_dma *psc_dma, const char *name) +{ + if (strcmp(name, "playback_underrun") == 0) + return &psc_dma->stats.underrun_count; + if (strcmp(name, "capture_overrun") == 0) + return &psc_dma->stats.overrun_count; + + return NULL; +} + +static ssize_t psc_dma_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct psc_dma *psc_dma = dev_get_drvdata(dev); + int *attrib; + + attrib = psc_dma_get_stat_attr(psc_dma, attr->attr.name); + if (!attrib) + return 0; + + return sprintf(buf, "%i\n", *attrib); +} + +static ssize_t psc_dma_stat_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct psc_dma *psc_dma = dev_get_drvdata(dev); + int *attrib; + + attrib = psc_dma_get_stat_attr(psc_dma, attr->attr.name); + if (!attrib) + return 0; + + *attrib = simple_strtoul(buf, NULL, 0); + return count; +} + +static DEVICE_ATTR(status, 0644, psc_dma_status_show, NULL); +static DEVICE_ATTR(playback_underrun, 0644, psc_dma_stat_show, + psc_dma_stat_store); +static DEVICE_ATTR(capture_overrun, 0644, psc_dma_stat_show, + psc_dma_stat_store); + + +int mpc5200_audio_dma_create(struct of_device *op, struct snd_soc_dai *template, int tsize) +{ + phys_addr_t fifo; + struct psc_dma *psc_dma; + struct resource res; + int i, nDAI, size, psc_id, irq, rc; + const __be32 *prop; + void __iomem *regs; + + /* 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_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); + if (!psc_dma) { + iounmap(regs); + return -ENOMEM; + } + + spin_lock_init(&psc_dma->lock); + psc_dma->irq = irq; + psc_dma->psc_regs = regs; + psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; + psc_dma->dev = &op->dev; + psc_dma->playback.psc_dma = psc_dma; + psc_dma->capture.psc_dma = psc_dma; + snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u AC97", psc_id+1); + + /* Fill out the CPU DAI structure */ + nDAI = min(tsize, SOC_OF_SIMPLE_MAX_DAI); + for (i = 0; i < nDAI; i++) { + memcpy(&psc_dma->dai[i], &template[i], sizeof(struct snd_soc_dai)); + psc_dma->dai[i].private_data = psc_dma; + snprintf(psc_dma->stream_name[i], PSC_STREAM_NAME_LEN, template[i].name, psc_dma->name); + psc_dma->dai[i].name = psc_dma->stream_name[i]; + psc_dma->dai[i].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_dma->capture.bcom_task = + bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); + psc_dma->playback.bcom_task = + bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); + if (!psc_dma->capture.bcom_task || + !psc_dma->playback.bcom_task) { + dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); + iounmap(regs); + kfree(psc_dma); + return -ENODEV; + } + + /* Disable all interrupts and reset the PSC */ + out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); /* reset receiver */ + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); /* reset transmitter */ + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); /* reset error */ + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); /* reset mode */ + + /* Set up mode register; + * First write: RxRdy (FIFO Alarm) generates rx FIFO irq + * Second write: register Normal mode for non loopback + */ + out_8(&psc_dma->psc_regs->mode, 0); + out_8(&psc_dma->psc_regs->mode, 0); + + /* Set the TX and RX fifo alarm thresholds */ + out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); + out_8(&psc_dma->fifo_regs->rfcntl, 0x4); + out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); + out_8(&psc_dma->fifo_regs->tfcntl, 0x7); + + /* Lookup the IRQ numbers */ + psc_dma->playback.irq = + bcom_get_task_irq(psc_dma->playback.bcom_task); + psc_dma->capture.irq = + bcom_get_task_irq(psc_dma->capture.bcom_task); + + 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_rx, IRQF_SHARED, + "psc-dma-capture", &psc_dma->capture); + rc |= request_irq(psc_dma->playback.irq, + &psc_dma_bcom_irq_tx, 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; + } + + /* Save what we've done so it can be found again later */ + dev_set_drvdata(&op->dev, psc_dma); + + /* Register the SYSFS files */ + rc = device_create_file(psc_dma->dev, &dev_attr_status); + rc |= device_create_file(psc_dma->dev, &dev_attr_capture_overrun); + rc |= device_create_file(psc_dma->dev, &dev_attr_playback_underrun); + if (rc) + dev_info(psc_dma->dev, "error creating sysfs files\n"); + + rc = snd_soc_register_dais(psc_dma->dai, nDAI); + if (rc != 0) { + pr_err("Failed to register DAI\n"); + return 0; + } + + /* Tell the ASoC OF helpers about it */ + of_snd_soc_register_cpu_dai(op->node, psc_dma->dai, nDAI); + + return 0; +} +EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); + +int mpc5200_audio_dma_destroy(struct of_device *op) +{ + struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); + + dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); + + bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); + bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); + + /* 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); + + iounmap(psc_dma->psc_regs); + iounmap(psc_dma->fifo_regs); + kfree(psc_dma); + dev_set_drvdata(&op->dev, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); + +static int __init mpc5200_soc_platform_init(void) +{ + /* Tell the ASoC OF helpers about it */ + of_snd_soc_register_platform(&mpc5200_audio_dma_platform); + return snd_soc_register_platform(&mpc5200_audio_dma_platform); +} +module_init(mpc5200_soc_platform_init); + +static void __exit mpc5200_soc_platform_exit(void) +{ + snd_soc_unregister_platform(&mpc5200_audio_dma_platform); +} +module_exit(mpc5200_soc_platform_exit);
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index a33232c..d71e503 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -5,8 +5,12 @@ #ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ #define __SOUND_SOC_FSL_MPC5200_DMA_H__
+#include <sound/soc-of-simple.h> + +#define PSC_STREAM_NAME_LEN 32 + /** - * psc_dma_stream - Data specific to a single stream (playback or capture) + * psc_ac97_stream - Data specific to a single stream (playback or capture) * @active: flag indicating if the stream is active * @psc_dma: pointer back to parent psc_dma data structure * @bcom_task: bestcomm task structure @@ -17,6 +21,9 @@ * @period_bytes: size of DMA period in bytes */ struct psc_dma_stream { + struct snd_pcm_runtime *runtime; + snd_pcm_uframes_t appl_ptr; + int active; struct psc_dma *psc_dma; struct bcom_task *bcom_task; @@ -27,6 +34,7 @@ struct psc_dma_stream { dma_addr_t period_next_pt; dma_addr_t period_current_pt; int period_bytes; + int period_size; };
/** @@ -48,9 +56,13 @@ struct psc_dma { struct mpc52xx_psc_fifo __iomem *fifo_regs; unsigned int irq; struct device *dev; - struct snd_soc_dai dai; + struct snd_soc_dai dai[SOC_OF_SIMPLE_MAX_DAI]; + char stream_name[SOC_OF_SIMPLE_MAX_DAI][PSC_STREAM_NAME_LEN]; spinlock_t lock; u32 sicr; + uint sysclk; + int imr; + unsigned int slots;
/* per-stream data */ struct psc_dma_stream playback; @@ -63,19 +75,9 @@ struct psc_dma { } stats; };
+int mpc5200_audio_dma_create(struct of_device *op, struct snd_soc_dai *template, int tsize); +int mpc5200_audio_dma_destroy(struct of_device *op);
-int psc_dma_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai); - -int psc_dma_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai); - -void psc_dma_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai); - -int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai); - -extern struct snd_soc_platform psc_dma_pcm_soc_platform; +extern struct snd_soc_platform mpc5200_audio_dma_platform;
#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 5dbf1bc..513d68d 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -82,8 +82,6 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, } out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - return 0; }
@@ -140,11 +138,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) * psc_i2s_dai_template: template CPU Digital Audio Interface */ static struct snd_soc_dai_ops psc_i2s_dai_ops = { - .startup = psc_dma_startup, .hw_params = psc_i2s_hw_params, - .hw_free = psc_dma_hw_free, - .shutdown = psc_dma_shutdown, - .trigger = psc_dma_trigger, .set_sysclk = psc_i2s_set_sysclk, .set_fmt = psc_i2s_set_fmt, }; @@ -166,70 +160,6 @@ static struct snd_soc_dai psc_i2s_dai_template = { };
/* --------------------------------------------------------------------- - * Sysfs attributes for debugging - */ - -static ssize_t psc_i2s_status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct psc_dma *psc_dma = 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_dma->psc_regs->sr_csr.status), - in_be32(&psc_dma->psc_regs->sicr), - in_be16(&psc_dma->fifo_regs->rfnum) & 0x1ff, - in_be16(&psc_dma->fifo_regs->rfstat), - in_be16(&psc_dma->fifo_regs->tfnum) & 0x1ff, - in_be16(&psc_dma->fifo_regs->tfstat)); -} - -static int *psc_i2s_get_stat_attr(struct psc_dma *psc_dma, const char *name) -{ - if (strcmp(name, "playback_underrun") == 0) - return &psc_dma->stats.underrun_count; - if (strcmp(name, "capture_overrun") == 0) - return &psc_dma->stats.overrun_count; - - return NULL; -} - -static ssize_t psc_i2s_stat_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct psc_dma *psc_dma = dev_get_drvdata(dev); - int *attrib; - - attrib = psc_i2s_get_stat_attr(psc_dma, 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_dma *psc_dma = dev_get_drvdata(dev); - int *attrib; - - attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); - if (!attrib) - return 0; - - *attrib = simple_strtoul(buf, NULL, 0); - return count; -} - -static DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL); -static DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, - psc_i2s_stat_store); -static 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 @@ -237,82 +167,18 @@ static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, static int __devinit psc_i2s_of_probe(struct of_device *op, const struct of_device_id *match) { - phys_addr_t fifo; + int rc; struct psc_dma *psc_dma; - 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_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); - if (!psc_dma) { - iounmap(regs); - return -ENOMEM; - } - spin_lock_init(&psc_dma->lock); - psc_dma->irq = irq; - psc_dma->psc_regs = regs; - psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; - psc_dma->dev = &op->dev; - psc_dma->playback.psc_dma = psc_dma; - psc_dma->capture.psc_dma = psc_dma; - snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_id+1); - - /* Fill out the CPU DAI structure */ - memcpy(&psc_dma->dai, &psc_i2s_dai_template, sizeof psc_dma->dai); - psc_dma->dai.private_data = psc_dma; - psc_dma->dai.name = psc_dma->name; - psc_dma->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_dma->capture.bcom_task = - bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); - psc_dma->playback.bcom_task = - bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); - if (!psc_dma->capture.bcom_task || - !psc_dma->playback.bcom_task) { - dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); - iounmap(regs); - kfree(psc_dma); - return -ENODEV; - } + rc = mpc5200_audio_dma_create(op, &psc_i2s_dai_template, 1); + if (rc != 0) + return rc;
- /* 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 transmitter */ - out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset receiver */ - out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ - out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ + psc_dma = dev_get_drvdata(&op->dev);
/* Configure the serial interface mode; defaulting to CODEC8 mode */ psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL; - if (of_get_property(op->node, "fsl,cellslave", NULL)) - psc_dma->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | - MPC52xx_PSC_SICR_GENCLK; out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
@@ -321,61 +187,17 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, if (!of_get_property(op->node, "codec-handle", NULL)) return 0;
- /* Set up mode register; - * First write: RxRdy (FIFO Alarm) generates rx FIFO irq - * Second write: register Normal mode for non loopback - */ - out_8(&psc_dma->psc_regs->mode, 0); - out_8(&psc_dma->psc_regs->mode, 0); - - /* Set the TX and RX fifo alarm thresholds */ - out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); - out_8(&psc_dma->fifo_regs->rfcntl, 0x4); - out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); - out_8(&psc_dma->fifo_regs->tfcntl, 0x7); - - /* Lookup the IRQ numbers */ - psc_dma->playback.irq = - bcom_get_task_irq(psc_dma->playback.bcom_task); - psc_dma->capture.irq = - bcom_get_task_irq(psc_dma->capture.bcom_task); - - /* Save what we've done so it can be found again later */ - dev_set_drvdata(&op->dev, psc_dma); - - /* Register the SYSFS files */ - rc = device_create_file(psc_dma->dev, &dev_attr_status); - rc |= device_create_file(psc_dma->dev, &dev_attr_capture_overrun); - rc |= device_create_file(psc_dma->dev, &dev_attr_playback_underrun); - if (rc) - dev_info(psc_dma->dev, "error creating sysfs files\n"); - - snd_soc_register_platform(&psc_dma_pcm_soc_platform); - - /* Tell the ASoC OF helpers about it */ - of_snd_soc_register_platform(&mpc5200_audio_dma_platform); - of_snd_soc_register_cpu_dai(op->node, &psc_dma->dai, 1); + /* Go */ + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_TX_ENABLE); + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RX_ENABLE);
return 0; + }
static int __devexit psc_i2s_of_remove(struct of_device *op) { - struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); - - dev_dbg(&op->dev, "psc_i2s_remove()\n"); - - snd_soc_unregister_platform(&psc_dma_pcm_soc_platform); - - bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); - bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); - - iounmap(psc_dma->psc_regs); - iounmap(psc_dma->fifo_regs); - kfree(psc_dma); - dev_set_drvdata(&op->dev, NULL); - - return 0; + return mpc5200_audio_dma_destroy(op); }
/* Match table for of_platform binding */
On Wed, May 13, 2009 at 09:59:18PM -0400, Jon Smirl wrote:
-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,
- .formats = SNDRV_PCM_FMTBIT_S32_BE,
This seems like a major and very surprising reduction in functionality. Why is this required?
- if (!psc_dma->playback.active &&
!psc_dma->capture.active) {
/* Setup the IRQs */
- }
Should this be implemented or removed?
- 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, psc_dma->imr);
//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 */
- }
The commented code should go.
On Thu, May 14, 2009 at 6:51 AM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Wed, May 13, 2009 at 09:59:18PM -0400, Jon Smirl wrote:
-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,
- .formats = SNDRV_PCM_FMTBIT_S32_BE,
This seems like a major and very surprising reduction in functionality. Why is this required?
I think I did this backwards. The AC97 DAI should be SNDRV_PCM_FMTBIT_S32_BE only and the DMA engine allow all four.
On Thu, May 14, 2009 at 10:50:42AM -0400, Jon Smirl wrote:
On Thu, May 14, 2009 at 6:51 AM, Mark Brown
This seems like a major and very surprising reduction in functionality. Why is this required?
I think I did this backwards. The AC97 DAI should be SNDRV_PCM_FMTBIT_S32_BE only and the DMA engine allow all four.
Yup, that'll work.
AC97 codec for STAC9766 used on the Efika. Support registration from device trees. Datasheet: http://www.idt.com/products/getDoc.cfm?docID=13134007
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/stac9766.c | 491 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/stac9766.h | 21 ++ 4 files changed, 518 insertions(+), 0 deletions(-) create mode 100644 sound/soc/codecs/stac9766.c create mode 100644 sound/soc/codecs/stac9766.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1c19ad5..d86b0de 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -19,6 +19,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4270 if I2C select SND_SOC_PCM3008 select SND_SOC_SSM2602 if I2C + select SND_SOC_STAC9766 select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC3X if I2C @@ -92,6 +93,9 @@ config SND_SOC_PCM3008 config SND_SOC_SSM2602 tristate
+config SND_SOC_STAC9766 + tristate + config SND_SOC_TLV320AIC23 tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3d31b6b..c6998e4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -7,6 +7,7 @@ snd-soc-cs4270-objs := cs4270.o snd-soc-l3-objs := l3.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-ssm2602-objs := ssm2602.o +snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o @@ -41,6 +42,7 @@ obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c new file mode 100644 index 0000000..9f9dfa8 --- /dev/null +++ b/sound/soc/codecs/stac9766.c @@ -0,0 +1,491 @@ +/* + * stac9766.c -- ALSA SoC STAC9766 codec support + * + * Copyright 2009 Jon Smirl, Digispeaker + * Author: Jon Smirl jonsmirl@gmail.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Features:- + * + * o Support for AC97 Codec, S/PDIF + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/soc-of-simple.h> + +#include "stac9766.h" + +#define STAC9766_VERSION "0.10" + +/* + * STAC9766 register cache + */ +static const u16 stac9766_reg[] = { + 0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */ + 0x0000, 0x0000, 0x8008, 0x8008, /* e */ + 0x8808, 0x8808, 0x8808, 0x8808, /* 16 */ + 0x8808, 0x0000, 0x8000, 0x0000, /* 1e */ + 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */ + 0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */ + 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */ + 0x0000, 0x2000, 0x0000, 0x0100, /* 3e */ + 0x0000, 0x0000, 0x0080, 0x0000, /* 46 */ + 0x0000, 0x0000, 0x0003, 0xffff, /* 4e */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 56 */ + 0x4000, 0x0000, 0x0000, 0x0000, /* 5e */ + 0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ + 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 7e */ +}; + +static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", "Line", "Stereo Mix", "Mono Mix", "Phone"}; +static const char *stac9766_mono_mux[] = {"Mix", "Mic"}; +static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"}; +static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"}; +static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"}; +static const char *stac9766_record_all_mux[] = {"All analog", "Analog plus DAC"}; +static const char *stac9766_boost1[] = {"0dB", "10dB"}; +static const char *stac9766_boost2[] = {"0dB", "20dB"}; +static const char *stac9766_stereo_mic[] = {"Off", "On"}; + +static const struct soc_enum stac9766_record_enum = + SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux); +static const struct soc_enum stac9766_mono_enum = + SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux); +static const struct soc_enum stac9766_mic_enum = + SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux); +static const struct soc_enum stac9766_SPDIF_enum = + SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux); +static const struct soc_enum stac9766_popbypass_enum = + SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux); +static const struct soc_enum stac9766_record_all_enum = + SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2, stac9766_record_all_mux); +static const struct soc_enum stac9766_boost1_enum = + SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */ +static const struct soc_enum stac9766_boost2_enum = + SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */ +static const struct soc_enum stac9766_stereo_mic_enum = + SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic); + +static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0); +static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250); +static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0); +static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200); + +static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { + SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv), + SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1), + SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1, master_tlv), + SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1), + SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1, master_tlv), + SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1), + + SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv), + SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1), + + + SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv), + SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1), + SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1), + SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv), + SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1), + + SOC_ENUM("Mic Boost1", stac9766_boost1_enum), + SOC_ENUM("Mic Boost2", stac9766_boost2_enum), + SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv), + SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1), + SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum), + + SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1), + SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1), + SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1), + SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1), + + SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1), + SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0), + SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1), + SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), + + SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum), + SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum), + SOC_ENUM("Record All Mux", stac9766_record_all_enum), + SOC_ENUM("Record Mux", stac9766_record_enum), + SOC_ENUM("Mono Mux", stac9766_mono_enum), + SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum), +}; + +int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + u16 *cache = codec->reg_cache; + + if (reg > AC97_STAC_PAGE0) { + stac9766_ac97_write(codec, AC97_INT_PAGING, 0); + soc_ac97_ops.write(codec->ac97, reg, val); + stac9766_ac97_write(codec, AC97_INT_PAGING, 1); + return 0; + } + if (reg / 2 > ARRAY_SIZE(stac9766_reg)) + return -EIO; + + soc_ac97_ops.write(codec->ac97, reg, val); + cache[reg / 2] = val; + return 0; +} + +unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, unsigned int reg) +{ + u16 val = 0, *cache = codec->reg_cache; + + if (reg > AC97_STAC_PAGE0) { + stac9766_ac97_write(codec, AC97_INT_PAGING, 0); + val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0); + stac9766_ac97_write(codec, AC97_INT_PAGING, 1); + return val; + } + if (reg / 2 > ARRAY_SIZE(stac9766_reg)) + return -EIO; + + if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || + reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 || + reg == AC97_VENDOR_ID2) { + + val = soc_ac97_ops.read(codec->ac97, reg); + return val; + } + return cache[reg / 2]; +} + +static int ac97_analog_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned short reg, vra; + + vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); + + vra |= 0x1; /* enable variable rate audio */ + + stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return stac9766_ac97_write(codec, reg, runtime->rate); +} + +static int ac97_digital_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned short reg, vra; + + stac9766_ac97_write(codec, AC97_SPDIF, 0x2002); + + vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); + + vra |= 0x5; /* Enable VRA and SPDIF out */ + printk("var is %x\n", vra); + + stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); + + reg = AC97_PCM_FRONT_DAC_RATE; + + return stac9766_ac97_write(codec, reg, runtime->rate); +} + +static int ac97_digital_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned short vra; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); + vra &= !0x04; + //stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); + break; + } + return 0; +} + +static int stac9766_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: /* full On */ + stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_PREPARE: /* partial On */ + stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_STANDBY: /* Off, with power */ + stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_OFF: /* Off, without power */ + /* disable everything including AC link */ + stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); + break; + } + codec->bias_level = level; + return 0; +} + +static int stac9766_codec_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int stac9766_codec_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + u16 id; + + /* give the codec an AC97 warm reset to start the link */ + codec->ac97->bus->ops->warm_reset(codec->ac97); + id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2); + if (id != 0x4c13) { + printk(KERN_ERR "stac9766 failed to resume"); + return -EIO; + } + stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) + stac9766_set_bias_level(codec, SND_SOC_BIAS_ON); + + return 0; +} + +static struct snd_soc_dai_ops stac9766_dai_ops_analog = +{ + .prepare = ac97_analog_prepare, +}; + +static struct snd_soc_dai_ops stac9766_dai_ops_digital = +{ + .prepare = ac97_digital_prepare, + .trigger = ac97_digital_trigger, +}; + +struct snd_soc_dai stac9766_dai[] = { +{ + .name = "stac9766 analog", + .id = 0, + .ac97_control = 1, + + /* stream cababilities */ + .playback = { + .stream_name = "stac9766 analog", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, + }, + .capture = { + .stream_name = "stac9766 analog", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, + }, + /* alsa ops */ + .ops = &stac9766_dai_ops_analog, +}, +{ + .name = "stac9766 IEC958", + .id = 1, + .ac97_control = 1, + + /* stream cababilities */ + .playback = { + .stream_name = "stac9766 IEC958", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, + }, + /* alsa ops */ + .ops = &stac9766_dai_ops_digital, +}}; +EXPORT_SYMBOL_GPL(stac9766_dai); + +int stac9766_reset(struct snd_soc_codec *codec, int try_warm) +{ + if (try_warm && soc_ac97_ops.warm_reset) { + soc_ac97_ops.warm_reset(codec->ac97); + if (stac9766_ac97_read(codec, 0) == stac9766_reg[0]) + return 1; + } + + soc_ac97_ops.reset(codec->ac97); + if (soc_ac97_ops.warm_reset) + soc_ac97_ops.warm_reset(codec->ac97); + if (stac9766_ac97_read(codec, 0) != stac9766_reg[0]) + return -EIO; + return 0; +} + +static int stac9766_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION); + + socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (socdev->card->codec == NULL) + return -ENOMEM; + codec = socdev->card->codec; + mutex_init(&codec->mutex); + + codec->reg_cache = kmemdup(stac9766_reg, sizeof(stac9766_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto cache_err; + } + codec->reg_cache_size = sizeof(stac9766_reg); + codec->reg_cache_step = 2; + + codec->name = "STAC9766"; + codec->owner = THIS_MODULE; + codec->dai = stac9766_dai; + codec->num_dai = ARRAY_SIZE(stac9766_dai); + codec->write = stac9766_ac97_write; + codec->read = stac9766_ac97_read; + codec->set_bias_level = stac9766_set_bias_level; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); + if (ret < 0) + goto codec_err; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) + goto pcm_err; + + /* do a cold reset for the controller and then try + * a warm reset followed by an optional cold reset for codec */ + stac9766_reset(codec, 0); + ret = stac9766_reset(codec, 1); + if (ret < 0) { + printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n"); + goto reset_err; + } + + stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + snd_soc_add_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE( + stac9766_snd_ac97_controls)); + + ret = snd_soc_init_card(socdev); + if (ret < 0) + goto reset_err; + return 0; + +reset_err: + snd_soc_free_pcms(socdev); +pcm_err: + snd_soc_free_ac97_codec(codec); +codec_err: + kfree(codec->private_data); +cache_err: + kfree(socdev->card->codec); + socdev->card->codec = NULL; + return ret; +} + +static int stac9766_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (codec == NULL) + return 0; + + snd_soc_free_pcms(socdev); + snd_soc_free_ac97_codec(codec); + kfree(codec->reg_cache); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_stac9766 = +{ + .probe = stac9766_codec_probe, + .remove = stac9766_codec_remove, + .suspend = stac9766_codec_suspend, + .resume = stac9766_codec_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_stac9766); + +static int __init stac9766_probe(struct platform_device *pdev) +{ + snd_soc_register_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai)); +#if defined(CONFIG_SND_SOC_OF_SIMPLE) + /* Tell the of_soc helper about this codec */ + of_snd_soc_register_codec(&soc_codec_dev_stac9766, pdev->dev.archdata.of_node, + stac9766_dai, ARRAY_SIZE(stac9766_dai), + pdev->dev.archdata.of_node); +#endif + return 0; +} + +static struct platform_driver stac9766_driver = +{ + .probe = stac9766_probe, + .driver = { + .name = "stac9766", + }, +}; + +static __init int stac9766_driver_init(void) +{ + return platform_driver_register(&stac9766_driver); +} + +static __exit void stac9766_driver_exit(void) +{ +} + +module_init(stac9766_driver_init); +module_exit(stac9766_driver_exit); + +MODULE_DESCRIPTION("ASoC stac9766 driver"); +MODULE_AUTHOR("Jon Smirl jonsmirl@gmail.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h new file mode 100644 index 0000000..65642eb --- /dev/null +++ b/sound/soc/codecs/stac9766.h @@ -0,0 +1,21 @@ +/* + * stac9766.h -- STAC9766 Soc Audio driver + */ + +#ifndef _STAC9766_H +#define _STAC9766_H + +#define AC97_STAC_PAGE0 0x1000 +#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A) +#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E) +#define AC97_STAC_STEREO_MIC 0x78 + +/* STAC9766 DAI ID's */ +#define STAC9766_DAI_AC97_ANALOG 0 +#define STAC9766_DAI_AC97_DIGITAL 1 + +extern struct snd_soc_dai stac9766_dai[]; +extern struct snd_soc_codec_device soc_codec_dev_stac9766; + + +#endif
On Wed, May 13, 2009 at 09:59:20PM -0400, Jon Smirl wrote:
This looks mostly good - a lot of the issues below are fairly nitpicky, though the issue with the of_simple integration needs to be dealt with.
select SND_SOC_SSM2602 if I2C
- select SND_SOC_STAC9766 select SND_SOC_TLV320AIC23 if I2C
This needs to be '... if SND_SOC_AC97_BUS' as for all the other AC97 CODECs.
+{
- u16 *cache = codec->reg_cache;
- if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
soc_ac97_ops.write(codec->ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
This could use a comment explaining what's going on with the paging - I'd expect page 0 to be the default page.
+static int ac97_digital_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned short reg, vra;
- stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
- vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
- vra |= 0x5; /* Enable VRA and SPDIF out */
- printk("var is %x\n", vra);
This looks like debug code that can be removed or turned into a pr_debug()?
+static int ac97_digital_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
Another indentation problem?
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_STOP:
vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
vra &= !0x04;
//stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
break;
Debug code that could be cleaned up?
+static int stac9766_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
+{
- switch (level) {
- case SND_SOC_BIAS_ON: /* full On */
stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SND_SOC_BIAS_PREPARE: /* partial On */
stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
You could skip the writes here - they're just doing the same thing as standby.
+static int stac9766_codec_resume(struct platform_device *pdev) +{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
- u16 id;
- /* give the codec an AC97 warm reset to start the link */
- codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
- if (id != 0x4c13) {
printk(KERN_ERR "stac9766 failed to resume");
return -EIO;
- }
Shouldn't this use the reset function?
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
Should use the standard define for AC97 formats here, and for the other DAIs.
- snd_soc_add_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE(
stac9766_snd_ac97_controls));
Odd place to wrap...
+static int __init stac9766_probe(struct platform_device *pdev) +{
- snd_soc_register_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
The same issues as apply to the WM9712 change apply here.
AC97 driver for mpc5200
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- sound/soc/fsl/Kconfig | 11 + sound/soc/fsl/Makefile | 1 sound/soc/fsl/mpc5200_psc_ac97.c | 379 ++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/mpc5200_psc_ac97.h | 13 + 4 files changed, 404 insertions(+), 0 deletions(-) create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.c create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8060b95..d8f63f5 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -30,3 +30,14 @@ config SND_SOC_MPC5200_I2S select PPC_BESTCOMM_GEN_BD help Say Y here to support the MPC5200 PSCs in I2S mode. + +config SND_SOC_MPC5200_AC97 + tristate "Freescale MPC5200 PSC in AC97 mode driver" + depends on PPC_MPC52xx && PPC_BESTCOMM + select SND_SOC_OF_SIMPLE + select SND_MPC52xx_DMA + select PPC_BESTCOMM_GEN_BD + help + Say Y here to support the MPC5200 PSCs in AC97 mode. + + diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 7731ef2..14631a1 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -13,4 +13,5 @@ obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o +obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c new file mode 100644 index 0000000..aa37c8d --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -0,0 +1,379 @@ +/* + * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip. + * + * Copyright 2008 Jon Smirl, Digispeaker + * Author: Jon Smirl jonsmirl@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#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> +#include <asm/time.h> +#include <asm/mpc52xx.h> +#include <asm/mpc52xx_psc.h> + +#include "mpc5200_dma.h" +#include "mpc5200_psc_ac97.h" + +MODULE_AUTHOR("Jon Smirl jonsmirl@gmail.com"); +MODULE_DESCRIPTION("mpc5200 AC97 module"); +MODULE_LICENSE("GPL"); + +#define DRV_NAME "mpc5200-psc-ac97" + +static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{ + struct psc_dma *psc_dma = ac97->private_data; + int timeout; + unsigned int val; + + spin_lock(&psc_dma->lock); + + /* Wait for it to be ready */ + timeout = 1000; + while ((--timeout) && (in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND) ) + udelay(10); + + if (!timeout) { + pr_err(timeout on ac97 bus (rdy)\n"); + return 0xffff; + } + + /* Do the read */ + out_be32(&psc_dma->psc_regs->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24)); + + /* Wait for the answer */ + timeout = 1000; + while ((--timeout) && !(in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_DATA_VAL) ) + udelay(10); + + if (!timeout) { + pr_err(timeout on ac97 read (val) %x\n", in_be16(&psc_dma->psc_regs->sr_csr.status)); + return 0xffff; + } + + /* Get the data */ + val = in_be32(&psc_dma->psc_regs->ac97_data); + if ( ((val>>24) & 0x7f) != reg ) { + pr_err(reg echo error on ac97 read\n"); + return 0xffff; + } + val = (val >> 8) & 0xffff; + + spin_unlock(&psc_dma->lock); + return (unsigned short) val; +} + +static void psc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) +{ + struct psc_dma *psc_dma = ac97->private_data; + int timeout; + + spin_lock(&psc_dma->lock); + + /* Wait for it to be ready */ + timeout = 1000; + while ((--timeout) && (in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND) ) + udelay(10); + + if (!timeout) { + pr_err(timeout on ac97 write\n"); + return; + } + + /* Write data */ + out_be32(&psc_dma->psc_regs->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8)); + + spin_unlock(&psc_dma->lock); +} + +static void psc_ac97_cold_reset(struct snd_ac97 *ac97) +{ + struct psc_dma *psc_dma = ac97->private_data; + + /* Do a cold reset */ + out_8(&psc_dma->psc_regs->op1, MPC52xx_PSC_OP_RES); + udelay(10); + out_8(&psc_dma->psc_regs->op0, MPC52xx_PSC_OP_RES); + udelay(50); + + /* PSC recover from cold reset (cfr user manual, not sure if useful) */ + out_be32(&psc_dma->psc_regs->sicr, in_be32(&psc_dma->psc_regs->sicr)); +} + +static void psc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + pr_info("psc_ac97_warm_reset\n"); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = psc_ac97_read, + .write = psc_ac97_write, + .reset = psc_ac97_cold_reset, + .warm_reset = psc_ac97_warm_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +#ifdef CONFIG_PM +static int psc_ac97_suspend(struct snd_soc_dai *dai) +{ + return 0; +} + +static int psc_ac97_resume(struct snd_soc_dai *dai) +{ + return 0; +} + +#else +#define psc_ac97_suspend NULL +#define psc_ac97_resume NULL +#endif + +static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + 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, "%s(substream=%p) p_size=%i p_bytes=%i" + " periods=%i buffer_size=%i buffer_bytes=%i channels=%i" + " rate=%i format=%i\n", + __func__, substream, params_period_size(params), + params_period_bytes(params), params_periods(params), + params_buffer_size(params), params_buffer_bytes(params), + params_channels(params), params_rate(params), params_format(params)); + + + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (params_channels(params) == 1) + psc_dma->slots |= 0x00000100; + else + psc_dma->slots |= 0x00000300; + } else { + if (params_channels(params) == 1) + psc_dma->slots |= 0x01000000; + else + psc_dma->slots |= 0x03000000; + } + + spin_lock(&psc_dma->lock); + out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); + spin_unlock(&psc_dma->lock); + + return 0; +} + +static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + 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; + + spin_lock(&psc_dma->lock); + if (params_channels(params) == 1) + out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000); + else + out_be32(&psc_dma->psc_regs->ac97_slots, 0x03000000); + spin_unlock(&psc_dma->lock); + + return 0; +} + +static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + 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; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + psc_dma->slots &= 0xFFFF0000; + else + psc_dma->slots &= 0x0000FFFF; + + spin_lock(&psc_dma->lock); + out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); + spin_unlock(&psc_dma->lock); + break; + } + return 0; +} + +/** + * psc_ac97_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 AC97 mode. Return an error if the format is + * not SND_SOC_DAIFMT_AC97. + * + * @format: one of SND_SOC_DAIFMT_xxx + */ +static int psc_ac97_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) +{ + struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "psc_ac97_set_fmt(cpu_dai=%p, format=%i)\n", + cpu_dai, format); + + return (format == SND_SOC_DAIFMT_AC97) ? 0 : -EINVAL; +} + +/* --------------------------------------------------------------------- + * ALSA SoC Bindings + * + * - Digital Audio Interface (DAI) template + * - create/destroy dai hooks + */ + +/** + * psc_ac97_dai_template: template CPU Digital Audio Interface + */ +static struct snd_soc_dai_ops psc_ac97_analog_ops = { + .hw_params = psc_ac97_hw_analog_params, + .set_fmt = psc_ac97_set_fmt, + .trigger = psc_ac97_trigger, +}; + +static struct snd_soc_dai_ops psc_ac97_digital_ops = { + .hw_params = psc_ac97_hw_digital_params, + .set_fmt = psc_ac97_set_fmt, +}; + +static struct snd_soc_dai psc_ac97_dai_template[] = { +{ + .name = "%s analog", + .suspend = psc_ac97_suspend, + .resume = psc_ac97_resume, + .playback = { + .channels_min = 1, + .channels_max = 6, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, + }, + .ops = &psc_ac97_analog_ops, +}, +{ + .name = "%s digital", + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, + }, + .ops = &psc_ac97_digital_ops, +}}; + + +/* --------------------------------------------------------------------- + * OF platform bus binding code: + * - Probe/remove operations + * - OF device match table + */ +static int __devinit psc_ac97_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + int rc; + struct psc_dma *psc_dma; + + rc = mpc5200_audio_dma_create(op, psc_ac97_dai_template, ARRAY_SIZE(psc_ac97_dai_template)); + if (rc != 0) + return rc; + + psc_dma = dev_get_drvdata(&op->dev); + + psc_dma->imr = 0; + out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); + + out_8(&psc_dma->psc_regs->op1, MPC52xx_PSC_OP_RES); + udelay(10); + out_8(&psc_dma->psc_regs->op0, MPC52xx_PSC_OP_RES); + udelay(50); + + /* Configure the serial interface mode to AC97 */ + psc_dma->sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97; + out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr); + + /* No slots active */ + out_be32(&psc_dma->psc_regs->ac97_slots, 0x00000000); + + /* Go */ + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_TX_ENABLE); + out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RX_ENABLE); + + return 0; +} + +static int __devexit psc_ac97_of_remove(struct of_device *op) +{ + return mpc5200_audio_dma_destroy(op); +} + +/* Match table for of_platform binding */ +static struct of_device_id psc_ac97_match[] __devinitdata = { + { .compatible = "fsl,mpc5200-psc-ac97", }, + { .compatible = "fsl,mpc5200b-psc-ac97", }, + {} +}; +MODULE_DEVICE_TABLE(of, psc_ac97_match); + +static struct of_platform_driver psc_ac97_driver = { + .match_table = psc_ac97_match, + .probe = psc_ac97_of_probe, + .remove = __devexit_p(psc_ac97_of_remove), + .driver = { + .name = "mpc5200-psc-ac97", + .owner = THIS_MODULE, + }, +}; + +/* --------------------------------------------------------------------- + * Module setup and teardown; simply register the of_platform driver + * for the PSC in AC97 mode. + */ +static int __init psc_ac97_init(void) +{ + return of_register_platform_driver(&psc_ac97_driver); +} +module_init(psc_ac97_init); + +static void __exit psc_ac97_exit(void) +{ + of_unregister_platform_driver(&psc_ac97_driver); +} +module_exit(psc_ac97_exit); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h new file mode 100644 index 0000000..d028c55 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_ac97.h @@ -0,0 +1,13 @@ +/* + * Freescale MPC5200 PSC in AC97 mode + * ALSA SoC Digital Audio Interface (DAI) driver + * + */ + +#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ +#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ + +#define MPC5200_AC97_ANALOG 0 +#define MPC5200_AC97_DIGITAL 1 + +#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
On Wed, May 13, 2009 at 9:59 PM, Jon Smirl jonsmirl@gmail.com wrote:
AC97 driver for mpc5200
I made a mistake removing the debug info from this one. Add a quote on the pr_err() calls.
On Wed, May 13, 2009 at 09:59:22PM -0400, Jon Smirl wrote:
Looks mostly good but there's a few things that it'd be nice to address:
+MODULE_AUTHOR("Jon Smirl jonsmirl@gmail.com"); +MODULE_DESCRIPTION("mpc5200 AC97 module"); +MODULE_LICENSE("GPL");
Normally this comes at the bottom of the file.
+#define DRV_NAME "mpc5200-psc-ac97"
+static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{
- struct psc_dma *psc_dma = ac97->private_data;
- int timeout;
- unsigned int val;
- spin_lock(&psc_dma->lock);
Does this need to protect against interrupts?
+static void psc_ac97_warm_reset(struct snd_ac97 *ac97) +{
- pr_info("psc_ac97_warm_reset\n");
+}
Remove this - it's only going to confuse things to provide it if you can't implement it, anything that actually needs the warm reset is going to get upset anyway.
+#ifdef CONFIG_PM +static int psc_ac97_suspend(struct snd_soc_dai *dai) +{
- return 0;
+}
+static int psc_ac97_resume(struct snd_soc_dai *dai) +{
- return 0;
+}
All the suspend and resume stuff can be removed if there's no implementation.
+static int psc_ac97_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) +{
- struct psc_dma *psc_dma = cpu_dai->private_data;
- dev_dbg(psc_dma->dev, "psc_ac97_set_fmt(cpu_dai=%p, format=%i)\n",
cpu_dai, format);
- return (format == SND_SOC_DAIFMT_AC97) ? 0 : -EINVAL;
+}
Remove this, there's no need to provide a set_fmt operation for AC97 DAIs.
Fabric bindings for STAC9766 on the Efika. This includes an example of a custom fabric driver.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- arch/powerpc/platforms/52xx/efika.c | 8 +++ sound/soc/fsl/Kconfig | 8 +++ sound/soc/fsl/Makefile | 3 + sound/soc/fsl/efika-audio-fabric.c | 95 +++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletions(-) create mode 100644 sound/soc/fsl/efika-audio-fabric.c
diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index a2068fa..c7a6a48 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -211,12 +211,18 @@ static int __init efika_probe(void) return 1; }
+static void __init efika_declare_platform_devices(void) +{ + mpc52xx_declare_of_platform_devices(); + platform_device_register_simple("efika-audio-fabric", 0, NULL, 0); +} + define_machine(efika) { .name = EFIKA_PLATFORM_NAME, .probe = efika_probe, .setup_arch = efika_setup_arch, - .init = mpc52xx_declare_of_platform_devices, + .init = efika_declare_platform_devices, .show_cpuinfo = efika_show_cpuinfo, .init_IRQ = mpc52xx_init_irq, .get_irq = mpc52xx_get_irq, diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index d8f63f5..266c6b7 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -40,4 +40,12 @@ config SND_SOC_MPC5200_AC97 help Say Y here to support the MPC5200 PSCs in AC97 mode.
+config SND_MPC52xx_SOC_EFIKA + tristate "SoC AC97 Audio support for bbplan Efika and STAC9766" + depends on PPC_EFIKA + select SND_SOC_MPC5200_AC97 + select SND_SOC_STAC9766 + help + Say Y if you want to add support for sound on the Efika. +
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 14631a1..f406470 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -15,3 +15,6 @@ obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
+# MPC5200 Machine Support +obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o + diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c new file mode 100644 index 0000000..56ae471 --- /dev/null +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -0,0 +1,95 @@ +/* + * Efika driver for the PSC of the Freescale MPC52xx configured as AC97 interface + * + * Copyright 2008 Jon Smirl, Digispeaker + * Author: Jon Smirl jonsmirl@gmail.com + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#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 "mpc5200_dma.h" +#include "mpc5200_psc_ac97.h" +#include "../codecs/stac9766.h" + +static int efika_init(struct snd_soc_codec *codec) +{ + /* Skeleton driver showing framework for setting + * up board specific fabric drivers. + * + * set up Efika specific controls here + * + * Loading of this driver is trigger by + * platform_device_register_simple("efika-audio-fabric", 0, NULL, 0); + * in arch/powerpc/platforms/52xx/efika.c + */ + return 0; +} + +static int efika_stac9766_probe(struct platform_device *pdev) +{ + of_snd_soc_register_fabric("Efika", NULL, efika_init); + return 0; +} + +#ifdef CONFIG_PM + +static int efika_stac9766_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int efika_stac9766_resume(struct platform_device *pdev) +{ + return 0; +} + +#else +#define efika_stac9766_suspend NULL +#define efika_stac9766_resume NULL +#endif + +static struct platform_driver efika_fabric = { + .probe = efika_stac9766_probe, + .suspend = efika_stac9766_suspend, + .resume = efika_stac9766_resume, + .driver = { + .name = "efika-audio-fabric", + }, +}; + +static __init int efika_fabric_init(void) +{ + return platform_driver_register(&efika_fabric); +} + +static __exit void efika_fabric_exit(void) +{ +} + +module_init(efika_fabric_init); +module_exit(efika_fabric_exit); + + +MODULE_AUTHOR("Jon Smirl jonsmirl@gmail.com"); +MODULE_DESCRIPTION(DRV_NAME ": mpc5200 Efika fabric driver"); +MODULE_LICENSE("GPL"); +
Support for AC97 on Phytec pmc030 base board. This uses the generic fabric driver binding.
Signed-off-by: Jon Smirl jonsmirl@gmail.com --- arch/powerpc/boot/dts/pcm030.dts | 8 ++++++++ sound/soc/fsl/Kconfig | 7 +++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts index 8958347..001c8ef 100644 --- a/arch/powerpc/boot/dts/pcm030.dts +++ b/arch/powerpc/boot/dts/pcm030.dts @@ -190,6 +190,14 @@ cell-index = <0>; reg = <0x2000 0x100>; interrupts = <2 1 0>; + + #address-cells = <1>; + #size-cells = <0>; + + codec@0 { + compatible = "wlf,wm9712"; + reg = <0x0>; + }; };
/* PSC2 port is used by CAN1/2 */ diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 266c6b7..11735e4 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -48,4 +48,11 @@ config SND_MPC52xx_SOC_EFIKA help Say Y if you want to add support for sound on the Efika.
+config SND_MPC52xx_SOC_PCM030 + tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712" + depends on PPC_MPC5200_SIMPLE + select SND_SOC_MPC5200_AC97 + select SND_SOC_WM9712 + help + Say Y if you want to add support for sound on the Phytec pcm030 baseboard.
On Wed, May 13, 2009 at 9:58 PM, Jon Smirl jonsmirl@gmail.com wrote:
The following series implements audio support for the mpc5200. It adds an AC97 driver and STAC9766 codec driver. Board support for the Efika and Phytec pcm030 are also included. I'm sure everyone will have some edits on this first version.
Series is based on 13e2c86c20f5d07b20d5a6dee24bcdd2b30b859f from broonie/for-2.6.31 git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
I pushed the tree out to http://git.digispeaker.com
On Wed, May 13, 2009 at 7:58 PM, Jon Smirl jonsmirl@gmail.com wrote:
The following series implements audio support for the mpc5200. It adds an AC97 driver and STAC9766 codec driver. Board support for the Efika and Phytec pcm030 are also included. I'm sure everyone will have some edits on this first version.
BTW, please also cc: the linuxppc-dev mailing list for the next issue of this series.
Thanks, g.
Jon Smirl (13): Support for AC97 on Phytec pmc030 base board. Fabric bindings for STAC9766 on the Efika AC97 driver for mpc5200 Codec for STAC9766 used on the Efika Main rewite of the mpc5200 audio DMA code Have the WM9712 driver self register itself Add SNDRV_PCM_FMTBIT_S32_BE as a valid AC97 format Allow a custom ASOC fabric driver with soc-of-simple Add a few more mpc5200 PSC defines redo 2008f137e92220b98120c4803499cdddb2b0fb06 Rename the PSC functions to DMA Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Temporarily undo 2008f137e92220b98120c4803499cdddb2b0fb06
arch/powerpc/boot/dts/pcm030.dts | 8 arch/powerpc/include/asm/mpc52xx_psc.h | 10 arch/powerpc/platforms/52xx/efika.c | 8 include/sound/soc-dai.h | 3 include/sound/soc-of-simple.h | 16 + sound/last.c | 6 sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/stac9766.c | 491 ++++++++++++++++++++++ sound/soc/codecs/stac9766.h | 21 + sound/soc/codecs/tlv320aic26.c | 2 sound/soc/codecs/wm9712.c | 34 ++ sound/soc/fsl/Kconfig | 30 + sound/soc/fsl/Makefile | 6 sound/soc/fsl/efika-audio-fabric.c | 95 ++++ sound/soc/fsl/mpc5200_dma.c | 693 +++++++++++++++++++++++++++++++ sound/soc/fsl/mpc5200_dma.h | 83 ++++ sound/soc/fsl/mpc5200_psc_ac97.c | 379 +++++++++++++++++ sound/soc/fsl/mpc5200_psc_ac97.h | 13 + sound/soc/fsl/mpc5200_psc_i2s.c | 707 +------------------------------- sound/soc/fsl/soc-of-simple.c | 160 ++++++- 21 files changed, 2053 insertions(+), 718 deletions(-) create mode 100644 sound/soc/codecs/stac9766.c create mode 100644 sound/soc/codecs/stac9766.h create mode 100644 sound/soc/fsl/efika-audio-fabric.c create mode 100644 sound/soc/fsl/mpc5200_dma.c create mode 100644 sound/soc/fsl/mpc5200_dma.h create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.c create mode 100644 sound/soc/fsl/mpc5200_psc_ac97.h
-- Jon Smirl jonsmirl@gmail.com
participants (3)
-
Grant Likely
-
Jon Smirl
-
Mark Brown