* [PATCH V2 0/9] mpc5200 audio rework for AC97
@ 2009-05-23 23:12 Jon Smirl
2009-05-23 23:12 ` [PATCH V2 1/9] Register the wm9712 DAIs on module load Jon Smirl
` (10 more replies)
0 siblings, 11 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:12 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
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.
Mark is not enthused about soc-of-simple.c so rather than extend it for AC97 I altered the drivers to not use it. Instead they use the old way of manually binding everything. Mark would like to see OF binding more closely integrated to the core. Once a proper solution for OF binding is agreed upon it is easy to convert the existing drivers. A first step would be converting the existing codec drivers so that they can be dynamically loaded.
Grant, please check over the spin locking on register access. I'm not clear on why and when the registers have to be protected.
Once these basic drivers are in-kernel and more people are testing them, I can add more features like pause/resume, power management, etc based on feedback.
I2S will get examined in more detail for the next kernel cycle. Our multi-channel prototype I2S hardware is just about working. Once I get the multi-channel hardware I will implement and heavily test a lot more I2S capability.
---
Jon Smirl (9):
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
Add a few more mpc5200 PSC defines
Rename the PSC functions to DMA
Basic split of mpc5200 DMA code out from mpc5200_psc_i2s
Register the wm9712 DAIs on module load
sound/soc/fsl/Kconfig | 31 +
sound/soc/fsl/Makefile | 7
sound/soc/fsl/efika-audio-fabric.c | 94 ++++
sound/soc/fsl/mpc5200_dma.c | 635 ++++++++++++++++++++++++++++++
sound/soc/fsl/mpc5200_dma.h | 80 ++++
sound/soc/fsl/mpc5200_psc_ac97.c | 394 ++++++++++++++++++
sound/soc/fsl/mpc5200_psc_ac97.h | 15 +
sound/soc/fsl/mpc5200_psc_i2s.c | 750 ++---------------------------------
sound/soc/fsl/mpc5200_psc_i2s.h | 12 +
sound/soc/fsl/pcm030-audio-fabric.c | 94 ++++
10 files changed, 1414 insertions(+), 698 deletions(-)
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
create mode 100644 sound/soc/fsl/mpc5200_psc_i2s.h
create mode 100644 sound/soc/fsl/pcm030-audio-fabric.c
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH V2 1/9] Register the wm9712 DAIs on module load
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
@ 2009-05-23 23:12 ` Jon Smirl
2009-05-24 11:11 ` [alsa-devel] " Mark Brown
2009-05-23 23:12 ` [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Jon Smirl
` (9 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:12 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
Register the wm9712 DAIs on module load
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
0 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 1fd4e88..49ad987 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -742,6 +742,18 @@ struct snd_soc_codec_device soc_codec_dev_wm9712 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
+static int __init wm9712_modinit(void)
+{
+ return snd_soc_register_dais(wm9712_dai, ARRAY_SIZE(wm9712_dai));
+}
+module_init(wm9712_modinit);
+
+static void __exit wm9712_exit(void)
+{
+ snd_soc_unregister_dais(wm9712_dai, ARRAY_SIZE(wm9712_dai));
+}
+module_exit(wm9712_exit);
+
MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
2009-05-23 23:12 ` [PATCH V2 1/9] Register the wm9712 DAIs on module load Jon Smirl
@ 2009-05-23 23:12 ` Jon Smirl
2009-05-24 14:11 ` Grant Likely
2009-05-23 23:13 ` [PATCH V2 3/9] Rename the PSC functions to DMA Jon Smirl
` (8 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:12 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
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 | 458 +++++++++++++++++++++++++++++++++++++
sound/soc/fsl/mpc5200_dma.h | 81 +++++++
sound/soc/fsl/mpc5200_psc_i2s.c | 485 ---------------------------------------
5 files changed, 547 insertions(+), 483 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..4bae8d6
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -0,0 +1,458 @@
+/*
+ * 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 |
+ 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,
+ .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 1111c71..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,158 +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 |
- 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,
- .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
*/
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 3/9] Rename the PSC functions to DMA
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
2009-05-23 23:12 ` [PATCH V2 1/9] Register the wm9712 DAIs on module load Jon Smirl
2009-05-23 23:12 ` [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 11:15 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 4/9] Add a few more mpc5200 PSC defines Jon Smirl
` (7 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
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 4bae8d6..6850392 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 |
SNDRV_PCM_INFO_BATCH,
@@ -330,80 +330,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;
@@ -431,13 +431,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;
@@ -449,10 +449,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;
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 4/9] Add a few more mpc5200 PSC defines
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (2 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 3/9] Rename the PSC functions to DMA Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 14:13 ` Grant Likely
2009-05-23 23:13 ` [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code Jon Smirl
` (6 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 PSC registers. This patch is going in via Grant's tree.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
0 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/asm/mpc52xx_psc.h
index a218da6..fb84120 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
@@ -117,6 +127,7 @@
#define MPC52xx_PSC_SICR_SIM_FIR (0x6 << 24)
#define MPC52xx_PSC_SICR_SIM_CODEC_24 (0x7 << 24)
#define MPC52xx_PSC_SICR_SIM_CODEC_32 (0xf << 24)
+#define MPC52xx_PSC_SICR_AWR (1 << 30)
#define MPC52xx_PSC_SICR_GENCLK (1 << 23)
#define MPC52xx_PSC_SICR_I2S (1 << 22)
#define MPC52xx_PSC_SICR_CLKPOL (1 << 21)
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (3 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 4/9] Add a few more mpc5200 PSC defines Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 11:26 ` [alsa-devel] " Mark Brown
2009-05-24 18:55 ` Wolfram Sang
2009-05-23 23:13 ` [PATCH V2 6/9] Codec for STAC9766 used on the Efika Jon Smirl
` (5 subsequent siblings)
10 siblings, 2 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
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/Kconfig | 1
sound/soc/fsl/mpc5200_dma.c | 505 ++++++++++++++++++++++++++-------------
sound/soc/fsl/mpc5200_dma.h | 29 +-
sound/soc/fsl/mpc5200_psc_i2s.c | 243 +++----------------
sound/soc/fsl/mpc5200_psc_i2s.h | 12 +
5 files changed, 407 insertions(+), 383 deletions(-)
create mode 100644 sound/soc/fsl/mpc5200_psc_i2s.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index dc79bdf..1918c78 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -25,7 +25,6 @@ config SND_SOC_MPC8610_HPCD
config SND_SOC_MPC5200_I2S
tristate "Freescale MPC5200 PSC in I2S mode driver"
depends on PPC_MPC52xx && PPC_BESTCOMM
- select SND_SOC_OF_SIMPLE
select SND_MPC52xx_DMA
select PPC_BESTCOMM_GEN_BD
help
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index 6850392..95df860 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -3,23 +3,13 @@
* ALSA SoC Platform driver
*
* Copyright (C) 2008 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
*/
-#include <linux/init.h>
#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/dma-mapping.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include <sound/soc.h>
-#include <sound/soc-of-simple.h>
#include <sysdev/bestcomm/bestcomm.h>
#include <sysdev/bestcomm/gen_bd.h>
@@ -27,10 +17,6 @@
#include "mpc5200_dma.h"
-MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
-MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");
-MODULE_LICENSE("GPL");
-
/*
* Interrupt handlers
*/
@@ -50,7 +36,7 @@ static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma)
if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
psc_dma->stats.overrun_count++;
- out_8(®s->command, 4 << 4); /* reset the error status */
+ out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
return IRQ_HANDLED;
}
@@ -81,8 +67,21 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
s->period_next_pt = s->period_start;
}
+static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
+{
+ while (s->appl_ptr < s->runtime->control->appl_ptr) {
+
+ if (bcom_queue_full(s->bcom_task))
+ return;
+
+ s->appl_ptr += s->period_size;
+
+ psc_dma_bcom_enqueue_next_buffer(s);
+ }
+}
+
/* Bestcomm DMA irq handler */
-static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
+static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
{
struct psc_dma_stream *s = _psc_dma_stream;
@@ -90,12 +89,12 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
* and enqueue a new one in it's place. */
while (bcom_buffer_done(s->bcom_task)) {
bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
s->period_current_pt += s->period_bytes;
if (s->period_current_pt >= s->period_end)
s->period_current_pt = s->period_start;
- psc_dma_bcom_enqueue_next_buffer(s);
- bcom_enable(s->bcom_task);
}
+ psc_dma_bcom_enqueue_tx(s);
/* If the stream is active, then also inform the PCM middle layer
* of the period finished event. */
@@ -105,49 +104,31 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream)
return IRQ_HANDLED;
}
-/**
- * psc_dma_startup: create a new substream
- *
- * This is the first function called when a stream is opened.
- *
- * If this is the first stream open, then grab the IRQ and program most of
- * the PSC registers.
- */
-int psc_dma_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
- int rc;
+ struct psc_dma_stream *s = _psc_dma_stream;
- dev_dbg(psc_dma->dev, "psc_dma_startup(substream=%p)\n", substream);
+ /* For each finished period, dequeue the completed period buffer
+ * and enqueue a new one in it's place. */
+ while (bcom_buffer_done(s->bcom_task)) {
+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
- if (!psc_dma->playback.active &&
- !psc_dma->capture.active) {
- /* Setup the IRQs */
- rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,
- "psc-dma-status", psc_dma);
- rc |= request_irq(psc_dma->capture.irq,
- &psc_dma_bcom_irq, IRQF_SHARED,
- "psc-dma-capture", &psc_dma->capture);
- rc |= request_irq(psc_dma->playback.irq,
- &psc_dma_bcom_irq, IRQF_SHARED,
- "psc-dma-playback", &psc_dma->playback);
- if (rc) {
- free_irq(psc_dma->irq, psc_dma);
- free_irq(psc_dma->capture.irq,
- &psc_dma->capture);
- free_irq(psc_dma->playback.irq,
- &psc_dma->playback);
- return -ENODEV;
- }
+ s->period_current_pt += s->period_bytes;
+ if (s->period_current_pt >= s->period_end)
+ s->period_current_pt = s->period_start;
+
+ psc_dma_bcom_enqueue_next_buffer(s);
}
- return 0;
+ /* If the stream is active, then also inform the PCM middle layer
+ * of the period finished event. */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ return IRQ_HANDLED;
}
-int psc_dma_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int psc_dma_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
@@ -159,8 +140,7 @@ int psc_dma_hw_free(struct snd_pcm_substream *substream,
* This function is called by ALSA to start, stop, pause, and resume the DMA
* transfer of data.
*/
-int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
@@ -168,8 +148,8 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd,
struct psc_dma_stream *s;
struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
u16 imr;
- u8 psc_cmd;
unsigned long flags;
+ int i;
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
s = &psc_dma->capture;
@@ -189,68 +169,44 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd,
(s->period_bytes * runtime->periods);
s->period_next_pt = s->period_start;
s->period_current_pt = s->period_start;
+ s->period_size = runtime->period_size;
s->active = 1;
- /* First; reset everything */
+ /* track appl_ptr so that we have a better chance of detecting
+ * end of stream and not over running it.
+ */
+ s->runtime = runtime;
+ s->appl_ptr = s->runtime->control->appl_ptr - (runtime->period_size * runtime->periods);
+
+ /* Fill up the bestcomm bd queue and enable DMA.
+ * This will begin filling the PSC's fifo.
+ */
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
- out_8(®s->command, MPC52xx_PSC_RST_RX);
- out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
+ bcom_gen_bd_rx_reset(s->bcom_task);
+ for (i = 0; i < runtime->periods; i++)
+ if (!bcom_queue_full(s->bcom_task))
+ psc_dma_bcom_enqueue_next_buffer(s);
} else {
- out_8(®s->command, MPC52xx_PSC_RST_TX);
- out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
+ bcom_gen_bd_tx_reset(s->bcom_task);
+ psc_dma_bcom_enqueue_tx(s);
}
- /* Next, fill up the bestcomm bd queue and enable DMA.
- * This will begin filling the PSC's fifo. */
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
- bcom_gen_bd_rx_reset(s->bcom_task);
- else
- bcom_gen_bd_tx_reset(s->bcom_task);
- while (!bcom_queue_full(s->bcom_task))
- psc_dma_bcom_enqueue_next_buffer(s);
bcom_enable(s->bcom_task);
- /* Due to errata in the dma mode; need to line up enabling
- * the transmitter with a transition on the frame sync
- * line */
-
spin_lock_irqsave(&psc_dma->lock, flags);
- /* first make sure it is low */
- while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0)
- ;
- /* then wait for the transition to high */
- while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0)
- ;
- /* Finally, enable the PSC.
- * Receiver must always be enabled; even when we only want
- * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */
- psc_cmd = MPC52xx_PSC_RX_ENABLE;
- if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
- psc_cmd |= MPC52xx_PSC_TX_ENABLE;
- out_8(®s->command, psc_cmd);
+ out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
spin_unlock_irqrestore(&psc_dma->lock, flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
- /* Turn off the PSC */
s->active = 0;
- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (!psc_dma->playback.active) {
- out_8(®s->command, 2 << 4); /* reset rx */
- out_8(®s->command, 3 << 4); /* reset tx */
- out_8(®s->command, 4 << 4); /* reset err */
- }
- } else {
- out_8(®s->command, 3 << 4); /* reset tx */
- out_8(®s->command, 4 << 4); /* reset err */
- if (!psc_dma->capture.active)
- out_8(®s->command, 2 << 4); /* reset rx */
- }
bcom_disable(s->bcom_task);
- while (!bcom_queue_empty(s->bcom_task))
- bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ bcom_gen_bd_rx_reset(s->bcom_task);
+ else
+ bcom_gen_bd_tx_reset(s->bcom_task);
break;
@@ -265,44 +221,11 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd,
imr |= MPC52xx_PSC_IMR_TXEMP;
if (psc_dma->capture.active)
imr |= MPC52xx_PSC_IMR_ORERR;
- out_be16(®s->isr_imr.imr, imr);
+ out_be16(®s->isr_imr.imr, psc_dma->imr | imr);
return 0;
}
-/**
- * psc_dma_shutdown: shutdown the data transfer on a stream
- *
- * Shutdown the PSC if there are no other substreams open.
- */
-void psc_dma_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
-
- dev_dbg(psc_dma->dev, "psc_dma_shutdown(substream=%p)\n", substream);
-
- /*
- * If this is the last active substream, disable the PSC and release
- * the IRQ.
- */
- if (!psc_dma->playback.active &&
- !psc_dma->capture.active) {
-
- /* Disable all interrupts and reset the PSC */
- out_be16(&psc_dma->psc_regs->isr_imr.imr, 0);
- out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset tx */
- out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset rx */
- out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */
- out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
-
- /* Release irqs */
- free_irq(psc_dma->irq, psc_dma);
- free_irq(psc_dma->capture.irq, &psc_dma->capture);
- free_irq(psc_dma->playback.irq, &psc_dma->playback);
- }
-}
/* ---------------------------------------------------------------------
* The PSC DMA 'ASoC platform' driver
@@ -312,62 +235,78 @@ void psc_dma_shutdown(struct snd_pcm_substream *substream,
* interaction with the attached codec
*/
-static const struct snd_pcm_hardware psc_dma_pcm_hardware = {
+static const struct snd_pcm_hardware psc_dma_hardware = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_BATCH,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
.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;
+ }
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, 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;
@@ -384,60 +323,78 @@ psc_dma_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, count);
}
-static struct snd_pcm_ops psc_dma_pcm_ops = {
- .open = psc_dma_pcm_open,
- .close = psc_dma_pcm_close,
+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_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:
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_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 +406,230 @@ 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)
+{
+ phys_addr_t fifo;
+ struct psc_dma *psc_dma;
+ struct resource res;
+ int size, irq, rc;
+ const __be32 *prop;
+ void __iomem *regs;
+
+ /* 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;
+ }
+
+ /* Get the PSC ID */
+ prop = of_get_property(op->node, "cell-index", &size);
+ if (!prop || size < sizeof *prop)
+ return -ENODEV;
+
+ spin_lock_init(&psc_dma->lock);
+ psc_dma->id = be32_to_cpu(*prop);
+ 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_dma->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_dma->id, 10, fifo, 512);
+ psc_dma->playback.bcom_task =
+ bcom_psc_gen_bd_tx_init(psc_dma->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");
+
+ 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 */
+ 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);
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h
index a33232c..3b2ded9 100644
--- a/sound/soc/fsl/mpc5200_dma.h
+++ b/sound/soc/fsl/mpc5200_dma.h
@@ -5,8 +5,10 @@
#ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__
#define __SOUND_SOC_FSL_MPC5200_DMA_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 +19,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 +32,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 +54,12 @@ struct psc_dma {
struct mpc52xx_psc_fifo __iomem *fifo_regs;
unsigned int irq;
struct device *dev;
- struct snd_soc_dai dai;
spinlock_t lock;
u32 sicr;
+ uint sysclk;
+ int imr;
+ int id;
+ unsigned int slots;
/* per-stream data */
struct psc_dma_stream playback;
@@ -63,19 +72,9 @@ struct psc_dma {
} stats;
};
+int mpc5200_audio_dma_create(struct of_device *op);
+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 12a7917..0bf24a3 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -5,32 +5,21 @@
* 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_psc_i2s.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");
-
/**
* PSC_I2S_RATES: sample rates supported by the I2S
*
@@ -46,8 +35,7 @@ MODULE_LICENSE("GPL");
* PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
*/
#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_BE)
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
@@ -82,8 +70,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,16 +126,13 @@ 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,
};
-static struct snd_soc_dai psc_i2s_dai_template = {
+struct snd_soc_dai psc_i2s_dai[] = {{
+ .name = "I2S",
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -163,71 +146,8 @@ static struct snd_soc_dai psc_i2s_dai_template = {
.formats = PSC_I2S_FORMATS,
},
.ops = &psc_i2s_dai_ops,
-};
-
-/* ---------------------------------------------------------------------
- * 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);
+}};
+EXPORT_SYMBOL_GPL(psc_i2s_dai);
/* ---------------------------------------------------------------------
* OF platform bus binding code:
@@ -237,82 +157,26 @@ 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;
- }
+ struct mpc52xx_psc __iomem *regs;
- /* 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);
+ if (rc != 0)
+ return rc;
+
+ rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
+ if (rc != 0) {
+ pr_err("Failed to register DAI\n");
+ return 0;
}
- /* 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);
+ regs = psc_dma->psc_regs;
/* 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,66 +185,36 @@ 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(&psc_dma_pcm_soc_platform, op->node,
- &psc_dma->dai);
+ /* Due to errata in the dma mode; need to line up enabling
+ * the transmitter with a transition on the frame sync
+ * line */
+
+ /* 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) */
+
+ /* Go */
+ out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_TX_ENABLE | 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 */
static struct of_device_id psc_i2s_match[] __devinitdata = {
{ .compatible = "fsl,mpc5200-psc-i2s", },
+ { .compatible = "fsl,mpc5200b-psc-i2s", },
{}
};
MODULE_DEVICE_TABLE(of, psc_i2s_match);
@@ -411,4 +245,7 @@ static void __exit psc_i2s_exit(void)
}
module_exit(psc_i2s_exit);
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h
new file mode 100644
index 0000000..ce55e07
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.h
@@ -0,0 +1,12 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+#define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+
+extern struct snd_soc_dai psc_i2s_dai[];
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 6/9] Codec for STAC9766 used on the Efika
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (4 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 11:46 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 7/9] AC97 driver for mpc5200 Jon Smirl
` (4 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
AC97 codec for STAC9766 used on the Efika.
Datasheet: http://www.idt.com/products/getDoc.cfm?docID=13134007
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
0 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7f78b65..cb07d9b 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 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC3X if I2C
@@ -93,6 +94,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 70c55fa..46c007c 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
@@ -42,6 +43,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..7740cd5
--- /dev/null
+++ b/sound/soc/codecs/stac9766.c
@@ -0,0 +1,470 @@
+/*
+ * 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 */
+
+ 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 */
+ case SND_SOC_BIAS_PREPARE: /* partial On */
+ 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;
+}
+
+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_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, reset;
+
+ reset = 0;
+ /* give the codec an AC97 warm reset to start the link */
+reset:
+ if (reset > 5) {
+ printk(KERN_ERR "stac9766 failed to resume");
+ return -EIO;
+ }
+ codec->ac97->bus->ops->warm_reset(codec->ac97);
+ id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+ if (id != 0x4c13) {
+ stac9766_reset(codec, 0);
+ reset++;
+ goto reset;
+ }
+ 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 = SND_SOC_STD_AC97_FMTS,
+ },
+ .capture = {
+ .stream_name = "stac9766 analog",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SND_SOC_STD_AC97_FMTS,
+ },
+ /* 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);
+
+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_modinit(void)
+{
+ return snd_soc_register_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
+}
+module_init(stac9766_modinit);
+
+static void __exit stac9766_exit(void)
+{
+ snd_soc_unregister_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
+}
+module_exit(stac9766_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
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 7/9] AC97 driver for mpc5200
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (5 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 6/9] Codec for STAC9766 used on the Efika Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 12:10 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika Jon Smirl
` (3 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
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 | 394 ++++++++++++++++++++++++++++++++++++++
sound/soc/fsl/mpc5200_psc_ac97.h | 15 +
4 files changed, 421 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 1918c78..3bce952 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -29,3 +29,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 AC97_BUS
+ 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..fa1bb9a
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -0,0 +1,394 @@
+/*
+ * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
+ *
+ * Copyright (C) 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mpc52xx_psc.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+
+#define DRV_NAME "mpc5200-psc-ac97"
+
+/* ALSA only supports a single AC97 device so static is recommend here */
+static struct psc_dma *psc_dma;
+
+static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ 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)
+{
+ 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 mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+ /* Do a cold reset */
+ out_8(®s->op1, MPC52xx_PSC_OP_RES);
+ udelay(10);
+ out_8(®s->op0, MPC52xx_PSC_OP_RES);
+ udelay(50);
+
+ /* PSC recover from cold reset (cfr user manual, not sure if useful) */
+ out_be32(®s->sicr, in_be32(®s->sicr));
+}
+
+static void psc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+ out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR);
+ udelay(3);
+ out_be32(®s->sicr, psc_dma->sicr);
+}
+
+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;
+}
+
+/* ---------------------------------------------------------------------
+ * 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,
+ .trigger = psc_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops psc_ac97_digital_ops = {
+ .hw_params = psc_ac97_hw_digital_params,
+};
+
+struct snd_soc_dai psc_ac97_dai[] = {
+{
+ .name = "AC97",
+ .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_S32_BE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_BE,
+ },
+ .ops = &psc_ac97_analog_ops,
+},
+{
+ .name = "SPDIF",
+ .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,
+}};
+EXPORT_SYMBOL_GPL(psc_ac97_dai);
+
+
+
+/* ---------------------------------------------------------------------
+ * 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, i, id1, id2, timeout, max_reset;
+ struct snd_ac97 ac97;
+ struct mpc52xx_psc __iomem *regs;
+
+ rc = mpc5200_audio_dma_create(op);
+ if (rc != 0)
+ return rc;
+
+ for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+ psc_ac97_dai[i].dev = &op->dev;
+
+ rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
+ if (rc != 0) {
+ pr_err("Failed to register DAI\n");
+ return 0;
+ }
+
+ psc_dma = dev_get_drvdata(&op->dev);
+ regs = psc_dma->psc_regs;
+ ac97.private_data = psc_dma;
+
+ for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+ psc_ac97_dai[i].private_data = psc_dma;
+
+ psc_dma->imr = 0;
+ out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+
+ /* Configure the serial interface mode to AC97 */
+ psc_dma->sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97;
+ out_be32(®s->sicr, psc_dma->sicr);
+
+ /* No slots active */
+ out_be32(®s->ac97_slots, 0x00000000);
+
+ /* AC97 clock is generated by the codec.
+ * Ensure that it starts ticking after codec reset.
+ */
+ max_reset = 0;
+reset:
+ if (max_reset++ > 5) {
+ dev_err(&op->dev, "AC97 codec failed to reset\n");
+ mpc5200_audio_dma_destroy(op);
+ return -ENODEV;
+ }
+
+ psc_ac97_cold_reset(&ac97);
+ psc_ac97_warm_reset(&ac97);
+
+ /* first make sure it is low */
+ timeout = 0;
+ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) {
+ udelay(1);
+ if (timeout++ > 1000)
+ goto reset;
+ }
+ /* then wait for the transition to high */
+ timeout = 0;
+ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) {
+ udelay(1);
+ if (timeout++ > 1000)
+ psc_ac97_warm_reset(&ac97);
+ }
+
+ /* Go */
+ out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
+
+ id1 = psc_ac97_read(&ac97, AC97_VENDOR_ID1);
+ id2 = psc_ac97_read(&ac97, AC97_VENDOR_ID2);
+
+ dev_info(&op->dev, "Codec ID is %04x %04x\n", id1, id2);
+
+ 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);
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION("mpc5200 AC97 module");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h
new file mode 100644
index 0000000..4bc18c3
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_ac97.h
@@ -0,0 +1,15 @@
+/*
+ * 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__
+
+extern struct snd_soc_dai psc_ac97_dai[];
+
+#define MPC5200_AC97_NORMAL 0
+#define MPC5200_AC97_SPDIF 1
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (6 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 7/9] AC97 driver for mpc5200 Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 12:12 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board Jon Smirl
` (2 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
Fabric bindings for STAC9766 AC97 codec on the Efika.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
sound/soc/fsl/Kconfig | 8 +++
sound/soc/fsl/Makefile | 3 +
sound/soc/fsl/efika-audio-fabric.c | 94 ++++++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/fsl/efika-audio-fabric.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3bce952..edd8516 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -39,4 +39,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..5126a81
--- /dev/null
+++ b/sound/soc/fsl/efika-audio-fabric.c
@@ -0,0 +1,94 @@
+/*
+ * 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 struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link efika_fabric_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 Analog",
+ .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG],
+ .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+ .name = "AC97",
+ .stream_name = "AC97 IEC958",
+ .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL],
+ .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int efika_fabric_init(void)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ if (!machine_is_compatible("bplan,efika"))
+ return -ENODEV;
+
+ card.platform = &mpc5200_audio_dma_platform;
+ card.name = "Efika";
+ card.dai_link = efika_fabric_dai;
+ card.num_links = ARRAY_SIZE(efika_fabric_dai);
+
+ device.card = &card;
+ device.codec_dev = &soc_codec_dev_stac9766;
+
+ pdev = platform_device_alloc("soc-audio", 1);
+ if (!pdev) {
+ pr_err("efika_fabric_init: platform_device_alloc() failed\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, &device);
+ device.dev = &pdev->dev;
+
+ rc = platform_device_add(pdev);
+ if (rc) {
+ pr_err("efika_fabric_init: platform_device_add() failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+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");
+
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board.
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (7 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika Jon Smirl
@ 2009-05-23 23:13 ` Jon Smirl
2009-05-24 12:13 ` [alsa-devel] " Mark Brown
2009-05-23 23:20 ` [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
2009-05-24 11:08 ` [alsa-devel] " Mark Brown
10 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:13 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
Support for AC97 on Phytec pmc030 base board. A wm9712 AC97 codec is used.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
sound/soc/fsl/Kconfig | 7 +++
sound/soc/fsl/Makefile | 1
sound/soc/fsl/pcm030-audio-fabric.c | 94 +++++++++++++++++++++++++++++++++++
3 files changed, 102 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/fsl/pcm030-audio-fabric.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index edd8516..5080e3e 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -47,4 +47,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.
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index f406470..5806d11 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
# MPC5200 Machine Support
obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
+obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
new file mode 100644
index 0000000..4bd957a
--- /dev/null
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -0,0 +1,94 @@
+/*
+ * Phytec pcm030 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/wm9712.h"
+
+static struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link pcm030_fabric_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 Analog",
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+ .name = "AC97",
+ .stream_name = "AC97 IEC958",
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+ .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int pcm030_fabric_init(void)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ if (!machine_is_compatible("phytec,pcm030"))
+ return -ENODEV;
+
+ card.platform = &mpc5200_audio_dma_platform;
+ card.name = "pcm030";
+ card.dai_link = pcm030_fabric_dai;
+ card.num_links = ARRAY_SIZE(pcm030_fabric_dai);
+
+ device.card = &card;
+ device.codec_dev = &soc_codec_dev_wm9712;
+
+ pdev = platform_device_alloc("soc-audio", 1);
+ if (!pdev) {
+ pr_err("pcm030_fabric_init: platform_device_alloc() failed\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, &device);
+ device.dev = &pdev->dev;
+
+ rc = platform_device_add(pdev);
+ if (rc) {
+ pr_err("pcm030_fabric_init: platform_device_add() failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static __exit void pcm030_fabric_exit(void)
+{
+}
+
+module_init(pcm030_fabric_init);
+module_exit(pcm030_fabric_exit);
+
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION(DRV_NAME ": mpc5200 pcm030 fabric driver");
+MODULE_LICENSE("GPL");
+
^ permalink raw reply related [flat|nested] 35+ messages in thread
* Re: [PATCH V2 0/9] mpc5200 audio rework for AC97
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (8 preceding siblings ...)
2009-05-23 23:13 ` [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board Jon Smirl
@ 2009-05-23 23:20 ` Jon Smirl
2009-05-24 11:08 ` [alsa-devel] " Mark Brown
10 siblings, 0 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-23 23:20 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, alsa-devel, broonie
On Sat, May 23, 2009 at 7:12 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.
Series is based on branch for-2.6.31 commit
0154724d487586241c1ad57cfd348ed2ff2274e2 at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 0/9] mpc5200 audio rework for AC97
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
` (9 preceding siblings ...)
2009-05-23 23:20 ` [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
@ 2009-05-24 11:08 ` Mark Brown
2009-05-24 15:21 ` Jon Smirl
10 siblings, 1 reply; 35+ messages in thread
From: Mark Brown @ 2009-05-24 11:08 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:12:55PM -0400, Jon Smirl wrote:
[Jon, could you please fix the word wrapping in your MUA, there's none
at all which makes it more difficult than it needs to be to respond to
your e-mail.]
> Mark is not enthused about soc-of-simple.c so rather than extend it
> for AC97 I altered the drivers to not use it. Instead they use the old
> way of manually binding everything. Mark would like to see OF binding
> more closely integrated to the core. Once a proper solution for OF
I really don't feel that this reflects the issues I had with the code
well. There are two basic issues here:
- You had been trying to represent the AC97 CODEC drivers as platform
devices which is not a good approach, largely since AC97 is a real
bus and should be represented as such.
- soc-of-simple really should be merged into the core now, there's no
need to have to add changes to individual drivers any more since
drivers are already regstering with the core.
These two points are basically unrelated except in that your motiviation
for doing the platform device thing was trying to fit your machine
drivers into the existing soc-of-simple framework. It's the changes to
the cross-platform AC97 drivers that were causing real problems,
soc-of-simple is only an issue in that using it motivates these other
changes.
> binding is agreed upon it is easy to convert the existing drivers. A
> first step would be converting the existing codec drivers so that they
> can be dynamically loaded.
I'm not aware of any CODEC drivers which can't currently be built and
used as modules. If you mean "load via the normal device model" then
yes, that'd be very good (and is in progress) but it's another issue and
as I explained last time AC97 poses particular problems there.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 1/9] Register the wm9712 DAIs on module load
2009-05-23 23:12 ` [PATCH V2 1/9] Register the wm9712 DAIs on module load Jon Smirl
@ 2009-05-24 11:11 ` Mark Brown
2009-05-24 15:28 ` Jon Smirl
0 siblings, 1 reply; 35+ messages in thread
From: Mark Brown @ 2009-05-24 11:11 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:12:57PM -0400, Jon Smirl wrote:
> Register the wm9712 DAIs on module load
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
Why do you wish to do this - ASoC does not require or use DAI registration
for AC97 CODECs?
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 3/9] Rename the PSC functions to DMA
2009-05-23 23:13 ` [PATCH V2 3/9] Rename the PSC functions to DMA Jon Smirl
@ 2009-05-24 11:15 ` Mark Brown
2009-05-24 14:12 ` Grant Likely
0 siblings, 1 reply; 35+ messages in thread
From: Mark Brown @ 2009-05-24 11:15 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:01PM -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.
I'm OK with both these refactoring patches if Grant is - Grant, if I
could get your ack I'd like to merge these as soon as possible since
they're very big changes and it'd cut down on review effort.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-23 23:13 ` [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code Jon Smirl
@ 2009-05-24 11:26 ` Mark Brown
2009-05-24 18:55 ` Wolfram Sang
1 sibling, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 11:26 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:05PM -0400, Jon Smirl wrote:
This is all basically OK bearing in mind that I know nothing about the
hardware. A couple of things, though:
> +/* ---------------------------------------------------------------------
> + * Sysfs attributes for debugging
> + */
These look like they may be better placed in debugfs?
> +EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);
> +EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy);
> +static int __init mpc5200_soc_platform_init(void)
> +{
> + /* Tell the ASoC OF helpers about it */
> + 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);
This all looks a bit odd - I'd expect to see the platform registering
with the core when it is probed rather than on module load. Looking at
the code it appears that you're automatically instantiating the DMA
driver from the node for the DAI? If that is the case then you ought
to be calling the platform registration functions from the dma_create()
and dma_destroy() functions instead.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 6/9] Codec for STAC9766 used on the Efika
2009-05-23 23:13 ` [PATCH V2 6/9] Codec for STAC9766 used on the Efika Jon Smirl
@ 2009-05-24 11:46 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 11:46 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:07PM -0400, Jon Smirl wrote:
> AC97 codec for STAC9766 used on the Efika.
> Datasheet: http://www.idt.com/products/getDoc.cfm?docID=13134007
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
This looks mostly good, a couple of issues below. I'll apply the patch
but please submit a followup patch for resume as discussed below.
> +static int ac97_digital_trigger(struct snd_pcm_substream *substream,
> + int cmd, struct snd_soc_dai *dai)
Very strange indentation here...
> +static int stac9766_codec_resume(struct platform_device *pdev)
> +{
> + /* give the codec an AC97 warm reset to start the link */
> +reset:
> + if (reset > 5) {
> + printk(KERN_ERR "stac9766 failed to resume");
> + return -EIO;
> + }
> + codec->ac97->bus->ops->warm_reset(codec->ac97);
> + id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
> + if (id != 0x4c13) {
> + stac9766_reset(codec, 0);
> + reset++;
> + goto reset;
> + }
As I said last time I'd this looks like it should be using the reset
function that you've provided?
The loop is new since last time and seems very suspicious - are you sure
that this is a CODEC problem that's being worked around and not an issue
with the AC97 controller or with the specific system starting up the
master clock for the CODEC after resume? If it's an issue with the
controler then it'd be better to fix it there so that all CODEC drivers
get the benefit. If it's a CODEC issue a comment explaining the problem
would be helpful since it's not the sort of issue I'd expect to see in a
CODEC. The fact that it's not needed on initial boot suggests something
controller or external clock related.
Either way, it'd be much clearer to rewrite it using a for or while loop
rather than a goto. Please submit a followup patch fixing at least this
issue.
> +static int __init stac9766_modinit(void)
> +{
> + return snd_soc_register_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
> +}
> +module_init(stac9766_modinit);
> +
> +static void __exit stac9766_exit(void)
> +{
> + snd_soc_unregister_dais(stac9766_dai, ARRAY_SIZE(stac9766_dai));
> +}
> +module_exit(stac9766_exit);
These are not needed for AC97 CODECs, ASoC doesn't wait for them to
probe. I'll apply a patch removing these.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 7/9] AC97 driver for mpc5200
2009-05-23 23:13 ` [PATCH V2 7/9] AC97 driver for mpc5200 Jon Smirl
@ 2009-05-24 12:10 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 12:10 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:09PM -0400, Jon Smirl wrote:
> +#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
These can all just be removed, they can be added later if they are
implemented.
> +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;
Note that cpu_dai is passed in as an argument here - this didn't used to
be the case which is why most drivers fish it out of rtd but that's not
required any more.
> +static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
Again with the odd indentation.
> +static int __devinit psc_ac97_of_probe(struct of_device *op,
> + const struct of_device_id *match)
> +{
> + rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
> + if (rc != 0) {
> + pr_err("Failed to register DAI\n");
> + return 0;
> + }
I'd expect to see the error passed back to the caller here? I'd also
expect to see this happening right at the end of this function after
you're happy everything else has worked, otherwise the core might try to
start using the half-initialised driver.
> + /* AC97 clock is generated by the codec.
> + * Ensure that it starts ticking after codec reset.
> + */
> + max_reset = 0;
> +reset:
> + if (max_reset++ > 5) {
> + dev_err(&op->dev, "AC97 codec failed to reset\n");
> + mpc5200_audio_dma_destroy(op);
> + return -ENODEV;
> + }
> +
> + psc_ac97_cold_reset(&ac97);
> + psc_ac97_warm_reset(&ac97);
> +
> + /* first make sure it is low */
> + timeout = 0;
> + while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) {
> + udelay(1);
> + if (timeout++ > 1000)
> + goto reset;
> + }
This looks awfully similar to the logic in the resume function of your
CODEC driver... In general an ASoC AC97 DAI driver shouldn't be trying
to bring the CODEC up like this, it should normally leave that up to the
CODEC driver. This is mostly because some system designers do strange
and wonderful things with clocking which require some system specific
code to either bring up the master clock for the codec before it'll
reset or reconfigure the clocking to something standard.
The issue with the use of goto rather than a real loop also applies
here.
> + /* then wait for the transition to high */
> + timeout = 0;
> + while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) {
> + udelay(1);
> + if (timeout++ > 1000)
> + psc_ac97_warm_reset(&ac97);
> + }
Shouldn't this be integrated into the warm reset operation (or to put it
another way, why wouldn't a warm reset want to do this)?
> +
> + /* Go */
> + out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
> +
> + id1 = psc_ac97_read(&ac97, AC97_VENDOR_ID1);
> + id2 = psc_ac97_read(&ac97, AC97_VENDOR_ID2);
> +
> + dev_info(&op->dev, "Codec ID is %04x %04x\n", id1, id2);
You really can't rely on this working in general system designs - like I
say, some platforms will require additional work to bring the CODEC up.
I'd just remove it, it's purely for information anyway. The out_8()
probably ought to be moved into the ASoC probe() function (called once
the card is coming up).
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika
2009-05-23 23:13 ` [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika Jon Smirl
@ 2009-05-24 12:12 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 12:12 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:11PM -0400, Jon Smirl wrote:
> Fabric bindings for STAC9766 AC97 codec on the Efika.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
This is OK, but obviously depends on previous patches so I'll not apply
it just yet. You might want to run it (and many of your other patches)
through checkpatch, though. One nit:
> +static __exit void efika_fabric_exit(void)
> +{
> +}
This should either do something or be removed.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board.
2009-05-23 23:13 ` [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board Jon Smirl
@ 2009-05-24 12:13 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 12:13 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sat, May 23, 2009 at 07:13:13PM -0400, Jon Smirl wrote:
> Support for AC97 on Phytec pmc030 base board. A wm9712 AC97 codec is used.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
Same comments as for the previous driver - it's OK once the AC97 DAI
driver goes in.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s
2009-05-23 23:12 ` [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Jon Smirl
@ 2009-05-24 14:11 ` Grant Likely
0 siblings, 0 replies; 35+ messages in thread
From: Grant Likely @ 2009-05-24 14:11 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel, broonie
On Sat, May 23, 2009 at 5:12 PM, Jon Smirl <jonsmirl@gmail.com> wrote:
> Basic split of mpc5200 DMA code out from i2s into a standalone file.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
I haven't looked in detail, but I'm okay with this in principle.
Acked-by: Grant Likely <grant.likely@secretlab.ca>
g.
> ---
> =A0sound/soc/fsl/Kconfig =A0 =A0 =A0 =A0 =A0 | =A0 =A04
> =A0sound/soc/fsl/Makefile =A0 =A0 =A0 =A0 =A0| =A0 =A02
> =A0sound/soc/fsl/mpc5200_dma.c =A0 =A0 | =A0458 +++++++++++++++++++++++++=
++++++++++++
> =A0sound/soc/fsl/mpc5200_dma.h =A0 =A0 | =A0 81 +++++++
> =A0sound/soc/fsl/mpc5200_psc_i2s.c | =A0485 -----------------------------=
----------
> =A05 files changed, 547 insertions(+), 483 deletions(-)
> =A0create mode 100644 sound/soc/fsl/mpc5200_dma.c
> =A0create 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 @@
> =A0config SND_SOC_OF_SIMPLE
> =A0 =A0 =A0 =A0tristate
> +
> +config SND_MPC52xx_DMA
> + =A0 =A0 =A0 tristate
>
> =A0# ASoC platform support for the Freescale MPC8610 SOC. =A0This compile=
s drivers
> =A0# for the SSI and the Elo DMA controller. =A0You will still need to se=
lect
> @@ -23,6 +26,7 @@ config SND_SOC_MPC5200_I2S
> =A0 =A0 =A0 =A0tristate "Freescale MPC5200 PSC in I2S mode driver"
> =A0 =A0 =A0 =A0depends on PPC_MPC52xx && PPC_BESTCOMM
> =A0 =A0 =A0 =A0select SND_SOC_OF_SIMPLE
> + =A0 =A0 =A0 select SND_MPC52xx_DMA
> =A0 =A0 =A0 =A0select PPC_BESTCOMM_GEN_BD
> =A0 =A0 =A0 =A0help
> =A0 =A0 =A0 =A0 =A0Say 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 :=3D fsl_ssi.o
> =A0snd-soc-fsl-dma-objs :=3D fsl_dma.o
> =A0obj-$(CONFIG_SND_SOC_MPC8610) +=3D snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
>
> +# MPC5200 Platform Support
> +obj-$(CONFIG_SND_MPC52xx_DMA) +=3D mpc5200_dma.o
> =A0obj-$(CONFIG_SND_SOC_MPC5200_I2S) +=3D 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..4bae8d6
> --- /dev/null
> +++ b/sound/soc/fsl/mpc5200_dma.c
> @@ -0,0 +1,458 @@
> +/*
> + * 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)
> +{
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D _psc_i2s;
> + =A0 =A0 =A0 struct mpc52xx_psc __iomem *regs =3D psc_i2s->psc_regs;
> + =A0 =A0 =A0 u16 isr;
> +
> + =A0 =A0 =A0 isr =3D in_be16(®s->mpc52xx_psc_isr);
> +
> + =A0 =A0 =A0 /* Playback underrun error */
> + =A0 =A0 =A0 if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEM=
P))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s->stats.underrun_count++;
> +
> + =A0 =A0 =A0 /* Capture overrun error */
> + =A0 =A0 =A0 if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR=
))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s->stats.overrun_count++;
> +
> + =A0 =A0 =A0 out_8(®s->command, 4 << 4); =A0/* reset the error status=
*/
> +
> + =A0 =A0 =A0 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 i=
n
> + * the queue. =A0Otherwise the enqueue will fail and the audio ring buff=
er
> + * will get out of sync
> + */
> +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
> +{
> + =A0 =A0 =A0 struct bcom_bd *bd;
> +
> + =A0 =A0 =A0 /* Prepare and enqueue the next buffer descriptor */
> + =A0 =A0 =A0 bd =3D bcom_prepare_next_buffer(s->bcom_task);
> + =A0 =A0 =A0 bd->status =3D s->period_bytes;
> + =A0 =A0 =A0 bd->data[0] =3D s->period_next_pt;
> + =A0 =A0 =A0 bcom_submit_next_buffer(s->bcom_task, NULL);
> +
> + =A0 =A0 =A0 /* Update for next period */
> + =A0 =A0 =A0 s->period_next_pt +=3D s->period_bytes;
> + =A0 =A0 =A0 if (s->period_next_pt >=3D s->period_end)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_next_pt =3D s->period_start;
> +}
> +
> +/* Bestcomm DMA irq handler */
> +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
> +{
> + =A0 =A0 =A0 struct psc_i2s_stream *s =3D _psc_i2s_stream;
> +
> + =A0 =A0 =A0 /* For each finished period, dequeue the completed period b=
uffer
> + =A0 =A0 =A0 =A0* and enqueue a new one in it's place. */
> + =A0 =A0 =A0 while (bcom_buffer_done(s->bcom_task)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_retrieve_buffer(s->bcom_task, NULL, NU=
LL);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt +=3D s->period_bytes;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (s->period_current_pt >=3D s->period_end=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt =3D s-=
>period_start;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s_bcom_enqueue_next_buffer(s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_enable(s->bcom_task);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* If the stream is active, then also inform the PCM middle=
layer
> + =A0 =A0 =A0 =A0* of the period finished event. */
> + =A0 =A0 =A0 if (s->active)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_pcm_period_elapsed(s->stream);
> +
> + =A0 =A0 =A0 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,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> + =A0 =A0 =A0 int rc;
> +
> + =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=3D%p)\n", =
substream);
> +
> + =A0 =A0 =A0 if (!psc_i2s->playback.active &&
> + =A0 =A0 =A0 =A0 =A0 !psc_i2s->capture.active) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Setup the IRQs */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D request_irq(psc_i2s->irq, &psc_i2s_s=
tatus_irq, IRQF_SHARED,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"psc-i2s=
-status", psc_i2s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc |=3D request_irq(psc_i2s->capture.irq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &psc_i2=
s_bcom_irq, IRQF_SHARED,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "psc-i2=
s-capture", &psc_i2s->capture);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc |=3D request_irq(psc_i2s->playback.irq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &psc_i2=
s_bcom_irq, IRQF_SHARED,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "psc-i2=
s-playback", &psc_i2s->playback);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->irq, psc_=
i2s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->capture.i=
rq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&psc_i2s=
->capture);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->playback.=
irq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&psc_i2s=
->playback);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +int psc_i2s_hw_free(struct snd_pcm_substream *substream,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> +{
> + =A0 =A0 =A0 snd_pcm_set_runtime_buffer(substream, NULL);
> + =A0 =A0 =A0 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,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> + =A0 =A0 =A0 struct snd_pcm_runtime *runtime =3D substream->runtime;
> + =A0 =A0 =A0 struct psc_i2s_stream *s;
> + =A0 =A0 =A0 struct mpc52xx_psc __iomem *regs =3D psc_i2s->psc_regs;
> + =A0 =A0 =A0 u16 imr;
> + =A0 =A0 =A0 u8 psc_cmd;
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> +
> + =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=3D%p, cmd=
=3D%i)"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 " stream_id=3D%i\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream, cmd, substream->pstr->stream);
> +
> + =A0 =A0 =A0 switch (cmd) {
> + =A0 =A0 =A0 case SNDRV_PCM_TRIGGER_START:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_bytes =3D frames_to_bytes(runtime=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 runtime->period_size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_start =3D virt_to_phys(runtime->d=
ma_area);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_end =3D s->period_start +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (s->period_=
bytes * runtime->periods);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_next_pt =3D s->period_start;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt =3D s->period_start;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->active =3D 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* First; reset everything */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_RX);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_ERR_STAT);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_TX);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_ERR_STAT);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Next, fill up the bestcomm bd queue and =
enable DMA.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* This will begin filling the PSC's fifo=
. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_gen_bd_rx_reset(s->bco=
m_task);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_gen_bd_tx_reset(s->bco=
m_task);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (!bcom_queue_full(s->bcom_task))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s_bcom_enqueue_next_b=
uffer(s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_enable(s->bcom_task);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Due to errata in the i2s mode; need to l=
ine up enabling
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the transmitter with a transition on t=
he frame sync
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* line */
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(&psc_i2s->lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* first make sure it is low */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 while ((in_8(®s->ipcr_acr.ipcr) & 0x80) =
!=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* then wait for the transition to high */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 while ((in_8(®s->ipcr_acr.ipcr) & 0x80) =
=3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Finally, enable the PSC.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Receiver must always be enabled; even =
when we only want
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* transmit. =A0(see 15.3.2.3 of MPC5200B=
User's Guide) */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_cmd =3D MPC52xx_PSC_RX_ENABLE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_PLAYBACK)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_cmd |=3D MPC52xx_PSC_TX=
_ENABLE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, psc_cmd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&psc_i2s->lock, flag=
s);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case SNDRV_PCM_TRIGGER_STOP:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Turn off the PSC */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->active =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!psc_i2s->playback.acti=
ve) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 2 << 4); =A0/* reset rx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 3 << 4); =A0/* reset tx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 4 << 4); =A0/* reset err */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, 3 << =
4); =A0/* reset tx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, 4 << =
4); =A0/* reset err */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!psc_i2s->capture.activ=
e)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 2 << 4); =A0/* reset rx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_disable(s->bcom_task);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (!bcom_queue_empty(s->bcom_task))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_retrieve_buffer(s->bco=
m_task, NULL, NULL);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "invalid command\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Update interrupt enable settings */
> + =A0 =A0 =A0 imr =3D 0;
> + =A0 =A0 =A0 if (psc_i2s->playback.active)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 imr |=3D MPC52xx_PSC_IMR_TXEMP;
> + =A0 =A0 =A0 if (psc_i2s->capture.active)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 imr |=3D MPC52xx_PSC_IMR_ORERR;
> + =A0 =A0 =A0 out_be16(®s->isr_imr.imr, imr);
> +
> + =A0 =A0 =A0 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,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_d=
ai *dai)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> +
> + =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=3D%p)\n",=
substream);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* If this is the last active substream, disable the PSC =
and release
> + =A0 =A0 =A0 =A0* the IRQ.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (!psc_i2s->playback.active &&
> + =A0 =A0 =A0 =A0 =A0 !psc_i2s->capture.active) {
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Disable all interrupts and reset the PSC=
*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 3 << 4);=
/* reset tx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 2 << 4);=
/* reset rx */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 1 << 4);=
/* reset mode */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 4 << 4);=
/* reset error */
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Release irqs */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->irq, psc_i2s);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->capture.irq, &psc_i2s->ca=
pture);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->playback.irq, &psc_i2s->p=
layback);
> + =A0 =A0 =A0 }
> +}
> +
> +/* ---------------------------------------------------------------------
> + * 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 =3D {
> + =A0 =A0 =A0 .info =3D SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO=
_BLOCK_TRANSFER |
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_INFO_BATCH,
> + =A0 =A0 =A0 .formats =3D SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE =
|
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_=
FMTBIT_S32_BE,
> + =A0 =A0 =A0 .rate_min =3D 8000,
> + =A0 =A0 =A0 .rate_max =3D 48000,
> + =A0 =A0 =A0 .channels_min =3D 2,
> + =A0 =A0 =A0 .channels_max =3D 2,
> + =A0 =A0 =A0 .period_bytes_max =A0 =A0 =A0 =3D 1024 * 1024,
> + =A0 =A0 =A0 .period_bytes_min =A0 =A0 =A0 =3D 32,
> + =A0 =A0 =A0 .periods_min =A0 =A0 =A0 =A0 =A0 =A0=3D 2,
> + =A0 =A0 =A0 .periods_max =A0 =A0 =A0 =A0 =A0 =A0=3D 256,
> + =A0 =A0 =A0 .buffer_bytes_max =A0 =A0 =A0 =3D 2 * 1024 * 1024,
> + =A0 =A0 =A0 .fifo_size =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0,
> +};
> +
> +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s_stream *s;
> +
> + =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=3D%p)\n",=
substream);
> +
> + =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> +
> + =A0 =A0 =A0 snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardwa=
re);
> +
> + =A0 =A0 =A0 s->stream =3D substream;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s_stream *s;
> +
> + =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=3D%p)\n"=
, substream);
> +
> + =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> +
> + =A0 =A0 =A0 s->stream =3D NULL;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static snd_pcm_uframes_t
> +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> + =A0 =A0 =A0 struct psc_i2s_stream *s;
> + =A0 =A0 =A0 dma_addr_t count;
> +
> + =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> +
> + =A0 =A0 =A0 count =3D s->period_current_pt - s->period_start;
> +
> + =A0 =A0 =A0 return bytes_to_frames(substream->runtime, count);
> +}
> +
> +static struct snd_pcm_ops psc_i2s_pcm_ops =3D {
> + =A0 =A0 =A0 .open =A0 =A0 =A0 =A0 =A0 =3D psc_i2s_pcm_open,
> + =A0 =A0 =A0 .close =A0 =A0 =A0 =A0 =A0=3D psc_i2s_pcm_close,
> + =A0 =A0 =A0 .ioctl =A0 =A0 =A0 =A0 =A0=3D snd_pcm_lib_ioctl,
> + =A0 =A0 =A0 .pointer =A0 =A0 =A0 =A0=3D psc_i2s_pcm_pointer,
> +};
> +
> +static u64 psc_i2s_pcm_dmamask =3D 0xffffffff;
> +static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *da=
i,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_pcm *pcm)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D pcm->private_data;
> + =A0 =A0 =A0 size_t size =3D psc_i2s_pcm_hardware.buffer_bytes_max;
> + =A0 =A0 =A0 int rc =3D 0;
> +
> + =A0 =A0 =A0 dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=3D%p, dai=
=3D%p, pcm=3D%p)\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 card, dai, pcm);
> +
> + =A0 =A0 =A0 if (!card->dev->dma_mask)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 card->dev->dma_mask =3D &psc_i2s_pcm_dmamas=
k;
> + =A0 =A0 =A0 if (!card->dev->coherent_dma_mask)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 card->dev->coherent_dma_mask =3D 0xffffffff=
;
> +
> + =A0 =A0 =A0 if (pcm->streams[0].substream) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D snd_dma_alloc_pages(SNDRV_DMA_TYPE_D=
EV, pcm->dev, size,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &pcm->streams[0].substream->dma_buffer);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto playback_alloc_err;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (pcm->streams[1].substream) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D snd_dma_alloc_pages(SNDRV_DMA_TYPE_D=
EV, pcm->dev, size,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &pcm->streams[1].substream->dma_buffer);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto capture_alloc_err;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +
> + capture_alloc_err:
> + =A0 =A0 =A0 if (pcm->streams[0].substream)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_dma_free_pages(&pcm->streams[0].substre=
am->dma_buffer);
> + playback_alloc_err:
> + =A0 =A0 =A0 dev_err(card->dev, "Cannot allocate buffer(s)\n");
> + =A0 =A0 =A0 return -ENOMEM;
> +}
> +
> +static void psc_i2s_pcm_free(struct snd_pcm *pcm)
> +{
> + =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D pcm->private_data;
> + =A0 =A0 =A0 struct snd_pcm_substream *substream;
> + =A0 =A0 =A0 int stream;
> +
> + =A0 =A0 =A0 dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=3D%p)\n", p=
cm);
> +
> + =A0 =A0 =A0 for (stream =3D 0; stream < 2; stream++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream =3D pcm->streams[stream].substrea=
m;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_dma_free_pages(&substre=
am->dma_buffer);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream->dma_buffer.area =
=3D NULL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream->dma_buffer.addr =
=3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +}
> +
> +struct snd_soc_platform psc_i2s_pcm_soc_platform =3D {
> + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "mpc5200-psc-audio",
> + =A0 =A0 =A0 .pcm_ops =A0 =A0 =A0 =A0=3D &psc_i2s_pcm_ops,
> + =A0 =A0 =A0 .pcm_new =A0 =A0 =A0 =A0=3D &psc_i2s_pcm_new,
> + =A0 =A0 =A0 .pcm_free =A0 =A0 =A0 =3D &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 captur=
e)
> + * @active: =A0 =A0 =A0 =A0 =A0 =A0flag indicating if the stream is acti=
ve
> + * @psc_i2s: =A0 =A0 =A0 =A0 =A0 pointer back to parent psc_i2s data str=
ucture
> + * @bcom_task: =A0 =A0 =A0 =A0 bestcomm task structure
> + * @irq: =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq number for bestcomm task
> + * @period_start: =A0 =A0 =A0physical address of start of DMA region
> + * @period_end: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0physical address of end o=
f DMA region
> + * @period_next_pt: =A0 =A0physical address of next DMA buffer to enqueu=
e
> + * @period_bytes: =A0 =A0 =A0size of DMA period in bytes
> + */
> +struct psc_i2s_stream {
> + =A0 =A0 =A0 int active;
> + =A0 =A0 =A0 struct psc_i2s *psc_i2s;
> + =A0 =A0 =A0 struct bcom_task *bcom_task;
> + =A0 =A0 =A0 int irq;
> + =A0 =A0 =A0 struct snd_pcm_substream *stream;
> + =A0 =A0 =A0 dma_addr_t period_start;
> + =A0 =A0 =A0 dma_addr_t period_end;
> + =A0 =A0 =A0 dma_addr_t period_next_pt;
> + =A0 =A0 =A0 dma_addr_t period_current_pt;
> + =A0 =A0 =A0 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
> + * =A0 =A0 =A0 =A0with this value.
> + * @playback: Playback stream context data
> + * @capture: Capture stream context data
> + */
> +struct psc_i2s {
> + =A0 =A0 =A0 char name[32];
> + =A0 =A0 =A0 struct mpc52xx_psc __iomem *psc_regs;
> + =A0 =A0 =A0 struct mpc52xx_psc_fifo __iomem *fifo_regs;
> + =A0 =A0 =A0 unsigned int irq;
> + =A0 =A0 =A0 struct device *dev;
> + =A0 =A0 =A0 struct snd_soc_dai dai;
> + =A0 =A0 =A0 spinlock_t lock;
> + =A0 =A0 =A0 u32 sicr;
> +
> + =A0 =A0 =A0 /* per-stream data */
> + =A0 =A0 =A0 struct psc_i2s_stream playback;
> + =A0 =A0 =A0 struct psc_i2s_stream capture;
> +
> + =A0 =A0 =A0 /* Statistics */
> + =A0 =A0 =A0 struct {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int overrun_count;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int underrun_count;
> + =A0 =A0 =A0 } stats;
> +};
> +
> +
> +int psc_i2s_startup(struct snd_pcm_substream *substream,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai);
> +
> +int psc_i2s_hw_free(struct snd_pcm_substream *substream,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai);
> +
> +void psc_i2s_shutdown(struct snd_pcm_substream *substream,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_d=
ai *dai);
> +
> +int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct 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 1111c71..8974b53 100644
> --- a/sound/soc/fsl/mpc5200_psc_i2s.c
> +++ b/sound/soc/fsl/mpc5200_psc_i2s.c
> @@ -25,6 +25,8 @@
> =A0#include <sysdev/bestcomm/gen_bd.h>
> =A0#include <asm/mpc52xx_psc.h>
>
> +#include "mpc5200_dma.h"
> +
> =A0MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
> =A0MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
> =A0MODULE_LICENSE("GPL");
> @@ -47,179 +49,6 @@ MODULE_LICENSE("GPL");
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_FMTBIT_S24_BE |=
SNDRV_PCM_FMTBIT_S24_BE | \
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_FMTBIT_S32_BE)
>
> -/**
> - * psc_i2s_stream - Data specific to a single stream (playback or captur=
e)
> - * @active: =A0 =A0 =A0 =A0 =A0 =A0flag indicating if the stream is acti=
ve
> - * @psc_i2s: =A0 =A0 =A0 =A0 =A0 pointer back to parent psc_i2s data str=
ucture
> - * @bcom_task: =A0 =A0 =A0 =A0 bestcomm task structure
> - * @irq: =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq number for bestcomm task
> - * @period_start: =A0 =A0 =A0physical address of start of DMA region
> - * @period_end: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0physical address of end o=
f DMA region
> - * @period_next_pt: =A0 =A0physical address of next DMA buffer to enqueu=
e
> - * @period_bytes: =A0 =A0 =A0size of DMA period in bytes
> - */
> -struct psc_i2s_stream {
> - =A0 =A0 =A0 int active;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s;
> - =A0 =A0 =A0 struct bcom_task *bcom_task;
> - =A0 =A0 =A0 int irq;
> - =A0 =A0 =A0 struct snd_pcm_substream *stream;
> - =A0 =A0 =A0 dma_addr_t period_start;
> - =A0 =A0 =A0 dma_addr_t period_end;
> - =A0 =A0 =A0 dma_addr_t period_next_pt;
> - =A0 =A0 =A0 dma_addr_t period_current_pt;
> - =A0 =A0 =A0 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
> - * =A0 =A0 =A0 =A0with this value.
> - * @playback: Playback stream context data
> - * @capture: Capture stream context data
> - */
> -struct psc_i2s {
> - =A0 =A0 =A0 char name[32];
> - =A0 =A0 =A0 struct mpc52xx_psc __iomem *psc_regs;
> - =A0 =A0 =A0 struct mpc52xx_psc_fifo __iomem *fifo_regs;
> - =A0 =A0 =A0 unsigned int irq;
> - =A0 =A0 =A0 struct device *dev;
> - =A0 =A0 =A0 struct snd_soc_dai dai;
> - =A0 =A0 =A0 spinlock_t lock;
> - =A0 =A0 =A0 u32 sicr;
> -
> - =A0 =A0 =A0 /* per-stream data */
> - =A0 =A0 =A0 struct psc_i2s_stream playback;
> - =A0 =A0 =A0 struct psc_i2s_stream capture;
> -
> - =A0 =A0 =A0 /* Statistics */
> - =A0 =A0 =A0 struct {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 int overrun_count;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 int underrun_count;
> - =A0 =A0 =A0 } stats;
> -};
> -
> -/*
> - * Interrupt handlers
> - */
> -static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
> -{
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D _psc_i2s;
> - =A0 =A0 =A0 struct mpc52xx_psc __iomem *regs =3D psc_i2s->psc_regs;
> - =A0 =A0 =A0 u16 isr;
> -
> - =A0 =A0 =A0 isr =3D in_be16(®s->mpc52xx_psc_isr);
> -
> - =A0 =A0 =A0 /* Playback underrun error */
> - =A0 =A0 =A0 if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEM=
P))
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s->stats.underrun_count++;
> -
> - =A0 =A0 =A0 /* Capture overrun error */
> - =A0 =A0 =A0 if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR=
))
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s->stats.overrun_count++;
> -
> - =A0 =A0 =A0 out_8(®s->command, 4 << 4); =A0/* reset the error status=
*/
> -
> - =A0 =A0 =A0 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 i=
n
> - * the queue. =A0Otherwise the enqueue will fail and the audio ring buff=
er
> - * will get out of sync
> - */
> -static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
> -{
> - =A0 =A0 =A0 struct bcom_bd *bd;
> -
> - =A0 =A0 =A0 /* Prepare and enqueue the next buffer descriptor */
> - =A0 =A0 =A0 bd =3D bcom_prepare_next_buffer(s->bcom_task);
> - =A0 =A0 =A0 bd->status =3D s->period_bytes;
> - =A0 =A0 =A0 bd->data[0] =3D s->period_next_pt;
> - =A0 =A0 =A0 bcom_submit_next_buffer(s->bcom_task, NULL);
> -
> - =A0 =A0 =A0 /* Update for next period */
> - =A0 =A0 =A0 s->period_next_pt +=3D s->period_bytes;
> - =A0 =A0 =A0 if (s->period_next_pt >=3D s->period_end)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_next_pt =3D s->period_start;
> -}
> -
> -/* Bestcomm DMA irq handler */
> -static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
> -{
> - =A0 =A0 =A0 struct psc_i2s_stream *s =3D _psc_i2s_stream;
> -
> - =A0 =A0 =A0 /* For each finished period, dequeue the completed period b=
uffer
> - =A0 =A0 =A0 =A0* and enqueue a new one in it's place. */
> - =A0 =A0 =A0 while (bcom_buffer_done(s->bcom_task)) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_retrieve_buffer(s->bcom_task, NULL, NU=
LL);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt +=3D s->period_bytes;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (s->period_current_pt >=3D s->period_end=
)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt =3D s-=
>period_start;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s_bcom_enqueue_next_buffer(s);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_enable(s->bcom_task);
> - =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 /* If the stream is active, then also inform the PCM middle=
layer
> - =A0 =A0 =A0 =A0* of the period finished event. */
> - =A0 =A0 =A0 if (s->active)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_pcm_period_elapsed(s->stream);
> -
> - =A0 =A0 =A0 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,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> - =A0 =A0 =A0 int rc;
> -
> - =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=3D%p)\n", =
substream);
> -
> - =A0 =A0 =A0 if (!psc_i2s->playback.active &&
> - =A0 =A0 =A0 =A0 =A0 !psc_i2s->capture.active) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Setup the IRQs */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D request_irq(psc_i2s->irq, &psc_i2s_s=
tatus_irq, IRQF_SHARED,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"psc-i2s=
-status", psc_i2s);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc |=3D request_irq(psc_i2s->capture.irq,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &psc_i2=
s_bcom_irq, IRQF_SHARED,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "psc-i2=
s-capture", &psc_i2s->capture);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc |=3D request_irq(psc_i2s->playback.irq,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &psc_i2=
s_bcom_irq, IRQF_SHARED,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "psc-i2=
s-playback", &psc_i2s->playback);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->irq, psc_=
i2s);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->capture.i=
rq,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&psc_i2s=
->capture);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->playback.=
irq,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&psc_i2s=
->playback);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> - =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 return 0;
> -}
> -
> =A0static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct sn=
d_pcm_hw_params *params,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct sn=
d_soc_dai *dai)
> @@ -258,164 +87,6 @@ static int psc_i2s_hw_params(struct snd_pcm_substrea=
m *substream,
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> -static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> -{
> - =A0 =A0 =A0 snd_pcm_set_runtime_buffer(substream, NULL);
> - =A0 =A0 =A0 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,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_dai *=
dai)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> - =A0 =A0 =A0 struct snd_pcm_runtime *runtime =3D substream->runtime;
> - =A0 =A0 =A0 struct psc_i2s_stream *s;
> - =A0 =A0 =A0 struct mpc52xx_psc __iomem *regs =3D psc_i2s->psc_regs;
> - =A0 =A0 =A0 u16 imr;
> - =A0 =A0 =A0 u8 psc_cmd;
> - =A0 =A0 =A0 unsigned long flags;
> -
> - =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> - =A0 =A0 =A0 else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> -
> - =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=3D%p, cmd=
=3D%i)"
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 " stream_id=3D%i\n",
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream, cmd, substream->pstr->stream);
> -
> - =A0 =A0 =A0 switch (cmd) {
> - =A0 =A0 =A0 case SNDRV_PCM_TRIGGER_START:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_bytes =3D frames_to_bytes(runtime=
,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 runtime->period_size);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_start =3D virt_to_phys(runtime->d=
ma_area);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_end =3D s->period_start +
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (s->period_=
bytes * runtime->periods);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_next_pt =3D s->period_start;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->period_current_pt =3D s->period_start;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->active =3D 1;
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* First; reset everything */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_RX);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_ERR_STAT);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_TX);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, MPC52=
xx_PSC_RST_ERR_STAT);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Next, fill up the bestcomm bd queue and =
enable DMA.
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* This will begin filling the PSC's fifo=
. */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_gen_bd_rx_reset(s->bco=
m_task);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_gen_bd_tx_reset(s->bco=
m_task);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (!bcom_queue_full(s->bcom_task))
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_i2s_bcom_enqueue_next_b=
uffer(s);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_enable(s->bcom_task);
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Due to errata in the i2s mode; need to l=
ine up enabling
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the transmitter with a transition on t=
he frame sync
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* line */
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(&psc_i2s->lock, flags);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* first make sure it is low */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 while ((in_8(®s->ipcr_acr.ipcr) & 0x80) =
!=3D 0)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* then wait for the transition to high */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 while ((in_8(®s->ipcr_acr.ipcr) & 0x80) =
=3D=3D 0)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Finally, enable the PSC.
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Receiver must always be enabled; even =
when we only want
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* transmit. =A0(see 15.3.2.3 of MPC5200B=
User's Guide) */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_cmd =3D MPC52xx_PSC_RX_ENABLE;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_PLAYBACK)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 psc_cmd |=3D MPC52xx_PSC_TX=
_ENABLE;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, psc_cmd);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&psc_i2s->lock, flag=
s);
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> -
> - =A0 =A0 =A0 case SNDRV_PCM_TRIGGER_STOP:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Turn off the PSC */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s->active =3D 0;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PC=
M_STREAM_CAPTURE) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!psc_i2s->playback.acti=
ve) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 2 << 4); =A0/* reset rx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 3 << 4); =A0/* reset tx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 4 << 4); =A0/* reset err */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, 3 << =
4); =A0/* reset tx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->command, 4 << =
4); =A0/* reset err */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!psc_i2s->capture.activ=
e)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s=
->command, 2 << 4); =A0/* reset rx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_disable(s->bcom_task);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (!bcom_queue_empty(s->bcom_task))
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bcom_retrieve_buffer(s->bco=
m_task, NULL, NULL);
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> -
> - =A0 =A0 =A0 default:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "invalid command\n");
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> - =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 /* Update interrupt enable settings */
> - =A0 =A0 =A0 imr =3D 0;
> - =A0 =A0 =A0 if (psc_i2s->playback.active)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 imr |=3D MPC52xx_PSC_IMR_TXEMP;
> - =A0 =A0 =A0 if (psc_i2s->capture.active)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 imr |=3D MPC52xx_PSC_IMR_ORERR;
> - =A0 =A0 =A0 out_be16(®s->isr_imr.imr, imr);
> -
> - =A0 =A0 =A0 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,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_soc_d=
ai *dai)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> -
> - =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=3D%p)\n",=
substream);
> -
> - =A0 =A0 =A0 /*
> - =A0 =A0 =A0 =A0* If this is the last active substream, disable the PSC =
and release
> - =A0 =A0 =A0 =A0* the IRQ.
> - =A0 =A0 =A0 =A0*/
> - =A0 =A0 =A0 if (!psc_i2s->playback.active &&
> - =A0 =A0 =A0 =A0 =A0 !psc_i2s->capture.active) {
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Disable all interrupts and reset the PSC=
*/
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0=
);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 3 << 4);=
/* reset tx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 2 << 4);=
/* reset rx */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 1 << 4);=
/* reset mode */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(&psc_i2s->psc_regs->command, 4 << 4);=
/* reset error */
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Release irqs */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->irq, psc_i2s);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->capture.irq, &psc_i2s->ca=
pture);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_irq(psc_i2s->playback.irq, &psc_i2s->p=
layback);
> - =A0 =A0 =A0 }
> -}
> -
> =A0/**
> =A0* psc_i2s_set_sysclk: set the clock frequency and direction
> =A0*
> @@ -495,158 +166,6 @@ static struct snd_soc_dai psc_i2s_dai_template =3D =
{
> =A0};
>
> =A0/* -------------------------------------------------------------------=
--
> - * 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 =3D {
> - =A0 =A0 =A0 .info =3D SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO=
_BLOCK_TRANSFER |
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 SNDRV_PCM_INFO_BATCH,
> - =A0 =A0 =A0 .formats =3D SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE =
|
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_=
FMTBIT_S32_BE,
> - =A0 =A0 =A0 .rate_min =3D 8000,
> - =A0 =A0 =A0 .rate_max =3D 48000,
> - =A0 =A0 =A0 .channels_min =3D 2,
> - =A0 =A0 =A0 .channels_max =3D 2,
> - =A0 =A0 =A0 .period_bytes_max =A0 =A0 =A0 =3D 1024 * 1024,
> - =A0 =A0 =A0 .period_bytes_min =A0 =A0 =A0 =3D 32,
> - =A0 =A0 =A0 .periods_min =A0 =A0 =A0 =A0 =A0 =A0=3D 2,
> - =A0 =A0 =A0 .periods_max =A0 =A0 =A0 =A0 =A0 =A0=3D 256,
> - =A0 =A0 =A0 .buffer_bytes_max =A0 =A0 =A0 =3D 2 * 1024 * 1024,
> - =A0 =A0 =A0 .fifo_size =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D 0,
> -};
> -
> -static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s_stream *s;
> -
> - =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=3D%p)\n",=
substream);
> -
> - =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> - =A0 =A0 =A0 else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> -
> - =A0 =A0 =A0 snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardwa=
re);
> -
> - =A0 =A0 =A0 s->stream =3D substream;
> - =A0 =A0 =A0 return 0;
> -}
> -
> -static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s_stream *s;
> -
> - =A0 =A0 =A0 dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=3D%p)\n"=
, substream);
> -
> - =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> - =A0 =A0 =A0 else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> -
> - =A0 =A0 =A0 s->stream =3D NULL;
> - =A0 =A0 =A0 return 0;
> -}
> -
> -static snd_pcm_uframes_t
> -psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D substream->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s *psc_i2s =3D rtd->dai->cpu_dai->private_data=
;
> - =A0 =A0 =A0 struct psc_i2s_stream *s;
> - =A0 =A0 =A0 dma_addr_t count;
> -
> - =A0 =A0 =A0 if (substream->pstr->stream =3D=3D SNDRV_PCM_STREAM_CAPTURE=
)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->capture;
> - =A0 =A0 =A0 else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D &psc_i2s->playback;
> -
> - =A0 =A0 =A0 count =3D s->period_current_pt - s->period_start;
> -
> - =A0 =A0 =A0 return bytes_to_frames(substream->runtime, count);
> -}
> -
> -static struct snd_pcm_ops psc_i2s_pcm_ops =3D {
> - =A0 =A0 =A0 .open =A0 =A0 =A0 =A0 =A0 =3D psc_i2s_pcm_open,
> - =A0 =A0 =A0 .close =A0 =A0 =A0 =A0 =A0=3D psc_i2s_pcm_close,
> - =A0 =A0 =A0 .ioctl =A0 =A0 =A0 =A0 =A0=3D snd_pcm_lib_ioctl,
> - =A0 =A0 =A0 .pointer =A0 =A0 =A0 =A0=3D psc_i2s_pcm_pointer,
> -};
> -
> -static u64 psc_i2s_pcm_dmamask =3D 0xffffffff;
> -static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *da=
i,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct snd_pcm *pcm)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D pcm->private_data;
> - =A0 =A0 =A0 size_t size =3D psc_i2s_pcm_hardware.buffer_bytes_max;
> - =A0 =A0 =A0 int rc =3D 0;
> -
> - =A0 =A0 =A0 dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=3D%p, dai=
=3D%p, pcm=3D%p)\n",
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 card, dai, pcm);
> -
> - =A0 =A0 =A0 if (!card->dev->dma_mask)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 card->dev->dma_mask =3D &psc_i2s_pcm_dmamas=
k;
> - =A0 =A0 =A0 if (!card->dev->coherent_dma_mask)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 card->dev->coherent_dma_mask =3D 0xffffffff=
;
> -
> - =A0 =A0 =A0 if (pcm->streams[0].substream) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D snd_dma_alloc_pages(SNDRV_DMA_TYPE_D=
EV, pcm->dev, size,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &pcm->streams[0].substream->dma_buffer);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto playback_alloc_err;
> - =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 if (pcm->streams[1].substream) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rc =3D snd_dma_alloc_pages(SNDRV_DMA_TYPE_D=
EV, pcm->dev, size,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &pcm->streams[1].substream->dma_buffer);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rc)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto capture_alloc_err;
> - =A0 =A0 =A0 }
> -
> - =A0 =A0 =A0 return 0;
> -
> - capture_alloc_err:
> - =A0 =A0 =A0 if (pcm->streams[0].substream)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_dma_free_pages(&pcm->streams[0].substre=
am->dma_buffer);
> - playback_alloc_err:
> - =A0 =A0 =A0 dev_err(card->dev, "Cannot allocate buffer(s)\n");
> - =A0 =A0 =A0 return -ENOMEM;
> -}
> -
> -static void psc_i2s_pcm_free(struct snd_pcm *pcm)
> -{
> - =A0 =A0 =A0 struct snd_soc_pcm_runtime *rtd =3D pcm->private_data;
> - =A0 =A0 =A0 struct snd_pcm_substream *substream;
> - =A0 =A0 =A0 int stream;
> -
> - =A0 =A0 =A0 dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=3D%p)\n", p=
cm);
> -
> - =A0 =A0 =A0 for (stream =3D 0; stream < 2; stream++) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream =3D pcm->streams[stream].substrea=
m;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (substream) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 snd_dma_free_pages(&substre=
am->dma_buffer);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream->dma_buffer.area =
=3D NULL;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 substream->dma_buffer.addr =
=3D 0;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> - =A0 =A0 =A0 }
> -}
> -
> -struct snd_soc_platform psc_i2s_pcm_soc_platform =3D {
> - =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "mpc5200-psc-audio",
> - =A0 =A0 =A0 .pcm_ops =A0 =A0 =A0 =A0=3D &psc_i2s_pcm_ops,
> - =A0 =A0 =A0 .pcm_new =A0 =A0 =A0 =A0=3D &psc_i2s_pcm_new,
> - =A0 =A0 =A0 .pcm_free =A0 =A0 =A0 =3D &psc_i2s_pcm_free,
> -};
> -
> -/* ---------------------------------------------------------------------
> =A0* Sysfs attributes for debugging
> =A0*/
>
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 3/9] Rename the PSC functions to DMA
2009-05-24 11:15 ` [alsa-devel] " Mark Brown
@ 2009-05-24 14:12 ` Grant Likely
0 siblings, 0 replies; 35+ messages in thread
From: Grant Likely @ 2009-05-24 14:12 UTC (permalink / raw)
To: Mark Brown; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 5:15 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Sat, May 23, 2009 at 07:13:01PM -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.
>
> I'm OK with both these refactoring patches if Grant is - Grant, if I
> could get your ack I'd like to merge these as soon as possible since
> they're very big changes and it'd cut down on review effort.
Acked-by: Grant Likely <grant.likely@secretlab.ca>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 4/9] Add a few more mpc5200 PSC defines
2009-05-23 23:13 ` [PATCH V2 4/9] Add a few more mpc5200 PSC defines Jon Smirl
@ 2009-05-24 14:13 ` Grant Likely
2009-05-24 18:00 ` Mark Brown
0 siblings, 1 reply; 35+ messages in thread
From: Grant Likely @ 2009-05-24 14:13 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel, broonie
On Sat, May 23, 2009 at 5:13 PM, Jon Smirl <jonsmirl@gmail.com> wrote:
> Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 P=
SC registers. This patch is going in via Grant's tree.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> =A00 files changed, 0 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/includ=
e/asm/mpc52xx_psc.h
> index a218da6..fb84120 100644
> --- a/arch/powerpc/include/asm/mpc52xx_psc.h
> +++ b/arch/powerpc/include/asm/mpc52xx_psc.h
> @@ -28,6 +28,10 @@
> =A0#define MPC52xx_PSC_MAXNUM =A0 =A0 6
>
> =A0/* Programmable Serial Controller (PSC) status register bits */
> +#define MPC52xx_PSC_SR_UNEX_RX 0x0001
> +#define MPC52xx_PSC_SR_DATA_VAL =A0 =A0 =A0 =A00x0002
> +#define MPC52xx_PSC_SR_DATA_OVR =A0 =A0 =A0 =A00x0004
> +#define MPC52xx_PSC_SR_CMDSEND 0x0008
> =A0#define MPC52xx_PSC_SR_CDE =A0 =A0 0x0080
> =A0#define MPC52xx_PSC_SR_RXRDY =A0 0x0100
> =A0#define MPC52xx_PSC_SR_RXFULL =A00x0200
> @@ -61,6 +65,12 @@
> =A0#define MPC52xx_PSC_RXTX_FIFO_EMPTY =A0 =A00x0001
>
> =A0/* PSC interrupt status/mask bits */
> +#define MPC52xx_PSC_IMR_UNEX_RX_SLOT 0x0001
> +#define MPC52xx_PSC_IMR_DATA_VALID =A0 =A0 0x0002
> +#define MPC52xx_PSC_IMR_DATA_OVR =A0 =A0 =A0 0x0004
> +#define MPC52xx_PSC_IMR_CMD_SEND =A0 =A0 =A0 0x0008
> +#define MPC52xx_PSC_IMR_ERROR =A0 =A0 =A0 =A0 =A00x0040
> +#define MPC52xx_PSC_IMR_DEOF =A0 =A0 =A0 =A0 =A0 0x0080
> =A0#define MPC52xx_PSC_IMR_TXRDY =A0 =A0 =A0 =A0 =A00x0100
> =A0#define MPC52xx_PSC_IMR_RXRDY =A0 =A0 =A0 =A0 =A00x0200
> =A0#define MPC52xx_PSC_IMR_DB =A0 =A0 =A0 =A0 =A0 =A0 0x0400
> @@ -117,6 +127,7 @@
> =A0#define MPC52xx_PSC_SICR_SIM_FIR =A0 =A0 =A0 =A0 =A0 =A0 =A0 (0x6 << 2=
4)
> =A0#define MPC52xx_PSC_SICR_SIM_CODEC_24 =A0 =A0 =A0 =A0 =A0(0x7 << 24)
> =A0#define MPC52xx_PSC_SICR_SIM_CODEC_32 =A0 =A0 =A0 =A0 =A0(0xf << 24)
> +#define MPC52xx_PSC_SICR_AWR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1 << 3=
0)
> =A0#define MPC52xx_PSC_SICR_GENCLK =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0(1 << 23)
> =A0#define MPC52xx_PSC_SICR_I2S =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1 <<=
22)
> =A0#define MPC52xx_PSC_SICR_CLKPOL =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0(1 << 21)
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 0/9] mpc5200 audio rework for AC97
2009-05-24 11:08 ` [alsa-devel] " Mark Brown
@ 2009-05-24 15:21 ` Jon Smirl
2009-05-24 18:35 ` Mark Brown
0 siblings, 1 reply; 35+ messages in thread
From: Jon Smirl @ 2009-05-24 15:21 UTC (permalink / raw)
To: Mark Brown; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 7:08 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> I'm not aware of any CODEC drivers which can't currently be built and
> used as modules. =A0If you mean "load via the normal device model" then
> yes, that'd be very good (and is in progress) but it's another issue and
> as I explained last time AC97 poses particular problems there.
>
I mean "load via the normal device model". For example the AC97
drivers need to be loadable by the codec id. There's no entry in
scripts/mod/file2alias.c for dynamically loading the modules. They
don't have an id_table.
My AC97 driver is detecting the codec id and printing it before trying
to access the codec driver. I can convert that to a load_module() call
when the drivers are ready.
The core needs to detect if a specific codec id can't be supported to
fall back to the generic AC97 driver.
--=20
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 1/9] Register the wm9712 DAIs on module load
2009-05-24 11:11 ` [alsa-devel] " Mark Brown
@ 2009-05-24 15:28 ` Jon Smirl
2009-05-24 15:32 ` Jon Smirl
2009-05-24 19:14 ` Mark Brown
0 siblings, 2 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-24 15:28 UTC (permalink / raw)
To: Mark Brown; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 7:11 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Sat, May 23, 2009 at 07:12:57PM -0400, Jon Smirl wrote:
>> Register the wm9712 DAIs on module load
>
>> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
>
> Why do you wish to do this - ASoC does not require or use DAI registration
> for AC97 CODECs?
>
Then what is wrong with my binding code? If I take out the
registration my bind fails.
static struct snd_soc_dai_link pcm030_fabric_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 Analog",
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
.cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
},
{
.name = "AC97",
.stream_name = "AC97 IEC958",
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
.cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
},
};
static __init int pcm030_fabric_init(void)
{
struct platform_device *pdev;
int rc;
if (!machine_is_compatible("phytec,pcm030"))
return -ENODEV;
card.platform = &mpc5200_audio_dma_platform;
card.name = "pcm030";
card.dai_link = pcm030_fabric_dai;
card.num_links = ARRAY_SIZE(pcm030_fabric_dai);
device.card = &card;
device.codec_dev = &soc_codec_dev_wm9712;
pdev = platform_device_alloc("soc-audio", 1);
if (!pdev) {
pr_err("pcm030_fabric_init: platform_device_alloc() failed\n");
return -ENODEV;
}
platform_set_drvdata(pdev, &device);
device.dev = &pdev->dev;
rc = platform_device_add(pdev);
if (rc) {
pr_err("pcm030_fabric_init: platform_device_add() failed\n");
return -ENODEV;
}
return 0;
}
Advanced Linux Sound Architecture Driver Version 1.0.19.
No device for DAI stac9766 analog
No device for DAI stac9766 IEC958
No device for DAI tas5504
irq: irq 129 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 129
irq: irq 194 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 194
irq: irq 195 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 195
mpc5200-psc-ac97 f0002000.ac97: Codec ID is 574d 4c12
ALSA device list:
No soundcards found.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 1/9] Register the wm9712 DAIs on module load
2009-05-24 15:28 ` Jon Smirl
@ 2009-05-24 15:32 ` Jon Smirl
2009-05-24 19:14 ` Mark Brown
1 sibling, 0 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-24 15:32 UTC (permalink / raw)
To: Mark Brown; +Cc: linuxppc-dev, alsa-devel
Output with SOC DEBUG turned on:
Advanced Linux Sound Architecture Driver Version 1.0.19.
No device for DAI stac9766 analog
Registered DAI 'stac9766 analog'
No device for DAI stac9766 IEC958
Registered DAI 'stac9766 IEC958'
No device for DAI tas5504
Registered DAI 'tas5504'
Registered platform 'mpc5200-psc-audio'
irq: irq 129 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 129
irq: irq 194 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 194
irq: irq 195 on host /soc5200@f0000000/interrupt-controller@500 mapped
to virtual irq 195
Registered DAI 'AC97'
Registered DAI 'SPDIF'
mpc5200-psc-ac97 f0002000.ac97: Codec ID is 574d 4c12
soc-audio soc-audio.1: DAI AC97 HiFi not registered
soc-audio soc-audio.1: Registered card 'pcm030'
ALSA device list:
No soundcards found.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 4/9] Add a few more mpc5200 PSC defines
2009-05-24 14:13 ` Grant Likely
@ 2009-05-24 18:00 ` Mark Brown
2009-05-24 18:19 ` Grant Likely
0 siblings, 1 reply; 35+ messages in thread
From: Mark Brown @ 2009-05-24 18:00 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 08:13:19AM -0600, Grant Likely wrote:
> On Sat, May 23, 2009 at 5:13 PM, Jon Smirl <jonsmirl@gmail.com> wrote:
> > Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 PSC registers. This patch is going in via Grant's tree.
> > Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
> Acked-by: Grant Likely <grant.likely@secretlab.ca>
Jon's commit log says this is going in via your tree - is that the case
or should I apply it to ASoC?
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 4/9] Add a few more mpc5200 PSC defines
2009-05-24 18:00 ` Mark Brown
@ 2009-05-24 18:19 ` Grant Likely
2009-05-24 18:32 ` [alsa-devel] " Mark Brown
0 siblings, 1 reply; 35+ messages in thread
From: Grant Likely @ 2009-05-24 18:19 UTC (permalink / raw)
To: Mark Brown; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 12:00 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Sun, May 24, 2009 at 08:13:19AM -0600, Grant Likely wrote:
>> On Sat, May 23, 2009 at 5:13 PM, Jon Smirl <jonsmirl@gmail.com> wrote:
>> > Add a few more mpc5200 PSC defines. More bit fields defines for mpc5200 PSC registers. This patch is going in via Grant's tree.
>
>> > Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
>
>> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>
> Jon's commit log says this is going in via your tree - is that the case
> or should I apply it to ASoC?
Nothing else needs it in MPC5200 land and I haven't applied it to my
tree yet. Go ahead and add it to the ASoC tree.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 4/9] Add a few more mpc5200 PSC defines
2009-05-24 18:19 ` Grant Likely
@ 2009-05-24 18:32 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 18:32 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 12:19:37PM -0600, Grant Likely wrote:
> Nothing else needs it in MPC5200 land and I haven't applied it to my
> tree yet. Go ahead and add it to the ASoC tree.
OK, applied this and the two refactoring patches - thanks!
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 0/9] mpc5200 audio rework for AC97
2009-05-24 15:21 ` Jon Smirl
@ 2009-05-24 18:35 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 18:35 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 11:21:15AM -0400, Jon Smirl wrote:
> My AC97 driver is detecting the codec id and printing it before trying
> to access the codec driver. I can convert that to a load_module() call
> when the drivers are ready.
No, your AC97 driver shouldn't be doing any of this at all - it should
be leaving any enumeration of the hardware up to the machine driver and
the core. In so far as it is standardised the process for probing AC97
is something that can be implemented in terms of the operations exported
by the DAI so it should be done in the core for all AC97 DAIs. Where
standardised probing can't work it needs to be machine specific anyway.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-23 23:13 ` [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code Jon Smirl
2009-05-24 11:26 ` [alsa-devel] " Mark Brown
@ 2009-05-24 18:55 ` Wolfram Sang
2009-05-24 20:10 ` Jon Smirl
1 sibling, 1 reply; 35+ messages in thread
From: Wolfram Sang @ 2009-05-24 18:55 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel, broonie
[-- Attachment #1: Type: text/plain, Size: 274 bytes --]
> Rewrite the mpc5200 audio DMA code to support both I2S and AC97. Make it more robust.
Why is it more robust?
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [alsa-devel] [PATCH V2 1/9] Register the wm9712 DAIs on module load
2009-05-24 15:28 ` Jon Smirl
2009-05-24 15:32 ` Jon Smirl
@ 2009-05-24 19:14 ` Mark Brown
1 sibling, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-24 19:14 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
On Sun, May 24, 2009 at 11:28:15AM -0400, Jon Smirl wrote:
> On Sun, May 24, 2009 at 7:11 AM, Mark Brown
> > Why do you wish to do this - ASoC does not require or use DAI registration
> > for AC97 CODECs?
> Then what is wrong with my binding code? If I take out the
> registration my bind fails.
It appears that the problem here is that your CPU DAI isn't marked as an
ac97 DAI by having ac97_control set so the core expects the codec to
instantiate prior to the card. Setting ac97_control ought to fix the
problem, but obviously I can't test.
When looking at problems like this it's worth taking a step back and
looking at why the existing code is the way that it is when considering
if you've found the right fix. In this case AC97 is fairly widely used
and there are also a number of WM9712 users but neither WM9712 or any of
the other AC97 CODEC drivers ever register their DAIs. That should be
an indication that either things probably should work without a change
to existing code or things are completely broken and something wider
than a change in a single driver is required in order to get things
working.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-24 18:55 ` Wolfram Sang
@ 2009-05-24 20:10 ` Jon Smirl
2009-05-24 20:35 ` Wolfram Sang
2009-05-25 7:56 ` Juergen Beisert
0 siblings, 2 replies; 35+ messages in thread
From: Jon Smirl @ 2009-05-24 20:10 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linuxppc-dev, alsa-devel, broonie
On Sun, May 24, 2009 at 2:55 PM, Wolfram Sang <w.sang@pengutronix.de> wrote=
:
>> Rewrite the mpc5200 audio DMA code to support both I2S and AC97. Make it=
more robust.
>
> Why is it more robust?
I've implemented retries for when the AC97 hardware doesn't reset on
first try. About 10% of the time both the Efika and pcm030 AC97 codecs
don't reset on first try and need to be poked multiple times. Failure
is indicated by not having the link clock start ticking. Every once in
a while even five pokes won't get the link started and I have to power
cycle.
I don't have an oscilloscope, after I get these basic drivers in maybe
someone can put a scope on this and figure out why reset is failing.
I've read the various datasheets and I believe my reset pulses have
the correct timings.
>
> --
> Pengutronix e.K. =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | Wo=
lfram Sang =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|
> Industrial Linux Solutions =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 | http://www.p=
engutronix.de/ =A0|
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (GNU/Linux)
>
> iEYEARECAAYFAkoZmA0ACgkQD27XaX1/VRvugwCgsluxfp1rJH2MVFMTH6Yqo8bX
> dnIAn1z0QRIFEUJa0XpGFE937siwf8Cy
> =3DM0wP
> -----END PGP SIGNATURE-----
>
>
--=20
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-24 20:10 ` Jon Smirl
@ 2009-05-24 20:35 ` Wolfram Sang
2009-05-25 7:56 ` Juergen Beisert
1 sibling, 0 replies; 35+ messages in thread
From: Wolfram Sang @ 2009-05-24 20:35 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel, broonie
[-- Attachment #1: Type: text/plain, Size: 1149 bytes --]
On Sun, May 24, 2009 at 04:10:52PM -0400, Jon Smirl wrote:
> On Sun, May 24, 2009 at 2:55 PM, Wolfram Sang <w.sang@pengutronix.de> wrote:
> >> Rewrite the mpc5200 audio DMA code to support both I2S and AC97. Make it more robust.
> >
> > Why is it more robust?
>
> I've implemented retries for when the AC97 hardware doesn't reset on
> first try. About 10% of the time both the Efika and pcm030 AC97 codecs
> don't reset on first try and need to be poked multiple times. Failure
> is indicated by not having the link clock start ticking. Every once in
> a while even five pokes won't get the link started and I have to power
> cycle.
>
> I don't have an oscilloscope, after I get these basic drivers in maybe
> someone can put a scope on this and figure out why reset is failing.
> I've read the various datasheets and I believe my reset pulses have
> the correct timings.
That's good to know. In fact, I think a summary of this should go into the
patch description.
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-24 20:10 ` Jon Smirl
2009-05-24 20:35 ` Wolfram Sang
@ 2009-05-25 7:56 ` Juergen Beisert
2009-05-25 10:51 ` Mark Brown
1 sibling, 1 reply; 35+ messages in thread
From: Juergen Beisert @ 2009-05-25 7:56 UTC (permalink / raw)
To: linuxppc-dev; +Cc: alsa-devel, broonie
Jon,
On Sonntag, 24. Mai 2009, Jon Smirl wrote:
> On Sun, May 24, 2009 at 2:55 PM, Wolfram Sang <w.sang@pengutronix.de> wrote:
> >> Rewrite the mpc5200 audio DMA code to support both I2S and AC97. Make it
> >> more robust.
> >
> > Why is it more robust?
>
> I've implemented retries for when the AC97 hardware doesn't reset on
> first try. About 10% of the time both the Efika and pcm030 AC97 codecs
> don't reset on first try and need to be poked multiple times. Failure
> is indicated by not having the link clock start ticking. Every once in
> a while even five pokes won't get the link started and I have to power
> cycle.
Do you know this (from our website)?
"The AC97 mixer needs a hardware patch to make it wake up from power down
mode. Download here the instruction how to do so."
http://www.pengutronix.de/oselas/bsp/phytec/download/phyCORE-MPC5200B-tiny/TN-015e_1.pdf
jbe
--
Pengutronix e.K. | Juergen Beisert |
Linux Solutions for Science and Industry | Phone: +49-8766-939 228 |
Vertretung Sued/Muenchen, Germany | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de/ |
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code
2009-05-25 7:56 ` Juergen Beisert
@ 2009-05-25 10:51 ` Mark Brown
0 siblings, 0 replies; 35+ messages in thread
From: Mark Brown @ 2009-05-25 10:51 UTC (permalink / raw)
To: Juergen Beisert; +Cc: linuxppc-dev, alsa-devel
On Mon, May 25, 2009 at 09:56:27AM +0200, Juergen Beisert wrote:
> Do you know this (from our website)?
> "The AC97 mixer needs a hardware patch to make it wake up from power down
> mode. Download here the instruction how to do so."
> http://www.pengutronix.de/oselas/bsp/phytec/download/phyCORE-MPC5200B-tiny/TN-015e_1.pdf
This modification should not be required - a warm reset should be enough
to bring the AC97 link up. The current ASoC driver for the WM9712 will
do this automatically - the expectation for non-ASoC systems is that the
warm reset will always be issued.
That said, I'd be surprised if it were the same problem given that
iterating cold resets appears to resolve whatever the issues is.
^ permalink raw reply [flat|nested] 35+ messages in thread
end of thread, other threads:[~2009-05-25 10:51 UTC | newest]
Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-05-23 23:12 [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
2009-05-23 23:12 ` [PATCH V2 1/9] Register the wm9712 DAIs on module load Jon Smirl
2009-05-24 11:11 ` [alsa-devel] " Mark Brown
2009-05-24 15:28 ` Jon Smirl
2009-05-24 15:32 ` Jon Smirl
2009-05-24 19:14 ` Mark Brown
2009-05-23 23:12 ` [PATCH V2 2/9] Basic split of mpc5200 DMA code out from mpc5200_psc_i2s Jon Smirl
2009-05-24 14:11 ` Grant Likely
2009-05-23 23:13 ` [PATCH V2 3/9] Rename the PSC functions to DMA Jon Smirl
2009-05-24 11:15 ` [alsa-devel] " Mark Brown
2009-05-24 14:12 ` Grant Likely
2009-05-23 23:13 ` [PATCH V2 4/9] Add a few more mpc5200 PSC defines Jon Smirl
2009-05-24 14:13 ` Grant Likely
2009-05-24 18:00 ` Mark Brown
2009-05-24 18:19 ` Grant Likely
2009-05-24 18:32 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 5/9] Main rewite of the mpc5200 audio DMA code Jon Smirl
2009-05-24 11:26 ` [alsa-devel] " Mark Brown
2009-05-24 18:55 ` Wolfram Sang
2009-05-24 20:10 ` Jon Smirl
2009-05-24 20:35 ` Wolfram Sang
2009-05-25 7:56 ` Juergen Beisert
2009-05-25 10:51 ` Mark Brown
2009-05-23 23:13 ` [PATCH V2 6/9] Codec for STAC9766 used on the Efika Jon Smirl
2009-05-24 11:46 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 7/9] AC97 driver for mpc5200 Jon Smirl
2009-05-24 12:10 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 8/9] Fabric bindings for STAC9766 on the Efika Jon Smirl
2009-05-24 12:12 ` [alsa-devel] " Mark Brown
2009-05-23 23:13 ` [PATCH V2 9/9] Support for AC97 on Phytec pmc030 base board Jon Smirl
2009-05-24 12:13 ` [alsa-devel] " Mark Brown
2009-05-23 23:20 ` [PATCH V2 0/9] mpc5200 audio rework for AC97 Jon Smirl
2009-05-24 11:08 ` [alsa-devel] " Mark Brown
2009-05-24 15:21 ` Jon Smirl
2009-05-24 18:35 ` Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).