summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/atmel/Kconfig13
-rw-r--r--sound/soc/atmel/Makefile4
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c240
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c401
-rw-r--r--sound/soc/atmel/atmel-pcm.c401
-rw-r--r--sound/soc/atmel/atmel-pcm.h34
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c168
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h3
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c116
-rw-r--r--sound/soc/codecs/Kconfig7
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ab8500-codec.c2
-rw-r--r--sound/soc/codecs/ak4104.c65
-rw-r--r--sound/soc/codecs/ak4535.c7
-rw-r--r--sound/soc/codecs/ak4642.c23
-rw-r--r--sound/soc/codecs/arizona.c21
-rw-r--r--sound/soc/codecs/arizona.h65
-rw-r--r--sound/soc/codecs/cs4271.c21
-rw-r--r--sound/soc/codecs/wm2200.c634
-rw-r--r--sound/soc/codecs/wm5102.c120
-rw-r--r--sound/soc/codecs/wm5110.c49
-rw-r--r--sound/soc/codecs/wm_adsp.c666
-rw-r--r--sound/soc/codecs/wm_adsp.h59
-rw-r--r--sound/soc/codecs/wmfw.h128
-rw-r--r--sound/soc/fsl/Kconfig4
-rw-r--r--sound/soc/fsl/Makefile10
-rw-r--r--sound/soc/mxs/mxs-saif.c2
27 files changed, 2584 insertions, 681 deletions
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 72b09cfd3dc3..d1b691bf8e2d 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -6,6 +6,14 @@ config SND_ATMEL_SOC
the ATMEL SSC interface. You will also need
to select the audio interfaces to support below.
+config SND_ATMEL_SOC_PDC
+ tristate
+ depends on SND_ATMEL_SOC
+
+config SND_ATMEL_SOC_DMA
+ tristate
+ depends on SND_ATMEL_SOC
+
config SND_ATMEL_SOC_SSC
tristate
depends on SND_ATMEL_SOC
@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
- depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \
- AT91_PROGRAMMABLE_CLOCKS
+ depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
+ select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_WM8731
help
@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board"
depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+ select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_TLV320AIC23
help
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index a5c0bf19da78..41967ccb6f41 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -1,8 +1,12 @@
# AT91 Platform Support
snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
+obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
# AT91 Machine Support
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
new file mode 100644
index 000000000000..30184a4a147a
--- /dev/null
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -0,0 +1,240 @@
+/*
+ * atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
+ *
+ * Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ *
+ * Based on atmel-pcm by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ * Copyright 2008 Atmel
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/atmel-ssc.h>
+#include <linux/platform_data/dma-atmel.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "atmel-pcm.h"
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 256, /* lighting DMA overhead */
+ .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
+ .periods_min = 8,
+ .periods_max = 1024, /* no limit */
+ .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
+};
+
+/**
+ * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
+ *
+ * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
+ * check if any overrun occured.
+ */
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+ struct snd_pcm_substream *substream)
+{
+ struct atmel_pcm_dma_params *prtd;
+
+ prtd = snd_dmaengine_pcm_get_data(substream);
+
+ if (ssc_sr & prtd->mask->ssc_error) {
+ if (snd_pcm_running(substream))
+ pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "underrun" : "overrun", prtd->name,
+ ssc_sr);
+
+ /* stop RX and capture: will be enabled again at restart */
+ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+ /* now drain RHR and read status to remove xrun condition */
+ ssc_readx(prtd->ssc->regs, SSC_RHR);
+ ssc_readx(prtd->ssc->regs, SSC_SR);
+ }
+}
+
+/*--------------------------------------------------------------------------*\
+ * DMAENGINE operations
+\*--------------------------------------------------------------------------*/
+static bool filter(struct dma_chan *chan, void *slave)
+{
+ struct at_dma_slave *sl = slave;
+
+ if (sl->dma_dev == chan->device->dev) {
+ chan->private = sl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct atmel_pcm_dma_params *prtd;
+ struct ssc_device *ssc;
+ struct dma_chan *dma_chan;
+ struct dma_slave_config slave_config;
+ int ret;
+
+ prtd = snd_dmaengine_pcm_get_data(substream);
+ ssc = prtd->ssc;
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ &slave_config);
+ if (ret) {
+ pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR;
+ slave_config.dst_maxburst = 1;
+ } else {
+ slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR;
+ slave_config.src_maxburst = 1;
+ }
+
+ slave_config.device_fc = false;
+
+ dma_chan = snd_dmaengine_pcm_get_chan(substream);
+ if (dmaengine_slave_config(dma_chan, &slave_config)) {
+ pr_err("atmel-pcm: failed to configure dma channel\n");
+ ret = -EBUSY;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_pcm_dma_params *prtd;
+ struct ssc_device *ssc;
+ struct at_dma_slave *sdata = NULL;
+ int ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ ssc = prtd->ssc;
+ if (ssc->pdev)
+ sdata = ssc->pdev->dev.platform_data;
+
+ ret = snd_dmaengine_pcm_open(substream, filter, sdata);
+ if (ret) {
+ pr_err("atmel-pcm: dmaengine pcm open failed\n");
+ return -EINVAL;
+ }
+
+ snd_dmaengine_pcm_set_data(substream, prtd);
+
+ ret = atmel_pcm_configure_dma(substream, params);
+ if (ret) {
+ pr_err("atmel-pcm: failed to configure dmai\n");
+ goto err;
+ }
+
+ prtd->dma_intr_handler = atmel_pcm_dma_irq;
+
+ return 0;
+err:
+ snd_dmaengine_pcm_close(substream);
+ return ret;
+}
+
+static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_pcm_dma_params *prtd;
+
+ prtd = snd_dmaengine_pcm_get_data(substream);
+
+ ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error);
+ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable);
+
+ return 0;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware);
+
+ return 0;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+ snd_dmaengine_pcm_close(substream);
+
+ return 0;
+}
+
+static struct snd_pcm_ops atmel_pcm_ops = {
+ .open = atmel_pcm_open,
+ .close = atmel_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_pcm_hw_params,
+ .prepare = atmel_pcm_dma_prepare,
+ .trigger = snd_dmaengine_pcm_trigger,
+ .pointer = snd_dmaengine_pcm_pointer_no_residue,
+ .mmap = atmel_pcm_mmap,
+};
+
+static struct snd_soc_platform_driver atmel_soc_platform = {
+ .ops = &atmel_pcm_ops,
+ .pcm_new = atmel_pcm_new,
+ .pcm_free = atmel_pcm_free,
+};
+
+int atmel_pcm_dma_platform_register(struct device *dev)
+{
+ return snd_soc_register_platform(dev, &atmel_soc_platform);
+}
+EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
+
+void atmel_pcm_dma_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_platform(dev);
+}
+EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
+
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Atmel DMA based PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
new file mode 100644
index 000000000000..6a293c713a38
--- /dev/null
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -0,0 +1,401 @@
+/*
+ * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 MontaVista Software, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "atmel-pcm.h"
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ * them against real values for AT32
+ */
+static const struct snd_pcm_hardware atmel_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct atmel_runtime_data {
+ struct atmel_pcm_dma_params *params;
+ dma_addr_t dma_buffer; /* physical address of dma buffer */
+ dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+ size_t period_size;
+
+ dma_addr_t period_ptr; /* physical address of next period */
+
+ /* PDC register save */
+ u32 pdc_xpr_save;
+ u32 pdc_xcr_save;
+ u32 pdc_xnpr_save;
+ u32 pdc_xncr_save;
+};
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+ struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ static int count;
+
+ count++;
+
+ if (ssc_sr & params->mask->ssc_endbuf) {
+ pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "underrun" : "overrun",
+ params->name, ssc_sr, count);
+
+ /* re-start the PDC */
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ }
+
+ if (ssc_sr & params->mask->ssc_endx) {
+ /* Load the PDC next pointer and counter registers */
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+ }
+
+ snd_pcm_period_elapsed(substream);
+}
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* this may get called several times by oss emulation
+ * with different params */
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
+
+ prtd->dma_buffer = runtime->dma_addr;
+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+ prtd->period_size = params_period_bytes(params);
+
+ pr_debug("atmel-pcm: "
+ "hw_params: DMA for %s initialized "
+ "(dma_bytes=%u, period_size=%u)\n",
+ prtd->params->name,
+ runtime->dma_bytes,
+ prtd->period_size);
+ return 0;
+}
+
+static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+
+ if (params != NULL) {
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->params->dma_intr_handler = NULL;
+ }
+
+ return 0;
+}
+
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+
+ ssc_writex(params->ssc->regs, SSC_IDR,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ return 0;
+}
+
+static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct atmel_runtime_data *prtd = rtd->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ int ret = 0;
+
+ pr_debug("atmel-pcm:buffer_size = %ld,"
+ "dma_area = %p, dma_bytes = %u\n",
+ rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ prtd->period_ptr += prtd->period_size;
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ pr_debug("atmel-pcm: trigger: "
+ "period_ptr=%lx, xpr=%u, "
+ "xcr=%u, xnpr=%u, xncr=%u\n",
+ (unsigned long)prtd->period_ptr,
+ ssc_readx(params->ssc->regs, params->pdc->xpr),
+ ssc_readx(params->ssc->regs, params->pdc->xcr),
+ ssc_readx(params->ssc->regs, params->pdc->xnpr),
+ ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+ ssc_writex(params->ssc->regs, SSC_IER,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_enable);
+
+ pr_debug("sr=%u imr=%u\n",
+ ssc_readx(params->ssc->regs, SSC_SR),
+ ssc_readx(params->ssc->regs, SSC_IER));
+ break; /* SNDRV_PCM_TRIGGER_START */
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t atmel_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd = runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ dma_addr_t ptr;
+ snd_pcm_uframes_t x;
+
+ ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+ x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+
+ return x;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
+
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ runtime->private_data = prtd;
+
+ out:
+ return ret;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static struct snd_pcm_ops atmel_pcm_ops = {
+ .open = atmel_pcm_open,
+ .close = atmel_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_pcm_hw_params,
+ .hw_free = atmel_pcm_hw_free,
+ .prepare = atmel_pcm_prepare,
+ .trigger = atmel_pcm_trigger,
+ .pointer = atmel_pcm_pointer,
+ .mmap = atmel_pcm_mmap,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+#ifdef CONFIG_PM
+static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct atmel_runtime_data *prtd;
+ struct atmel_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* disable the PDC and save the PDC registers */
+
+ ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+ prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+ prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+ prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+ prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+ return 0;
+}
+
+static int atmel_pcm_resume(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct atmel_runtime_data *prtd;
+ struct atmel_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* restore the PDC registers and enable the PDC */
+ ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+ ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+ return 0;
+}
+#else
+#define atmel_pcm_suspend NULL
+#define atmel_pcm_resume NULL
+#endif
+
+static struct snd_soc_platform_driver atmel_soc_platform = {
+ .ops = &atmel_pcm_ops,
+ .pcm_new = atmel_pcm_new,
+ .pcm_free = atmel_pcm_free,
+ .suspend = atmel_pcm_suspend,
+ .resume = atmel_pcm_resume,
+};
+
+int atmel_pcm_pdc_platform_register(struct device *dev)
+{
+ return snd_soc_register_platform(dev, &atmel_soc_platform);
+}
+EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
+
+void atmel_pcm_pdc_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_platform(dev);
+}
+EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
+
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index 9b84f985770e..e99f1811300a 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -32,80 +32,25 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-#include <linux/atmel-ssc.h>
-
-#include <sound/core.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
#include <sound/soc.h>
-
#include "atmel-pcm.h"
-
-/*--------------------------------------------------------------------------*\
- * Hardware definition
-\*--------------------------------------------------------------------------*/
-/* TODO: These values were taken from the AT91 platform driver, check
- * them against real values for AT32
- */
-static const struct snd_pcm_hardware atmel_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 2,
- .periods_max = 1024,
- .buffer_bytes_max = 32 * 1024,
-};
-
-
-/*--------------------------------------------------------------------------*\
- * Data types
-\*--------------------------------------------------------------------------*/
-struct atmel_runtime_data {
- struct atmel_pcm_dma_params *params;
- dma_addr_t dma_buffer; /* physical address of dma buffer */
- dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
- size_t period_size;
-
- dma_addr_t period_ptr; /* physical address of next period */
-
- /* PDC register save */
- u32 pdc_xpr_save;
- u32 pdc_xcr_save;
- u32 pdc_xnpr_save;
- u32 pdc_xncr_save;
-};
-
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = atmel_pcm_hardware.buffer_bytes_max;
+ size_t size = ATMEL_SSC_DMABUF_SIZE;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- pr_debug("atmel-pcm:"
- "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- (void *) buf->area,
- (void *) buf->addr,
- size);
+ &buf->addr, GFP_KERNEL);
+ pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
+ (void *)buf->area, (void *)buf->addr, size);
if (!buf->area)
return -ENOMEM;
@@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
buf->bytes = size;
return 0;
}
-/*--------------------------------------------------------------------------*\
- * ISR
-\*--------------------------------------------------------------------------*/
-static void atmel_pcm_dma_irq(u32 ssc_sr,
- struct snd_pcm_substream *substream)
-{
- struct atmel_runtime_data *prtd = substream->runtime->private_data;
- struct atmel_pcm_dma_params *params = prtd->params;
- static int count;
-
- count++;
-
- if (ssc_sr & params->mask->ssc_endbuf) {
- pr_warning("atmel-pcm: buffer %s on %s"
- " (SSC_SR=%#x, count=%d)\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK
- ? "underrun" : "overrun",
- params->name, ssc_sr, count);
-
- /* re-start the PDC */
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end)
- prtd->period_ptr = prtd->dma_buffer;
-
- ssc_writex(params->ssc->regs, params->pdc->xpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_enable);
- }
-
- if (ssc_sr & params->mask->ssc_endx) {
- /* Load the PDC next pointer and counter registers */
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end)
- prtd->period_ptr = prtd->dma_buffer;
-
- ssc_writex(params->ssc->regs, params->pdc->xnpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
- }
-
- snd_pcm_period_elapsed(substream);
-}
-
-
-/*--------------------------------------------------------------------------*\
- * PCM operations
-\*--------------------------------------------------------------------------*/
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct atmel_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* this may get called several times by oss emulation
- * with different params */
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
-
- prtd->dma_buffer = runtime->dma_addr;
- prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
- prtd->period_size = params_period_bytes(params);
-
- pr_debug("atmel-pcm: "
- "hw_params: DMA for %s initialized "
- "(dma_bytes=%u, period_size=%u)\n",
- prtd->params->name,
- runtime->dma_bytes,
- prtd->period_size);
- return 0;
-}
-
-static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_runtime_data *prtd = substream->runtime->private_data;
- struct atmel_pcm_dma_params *params = prtd->params;
-
- if (params != NULL) {
- ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
- params->mask->pdc_disable);
- prtd->params->dma_intr_handler = NULL;
- }
-
- return 0;
-}
-
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct atmel_runtime_data *prtd = substream->runtime->private_data;
- struct atmel_pcm_dma_params *params = prtd->params;
-
- ssc_writex(params->ssc->regs, SSC_IDR,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
- return 0;
-}
-
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_pcm_runtime *rtd = substream->runtime;
- struct atmel_runtime_data *prtd = rtd->private_data;
- struct atmel_pcm_dma_params *params = prtd->params;
- int ret = 0;
-
- pr_debug("atmel-pcm:buffer_size = %ld,"
- "dma_area = %p, dma_bytes = %u\n",
- rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- prtd->period_ptr = prtd->dma_buffer;
-
- ssc_writex(params->ssc->regs, params->pdc->xpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
-
- prtd->period_ptr += prtd->period_size;
- ssc_writex(params->ssc->regs, params->pdc->xnpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
-
- pr_debug("atmel-pcm: trigger: "
- "period_ptr=%lx, xpr=%u, "
- "xcr=%u, xnpr=%u, xncr=%u\n",
- (unsigned long)prtd->period_ptr,
- ssc_readx(params->ssc->regs, params->pdc->xpr),
- ssc_readx(params->ssc->regs, params->pdc->xcr),
- ssc_readx(params->ssc->regs, params->pdc->xnpr),
- ssc_readx(params->ssc->regs, params->pdc->xncr));
-
- ssc_writex(params->ssc->regs, SSC_IER,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
- ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
- params->mask->pdc_enable);
-
- pr_debug("sr=%u imr=%u\n",
- ssc_readx(params->ssc->regs, SSC_SR),
- ssc_readx(params->ssc->regs, SSC_IER));
- break; /* SNDRV_PCM_TRIGGER_START */
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_enable);
- break;
-
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static snd_pcm_uframes_t atmel_pcm_pointer(
- struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct atmel_runtime_data *prtd = runtime->private_data;
- struct atmel_pcm_dma_params *params = prtd->params;
- dma_addr_t ptr;
- snd_pcm_uframes_t x;
-
- ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
- x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
- if (x == runtime->buffer_size)
- x = 0;
-
- return x;
-}
-
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct atmel_runtime_data *prtd;
- int ret = 0;
-
- snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
-
- /* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- runtime->private_data = prtd;
-
- out:
- return ret;
-}
-
-static int atmel_pcm_close(struct snd_pcm_substream *substream)
-{
- struct atmel_runtime_data *prtd = substream->runtime->private_data;
-
- kfree(prtd);
- return 0;
-}
-
-static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+int atmel_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
+EXPORT_SYMBOL_GPL(atmel_pcm_mmap);
-static struct snd_pcm_ops atmel_pcm_ops = {
- .open = atmel_pcm_open,
- .close = atmel_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = atmel_pcm_hw_params,
- .hw_free = atmel_pcm_hw_free,
- .prepare = atmel_pcm_prepare,
- .trigger = atmel_pcm_trigger,
- .pointer = atmel_pcm_pointer,
- .mmap = atmel_pcm_mmap,
-};
-
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32);
-static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
+int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
ret = atmel_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
@@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- pr_debug("atmel-pcm:"
- "Allocating PCM capture DMA buffer\n");
+ pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
ret = atmel_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
@@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
out:
return ret;
}
+EXPORT_SYMBOL_GPL(atmel_pcm_new);
-static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
+void atmel_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
@@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
buf->area = NULL;
}
}
+EXPORT_SYMBOL_GPL(atmel_pcm_free);
-#ifdef CONFIG_PM
-static int atmel_pcm_suspend(struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct atmel_runtime_data *prtd;
- struct atmel_pcm_dma_params *params;
-
- if (!runtime)
- return 0;
-
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* disable the PDC and save the PDC registers */
-
- ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
-
- prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
- prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
- prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
- prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
- return 0;
-}
-
-static int atmel_pcm_resume(struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct atmel_runtime_data *prtd;
- struct atmel_pcm_dma_params *params;
-
- if (!runtime)
- return 0;
-
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* restore the PDC registers and enable the PDC */
- ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
- ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
- ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
- ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
- ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
- return 0;
-}
-#else
-#define atmel_pcm_suspend NULL
-#define atmel_pcm_resume NULL
-#endif
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
- .ops = &atmel_pcm_ops,
- .pcm_new = atmel_pcm_new,
- .pcm_free = atmel_pcm_free_dma_buffers,
- .suspend = atmel_pcm_suspend,
- .resume = atmel_pcm_resume,
-};
-
-static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
-}
-
-static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver atmel_pcm_driver = {
- .driver = {
- .name = "atmel-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = atmel_soc_platform_probe,
- .remove = __devexit_p(atmel_soc_platform_remove),
-};
-
-module_platform_driver(atmel_pcm_driver);
-
-MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
-MODULE_DESCRIPTION("Atmel PCM module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 5e0a95e64329..bb45d20e7250 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -36,6 +36,8 @@
#include <linux/atmel-ssc.h>
+#define ATMEL_SSC_DMABUF_SIZE (64 * 1024)
+
/*
* Registers and status bits that are required by the PCM driver.
*/
@@ -50,6 +52,7 @@ struct atmel_pdc_regs {
struct atmel_ssc_mask {
u32 ssc_enable; /* SSC recv/trans enable */
u32 ssc_disable; /* SSC recv/trans disable */
+ u32 ssc_error; /* SSC error conditions */
u32 ssc_endx; /* SSC ENDTX or ENDRX */
u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
u32 pdc_enable; /* PDC recv/trans enable */
@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params {
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
+int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd);
+void atmel_pcm_free(struct snd_pcm *pcm);
+int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma);
+
+#ifdef CONFIG_SND_ATMEL_SOC_PDC
+int atmel_pcm_pdc_platform_register(struct device *dev);
+void atmel_pcm_pdc_platform_unregister(struct device *dev);
+#else
+static inline int atmel_pcm_pdc_platform_register(struct device *dev)
+{
+ return 0;
+}
+static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
+{
+}
+#endif
+
+#ifdef CONFIG_SND_ATMEL_SOC_DMA
+int atmel_pcm_dma_platform_register(struct device *dev);
+void atmel_pcm_dma_platform_unregister(struct device *dev);
+#else
+static inline int atmel_pcm_dma_platform_register(struct device *dev)
+{
+ return 0;
+}
+static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
+{
+}
+#endif
+
#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 354341ec0f42..1c7663422054 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -48,11 +48,7 @@
#include "atmel_ssc_dai.h"
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES 1
-#else
#define NUM_SSC_DEVICES 3
-#endif
/*
* SSC PDC registers required by the PCM DMA engine.
@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask,
} },
-#if NUM_SSC_DEVICES == 3
{{
.name = "SSC1 PCM out",
.pdc = &pdc_tx_reg,
@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask,
} },
-#endif
};
@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
-#if NUM_SSC_DEVICES == 3
{
.name = "ssc1",
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0,
},
-#endif
};
@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
static int atmel_ssc_probe(struct snd_soc_dai *dai)
{
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
- int ret = 0;
snd_soc_dai_set_drvdata(dai, ssc_p);
- /*
- * Request SSC device
- */
- ssc_p->ssc = ssc_request(dai->id);
- if (IS_ERR(ssc_p->ssc)) {
- printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
- ret = PTR_ERR(ssc_p->ssc);
- }
-
- return ret;
-}
-
-static int atmel_ssc_remove(struct snd_soc_dai *dai)
-{
- struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
-
- ssc_free(ssc_p->ssc);
return 0;
}
@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.set_clkdiv = atmel_ssc_set_dai_clkdiv,
};
-static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
- {
- .name = "atmel-ssc-dai.0",
- .probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .ops = &atmel_ssc_dai_ops,
- },
-#if NUM_SSC_DEVICES == 3
- {
- .name = "atmel-ssc-dai.1",
+static struct snd_soc_dai_driver atmel_ssc_dai = {
.probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
- },
- {
- .name = "atmel-ssc-dai.2",
- .probe = atmel_ssc_probe,
- .remove = atmel_ssc_remove,
- .suspend = atmel_ssc_suspend,
- .resume = atmel_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = ATMEL_SSC_RATES,
- .formats = ATMEL_SSC_FORMATS,},
- .ops = &atmel_ssc_dai_ops,
- },
-#endif
};
-static __devinit int asoc_ssc_probe(struct platform_device *pdev)
+static int asoc_ssc_init(struct device *dev)
{
- BUG_ON(pdev->id < 0);
- BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
- return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
-}
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ssc_device *ssc = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = snd_soc_register_dai(dev, &atmel_ssc_dai);
+ if (ret) {
+ dev_err(dev, "Could not register DAI: %d\n", ret);
+ goto err;
+ }
+
+ if (ssc->pdata->use_dma)
+ ret = atmel_pcm_dma_platform_register(dev);
+ else
+ ret = atmel_pcm_pdc_platform_register(dev);
+
+ if (ret) {
+ dev_err(dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_dai;
+ };
-static int __devexit asoc_ssc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
return 0;
+
+err_unregister_dai:
+ snd_soc_unregister_dai(dev);
+err:
+ return ret;
}
-static struct platform_driver asoc_ssc_driver = {
- .driver = {
- .name = "atmel-ssc-dai",
- .owner = THIS_MODULE,
- },
+static void asoc_ssc_exit(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ssc_device *ssc = platform_get_drvdata(pdev);
- .probe = asoc_ssc_probe,
- .remove = __devexit_p(asoc_ssc_remove),
-};
+ if (ssc->pdata->use_dma)
+ atmel_pcm_dma_platform_unregister(dev);
+ else
+ atmel_pcm_pdc_platform_unregister(dev);
+
+ snd_soc_unregister_dai(dev);
+}
/**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use.
@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = {
int atmel_ssc_set_audio(int ssc_id)
{
struct ssc_device *ssc;
- static struct platform_device *dma_pdev;
- struct platform_device *ssc_pdev;
int ret;
- if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
- return -EINVAL;
-
- /* Allocate a dummy device for DMA if we don't have one already */
- if (!dma_pdev) {
- dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
- if (!dma_pdev)
- return -ENOMEM;
-
- ret = platform_device_add(dma_pdev);
- if (ret < 0) {
- platform_device_put(dma_pdev);
- dma_pdev = NULL;
- return ret;
- }
- }
-
- ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
- if (!ssc_pdev)
- return -ENOMEM;
-
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
- if (IS_ERR(ssc))
- pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
+ if (IS_ERR(ssc)) {
+ pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n",
PTR_ERR(ssc));
- else {
- ssc_pdev->dev.parent = &(ssc->pdev->dev);
- ssc_free(ssc);
+ return PTR_ERR(ssc);
+ } else {
+ ssc_info[ssc_id].ssc = ssc;
}
- ret = platform_device_add(ssc_pdev);
- if (ret < 0)
- platform_device_put(ssc_pdev);
+ ret = asoc_ssc_init(&ssc->pdev->dev);
return ret;
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
-module_platform_driver(asoc_ssc_driver);
+void atmel_ssc_put_audio(int ssc_id)
+{
+ struct ssc_device *ssc = ssc_info[ssc_id].ssc;
+
+ ssc_free(ssc);
+ asoc_ssc_exit(&ssc->pdev->dev);
+}
+EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
/* Module information */
MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
index 5d4f0f9b4d9a..b1f08d511495 100644
--- a/sound/soc/atmel/atmel_ssc_dai.h
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -117,6 +117,7 @@ struct atmel_ssc_info {
struct atmel_ssc_state ssc_state;
};
-int atmel_ssc_set_audio(int ssc);
+int atmel_ssc_set_audio(int ssc_id);
+void atmel_ssc_put_audio(int ssc_id);
#endif /* _AT91_SSC_DAI_H */
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index c88351488f45..0744610e53dd 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -38,6 +38,8 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
+#include <linux/pinctrl/consumer.h>
+
#include <linux/atmel-ssc.h>
#include <sound/core.h>
@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_link at91sam9g20ek_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .cpu_dai_name = "atmel-ssc-dai.0",
+ .cpu_dai_name = "at91rm9200_ssc.0",
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
- .platform_name = "atmel-pcm-audio",
+ .platform_name = "at91rm9200_ssc.0",
.codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops,
};
@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.set_bias_level = at91sam9g20ek_set_bias_level,
};
-static struct platform_device *at91sam9g20ek_snd_device;
-
-static int __init at91sam9g20ek_init(void)
+static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec_np, *cpu_np;
struct clk *pllb;
+ struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
+ struct pinctrl *pinctrl;
int ret;
- if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
- return -ENODEV;
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(&pdev->dev, "Failed to request pinctrl for mck\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ if (!np) {
+ if (!(machine_is_at91sam9g20ek() ||
+ machine_is_at91sam9g20ek_2mmc()))
+ return -ENODEV;
+ }
ret = atmel_ssc_set_audio(0);
- if (ret != 0) {
- pr_err("Failed to set SSC 0 for audio: %d\n", ret);
- return ret;
+ if (ret) {
+ dev_err(&pdev->dev, "ssc channel is not valid\n");
+ return -EINVAL;
}
/*
@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void)
clk_set_rate(mclk, MCLK_RATE);
- at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
- if (!at91sam9g20ek_snd_device) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- ret = -ENOMEM;
- goto err_mclk;
+ card->dev = &pdev->dev;
+
+ /* Parse device node info */
+ if (np) {
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card,
+ "atmel,audio-routing");
+ if (ret)
+ goto err;
+
+ /* Parse codec info */
+ at91sam9g20ek_dai.codec_name = NULL;
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "codec info missing\n");
+ return -EINVAL;
+ }
+ at91sam9g20ek_dai.codec_of_node = codec_np;
+
+ /* Parse dai and platform info */
+ at91sam9g20ek_dai.cpu_dai_name = NULL;
+ at91sam9g20ek_dai.platform_name = NULL;
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "dai and pcm info missing\n");
+ return -EINVAL;
+ }
+ at91sam9g20ek_dai.cpu_of_node = cpu_np;
+ at91sam9g20ek_dai.platform_of_node = cpu_np;
+
+ of_node_put(codec_np);
+ of_node_put(cpu_np);
}
- platform_set_drvdata(at91sam9g20ek_snd_device,
- &snd_soc_at91sam9g20ek);
-
- ret = platform_device_add(at91sam9g20ek_snd_device);
+ ret = snd_soc_register_card(card);
if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- goto err_device_add;
+ printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n");
}
return ret;
-err_device_add:
- platform_device_put(at91sam9g20ek_snd_device);
err_mclk:
clk_put(mclk);
mclk = NULL;
err:
+ atmel_ssc_put_audio(0);
return ret;
}
-static void __exit at91sam9g20ek_exit(void)
+static int __devexit at91sam9g20ek_audio_remove(struct platform_device *pdev)
{
- platform_device_unregister(at91sam9g20ek_snd_device);
- at91sam9g20ek_snd_device = NULL;
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ atmel_ssc_put_audio(0);
+ snd_soc_unregister_card(card);
clk_put(mclk);
mclk = NULL;
+
+ return 0;
}
-module_init(at91sam9g20ek_init);
-module_exit(at91sam9g20ek_exit);
+#ifdef CONFIG_OF
+static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
+#endif
+
+static struct platform_driver at91sam9g20ek_audio_driver = {
+ .driver = {
+ .name = "at91sam9g20ek-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
+ },
+ .probe = at91sam9g20ek_audio_probe,
+ .remove = __devexit_p(at91sam9g20ek_audio_remove),
+};
+
+module_platform_driver(at91sam9g20ek_audio_driver);
/* Module information */
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_ALIAS("platform:at91sam9g20ek-audio");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b92759a39361..80799639814e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -146,6 +146,13 @@ config SND_SOC_WM_HUBS
default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
+config SND_SOC_WM_ADSP
+ tristate
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM2200=y
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM2200=m
+
config SND_SOC_AB8500_CODEC
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 9bd4d95aab4f..61633d5ff3da 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -62,6 +62,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
@@ -229,6 +230,7 @@ obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index af547490b4f7..4d96090db662 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2356,7 +2356,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
-struct snd_soc_dai_driver ab8500_codec_dai[] = {
+static struct snd_soc_dai_driver ab8500_codec_dai[] = {
{
.name = "ab8500-codec-dai.0",
.id = 0,
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 31d4483245d0..4b11b82b2273 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -15,6 +15,8 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <sound/asoundef.h>
/* AK4104 registers addresses */
@@ -98,14 +100,32 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
val = 0;
switch (params_rate(params)) {
+ case 22050:
+ val |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ val |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ val |= IEC958_AES3_CON_FS_32000;
+ break;
case 44100:
val |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
val |= IEC958_AES3_CON_FS_48000;
break;
- case 32000:
- val |= IEC958_AES3_CON_FS_32000;
+ case 88200:
+ val |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ val |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ val |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ val |= IEC958_AES3_CON_FS_192000;
break;
default:
dev_err(codec->dev, "unsupported sampling rate\n");
@@ -186,6 +206,7 @@ static const struct regmap_config ak4104_regmap = {
static int ak4104_spi_probe(struct spi_device *spi)
{
+ struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
unsigned int val;
int ret;
@@ -201,49 +222,59 @@ static int ak4104_spi_probe(struct spi_device *spi)
if (ak4104 == NULL)
return -ENOMEM;
- ak4104->regmap = regmap_init_spi(spi, &ak4104_regmap);
+ ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
if (IS_ERR(ak4104->regmap)) {
ret = PTR_ERR(ak4104->regmap);
return ret;
}
+ if (np) {
+ enum of_gpio_flags flags;
+ int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
+
+ if (gpio_is_valid(gpio)) {
+ ret = devm_gpio_request_one(&spi->dev, gpio,
+ flags & OF_GPIO_ACTIVE_LOW ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+ "ak4104 reset");
+ if (ret < 0)
+ return ret;
+ }
+ }
+
/* read the 'reserved' register - according to the datasheet, it
* should contain 0x5b. Not a good way to verify the presence of
* the device, but there is no hardware ID register. */
ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
if (ret != 0)
- goto err;
- if (val != AK4104_RESERVED_VAL) {
- ret = -ENODEV;
- goto err;
- }
+ return ret;
+ if (val != AK4104_RESERVED_VAL)
+ return -ENODEV;
spi_set_drvdata(spi, ak4104);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_device_ak4104, &ak4104_dai, 1);
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- regmap_exit(ak4104->regmap);
return ret;
}
static int __devexit ak4104_spi_remove(struct spi_device *spi)
{
- struct ak4104_private *ak4101 = spi_get_drvdata(spi);
- regmap_exit(ak4101->regmap);
snd_soc_unregister_codec(&spi->dev);
return 0;
}
+static const struct of_device_id ak4104_of_match[] = {
+ { .compatible = "asahi-kasei,ak4104", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak4104_of_match);
+
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = ak4104_of_match,
},
.probe = ak4104_spi_probe,
.remove = __devexit_p(ak4104_spi_remove),
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 618fdc30f73e..fc5581063b2d 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -447,7 +447,7 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
if (ak4535 == NULL)
return -ENOMEM;
- ak4535->regmap = regmap_init_i2c(i2c, &ak4535_regmap);
+ ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
if (IS_ERR(ak4535->regmap)) {
ret = PTR_ERR(ak4535->regmap);
dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
@@ -458,18 +458,13 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ak4535, &ak4535_dai, 1);
- if (ret != 0)
- regmap_exit(ak4535->regmap);
return ret;
}
static __devexit int ak4535_i2c_remove(struct i2c_client *client)
{
- struct ak4535_priv *ak4535 = i2c_get_clientdata(client);
-
snd_soc_unregister_codec(&client->dev);
- regmap_exit(ak4535->regmap);
return 0;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index b3e24f289421..546466abb77f 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -194,12 +194,6 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
{"LINEOUT Mixer", "DACL", "DAC"},
};
-/* codec private data */
-struct ak4642_priv {
- unsigned int sysclk;
- enum snd_soc_control_type control_type;
-};
-
/*
* ak4642 register cache
*/
@@ -468,10 +462,9 @@ static int ak4642_resume(struct snd_soc_codec *codec)
static int ak4642_probe(struct snd_soc_codec *codec)
{
- struct ak4642_priv *ak4642 = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4642->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -523,21 +516,9 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
static __devinit int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct ak4642_priv *ak4642;
- int ret;
-
- ak4642 = devm_kzalloc(&i2c->dev, sizeof(struct ak4642_priv),
- GFP_KERNEL);
- if (!ak4642)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, ak4642);
- ak4642->control_type = SND_SOC_I2C;
-
- ret = snd_soc_register_codec(&i2c->dev,
+ return snd_soc_register_codec(&i2c->dev,
(struct snd_soc_codec_driver *)id->driver_data,
&ak4642_dai, 1);
- return ret;
}
static __devexit int ak4642_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 054967d8bac2..87cfaa3a6f0e 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -380,6 +380,18 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
case 49152000:
val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
+ case 67737600:
+ case 73728000:
+ val |= 4 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 90316800:
+ case 98304000:
+ val |= 5 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 135475200:
+ case 147456000:
+ val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
default:
return -EINVAL;
}
@@ -925,6 +937,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
bool ena;
int ret;
+ if (fll->fref == Fref && fll->fout == Fout)
+ return 0;
+
ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
if (ret != 0) {
arizona_fll_err(fll, "Failed to read current state: %d\n",
@@ -970,6 +985,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
if (ena)
pm_runtime_put_autosuspend(arizona->dev);
+ fll->fref = Fref;
+ fll->fout = Fout;
+
return 0;
}
@@ -1002,6 +1020,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
if (ret == 0)
arizona_fll_warn(fll, "Timed out waiting for lock\n");
+ fll->fref = Fref;
+ fll->fout = Fout;
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_set_fll);
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 36ec64946120..84c415d335bd 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -17,6 +17,8 @@
#include <sound/soc.h>
+#include "wm_adsp.h"
+
#define ARIZONA_CLK_SYSCLK 1
#define ARIZONA_CLK_ASYNCCLK 2
#define ARIZONA_CLK_OPCLK 3
@@ -46,15 +48,18 @@
#define ARIZONA_MIXER_VOL_SHIFT 1
#define ARIZONA_MIXER_VOL_WIDTH 7
-#define ARIZONA_MAX_DAI 3
+#define ARIZONA_MAX_DAI 4
+#define ARIZONA_MAX_ADSP 4
struct arizona;
+struct wm_adsp;
struct arizona_dai_priv {
int clk;
};
struct arizona_priv {
+ struct wm_adsp adsp[ARIZONA_MAX_ADSP];
struct arizona *arizona;
int sysclk;
int asyncclk;
@@ -89,19 +94,30 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
const struct snd_kcontrol_new name##_mux = \
SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+#define ARIZONA_MUX_ENUMS(name, base_reg) \
+ static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static ARIZONA_MUX_CTL_DECL(name)
+
#define ARIZONA_MIXER_ENUMS(name, base_reg) \
- static ARIZONA_MUX_ENUM_DECL(name##_in1_enum, base_reg); \
- static ARIZONA_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \
- static ARIZONA_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \
- static ARIZONA_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \
- static ARIZONA_MUX_CTL_DECL(name##_in1); \
- static ARIZONA_MUX_CTL_DECL(name##_in2); \
- static ARIZONA_MUX_CTL_DECL(name##_in3); \
- static ARIZONA_MUX_CTL_DECL(name##_in4)
+ ARIZONA_MUX_ENUMS(name##_in1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
#define ARIZONA_MUX(name, ctrl) \
SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+#define ARIZONA_MUX_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input", &name##_mux)
+
#define ARIZONA_MIXER_WIDGETS(name, name_str) \
ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
@@ -109,6 +125,19 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+ ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+ ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+ ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define ARIZONA_MUX_ROUTES(name) \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
#define ARIZONA_MIXER_ROUTES(widget, name) \
{ widget, NULL, name " Mixer" }, \
{ name " Mixer", NULL, name " Input 1" }, \
@@ -120,6 +149,22 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+#define ARIZONA_DSP_ROUTES(name) \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+ ARIZONA_MIXER_ROUTES(name, name "L"), \
+ ARIZONA_MIXER_ROUTES(name, name "R")
+
extern const struct soc_enum arizona_lhpf1_mode;
extern const struct soc_enum arizona_lhpf2_mode;
extern const struct soc_enum arizona_lhpf3_mode;
@@ -146,6 +191,8 @@ struct arizona_fll {
unsigned int vco_mult;
struct completion lock;
struct completion ok;
+ unsigned int fref;
+ unsigned int fout;
char lock_name[ARIZONA_FLL_NAME_LEN];
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index e3f0a7f3131e..6ad3878db8fc 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -474,15 +474,25 @@ static int cs4271_probe(struct snd_soc_codec *codec)
struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
int ret;
int gpio_nreset = -EINVAL;
+ int amutec_eq_bmutec = 0;
#ifdef CONFIG_OF
- if (of_match_device(cs4271_dt_ids, codec->dev))
+ if (of_match_device(cs4271_dt_ids, codec->dev)) {
gpio_nreset = of_get_named_gpio(codec->dev->of_node,
"reset-gpio", 0);
+
+ if (!of_get_property(codec->dev->of_node,
+ "cirrus,amutec-eq-bmutec", NULL))
+ amutec_eq_bmutec = 1;
+ }
#endif
- if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
- gpio_nreset = cs4271plat->gpio_nreset;
+ if (cs4271plat) {
+ if (gpio_is_valid(cs4271plat->gpio_nreset))
+ gpio_nreset = cs4271plat->gpio_nreset;
+
+ amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+ }
if (gpio_nreset >= 0)
if (devm_gpio_request(codec->dev, gpio_nreset, "CS4271 Reset"))
@@ -528,6 +538,11 @@ static int cs4271_probe(struct snd_soc_codec *codec)
/* Power-up sequence requires 85 uS */
udelay(85);
+ if (amutec_eq_bmutec)
+ snd_soc_update_bits(codec, CS4271_MODE2,
+ CS4271_MODE2_MUTECAEQUB,
+ CS4271_MODE2_MUTECAEQUB);
+
return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
ARRAY_SIZE(cs4271_snd_controls));
}
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index eab64a193989..06d4e612a164 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
@@ -32,6 +33,39 @@
#include <sound/wm2200.h>
#include "wm2200.h"
+#include "wmfw.h"
+
+#define WM2200_DSP_CONTROL_1 0x00
+#define WM2200_DSP_CONTROL_2 0x02
+#define WM2200_DSP_CONTROL_3 0x03
+#define WM2200_DSP_CONTROL_4 0x04
+#define WM2200_DSP_CONTROL_5 0x06
+#define WM2200_DSP_CONTROL_6 0x07
+#define WM2200_DSP_CONTROL_7 0x08
+#define WM2200_DSP_CONTROL_8 0x09
+#define WM2200_DSP_CONTROL_9 0x0A
+#define WM2200_DSP_CONTROL_10 0x0B
+#define WM2200_DSP_CONTROL_11 0x0C
+#define WM2200_DSP_CONTROL_12 0x0D
+#define WM2200_DSP_CONTROL_13 0x0F
+#define WM2200_DSP_CONTROL_14 0x10
+#define WM2200_DSP_CONTROL_15 0x11
+#define WM2200_DSP_CONTROL_16 0x12
+#define WM2200_DSP_CONTROL_17 0x13
+#define WM2200_DSP_CONTROL_18 0x14
+#define WM2200_DSP_CONTROL_19 0x16
+#define WM2200_DSP_CONTROL_20 0x17
+#define WM2200_DSP_CONTROL_21 0x18
+#define WM2200_DSP_CONTROL_22 0x1A
+#define WM2200_DSP_CONTROL_23 0x1B
+#define WM2200_DSP_CONTROL_24 0x1C
+#define WM2200_DSP_CONTROL_25 0x1E
+#define WM2200_DSP_CONTROL_26 0x20
+#define WM2200_DSP_CONTROL_27 0x21
+#define WM2200_DSP_CONTROL_28 0x22
+#define WM2200_DSP_CONTROL_29 0x23
+#define WM2200_DSP_CONTROL_30 0x24
+#define WM2200_DSP_CONTROL_31 0x26
/* The code assumes DCVDD is generated internally */
#define WM2200_NUM_CORE_SUPPLIES 2
@@ -64,6 +98,66 @@ struct wm2200_priv {
int sysclk;
};
+#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
+#define WM2200_DSP_SPACING 12288
+
+#define WM2200_DSP1_DM_BASE (WM2200_DSP_RANGE_BASE + (0 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_PM_BASE (WM2200_DSP_RANGE_BASE + (1 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_ZM_BASE (WM2200_DSP_RANGE_BASE + (2 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_DM_BASE (WM2200_DSP_RANGE_BASE + (3 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_PM_BASE (WM2200_DSP_RANGE_BASE + (4 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_ZM_BASE (WM2200_DSP_RANGE_BASE + (5 * WM2200_DSP_SPACING))
+
+static const struct regmap_range_cfg wm2200_ranges[] = {
+ /* DSP1 DM */
+ { .range_min = WM2200_DSP1_DM_BASE,
+ .range_max = WM2200_DSP1_DM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_3,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP1_DM_0, .window_len = 2048, },
+
+ /* DSP1 PM */
+ { .range_min = WM2200_DSP1_PM_BASE,
+ .range_max = WM2200_DSP1_PM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_2,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP1_PM_0, .window_len = 768, },
+
+ /* DSP1 ZM */
+ { .range_min = WM2200_DSP1_ZM_BASE,
+ .range_max = WM2200_DSP1_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP1_CONTROL_4,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP1_ZM_0, .window_len = 1024, },
+
+ /* DSP2 DM */
+ { .range_min = WM2200_DSP2_DM_BASE,
+ .range_max = WM2200_DSP2_DM_BASE + 4095,
+ .selector_reg = WM2200_DSP2_CONTROL_3,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP2_DM_0, .window_len = 2048, },
+
+ /* DSP2 PM */
+ { .range_min = WM2200_DSP2_PM_BASE,
+ .range_max = WM2200_DSP2_PM_BASE + 11287,
+ .selector_reg = WM2200_DSP2_CONTROL_2,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP2_PM_0, .window_len = 768, },
+
+ /* DSP2 ZM */
+ { .range_min = WM2200_DSP2_ZM_BASE,
+ .range_max = WM2200_DSP2_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP2_CONTROL_4,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
+};
+
static struct reg_default wm2200_reg_defaults[] = {
{ 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */
{ 0x0102, 0x0000 }, /* R258 - Clocking 3 */
@@ -407,6 +501,16 @@ static struct reg_default wm2200_reg_defaults[] = {
static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
switch (reg) {
case WM2200_SOFTWARE_RESET:
case WM2200_DEVICE_REVISION:
@@ -423,6 +527,16 @@ static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
static bool wm2200_readable_register(struct device *dev, unsigned int reg)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
switch (reg) {
case WM2200_SOFTWARE_RESET:
case WM2200_DEVICE_REVISION:
@@ -873,6 +987,400 @@ static int wm2200_reset(struct wm2200_priv *wm2200)
}
}
+static int wm2200_dsp_load(struct snd_soc_codec *codec, int base)
+{
+ const struct firmware *firmware;
+ struct regmap *regmap = codec->control_data;
+ unsigned int pos = 0;
+ const struct wmfw_header *header;
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+ const struct wmfw_footer *footer;
+ const struct wmfw_region *region;
+ const char *file, *region_name;
+ char *text;
+ unsigned int dm, pm, zm, reg;
+ int regions = 0;
+ int ret, offset, type;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ file = "wm2200-dsp1.wmfw";
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ file = "wm2200-dsp2.wmfw";
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&firmware, file, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request '%s'\n", file);
+ return ret;
+ }
+
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ if (pos >= firmware->size) {
+ dev_err(codec->dev, "%s: file too short, %d bytes\n",
+ file, firmware->size);
+ return -EINVAL;
+ }
+
+ header = (void*)&firmware->data[0];
+
+ if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+ dev_err(codec->dev, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ if (header->ver != 0) {
+ dev_err(codec->dev, "%s: unknown file format %d\n",
+ file, header->ver);
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(header->len) != sizeof(*header) +
+ sizeof(*adsp1_sizes) + sizeof(*footer)) {
+ dev_err(codec->dev, "%s: unexpected header length %d\n",
+ file, le32_to_cpu(header->len));
+ return -EINVAL;
+ }
+
+ if (header->core != WMFW_ADSP1) {
+ dev_err(codec->dev, "%s: invalid core %d\n",
+ file, header->core);
+ return -EINVAL;
+ }
+
+ adsp1_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp1_sizes[1]);
+
+ dev_dbg(codec->dev, "%s: %d DM, %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp1_sizes->dm),
+ le32_to_cpu(adsp1_sizes->pm), le32_to_cpu(adsp1_sizes->zm));
+
+ dev_dbg(codec->dev, "%s: timestamp %llu\n", file,
+ le64_to_cpu(footer->timestamp));
+
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*region)) {
+ region = (void *)&(firmware->data[pos]);
+ region_name = "Unknown";
+ reg = 0;
+ text = NULL;
+ offset = le32_to_cpu(region->offset) & 0xffffff;
+ type = be32_to_cpu(region->type) & 0xff;
+
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ region_name = "Firmware name";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_INFO_TEXT:
+ region_name = "Information";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "Absolute";
+ reg = offset;
+ break;
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ reg = pm + (offset * 3);
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ reg = dm + (offset * 2);
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ reg = zm + (offset * 2);
+ break;
+ default:
+ dev_warn(codec->dev,
+ "%s.%d: Unknown region type %x at %d(%x)\n",
+ file, regions, type, pos, pos);
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s.%d: %d bytes at %d in %s\n", file,
+ regions, le32_to_cpu(region->len), offset,
+ region_name);
+
+ if (text) {
+ memcpy(text, region->data, le32_to_cpu(region->len));
+ dev_info(codec->dev, "%s: %s\n", file, text);
+ kfree(text);
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, region->data,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out;
+ }
+ }
+
+ pos += le32_to_cpu(region->len) + sizeof(*region);
+ regions++;
+ }
+
+ if (pos > firmware->size)
+ dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+ file, regions, pos - firmware->size);
+
+out:
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int wm2200_setup_algs(struct snd_soc_codec *codec, int base)
+{
+ struct regmap *regmap = codec->control_data;
+ struct wmfw_adsp1_id_hdr id;
+ struct wmfw_adsp1_alg_hdr *alg;
+ size_t algs;
+ int zm, dm, pm, ret, i;
+ __be32 val;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = regmap_raw_read(regmap, dm, &id, sizeof(id));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ algs = be32_to_cpu(id.algs);
+ dev_info(codec->dev, "Firmware: %x v%d.%d.%d, %d algorithms\n",
+ be32_to_cpu(id.fw.id),
+ (be32_to_cpu(id.fw.ver) & 0xff000) >> 16,
+ (be32_to_cpu(id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(id.fw.ver) & 0xff,
+ algs);
+
+ /* Read the terminator first to validate the length */
+ ret = regmap_raw_read(regmap, dm +
+ (sizeof(id) + (algs * sizeof(*alg))) / 2,
+ &val, sizeof(val));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm list end: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (be32_to_cpu(val) != 0xbedead)
+ dev_warn(codec->dev, "Algorithm list end %x 0x%x != 0xbeadead\n",
+ (sizeof(id) + (algs * sizeof(*alg))) / 2,
+ be32_to_cpu(val));
+
+ alg = kzalloc(sizeof(*alg) * algs, GFP_KERNEL);
+ if (!alg)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(regmap, dm + (sizeof(id) / 2),
+ alg, algs * sizeof(*alg));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm list: %d\n",
+ ret);
+ goto out;
+ }
+
+ for (i = 0; i < algs; i++) {
+ dev_info(codec->dev, "%d: ID %x v%d.%d.%d\n",
+ i, be32_to_cpu(alg[i].alg.id),
+ (be32_to_cpu(alg[i].alg.ver) & 0xff000) >> 16,
+ (be32_to_cpu(alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(alg[i].alg.ver) & 0xff);
+ }
+
+out:
+ kfree(alg);
+ return ret;
+}
+
+static int wm2200_load_coeff(struct snd_soc_codec *codec, int base)
+{
+ struct regmap *regmap = codec->control_data;
+ struct wmfw_coeff_hdr *hdr;
+ struct wmfw_coeff_item *blk;
+ const struct firmware *firmware;
+ const char *file, *region_name;
+ int ret, dm, pm, zm, pos, blocks, type, offset, reg;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ file = "wm2200-dsp1.bin";
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ file = "wm2200-dsp2.bin";
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&firmware, file, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request '%s'\n", file);
+ return ret;
+ }
+
+ if (sizeof(*hdr) >= firmware->size) {
+ dev_err(codec->dev, "%s: file too short, %d bytes\n",
+ file, firmware->size);
+ return -EINVAL;
+ }
+
+ hdr = (void*)&firmware->data[0];
+ if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+ dev_err(codec->dev, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
+
+ pos = le32_to_cpu(hdr->len);
+
+ blocks = 0;
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*blk)) {
+ blk = (void*)(&firmware->data[pos]);
+
+ type = be32_to_cpu(blk->type) & 0xff;
+ offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+ dev_dbg(codec->dev, "%s.%d: %x v%d.%d.%d\n",
+ file, blocks, le32_to_cpu(blk->id),
+ (le32_to_cpu(blk->ver) >> 16) & 0xff,
+ (le32_to_cpu(blk->ver) >> 8) & 0xff,
+ le32_to_cpu(blk->ver) & 0xff);
+ dev_dbg(codec->dev, "%s.%d: %d bytes at 0x%x in %x\n",
+ file, blocks, le32_to_cpu(blk->len), offset, type);
+
+ reg = 0;
+ region_name = "Unknown";
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ case WMFW_INFO_TEXT:
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "register";
+ reg = offset;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown region type %x\n", type);
+ break;
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, blk->data,
+ le32_to_cpu(blk->len));
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "%s.%d: Failed to write to %x in %s\n",
+ file, blocks, reg, region_name);
+ }
+ }
+
+ pos += le32_to_cpu(blk->len) + sizeof(*blk);
+ blocks++;
+ }
+
+ if (pos > firmware->size)
+ dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+ file, blocks, pos - firmware->size);
+
+ return 0;
+}
+
+static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int base = w->reg - WM2200_DSP_CONTROL_30;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = wm2200_dsp_load(codec, base);
+ if (ret != 0)
+ return ret;
+
+ ret = wm2200_setup_algs(codec, base);
+ if (ret != 0)
+ return ret;
+
+ ret = wm2200_load_coeff(codec, base);
+ if (ret != 0)
+ return ret;
+
+ /* Start the core running */
+ snd_soc_update_bits(codec, w->reg,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Halt the core */
+ snd_soc_update_bits(codec, w->reg,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+ 0);
+
+ snd_soc_update_bits(codec, base + WM2200_DSP_CONTROL_19,
+ WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
@@ -880,7 +1388,7 @@ static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
static const char *wm2200_mixer_texts[] = {
"None",
"Tone Generator",
- "AEC loopback",
+ "AEC Loopback",
"IN1L",
"IN1R",
"IN2L",
@@ -976,6 +1484,20 @@ static int wm2200_mixer_values[] = {
static WM2200_MUX_CTL_DECL(name##_in3); \
static WM2200_MUX_CTL_DECL(name##_in4)
+#define WM2200_DSP_ENUMS(name, base_reg) \
+ static WM2200_MUX_ENUM_DECL(name##_aux1_enum, base_reg); \
+ static WM2200_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 1); \
+ static WM2200_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 2); \
+ static WM2200_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 3); \
+ static WM2200_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 4); \
+ static WM2200_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 5); \
+ static WM2200_MUX_CTL_DECL(name##_aux1); \
+ static WM2200_MUX_CTL_DECL(name##_aux2); \
+ static WM2200_MUX_CTL_DECL(name##_aux3); \
+ static WM2200_MUX_CTL_DECL(name##_aux4); \
+ static WM2200_MUX_CTL_DECL(name##_aux5); \
+ static WM2200_MUX_CTL_DECL(name##_aux6);
+
static const struct snd_kcontrol_new wm2200_snd_controls[] = {
SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
WM2200_IN1_OSR_SHIFT, 1, 0),
@@ -1051,6 +1573,9 @@ WM2200_MIXER_ENUMS(DSP1R, WM2200_DSP1RMIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(DSP2L, WM2200_DSP2LMIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(DSP2R, WM2200_DSP2RMIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP1, WM2200_DSP1AUX1MIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP2, WM2200_DSP2AUX1MIX_INPUT_1_SOURCE);
+
WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
@@ -1064,8 +1589,19 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
WM2200_MUX(name_str " Input 4", &name##_in4_mux), \
SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+#define WM2200_DSP_WIDGETS(name, name_str) \
+ WM2200_MIXER_WIDGETS(name##L, name_str "L"), \
+ WM2200_MIXER_WIDGETS(name##R, name_str "R"), \
+ WM2200_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ WM2200_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ WM2200_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ WM2200_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ WM2200_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ WM2200_MUX(name_str " Aux 6", &name##_aux6_mux)
+
#define WM2200_MIXER_INPUT_ROUTES(name) \
{ name, "Tone Generator", "Tone Generator" }, \
+ { name, "AEC Loopback", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
{ name, "IN2L", "IN2L PGA" }, \
@@ -1106,6 +1642,33 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
WM2200_MIXER_INPUT_ROUTES(name " Input 3"), \
WM2200_MIXER_INPUT_ROUTES(name " Input 4")
+#define WM2200_DSP_AUX_ROUTES(name) \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 6")
+
+static const char *wm2200_aec_loopback_texts[] = {
+ "OUT1L", "OUT1R", "OUT2L", "OUT2R",
+};
+
+static const struct soc_enum wm2200_aec_loopback =
+ SOC_ENUM_SINGLE(WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_SRC_SHIFT,
+ ARRAY_SIZE(wm2200_aec_loopback_texts),
+ wm2200_aec_loopback_texts);
+
+static const struct snd_kcontrol_new wm2200_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback);
+
static const struct snd_soc_dapm_widget wm2200_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", WM2200_CLOCKING_3, WM2200_SYSCLK_ENA_SHIFT, 0,
NULL, 0),
@@ -1165,8 +1728,12 @@ SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0,
NULL, 0),
-SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
-SND_SOC_DAPM_PGA_E("DSP2", SND_SOC_NOPM, 1, 0, NULL, 0, NULL, 0),
+SND_SOC_DAPM_PGA_E("DSP1", WM2200_DSP1_CONTROL_30, WM2200_DSP1_SYS_ENA_SHIFT,
+ 0, NULL, 0, wm2200_dsp_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("DSP2", WM2200_DSP2_CONTROL_30, WM2200_DSP2_SYS_ENA_SHIFT,
+ 0, NULL, 0, wm2200_dsp_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0,
WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0),
@@ -1181,6 +1748,9 @@ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 4,
SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 5,
WM2200_AUDIO_IF_1_22, WM2200_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_MUX("AEC Loopback", WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_ENA_SHIFT, 0, &wm2200_aec_loopback_mux),
+
SND_SOC_DAPM_PGA_S("OUT1L", 0, WM2200_OUTPUT_ENABLES,
WM2200_OUT1L_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("OUT1R", 0, WM2200_OUTPUT_ENABLES,
@@ -1231,10 +1801,8 @@ WM2200_MIXER_WIDGETS(EQR, "EQR"),
WM2200_MIXER_WIDGETS(LHPF1, "LHPF1"),
WM2200_MIXER_WIDGETS(LHPF2, "LHPF2"),
-WM2200_MIXER_WIDGETS(DSP1L, "DSP1L"),
-WM2200_MIXER_WIDGETS(DSP1R, "DSP1R"),
-WM2200_MIXER_WIDGETS(DSP2L, "DSP2L"),
-WM2200_MIXER_WIDGETS(DSP2R, "DSP2R"),
+WM2200_DSP_WIDGETS(DSP1, "DSP1"),
+WM2200_DSP_WIDGETS(DSP2, "DSP2"),
WM2200_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
WM2200_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
@@ -1326,11 +1894,19 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
{ "SPK", NULL, "OUT2L" },
{ "SPK", NULL, "OUT2R" },
+ { "AEC Loopback", "OUT1L", "OUT1L" },
+ { "AEC Loopback", "OUT1R", "OUT1R" },
+ { "AEC Loopback", "OUT2L", "OUT2L" },
+ { "AEC Loopback", "OUT2R", "OUT2R" },
+
WM2200_MIXER_ROUTES("DSP1", "DSP1L"),
WM2200_MIXER_ROUTES("DSP1", "DSP1R"),
WM2200_MIXER_ROUTES("DSP2", "DSP2L"),
WM2200_MIXER_ROUTES("DSP2", "DSP2R"),
+ WM2200_DSP_AUX_ROUTES("DSP1"),
+ WM2200_DSP_AUX_ROUTES("DSP2"),
+
WM2200_MIXER_ROUTES("OUT1L", "OUT1L"),
WM2200_MIXER_ROUTES("OUT1R", "OUT1R"),
WM2200_MIXER_ROUTES("OUT2L", "OUT2L"),
@@ -1968,12 +2544,15 @@ static const struct regmap_config wm2200_regmap = {
.reg_bits = 16,
.val_bits = 16,
- .max_register = WM2200_MAX_REGISTER,
+ .max_register = WM2200_MAX_REGISTER + (ARRAY_SIZE(wm2200_ranges) *
+ WM2200_DSP_SPACING),
.reg_defaults = wm2200_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
.volatile_reg = wm2200_volatile_register,
.readable_reg = wm2200_readable_register,
.cache_type = REGCACHE_RBTREE,
+ .ranges = wm2200_ranges,
+ .num_ranges = ARRAY_SIZE(wm2200_ranges),
};
static const unsigned int wm2200_dig_vu[] = {
@@ -2011,7 +2590,7 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
wm2200->dev = &i2c->dev;
init_completion(&wm2200->fll_lock);
- wm2200->regmap = regmap_init_i2c(i2c, &wm2200_regmap);
+ wm2200->regmap = devm_regmap_init_i2c(i2c, &wm2200_regmap);
if (IS_ERR(wm2200->regmap)) {
ret = PTR_ERR(wm2200->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2027,8 +2606,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
for (i = 0; i < ARRAY_SIZE(wm2200->core_supplies); i++)
wm2200->core_supplies[i].supply = wm2200_core_supply_names[i];
- ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
ret);
@@ -2044,8 +2624,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
}
if (wm2200->pdata.ldo_ena) {
- ret = gpio_request_one(wm2200->pdata.ldo_ena,
- GPIOF_OUT_INIT_HIGH, "WM2200 LDOENA");
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 LDOENA");
if (ret < 0) {
dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
wm2200->pdata.ldo_ena, ret);
@@ -2055,8 +2636,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
}
if (wm2200->pdata.reset) {
- ret = gpio_request_one(wm2200->pdata.reset,
- GPIOF_OUT_INIT_HIGH, "WM2200 /RESET");
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 /RESET");
if (ret < 0) {
dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
wm2200->pdata.reset, ret);
@@ -2166,23 +2748,16 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
err_reset:
- if (wm2200->pdata.reset) {
+ if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- gpio_free(wm2200->pdata.reset);
- }
err_ldo:
- if (wm2200->pdata.ldo_ena) {
+ if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
- gpio_free(wm2200->pdata.ldo_ena);
- }
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
err_core:
- regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
err_regmap:
- regmap_exit(wm2200->regmap);
err:
return ret;
}
@@ -2194,17 +2769,10 @@ static __devexit int wm2200_i2c_remove(struct i2c_client *i2c)
snd_soc_unregister_codec(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
- if (wm2200->pdata.reset) {
+ if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- gpio_free(wm2200->pdata.reset);
- }
- if (wm2200->pdata.ldo_ena) {
+ if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
- gpio_free(wm2200->pdata.ldo_ena);
- }
- regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
- regmap_exit(wm2200->regmap);
return 0;
}
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 7394e73fa43c..53793b1849f7 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -31,6 +31,7 @@
#include "arizona.h"
#include "wm5102.h"
+#include "wm_adsp.h"
struct wm5102_priv {
struct arizona_priv core;
@@ -42,6 +43,13 @@ static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x190000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
+};
+
static const struct reg_default wm5102_sysclk_reva_patch[] = {
{ 0x3000, 0x2225 },
{ 0x3001, 0x3a03 },
@@ -687,6 +695,9 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+
SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
@@ -708,14 +719,6 @@ ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
-SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUT1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUT2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
- ARIZONA_OUT4_OSR_SHIFT, 1, 0),
SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
ARIZONA_OUT5_OSR_SHIFT, 1, 0),
@@ -745,17 +748,6 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUTPUT_PATH_CONFIG_1R,
- ARIZONA_OUT1L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUTPUT_PATH_CONFIG_2R,
- ARIZONA_OUT2L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
-
SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
@@ -819,11 +811,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
static const char *wm5102_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
@@ -864,6 +860,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
@@ -894,9 +891,9 @@ SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
- ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
- ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
@@ -996,6 +993,8 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
+
SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
@@ -1071,10 +1070,12 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+WM_ADSP2("DSP1", 0),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1094,6 +1095,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
{ name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
@@ -1127,7 +1129,13 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "ASRC1L", "ASRC1L" }, \
{ name, "ASRC1R", "ASRC1R" }, \
{ name, "ASRC2L", "ASRC2L" }, \
- { name, "ASRC2R", "ASRC2R" }
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }
static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF2 Capture", NULL, "DBVDD2" },
@@ -1213,6 +1221,11 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "IN3L PGA", NULL, "IN3L" },
{ "IN3R PGA", NULL, "IN3R" },
+ { "ASRC1L", NULL, "ASRC1L Input" },
+ { "ASRC1R", NULL, "ASRC1R Input" },
+ { "ASRC2L", NULL, "ASRC2L Input" },
+ { "ASRC2R", NULL, "ASRC2R Input" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1255,10 +1268,12 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
- ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
- ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
- ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+ ARIZONA_MUX_ROUTES("ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R"),
+
+ ARIZONA_DSP_ROUTES("DSP1"),
{ "AEC Loopback", "HPOUT1L", "OUT1L" },
{ "AEC Loopback", "HPOUT1R", "OUT1R" },
@@ -1377,9 +1392,28 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
codec->control_data = priv->core.arizona->regmap;
- return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5102_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
}
#define WM5102_DIG_VU 0x0200
@@ -1406,6 +1440,7 @@ static unsigned int wm5102_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
.probe = wm5102_codec_probe,
+ .remove = wm5102_codec_remove,
.idle_bias_off = true,
@@ -1424,7 +1459,7 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm5102_priv *wm5102;
- int i;
+ int i, ret;
wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv),
GFP_KERNEL);
@@ -1434,6 +1469,19 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
wm5102->core.arizona = arizona;
+ wm5102->core.adsp[0].part = "wm5102";
+ wm5102->core.adsp[0].num = 1;
+ wm5102->core.adsp[0].type = WMFW_ADSP2;
+ wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
+ wm5102->core.adsp[0].dev = arizona->dev;
+ wm5102->core.adsp[0].regmap = arizona->regmap;
+ wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
+ wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+
+ ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+ if (ret != 0)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
wm5102->fll[i].vco_mult = 1;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 9211e4192f71..2a075ad00d5b 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -308,10 +308,10 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
static const char *wm5110_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
@@ -352,6 +352,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
@@ -585,10 +586,10 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -610,6 +611,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
{ name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
@@ -786,10 +788,10 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
- ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
- ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
- ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+ ARIZONA_MUX_ROUTES("ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R"),
{ "HPOUT1L", NULL, "OUT1L" },
{ "HPOUT1R", NULL, "OUT1R" },
@@ -902,9 +904,29 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
codec->control_data = priv->core.arizona->regmap;
- return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ priv->core.arizona->dapm = &codec->dapm;
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5110_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
}
#define WM5110_DIG_VU 0x0200
@@ -935,6 +957,7 @@ static unsigned int wm5110_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.probe = wm5110_codec_probe,
+ .remove = wm5110_codec_remove,
.idle_bias_off = true,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
new file mode 100644
index 000000000000..7fdb68ef384e
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.c
@@ -0,0 +1,666 @@
+/*
+ * wm_adsp.c -- Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/registers.h>
+
+#include "wm_adsp.h"
+
+#define adsp_crit(_dsp, fmt, ...) \
+ dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_err(_dsp, fmt, ...) \
+ dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_warn(_dsp, fmt, ...) \
+ dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_info(_dsp, fmt, ...) \
+ dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_dbg(_dsp, fmt, ...) \
+ dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+
+#define ADSP1_CONTROL_1 0x00
+#define ADSP1_CONTROL_2 0x02
+#define ADSP1_CONTROL_3 0x03
+#define ADSP1_CONTROL_4 0x04
+#define ADSP1_CONTROL_5 0x06
+#define ADSP1_CONTROL_6 0x07
+#define ADSP1_CONTROL_7 0x08
+#define ADSP1_CONTROL_8 0x09
+#define ADSP1_CONTROL_9 0x0A
+#define ADSP1_CONTROL_10 0x0B
+#define ADSP1_CONTROL_11 0x0C
+#define ADSP1_CONTROL_12 0x0D
+#define ADSP1_CONTROL_13 0x0F
+#define ADSP1_CONTROL_14 0x10
+#define ADSP1_CONTROL_15 0x11
+#define ADSP1_CONTROL_16 0x12
+#define ADSP1_CONTROL_17 0x13
+#define ADSP1_CONTROL_18 0x14
+#define ADSP1_CONTROL_19 0x16
+#define ADSP1_CONTROL_20 0x17
+#define ADSP1_CONTROL_21 0x18
+#define ADSP1_CONTROL_22 0x1A
+#define ADSP1_CONTROL_23 0x1B
+#define ADSP1_CONTROL_24 0x1C
+#define ADSP1_CONTROL_25 0x1E
+#define ADSP1_CONTROL_26 0x20
+#define ADSP1_CONTROL_27 0x21
+#define ADSP1_CONTROL_28 0x22
+#define ADSP1_CONTROL_29 0x23
+#define ADSP1_CONTROL_30 0x24
+#define ADSP1_CONTROL_31 0x26
+
+/*
+ * ADSP1 Control 19
+ */
+#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+
+/*
+ * ADSP1 Control 30
+ */
+#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP1_START 0x0001 /* DSP1_START */
+#define ADSP1_START_MASK 0x0001 /* DSP1_START */
+#define ADSP1_START_SHIFT 0 /* DSP1_START */
+#define ADSP1_START_WIDTH 1 /* DSP1_START */
+
+#define ADSP2_CONTROL 0
+#define ADSP2_CLOCKING 1
+#define ADSP2_STATUS1 4
+
+/*
+ * ADSP2 Control
+ */
+
+#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
+#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP2_START 0x0001 /* DSP1_START */
+#define ADSP2_START_MASK 0x0001 /* DSP1_START */
+#define ADSP2_START_SHIFT 0 /* DSP1_START */
+#define ADSP2_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * ADSP2 clocking
+ */
+#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+/*
+ * ADSP2 Status 1
+ */
+#define ADSP2_RAM_RDY 0x0001
+#define ADSP2_RAM_RDY_MASK 0x0001
+#define ADSP2_RAM_RDY_SHIFT 0
+#define ADSP2_RAM_RDY_WIDTH 1
+
+
+static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+ int type)
+{
+ int i;
+
+ for (i = 0; i < dsp->num_mems; i++)
+ if (dsp->mem[i].type == type)
+ return &dsp->mem[i];
+
+ return NULL;
+}
+
+static int wm_adsp_load(struct wm_adsp *dsp)
+{
+ const struct firmware *firmware;
+ struct regmap *regmap = dsp->regmap;
+ unsigned int pos = 0;
+ const struct wmfw_header *header;
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+ const struct wmfw_adsp2_sizes *adsp2_sizes;
+ const struct wmfw_footer *footer;
+ const struct wmfw_region *region;
+ const struct wm_adsp_region *mem;
+ const char *region_name;
+ char *file, *text;
+ unsigned int reg;
+ int regions = 0;
+ int ret, offset, type, sizes;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to request '%s'\n", file);
+ goto out;
+ }
+ ret = -EINVAL;
+
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ if (pos >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ header = (void*)&firmware->data[0];
+
+ if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ goto out_fw;
+ }
+
+ if (header->ver != 0) {
+ adsp_err(dsp, "%s: unknown file format %d\n",
+ file, header->ver);
+ goto out_fw;
+ }
+
+ if (header->core != dsp->type) {
+ adsp_err(dsp, "%s: invalid core %d != %d\n",
+ file, header->core, dsp->type);
+ goto out_fw;
+ }
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ adsp1_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp1_sizes[1]);
+ sizes = sizeof(*adsp1_sizes);
+
+ adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp1_sizes->dm),
+ le32_to_cpu(adsp1_sizes->pm),
+ le32_to_cpu(adsp1_sizes->zm));
+ break;
+
+ case WMFW_ADSP2:
+ pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
+ adsp2_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp2_sizes[1]);
+ sizes = sizeof(*adsp2_sizes);
+
+ adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp2_sizes->xm),
+ le32_to_cpu(adsp2_sizes->ym),
+ le32_to_cpu(adsp2_sizes->pm),
+ le32_to_cpu(adsp2_sizes->zm));
+ break;
+
+ default:
+ BUG_ON(NULL == "Unknown DSP type");
+ goto out_fw;
+ }
+
+ if (le32_to_cpu(header->len) != sizeof(*header) +
+ sizes + sizeof(*footer)) {
+ adsp_err(dsp, "%s: unexpected header length %d\n",
+ file, le32_to_cpu(header->len));
+ goto out_fw;
+ }
+
+ adsp_dbg(dsp, "%s: timestamp %llu\n", file,
+ le64_to_cpu(footer->timestamp));
+
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*region)) {
+ region = (void *)&(firmware->data[pos]);
+ region_name = "Unknown";
+ reg = 0;
+ text = NULL;
+ offset = le32_to_cpu(region->offset) & 0xffffff;
+ type = be32_to_cpu(region->type) & 0xff;
+ mem = wm_adsp_find_region(dsp, type);
+
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ region_name = "Firmware name";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_INFO_TEXT:
+ region_name = "Information";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "Absolute";
+ reg = offset;
+ break;
+ case WMFW_ADSP1_PM:
+ BUG_ON(!mem);
+ region_name = "PM";
+ reg = mem->base + (offset * 3);
+ break;
+ case WMFW_ADSP1_DM:
+ BUG_ON(!mem);
+ region_name = "DM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP2_XM:
+ BUG_ON(!mem);
+ region_name = "XM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP2_YM:
+ BUG_ON(!mem);
+ region_name = "YM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP1_ZM:
+ BUG_ON(!mem);
+ region_name = "ZM";
+ reg = mem->base + (offset * 2);
+ break;
+ default:
+ adsp_warn(dsp,
+ "%s.%d: Unknown region type %x at %d(%x)\n",
+ file, regions, type, pos, pos);
+ break;
+ }
+
+ adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
+ regions, le32_to_cpu(region->len), offset,
+ region_name);
+
+ if (text) {
+ memcpy(text, region->data, le32_to_cpu(region->len));
+ adsp_info(dsp, "%s: %s\n", file, text);
+ kfree(text);
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, region->data,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out_fw;
+ }
+ }
+
+ pos += le32_to_cpu(region->len) + sizeof(*region);
+ regions++;
+ }
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, regions, pos - firmware->size);
+
+out_fw:
+ release_firmware(firmware);
+out:
+ kfree(file);
+
+ return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+ struct regmap *regmap = dsp->regmap;
+ struct wmfw_coeff_hdr *hdr;
+ struct wmfw_coeff_item *blk;
+ const struct firmware *firmware;
+ const char *region_name;
+ int ret, pos, blocks, type, offset, reg;
+ char *file;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_warn(dsp, "Failed to request '%s'\n", file);
+ ret = 0;
+ goto out;
+ }
+ ret = -EINVAL;
+
+ if (sizeof(*hdr) >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ hdr = (void*)&firmware->data[0];
+ if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
+
+ pos = le32_to_cpu(hdr->len);
+
+ blocks = 0;
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*blk)) {
+ blk = (void*)(&firmware->data[pos]);
+
+ type = be32_to_cpu(blk->type) & 0xff;
+ offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+ adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
+ file, blocks, le32_to_cpu(blk->id),
+ (le32_to_cpu(blk->ver) >> 16) & 0xff,
+ (le32_to_cpu(blk->ver) >> 8) & 0xff,
+ le32_to_cpu(blk->ver) & 0xff);
+ adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
+ file, blocks, le32_to_cpu(blk->len), offset, type);
+
+ reg = 0;
+ region_name = "Unknown";
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ case WMFW_INFO_TEXT:
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "register";
+ reg = offset;
+ break;
+ default:
+ adsp_err(dsp, "Unknown region type %x\n", type);
+ break;
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, blk->data,
+ le32_to_cpu(blk->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write to %x in %s\n",
+ file, blocks, reg, region_name);
+ }
+ }
+
+ pos += le32_to_cpu(blk->len) + sizeof(*blk);
+ blocks++;
+ }
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, blocks, pos - firmware->size);
+
+out_fw:
+ release_firmware(firmware);
+out:
+ kfree(file);
+ return 0;
+}
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Start the core running */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START,
+ ADSP1_CORE_ENA | ADSP1_START);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Halt the core */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
+ ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
+
+static int wm_adsp2_ena(struct wm_adsp *dsp)
+{
+ unsigned int val;
+ int ret, count;
+
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+
+ /* Wait for the RAM to start, should be near instantaneous */
+ count = 0;
+ do {
+ ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
+ &val);
+ if (ret != 0)
+ return ret;
+ } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
+
+ if (!(val & ADSP2_RAM_RDY)) {
+ adsp_err(dsp, "Failed to start DSP RAM\n");
+ return -EBUSY;
+ }
+
+ adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+ adsp_info(dsp, "RAM ready after %d polls\n", count);
+
+ return 0;
+}
+
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ unsigned int val;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dsp->dvfs) {
+ ret = regmap_read(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING, &val);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to read clocking: %d\n", ret);
+ return ret;
+ }
+
+ if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
+ ret = regulator_enable(dsp->dvfs);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to enable supply: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(dsp->dvfs,
+ 1800000,
+ 1800000);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to raise supply: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ ret = wm_adsp2_ena(dsp);
+ if (ret != 0)
+ return ret;
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
+ if (ret != 0)
+ goto err;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA |
+ ADSP2_START, 0);
+
+ if (dsp->dvfs) {
+ ret = regulator_set_voltage(dsp->dvfs, 1200000,
+ 1800000);
+ if (ret != 0)
+ dev_warn(dsp->dev,
+ "Failed to lower supply: %d\n",
+ ret);
+
+ ret = regulator_disable(dsp->dvfs);
+ if (ret != 0)
+ dev_err(dsp->dev,
+ "Failed to enable supply: %d\n",
+ ret);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+{
+ int ret;
+
+ if (dvfs) {
+ adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
+ if (IS_ERR(adsp->dvfs)) {
+ ret = PTR_ERR(adsp->dvfs);
+ dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(adsp->dvfs);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_disable(adsp->dvfs);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
new file mode 100644
index 000000000000..ffd29a4609e2
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.h
@@ -0,0 +1,59 @@
+/*
+ * wm_adsp.h -- Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#ifndef __WM_ADSP_H
+#define __WM_ADSP_H
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wmfw.h"
+
+struct regulator;
+
+struct wm_adsp_region {
+ int type;
+ unsigned int base;
+};
+
+struct wm_adsp {
+ const char *part;
+ int num;
+ int type;
+ struct device *dev;
+ struct regmap *regmap;
+
+ int base;
+
+ const struct wm_adsp_region *mem;
+ int num_mems;
+
+ struct regulator *dvfs;
+};
+
+#define WM_ADSP1(wname, num) \
+ { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+ .shift = num, .event = wm_adsp1_event, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+#define WM_ADSP2(wname, num) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+ .shift = num, .event = wm_adsp2_event, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
new file mode 100644
index 000000000000..5632ded67fdd
--- /dev/null
+++ b/sound/soc/codecs/wmfw.h
@@ -0,0 +1,128 @@
+/*
+ * wmfw.h - Wolfson firmware format information
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#ifndef __WMFW_H
+#define __WMFW_H
+
+#include <linux/types.h>
+
+struct wmfw_header {
+ char magic[4];
+ __le32 len;
+ __le16 rev;
+ u8 core;
+ u8 ver;
+} __packed;
+
+struct wmfw_footer {
+ __le64 timestamp;
+ __le32 checksum;
+} __packed;
+
+struct wmfw_adsp1_sizes {
+ __le32 dm;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_adsp2_sizes {
+ __le32 xm;
+ __le32 ym;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_region {
+ union {
+ __be32 type;
+ __le32 offset;
+ };
+ __le32 len;
+ u8 data[];
+} __packed;
+
+struct wmfw_id_hdr {
+ __be32 core_id;
+ __be32 core_rev;
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 dm;
+ __be32 algs;
+} __packed;
+
+struct wmfw_adsp2_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+ __be32 algs;
+} __packed;
+
+struct wmfw_alg_hdr {
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 dm;
+} __packed;
+
+struct wmfw_adsp2_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+} __packed;
+
+struct wmfw_coeff_hdr {
+ u8 magic[4];
+ __le32 len;
+ __le32 ver;
+ u8 data[];
+} __packed;
+
+struct wmfw_coeff_item {
+ union {
+ __be32 type;
+ __le32 offset;
+ };
+ __le32 id;
+ __le32 ver;
+ __le32 sr;
+ __le32 len;
+ u8 data[];
+} __packed;
+
+#define WMFW_ADSP1 1
+#define WMFW_ADSP2 2
+
+#define WMFW_ABSOLUTE 0xf0
+#define WMFW_NAME_TEXT 0xfe
+#define WMFW_INFO_TEXT 0xff
+
+#define WMFW_ADSP1_PM 2
+#define WMFW_ADSP1_DM 3
+#define WMFW_ADSP1_ZM 4
+
+#define WMFW_ADSP2_PM 2
+#define WMFW_ADSP2_ZM 4
+#define WMFW_ADSP2_XM 5
+#define WMFW_ADSP2_YM 6
+
+#endif
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 4563b28bd625..aa73efa64cb4 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -98,12 +98,12 @@ config SND_SOC_IMX_PCM
tristate
config SND_SOC_IMX_PCM_FIQ
- tristate
+ bool
select FIQ
select SND_SOC_IMX_PCM
config SND_SOC_IMX_PCM_DMA
- tristate
+ bool
select SND_SOC_DMAENGINE_PCM
select SND_SOC_IMX_PCM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 5f3cf3f52ea0..c3deee897b5a 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -26,14 +26,18 @@ obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
# i.MX Platform Support
snd-soc-imx-ssi-objs := imx-ssi.o
snd-soc-imx-audmux-objs := imx-audmux.o
+snd-soc-imx-pcm-objs := imx-pcm.o
+ifneq ($(CONFIG_SND_SOC_IMX_PCM_FIQ),)
+ snd-soc-imx-pcm-objs += imx-pcm-fiq.o
+endif
+ifneq ($(CONFIG_SND_SOC_IMX_PCM_DMA),)
+ snd-soc-imx-pcm-objs += imx-pcm-dma.o
+endif
obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
-snd-soc-imx-pcm-y := imx-pcm.o
-snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
-snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index c294fbb523fc..b304e375568a 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -229,6 +229,7 @@ int mxs_saif_put_mclk(unsigned int saif_id)
saif->mclk_in_use = 0;
return 0;
}
+EXPORT_SYMBOL_GPL(mxs_saif_put_mclk);
/*
* Get MCLK and set clock rate, then enable it
@@ -282,6 +283,7 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
return 0;
}
+EXPORT_SYMBOL_GPL(mxs_saif_get_mclk);
/*
* SAIF DAI format configuration.