summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig19
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/at32/Kconfig34
-rw-r--r--sound/soc/at32/Makefile11
-rw-r--r--sound/soc/at32/at32-pcm.c491
-rw-r--r--sound/soc/at32/at32-pcm.h79
-rw-r--r--sound/soc/at32/at32-ssc.c849
-rw-r--r--sound/soc/at32/at32-ssc.h59
-rw-r--r--sound/soc/at32/playpaq_wm8510.c522
-rw-r--r--sound/soc/at91/Kconfig2
-rw-r--r--sound/soc/at91/at91-pcm.c6
-rw-r--r--sound/soc/at91/at91-ssc.c14
-rw-r--r--sound/soc/at91/at91-ssc.h2
-rw-r--r--sound/soc/at91/eti_b1_wm8731.c53
-rw-r--r--sound/soc/au1x/Kconfig32
-rw-r--r--sound/soc/au1x/Makefile13
-rw-r--r--sound/soc/au1x/dbdma2.c421
-rw-r--r--sound/soc/au1x/psc-ac97.c387
-rw-r--r--sound/soc/au1x/psc-i2s.c414
-rw-r--r--sound/soc/au1x/psc.h53
-rw-r--r--sound/soc/au1x/sample-ac97.c144
-rw-r--r--sound/soc/codecs/Kconfig22
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ac97.c31
-rw-r--r--sound/soc/codecs/ac97.h2
-rw-r--r--sound/soc/codecs/ak4535.c696
-rw-r--r--sound/soc/codecs/ak4535.h46
-rw-r--r--sound/soc/codecs/cs4270.c8
-rw-r--r--sound/soc/codecs/cs4270.h2
-rw-r--r--sound/soc/codecs/tlv320aic3x.c384
-rw-r--r--sound/soc/codecs/tlv320aic3x.h55
-rw-r--r--sound/soc/codecs/uda1380.c852
-rw-r--r--sound/soc/codecs/uda1380.h89
-rw-r--r--sound/soc/codecs/wm8510.c817
-rw-r--r--sound/soc/codecs/wm8510.h103
-rw-r--r--sound/soc/codecs/wm8731.c79
-rw-r--r--sound/soc/codecs/wm8731.h2
-rw-r--r--sound/soc/codecs/wm8750.c87
-rw-r--r--sound/soc/codecs/wm8750.h2
-rw-r--r--sound/soc/codecs/wm8753.c183
-rw-r--r--sound/soc/codecs/wm8753.h2
-rw-r--r--sound/soc/codecs/wm8990.c1626
-rw-r--r--sound/soc/codecs/wm8990.h832
-rw-r--r--sound/soc/codecs/wm9712.c53
-rw-r--r--sound/soc/codecs/wm9712.h2
-rw-r--r--sound/soc/codecs/wm9713.c79
-rw-r--r--sound/soc/codecs/wm9713.h2
-rw-r--r--sound/soc/davinci/Kconfig2
-rw-r--r--sound/soc/davinci/davinci-evm.c40
-rw-r--r--sound/soc/davinci/davinci-i2s.c16
-rw-r--r--sound/soc/davinci/davinci-i2s.h2
-rw-r--r--sound/soc/davinci/davinci-pcm.c2
-rw-r--r--sound/soc/fsl/Kconfig6
-rw-r--r--sound/soc/fsl/fsl_dma.c2
-rw-r--r--sound/soc/fsl/fsl_dma.h2
-rw-r--r--sound/soc/fsl/fsl_ssi.c24
-rw-r--r--sound/soc/fsl/fsl_ssi.h4
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c72
-rw-r--r--sound/soc/omap/Kconfig4
-rw-r--r--sound/soc/omap/n810.c106
-rw-r--r--sound/soc/omap/omap-mcbsp.c16
-rw-r--r--sound/soc/omap/omap-mcbsp.h2
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/pxa/Kconfig11
-rw-r--r--sound/soc/pxa/Makefile3
-rw-r--r--sound/soc/pxa/corgi.c70
-rw-r--r--sound/soc/pxa/em-x270.c102
-rw-r--r--sound/soc/pxa/poodle.c50
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c18
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h2
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c29
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h2
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c2
-rw-r--r--sound/soc/pxa/spitz.c91
-rw-r--r--sound/soc/pxa/tosa.c47
-rw-r--r--sound/soc/s3c24xx/Kconfig4
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c237
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c15
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h2
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c15
-rw-r--r--sound/soc/s3c24xx/s3c24xx-ac97.h2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c25
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.h2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c6
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c3
-rw-r--r--sound/soc/sh/Kconfig5
-rw-r--r--sound/soc/sh/dma-sh7760.c2
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/sh7760-ac97.c4
-rw-r--r--sound/soc/sh/ssi.c8
-rw-r--r--sound/soc/soc-core.c443
-rw-r--r--sound/soc/soc-dapm.c344
92 files changed, 10356 insertions, 1164 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 18f28ac4bfe..f743530add8 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -2,15 +2,8 @@
# SoC audio configuration
#
-menu "System on Chip audio support"
- depends on SND!=n
-
-config SND_SOC_AC97_BUS
- bool
-
-config SND_SOC
+menuconfig SND_SOC
tristate "ALSA for SoC audio support"
- depends on SND
select SND_PCM
---help---
@@ -23,8 +16,15 @@ config SND_SOC
This ASoC audio support can also be built as a module. If so, the module
will be called snd-soc-core.
+if SND_SOC
+
+config SND_SOC_AC97_BUS
+ bool
+
# All the supported Soc's
+source "sound/soc/at32/Kconfig"
source "sound/soc/at91/Kconfig"
+source "sound/soc/au1x/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
@@ -35,4 +35,5 @@ source "sound/soc/omap/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
-endmenu
+endif # SND_SOC
+
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 782db212710..933a66d3080 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,5 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
+obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
+obj-$(CONFIG_SND_SOC) += omap/ au1x/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
new file mode 100644
index 00000000000..b0765e86c08
--- /dev/null
+++ b/sound/soc/at32/Kconfig
@@ -0,0 +1,34 @@
+config SND_AT32_SOC
+ tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
+ depends on AVR32 && SND_SOC
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the AT32 SSC interface. You will also need to
+ to select the audio interfaces to support below.
+
+
+config SND_AT32_SOC_SSC
+ tristate
+
+
+
+config SND_AT32_SOC_PLAYPAQ
+ tristate "SoC Audio support for PlayPaq with WM8510"
+ depends on SND_AT32_SOC && BOARD_PLAYPAQ
+ select SND_AT32_SOC_SSC
+ select SND_SOC_WM8510
+ help
+ Say Y or M here if you want to add support for SoC audio
+ on the LRS PlayPaq.
+
+
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+ bool "Run CODEC on PlayPaq in slave mode"
+ depends on SND_AT32_SOC_PLAYPAQ
+ default n
+ help
+ Say Y if you want to run with the AT32 SSC generating the BCLK
+ and FRAME signals on the PlayPaq. Unless you want to play
+ with the AT32 as the SSC master, you probably want to say N here,
+ as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
new file mode 100644
index 00000000000..c03e55ecece
--- /dev/null
+++ b/sound/soc/at32/Makefile
@@ -0,0 +1,11 @@
+# AT32 Platform Support
+snd-soc-at32-objs := at32-pcm.o
+snd-soc-at32-ssc-objs := at32-ssc.o
+
+obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
+obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
new file mode 100644
index 00000000000..435f1daf177
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.c
@@ -0,0 +1,491 @@
+/* sound/soc/at32/at32-pcm.c
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-pcm.c to
+ * the AVR32 kernel. Thanks to Frank Mandarino for that code.
+ */
+
+#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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "at32-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 at32_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE),
+
+ .formats = SNDRV_PCM_FMTBIT_S16,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192, /* 512 frames * 16 bytes / frame */
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 32 * 1024,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct at32_runtime_data {
+ struct at32_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 */
+ int periods; /* period index of period_ptr */
+
+ /* Save PDC registers (for power management) */
+ u32 pdc_xpr_save;
+ u32 pdc_xcr_save;
+ u32 pdc_xnpr_save;
+ u32 pdc_xncr_save;
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
+ size_t size = at32_pcm_hardware.buffer_bytes_max;
+
+ dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dmabuf->dev.dev = pcm->card->dev;
+ dmabuf->private_data = NULL;
+ dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &dmabuf->addr, GFP_KERNEL);
+ pr_debug("at32_pcm: preallocate_dma_buffer: "
+ "area=%p, addr=%p, size=%ld\n",
+ (void *)dmabuf->area, (void *)dmabuf->addr, size);
+
+ if (!dmabuf->area)
+ return -ENOMEM;
+
+ dmabuf->bytes = size;
+ return 0;
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct at32_runtime_data *prtd = rtd->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+ static int count;
+
+ count++;
+ if (ssc_sr & params->mask->ssc_endbuf) {
+ pr_warning("at32-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 at32_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_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 = rtd->dai->cpu_dai->dma_data;
+ prtd->params->dma_intr_handler = at32_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("hw_params: DMA for %s initialized "
+ "(dma_bytes=%ld, period_size=%ld)\n",
+ prtd->params->name, runtime->dma_bytes, prtd->period_size);
+
+ return 0;
+}
+
+
+
+static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+ struct at32_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 at32_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+ struct at32_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 at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct at32_runtime_data *prtd = rtd->private_data;
+ struct at32_pcm_dma_params *params = prtd->params;
+ int ret = 0;
+
+ pr_debug("at32_pcm_trigger: buffer_size = %ld, "
+ "dma_area = %p, dma_bytes = %ld\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("trigger: period_ptr=%lx, xpr=%x, "
+ "xcr=%d, xnpr=%x, xncr=%d\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=%x, imr=%x\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 at32_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_runtime_data *prtd = runtime->private_data;
+ struct at32_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 at32_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct at32_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &at32_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(*prtd), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ runtime->private_data = prtd;
+
+
+out:
+ return ret;
+}
+
+
+
+static int at32_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct at32_runtime_data *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+
+static int at32_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);
+}
+
+
+
+static struct snd_pcm_ops at32_pcm_ops = {
+ .open = at32_pcm_open,
+ .close = at32_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = at32_pcm_hw_params,
+ .hw_free = at32_pcm_hw_free,
+ .prepare = at32_pcm_prepare,
+ .trigger = at32_pcm_trigger,
+ .pointer = at32_pcm_pointer,
+ .mmap = at32_pcm_mmap,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 at32_pcm_dmamask = 0xffffffff;
+
+static int at32_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &at32_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = at32_pcm_preallocate_dma_buffer(
+ pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
+ ret = at32_pcm_preallocate_dma_buffer(
+ pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+
+out:
+ return ret;
+}
+
+
+
+static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (substream == NULL)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_pcm_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at32_runtime_data *prtd;
+ struct at32_pcm_dma_params *params;
+
+ if (runtime == NULL)
+ return 0;
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* Disable the PDC and save the PDC registers */
+ ssc_writex(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 at32_pcm_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct at32_runtime_data *prtd;
+ struct at32_pcm_dma_params *params;
+
+ if (runtime == NULL)
+ 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_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+ return 0;
+}
+#else /* CONFIG_PM */
+# define at32_pcm_suspend NULL
+# define at32_pcm_resume NULL
+#endif /* CONFIG_PM */
+
+
+
+struct snd_soc_platform at32_soc_platform = {
+ .name = "at32-audio",
+ .pcm_ops = &at32_pcm_ops,
+ .pcm_new = at32_pcm_new,
+ .pcm_free = at32_pcm_free_dma_buffers,
+ .suspend = at32_pcm_suspend,
+ .resume = at32_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(at32_soc_platform);
+
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("Atmel AT32 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
new file mode 100644
index 00000000000..2a52430417d
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.h
@@ -0,0 +1,79 @@
+/* sound/soc/at32/at32-pcm.h
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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 __SOUND_SOC_AT32_AT32_PCM_H
+#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
+
+#include <linux/atmel-ssc.h>
+
+
+/*
+ * Registers and status bits that are required by the PCM driver
+ * TODO: Is ptcr really used?
+ */
+struct at32_pdc_regs {
+ u32 xpr; /* PDC RX/TX pointer */
+ u32 xcr; /* PDC RX/TX counter */
+ u32 xnpr; /* PDC next RX/TX pointer */
+ u32 xncr; /* PDC next RX/TX counter */
+ u32 ptcr; /* PDC transfer control */
+};
+
+
+
+/*
+ * SSC mask info
+ */
+struct at32_ssc_mask {
+ u32 ssc_enable; /* SSC RX/TX enable */
+ u32 ssc_disable; /* SSC RX/TX disable */
+ u32 ssc_endx; /* SSC ENDTX or ENDRX */
+ u32 ssc_endbuf; /* SSC TXBUFF or RXBUFF */
+ u32 pdc_enable; /* PDC RX/TX enable */
+ u32 pdc_disable; /* PDC RX/TX disable */
+};
+
+
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation. All fields except dma_intr_handler() are initialized
+ * by the interface. The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct at32_pcm_dma_params {
+ char *name; /* stream identifier */
+ int pdc_xfer_size; /* PDC counter increment in bytes */
+ struct ssc_device *ssc; /* SSC device for stream */
+ struct at32_pdc_regs *pdc; /* PDC register info */
+ struct at32_ssc_mask *mask; /* SSC mask info */
+ struct snd_pcm_substream *substream;
+ void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
+};
+
+
+
+/*
+ * The AT32 ASoC platform driver
+ */
+extern struct snd_soc_platform at32_soc_platform;
+
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
+
+#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
new file mode 100644
index 00000000000..4ef6492c902
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.c
@@ -0,0 +1,849 @@
+/* sound/soc/at32/at32-ssc.c
+ * ASoC platform driver for AT32 using SSC as DAI
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-ssc.c to
+ * the AVR32 kernel. Thanks to Frank Mandarino for that code.
+ */
+
+/* #define DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.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/initval.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Constants
+\*-------------------------------------------------------------------------*/
+#define NUM_SSC_DEVICES 3
+
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED 0
+#define SSC_DIR_MASK_PLAYBACK 1
+#define SSC_DIR_MASK_CAPTURE 2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS 0
+#define SSC_START_TX_RX 1
+#define SSC_START_LOW_RF 2
+#define SSC_START_HIGH_RF 3
+#define SSC_START_FALLING_RF 4
+#define SSC_START_RISING_RF 5
+#define SSC_START_LEVEL_RF 6
+#define SSC_START_EDGE_RF 7
+#define SSS_START_COMPARE_0 8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING 0
+#define SSC_CKI_RISING 1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE 0
+#define SSC_CKO_CONTINUOUS 1
+#define SSC_CKO_TRANSFER 2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV 0
+#define SSC_CKS_CLOCK 1
+#define SSC_CKS_PIN 2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE 0
+#define SSC_FSEDGE_NEGATIVE 1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE 0
+#define SSC_FSOS_NEGATIVE 1
+#define SSC_FSOS_POSITIVE 2
+#define SSC_FSOS_LOW 3
+#define SSC_FSOS_HIGH 4
+#define SSC_FSOS_TOGGLE 5
+
+#define START_DELAY 1
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Module data
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC PDC registered required by the PCM DMA engine
+ */
+static struct at32_pdc_regs pdc_tx_reg = {
+ .xpr = SSC_PDC_TPR,
+ .xcr = SSC_PDC_TCR,
+ .xnpr = SSC_PDC_TNPR,
+ .xncr = SSC_PDC_TNCR,
+};
+
+
+
+static struct at32_pdc_regs pdc_rx_reg = {
+ .xpr = SSC_PDC_RPR,
+ .xcr = SSC_PDC_RCR,
+ .xnpr = SSC_PDC_RNPR,
+ .xncr = SSC_PDC_RNCR,
+};
+
+
+
+/*
+ * SSC and PDC status bits for transmit and receive
+ */
+static struct at32_ssc_mask ssc_tx_mask = {
+ .ssc_enable = SSC_BIT(CR_TXEN),
+ .ssc_disable = SSC_BIT(CR_TXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDTX),
+ .ssc_endbuf = SSC_BIT(SR_TXBUFE),
+ .pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
+ .pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
+};
+
+
+
+static struct at32_ssc_mask ssc_rx_mask = {
+ .ssc_enable = SSC_BIT(CR_RXEN),
+ .ssc_disable = SSC_BIT(CR_RXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDRX),
+ .ssc_endbuf = SSC_BIT(SR_RXBUFF),
+ .pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
+ .pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
+};
+
+
+
+/*
+ * DMA parameters for each SSC
+ */
+static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+ {
+ {
+ .name = "SSC0 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC0 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+ {
+ {
+ .name = "SSC1 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC1 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+ {
+ {
+ .name = "SSC2 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC2 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ },
+ },
+};
+
+
+
+static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+ {
+ .name = "ssc0",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc1",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc2",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+};
+
+
+
+
+/*-------------------------------------------------------------------------*\
+ * ISR
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC interrupt handler. Passes PDC interrupts to the DMA interrupt
+ * handler in the PCM driver.
+ */
+static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
+{
+ struct at32_ssc_info *ssc_p = dev_id;
+ struct at32_pcm_dma_params *dma_params;
+ u32 ssc_sr;
+ u32 ssc_substream_mask;
+ int i;
+
+ ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
+ ssc_readl(ssc_p->ssc->regs, IMR));
+
+ /*
+ * Loop through substreams attached to this SSC. If a DMA-related
+ * interrupt occured on that substream, call the DMA interrupt
+ * handler function, if one has been registered in the dma_param
+ * structure by the PCM driver.
+ */
+ for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+ dma_params = ssc_p->dma_params[i];
+
+ if ((dma_params != NULL) &&
+ (dma_params->dma_intr_handler != NULL)) {
+ ssc_substream_mask = (dma_params->mask->ssc_endx |
+ dma_params->mask->ssc_endbuf);
+ if (ssc_sr & ssc_substream_mask) {
+ dma_params->dma_intr_handler(ssc_sr,
+ dma_params->
+ substream);
+ }
+ }
+ }
+
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup. Only that one substream allowed in each direction.
+ */
+static int at32_ssc_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ int dir_mask;
+
+ dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
+
+ spin_lock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask) {
+ spin_unlock_irq(&ssc_p->lock);
+ return -EBUSY;
+ }
+ ssc_p->dir_mask |= dir_mask;
+ spin_unlock_irq(&ssc_p->lock);
+
+ return 0;
+}
+
+
+
+/*
+ * Shutdown. Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at32_pcm_dma_params *dma_params;
+ int dir_mask;
+
+ dma_params = ssc_p->dma_params[substream->stream];
+
+ if (dma_params != NULL) {
+ ssc_writel(dma_params->ssc->regs, CR,
+ dma_params->mask->ssc_disable);
+ pr_debug("%s disabled SSC_SR=0x%08x\n",
+ (substream->stream ? "receiver" : "transmit"),
+ ssc_readl(ssc_p->ssc->regs, SR));
+
+ dma_params->ssc = NULL;
+ dma_params->substream = NULL;
+ ssc_p->dma_params[substream->stream] = NULL;
+ }
+
+
+ dir_mask = 1 << substream->stream;
+ spin_lock_irq(&ssc_p->lock);
+ ssc_p->dir_mask &= ~dir_mask;
+ if (!ssc_p->dir_mask) {
+ /* Shutdown the SSC clock */
+ pr_debug("at32-ssc: Stopping user %d clock\n",
+ ssc_p->ssc->user);
+ clk_disable(ssc_p->ssc->clk);
+
+ if (ssc_p->initialized) {
+ free_irq(ssc_p->ssc->irq, ssc_p);
+ ssc_p->initialized = 0;
+ }
+
+ /* Reset the SSC */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ /* clear the SSC dividers */
+ ssc_p->cmr_div = 0;
+ ssc_p->tcmr_period = 0;
+ ssc_p->rcmr_period = 0;
+ }
+ spin_unlock_irq(&ssc_p->lock);
+}
+
+
+
+/*
+ * Set the SSC system clock rate
+ */
+static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ /* TODO: What the heck do I do here? */
+ return 0;
+}
+
+
+
+/*
+ * Record DAI format for use by hw_params()
+ */
+static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ ssc_p->daifmt = fmt;
+ return 0;
+}
+
+
+
+/*
+ * Record SSC clock dividers for use in hw_params()
+ */
+static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ switch (div_id) {
+ case AT32_SSC_CMR_DIV:
+ /*
+ * The same master clock divider is used for both
+ * transmit and receive, so if a value has already
+ * been set, it must match this value
+ */
+ if (ssc_p->cmr_div == 0)
+ ssc_p->cmr_div = div;
+ else if (div != ssc_p->cmr_div)
+ return -EBUSY;
+ break;
+
+ case AT32_SSC_TCMR_PERIOD:
+ ssc_p->tcmr_period = div;
+ break;
+
+ case AT32_SSC_RCMR_PERIOD:
+ ssc_p->rcmr_period = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Configure the SSC
+ */
+static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int id = rtd->dai->cpu_dai->id;
+ struct at32_ssc_info *ssc_p = &ssc_info[id];
+ struct at32_pcm_dma_params *dma_params;
+ int channels, bits;
+ u32 tfmr, rfmr, tcmr, rcmr;
+ int start_event;
+ int ret;
+
+
+ /*
+ * Currently, there is only one set of dma_params for each direction.
+ * If more are added, this code will have to be changed to select
+ * the proper set
+ */
+ dma_params = &ssc_dma_params[id][substream->stream];
+ dma_params->ssc = ssc_p->ssc;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[substream->stream] = dma_params;
+
+
+ /*
+ * The cpu_dai->dma_data field is only used to communicate the
+ * appropriate DMA parameters to the PCM driver's hw_params()
+ * function. It should not be used for other purposes as it
+ * is common to all substreams.
+ */
+ rtd->dai->cpu_dai->dma_data = dma_params;
+
+ channels = params_channels(params);
+
+
+ /*
+ * Determine sample size in bits and the PDC increment
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bits = 8;
+ dma_params->pdc_xfer_size = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16:
+ bits = 16;
+ dma_params->pdc_xfer_size = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24:
+ bits = 24;
+ dma_params->pdc_xfer_size = 4;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32:
+ bits = 32;
+ dma_params->pdc_xfer_size = 4;
+ break;
+
+ default:
+ pr_warning("at32-ssc: Unsupported PCM format %d",
+ params_format(params));
+ return -EINVAL;
+ }
+ pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
+ bits, dma_params->pdc_xfer_size, channels);
+
+
+ /*
+ * The SSC only supports up to 16-bit samples in I2S format, due
+ * to the size of the Frame Mode Register FSLEN field.
+ */
+ if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ if (bits > 16) {
+ pr_warning("at32-ssc: "
+ "sample size %d is too large for I2S\n",
+ bits);
+ return -EINVAL;
+ }
+
+
+ /*
+ * Compute the SSC register settings
+ */
+ switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * I2S format, SSC provides BCLK and LRS clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output on the SSC TK line
+ */
+ pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
+ rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+ SSC_BF(RCMR_STTDLY, START_DELAY) |
+ SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
+ SSC_BF(RFMR_FSLEN, bits - 1) |
+ SSC_BF(RFMR_DATNB, channels - 1) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+ SSC_BF(TCMR_STTDLY, START_DELAY) |
+ SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
+ SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+ SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
+ SSC_BF(TFMR_FSLEN, bits - 1) |
+ SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
+ SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ /*
+ * I2S format, CODEC supplies BCLK and LRC clock.
+ *
+ * The SSC transmit clock is obtained from the BCLK signal
+ * on the TK line, and the SSC receive clock is generated from
+ * the transmit clock.
+ *
+ * For single channel data, one sample is transferred on the
+ * falling edge of the LRC clock. For two channel data, one
+ * sample is transferred on both edges of the LRC clock.
+ */
+ pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
+ start_event = ((channels == 1) ?
+ SSC_START_FALLING_RF : SSC_START_EDGE_RF);
+
+ rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
+ SSC_BF(RCMR_START, start_event) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
+ SSC_BF(TCMR_START, start_event) |
+ SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(TCMR_CKS, SSC_CKS_PIN));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
+ SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output on the SSC TK line
+ */
+ pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
+ rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+ SSC_BF(RCMR_STTDLY, 1) |
+ SSC_BF(RCMR_START, SSC_START_RISING_RF) |
+ SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+ SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+ rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
+ SSC_BF(RFMR_DATNB, channels - 1) |
+ SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+ tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+ SSC_BF(TCMR_STTDLY, 1) |
+ SSC_BF(TCMR_START, SSC_START_RISING_RF) |
+ SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
+ SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+ SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+ tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+ SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
+ SSC_BF(TFMR_DATNB, channels - 1) |
+ SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+ break;
+
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ default:
+ pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
+ ssc_p->daifmt);
+ return -EINVAL;
+ break;
+ }
+ pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+ rcmr, rfmr, tcmr, tfmr);
+
+
+ if (!ssc_p->initialized) {
+ /* enable peripheral clock */
+ pr_debug("at32-ssc: Starting clock\n");
+ clk_enable(ssc_p->ssc->clk);
+
+ /* Reset the SSC and its PDC registers */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+ ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+ ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
+ ssc_p->name, ssc_p);
+ if (ret < 0) {
+ pr_warning("at32-ssc: request irq failed (%d)\n", ret);
+ pr_debug("at32-ssc: Stopping clock\n");
+ clk_disable(ssc_p->ssc->clk);
+ return ret;
+ }
+
+ ssc_p->initialized = 1;
+ }
+
+ /* Set SSC clock mode register */
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+ /* set receive clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+ /* set transmit clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+ ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+ pr_debug("at32-ssc: SSC initialized\n");
+ return 0;
+}
+
+
+
+static int at32_ssc_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct at32_pcm_dma_params *dma_params;
+
+ dma_params = ssc_p->dma_params[substream->stream];
+
+ ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+ return 0;
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_ssc_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* Save the status register before disabling transmit and receive */
+ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+ /* Save the current interrupt mask, then disable unmasked interrupts */
+ ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+ ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+ ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+ ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+ ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+ ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+ ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+ return 0;
+}
+
+
+
+static int at32_ssc_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p;
+ u32 cr;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* restore SSC register settings */
+ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+ ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+ ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+ /* re-enable interrupts */
+ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+ /* Re-enable recieve and transmit as appropriate */
+ cr = 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+ ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+ return 0;
+}
+#else /* CONFIG_PM */
+# define at32_ssc_suspend NULL
+# define at32_ssc_resume NULL
+#endif /* CONFIG_PM */
+
+
+#define AT32_SSC_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+
+#define AT32_SSC_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16 | \
+ SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
+
+
+struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
+ {
+ .name = "at32-ssc0",
+ .id = 0,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[0],
+ },
+ {
+ .name = "at32-ssc1",
+ .id = 1,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[1],
+ },
+ {
+ .name = "at32-ssc2",
+ .id = 2,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = at32_ssc_suspend,
+ .resume = at32_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AT32_SSC_RATES,
+ .formats = AT32_SSC_FORMATS,
+ },
+ .ops = {
+ .startup = at32_ssc_startup,
+ .shutdown = at32_ssc_shutdown,
+ .prepare = at32_ssc_prepare,
+ .hw_params = at32_ssc_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = at32_ssc_set_dai_sysclk,
+ .set_fmt = at32_ssc_set_dai_fmt,
+ .set_clkdiv = at32_ssc_set_dai_clkdiv,
+ },
+ .private_data = &ssc_info[2],
+ },
+};
+EXPORT_SYMBOL_GPL(at32_ssc_dai);
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
new file mode 100644
index 00000000000..3c052dbbe46
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.h
@@ -0,0 +1,59 @@
+/* sound/soc/at32/at32-ssc.h
+ * ASoC SSC interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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 __SOUND_SOC_AT32_AT32_SSC_H
+#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "at32-pcm.h"
+
+
+
+struct at32_ssc_state {
+ u32 ssc_cmr;
+ u32 ssc_rcmr;
+ u32 ssc_rfmr;
+ u32 ssc_tcmr;
+ u32 ssc_tfmr;
+ u32 ssc_sr;
+ u32 ssc_imr;
+};
+
+
+
+struct at32_ssc_info {
+ char *name;
+ struct ssc_device *ssc;
+ spinlock_t lock; /* lock for dir_mask */
+ unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
+ unsigned short initialized; /* true if SSC has been initialized */
+ unsigned short daifmt;
+ unsigned short cmr_div;
+ unsigned short tcmr_period;
+ unsigned short rcmr_period;
+ struct at32_pcm_dma_params *dma_params[2];
+ struct at32_ssc_state ssc_state;
+};
+
+
+/* SSC divider ids */
+#define AT32_SSC_CMR_DIV 0 /* MCK divider for BCLK */
+#define AT32_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
+#define AT32_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
+
+
+extern struct snd_soc_dai at32_ssc_dai[];
+
+
+
+#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
new file mode 100644
index 00000000000..fee5f8e5895
--- /dev/null
+++ b/sound/soc/at32/playpaq_wm8510.c
@@ -0,0 +1,522 @@
+/* sound/soc/at32/playpaq_wm8510.c
+ * ASoC machine driver for PlayPaq using WM8510 codec
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.org>
+ *
+ * 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.
+ *
+ * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
+ *
+ * NOTE: If you don't have the AT32 enhanced portmux configured (which
+ * isn't currently in the mainline or Atmel patched kernel), you will
+ * need to set the MCLK pin (PA30) to peripheral A in your board initialization
+ * code. Something like:
+ * at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/arch/at32ap700x.h>
+#include <asm/arch/portmux.h>
+
+#include "../codecs/wm8510.h"
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+/*-------------------------------------------------------------------------*\
+ * constants
+\*-------------------------------------------------------------------------*/
+#define MCLK_PIN GPIO_PIN_PA(30)
+#define MCLK_PERIPH GPIO_PERIPH_A
+
+
+/*-------------------------------------------------------------------------*\
+ * data types
+\*-------------------------------------------------------------------------*/
+/* SSC clocking data */
+struct ssc_clock_data {
+ /* CMR div */
+ unsigned int cmr_div;
+
+ /* Frame period (as needed by xCMR.PERIOD) */
+ unsigned int period;
+
+ /* The SSC clock rate these settings where calculated for */
+ unsigned long ssc_rate;
+};
+
+
+/*-------------------------------------------------------------------------*\
+ * module data
+\*-------------------------------------------------------------------------*/
+static struct clk *_gclk0;
+static struct clk *_pll0;
+
+#define CODEC_CLK (_gclk0)
+
+
+/*-------------------------------------------------------------------------*\
+ * Sound SOC operations
+\*-------------------------------------------------------------------------*/
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ struct ssc_clock_data cd;
+ unsigned int rate, width_bits, channels;
+ unsigned int bitrate, ssc_div;
+ unsigned actual_rate;
+
+
+ /*
+ * Figure out required bitrate
+ */
+ rate = params_rate(params);
+ channels = params_channels(params);
+ width_bits = snd_pcm_format_physical_width(params_format(params));
+ bitrate = rate * width_bits * channels;
+
+
+ /*
+ * Figure out required SSC divider and period for required bitrate
+ */
+ cd.ssc_rate = clk_get_rate(ssc->clk);
+ ssc_div = cd.ssc_rate / bitrate;
+ cd.cmr_div = ssc_div / 2;
+ if (ssc_div & 1) {
+ /* round cmr_div up */
+ cd.cmr_div++;
+ }
+ cd.period = width_bits - 1;
+
+
+ /*
+ * Find actual rate, compare to requested rate
+ */
+ actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
+ pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+ rate, actual_rate);
+
+
+ return cd;
+}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+
+static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
+ int ret;
+
+
+ /* Due to difficulties with getting the correct clocks from the AT32's
+ * PLL0, we're going to let the CODEC be in charge of all the clocks
+ */
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+#else
+ struct ssc_clock_data cd;
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+#endif
+
+ if (ssc == NULL) {
+ pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
+ return -EINVAL;
+ }
+
+
+ /*
+ * Figure out PLL and BCLK dividers for WM8510
+ */
+ switch (params_rate(params)) {
+ case 48000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 44100:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 22050:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_2;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 16000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_3;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 11025:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_4;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 8000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_6;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ default:
+ pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+
+ /*
+ * set CPU and CODEC DAI configuration
+ */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CODEC DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ /*
+ * Set CPU clock configuration
+ */
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
+ pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
+ cd.cmr_div, cd.period);
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
+ cd.period);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU transmit period (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ /*
+ * Set CODEC clock configuration
+ */
+ pr_debug("playpaq_wm8510: "
+ "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
+ clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
+
+
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
+ if (ret < 0) {
+ pr_warning
+ ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ clk_get_rate(CODEC_CLK), pll_out);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ return 0;
+}
+
+
+
+static struct snd_soc_ops playpaq_wm8510_ops = {
+ .hw_params = playpaq_wm8510_hw_params,
+};
+
+
+
+static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+
+
+static const char *intercon[][3] = {
+ /* speaker connected to SPKOUT */
+ {"Ext Spk", NULL, "SPKOUTP"},
+ {"Ext Spk", NULL, "SPKOUTN"},
+
+ {"Mic Bias", NULL, "Int Mic"},
+ {"MICN", NULL, "Mic Bias"},
+ {"MICP", NULL, "Mic Bias"},
+
+ /* Terminator */
+ {NULL, NULL, NULL},
+};
+
+
+
+static int playpaq_wm8510_init(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /*
+ * Add DAPM widgets
+ */
+ for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
+ snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+
+
+
+ /*
+ * Setup audio path interconnects
+ */
+ for (i = 0; intercon[i][0] != NULL; i++) {
+ snd_soc_dapm_connect_input(codec,
+ intercon[i][0],
+ intercon[i][1], intercon[i][2]);
+ }
+
+
+ /* always connected pins */
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ snd_soc_dapm_sync(codec);
+
+
+
+ /* Make CSB show PLL rate */
+ snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
+ WM8510_OPCLKDIV_1 | 4);
+
+ return 0;
+}
+
+
+
+static struct snd_soc_dai_link playpaq_wm8510_dai = {
+ .name = "WM8510",
+ .stream_name = "WM8510 PCM",
+ .cpu_dai = &at32_ssc_dai[0],
+ .codec_dai = &wm8510_dai,
+ .init = playpaq_wm8510_init,
+ .ops = &playpaq_wm8510_ops,
+};
+
+
+
+static struct snd_soc_machine snd_soc_machine_playpaq = {
+ .name = "LRS_PlayPaq_WM8510",
+ .dai_link = &playpaq_wm8510_dai,
+ .num_links = 1,
+};
+
+
+
+static struct wm8510_setup_data playpaq_wm8510_setup = {
+ .i2c_address = 0x1a,
+};
+
+
+
+static struct snd_soc_device playpaq_wm8510_snd_devdata = {
+ .machine = &snd_soc_machine_playpaq,
+ .platform = &at32_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8510,
+ .codec_data = &playpaq_wm8510_setup,
+};
+
+static struct platform_device *playpaq_snd_device;
+
+
+static int __init playpaq_asoc_init(void)
+{
+ int ret = 0;
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc = NULL;
+
+
+ /*
+ * Request SSC device
+ */
+ ssc = ssc_request(0);
+ if (IS_ERR(ssc)) {
+ ret = PTR_ERR(ssc);
+ ssc = NULL;
+ goto err_ssc;
+ }
+ ssc_p->ssc = ssc;
+
+
+ /*
+ * Configure MCLK for WM8510
+ */
+ _gclk0 = clk_get(NULL, "gclk0");
+ if (IS_ERR(_gclk0)) {
+ _gclk0 = NULL;
+ goto err_gclk0;
+ }
+ _pll0 = clk_get(NULL, "pll0");
+ if (IS_ERR(_pll0)) {
+ _pll0 = NULL;
+ goto err_pll0;
+ }
+ if (clk_set_parent(_gclk0, _pll0)) {
+ pr_warning("snd-soc-playpaq: "
+ "Failed to set PLL0 as parent for DAC clock\n");
+ goto err_set_clk;
+ }
+ clk_set_rate(CODEC_CLK, 12000000);
+ clk_enable(CODEC_CLK);
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
+#endif
+
+
+ /*
+ * Create and register platform device
+ */
+ playpaq_snd_device = platform_device_alloc("soc-audio", 0);
+ if (playpaq_snd_device == NULL) {
+ ret = -ENOMEM;
+ goto err_device_alloc;
+ }
+
+ platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
+ playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
+
+ ret = platform_device_add(playpaq_snd_device);
+ if (ret) {
+ pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
+ ret);
+ goto err_device_add;
+ }
+
+ return 0;
+
+
+err_device_add:
+ if (playpaq_snd_device != NULL) {
+ platform_device_put(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+ }
+err_device_alloc:
+err_set_clk:
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+err_pll0:
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+err_gclk0:
+ if (ssc != NULL) {
+ ssc_free(ssc);
+ ssc = NULL;
+ }
+err_ssc:
+ return ret;
+}
+
+
+static void __exit playpaq_asoc_exit(void)
+{
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc;
+
+ if (ssc_p != NULL) {
+ ssc = ssc_p->ssc;
+ if (ssc != NULL)
+ ssc_free(ssc);
+ ssc_p->ssc = NULL;
+ }
+
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_free_pin(MCLK_PIN);
+#endif
+
+ platform_device_unregister(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+}
+
+module_init(playpaq_asoc_init);
+module_exit(playpaq_asoc_exit);
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index 5cb93fd3a40..905186502e0 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -1,6 +1,6 @@
config SND_AT91_SOC
tristate "SoC Audio for the Atmel AT91 System-on-Chip"
- depends on ARCH_AT91 && SND_SOC
+ depends on ARCH_AT91
help
Say Y or M if you want to add support for codecs attached to
the AT91 SSC interface. You will also need
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
index ccac6bd2889..d47492b2b6e 100644
--- a/sound/soc/at91/at91-pcm.c
+++ b/sound/soc/at91/at91-pcm.c
@@ -318,7 +318,7 @@ static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
static u64 at91_pcm_dmamask = 0xffffffff;
static int at91_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret = 0;
@@ -367,7 +367,7 @@ static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
#ifdef CONFIG_PM
static int at91_pcm_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct at91_runtime_data *prtd;
@@ -392,7 +392,7 @@ static int at91_pcm_suspend(struct platform_device *pdev,
}
static int at91_pcm_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct at91_runtime_data *prtd;
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index bc35d00a38f..090e607f869 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -41,7 +41,7 @@
#define DBG(x...)
#endif
-#if defined(CONFIG_ARCH_AT91SAM9260)
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
#define NUM_SSC_DEVICES 1
#else
#define NUM_SSC_DEVICES 3
@@ -281,7 +281,7 @@ static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
/*
* Record the SSC system clock rate.
*/
-static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
/*
@@ -303,7 +303,7 @@ static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Record the DAI format for use in hw_params().
*/
-static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -315,7 +315,7 @@ static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
/*
* Record SSC clock dividers for use in hw_params().
*/
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -634,7 +634,7 @@ static int at91_ssc_prepare(struct snd_pcm_substream *substream)
#ifdef CONFIG_PM
static int at91_ssc_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
struct at91_ssc_info *ssc_p;
@@ -662,7 +662,7 @@ static int at91_ssc_suspend(struct platform_device *pdev,
}
static int at91_ssc_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
struct at91_ssc_info *ssc_p;
@@ -700,7 +700,7 @@ static int at91_ssc_resume(struct platform_device *pdev,
#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
+struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
{ .name = "at91-ssc0",
.id = 0,
.type = SND_SOC_DAI_PCM,
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
index b188f973df9..6b7bf382d06 100644
--- a/sound/soc/at91/at91-ssc.h
+++ b/sound/soc/at91/at91-ssc.h
@@ -21,7 +21,7 @@
#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
-extern struct snd_soc_cpu_dai at91_ssc_dai[];
+extern struct snd_soc_dai at91_ssc_dai[];
#endif /* _AT91_SSC_H */
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index 1347dcf3f80..d532de95424 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -53,18 +53,18 @@ static struct clk *pllb_clk;
static int eti_b1_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
/* cpu clock is the AT91 master clock sent to the SSC */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
60000000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* codec system clock is supplied by PCK1, set to 12MHz */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
12000000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -87,8 +87,8 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
@@ -96,13 +96,13 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
int cmr_div, period;
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
@@ -141,17 +141,17 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
}
/* set the MCK divider for BCLK */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
if (ret < 0)
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* set the BCLK divider for DACLRC */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai,
AT91SSC_TCMR_PERIOD, period);
} else {
/* set the BCLK divider for ADCLRC */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai,
AT91SSC_RCMR_PERIOD, period);
}
if (ret < 0)
@@ -163,13 +163,13 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
*/
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
@@ -191,7 +191,7 @@ static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", NULL),
};
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
/* speaker connected to LHPOUT */
{"Ext Spk", NULL, "LHPOUT"},
@@ -199,9 +199,6 @@ static const char *intercon[][3] = {
/* mic is connected to Mic Jack, with WM8731 Mic Bias */
{"MICIN", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Int Mic"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
/*
@@ -209,30 +206,24 @@ static const char *intercon[][3] = {
*/
static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
{
- int i;
-
DBG("eti_b1_wm8731_init() called\n");
/* Add specific widgets */
- for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
- snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
- }
+ snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets,
+ ARRAY_SIZE(eti_b1_dapm_widgets));
/* Set up specific audio path interconnects */
- for(i = 0; intercon[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, intercon[i][0],
- intercon[i][1], intercon[i][2]);
- }
+ snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon));
/* not connected */
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+ snd_soc_dapm_disable_pin(codec, "RLINEIN");
+ snd_soc_dapm_disable_pin(codec, "LLINEIN");
/* always connected */
- snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
new file mode 100644
index 00000000000..410a893aa66
--- /dev/null
+++ b/sound/soc/au1x/Kconfig
@@ -0,0 +1,32 @@
+##
+## Au1200/Au1550 PSC + DBDMA
+##
+config SND_SOC_AU1XPSC
+ tristate "SoC Audio for Au1200/Au1250/Au1550"
+ depends on SOC_AU1200 || SOC_AU1550
+ help
+ This option enables support for the Programmable Serial
+ Controllers in AC97 and I2S mode, and the Descriptor-Based DMA
+ Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC.
+
+config SND_SOC_AU1XPSC_I2S
+ tristate
+
+config SND_SOC_AU1XPSC_AC97
+ tristate
+ select AC97_BUS
+ select SND_AC97_CODEC
+ select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_SAMPLE_PSC_AC97
+ tristate "Sample Au12x0/Au1550 PSC AC97 sound machine"
+ depends on SND_SOC_AU1XPSC
+ select SND_SOC_AU1XPSC_AC97
+ select SND_SOC_AC97_CODEC
+ help
+ This is a sample AC97 sound machine for use in Au12x0/Au1550
+ based systems which have audio on PSC1 (e.g. Db1200 demoboard).
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
new file mode 100644
index 00000000000..6c6950b8003
--- /dev/null
+++ b/sound/soc/au1x/Makefile
@@ -0,0 +1,13 @@
+# Au1200/Au1550 PSC audio
+snd-soc-au1xpsc-dbdma-objs := dbdma2.o
+snd-soc-au1xpsc-i2s-objs := psc-i2s.o
+snd-soc-au1xpsc-ac97-objs := psc-ac97.o
+
+obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
+
+# Boards
+snd-soc-sample-ac97-objs := sample-ac97.o
+
+obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
new file mode 100644
index 00000000000..1466d932880
--- /dev/null
+++ b/sound/soc/au1x/dbdma2.c
@@ -0,0 +1,421 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * DMA glue for Au1x-PSC audio.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/*#define PCM_DEBUG*/
+
+#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x)
+#ifdef PCM_DEBUG
+#define DBG MSG
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+struct au1xpsc_audio_dmadata {
+ /* DDMA control data */
+ unsigned int ddma_id; /* DDMA direction ID for this PSC */
+ u32 ddma_chan; /* DDMA context */
+
+ /* PCM context (for irq handlers) */
+ struct snd_pcm_substream *substream;
+ unsigned long curr_period; /* current segment DDMA is working on */
+ unsigned long q_period; /* queue period(s) */
+ unsigned long dma_area; /* address of queued DMA area */
+ unsigned long dma_area_s; /* start address of DMA area */
+ unsigned long pos; /* current byte position being played */
+ unsigned long periods; /* number of SG segments in total */
+ unsigned long period_bytes; /* size in bytes of one SG segment */
+
+ /* runtime data */
+ int msbits;
+};
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
+
+/*
+ * These settings are somewhat okay, at least on my machine audio plays
+ * almost skip-free. Especially the 64kB buffer seems to help a LOT.
+ */
+#define AU1XPSC_PERIOD_MIN_BYTES 1024
+#define AU1XPSC_BUFFER_MIN_BYTES 65536
+
+#define AU1XPSC_PCM_FMTS \
+ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \
+ 0)
+
+/* PCM hardware DMA capabilities - platform specific */
+static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = AU1XPSC_PCM_FMTS,
+ .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
+ .period_bytes_max = 4096 * 1024 - 1,
+ .periods_min = 2,
+ .periods_max = 4096, /* 2 to as-much-as-you-like */
+ .buffer_bytes_max = 4096 * 1024 - 1,
+ .fifo_size = 16, /* fifo entries of AC97/I2S PSC */
+};
+
+static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
+{
+ au1xxx_dbdma_put_source_flags(cd->ddma_chan,
+ (void *)phys_to_virt(cd->dma_area),
+ cd->period_bytes, DDMA_FLAGS_IE);
+
+ /* update next-to-queue period */
+ ++cd->q_period;
+ cd->dma_area += cd->period_bytes;
+ if (cd->q_period >= cd->periods) {
+ cd->q_period = 0;
+ cd->dma_area = cd->dma_area_s;
+ }
+}
+
+static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
+{
+ au1xxx_dbdma_put_dest_flags(cd->ddma_chan,
+ (void *)phys_to_virt(cd->dma_area),
+ cd->period_bytes, DDMA_FLAGS_IE);
+
+ /* update next-to-queue period */
+ ++cd->q_period;
+ cd->dma_area += cd->period_bytes;
+ if (cd->q_period >= cd->periods) {
+ cd->q_period = 0;
+ cd->dma_area = cd->dma_area_s;
+ }
+}
+
+static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
+{
+ struct au1xpsc_audio_dmadata *cd = dev_id;
+
+ cd->pos += cd->period_bytes;
+ if (++cd->curr_period >= cd->periods) {
+ cd->pos = 0;
+ cd->curr_period = 0;
+ }
+ snd_pcm_period_elapsed(cd->substream);
+ au1x_pcm_queue_tx(cd);
+}
+
+static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
+{
+ struct au1xpsc_audio_dmadata *cd = dev_id;
+
+ cd->pos += cd->period_bytes;
+ if (++cd->curr_period >= cd->periods) {
+ cd->pos = 0;
+ cd->curr_period = 0;
+ }
+ snd_pcm_period_elapsed(cd->substream);
+ au1x_pcm_queue_rx(cd);
+}
+
+static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
+{
+ if (pcd->ddma_chan) {
+ au1xxx_dbdma_stop(pcd->ddma_chan);
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+ au1xxx_dbdma_chan_free(pcd->ddma_chan);
+ pcd->ddma_chan = 0;
+ pcd->msbits = 0;
+ }
+}
+
+/* in case of missing DMA ring or changed TX-source / RX-dest bit widths,
+ * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according
+ * to ALSA-supplied sample depth. This is due to limitations in the dbdma api
+ * (cannot adjust source/dest widths of already allocated descriptor ring).
+ */
+static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
+ int stype, int msbits)
+{
+ /* DMA only in 8/16/32 bit widths */
+ if (msbits == 24)
+ msbits = 32;
+
+ /* check current config: correct bits and descriptors allocated? */
+ if ((pcd->ddma_chan) && (msbits == pcd->msbits))
+ goto out; /* all ok! */
+
+ au1x_pcm_dbdma_free(pcd);
+
+ if (stype == PCM_RX)
+ pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
+ DSCR_CMD0_ALWAYS,
+ au1x_pcm_dmarx_cb, (void *)pcd);
+ else
+ pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
+ pcd->ddma_id,
+ au1x_pcm_dmatx_cb, (void *)pcd);
+
+ if (!pcd->ddma_chan)
+ return -ENOMEM;;
+
+ au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
+ au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
+
+ pcd->msbits = msbits;
+
+ au1xxx_dbdma_stop(pcd->ddma_chan);
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+
+out:
+ return 0;
+}
+
+static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct au1xpsc_audio_dmadata *pcd;
+ int stype, ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ goto out;
+
+ stype = SUBSTREAM_TYPE(substream);
+ pcd = au1xpsc_audio_pcmdma[stype];
+
+ DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
+ "runtime->min_align %d\n",
+ (unsigned long)runtime->dma_area,
+ (unsigned long)runtime->dma_addr, runtime->dma_bytes,
+ runtime->min_align);
+
+ DBG("bits %d frags %d frag_bytes %d is_rx %d\n", params->msbits,
+ params_periods(params), params_period_bytes(params), stype);
+
+ ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
+ if (ret) {
+ MSG("DDMA channel (re)alloc failed!\n");
+ goto out;
+ }
+
+ pcd->substream = substream;
+ pcd->period_bytes = params_period_bytes(params);
+ pcd->periods = params_periods(params);
+ pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr;
+ pcd->q_period = 0;
+ pcd->curr_period = 0;
+ pcd->pos = 0;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct au1xpsc_audio_dmadata *pcd =
+ au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
+
+ au1xxx_dbdma_reset(pcd->ddma_chan);
+
+ if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+ au1x_pcm_queue_rx(pcd);
+ au1x_pcm_queue_rx(pcd);
+ } else {
+ au1x_pcm_queue_tx(pcd);
+ au1x_pcm_queue_tx(pcd);
+ }
+
+ return 0;
+}
+
+static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ au1xxx_dbdma_start(c);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ au1xxx_dbdma_stop(c);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t
+au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ return bytes_to_frames(substream->runtime,
+ au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
+}
+
+static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
+ return 0;
+}
+
+static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+{
+ au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
+ return 0;
+}
+
+struct snd_pcm_ops au1xpsc_pcm_ops = {
+ .open = au1xpsc_pcm_open,
+ .close = au1xpsc_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = au1xpsc_pcm_hw_params,
+ .hw_free = au1xpsc_pcm_hw_free,
+ .prepare = au1xpsc_pcm_prepare,
+ .trigger = au1xpsc_pcm_trigger,
+ .pointer = au1xpsc_pcm_pointer,
+};
+
+static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int au1xpsc_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
+
+ return 0;
+}
+
+static int au1xpsc_pcm_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ int ret;
+
+ if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
+ return -EBUSY;
+
+ /* TX DMA */
+ au1xpsc_audio_pcmdma[PCM_TX]
+ = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ if (!au1xpsc_audio_pcmdma[PCM_TX])
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out1;
+ }
+ (au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
+
+ /* RX DMA */
+ au1xpsc_audio_pcmdma[PCM_RX]
+ = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ if (!au1xpsc_audio_pcmdma[PCM_RX])
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r) {
+ ret = -ENODEV;
+ goto out2;
+ }
+ (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
+
+ return 0;
+
+out2:
+ kfree(au1xpsc_audio_pcmdma[PCM_RX]);
+ au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+out1:
+ kfree(au1xpsc_audio_pcmdma[PCM_TX]);
+ au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+ return ret;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (au1xpsc_audio_pcmdma[i]) {
+ au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
+ kfree(au1xpsc_audio_pcmdma[i]);
+ au1xpsc_audio_pcmdma[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+ .name = "au1xpsc-pcm-dbdma",
+ .probe = au1xpsc_pcm_probe,
+ .remove = au1xpsc_pcm_remove,
+ .pcm_ops = &au1xpsc_pcm_ops,
+ .pcm_new = au1xpsc_pcm_new,
+ .pcm_free = au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __init au1xpsc_audio_dbdma_init(void)
+{
+ au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+ au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_audio_dbdma_exit(void)
+{
+}
+
+module_init(au1xpsc_audio_dbdma_init);
+module_exit(au1xpsc_audio_dbdma_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
new file mode 100644
index 00000000000..57facbad682
--- /dev/null
+++ b/sound/soc/au1x/psc-ac97.c
@@ -0,0 +1,387 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC AC97 glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+#define AC97_DIR \
+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES \
+ SNDRV_PCM_RATE_8000_48000
+
+#define AC97_FMTS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
+
+#define AC97PCR_START(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+#define AC97PCR_STOP(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+#define AC97PCR_CLRFIFO(stype) \
+ ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
+
+/* AC97 controller reads codec register */
+static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned short data, tmo;
+
+ au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+ au_sync();
+
+ tmo = 1000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+ udelay(2);
+
+ if (!tmo)
+ data = 0xffff;
+ else
+ data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+
+ return data;
+}
+
+/* AC97 controller writes to codec register */
+static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned int tmo;
+
+ au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+ au_sync();
+ tmo = 1000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+ au_sync();
+
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+}
+
+/* AC97 controller asserts a warm reset */
+static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+
+ au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
+ au_sync();
+ msleep(10);
+ au_writel(0, AC97_RST(pscdata));
+ au_sync();
+}
+
+static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ int i;
+
+ /* disable PSC during cold reset */
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ /* issue cold reset */
+ au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
+ au_sync();
+ msleep(500);
+ au_writel(0, AC97_RST(pscdata));
+ au_sync();
+
+ /* enable PSC */
+ au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ /* wait for PSC to indicate it's ready */
+ i = 100000;
+ while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
+ au_sync();
+
+ if (i == 0) {
+ printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
+ return;
+ }
+
+ /* enable the ac97 function */
+ au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ /* wait for AC97 core to become ready */
+ i = 100000;
+ while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
+ au_sync();
+ if (i == 0)
+ printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = au1xpsc_ac97_read,
+ .write = au1xpsc_ac97_write,
+ .reset = au1xpsc_ac97_cold_reset,
+ .warm_reset = au1xpsc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ unsigned long r, stat;
+ int chans, stype = SUBSTREAM_TYPE(substream);
+
+ chans = params_channels(params);
+
+ r = au_readl(AC97_CFG(pscdata));
+ stat = au_readl(AC97_STAT(pscdata));
+
+ /* already active? */
+ if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
+ /* reject parameters not currently set up */
+ if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
+ (pscdata->rate != params_rate(params)))
+ return -EINVAL;
+ } else {
+ /* disable AC97 device controller first */
+ au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
+ r &= ~PSC_AC97CFG_LEN_MASK;
+ r |= PSC_AC97CFG_SET_LEN(params->msbits);
+
+ /* channels: enable slots for front L/R channel */
+ if (stype == PCM_TX) {
+ r &= ~PSC_AC97CFG_TXSLOT_MASK;
+ r |= PSC_AC97CFG_TXSLOT_ENA(3);
+ r |= PSC_AC97CFG_TXSLOT_ENA(4);
+ } else {
+ r &= ~PSC_AC97CFG_RXSLOT_MASK;
+ r |= PSC_AC97CFG_RXSLOT_ENA(3);
+ r |= PSC_AC97CFG_RXSLOT_ENA(4);
+ }
+
+ /* finally enable the AC97 controller again */
+ au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ pscdata->cfg = r;
+ pscdata->rate = params_rate(params);
+ }
+
+ return 0;
+}
+
+static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ /* FIXME */
+ struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ int ret, stype = SUBSTREAM_TYPE(substream);
+
+ ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
+ au_sync();
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
+ au_sync();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int au1xpsc_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct resource *r;
+ unsigned long sel;
+
+ if (au1xpsc_ac97_workdata)
+ return -EBUSY;
+
+ au1xpsc_ac97_workdata =
+ kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!au1xpsc_ac97_workdata)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out0;
+ }
+
+ ret = -EBUSY;
+ au1xpsc_ac97_workdata->ioarea =
+ request_mem_region(r->start, r->end - r->start + 1,
+ "au1xpsc_ac97");
+ if (!au1xpsc_ac97_workdata->ioarea)
+ goto out0;
+
+ au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
+ if (!au1xpsc_ac97_workdata->mmio)
+ goto out1;
+
+ /* configuration: max dma trigger threshold, enable ac97 */
+ au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+ PSC_AC97CFG_TT_FIFO8 |
+ PSC_AC97CFG_DE_ENABLE;
+
+ /* preserve PSC clock source set up by platform (dev.platform_data
+ * is already occupied by soc layer)
+ */
+ sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+ /* next up: cold reset. Dont check for PSC-ready now since
+ * there may not be any codec clock yet.
+ */
+
+ return 0;
+
+out1:
+ release_resource(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata->ioarea);
+out0:
+ kfree(au1xpsc_ac97_workdata);
+ au1xpsc_ac97_workdata = NULL;
+ return ret;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* disable PSC completely */
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ iounmap(au1xpsc_ac97_workdata->mmio);
+ release_resource(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata->ioarea);
+ kfree(au1xpsc_ac97_workdata);
+ au1xpsc_ac97_workdata = NULL;
+}
+
+static int au1xpsc_ac97_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* save interesting registers and disable PSC */
+ au1xpsc_ac97_workdata->pm[0] =
+ au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+
+ au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xpsc_ac97_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ /* restore PSC clock config */
+ au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
+ PSC_SEL(au1xpsc_ac97_workdata));
+ au_sync();
+
+ /* after this point the ac97 core will cold-reset the codec.
+ * During cold-reset the PSC is reinitialized and the last
+ * configuration set up in hw_params() is restored.
+ */
+ return 0;
+}
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+ .name = "au1xpsc_ac97",
+ .type = SND_SOC_DAI_AC97,
+ .probe = au1xpsc_ac97_probe,
+ .remove = au1xpsc_ac97_remove,
+ .suspend = au1xpsc_ac97_suspend,
+ .resume = au1xpsc_ac97_resume,
+ .playback = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = AC97_RATES,
+ .formats = AC97_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = {
+ .trigger = au1xpsc_ac97_trigger,
+ .hw_params = au1xpsc_ac97_hw_params,
+ },
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __init au1xpsc_ac97_init(void)
+{
+ au1xpsc_ac97_workdata = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_ac97_exit(void)
+{
+}
+
+module_init(au1xpsc_ac97_init);
+module_exit(au1xpsc_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
new file mode 100644
index 00000000000..ba4b5c199f2
--- /dev/null
+++ b/sound/soc/au1x/psc-i2s.c
@@ -0,0 +1,414 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC I2S glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/* supported I2S DAI hardware formats */
+#define AU1XPSC_I2S_DAIFMT \
+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | \
+ SND_SOC_DAIFMT_NB_NF)
+
+/* supported I2S direction */
+#define AU1XPSC_I2S_DIR \
+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AU1XPSC_I2S_RATES \
+ SNDRV_PCM_RATE_8000_192000
+
+#define AU1XPSC_I2S_FMTS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+#define I2SSTAT_BUSY(stype) \
+ ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+#define I2SPCR_START(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+#define I2SPCR_STOP(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+#define I2SPCR_CLRFIFO(stype) \
+ ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
+
+static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ unsigned long ct;
+ int ret;
+
+ ret = -EINVAL;
+
+ ct = pscdata->cfg;
+
+ ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ); /* left-justified */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ct |= PSC_I2SCFG_XM; /* enable I2S mode */
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ ct |= PSC_I2SCFG_MLJ; /* LSB (right-) justified */
+ break;
+ default:
+ goto out;
+ }
+
+ ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI); /* IB-IF */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ct |= PSC_I2SCFG_BI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ct |= PSC_I2SCFG_WI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ goto out;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */
+ ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
+ ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */
+ break;
+ default:
+ goto out;
+ }
+
+ pscdata->cfg = ct;
+ ret = 0;
+out:
+ return ret;
+}
+
+static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+
+ int cfgbits;
+ unsigned long stat;
+
+ /* check if the PSC is already streaming data */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
+ /* reject parameters not currently set up in hardware */
+ cfgbits = au_readl(I2S_CFG(pscdata));
+ if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
+ (params_rate(params) != pscdata->rate))
+ return -EINVAL;
+ } else {
+ /* set sample bitdepth */
+ pscdata->cfg &= ~(0x1f << 4);
+ pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
+ /* remember current rate for other stream */
+ pscdata->rate = params_rate(params);
+ }
+ return 0;
+}
+
+/* Configure PSC late: on my devel systems the codec is I2S master and
+ * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit. ASoC
+ * uses aggressive PM and switches the codec off when it is not in use
+ * which also means the PSC unit doesn't get any clocks and is therefore
+ * dead. That's why this chunk here gets called from the trigger callback
+ * because I can be reasonably certain the codec is driving the clocks.
+ */
+static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
+{
+ unsigned long tmo;
+
+ /* bring PSC out of sleep, and configure I2S unit */
+ au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+ au_sync();
+
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
+ tmo--;
+
+ if (!tmo)
+ goto psc_err;
+
+ au_writel(0, I2S_CFG(pscdata));
+ au_sync();
+ au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
+ au_sync();
+
+ /* wait for I2S controller to become ready */
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
+ tmo--;
+
+ if (tmo)
+ return 0;
+
+psc_err:
+ au_writel(0, I2S_CFG(pscdata));
+ au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ au_sync();
+ return -ETIMEDOUT;
+}
+
+static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
+{
+ unsigned long tmo, stat;
+ int ret;
+
+ ret = 0;
+
+ /* if both TX and RX are idle, configure the PSC */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
+ ret = au1xpsc_i2s_configure(pscdata);
+ if (ret)
+ goto out;
+ }
+
+ au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
+ au_sync();
+ au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
+ au_sync();
+
+ /* wait for start confirmation */
+ tmo = 1000000;
+ while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ tmo--;
+
+ if (!tmo) {
+ au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ au_sync();
+ ret = -ETIMEDOUT;
+ }
+out:
+ return ret;
+}
+
+static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
+{
+ unsigned long tmo, stat;
+
+ au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+ au_sync();
+
+ /* wait for stop confirmation */
+ tmo = 1000000;
+ while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+ tmo--;
+
+ /* if both TX and RX are idle, disable PSC */
+ stat = au_readl(I2S_STAT(pscdata));
+ if (!(stat & (PSC_I2SSTAT_RB | PSC_I2SSTAT_RB))) {
+ au_writel(0, I2S_CFG(pscdata));
+ au_sync();
+ au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+ au_sync();
+ }
+ return 0;
+}
+
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ int ret, stype = SUBSTREAM_TYPE(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = au1xpsc_i2s_start(pscdata, stype);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = au1xpsc_i2s_stop(pscdata, stype);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int au1xpsc_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct resource *r;
+ unsigned long sel;
+ int ret;
+
+ if (au1xpsc_i2s_workdata)
+ return -EBUSY;
+
+ au1xpsc_i2s_workdata =
+ kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+ if (!au1xpsc_i2s_workdata)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENODEV;
+ goto out0;
+ }
+
+ ret = -EBUSY;
+ au1xpsc_i2s_workdata->ioarea =
+ request_mem_region(r->start, r->end - r->start + 1,
+ "au1xpsc_i2s");
+ if (!au1xpsc_i2s_workdata->ioarea)
+ goto out0;
+
+ au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
+ if (!au1xpsc_i2s_workdata->mmio)
+ goto out1;
+
+ /* preserve PSC clock source set up by platform (dev.platform_data
+ * is already occupied by soc layer)
+ */
+ sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+
+ /* preconfigure: set max rx/tx fifo depths */
+ au1xpsc_i2s_workdata->cfg |=
+ PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+
+ /* don't wait for I2S core to become ready now; clocks may not
+ * be running yet; depending on clock input for PSC a wait might
+ * time out.
+ */
+
+ return 0;
+
+out1:
+ release_resource(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata->ioarea);
+out0:
+ kfree(au1xpsc_i2s_workdata);
+ au1xpsc_i2s_workdata = NULL;
+ return ret;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ iounmap(au1xpsc_i2s_workdata->mmio);
+ release_resource(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata->ioarea);
+ kfree(au1xpsc_i2s_workdata);
+ au1xpsc_i2s_workdata = NULL;
+}
+
+static int au1xpsc_i2s_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ /* save interesting register and disable PSC */
+ au1xpsc_i2s_workdata->pm[0] =
+ au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+
+ au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xpsc_i2s_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ /* select I2S mode and PSC clock */
+ au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+ au_sync();
+ au_writel(au1xpsc_i2s_workdata->pm[0],
+ PSC_SEL(au1xpsc_i2s_workdata));
+ au_sync();
+
+ return 0;
+}
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+ .name = "au1xpsc_i2s",
+ .type = SND_SOC_DAI_I2S,
+ .probe = au1xpsc_i2s_probe,
+ .remove = au1xpsc_i2s_remove,
+ .suspend = au1xpsc_i2s_suspend,
+ .resume = au1xpsc_i2s_resume,
+ .playback = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .capture = {
+ .rates = AU1XPSC_I2S_RATES,
+ .formats = AU1XPSC_I2S_FMTS,
+ .channels_min = 2,
+ .channels_max = 8, /* 2 without external help */
+ },
+ .ops = {
+ .trigger = au1xpsc_i2s_trigger,
+ .hw_params = au1xpsc_i2s_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = au1xpsc_i2s_set_fmt,
+ },
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_init(void)
+{
+ au1xpsc_i2s_workdata = NULL;
+ return 0;
+}
+
+static void __exit au1xpsc_i2s_exit(void)
+{
+}
+
+module_init(au1xpsc_i2s_init);
+module_exit(au1xpsc_i2s_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
new file mode 100644
index 00000000000..8fdb1a04a07
--- /dev/null
+++ b/sound/soc/au1x/psc.h
@@ -0,0 +1,53 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ * of a PSC. Multiple independent audio devices are impossible
+ * with ASoC v1.
+ */
+
+#ifndef _AU1X_PCM_H
+#define _AU1X_PCM_H
+
+extern struct snd_soc_dai au1xpsc_ac97_dai;
+extern struct snd_soc_dai au1xpsc_i2s_dai;
+extern struct snd_soc_platform au1xpsc_soc_platform;
+extern struct snd_ac97_bus_ops soc_ac97_ops;
+
+struct au1xpsc_audio_data {
+ void __iomem *mmio;
+
+ unsigned long cfg;
+ unsigned long rate;
+
+ unsigned long pm[2];
+ struct resource *ioarea;
+};
+
+#define PCM_TX 0
+#define PCM_RX 1
+
+#define SUBSTREAM_TYPE(substream) \
+ ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
+
+/* easy access macros */
+#define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
+#define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
+#define I2S_STAT(x) ((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET)
+#define I2S_CFG(x) ((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET)
+#define I2S_PCR(x) ((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET)
+#define AC97_CFG(x) ((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET)
+#define AC97_CDC(x) ((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET)
+#define AC97_EVNT(x) ((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET)
+#define AC97_PCR(x) ((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET)
+#define AC97_RST(x) ((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET)
+#define AC97_STAT(x) ((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET)
+
+#endif
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
new file mode 100644
index 00000000000..f75ae7f62c3
--- /dev/null
+++ b/sound/soc/au1x/sample-ac97.c
@@ -0,0 +1,144 @@
+/*
+ * Sample Au12x0/Au1550 PSC AC97 sound machine.
+ *
+ * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms outlined in the file COPYING at the root of this
+ * source archive.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#include "../codecs/ac97.h"
+#include "psc.h"
+
+static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */
+ .codec_dai = &ac97_dai, /* see codecs/ac97.c */
+ .init = au1xpsc_sample_ac97_init,
+ .ops = NULL,
+};
+
+static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+ .name = "Au1xxx PSC AC97 Audio",
+ .dai_link = &au1xpsc_sample_ac97_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
+ .machine = &au1xpsc_sample_ac97_machine,
+ .platform = &au1xpsc_soc_platform, /* see dbdma2.c */
+ .codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct resource au1xpsc_psc1_res[] = {
+ [0] = {
+ .start = CPHYSADDR(PSC1_BASE_ADDR),
+ .end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+#ifdef CONFIG_SOC_AU1200
+ .start = AU1200_PSC1_INT,
+ .end = AU1200_PSC1_INT,
+#elif defined(CONFIG_SOC_AU1550)
+ .start = AU1550_PSC1_INT,
+ .end = AU1550_PSC1_INT,
+#endif
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = DSCR_CMD0_PSC1_TX,
+ .end = DSCR_CMD0_PSC1_TX,
+ .flags = IORESOURCE_DMA,
+ },
+ [3] = {
+ .start = DSCR_CMD0_PSC1_RX,
+ .end = DSCR_CMD0_PSC1_RX,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct platform_device *au1xpsc_sample_ac97_dev;
+
+static int __init au1xpsc_sample_ac97_load(void)
+{
+ int ret;
+
+#ifdef CONFIG_SOC_AU1200
+ unsigned long io;
+
+ /* modify sys_pinfunc for AC97 on PSC1 */
+ io = au_readl(SYS_PINFUNC);
+ io |= SYS_PINFUNC_P1C;
+ io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
+ au_writel(io, SYS_PINFUNC);
+ au_sync();
+#endif
+
+ ret = -ENOMEM;
+
+ /* setup PSC clock source for AC97 part: external clock provided
+ * by codec. The psc-ac97.c driver depends on this setting!
+ */
+ au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
+ au_sync();
+
+ au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
+ if (!au1xpsc_sample_ac97_dev)
+ goto out;
+
+ au1xpsc_sample_ac97_dev->resource =
+ kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
+ ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
+ au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
+ au1xpsc_sample_ac97_dev->id = 1;
+
+ platform_set_drvdata(au1xpsc_sample_ac97_dev,
+ &au1xpsc_sample_ac97_devdata);
+ au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
+ ret = platform_device_add(au1xpsc_sample_ac97_dev);
+
+ if (ret) {
+ platform_device_put(au1xpsc_sample_ac97_dev);
+ au1xpsc_sample_ac97_dev = NULL;
+ }
+
+out:
+ return ret;
+}
+
+static void __exit au1xpsc_sample_ac97_exit(void)
+{
+ platform_device_unregister(au1xpsc_sample_ac97_dev);
+}
+
+module_init(au1xpsc_sample_ac97_load);
+module_exit(au1xpsc_sample_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3903ab7dfa4..1db04a28a53 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,31 +1,37 @@
config SND_SOC_AC97_CODEC
tristate
- depends on SND_SOC
+ select SND_AC97_CODEC
+
+config SND_SOC_AK4535
+ tristate
+
+config SND_SOC_UDA1380
+ tristate
+
+config SND_SOC_WM8510
+ tristate
config SND_SOC_WM8731
tristate
- depends on SND_SOC
config SND_SOC_WM8750
tristate
- depends on SND_SOC
config SND_SOC_WM8753
tristate
- depends on SND_SOC
+
+config SND_SOC_WM8990
+ tristate
config SND_SOC_WM9712
tristate
- depends on SND_SOC
config SND_SOC_WM9713
tristate
- depends on SND_SOC
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
- depends on SND_SOC
# Cirrus Logic CS4270 Codec Hardware Mute Support
# Select if you have external muting circuitry attached to your CS4270.
@@ -43,4 +49,4 @@ config SND_SOC_CS4270_VD33_ERRATA
config SND_SOC_TLV320AIC3X
tristate
- depends on SND_SOC && I2C
+ depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4e1314c9d3e..d7b97abcf72 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,16 +1,24 @@
snd-soc-ac97-objs := ac97.o
+snd-soc-ak4535-objs := ak4535.o
+snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8990-objs := wm8990.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 2a1ffe39690..61fd96ca7bc 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -10,9 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 17th Oct 2005 Initial version.
- *
* Generic AC97 support.
*/
@@ -24,6 +21,7 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include "ac97.h"
#define AC97_VERSION "0.6"
@@ -43,7 +41,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
-struct snd_soc_codec_dai ac97_dai = {
+struct snd_soc_dai ac97_dai = {
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97,
.playback = {
@@ -146,9 +144,34 @@ static int ac97_soc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_ac97_suspend(socdev->codec->ac97);
+
+ return 0;
+}
+
+static int ac97_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_ac97_resume(socdev->codec->ac97);
+
+ return 0;
+}
+#else
+#define ac97_soc_suspend NULL
+#define ac97_soc_resume NULL
+#endif
+
struct snd_soc_codec_device soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.remove = ac97_soc_remove,
+ .suspend = ac97_soc_suspend,
+ .resume = ac97_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
index 2bf6d69fd06..281aa42e2bb 100644
--- a/sound/soc/codecs/ac97.h
+++ b/sound/soc/codecs/ac97.h
@@ -14,6 +14,6 @@
#define __LINUX_SND_SOC_AC97_H
extern struct snd_soc_codec_device soc_codec_dev_ac97;
-extern struct snd_soc_codec_dai ac97_dai;
+extern struct snd_soc_dai ac97_dai;
#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
new file mode 100644
index 00000000000..b26003c4f3e
--- /dev/null
+++ b/sound/soc/codecs/ak4535.c
@@ -0,0 +1,696 @@
+/*
+ * ak4535.c -- AK4535 ALSA Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak4535.h"
+
+#define AUDIO_NAME "ak4535"
+#define AK4535_VERSION "0.3"
+
+struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+/* codec private data */
+struct ak4535_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * ak4535 register cache
+ */
+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
+ 0x0000, 0x0080, 0x0000, 0x0003,
+ 0x0002, 0x0000, 0x0011, 0x0001,
+ 0x0000, 0x0040, 0x0036, 0x0010,
+ 0x0000, 0x0000, 0x0057, 0x0000,
+};
+
+/*
+ * read ak4535 register cache
+ */
+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4535_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 data;
+ data = reg;
+
+ if (codec->hw_write(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ if (codec->hw_read(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ return data;
+};
+
+/*
+ * write ak4535 register cache
+ */
+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4535_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the AK4535 register space
+ */
+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D8 AK4535 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ ak4535_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static int ak4535_sync(struct snd_soc_codec *codec)
+{
+ u16 *cache = codec->reg_cache;
+ int i, r = 0;
+
+ for (i = 0; i < AK4535_CACHEREGNUM; i++)
+ r |= ak4535_write(codec, i, cache[i]);
+
+ return r;
+};
+
+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
+static const char *ak4535_mic_select[] = {"Internal", "External"};
+
+static const struct soc_enum ak4535_enum[] = {
+ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
+ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
+ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
+ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
+ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
+};
+
+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
+ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
+ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
+ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
+ SOC_ENUM("Headphone Output", ak4535_enum[2]),
+ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
+ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
+ SOC_ENUM("Mic Select", ak4535_enum[4]),
+ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
+ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
+ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
+ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
+ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
+ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
+ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
+ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
+ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
+};
+
+/* add non dapm controls */
+static int ak4535_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4535_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
+
+/* HP L switch */
+static const struct snd_kcontrol_new ak4535_hpl_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
+
+/* HP R switch */
+static const struct snd_kcontrol_new ak4535_hpr_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4535_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
+
+/* Line out switch */
+static const struct snd_kcontrol_new ak4535_line_control =
+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
+
+/* ak4535 dapm widgets */
+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mixer_controls[0],
+ ARRAY_SIZE(ak4535_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4535_input_mux_control),
+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_mono2_control),
+ /* speaker powersave bit */
+ SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_line_control),
+ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpl_control),
+ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
+ &ak4535_hpr_control),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPP"),
+ SND_SOC_DAPM_OUTPUT("SPN"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("MIN"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /*stereo mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* mono1 mixer */
+ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Input Mux", "Internal", "Mic Int Bias"},
+ {"Input Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Input Mux"},
+
+ /* line out */
+ {"LOUT", NULL, "Line Out Enable"},
+ {"ROUT", NULL, "Line Out Enable"},
+ {"Line Out Enable", "Switch", "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* mono1 out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* left HP */
+ {"HPL", NULL, "Left HP Enable"},
+ {"Left HP Enable", "Switch", "HP L Amp"},
+ {"HP L Amp", NULL, "Stereo Mixer"},
+
+ /* right HP */
+ {"HPR", NULL, "Right HP Enable"},
+ {"Right HP Enable", "Switch", "HP R Amp"},
+ {"HP R Amp", NULL, "Stereo Mixer"},
+
+ /* speaker */
+ {"SPP", NULL, "Speaker Enable"},
+ {"SPN", NULL, "Speaker Enable"},
+ {"Speaker Enable", "Switch", "Spk Amp"},
+ {"Spk Amp", NULL, "MIN"},
+
+ /* mono 2 */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Stereo Mixer"},
+
+ /* Aux In */
+ {"Aux In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "Aux In"},
+};
+
+static int ak4535_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets,
+ ARRAY_SIZE(ak4535_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4535_priv *ak4535 = codec->private_data;
+
+ ak4535->sysclk = freq;
+ return 0;
+}
+
+static int ak4535_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct ak4535_priv *ak4535 = codec->private_data;
+ u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
+ int rate = params_rate(params), fs = 256;
+
+ if (rate)
+ fs = ak4535->sysclk / rate;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 |= (0x2 << 5);
+ break;
+ case 512:
+ mode2 |= (0x1 << 5);
+ break;
+ case 256:
+ break;
+ }
+
+ /* set rate */
+ ak4535_write(codec, AK4535_MODE2, mode2);
+ return 0;
+}
+
+static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x0002;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x0001;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* use 32 fs for BCLK to save power */
+ mode1 |= 0x4;
+
+ ak4535_write(codec, AK4535_MODE1, mode1);
+ return 0;
+}
+
+static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
+ if (!mute)
+ ak4535_write(codec, AK4535_DAC, mute_reg);
+ else
+ ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+ return 0;
+}
+
+static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 i;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ak4535_mute(codec->dai, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ ak4535_mute(codec->dai, 1);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ i = ak4535_read_reg_cache(codec, AK4535_PM1);
+ ak4535_write(codec, AK4535_PM1, i | 0x80);
+ i = ak4535_read_reg_cache(codec, AK4535_PM2);
+ ak4535_write(codec, AK4535_PM2, i & (~0x80));
+ break;
+ case SND_SOC_BIAS_OFF:
+ i = ak4535_read_reg_cache(codec, AK4535_PM1);
+ ak4535_write(codec, AK4535_PM1, i & (~0x80));
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai ak4535_dai = {
+ .name = "AK4535",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4535_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = ak4535_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = ak4535_set_dai_fmt,
+ .digital_mute = ak4535_mute,
+ .set_sysclk = ak4535_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(ak4535_dai);
+
+static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak4535_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ ak4535_sync(codec);
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ ak4535_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the AK4535 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4535_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "AK4535";
+ codec->owner = THIS_MODULE;
+ codec->read = ak4535_read_reg_cache;
+ codec->write = ak4535_write;
+ codec->set_bias_level = ak4535_set_bias_level;
+ codec->dai = &ak4535_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
+ codec->reg_cache = kmemdup(ak4535_reg, sizeof(ak4535_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4535: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ak4535_add_controls(codec);
+ ak4535_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4535: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+
+ return ret;
+}
+
+static struct snd_soc_device *ak4535_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+#define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver ak4535_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = ak4535_socdev;
+ struct ak4535_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = ak4535_init(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "failed to initialise AK4535\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int ak4535_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int ak4535_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, ak4535_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver ak4535_i2c_driver = {
+ .driver = {
+ .name = "AK4535 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .id = I2C_DRIVERID_AK4535,
+ .attach_adapter = ak4535_i2c_attach,
+ .detach_client = ak4535_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "AK4535",
+ .driver = &ak4535_i2c_driver,
+};
+#endif
+
+static int ak4535_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct ak4535_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct ak4535_priv *ak4535;
+ int ret = 0;
+
+ printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
+ if (ak4535 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = ak4535;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ak4535_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->hw_read = (hw_read_t)i2c_master_recv;
+ ret = i2c_add_driver(&ak4535_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int ak4535_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&ak4535_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
+ .probe = ak4535_probe,
+ .remove = ak4535_remove,
+ .suspend = ak4535_suspend,
+ .resume = ak4535_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
+
+MODULE_DESCRIPTION("Soc AK4535 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
new file mode 100644
index 00000000000..e9fe30e2c05
--- /dev/null
+++ b/sound/soc/codecs/ak4535.h
@@ -0,0 +1,46 @@
+/*
+ * ak4535.h -- AK4535 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.h
+ *
+ * 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 _AK4535_H
+#define _AK4535_H
+
+/* AK4535 register space */
+
+#define AK4535_PM1 0x0
+#define AK4535_PM2 0x1
+#define AK4535_SIG1 0x2
+#define AK4535_SIG2 0x3
+#define AK4535_MODE1 0x4
+#define AK4535_MODE2 0x5
+#define AK4535_DAC 0x6
+#define AK4535_MIC 0x7
+#define AK4535_TIMER 0x8
+#define AK4535_ALC1 0x9
+#define AK4535_ALC2 0xa
+#define AK4535_PGA 0xb
+#define AK4535_LATT 0xc
+#define AK4535_RATT 0xd
+#define AK4535_VOL 0xe
+#define AK4535_STATUS 0xf
+
+#define AK4535_CACHEREGNUM 0x10
+
+struct ak4535_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak4535_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e73fcfd9f5c..9deb8c74fdf 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -201,7 +201,7 @@ static struct {
* driver what the input settings can be. This would need to be implemented
* for stand-alone mode to work.
*/
-static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -251,7 +251,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
* data for playback only, but ASoC currently does not support different
* formats for playback vs. record.
*/
-static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -471,7 +471,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
* board does not have the MUTEA or MUTEB pins connected to such circuitry,
* then this function will do nothing.
*/
-static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute)
+static int cs4270_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int reg6;
@@ -667,7 +667,7 @@ error:
#endif /* USE_I2C*/
-struct snd_soc_codec_dai cs4270_dai = {
+struct snd_soc_dai cs4270_dai = {
.name = "CS4270",
.playback = {
.stream_name = "Playback",
diff --git a/sound/soc/codecs/cs4270.h b/sound/soc/codecs/cs4270.h
index 0ced49b7804..adc6cd9667d 100644
--- a/sound/soc/codecs/cs4270.h
+++ b/sound/soc/codecs/cs4270.h
@@ -16,7 +16,7 @@
* The ASoC codec DAI structure for the CS4270. Assign this structure to
* the .codec_dai field of your machine driver's snd_soc_dai_link structure.
*/
-extern struct snd_soc_codec_dai cs4270_dai;
+extern struct snd_soc_dai cs4270_dai;
/*
* The ASoC codec device structure for the CS4270. Assign this structure
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 09b1661b8a3..b1dce5f459d 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -29,7 +29,7 @@
* ---------------------------------------
*
* Hence the machine layer should disable unsupported inputs/outputs by
- * snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ * snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
*/
#include <linux/module.h>
@@ -49,7 +49,7 @@
#include "tlv320aic3x.h"
#define AUDIO_NAME "aic3x"
-#define AIC3X_VERSION "0.1"
+#define AIC3X_VERSION "0.2"
/* codec private data */
struct aic3x_priv {
@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
+/*
+ * read from the aic3x register space
+ */
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
+ u8 *value)
+{
+ *value = reg & 0xff;
+ if (codec->hw_read(codec->control_data, value, 1) != 1)
+ return -EIO;
+
+ aic3x_write_reg_cache(codec, reg, *value);
+ return 0;
+}
+
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
@@ -192,7 +206,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
}
if (found)
- snd_soc_dapm_sync_endpoints(widget->codec);
+ snd_soc_dapm_sync(widget->codec);
}
ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -209,6 +223,8 @@ static const char *aic3x_right_hpcom_mux[] =
{ "differential of HPROUT", "constant VCM", "single-ended",
"differential of HPLCOM", "external feedback" };
static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
+static const char *aic3x_adc_hpf[] =
+ { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
#define LDAC_ENUM 0
#define RDAC_ENUM 1
@@ -218,6 +234,7 @@ static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
#define LINE1R_ENUM 5
#define LINE2L_ENUM 6
#define LINE2R_ENUM 7
+#define ADC_HPF_ENUM 8
static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -228,6 +245,7 @@ static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+ SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
};
static const struct snd_kcontrol_new aic3x_snd_controls[] = {
@@ -278,6 +296,8 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* Input */
SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+
+ SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
};
/* add non dapm controls */
@@ -441,11 +461,34 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line2_mux_controls),
+ /*
+ * Not a real mic bias widget but similar function. This is for dynamic
+ * control of GPIO1 digital mic modulator clock output function when
+ * using digital mic.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+ AIC3X_GPIO1_REG, 4, 0xf,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+ AIC3X_GPIO1_FUNC_DISABLED),
+
+ /*
+ * Also similar function like mic bias. Selects digital mic with
+ * configurable oversampling rate instead of ADC converter.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+ AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
/* Mic Bias */
- SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
- SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
- SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
- SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
+ MICBIAS_CTRL, 6, 3, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
+ MICBIAS_CTRL, 6, 3, 2, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
+ MICBIAS_CTRL, 6, 3, 3, 0),
/* Left PGA to Left Output bypass */
SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
@@ -483,7 +526,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINE2R"),
};
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
/* Left Output */
{"Left DAC Mux", "DAC_L1", "Left DAC"},
{"Left DAC Mux", "DAC_L2", "Left DAC"},
@@ -554,6 +597,7 @@ static const char *intercon[][3] = {
{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
{"Left ADC", NULL, "Left PGA Mixer"},
+ {"Left ADC", NULL, "GPIO1 dmic modclk"},
/* Right Input */
{"Right Line1R Mux", "single-ended", "LINE1R"},
@@ -567,6 +611,7 @@ static const char *intercon[][3] = {
{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
{"Right ADC", NULL, "Right PGA Mixer"},
+ {"Right ADC", NULL, "GPIO1 dmic modclk"},
/* Left PGA Bypass */
{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
@@ -628,101 +673,27 @@ static const char *intercon[][3] = {
{"Mono Out", NULL, "Right Line2 Bypass Mixer"},
{"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
- /* terminator */
- {NULL, NULL, NULL},
+ /*
+ * Logical path between digital mic enable and GPIO1 modulator clock
+ * output function
+ */
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+ {"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
};
static int aic3x_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+ ARRAY_SIZE(aic3x_dapm_widgets));
/* set up audio path interconnects */
- for (i = 0; intercon[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, intercon[i][0],
- intercon[i][1], intercon[i][2]);
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
-struct aic3x_rate_divs {
- u32 mclk;
- u32 rate;
- u32 fsref_reg;
- u8 sr_reg:4;
- u8 pllj_reg;
- u16 plld_reg;
-};
-
-/* AIC3X codec mclk clock divider coefficients */
-static const struct aic3x_rate_divs aic3x_divs[] = {
- /* 8k */
- {12000000, 8000, 48000, 0xa, 16, 3840},
- {19200000, 8000, 48000, 0xa, 10, 2400},
- {22579200, 8000, 48000, 0xa, 8, 7075},
- {33868800, 8000, 48000, 0xa, 5, 8049},
- /* 11.025k */
- {12000000, 11025, 44100, 0x6, 15, 528},
- {19200000, 11025, 44100, 0x6, 9, 4080},
- {22579200, 11025, 44100, 0x6, 8, 0},
- {33868800, 11025, 44100, 0x6, 5, 3333},
- /* 16k */
- {12000000, 16000, 48000, 0x4, 16, 3840},
- {19200000, 16000, 48000, 0x4, 10, 2400},
- {22579200, 16000, 48000, 0x4, 8, 7075},
- {33868800, 16000, 48000, 0x4, 5, 8049},
- /* 22.05k */
- {12000000, 22050, 44100, 0x2, 15, 528},
- {19200000, 22050, 44100, 0x2, 9, 4080},
- {22579200, 22050, 44100, 0x2, 8, 0},
- {33868800, 22050, 44100, 0x2, 5, 3333},
- /* 32k */
- {12000000, 32000, 48000, 0x1, 16, 3840},
- {19200000, 32000, 48000, 0x1, 10, 2400},
- {22579200, 32000, 48000, 0x1, 8, 7075},
- {33868800, 32000, 48000, 0x1, 5, 8049},
- /* 44.1k */
- {12000000, 44100, 44100, 0x0, 15, 528},
- {19200000, 44100, 44100, 0x0, 9, 4080},
- {22579200, 44100, 44100, 0x0, 8, 0},
- {33868800, 44100, 44100, 0x0, 5, 3333},
- /* 48k */
- {12000000, 48000, 48000, 0x0, 16, 3840},
- {19200000, 48000, 48000, 0x0, 10, 2400},
- {22579200, 48000, 48000, 0x0, 8, 7075},
- {33868800, 48000, 48000, 0x0, 5, 8049},
- /* 64k */
- {12000000, 64000, 96000, 0x1, 16, 3840},
- {19200000, 64000, 96000, 0x1, 10, 2400},
- {22579200, 64000, 96000, 0x1, 8, 7075},
- {33868800, 64000, 96000, 0x1, 5, 8049},
- /* 88.2k */
- {12000000, 88200, 88200, 0x0, 15, 528},
- {19200000, 88200, 88200, 0x0, 9, 4080},
- {22579200, 88200, 88200, 0x0, 8, 0},
- {33868800, 88200, 88200, 0x0, 5, 3333},
- /* 96k */
- {12000000, 96000, 96000, 0x0, 16, 3840},
- {19200000, 96000, 96000, 0x0, 10, 2400},
- {22579200, 96000, 96000, 0x0, 8, 7075},
- {33868800, 96000, 96000, 0x0, 5, 8049},
-};
-
-static inline int aic3x_get_divs(int mclk, int rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
- if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
- return i;
- }
-
- return 0;
-}
-
static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -730,49 +701,107 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct aic3x_priv *aic3x = codec->private_data;
- int i;
- u8 data, pll_p, pll_r, pll_j;
- u16 pll_d;
-
- i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
+ int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+ u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+ u16 pll_d = 1;
- /* Route Left DAC to left channel input and
- * right DAC to right channel input */
- data = (LDAC2LCH | RDAC2RCH);
- switch (aic3x_divs[i].fsref_reg) {
- case 44100:
- data |= FSREF_44100;
+ /* select data word length */
+ data =
+ aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
break;
- case 48000:
- data |= FSREF_48000;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ data |= (0x01 << 4);
break;
- case 88200:
- data |= FSREF_44100 | DUAL_RATE_MODE;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ data |= (0x02 << 4);
break;
- case 96000:
- data |= FSREF_48000 | DUAL_RATE_MODE;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ data |= (0x03 << 4);
break;
}
+ aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+
+ /* Fsref can be 44100 or 48000 */
+ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+
+ /* Try to find a value for Q which allows us to bypass the PLL and
+ * generate CODEC_CLK directly. */
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+
+ if (bypass_pll) {
+ pll_q &= 0xf;
+ aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+ aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+ } else
+ aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+
+ /* Route Left DAC to left channel input and
+ * right DAC to right channel input */
+ data = (LDAC2LCH | RDAC2RCH);
+ data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+ if (params_rate(params) >= 64000)
+ data |= DUAL_RATE_MODE;
aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
- data = aic3x_divs[i].sr_reg;
+ data = (fsref * 20) / params_rate(params);
+ if (params_rate(params) < 64000)
+ data /= 2;
+ data /= 5;
+ data -= 2;
data |= (data << 4);
aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
- /* Use PLL for generation Fsref by equation:
- * Fsref = (MCLK * K * R)/(2048 * P);
- * Fix P = 2 and R = 1 and calculate K, if
- * K = J.D, i.e. J - an interger portion of K and D is the fractional
- * one with 4 digits of precision;
- * Example:
- * For MCLK = 22.5792 MHz and Fsref = 48kHz:
- * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074
+ if (bypass_pll)
+ return 0;
+
+ /* Use PLL
+ * find an apropriate setup for j, d, r and p by iterating over
+ * p and r - j and d are calculated for each fraction.
+ * Up to 128 values are probed, the closest one wins the game.
+ * The sysclk is divided by 1000 to prevent integer overflows.
*/
- pll_p = 2;
- pll_r = 1;
- pll_j = aic3x_divs[i].pllj_reg;
- pll_d = aic3x_divs[i].plld_reg;
+ codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
+
+ for (r = 1; r <= 16; r++)
+ for (p = 1; p <= 8; p++) {
+ int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
+ u8 j = tmp / 10000;
+ u16 d = tmp % 10000;
+
+ if (j > 63)
+ continue;
+
+ if (d != 0 && aic3x->sysclk < 10000000)
+ continue;
+
+ /* This is actually 1000 * ((j + (d/10000)) * r) / p
+ * The term had to be converted to get rid of the
+ * division by 10000 */
+ clk = ((10000 * j * r) + (d * r)) / (10 * p);
+
+ /* check whether this values get closer than the best
+ * ones we had before */
+ if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+ pll_j = j; pll_d = d; pll_r = r; pll_p = p;
+ last_clk = clk;
+ }
+
+ /* Early exit for exact matches */
+ if (clk == codec_clk)
+ break;
+ }
+
+ if (last_clk == 0) {
+ printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+ return -EINVAL;
+ }
data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
@@ -782,28 +811,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
aic3x_write(codec, AIC3X_PLL_PROGD_REG,
(pll_d & 0x3F) << PLLD_LSB_SHIFT);
- /* select data word length */
- data =
- aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- data |= (0x01 << 4);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- data |= (0x02 << 4);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- data |= (0x03 << 4);
- break;
- }
- aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
-
return 0;
}
-static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
@@ -820,31 +831,25 @@ static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = codec->private_data;
- switch (freq) {
- case 12000000:
- case 19200000:
- case 22579200:
- case 33868800:
- aic3x->sysclk = freq;
- return 0;
- }
-
- return -EINVAL;
+ aic3x->sysclk = freq;
+ return 0;
}
-static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = codec->private_data;
- u8 iface_areg = 0;
- u8 iface_breg = 0;
+ u8 iface_areg, iface_breg;
+
+ iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+ iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -883,13 +888,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
struct aic3x_priv *aic3x = codec->private_data;
u8 reg;
- switch (event) {
- case SNDRV_CTL_POWER_D0:
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* all power is driven by DAPM system */
if (aic3x->master) {
/* enable pll */
@@ -898,10 +904,9 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
reg | PLL_ENABLE);
}
break;
- case SNDRV_CTL_POWER_D1:
- case SNDRV_CTL_POWER_D2:
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot:
+ case SND_SOC_BIAS_STANDBY:
/*
* all power is driven by DAPM system,
* so output power is safe if bypass was set
@@ -913,7 +918,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
reg & ~PLL_ENABLE);
}
break;
- case SNDRV_CTL_POWER_D3cold:
+ case SND_SOC_BIAS_OFF:
/* force all power off */
reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
@@ -949,16 +954,43 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
}
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
+{
+ u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+ u8 bit = gpio ? 3: 0;
+ u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
+ aic3x_write(codec, reg, val | (!!state << bit));
+}
+EXPORT_SYMBOL_GPL(aic3x_set_gpio);
+
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
+{
+ u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+ u8 val, bit = gpio ? 2: 1;
+
+ aic3x_read(codec, reg, &val);
+ return (val >> bit) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+
+int aic3x_headset_detected(struct snd_soc_codec *codec)
+{
+ u8 val;
+ aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
+ return (val >> 2) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+
#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
-struct snd_soc_codec_dai aic3x_dai = {
+struct snd_soc_dai aic3x_dai = {
.name = "aic3x",
.playback = {
.stream_name = "Playback",
@@ -988,7 +1020,7 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1008,7 +1040,7 @@ static int aic3x_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- aic3x_dapm_event(codec, codec->suspend_dapm_state);
+ aic3x_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -1020,16 +1052,17 @@ static int aic3x_resume(struct platform_device *pdev)
static int aic3x_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
+ struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0;
codec->name = "aic3x";
codec->owner = THIS_MODULE;
codec->read = aic3x_read_reg_cache;
codec->write = aic3x_write;
- codec->dapm_event = aic3x_dapm_event;
+ codec->set_bias_level = aic3x_set_bias_level;
codec->dai = &aic3x_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(aic3x_reg);
+ codec->reg_cache_size = ARRAY_SIZE(aic3x_reg);
codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -1108,7 +1141,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
/* off, with power on */
- aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* setup GPIO functions */
+ aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
+ aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
aic3x_add_controls(codec);
aic3x_add_widgets(codec);
@@ -1217,6 +1254,12 @@ static struct i2c_client client_template = {
.name = "AIC3X",
.driver = &aic3x_i2c_driver,
};
+
+static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+{
+ value[0] = i2c_smbus_read_byte_data(client, value[0]);
+ return (len == 1);
+}
#endif
static int aic3x_probe(struct platform_device *pdev)
@@ -1251,6 +1294,7 @@ static int aic3x_probe(struct platform_device *pdev)
if (setup->i2c_address) {
normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->hw_read = (hw_read_t) aic3x_i2c_read;
ret = i2c_add_driver(&aic3x_i2c_driver);
if (ret != 0)
printk(KERN_ERR "can't add i2c driver");
@@ -1268,7 +1312,7 @@ static int aic3x_remove(struct platform_device *pdev)
/* power down chip */
if (codec->control_data)
- aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d0cdeeb629d..d76c079b86e 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -37,6 +37,8 @@
#define AIC3X_ASD_INTF_CTRLB 9
/* Audio overflow status and PLL R value programming register */
#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL 12
/* ADC PGA Gain control registers */
#define LADC_VOL 15
@@ -108,6 +110,13 @@
#define DACR1_2_RLOPM_VOL 92
#define LLOPM_CTRL 86
#define RLOPM_CTRL 93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG 96
+#define AIC3X_RT_IRQ_FLAGS_REG 97
+#define AIC3X_GPIO1_REG 98
+#define AIC3X_GPIO2_REG 99
+#define AIC3X_GPIOA_REG 100
+#define AIC3X_GPIOB_REG 101
/* Clock generation control register */
#define AIC3X_CLKGEN_CTRL_REG 102
@@ -128,12 +137,15 @@
/* PLL registers bitfields */
#define PLLP_SHIFT 0
+#define PLLQ_SHIFT 3
#define PLLR_SHIFT 0
#define PLLJ_SHIFT 2
#define PLLD_MSB_SHIFT 0
#define PLLD_LSB_SHIFT 2
/* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV 0
+#define CODEC_CLKIN_CLKDIV 1
#define PLL_CLKIN_SHIFT 4
#define MCLK_SOURCE 0x0
#define PLL_CLKDIV_SHIFT 0
@@ -171,11 +183,52 @@
/* Default input volume */
#define DEFAULT_GAIN 0x20
+/* GPIO API */
+enum {
+ AIC3X_GPIO1_FUNC_DISABLED = 0,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX = 2,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4,
+ AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5,
+ AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6,
+ AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7,
+ AIC3X_GPIO1_FUNC_INPUT = 8,
+ AIC3X_GPIO1_FUNC_OUTPUT = 9,
+ AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10,
+ AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11,
+ AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13,
+ AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14,
+ AIC3X_GPIO1_FUNC_ALL_IRQ = 16
+};
+
+enum {
+ AIC3X_GPIO2_FUNC_DISABLED = 0,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2,
+ AIC3X_GPIO2_FUNC_INPUT = 3,
+ AIC3X_GPIO2_FUNC_OUTPUT = 4,
+ AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5,
+ AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8,
+ AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
+ AIC3X_GPIO2_FUNC_ALL_IRQ = 10,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
+ AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
+ AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13,
+ AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14,
+ AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15
+};
+
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+int aic3x_headset_detected(struct snd_soc_codec *codec);
+
struct aic3x_setup_data {
unsigned short i2c_address;
+ unsigned int gpio_func[2];
};
-extern struct snd_soc_codec_dai aic3x_dai;
+extern struct snd_soc_dai aic3x_dai;
extern struct snd_soc_codec_device soc_codec_dev_aic3x;
#endif /* _AIC3X_H */
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
new file mode 100644
index 00000000000..a52d6d9e007
--- /dev/null
+++ b/sound/soc/codecs/uda1380.c
@@ -0,0 +1,852 @@
+/*
+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
+ *
+ * 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.
+ *
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
+ * Improved support for DAPM and audio routing/mixing capabilities,
+ * added TLV support.
+ *
+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
+ * codec model.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright 2005 Openedhand Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "uda1380.h"
+
+#define UDA1380_VERSION "0.6"
+#define AUDIO_NAME "uda1380"
+
+/*
+ * uda1380 register cache
+ */
+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
+ 0x0502, 0x0000, 0x0000, 0x3f3f,
+ 0x0202, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xff00, 0x0000, 0x4800,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x8000, 0x0002, 0x0000,
+};
+
+/*
+ * read uda1380 register cache
+ */
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == UDA1380_RESET)
+ return 0;
+ if (reg >= UDA1380_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write uda1380 register cache
+ */
+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= UDA1380_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the UDA1380 register space
+ */
+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ /* data is
+ * data[0] is register offset
+ * data[1] is MS byte
+ * data[2] is LS byte
+ */
+ data[0] = reg;
+ data[1] = (value & 0xff00) >> 8;
+ data[2] = value & 0x00ff;
+
+ uda1380_write_reg_cache(codec, reg, value);
+
+ /* the interpolator & decimator regs must only be written when the
+ * codec DAI is active.
+ */
+ if (!codec->active && (reg >= UDA1380_MVOL))
+ return 0;
+ pr_debug("uda1380: hw write %x val %x\n", reg, value);
+ if (codec->hw_write(codec->control_data, data, 3) == 3) {
+ unsigned int val;
+ i2c_master_send(codec->control_data, data, 1);
+ i2c_master_recv(codec->control_data, data, 2);
+ val = (data[0]<<8) | data[1];
+ if (val != value) {
+ pr_debug("uda1380: READ BACK VAL %x\n",
+ (data[0]<<8) | data[1]);
+ return -EIO;
+ }
+ return 0;
+ } else
+ return -EIO;
+}
+
+#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
+
+/* declarations of ALSA reg_elem_REAL controls */
+static const char *uda1380_deemp[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+ "96kHz",
+};
+static const char *uda1380_input_sel[] = {
+ "Line",
+ "Mic + Line R",
+ "Line L",
+ "Mic",
+};
+static const char *uda1380_output_sel[] = {
+ "DAC",
+ "Analog Mixer",
+};
+static const char *uda1380_spf_mode[] = {
+ "Flat",
+ "Minimum1",
+ "Minimum2",
+ "Maximum"
+};
+static const char *uda1380_capture_sel[] = {
+ "ADC",
+ "Digital Mixer"
+};
+static const char *uda1380_sel_ns[] = {
+ "3rd-order",
+ "5th-order"
+};
+static const char *uda1380_mix_control[] = {
+ "off",
+ "PCM only",
+ "before sound processing",
+ "after sound processing"
+};
+static const char *uda1380_sdet_setting[] = {
+ "3200",
+ "4800",
+ "9600",
+ "19200"
+};
+static const char *uda1380_os_setting[] = {
+ "single-speed",
+ "double-speed (no mixing)",
+ "quad-speed (no mixing)"
+};
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
+ SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+};
+static const struct soc_enum uda1380_input_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel); /* SEL_MIC, SEL_LNA */
+static const struct soc_enum uda1380_output_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel); /* R02_EN_AVC */
+static const struct soc_enum uda1380_spf_enum =
+ SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode); /* M */
+static const struct soc_enum uda1380_capture_sel_enum =
+ SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel); /* SEL_SOURCE */
+static const struct soc_enum uda1380_sel_ns_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns); /* SEL_NS */
+static const struct soc_enum uda1380_mix_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control); /* MIX, MIX_POS */
+static const struct soc_enum uda1380_sdet_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting); /* SD_VALUE */
+static const struct soc_enum uda1380_os_enum =
+ SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting); /* OS */
+
+/*
+ * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
+
+/*
+ * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
+ * from -66 dB in 0.5 dB steps (2 dB steps, really) and
+ * from -52 dB in 0.25 dB steps
+ */
+static const unsigned int mvol_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
+ 16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
+ 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
+};
+
+/*
+ * from -72 dB in 1.5 dB steps (6 dB steps really),
+ * from -66 dB in 0.75 dB steps (3 dB steps really),
+ * from -60 dB in 0.5 dB steps (2 dB steps really) and
+ * from -46 dB in 0.25 dB steps
+ */
+static const unsigned int vc_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
+ 16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
+ 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
+};
+
+/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
+static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
+
+/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
+ * off at 18 dB max) */
+static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
+
+/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
+static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
+
+/* from 0 to 24 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+/* from 0 to 30 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
+ SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv), /* AVCR, AVCL */
+ SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv), /* MVCL, MVCR */
+ SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv), /* VC2 */
+ SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv), /* VC1 */
+ SOC_ENUM("Sound Processing Filter", uda1380_spf_enum), /* M */
+ SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), /* TRL, TRR */
+ SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv), /* BBL, BBR */
+/**/ SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1), /* MTM */
+ SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1), /* MT2 from decimation filter */
+ SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]), /* DE2 */
+ SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1), /* MT1, from digital data input */
+ SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]), /* DE1 */
+ SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
+ SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
+ SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
+ SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */
+ SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
+ SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
+ SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
+ SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv), /* ML_DEC, MR_DEC */
+/**/ SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1), /* MT_ADC */
+ SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+ SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0), /* ADCPOL_INV */
+ SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv), /* VGA_CTRL */
+ SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0), /* SKIP_DCFIL (before decimator) */
+ SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0), /* EN_DCFIL (at output of decimator) */
+ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0), /* TODO: enum, see table 62 */
+ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1), /* AGC_LEVEL */
+ /* -5.5, -8, -11.5, -14 dBFS */
+ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
+};
+
+/* add non dapm controls */
+static int uda1380_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Input mux */
+static const struct snd_kcontrol_new uda1380_input_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
+
+/* Output mux */
+static const struct snd_kcontrol_new uda1380_output_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+ SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
+
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_input_mux_control),
+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_output_mux_control),
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+ &uda1380_capture_mux_control),
+ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
+ SND_SOC_DAPM_INPUT("VINM"),
+ SND_SOC_DAPM_INPUT("VINL"),
+ SND_SOC_DAPM_INPUT("VINR"),
+ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
+ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* output mux */
+ {"HeadPhone Driver", NULL, "Output Mux"},
+ {"VOUTR", NULL, "Output Mux"},
+ {"VOUTL", NULL, "Output Mux"},
+
+ {"Analog Mixer", NULL, "VINR"},
+ {"Analog Mixer", NULL, "VINL"},
+ {"Analog Mixer", NULL, "DAC"},
+
+ {"Output Mux", "DAC", "DAC"},
+ {"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+ /* {"DAC", "Digital Mixer", "I2S" } */
+
+ /* headphone driver */
+ {"VOUTLHP", NULL, "HeadPhone Driver"},
+ {"VOUTRHP", NULL, "HeadPhone Driver"},
+
+ /* input mux */
+ {"Left ADC", NULL, "Input Mux"},
+ {"Input Mux", "Mic", "Mic LNA"},
+ {"Input Mux", "Mic + Line R", "Mic LNA"},
+ {"Input Mux", "Line L", "Left PGA"},
+ {"Input Mux", "Line", "Left PGA"},
+
+ /* right input */
+ {"Right ADC", "Mic + Line R", "Right PGA"},
+ {"Right ADC", "Line", "Right PGA"},
+
+ /* inputs */
+ {"Mic LNA", NULL, "VINM"},
+ {"Left PGA", NULL, "VINL"},
+ {"Right PGA", NULL, "VINR"},
+};
+
+static int uda1380_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
+ ARRAY_SIZE(uda1380_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
+
+ /* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORI_I2S | R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORI_MSB | R01_SFORO_I2S;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+ iface |= R01_SIM;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+/*
+ * Flush reg cache
+ * We can only write the interpolator and decimator registers
+ * when the DAI is being clocked by the CPU DAI. It's up to the
+ * machine and cpu DAI driver to do this before we are called.
+ */
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int reg, reg_start, reg_end, clk;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_start = UDA1380_MVOL;
+ reg_end = UDA1380_MIXER;
+ } else {
+ reg_start = UDA1380_DEC;
+ reg_end = UDA1380_AGC;
+ }
+
+ /* FIXME disable DAC_CLK */
+ clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+ uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
+
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ pr_debug("uda1380: flush reg %x val %x:", reg,
+ uda1380_read_reg_cache(codec, reg));
+ uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
+ }
+
+ /* FIXME enable DAC_CLK */
+ uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+
+ return 0;
+}
+
+static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* set WSPLL power and divider if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ int rate = params_rate(params);
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ clk &= ~0x3; /* clear SEL_LOOP_DIV */
+ switch (rate) {
+ case 6250 ... 12500:
+ clk |= 0x0;
+ break;
+ case 12501 ... 25000:
+ clk |= 0x1;
+ break;
+ case 25001 ... 50000:
+ clk |= 0x2;
+ break;
+ case 50001 ... 100000:
+ clk |= 0x3;
+ break;
+ }
+ uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk |= R00_EN_DAC | R00_EN_INT;
+ else
+ clk |= R00_EN_ADC | R00_EN_DEC;
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+ return 0;
+}
+
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+ /* shut down WSPLL power if running from this clock */
+ if (clk & R00_DAC_CLK) {
+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ clk &= ~(R00_EN_DAC | R00_EN_INT);
+ else
+ clk &= ~(R00_EN_ADC | R00_EN_DEC);
+
+ uda1380_write(codec, UDA1380_CLK, clk);
+}
+
+static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
+
+ /* FIXME: mute(codec,0) is called when the magician clock is already
+ * set to WSPLL, but for some unknown reason writing to interpolator
+ * registers works only when clocked by SYSCLK */
+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+ uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
+ if (mute)
+ uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
+ else
+ uda1380_write(codec, UDA1380_DEEMP, mute_reg);
+ uda1380_write(codec, UDA1380_CLK, clk);
+ return 0;
+}
+
+static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ uda1380_write(codec, UDA1380_PM, 0x0);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai uda1380_dai[] = {
+{
+ .name = "UDA1380",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .digital_mute = uda1380_mute,
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+{ /* playback only - dual interface */
+ .name = "UDA1380",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .digital_mute = uda1380_mute,
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+{ /* capture only - dual interface*/
+ .name = "UDA1380",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA1380_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .prepare = uda1380_pcm_prepare,
+ },
+ .dai_ops = {
+ .set_fmt = uda1380_set_dai_fmt,
+ },
+},
+};
+EXPORT_SYMBOL_GPL(uda1380_dai);
+
+static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int uda1380_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ uda1380_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the UDA1380 driver
+ * register mixer and dsp interfaces with the kernel
+ */
+static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "UDA1380";
+ codec->owner = THIS_MODULE;
+ codec->read = uda1380_read_reg_cache;
+ codec->write = uda1380_write;
+ codec->set_bias_level = uda1380_set_bias_level;
+ codec->dai = uda1380_dai;
+ codec->num_dai = ARRAY_SIZE(uda1380_dai);
+ codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+ codec->reg_cache_step = 1;
+ uda1380_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ pr_err("uda1380: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* set clock input */
+ switch (dac_clk) {
+ case UDA1380_DAC_CLK_SYSCLK:
+ uda1380_write(codec, UDA1380_CLK, 0);
+ break;
+ case UDA1380_DAC_CLK_WSPLL:
+ uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
+ break;
+ }
+
+ /* uda1380 init */
+ uda1380_add_controls(codec);
+ uda1380_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ pr_err("uda1380: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *uda1380_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+#define I2C_DRIVERID_UDA1380 0xfefe /* liam - need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver uda1380_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+
+static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = uda1380_socdev;
+ struct uda1380_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ pr_err("uda1380: failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = uda1380_init(socdev, setup->dac_clk);
+ if (ret < 0) {
+ pr_err("uda1380: failed to initialise UDA1380\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int uda1380_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int uda1380_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, uda1380_codec_probe);
+}
+
+static struct i2c_driver uda1380_i2c_driver = {
+ .driver = {
+ .name = "UDA1380 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .id = I2C_DRIVERID_UDA1380,
+ .attach_adapter = uda1380_i2c_attach,
+ .detach_client = uda1380_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "UDA1380",
+ .driver = &uda1380_i2c_driver,
+};
+#endif
+
+static int uda1380_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct uda1380_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ uda1380_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&uda1380_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&uda1380_i2c_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+ .probe = uda1380_probe,
+ .remove = uda1380_remove,
+ .suspend = uda1380_suspend,
+ .resume = uda1380_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
new file mode 100644
index 00000000000..50c603e2c9f
--- /dev/null
+++ b/sound/soc/codecs/uda1380.h
@@ -0,0 +1,89 @@
+/*
+ * Audio support for Philips UDA1380
+ *
+ * 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.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ */
+
+#ifndef _UDA1380_H
+#define _UDA1380_H
+
+#define UDA1380_CLK 0x00
+#define UDA1380_IFACE 0x01
+#define UDA1380_PM 0x02
+#define UDA1380_AMIX 0x03
+#define UDA1380_HP 0x04
+#define UDA1380_MVOL 0x10
+#define UDA1380_MIXVOL 0x11
+#define UDA1380_MODE 0x12
+#define UDA1380_DEEMP 0x13
+#define UDA1380_MIXER 0x14
+#define UDA1380_INTSTAT 0x18
+#define UDA1380_DEC 0x20
+#define UDA1380_PGA 0x21
+#define UDA1380_ADC 0x22
+#define UDA1380_AGC 0x23
+#define UDA1380_DECSTAT 0x28
+#define UDA1380_RESET 0x7f
+
+#define UDA1380_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC 0x0800
+#define R00_EN_DEC 0x0400
+#define R00_EN_DAC 0x0200
+#define R00_EN_INT 0x0100
+#define R00_DAC_CLK 0x0010
+#define R01_SFORI_I2S 0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB 0x0500
+#define R01_SFORI_MASK 0x0700
+#define R01_SFORO_I2S 0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB 0x0005
+#define R01_SFORO_MASK 0x0007
+#define R01_SEL_SOURCE 0x0040
+#define R01_SIM 0x0010
+#define R02_PON_PLL 0x8000
+#define R02_PON_HP 0x2000
+#define R02_PON_DAC 0x0400
+#define R02_PON_BIAS 0x0100
+#define R02_EN_AVC 0x0080
+#define R02_PON_AVC 0x0040
+#define R02_PON_LNA 0x0010
+#define R02_PON_PGAL 0x0008
+#define R02_PON_ADCL 0x0004
+#define R02_PON_PGAR 0x0002
+#define R02_PON_ADCR 0x0001
+#define R13_MTM 0x4000
+#define R14_SILENCE 0x0080
+#define R14_SDET_ON 0x0040
+#define R21_MT_ADC 0x8000
+#define R22_SEL_LNA 0x0008
+#define R22_SEL_MIC 0x0004
+#define R22_SKIP_DCFIL 0x0002
+#define R23_AGC_EN 0x0001
+
+struct uda1380_setup_data {
+ unsigned short i2c_address;
+ int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL 1
+};
+
+#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
+#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
+#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
+
+extern struct snd_soc_dai uda1380_dai[3];
+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
+
+#endif /* _UDA1380_H */
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
new file mode 100644
index 00000000000..67325fd9544
--- /dev/null
+++ b/sound/soc/codecs/wm8510.c
@@ -0,0 +1,817 @@
+/*
+ * wm8510.c -- WM8510 ALSA Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <liam.girdwood@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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8510.h"
+
+#define AUDIO_NAME "wm8510"
+#define WM8510_VERSION "0.6"
+
+struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+/*
+ * wm8510 register cache
+ * We can't read the WM8510 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0050, 0x0000, 0x0140, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00ff,
+ 0x0000, 0x0000, 0x0100, 0x00ff,
+ 0x0000, 0x0000, 0x012c, 0x002c,
+ 0x002c, 0x002c, 0x002c, 0x0000,
+ 0x0032, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0038, 0x000b, 0x0032, 0x0000,
+ 0x0008, 0x000c, 0x0093, 0x00e9,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0003, 0x0010, 0x0000, 0x0000,
+ 0x0000, 0x0002, 0x0001, 0x0000,
+ 0x0000, 0x0000, 0x0039, 0x0000,
+ 0x0001,
+};
+
+/*
+ * read wm8510 register cache
+ */
+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == WM8510_RESET)
+ return 0;
+ if (reg >= WM8510_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write wm8510 register cache
+ */
+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= WM8510_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8510 register space
+ */
+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8510 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8510_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
+
+static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8510_alc[] = { "ALC", "Limiter" };
+
+static const struct soc_enum wm8510_enum[] = {
+ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
+ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
+ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
+ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
+};
+
+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8510_enum[1]),
+SOC_ENUM("ADC Companding", wm8510_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
+
+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
+SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
+};
+
+/* add non dapm controls */
+static int wm8510_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8510_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_boost_controls[] = {
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 0),
+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
+SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
+SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
+ &wm8510_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
+ &wm8510_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8510_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0,
+ &wm8510_micpga_controls[0],
+ ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
+ &wm8510_boost_controls[0],
+ ARRAY_SIZE(wm8510_boost_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Microphone PGA */
+ {"Mic PGA", "MICN Switch", "MICN"},
+ {"Mic PGA", "MICP Switch", "MICP"},
+ { "Mic PGA", "AUX Switch", "Aux Input" },
+
+ /* Boost Mixer */
+ {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+ {"Boost Mixer", "Mic Volume", "MICP"},
+ {"Boost Mixer", "Aux Volume", "Aux Input"},
+
+ {"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8510_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets,
+ ARRAY_SIZE(wm8510_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct pll_ {
+ unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int n:4;
+ unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div.pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div.pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8510 N value %d outwith recommended range!d\n",
+ Ndiv);
+
+ pll_div.n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div.k = K;
+}
+
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+ wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+ /* Turn off PLL */
+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+ wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+ return 0;
+ }
+
+ pll_factors(freq_out*8, freq_in);
+
+ wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+ wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+ wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+ wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+/*
+ * Configure WM8510 clock dividers.
+ */
+static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8510_OPCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
+ wm8510_write(codec, WM8510_GPIO, reg | div);
+ break;
+ case WM8510_MCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
+ wm8510_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ case WM8510_ADCCLK:
+ reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
+ wm8510_write(codec, WM8510_ADC, reg | div);
+ break;
+ case WM8510_DACCLK:
+ reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
+ wm8510_write(codec, WM8510_DAC, reg | div);
+ break;
+ case WM8510_BCLKDIV:
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
+ wm8510_write(codec, WM8510_CLOCK, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+ u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x00018;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8510_write(codec, WM8510_IFACE, iface);
+ wm8510_write(codec, WM8510_CLOCK, clk);
+ return 0;
+}
+
+static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
+ u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0020;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0040;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x0060;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case SNDRV_PCM_RATE_8000:
+ adn |= 0x5 << 1;
+ break;
+ case SNDRV_PCM_RATE_11025:
+ adn |= 0x4 << 1;
+ break;
+ case SNDRV_PCM_RATE_16000:
+ adn |= 0x3 << 1;
+ break;
+ case SNDRV_PCM_RATE_22050:
+ adn |= 0x2 << 1;
+ break;
+ case SNDRV_PCM_RATE_32000:
+ adn |= 0x1 << 1;
+ break;
+ case SNDRV_PCM_RATE_44100:
+ case SNDRV_PCM_RATE_48000:
+ break;
+ }
+
+ wm8510_write(codec, WM8510_IFACE, iface);
+ wm8510_write(codec, WM8510_ADD, adn);
+ return 0;
+}
+
+static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+
+ if (mute)
+ wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+ else
+ wm8510_write(codec, WM8510_DAC, mute_reg);
+ return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ wm8510_write(codec, WM8510_POWER1, 0x1ff);
+ wm8510_write(codec, WM8510_POWER2, 0x1ff);
+ wm8510_write(codec, WM8510_POWER3, 0x1ff);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ wm8510_write(codec, WM8510_POWER1, 0x0);
+ wm8510_write(codec, WM8510_POWER2, 0x0);
+ wm8510_write(codec, WM8510_POWER3, 0x0);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8510_dai = {
+ .name = "WM8510 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8510_RATES,
+ .formats = WM8510_FORMATS,},
+ .ops = {
+ .hw_params = wm8510_pcm_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = wm8510_mute,
+ .set_fmt = wm8510_set_dai_fmt,
+ .set_clkdiv = wm8510_set_dai_clkdiv,
+ .set_pll = wm8510_set_dai_pll,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8510_dai);
+
+static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8510_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8510_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialise the WM8510 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8510_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "WM8510";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8510_read_reg_cache;
+ codec->write = wm8510_write;
+ codec->set_bias_level = wm8510_set_bias_level;
+ codec->dai = &wm8510_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
+ codec->reg_cache = kmemdup(wm8510_reg, sizeof(wm8510_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8510_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8510_add_controls(codec);
+ wm8510_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *wm8510_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8510 2 wire address is 0x1a
+ */
+#define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8510_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+
+static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8510_socdev;
+ struct wm8510_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ pr_err("failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8510_init(socdev);
+ if (ret < 0) {
+ pr_err("failed to initialise WM8510\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8510_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int wm8510_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8510_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8510_i2c_driver = {
+ .driver = {
+ .name = "WM8510 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .id = I2C_DRIVERID_WM8510,
+ .attach_adapter = wm8510_i2c_attach,
+ .detach_client = wm8510_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8510",
+ .driver = &wm8510_i2c_driver,
+};
+#endif
+
+static int wm8510_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8510_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ pr_info("WM8510 Audio Codec %s", WM8510_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ wm8510_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&wm8510_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int wm8510_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8510_i2c_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
+ .probe = wm8510_probe,
+ .remove = wm8510_remove,
+ .suspend = wm8510_suspend,
+ .resume = wm8510_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
+
+MODULE_DESCRIPTION("ASoC WM8510 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
new file mode 100644
index 00000000000..f5d2e42eb3f
--- /dev/null
+++ b/sound/soc/codecs/wm8510.h
@@ -0,0 +1,103 @@
+/*
+ * wm8510.h -- WM8510 Soc Audio driver
+ *
+ * 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 _WM8510_H
+#define _WM8510_H
+
+/* WM8510 register space */
+
+#define WM8510_RESET 0x0
+#define WM8510_POWER1 0x1
+#define WM8510_POWER2 0x2
+#define WM8510_POWER3 0x3
+#define WM8510_IFACE 0x4
+#define WM8510_COMP 0x5
+#define WM8510_CLOCK 0x6
+#define WM8510_ADD 0x7
+#define WM8510_GPIO 0x8
+#define WM8510_DAC 0xa
+#define WM8510_DACVOL 0xb
+#define WM8510_ADC 0xe
+#define WM8510_ADCVOL 0xf
+#define WM8510_EQ1 0x12
+#define WM8510_EQ2 0x13
+#define WM8510_EQ3 0x14
+#define WM8510_EQ4 0x15
+#define WM8510_EQ5 0x16
+#define WM8510_DACLIM1 0x18
+#define WM8510_DACLIM2 0x19
+#define WM8510_NOTCH1 0x1b
+#define WM8510_NOTCH2 0x1c
+#define WM8510_NOTCH3 0x1d
+#define WM8510_NOTCH4 0x1e
+#define WM8510_ALC1 0x20
+#define WM8510_ALC2 0x21
+#define WM8510_ALC3 0x22
+#define WM8510_NGATE 0x23
+#define WM8510_PLLN 0x24
+#define WM8510_PLLK1 0x25
+#define WM8510_PLLK2 0x26
+#define WM8510_PLLK3 0x27
+#define WM8510_ATTEN 0x28
+#define WM8510_INPUT 0x2c
+#define WM8510_INPPGA 0x2d
+#define WM8510_ADCBOOST 0x2f
+#define WM8510_OUTPUT 0x31
+#define WM8510_SPKMIX 0x32
+#define WM8510_SPKVOL 0x36
+#define WM8510_MONOMIX 0x38
+
+#define WM8510_CACHEREGNUM 57
+
+/* Clock divider Id's */
+#define WM8510_OPCLKDIV 0
+#define WM8510_MCLKDIV 1
+#define WM8510_ADCCLK 2
+#define WM8510_DACCLK 3
+#define WM8510_BCLKDIV 4
+
+/* DAC clock dividers */
+#define WM8510_DACCLK_F2 (1 << 3)
+#define WM8510_DACCLK_F4 (0 << 3)
+
+/* ADC clock dividers */
+#define WM8510_ADCCLK_F2 (1 << 3)
+#define WM8510_ADCCLK_F4 (0 << 3)
+
+/* PLL Out dividers */
+#define WM8510_OPCLKDIV_1 (0 << 4)
+#define WM8510_OPCLKDIV_2 (1 << 4)
+#define WM8510_OPCLKDIV_3 (2 << 4)
+#define WM8510_OPCLKDIV_4 (3 << 4)
+
+/* BCLK clock dividers */
+#define WM8510_BCLKDIV_1 (0 << 2)
+#define WM8510_BCLKDIV_2 (1 << 2)
+#define WM8510_BCLKDIV_4 (2 << 2)
+#define WM8510_BCLKDIV_8 (3 << 2)
+#define WM8510_BCLKDIV_16 (4 << 2)
+#define WM8510_BCLKDIV_32 (5 << 2)
+
+/* MCLK clock dividers */
+#define WM8510_MCLKDIV_1 (0 << 5)
+#define WM8510_MCLKDIV_1_5 (1 << 5)
+#define WM8510_MCLKDIV_2 (2 << 5)
+#define WM8510_MCLKDIV_3 (3 << 5)
+#define WM8510_MCLKDIV_4 (4 << 5)
+#define WM8510_MCLKDIV_6 (5 << 5)
+#define WM8510_MCLKDIV_8 (6 << 5)
+#define WM8510_MCLKDIV_12 (7 << 5)
+
+struct wm8510_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8510_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0cf9265fca8..369d39c3f74 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -31,25 +31,6 @@
#define AUDIO_NAME "wm8731"
#define WM8731_VERSION "0.13"
-/*
- * Debug
- */
-
-#define WM8731_DEBUG 0
-
-#ifdef WM8731_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
struct snd_soc_codec_device soc_codec_dev_wm8731;
/* codec private data */
@@ -193,7 +174,7 @@ SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"),
};
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -214,22 +195,14 @@ static const char *intercon[][3] = {
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8731_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
- /* set up audio path interconnects */
- for (i = 0; intercon[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, intercon[i][0],
- intercon[i][1], intercon[i][2]);
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -345,7 +318,7 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream)
}
}
-static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
@@ -357,7 +330,7 @@ static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -376,7 +349,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
}
-static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -435,29 +408,29 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
wm8731_write(codec, WM8731_PWR, reg);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
wm8731_write(codec, WM8731_PWR, reg | 0x0040);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_write(codec, WM8731_PWR, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -470,7 +443,7 @@ static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-struct snd_soc_codec_dai wm8731_dai = {
+struct snd_soc_dai wm8731_dai = {
.name = "WM8731",
.playback = {
.stream_name = "Playback",
@@ -503,7 +476,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_codec *codec = socdev->codec;
wm8731_write(codec, WM8731_ACTIVE, 0x0);
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -521,8 +494,8 @@ static int wm8731_resume(struct platform_device *pdev)
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
- wm8731_dapm_event(codec, codec->suspend_dapm_state);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -539,10 +512,10 @@ static int wm8731_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8731_read_reg_cache;
codec->write = wm8731_write;
- codec->dapm_event = wm8731_dapm_event;
+ codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(wm8731_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -557,7 +530,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
}
/* power on device */
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set the update bits */
reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
@@ -632,13 +605,13 @@ static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
ret = i2c_attach_client(i2c);
if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
+ pr_err("failed to attach codec at addr %x\n", addr);
goto err;
}
ret = wm8731_init(socdev);
if (ret < 0) {
- err("failed to initialise WM8731\n");
+ pr_err("failed to initialise WM8731\n");
goto err;
}
return ret;
@@ -689,7 +662,7 @@ static int wm8731_probe(struct platform_device *pdev)
struct wm8731_priv *wm8731;
int ret = 0;
- info("WM8731 Audio Codec %s", WM8731_VERSION);
+ pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -730,7 +703,7 @@ static int wm8731_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 5bcab6a7afb..99f2e3c60e3 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -38,7 +38,7 @@ struct wm8731_setup_data {
unsigned short i2c_address;
};
-extern struct snd_soc_codec_dai wm8731_dai;
+extern struct snd_soc_dai wm8731_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8731;
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 16cd5d4d5ad..e23cb09f0d1 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -31,25 +31,6 @@
#define AUDIO_NAME "WM8750"
#define WM8750_VERSION "0.12"
-/*
- * Debug
- */
-
-#define WM8750_DEBUG 0
-
-#ifdef WM8750_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
/* codec private data */
struct wm8750_priv {
unsigned int sysclk;
@@ -378,7 +359,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("RINPUT3"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left mixer */
{"Left Mixer", "Playback Switch", "Left DAC"},
{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
@@ -470,22 +451,14 @@ static const char *audio_map[][3] = {
/* ADC */
{"Left ADC", NULL, "Left ADC Mux"},
{"Right ADC", NULL, "Right ADC Mux"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8750_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+ ARRAY_SIZE(wm8750_dapm_widgets));
- /* set up audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -563,7 +536,7 @@ static inline int get_coeff(int mclk, int rate)
return -EINVAL;
}
-static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -581,7 +554,7 @@ static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
return -EINVAL;
}
-static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -674,7 +647,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8750_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
@@ -686,29 +659,29 @@ static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
wm8750_write(codec, WM8750_PWR1, 0x0001);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -719,7 +692,7 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-struct snd_soc_codec_dai wm8750_dai = {
+struct snd_soc_dai wm8750_dai = {
.name = "WM8750",
.playback = {
.stream_name = "Playback",
@@ -748,7 +721,7 @@ static void wm8750_work(struct work_struct *work)
{
struct snd_soc_codec *codec =
container_of(work, struct snd_soc_codec, delayed_work.work);
- wm8750_dapm_event(codec, codec->dapm_state);
+ wm8750_set_bias_level(codec, codec->bias_level);
}
static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
@@ -756,7 +729,7 @@ static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -777,12 +750,12 @@ static int wm8750_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* charge wm8750 caps */
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D0;
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_ON;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(1000));
}
@@ -803,10 +776,10 @@ static int wm8750_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8750_read_reg_cache;
codec->write = wm8750_write;
- codec->dapm_event = wm8750_dapm_event;
+ codec->set_bias_level = wm8750_set_bias_level;
codec->dai = &wm8750_dai;
codec->num_dai = 1;
- codec->reg_cache_size = sizeof(wm8750_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
@@ -821,8 +794,8 @@ static int wm8750_init(struct snd_soc_device *socdev)
}
/* charge output caps */
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
/* set the update bits */
@@ -904,13 +877,13 @@ static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
ret = i2c_attach_client(i2c);
if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
+ pr_err("failed to attach codec at addr %x\n", addr);
goto err;
}
ret = wm8750_init(socdev);
if (ret < 0) {
- err("failed to initialise WM8750\n");
+ pr_err("failed to initialise WM8750\n");
goto err;
}
return ret;
@@ -961,7 +934,7 @@ static int wm8750_probe(struct platform_device *pdev)
struct wm8750_priv *wm8750;
int ret = 0;
- info("WM8750 Audio Codec %s", WM8750_VERSION);
+ pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
@@ -1021,7 +994,7 @@ static int wm8750_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index a97a54a6348..8ef30e628b2 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -61,7 +61,7 @@ struct wm8750_setup_data {
unsigned short i2c_address;
};
-extern struct snd_soc_codec_dai wm8750_dai;
+extern struct snd_soc_dai wm8750_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8750;
#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index fb41826c4c4..8604809f0c3 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -55,25 +55,6 @@
#define AUDIO_NAME "wm8753"
#define WM8753_VERSION "0.16"
-/*
- * Debug
- */
-
-#define WM8753_DEBUG 0
-
-#ifdef WM8753_DEBUG
-#define dbg(format, arg...) \
- printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
- printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
- printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
- printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@ -260,28 +241,50 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
return 1;
}
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const unsigned int out_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ /* 0000000 - 0101111 = "Analogue mute" */
+ 0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
+};
+static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
static const struct snd_kcontrol_new wm8753_snd_controls[] = {
-SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
-
-SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
-
-SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
-SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
-
-SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
-
-SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
-SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
-SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
-
-SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
-
-SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
-SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1),
+SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
+ adc_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0,
+ 127, 0, out_tlv),
+
+SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7,
+ 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4,
+ 7, 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7,
+ 1, voice_mix_tlv),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7,
+ 1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7,
+ 1, 0),
+
+SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv),
+SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1,
+ voice_mix_tlv),
SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
SOC_ENUM("Bass Boost", wm8753_enum[0]),
@@ -291,10 +294,13 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
-SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv),
-SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
+SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1,
+ rec_mix_tlv),
+SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1,
+ rec_mix_tlv),
-SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0,
+ pga_tlv),
SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
@@ -326,8 +332,8 @@ SOC_ENUM("De-emphasis", wm8753_enum[8]),
SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
SOC_ENUM("Playback Phase", wm8753_enum[10]),
-SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
-SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
+SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv),
+SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv),
SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
@@ -523,7 +529,7 @@ SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_VMID("VREF"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left mixer */
{"Left Mixer", "Left Playback Switch", "Left DAC"},
{"Left Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -674,23 +680,14 @@ static const char *audio_map[][3] = {
/* ACOP */
{"ACOP", NULL, "ALC Mixer"},
-
- /* terminator */
- {NULL, NULL, NULL},
};
static int wm8753_add_widgets(struct snd_soc_codec *codec)
{
- int i;
+ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+ ARRAY_SIZE(wm8753_dapm_widgets));
- for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-
- /* set up the WM8753 audio map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -743,7 +740,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
u16 reg, enable;
@@ -866,7 +863,7 @@ static int get_coeff(int mclk, int rate)
/*
* Clock after PLL and dividers
*/
-static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -893,7 +890,7 @@ static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
/*
* Set's ADC and Voice DAC format.
*/
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -963,7 +960,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
/*
* Set's PCM dai fmt and BCLK.
*/
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1029,7 +1026,7 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1057,7 +1054,7 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
/*
* Set's HiFi DAC format.
*/
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1090,7 +1087,7 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
/*
* Set's I2S DAI format.
*/
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1198,7 +1195,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1213,7 +1210,7 @@ static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
@@ -1221,7 +1218,7 @@ static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1236,7 +1233,7 @@ static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1253,7 +1250,7 @@ static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
}
-static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8753_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
@@ -1274,29 +1271,29 @@ static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
return 0;
}
-static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
wm8753_write(codec, WM8753_PWR1, 0x0001);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -1319,7 +1316,7 @@ static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
-static const struct snd_soc_codec_dai wm8753_all_dai[] = {
+static const struct snd_soc_dai wm8753_all_dai[] = {
/* DAI HiFi mode 1 */
{ .name = "WM8753 HiFi",
.id = 1,
@@ -1459,7 +1456,7 @@ static const struct snd_soc_codec_dai wm8753_all_dai[] = {
},
};
-struct snd_soc_codec_dai wm8753_dai[2];
+struct snd_soc_dai wm8753_dai[2];
EXPORT_SYMBOL_GPL(wm8753_dai);
static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
@@ -1500,7 +1497,7 @@ static void wm8753_work(struct work_struct *work)
{
struct snd_soc_codec *codec =
container_of(work, struct snd_soc_codec, delayed_work.work);
- wm8753_dapm_event(codec, codec->dapm_state);
+ wm8753_set_bias_level(codec, codec->bias_level);
}
static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
@@ -1512,7 +1509,7 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
if (!codec->card)
return 0;
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1537,12 +1534,12 @@ static int wm8753_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* charge wm8753 caps */
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D0;
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_ON;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
}
@@ -1563,10 +1560,10 @@ static int wm8753_init(struct snd_soc_device *socdev)
codec->owner = THIS_MODULE;
codec->read = wm8753_read_reg_cache;
codec->write = wm8753_write;
- codec->dapm_event = wm8753_dapm_event;
+ codec->set_bias_level = wm8753_set_bias_level;
codec->dai = wm8753_dai;
codec->num_dai = 2;
- codec->reg_cache_size = sizeof(wm8753_reg);
+ codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
@@ -1584,8 +1581,8 @@ static int wm8753_init(struct snd_soc_device *socdev)
}
/* charge output caps */
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
- codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
@@ -1673,13 +1670,13 @@ static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
ret = i2c_attach_client(i2c);
if (ret < 0) {
- err("failed to attach codec at addr %x\n", addr);
+ pr_err("failed to attach codec at addr %x\n", addr);
goto err;
}
ret = wm8753_init(socdev);
if (ret < 0) {
- err("failed to initialise WM8753\n");
+ pr_err("failed to initialise WM8753\n");
goto err;
}
@@ -1731,7 +1728,7 @@ static int wm8753_probe(struct platform_device *pdev)
struct wm8753_priv *wm8753;
int ret = 0;
- info("WM8753 Audio Codec %s", WM8753_VERSION);
+ pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -1792,7 +1789,7 @@ static int wm8753_remove(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
- wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 95e2a1f5316..44f5f1ff0cc 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -120,7 +120,7 @@ struct wm8753_setup_data {
#define WM8753_DAI_HIFI 0
#define WM8753_DAI_VOICE 1
-extern struct snd_soc_codec_dai wm8753_dai[2];
+extern struct snd_soc_dai wm8753_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_wm8753;
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
new file mode 100644
index 00000000000..3ecce5168e9
--- /dev/null
+++ b/sound/soc/codecs/wm8990.c
@@ -0,0 +1,1626 @@
+/*
+ * wm8990.c -- WM8990 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * lg@opensource.wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8990.h"
+
+#define AUDIO_NAME "wm8990"
+#define WM8990_VERSION "0.2"
+
+/* codec private data */
+struct wm8990_priv {
+ unsigned int sysclk;
+ unsigned int pcmclk;
+};
+
+/*
+ * wm8990 register cache. Note that register 0 is not included in the
+ * cache.
+ */
+static const u16 wm8990_reg[] = {
+ 0x8990, /* R0 - Reset */
+ 0x0000, /* R1 - Power Management (1) */
+ 0x6000, /* R2 - Power Management (2) */
+ 0x0000, /* R3 - Power Management (3) */
+ 0x4050, /* R4 - Audio Interface (1) */
+ 0x4000, /* R5 - Audio Interface (2) */
+ 0x01C8, /* R6 - Clocking (1) */
+ 0x0000, /* R7 - Clocking (2) */
+ 0x0040, /* R8 - Audio Interface (3) */
+ 0x0040, /* R9 - Audio Interface (4) */
+ 0x0004, /* R10 - DAC CTRL */
+ 0x00C0, /* R11 - Left DAC Digital Volume */
+ 0x00C0, /* R12 - Right DAC Digital Volume */
+ 0x0000, /* R13 - Digital Side Tone */
+ 0x0100, /* R14 - ADC CTRL */
+ 0x00C0, /* R15 - Left ADC Digital Volume */
+ 0x00C0, /* R16 - Right ADC Digital Volume */
+ 0x0000, /* R17 */
+ 0x0000, /* R18 - GPIO CTRL 1 */
+ 0x1000, /* R19 - GPIO1 & GPIO2 */
+ 0x1010, /* R20 - GPIO3 & GPIO4 */
+ 0x1010, /* R21 - GPIO5 & GPIO6 */
+ 0x8000, /* R22 - GPIOCTRL 2 */
+ 0x0800, /* R23 - GPIO_POL */
+ 0x008B, /* R24 - Left Line Input 1&2 Volume */
+ 0x008B, /* R25 - Left Line Input 3&4 Volume */
+ 0x008B, /* R26 - Right Line Input 1&2 Volume */
+ 0x008B, /* R27 - Right Line Input 3&4 Volume */
+ 0x0000, /* R28 - Left Output Volume */
+ 0x0000, /* R29 - Right Output Volume */
+ 0x0066, /* R30 - Line Outputs Volume */
+ 0x0022, /* R31 - Out3/4 Volume */
+ 0x0079, /* R32 - Left OPGA Volume */
+ 0x0079, /* R33 - Right OPGA Volume */
+ 0x0003, /* R34 - Speaker Volume */
+ 0x0003, /* R35 - ClassD1 */
+ 0x0000, /* R36 */
+ 0x0100, /* R37 - ClassD3 */
+ 0x0000, /* R38 */
+ 0x0000, /* R39 - Input Mixer1 */
+ 0x0000, /* R40 - Input Mixer2 */
+ 0x0000, /* R41 - Input Mixer3 */
+ 0x0000, /* R42 - Input Mixer4 */
+ 0x0000, /* R43 - Input Mixer5 */
+ 0x0000, /* R44 - Input Mixer6 */
+ 0x0000, /* R45 - Output Mixer1 */
+ 0x0000, /* R46 - Output Mixer2 */
+ 0x0000, /* R47 - Output Mixer3 */
+ 0x0000, /* R48 - Output Mixer4 */
+ 0x0000, /* R49 - Output Mixer5 */
+ 0x0000, /* R50 - Output Mixer6 */
+ 0x0180, /* R51 - Out3/4 Mixer */
+ 0x0000, /* R52 - Line Mixer1 */
+ 0x0000, /* R53 - Line Mixer2 */
+ 0x0000, /* R54 - Speaker Mixer */
+ 0x0000, /* R55 - Additional Control */
+ 0x0000, /* R56 - AntiPOP1 */
+ 0x0000, /* R57 - AntiPOP2 */
+ 0x0000, /* R58 - MICBIAS */
+ 0x0000, /* R59 */
+ 0x0008, /* R60 - PLL1 */
+ 0x0031, /* R61 - PLL2 */
+ 0x0026, /* R62 - PLL3 */
+};
+
+/*
+ * read wm8990 register cache
+ */
+static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+ return cache[reg];
+}
+
+/*
+ * write wm8990 register cache
+ */
+static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+
+ /* Reset register is uncached */
+ if (reg == 0)
+ return;
+
+ cache[reg] = value;
+}
+
+/*
+ * write to the wm8990 register space
+ */
+static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+
+ data[0] = reg & 0xFF;
+ data[1] = (value >> 8) & 0xFF;
+ data[2] = value & 0xFF;
+
+ wm8990_write_reg_cache(codec, reg, value);
+
+ if (codec->hw_write(codec->control_data, data, 3) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = wm8990_read_reg_cache(codec, reg);
+ return wm8990_write(codec, reg, val | 0x0100);
+}
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+ tlv_array) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+static const char *wm8990_digital_sidetone[] =
+ {"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8990_left_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACL_SHIFT,
+ WM8990_ADC_TO_DACL_MASK,
+ wm8990_digital_sidetone);
+
+static const struct soc_enum wm8990_right_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADC_TO_DACR_SHIFT,
+ WM8990_ADC_TO_DACR_MASK,
+ wm8990_digital_sidetone);
+
+static const char *wm8990_adcmode[] =
+ {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8990_right_adcmode_enum =
+SOC_ENUM_SINGLE(WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_CUT_SHIFT,
+ WM8990_ADC_HPF_CUT_MASK,
+ wm8990_adcmode);
+
+static const struct snd_kcontrol_new wm8990_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+ WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+ WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+ WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+ WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv),
+
+/* LOUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME,
+ WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0),
+
+/* ROUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME,
+ WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0),
+
+/* LOPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME,
+ WM8990_LOPGAZC_BIT, 1, 0),
+
+/* ROPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME,
+ WM8990_ROPGAZC_BIT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LONMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOPMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_LOATTN_BIT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_RONMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROPMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+ WM8990_ROATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT3ATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+ WM8990_OUT4ATTN_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1,
+ WM8990_CDMODE_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME,
+ WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3,
+ WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3,
+ WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8990_LEFT_DAC_DIGITAL_VOLUME,
+ WM8990_DACL_VOL_SHIFT,
+ WM8990_DACL_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8990_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8990_DACR_VOL_SHIFT,
+ WM8990_DACR_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+ WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL,
+ WM8990_ADC_HPF_ENA_BIT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8990_LEFT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCL_VOL_SHIFT,
+ WM8990_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8990_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8990_ADCR_VOL_SHIFT,
+ WM8990_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LIN12VOL_SHIFT,
+ WM8990_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8990_LI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LIN34VOL_SHIFT,
+ WM8990_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8990_LI34MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RIN12VOL_SHIFT,
+ WM8990_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8990_RI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RIN34VOL_SHIFT,
+ WM8990_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8990_RI34MUTE_BIT, 1, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8990_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8990_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u16 reg, fakepower;
+
+ reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
+ fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+
+ if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
+ (1 << WM8990_AINLMUX_PWR_BIT))) {
+ reg |= WM8990_AINL_ENA;
+ } else {
+ reg &= ~WM8990_AINL_ENA;
+ }
+
+ if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) |
+ (1 << WM8990_AINRMUX_PWR_BIT))) {
+ reg |= WM8990_AINR_ENA;
+ } else {
+ reg &= ~WM8990_AINL_ENA;
+ }
+ wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+ return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u32 reg_shift = kcontrol->private_value & 0xfff;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
+ reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+ if (reg & WM8990_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+ if (reg & WM8990_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
+ reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ if (reg & WM8990_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3,
+ WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4,
+ WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+ 1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8990_ainlmux[] =
+ {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8990_ainlmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+ ARRAY_SIZE(wm8990_ainlmux), wm8990_ainlmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8990_ainrmux[] =
+ {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8990_ainrmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+ ARRAY_SIZE(wm8990_ainrmux), wm8990_ainrmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8990_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8990_INPUT_MIXER5, WM8990_LR4BVOL_SHIFT,
+ WM8990_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8990_INPUT_MIXER6, WM8990_RL4BVOL_SHIFT,
+ WM8990_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LRI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LLI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LR12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LL12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1,
+ WM8990_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RLI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RRI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RL12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RR12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2,
+ WM8990_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LROPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1,
+ WM8990_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LR12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1,
+ WM8990_LL12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+ WM8990_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RLOPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2,
+ WM8990_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2,
+ WM8990_RR12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+ WM8990_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LI4O3_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RPGAO4_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+ WM8990_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LI2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LB2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LOPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_LDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+ WM8990_ROPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+ WM8990_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCL_ENA_BIT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2,
+ WM8990_ADCR_ENA_BIT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT,
+ 0, &wm8990_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT,
+ 0, &wm8990_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT,
+ 0, &wm8990_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
+ 0, &wm8990_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0,
+ &wm8990_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixl_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX_E("AILNMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0,
+ &wm8990_dapm_ainlmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0,
+ &wm8990_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8990_dapm_inmixr_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX_E("AIRNMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0,
+ &wm8990_dapm_ainrmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACL_ENA_BIT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3,
+ WM8990_DACR_ENA_BIT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT,
+ 0, &wm8990_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0,
+ &wm8990_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0,
+ &wm8990_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0,
+ &wm8990_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0,
+ &wm8990_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0,
+ &wm8990_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0,
+ &wm8990_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0,
+ &wm8990_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT,
+ 0, &wm8990_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8990_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0,
+ NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0,
+ NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8990_POWER_MANAGEMENT_1,
+ WM8990_MICBIAS_ENA_BIT, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AILNMUX */
+ {"AILNMUX", "INMIXL Mix", "INMIXL"},
+ {"AILNMUX", "DIFFINL Mix", "LIN12PGA"},
+ {"AILNMUX", "DIFFINL Mix", "LIN34PGA"},
+ {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AILNMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AIRNMUX */
+ {"AIRNMUX", "INMIXR Mix", "INMIXR"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN12PGA"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN34PGA"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXN"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AIRNMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXP"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+static int wm8990_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8990_dapm_widgets,
+ ARRAY_SIZE(wm8990_dapm_widgets));
+
+ /* set up the WM8990 audio map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2;
+ u32 n;
+ u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ u16 reg;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct _pll_div pll_div;
+
+ if (freq_in && freq_out) {
+ pll_factors(&pll_div, freq_out * 4, freq_in);
+
+ /* Turn on PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg |= WM8990_PLL_ENA;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+ /* sysclk comes from PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+
+ /* set up N , fractional mode and pre-divisor if neccessary */
+ wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+ (pll_div.div2?WM8990_PRESCALE:0));
+ wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+ wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+ } else {
+ /* Turn on PLL */
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg &= ~WM8990_PLL_ENA;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+ }
+ return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8990_priv *wm8990 = codec->private_data;
+
+ wm8990->sysclk = freq;
+ return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+ audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8990_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8990_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8990_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8990_AIF_TMF_I2S;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8990_AIF_TMF_RIGHTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8990_AIF_TMF_LEFTJ;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8990_AIF_TMF_DSP;
+ audio1 &= ~WM8990_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8990_MCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_MCLK_DIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_DACCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_DAC_CLKDIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_ADCCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ ~WM8990_ADC_CLKDIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ break;
+ case WM8990_BCLK_DIV:
+ reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+ ~WM8990_BCLK_DIV_MASK;
+ wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8990_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8990_AIF_WL_MASK;
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ audio1 |= WM8990_AIF_WL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ audio1 |= WM8990_AIF_WL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio1 |= WM8990_AIF_WL_32BITS;
+ break;
+ }
+
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val;
+
+ val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+
+ if (mute)
+ wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ else
+ wm8990_write(codec, WM8990_DAC_CTRL, val);
+
+ return 0;
+}
+
+static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 val;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_VMIDTOG);
+
+ /* Delay to allow output caps to discharge */
+ msleep(msecs_to_jiffies(300));
+
+ /* Disable VMIDTOG */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL);
+
+ /* disable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, 0);
+
+ /* Enable outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+
+ msleep(msecs_to_jiffies(50));
+
+ /* Enable VMID at 2x50k */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+
+ msleep(msecs_to_jiffies(100));
+
+ /* Enable VREF */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ msleep(msecs_to_jiffies(600));
+
+ /* Enable BUFIOEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* Disable outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+ } else {
+ /* ON -> standby */
+
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_POBCTRL | WM8990_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ WM8990_BUFDCOPEN | WM8990_POBCTRL |
+ WM8990_BUFIOEN);
+
+ /* mute DAC */
+ val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
+ wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+ /* Disable VMID */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+
+ msleep(msecs_to_jiffies(300));
+
+ /* Enable all output discharge bits */
+ wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+ WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+ WM8990_DIS_ROUT);
+
+ /* Disable VREF */
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8990_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * The WM8990 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+struct snd_soc_dai wm8990_dai = {
+/* ADC/DAC on primary */
+ .name = "WM8990 ADC/DAC Primary",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8990_RATES,
+ .formats = WM8990_FORMATS,},
+ .ops = {
+ .hw_params = wm8990_hw_params,},
+ .dai_ops = {
+ .digital_mute = wm8990_mute,
+ .set_fmt = wm8990_set_dai_fmt,
+ .set_clkdiv = wm8990_set_dai_clkdiv,
+ .set_pll = wm8990_set_dai_pll,
+ .set_sysclk = wm8990_set_dai_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8990_dai);
+
+static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* we only need to suspend if we are a valid card */
+ if (!codec->card)
+ return 0;
+
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8990_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* we only need to resume if we are a valid card */
+ if (!codec->card)
+ return 0;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
+ if (i + 1 == WM8990_RESET)
+ continue;
+ data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+/*
+ * initialise the WM8990 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8990_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 reg;
+ int ret = 0;
+
+ codec->name = "WM8990";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8990_read_reg_cache;
+ codec->write = wm8990_write;
+ codec->set_bias_level = wm8990_set_bias_level;
+ codec->dai = &wm8990_dai;
+ codec->num_dai = 2;
+ codec->reg_cache_size = ARRAY_SIZE(wm8990_reg);
+ codec->reg_cache = kmemdup(wm8990_reg, sizeof(wm8990_reg), GFP_KERNEL);
+
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ wm8990_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8990: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* charge output caps */
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
+ wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+ ~WM8990_GPIO1_SEL_MASK;
+ wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+
+ reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+
+ wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ wm8990_add_controls(codec);
+ wm8990_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8990: failed to register card\n");
+ goto card_err;
+ }
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+ around */
+static struct snd_soc_device *wm8990_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM891 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x34
+ * high = 0x36
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8990_i2c_driver;
+static struct i2c_client client_template;
+
+static int wm8990_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct snd_soc_device *socdev = wm8990_socdev;
+ struct wm8990_setup_data *setup = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *i2c;
+ int ret;
+
+ if (addr != setup->i2c_address)
+ return -ENODEV;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+ if (i2c == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = i2c_attach_client(i2c);
+ if (ret < 0) {
+ pr_err("failed to attach codec at addr %x\n", addr);
+ goto err;
+ }
+
+ ret = wm8990_init(socdev);
+ if (ret < 0) {
+ pr_err("failed to initialise WM8990\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+
+static int wm8990_i2c_detach(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(codec->reg_cache);
+ kfree(client);
+ return 0;
+}
+
+static int wm8990_i2c_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, wm8990_codec_probe);
+}
+
+static struct i2c_driver wm8990_i2c_driver = {
+ .driver = {
+ .name = "WM8990 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = wm8990_i2c_attach,
+ .detach_client = wm8990_i2c_detach,
+ .command = NULL,
+};
+
+static struct i2c_client client_template = {
+ .name = "WM8990",
+ .driver = &wm8990_i2c_driver,
+};
+#endif
+
+static int wm8990_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8990_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct wm8990_priv *wm8990;
+ int ret = 0;
+
+ pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ wm8990 = kzalloc(sizeof(struct wm8990_priv), GFP_KERNEL);
+ if (wm8990 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = wm8990;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ wm8990_socdev = socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ normal_i2c[0] = setup->i2c_address;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = i2c_add_driver(&wm8990_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+ }
+#else
+ /* Add other interfaces here */
+#endif
+ return ret;
+}
+
+/* power down chip */
+static int wm8990_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8990_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8990 = {
+ .probe = wm8990_probe,
+ .remove = wm8990_remove,
+ .suspend = wm8990_suspend,
+ .resume = wm8990_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
+
+MODULE_DESCRIPTION("ASoC WM8990 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
new file mode 100644
index 00000000000..6bea5748528
--- /dev/null
+++ b/sound/soc/codecs/wm8990.h
@@ -0,0 +1,832 @@
+/*
+ * wm8990.h -- audio driver for WM8990
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __WM8990REGISTERDEFS_H__
+#define __WM8990REGISTERDEFS_H__
+
+/*
+ * Register values.
+ */
+#define WM8990_RESET 0x00
+#define WM8990_POWER_MANAGEMENT_1 0x01
+#define WM8990_POWER_MANAGEMENT_2 0x02
+#define WM8990_POWER_MANAGEMENT_3 0x03
+#define WM8990_AUDIO_INTERFACE_1 0x04
+#define WM8990_AUDIO_INTERFACE_2 0x05
+#define WM8990_CLOCKING_1 0x06
+#define WM8990_CLOCKING_2 0x07
+#define WM8990_AUDIO_INTERFACE_3 0x08
+#define WM8990_AUDIO_INTERFACE_4 0x09
+#define WM8990_DAC_CTRL 0x0A
+#define WM8990_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8990_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8990_DIGITAL_SIDE_TONE 0x0D
+#define WM8990_ADC_CTRL 0x0E
+#define WM8990_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8990_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8990_GPIO_CTRL_1 0x12
+#define WM8990_GPIO1_GPIO2 0x13
+#define WM8990_GPIO3_GPIO4 0x14
+#define WM8990_GPIO5_GPIO6 0x15
+#define WM8990_GPIOCTRL_2 0x16
+#define WM8990_GPIO_POL 0x17
+#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8990_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8990_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8990_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8990_OUT3_4_VOLUME 0x1F
+#define WM8990_LEFT_OPGA_VOLUME 0x20
+#define WM8990_RIGHT_OPGA_VOLUME 0x21
+#define WM8990_SPEAKER_VOLUME 0x22
+#define WM8990_CLASSD1 0x23
+#define WM8990_CLASSD3 0x25
+#define WM8990_INPUT_MIXER1 0x27
+#define WM8990_INPUT_MIXER2 0x28
+#define WM8990_INPUT_MIXER3 0x29
+#define WM8990_INPUT_MIXER4 0x2A
+#define WM8990_INPUT_MIXER5 0x2B
+#define WM8990_INPUT_MIXER6 0x2C
+#define WM8990_OUTPUT_MIXER1 0x2D
+#define WM8990_OUTPUT_MIXER2 0x2E
+#define WM8990_OUTPUT_MIXER3 0x2F
+#define WM8990_OUTPUT_MIXER4 0x30
+#define WM8990_OUTPUT_MIXER5 0x31
+#define WM8990_OUTPUT_MIXER6 0x32
+#define WM8990_OUT3_4_MIXER 0x33
+#define WM8990_LINE_MIXER1 0x34
+#define WM8990_LINE_MIXER2 0x35
+#define WM8990_SPEAKER_MIXER 0x36
+#define WM8990_ADDITIONAL_CONTROL 0x37
+#define WM8990_ANTIPOP1 0x38
+#define WM8990_ANTIPOP2 0x39
+#define WM8990_MICBIAS 0x3A
+#define WM8990_PLL1 0x3C
+#define WM8990_PLL2 0x3D
+#define WM8990_PLL3 0x3E
+#define WM8990_INTDRIVBITS 0x3F
+
+#define WM8990_REGISTER_COUNT 60
+#define WM8990_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8990_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8990_SPK_ENA 0x1000 /* SPK_ENA */
+#define WM8990_SPK_ENA_BIT 12
+#define WM8990_OUT3_ENA 0x0800 /* OUT3_ENA */
+#define WM8990_OUT3_ENA_BIT 11
+#define WM8990_OUT4_ENA 0x0400 /* OUT4_ENA */
+#define WM8990_OUT4_ENA_BIT 10
+#define WM8990_LOUT_ENA 0x0200 /* LOUT_ENA */
+#define WM8990_LOUT_ENA_BIT 9
+#define WM8990_ROUT_ENA 0x0100 /* ROUT_ENA */
+#define WM8990_ROUT_ENA_BIT 8
+#define WM8990_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
+#define WM8990_MICBIAS_ENA_BIT 4
+#define WM8990_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
+#define WM8990_VREF_ENA 0x0001 /* VREF_ENA */
+#define WM8990_VREF_ENA_BIT 0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8990_PLL_ENA 0x8000 /* PLL_ENA */
+#define WM8990_PLL_ENA_BIT 15
+#define WM8990_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8990_TSHUT_ENA_BIT 14
+#define WM8990_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8990_TSHUT_OPDIS_BIT 13
+#define WM8990_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8990_OPCLK_ENA_BIT 11
+#define WM8990_AINL_ENA 0x0200 /* AINL_ENA */
+#define WM8990_AINL_ENA_BIT 9
+#define WM8990_AINR_ENA 0x0100 /* AINR_ENA */
+#define WM8990_AINR_ENA_BIT 8
+#define WM8990_LIN34_ENA 0x0080 /* LIN34_ENA */
+#define WM8990_LIN34_ENA_BIT 7
+#define WM8990_LIN12_ENA 0x0040 /* LIN12_ENA */
+#define WM8990_LIN12_ENA_BIT 6
+#define WM8990_RIN34_ENA 0x0020 /* RIN34_ENA */
+#define WM8990_RIN34_ENA_BIT 5
+#define WM8990_RIN12_ENA 0x0010 /* RIN12_ENA */
+#define WM8990_RIN12_ENA_BIT 4
+#define WM8990_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8990_ADCL_ENA_BIT 1
+#define WM8990_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8990_ADCR_ENA_BIT 0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8990_LON_ENA 0x2000 /* LON_ENA */
+#define WM8990_LON_ENA_BIT 13
+#define WM8990_LOP_ENA 0x1000 /* LOP_ENA */
+#define WM8990_LOP_ENA_BIT 12
+#define WM8990_RON_ENA 0x0800 /* RON_ENA */
+#define WM8990_RON_ENA_BIT 11
+#define WM8990_ROP_ENA 0x0400 /* ROP_ENA */
+#define WM8990_ROP_ENA_BIT 10
+#define WM8990_LOPGA_ENA 0x0080 /* LOPGA_ENA */
+#define WM8990_LOPGA_ENA_BIT 7
+#define WM8990_ROPGA_ENA 0x0040 /* ROPGA_ENA */
+#define WM8990_ROPGA_ENA_BIT 6
+#define WM8990_LOMIX_ENA 0x0020 /* LOMIX_ENA */
+#define WM8990_LOMIX_ENA_BIT 5
+#define WM8990_ROMIX_ENA 0x0010 /* ROMIX_ENA */
+#define WM8990_ROMIX_ENA_BIT 4
+#define WM8990_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8990_DACL_ENA_BIT 1
+#define WM8990_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8990_DACR_ENA_BIT 0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8990_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8990_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8990_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8990_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8990_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8990_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8990_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8990_AIF_WL_16BITS (0 << 5)
+#define WM8990_AIF_WL_20BITS (1 << 5)
+#define WM8990_AIF_WL_24BITS (2 << 5)
+#define WM8990_AIF_WL_32BITS (3 << 5)
+#define WM8990_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8990_AIF_TMF_RIGHTJ (0 << 3)
+#define WM8990_AIF_TMF_LEFTJ (1 << 3)
+#define WM8990_AIF_TMF_I2S (2 << 3)
+#define WM8990_AIF_TMF_DSP (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8990_DACL_SRC 0x8000 /* DACL_SRC */
+#define WM8990_DACR_SRC 0x4000 /* DACR_SRC */
+#define WM8990_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8990_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8990_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST */
+#define WM8990_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8990_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8990_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8990_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8990_LOOPBACK 0x0001 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8990_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8990_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8990_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
+#define WM8990_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8990_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8990_BCLK_DIV_1 (0x0 << 1)
+#define WM8990_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8990_BCLK_DIV_2 (0x2 << 1)
+#define WM8990_BCLK_DIV_3 (0x3 << 1)
+#define WM8990_BCLK_DIV_4 (0x4 << 1)
+#define WM8990_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8990_BCLK_DIV_6 (0x6 << 1)
+#define WM8990_BCLK_DIV_8 (0x7 << 1)
+#define WM8990_BCLK_DIV_11 (0x8 << 1)
+#define WM8990_BCLK_DIV_12 (0x9 << 1)
+#define WM8990_BCLK_DIV_16 (0xA << 1)
+#define WM8990_BCLK_DIV_22 (0xB << 1)
+#define WM8990_BCLK_DIV_24 (0xC << 1)
+#define WM8990_BCLK_DIV_32 (0xD << 1)
+#define WM8990_BCLK_DIV_44 (0xE << 1)
+#define WM8990_BCLK_DIV_48 (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8990_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8990_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8990_CLK_FORCE 0x2000 /* CLK_FORCE */
+#define WM8990_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
+#define WM8990_MCLK_DIV_1 (0 << 11)
+#define WM8990_MCLK_DIV_2 (2 << 11)
+#define WM8990_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8990_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV */
+#define WM8990_ADC_CLKDIV_1 (0 << 5)
+#define WM8990_ADC_CLKDIV_1_5 (1 << 5)
+#define WM8990_ADC_CLKDIV_2 (2 << 5)
+#define WM8990_ADC_CLKDIV_3 (3 << 5)
+#define WM8990_ADC_CLKDIV_4 (4 << 5)
+#define WM8990_ADC_CLKDIV_5_5 (5 << 5)
+#define WM8990_ADC_CLKDIV_6 (6 << 5)
+#define WM8990_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
+#define WM8990_DAC_CLKDIV_1 (0 << 2)
+#define WM8990_DAC_CLKDIV_1_5 (1 << 2)
+#define WM8990_DAC_CLKDIV_2 (2 << 2)
+#define WM8990_DAC_CLKDIV_3 (3 << 2)
+#define WM8990_DAC_CLKDIV_4 (4 << 2)
+#define WM8990_DAC_CLKDIV_5_5 (5 << 2)
+#define WM8990_DAC_CLKDIV_6 (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8990_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8990_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
+#define WM8990_AIF_SEL 0x2000 /* AIF_SEL */
+#define WM8990_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
+#define WM8990_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8990_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
+#define WM8990_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
+#define WM8990_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8990_DACLRC_DIR 0x0800 /* DACLRC_DIR */
+#define WM8990_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8990_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
+#define WM8990_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8990_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8990_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8990_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
+#define WM8990_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
+#define WM8990_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8990_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8990_DACR_DATINV 0x0001 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8990_DACL_VOL_SHIFT 0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8990_DAC_VU 0x0100 /* DAC_VU */
+#define WM8990_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8990_DACR_VOL_SHIFT 0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8990_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL */
+#define WM8990_ADCL_DAC_SVOL_SHIFT 9
+#define WM8990_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL */
+#define WM8990_ADCR_DAC_SVOL_SHIFT 5
+#define WM8990_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
+#define WM8990_ADC_TO_DACL_SHIFT 2
+#define WM8990_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
+#define WM8990_ADC_TO_DACR_SHIFT 0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8990_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
+#define WM8990_ADC_HPF_ENA_BIT 8
+#define WM8990_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
+#define WM8990_ADC_HPF_CUT_SHIFT 5
+#define WM8990_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8990_ADCL_DATINV_BIT 1
+#define WM8990_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8990_ADCR_DATINV_BIT 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8990_ADCL_VOL_SHIFT 0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8990_ADC_VU 0x0100 /* ADC_VU */
+#define WM8990_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8990_ADCR_VOL_SHIFT 0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8990_IRQ 0x1000 /* IRQ */
+#define WM8990_TEMPOK 0x0800 /* TEMPOK */
+#define WM8990_MICSHRT 0x0400 /* MICSHRT */
+#define WM8990_MICDET 0x0200 /* MICDET */
+#define WM8990_PLL_LCK 0x0100 /* PLL_LCK */
+#define WM8990_GPI8_STATUS 0x0080 /* GPI8_STATUS */
+#define WM8990_GPI7_STATUS 0x0040 /* GPI7_STATUS */
+#define WM8990_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
+#define WM8990_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
+#define WM8990_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
+#define WM8990_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
+#define WM8990_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
+#define WM8990_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8990_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
+#define WM8990_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
+#define WM8990_GPIO2_PU 0x2000 /* GPIO2_PU */
+#define WM8990_GPIO2_PD 0x1000 /* GPIO2_PD */
+#define WM8990_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
+#define WM8990_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
+#define WM8990_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
+#define WM8990_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8990_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8990_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8990_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
+#define WM8990_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
+#define WM8990_GPIO4_PU 0x2000 /* GPIO4_PU */
+#define WM8990_GPIO4_PD 0x1000 /* GPIO4_PD */
+#define WM8990_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
+#define WM8990_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
+#define WM8990_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
+#define WM8990_GPIO3_PU 0x0020 /* GPIO3_PU */
+#define WM8990_GPIO3_PD 0x0010 /* GPIO3_PD */
+#define WM8990_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8990_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
+#define WM8990_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
+#define WM8990_GPIO6_PU 0x2000 /* GPIO6_PU */
+#define WM8990_GPIO6_PD 0x1000 /* GPIO6_PD */
+#define WM8990_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
+#define WM8990_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
+#define WM8990_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
+#define WM8990_GPIO5_PU 0x0020 /* GPIO5_PU */
+#define WM8990_GPIO5_PD 0x0010 /* GPIO5_PD */
+#define WM8990_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8990_RD_3W_ENA 0x8000 /* RD_3W_ENA */
+#define WM8990_MODE_3W4W 0x4000 /* MODE_3W4W */
+#define WM8990_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
+#define WM8990_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
+#define WM8990_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
+#define WM8990_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
+#define WM8990_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
+#define WM8990_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
+#define WM8990_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8990_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
+#define WM8990_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
+#define WM8990_GPI7_ENA 0x0001 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8990_IRQ_INV 0x1000 /* IRQ_INV */
+#define WM8990_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8990_MICSHRT_POL 0x0400 /* MICSHRT_POL */
+#define WM8990_MICDET_POL 0x0200 /* MICDET_POL */
+#define WM8990_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
+#define WM8990_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8990_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8990_GPIO6_POL 0x0020 /* GPIO6_POL */
+#define WM8990_GPIO5_POL 0x0010 /* GPIO5_POL */
+#define WM8990_GPIO4_POL 0x0008 /* GPIO4_POL */
+#define WM8990_GPIO3_POL 0x0004 /* GPIO3_POL */
+#define WM8990_GPIO2_POL 0x0002 /* GPIO2_POL */
+#define WM8990_GPIO1_POL 0x0001 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI12MUTE 0x0080 /* LI12MUTE */
+#define WM8990_LI12MUTE_BIT 7
+#define WM8990_LI12ZC 0x0040 /* LI12ZC */
+#define WM8990_LI12ZC_BIT 6
+#define WM8990_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
+#define WM8990_LIN12VOL_SHIFT 0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_LI34MUTE 0x0080 /* LI34MUTE */
+#define WM8990_LI34MUTE_BIT 7
+#define WM8990_LI34ZC 0x0040 /* LI34ZC */
+#define WM8990_LI34ZC_BIT 6
+#define WM8990_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
+#define WM8990_LIN34VOL_SHIFT 0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI12MUTE 0x0080 /* RI12MUTE */
+#define WM8990_RI12MUTE_BIT 7
+#define WM8990_RI12ZC 0x0040 /* RI12ZC */
+#define WM8990_RI12ZC_BIT 6
+#define WM8990_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
+#define WM8990_RIN12VOL_SHIFT 0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8990_IPVU 0x0100 /* IPVU */
+#define WM8990_RI34MUTE 0x0080 /* RI34MUTE */
+#define WM8990_RI34MUTE_BIT 7
+#define WM8990_RI34ZC 0x0040 /* RI34ZC */
+#define WM8990_RI34ZC_BIT 6
+#define WM8990_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
+#define WM8990_RIN34VOL_SHIFT 0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOZC 0x0080 /* LOZC */
+#define WM8990_LOZC_BIT 7
+#define WM8990_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8990_LOUTVOL_SHIFT 0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROZC 0x0080 /* ROZC */
+#define WM8990_ROZC_BIT 7
+#define WM8990_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8990_ROUTVOL_SHIFT 0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8990_LONMUTE 0x0040 /* LONMUTE */
+#define WM8990_LONMUTE_BIT 6
+#define WM8990_LOPMUTE 0x0020 /* LOPMUTE */
+#define WM8990_LOPMUTE_BIT 5
+#define WM8990_LOATTN 0x0010 /* LOATTN */
+#define WM8990_LOATTN_BIT 4
+#define WM8990_RONMUTE 0x0004 /* RONMUTE */
+#define WM8990_RONMUTE_BIT 2
+#define WM8990_ROPMUTE 0x0002 /* ROPMUTE */
+#define WM8990_ROPMUTE_BIT 1
+#define WM8990_ROATTN 0x0001 /* ROATTN */
+#define WM8990_ROATTN_BIT 0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8990_OUT3MUTE 0x0020 /* OUT3MUTE */
+#define WM8990_OUT3MUTE_BIT 5
+#define WM8990_OUT3ATTN 0x0010 /* OUT3ATTN */
+#define WM8990_OUT3ATTN_BIT 4
+#define WM8990_OUT4MUTE 0x0002 /* OUT4MUTE */
+#define WM8990_OUT4MUTE_BIT 1
+#define WM8990_OUT4ATTN 0x0001 /* OUT4ATTN */
+#define WM8990_OUT4ATTN_BIT 0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_LOPGAZC 0x0080 /* LOPGAZC */
+#define WM8990_LOPGAZC_BIT 7
+#define WM8990_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
+#define WM8990_LOPGAVOL_SHIFT 0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8990_OPVU 0x0100 /* OPVU */
+#define WM8990_ROPGAZC 0x0080 /* ROPGAZC */
+#define WM8990_ROPGAZC_BIT 7
+#define WM8990_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
+#define WM8990_ROPGAVOL_SHIFT 0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8990_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */
+#define WM8990_SPKVOL_SHIFT 0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8990_CDMODE 0x0100 /* CDMODE */
+#define WM8990_CDMODE_BIT 8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8990_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
+#define WM8990_DCGAIN_SHIFT 3
+#define WM8990_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
+#define WM8990_ACGAIN_SHIFT 0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8990_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
+#define WM8990_AINLMODE_SHIFT 2
+#define WM8990_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
+#define WM8990_AINRMODE_SHIFT 0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8990_LMP4 0x0080 /* LMP4 */
+#define WM8990_LMP4_BIT 7 /* LMP4 */
+#define WM8990_LMN3 0x0040 /* LMN3 */
+#define WM8990_LMN3_BIT 6 /* LMN3 */
+#define WM8990_LMP2 0x0020 /* LMP2 */
+#define WM8990_LMP2_BIT 5 /* LMP2 */
+#define WM8990_LMN1 0x0010 /* LMN1 */
+#define WM8990_LMN1_BIT 4 /* LMN1 */
+#define WM8990_RMP4 0x0008 /* RMP4 */
+#define WM8990_RMP4_BIT 3 /* RMP4 */
+#define WM8990_RMN3 0x0004 /* RMN3 */
+#define WM8990_RMN3_BIT 2 /* RMN3 */
+#define WM8990_RMP2 0x0002 /* RMP2 */
+#define WM8990_RMP2_BIT 1 /* RMP2 */
+#define WM8990_RMN1 0x0001 /* RMN1 */
+#define WM8990_RMN1_BIT 0 /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8990_L34MNB 0x0100 /* L34MNB */
+#define WM8990_L34MNB_BIT 8
+#define WM8990_L34MNBST 0x0080 /* L34MNBST */
+#define WM8990_L34MNBST_BIT 7
+#define WM8990_L12MNB 0x0020 /* L12MNB */
+#define WM8990_L12MNB_BIT 5
+#define WM8990_L12MNBST 0x0010 /* L12MNBST */
+#define WM8990_L12MNBST_BIT 4
+#define WM8990_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
+#define WM8990_LDBVOL_SHIFT 0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8990_R34MNB 0x0100 /* R34MNB */
+#define WM8990_R34MNB_BIT 8
+#define WM8990_R34MNBST 0x0080 /* R34MNBST */
+#define WM8990_R34MNBST_BIT 7
+#define WM8990_R12MNB 0x0020 /* R12MNB */
+#define WM8990_R12MNB_BIT 5
+#define WM8990_R12MNBST 0x0010 /* R12MNBST */
+#define WM8990_R12MNBST_BIT 4
+#define WM8990_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
+#define WM8990_RDBVOL_SHIFT 0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8990_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
+#define WM8990_LI2BVOL_SHIFT 6
+#define WM8990_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
+#define WM8990_LR4BVOL_SHIFT 3
+#define WM8990_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
+#define WM8990_LL4BVOL_SHIFT 0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8990_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
+#define WM8990_RI2BVOL_SHIFT 6
+#define WM8990_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
+#define WM8990_RL4BVOL_SHIFT 3
+#define WM8990_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
+#define WM8990_RR4BVOL_SHIFT 0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8990_LRBLO 0x0080 /* LRBLO */
+#define WM8990_LRBLO_BIT 7
+#define WM8990_LLBLO 0x0040 /* LLBLO */
+#define WM8990_LLBLO_BIT 6
+#define WM8990_LRI3LO 0x0020 /* LRI3LO */
+#define WM8990_LRI3LO_BIT 5
+#define WM8990_LLI3LO 0x0010 /* LLI3LO */
+#define WM8990_LLI3LO_BIT 4
+#define WM8990_LR12LO 0x0008 /* LR12LO */
+#define WM8990_LR12LO_BIT 3
+#define WM8990_LL12LO 0x0004 /* LL12LO */
+#define WM8990_LL12LO_BIT 2
+#define WM8990_LDLO 0x0001 /* LDLO */
+#define WM8990_LDLO_BIT 0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8990_RLBRO 0x0080 /* RLBRO */
+#define WM8990_RLBRO_BIT 7
+#define WM8990_RRBRO 0x0040 /* RRBRO */
+#define WM8990_RRBRO_BIT 6
+#define WM8990_RLI3RO 0x0020 /* RLI3RO */
+#define WM8990_RLI3RO_BIT 5
+#define WM8990_RRI3RO 0x0010 /* RRI3RO */
+#define WM8990_RRI3RO_BIT 4
+#define WM8990_RL12RO 0x0008 /* RL12RO */
+#define WM8990_RL12RO_BIT 3
+#define WM8990_RR12RO 0x0004 /* RR12RO */
+#define WM8990_RR12RO_BIT 2
+#define WM8990_RDRO 0x0001 /* RDRO */
+#define WM8990_RDRO_BIT 0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8990_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
+#define WM8990_LLI3LOVOL_SHIFT 6
+#define WM8990_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
+#define WM8990_LR12LOVOL_SHIFT 3
+#define WM8990_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
+#define WM8990_LL12LOVOL_SHIFT 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8990_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
+#define WM8990_RRI3ROVOL_SHIFT 6
+#define WM8990_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
+#define WM8990_RL12ROVOL_SHIFT 3
+#define WM8990_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
+#define WM8990_RR12ROVOL_SHIFT 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8990_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
+#define WM8990_LRI3LOVOL_SHIFT 6
+#define WM8990_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
+#define WM8990_LRBLOVOL_SHIFT 3
+#define WM8990_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
+#define WM8990_LLBLOVOL_SHIFT 0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8990_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
+#define WM8990_RLI3ROVOL_SHIFT 6
+#define WM8990_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
+#define WM8990_RLBROVOL_SHIFT 3
+#define WM8990_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
+#define WM8990_RRBROVOL_SHIFT 0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8990_VSEL_MASK 0x0180 /* VSEL - [8:7] */
+#define WM8990_LI4O3 0x0020 /* LI4O3 */
+#define WM8990_LI4O3_BIT 5
+#define WM8990_LPGAO3 0x0010 /* LPGAO3 */
+#define WM8990_LPGAO3_BIT 4
+#define WM8990_RI4O4 0x0002 /* RI4O4 */
+#define WM8990_RI4O4_BIT 1
+#define WM8990_RPGAO4 0x0001 /* RPGAO4 */
+#define WM8990_RPGAO4_BIT 0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8990_LLOPGALON 0x0040 /* LLOPGALON */
+#define WM8990_LLOPGALON_BIT 6
+#define WM8990_LROPGALON 0x0020 /* LROPGALON */
+#define WM8990_LROPGALON_BIT 5
+#define WM8990_LOPLON 0x0010 /* LOPLON */
+#define WM8990_LOPLON_BIT 4
+#define WM8990_LR12LOP 0x0004 /* LR12LOP */
+#define WM8990_LR12LOP_BIT 2
+#define WM8990_LL12LOP 0x0002 /* LL12LOP */
+#define WM8990_LL12LOP_BIT 1
+#define WM8990_LLOPGALOP 0x0001 /* LLOPGALOP */
+#define WM8990_LLOPGALOP_BIT 0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8990_RROPGARON 0x0040 /* RROPGARON */
+#define WM8990_RROPGARON_BIT 6
+#define WM8990_RLOPGARON 0x0020 /* RLOPGARON */
+#define WM8990_RLOPGARON_BIT 5
+#define WM8990_ROPRON 0x0010 /* ROPRON */
+#define WM8990_ROPRON_BIT 4
+#define WM8990_RL12ROP 0x0004 /* RL12ROP */
+#define WM8990_RL12ROP_BIT 2
+#define WM8990_RR12ROP 0x0002 /* RR12ROP */
+#define WM8990_RR12ROP_BIT 1
+#define WM8990_RROPGAROP 0x0001 /* RROPGAROP */
+#define WM8990_RROPGAROP_BIT 0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8990_LB2SPK 0x0080 /* LB2SPK */
+#define WM8990_LB2SPK_BIT 7
+#define WM8990_RB2SPK 0x0040 /* RB2SPK */
+#define WM8990_RB2SPK_BIT 6
+#define WM8990_LI2SPK 0x0020 /* LI2SPK */
+#define WM8990_LI2SPK_BIT 5
+#define WM8990_RI2SPK 0x0010 /* RI2SPK */
+#define WM8990_RI2SPK_BIT 4
+#define WM8990_LOPGASPK 0x0008 /* LOPGASPK */
+#define WM8990_LOPGASPK_BIT 3
+#define WM8990_ROPGASPK 0x0004 /* ROPGASPK */
+#define WM8990_ROPGASPK_BIT 2
+#define WM8990_LDSPK 0x0002 /* LDSPK */
+#define WM8990_LDSPK_BIT 1
+#define WM8990_RDSPK 0x0001 /* RDSPK */
+#define WM8990_RDSPK_BIT 0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8990_VROI 0x0001 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8990_DIS_LLINE 0x0020 /* DIS_LLINE */
+#define WM8990_DIS_RLINE 0x0010 /* DIS_RLINE */
+#define WM8990_DIS_OUT3 0x0008 /* DIS_OUT3 */
+#define WM8990_DIS_OUT4 0x0004 /* DIS_OUT4 */
+#define WM8990_DIS_LOUT 0x0002 /* DIS_LOUT */
+#define WM8990_DIS_ROUT 0x0001 /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8990_SOFTST 0x0040 /* SOFTST */
+#define WM8990_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8990_BUFDCOPEN 0x0004 /* BUFDCOPEN */
+#define WM8990_POBCTRL 0x0002 /* POBCTRL */
+#define WM8990_VMIDTOG 0x0001 /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8990_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
+#define WM8990_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
+#define WM8990_MCD 0x0004 /* MCD */
+#define WM8990_MBSEL 0x0001 /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8990_SDM 0x0080 /* SDM */
+#define WM8990_PRESCALE 0x0040 /* PRESCALE */
+#define WM8990_PLLN_MASK 0x000F /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8990_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8990_INMIXL_PWR_BIT 0
+#define WM8990_AINLMUX_PWR_BIT 1
+#define WM8990_INMIXR_PWR_BIT 2
+#define WM8990_AINRMUX_PWR_BIT 3
+
+struct wm8990_setup_data {
+ unsigned short i2c_address;
+};
+
+#define WM8990_MCLK_DIV 0
+#define WM8990_DACCLK_DIV 1
+#define WM8990_ADCCLK_DIV 2
+#define WM8990_BCLK_DIV 3
+
+extern struct snd_soc_dai wm8990_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8990;
+
+#endif /* __WM8990REGISTERDEFS_H__ */
+/*------------------------------ END OF FILE ---------------------------------*/
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 76c1e2d33e7..9fc8edd8222 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -9,9 +9,6 @@
* 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.
- *
- * Revision history
- * 4th Feb 2006 Initial version.
*/
#include <linux/init.h>
@@ -25,6 +22,7 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include "wm9712.h"
#define WM9712_VERSION "0.4"
@@ -351,7 +349,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
@@ -446,21 +444,14 @@ static const char *audio_map[][3] = {
{"Speaker PGA", NULL, "Speaker Mux"},
{"LOUT2", NULL, "Speaker PGA"},
{"ROUT2", NULL, "Speaker PGA"},
-
- {NULL, NULL, NULL},
};
static int wm9712_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets,
+ ARRAY_SIZE(wm9712_dapm_widgets));
- /* set up audio path connects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -541,7 +532,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
-struct snd_soc_codec_dai wm9712_dai[] = {
+struct snd_soc_dai wm9712_dai[] = {
{
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97_BUS,
@@ -574,23 +565,23 @@ struct snd_soc_codec_dai wm9712_dai[] = {
};
EXPORT_SYMBOL_GPL(wm9712_dai);
-static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -598,12 +589,12 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
if (try_warm && soc_ac97_ops.warm_reset) {
soc_ac97_ops.warm_reset(codec->ac97);
- if (!(ac97_read(codec, 0) & 0x8000))
+ if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
soc_ac97_ops.reset(codec->ac97);
- if (ac97_read(codec, 0) & 0x8000)
+ if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -618,7 +609,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev,
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -635,7 +626,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
return ret;
}
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret == 0) {
/* Sync reg_cache with the hardware after cold reset */
@@ -647,8 +638,8 @@ static int wm9712_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_ON);
return ret;
}
@@ -682,7 +673,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
codec->num_dai = ARRAY_SIZE(wm9712_dai);
codec->write = ac97_write;
codec->read = ac97_read;
- codec->dapm_event = wm9712_dapm_event;
+ codec->set_bias_level = wm9712_set_bias_level;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -706,7 +697,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm9712_add_controls(codec);
wm9712_add_widgets(codec);
ret = snd_soc_register_card(socdev);
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
index 719105d61e6..d29e8a18ca6 100644
--- a/sound/soc/codecs/wm9712.h
+++ b/sound/soc/codecs/wm9712.h
@@ -8,7 +8,7 @@
#define WM9712_DAI_AC97_HIFI 0
#define WM9712_DAI_AC97_AUX 1
-extern struct snd_soc_codec_dai wm9712_dai[2];
+extern struct snd_soc_dai wm9712_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_wm9712;
#endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 1f241161445..38d1fe0971f 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -10,9 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 4th Feb 2006 Initial version.
- *
* Features:-
*
* o Support for AC97 Codec, Voice DAC and Aux DAC
@@ -456,7 +453,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
SND_SOC_DAPM_VMID("VMID"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* left HP mixer */
{"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -607,21 +604,14 @@ static const char *audio_map[][3] = {
{"Capture Mono Mux", "Stereo", "Capture Mixer"},
{"Capture Mono Mux", "Left", "Left Capture Source"},
{"Capture Mono Mux", "Right", "Right Capture Source"},
-
- {NULL, NULL, NULL},
};
static int wm9713_add_widgets(struct snd_soc_codec *codec)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets,
+ ARRAY_SIZE(wm9713_dapm_widgets));
- /* set up audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
@@ -799,7 +789,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
-static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -810,7 +800,7 @@ static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
* Tristate the PCM DAI lines, tristate can be disabled by calling
* wm9713_set_dai_fmt()
*/
-static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
int tristate)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -826,7 +816,7 @@ static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
* Configure WM9713 clock dividers.
* Voice DAC needs 256 FS
*/
-static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -868,7 +858,7 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
return 0;
}
-static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -886,7 +876,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
gpio |= 0x0018;
break;
case SND_SOC_DAIFMT_CBS_CFS:
- reg |= 0x0200;
+ reg |= 0x2000;
gpio |= 0x001a;
break;
case SND_SOC_DAIFMT_CBS_CFM:
@@ -1011,15 +1001,24 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
}
-#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
+#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
#define WM9713_PCM_FORMATS \
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
-struct snd_soc_codec_dai wm9713_dai[] = {
+struct snd_soc_dai wm9713_dai[] = {
{
.name = "AC97 HiFi",
.type = SND_SOC_DAI_AC97_BUS,
@@ -1061,13 +1060,13 @@ struct snd_soc_codec_dai wm9713_dai[] = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 1,
- .rates = WM9713_RATES,
+ .rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = WM9713_RATES,
+ .rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.ops = {
.hw_params = wm9713_pcm_hw_params,
@@ -1086,44 +1085,44 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
if (try_warm && soc_ac97_ops.warm_reset) {
soc_ac97_ops.warm_reset(codec->ac97);
- if (!(ac97_read(codec, 0) & 0x8000))
+ if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
soc_ac97_ops.reset(codec->ac97);
- if (ac97_read(codec, 0) & 0x8000)
+ if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(wm9713_reset);
-static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
u16 reg;
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
/* enable thermal shutdown */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
/* enable master bias and vmid */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->bias_level = level;
return 0;
}
@@ -1160,7 +1159,7 @@ static int wm9713_soc_resume(struct platform_device *pdev)
return ret;
}
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* do we need to re-start the PLL ? */
if (wm9713->pll_out)
@@ -1176,8 +1175,8 @@ static int wm9713_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
return ret;
}
@@ -1216,7 +1215,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
codec->num_dai = ARRAY_SIZE(wm9713_dai);
codec->write = ac97_write;
codec->read = ac97_read;
- codec->dapm_event = wm9713_dapm_event;
+ codec->set_bias_level = wm9713_set_bias_level;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1238,7 +1237,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
goto reset_err;
}
- wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* unmute the adc - move to kcontrol */
reg = ac97_read(codec, AC97_CD) & 0x7fff;
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
index d357b6c8134..63b8d81756e 100644
--- a/sound/soc/codecs/wm9713.h
+++ b/sound/soc/codecs/wm9713.h
@@ -46,7 +46,7 @@
#define WM9713_DAI_PCM_VOICE 2
extern struct snd_soc_codec_device soc_codec_dev_wm9713;
-extern struct snd_soc_codec_dai wm9713_dai[3];
+extern struct snd_soc_dai wm9713_dai[3];
int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 20680c551aa..8f7e3383490 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,6 +1,6 @@
config SND_DAVINCI_SOC
tristate "SoC Audio for the TI DAVINCI chip"
- depends on ARCH_DAVINCI && SND_SOC
+ depends on ARCH_DAVINCI
help
Say Y or M if you want to add support for codecs attached to
the DAVINCI AC97 or I2S interface. You will also need
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index fcd16524033..5e2c306399e 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -33,24 +33,24 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret = 0;
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
SND_SOC_DAIFMT_IB_NF);
if (ret < 0)
return ret;
/* set the codec system clock */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
@@ -71,7 +71,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
};
/* davinci-evm machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* Headphone connected to HPLOUT, HPROUT */
{"Headphone Jack", NULL, "HPLOUT"},
{"Headphone Jack", NULL, "HPROUT"},
@@ -90,36 +90,30 @@ static const char *audio_map[][3] = {
{"LINE2L", NULL, "Line In"},
{"LINE1R", NULL, "Line In"},
{"LINE2R", NULL, "Line In"},
-
- {NULL, NULL, NULL},
};
/* Logic for a aic3x as connected on a davinci-evm */
static int evm_aic3x_init(struct snd_soc_codec *codec)
{
- int i;
-
/* Add davinci-evm specific widgets */
- for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+ ARRAY_SIZE(aic3x_dapm_widgets));
/* Set up davinci-evm specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
/* not connected */
- snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
- snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
- snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+ snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+ snd_soc_dapm_disable_pin(codec, "HPLCOM");
+ snd_soc_dapm_disable_pin(codec, "HPRCOM");
/* always connected */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line In", 1);
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index c421774b33e..5ebf1ff71c4 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -147,7 +147,7 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
static int davinci_i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
cpu_dai->dma_data = dev->dma_params[substream->stream];
@@ -155,7 +155,7 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
return 0;
}
-static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
@@ -295,11 +295,12 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static int davinci_i2s_probe(struct platform_device *pdev)
+static int davinci_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev;
struct resource *mem, *ioarea;
struct evm_snd_platform_data *pdata;
@@ -356,11 +357,12 @@ err_release_region:
return ret;
}
-static void davinci_i2s_remove(struct platform_device *pdev)
+static void davinci_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
struct resource *mem;
@@ -376,7 +378,7 @@ static void davinci_i2s_remove(struct platform_device *pdev)
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
-struct snd_soc_cpu_dai davinci_i2s_dai = {
+struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 9592d17db32..c5b091807ee 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -12,6 +12,6 @@
#ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H
-extern struct snd_soc_cpu_dai davinci_i2s_dai;
+extern struct snd_soc_dai davinci_i2s_dai;
#endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 6a76927c997..6a5e56a782b 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -350,7 +350,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
static u64 davinci_pcm_dmamask = 0xffffffff;
static int davinci_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret;
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 257101f44e9..3368ace6097 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,8 +1,6 @@
-menu "ALSA SoC audio for Freescale SOCs"
-
config SND_SOC_MPC8610
bool "ALSA SoC support for the MPC8610 SOC"
- depends on SND_SOC && MPC8610_HPCD
+ depends on MPC8610_HPCD
default y if MPC8610
help
Say Y if you want to add support for codecs attached to the SSI
@@ -16,5 +14,3 @@ config SND_SOC_MPC8610_HPCD
default y if MPC8610_HPCD
help
Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
-
-endmenu
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 78de7168d2b..da2bc590286 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -282,7 +282,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* once for each .dai_link in the machine driver's snd_soc_machine
* structure.
*/
-static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
index 430a6ce8b0d..385d4a42603 100644
--- a/sound/soc/fsl/fsl_dma.h
+++ b/sound/soc/fsl/fsl_dma.h
@@ -126,7 +126,7 @@ struct fsl_dma_link_descriptor {
u8 res[4]; /* Reserved */
} __attribute__ ((aligned(32), packed));
-/* DMA information needed to create a snd_soc_cpu_dai object
+/* DMA information needed to create a snd_soc_dai object
*
* ssi_stx_phys: bus address of SSI STX register to use
* ssi_srx_phys: bus address of SSI SRX register to use
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index f588545698f..71bff33f552 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -82,7 +82,7 @@ struct fsl_ssi_private {
struct device *dev;
unsigned int playback;
unsigned int capture;
- struct snd_soc_cpu_dai cpu_dai;
+ struct snd_soc_dai cpu_dai;
struct device_attribute dev_attr;
struct {
@@ -479,7 +479,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
* @freq: the frequency of the given clock ID, currently ignored
* @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
*/
-static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -497,7 +497,7 @@ static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
*
* @format: one of SND_SOC_DAIFMT_xxx
*/
-static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
{
return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
}
@@ -505,7 +505,7 @@ static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
/**
* fsl_ssi_dai_template: template CPU DAI for the SSI
*/
-static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
+static struct snd_soc_dai fsl_ssi_dai_template = {
.playback = {
/* The SSI does not support monaural audio. */
.channels_min = 2,
@@ -569,15 +569,15 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev,
}
/**
- * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
+ * fsl_ssi_create_dai: create a snd_soc_dai structure
*
- * This function is called by the machine driver to create a snd_soc_cpu_dai
+ * This function is called by the machine driver to create a snd_soc_dai
* structure. The function creates an ssi_private object, which contains
- * the snd_soc_cpu_dai. It also creates the sysfs statistics device.
+ * the snd_soc_dai. It also creates the sysfs statistics device.
*/
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
{
- struct snd_soc_cpu_dai *fsl_ssi_dai;
+ struct snd_soc_dai *fsl_ssi_dai;
struct fsl_ssi_private *ssi_private;
int ret = 0;
struct device_attribute *dev_attr;
@@ -588,7 +588,7 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
return NULL;
}
memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
- sizeof(struct snd_soc_cpu_dai));
+ sizeof(struct snd_soc_dai));
fsl_ssi_dai = &ssi_private->cpu_dai;
dev_attr = &ssi_private->dev_attr;
@@ -623,11 +623,11 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
/**
- * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
+ * fsl_ssi_destroy_dai: destroy the snd_soc_dai object
*
* This function undoes the operations of fsl_ssi_create_dai()
*/
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai)
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
{
struct fsl_ssi_private *ssi_private =
container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index c5ce88e1565..83b44d700e3 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -217,8 +217,8 @@ struct fsl_ssi_info {
struct device *dev;
};
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai);
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai);
#endif
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index a00aac7a71f..4bdc9d8fc90 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -58,9 +58,9 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
sound_device->dev.platform_data;
/* Program the signal routing between the SSI and the DMA */
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
@@ -96,62 +96,52 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct mpc8610_hpcd_data *machine_data =
rtd->socdev->dev->platform_data;
int ret = 0;
/* Tell the CPU driver what the serial protocol is. */
- if (cpu_dai->dai_ops.set_fmt) {
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
- machine_data->dai_format);
- if (ret < 0) {
- dev_err(substream->pcm->card->dev,
- "could not set CPU driver audio format\n");
- return ret;
- }
+ ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format);
+ if (ret < 0) {
+ dev_err(substream->pcm->card->dev,
+ "could not set CPU driver audio format\n");
+ return ret;
}
/* Tell the codec driver what the serial protocol is. */
- if (codec_dai->dai_ops.set_fmt) {
- ret = codec_dai->dai_ops.set_fmt(codec_dai,
- machine_data->dai_format);
- if (ret < 0) {
- dev_err(substream->pcm->card->dev,
- "could not set codec driver audio format\n");
- return ret;
- }
+ ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format);
+ if (ret < 0) {
+ dev_err(substream->pcm->card->dev,
+ "could not set codec driver audio format\n");
+ return ret;
}
/*
* Tell the CPU driver what the clock frequency is, and whether it's a
* slave or master.
*/
- if (cpu_dai->dai_ops.set_sysclk) {
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0,
- machine_data->clk_frequency,
- machine_data->cpu_clk_direction);
- if (ret < 0) {
- dev_err(substream->pcm->card->dev,
- "could not set CPU driver clock parameters\n");
- return ret;
- }
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
+ machine_data->clk_frequency,
+ machine_data->cpu_clk_direction);
+ if (ret < 0) {
+ dev_err(substream->pcm->card->dev,
+ "could not set CPU driver clock parameters\n");
+ return ret;
}
/*
* Tell the codec driver what the MCLK frequency is, and whether it's
* a slave or master.
*/
- if (codec_dai->dai_ops.set_sysclk) {
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0,
- machine_data->clk_frequency,
- machine_data->codec_clk_direction);
- if (ret < 0) {
- dev_err(substream->pcm->card->dev,
- "could not set codec driver clock params\n");
- return ret;
- }
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ machine_data->clk_frequency,
+ machine_data->codec_clk_direction);
+ if (ret < 0) {
+ dev_err(substream->pcm->card->dev,
+ "could not set codec driver clock params\n");
+ return ret;
}
return 0;
@@ -170,9 +160,9 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
/* Restore the signal routing */
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[0], 0);
- guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+ guts_set_dmacr(machine_data->guts, machine_data->dma_id,
machine_data->dma_channel_id[1], 0);
switch (machine_data->ssi_id) {
@@ -182,7 +172,7 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
break;
case 1:
clrsetbits_be32(&machine_data->guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+ CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
break;
}
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 0230d83e8e5..aea27e70043 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,5 +1,3 @@
-menu "SoC Audio for the Texas Instruments OMAP"
-
config SND_OMAP_SOC
tristate "SoC Audio for the Texas Instruments OMAP chips"
depends on ARCH_OMAP && SND_SOC
@@ -15,5 +13,3 @@ config SND_OMAP_SOC_N810
select SND_SOC_TLV320AIC3X
help
Say Y if you want to add support for SoC audio on Nokia N810.
-
-endmenu
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 6533563a601..02cec96859b 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -30,15 +30,15 @@
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
-#include <asm/arch/gpio.h>
+#include <linux/gpio.h>
#include <asm/arch/mcbsp.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"
#include "../codecs/tlv320aic3x.h"
-#define RX44_HEADSET_AMP_GPIO 10
-#define RX44_SPEAKER_AMP_GPIO 101
+#define N810_HEADSET_AMP_GPIO 10
+#define N810_SPEAKER_AMP_GPIO 101
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
@@ -46,13 +46,26 @@ static struct clk *func96m_clk;
static int n810_spk_func;
static int n810_jack_func;
+static int n810_dmic_func;
static void n810_ext_control(struct snd_soc_codec *codec)
{
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
+ if (n810_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
+
+ if (n810_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
- snd_soc_dapm_sync_endpoints(codec);
+ if (n810_dmic_func)
+ snd_soc_dapm_enable_pin(codec, "DMic");
+ else
+ snd_soc_dapm_disable_pin(codec, "DMic");
+
+ snd_soc_dapm_sync(codec);
}
static int n810_startup(struct snd_pcm_substream *substream)
@@ -73,12 +86,12 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int err;
/* Set codec DAI configuration */
- err = codec_dai->dai_ops.set_fmt(codec_dai,
+ err = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
@@ -86,7 +99,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
return err;
/* Set cpu DAI configuration */
- err = cpu_dai->dai_ops.set_fmt(cpu_dai,
+ err = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
@@ -94,7 +107,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
return err;
/* Set the codec system clock for DAC and ADC */
- err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
SND_SOC_CLOCK_IN);
return err;
@@ -150,13 +163,35 @@ static int n810_set_jack(struct snd_kcontrol *kcontrol,
return 1;
}
+static int n810_get_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = n810_dmic_func;
+
+ return 0;
+}
+
+static int n810_set_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (n810_dmic_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ n810_dmic_func = ucontrol->value.integer.value[0];
+ n810_ext_control(codec);
+
+ return 1;
+}
+
static int n810_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
- omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
else
- omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
return 0;
}
@@ -165,9 +200,9 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
- omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
else
- omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
return 0;
}
@@ -175,21 +210,27 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
+ SND_SOC_DAPM_MIC("DMic", NULL),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
{"Headphone Jack", NULL, "HPLOUT"},
{"Headphone Jack", NULL, "HPROUT"},
{"Ext Spk", NULL, "LLOUT"},
{"Ext Spk", NULL, "RLOUT"},
+
+ {"DMic Rate 64", NULL, "Mic Bias 2V"},
+ {"Mic Bias 2V", NULL, "DMic"},
};
static const char *spk_function[] = {"Off", "On"};
static const char *jack_function[] = {"Off", "Headphone"};
+static const char *input_function[] = {"ADC", "Digital Mic"};
static const struct soc_enum n810_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
};
static const struct snd_kcontrol_new aic33_n810_controls[] = {
@@ -197,6 +238,8 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
n810_get_spk, n810_set_spk),
SOC_ENUM_EXT("Jack Function", n810_enum[1],
n810_get_jack, n810_set_jack),
+ SOC_ENUM_EXT("Input Select", n810_enum[2],
+ n810_get_input, n810_set_input),
};
static int n810_aic33_init(struct snd_soc_codec *codec)
@@ -204,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
int i, err;
/* Not connected */
- snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
- snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
- snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+ snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+ snd_soc_dapm_disable_pin(codec, "HPLCOM");
+ snd_soc_dapm_disable_pin(codec, "HPRCOM");
/* Add N810 specific controls */
for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
@@ -217,15 +260,13 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
}
/* Add N810 specific widgets */
- for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
+ ARRAY_SIZE(aic33_dapm_widgets));
/* Set up N810 specific audio path audio_map */
- for (i = 0; i < ARRAY_SIZE(audio_map); i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -250,6 +291,8 @@ static struct snd_soc_machine snd_soc_machine_n810 = {
/* Audio private data */
static struct aic3x_setup_data n810_aic33_setup = {
.i2c_address = 0x18,
+ .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
+ .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
};
/* Audio subsystem */
@@ -267,7 +310,7 @@ static int __init n810_soc_init(void)
int err;
struct device *dev;
- if (!machine_is_nokia_n810())
+ if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
return -ENODEV;
n810_snd_device = platform_device_alloc("soc-audio", -1);
@@ -305,12 +348,12 @@ static int __init n810_soc_init(void)
clk_set_parent(sys_clkout2_src, func96m_clk);
clk_set_rate(sys_clkout2, 12000000);
- if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
+ if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
BUG();
- if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
+ if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
BUG();
- omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
- omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
+ gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
+ gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
return 0;
err2:
@@ -325,6 +368,9 @@ err1:
static void __exit n810_soc_exit(void)
{
+ gpio_free(N810_SPEAKER_AMP_GPIO);
+ gpio_free(N810_HEADSET_AMP_GPIO);
+
platform_device_unregister(n810_snd_device);
}
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 40d87e6d0de..00b0c9d73cd 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -103,7 +103,7 @@ static const unsigned long omap2420_mcbsp_port[][2] = {};
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
int err = 0;
@@ -116,7 +116,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
if (!cpu_dai->active) {
@@ -128,7 +128,7 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
int err = 0;
@@ -157,7 +157,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
@@ -223,7 +223,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
* This must be called before _set_clkdiv and _set_sysclk since McBSP register
* cache is initialized here
*/
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -292,7 +292,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
return 0;
}
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -347,7 +347,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
return 0;
}
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq,
int dir)
{
@@ -376,7 +376,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
return err;
}
-struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
+struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = {
{
.name = "omap-mcbsp-dai",
.id = 0,
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 9965fd4b042..ed8afb55067 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -44,6 +44,6 @@ enum omap_mcbsp_div {
*/
#define NUM_LINKS 1
-extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
+extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
#endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 62370202c64..e092f3d836d 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -316,7 +316,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
-int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 484f883459e..12f6ac99b04 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,6 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
- depends on ARCH_PXA && SND_SOC
+ depends on ARCH_PXA
help
Say Y or M if you want to add support for codecs attached to
the PXA2xx AC97, I2S or SSP interface. You will also need
@@ -62,3 +62,12 @@ config SND_PXA2XX_SOC_E800
help
Say Y if you want to add support for SoC audio on the
Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_EM_X270
+ tristate "SoC Audio support for CompuLab EM-x270"
+ depends on SND_PXA2XX_SOC && MACH_EM_X270
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on
+ CompuLab EM-x270.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 04e5646f75b..5bc8edf9dca 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -13,10 +13,11 @@ snd-soc-poodle-objs := poodle.o
snd-soc-tosa-objs := tosa.o
snd-soc-e800-objs := e800_wm9712.o
snd-soc-spitz-objs := spitz.o
+snd-soc-em-x270-objs := em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
-
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 7f32a116757..c0294464a23 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -11,10 +11,6 @@
* 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.
- *
- * Revision history
- * 30th Nov 2005 Initial version.
- *
*/
#include <linux/module.h>
@@ -54,47 +50,51 @@ static int corgi_spk_func;
static void corgi_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
-
/* set up jack connection */
switch (corgi_jack_func) {
case CORGI_HP:
- hp = 1;
/* set = unmute headphone */
set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_MIC:
- mic = 1;
/* reset = mute headphone */
reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_LINE:
- line = 1;
reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case CORGI_HEADSET:
- hs = 1;
- mic = 1;
reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
break;
}
if (corgi_spk_func == CORGI_SPK_ON)
- spk = 1;
-
- /* set the enpoints to their new connetion states */
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* signal a DAPM event */
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int corgi_startup(struct snd_pcm_substream *substream)
@@ -123,8 +123,8 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -143,25 +143,25 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -247,7 +247,7 @@ SND_SOC_DAPM_HP("Headset Jack", NULL),
};
/* Corgi machine audio map (connections to the codec pins) */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headset Jack - in = micin, out = LHPOUT*/
{"Headset Jack", NULL, "LHPOUT"},
@@ -265,8 +265,6 @@ static const char *audio_map[][3] = {
/* Same as the above but no mic bias for line signals */
{"MICIN", NULL, "Line Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -291,8 +289,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+ snd_soc_dapm_disable_pin(codec, "LLINEIN");
+ snd_soc_dapm_disable_pin(codec, "RLINEIN");
/* Add corgi specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
@@ -303,15 +301,13 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
}
/* Add corgi specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
/* Set up corgi specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
new file mode 100644
index 00000000000..02dcac39cdf
--- /dev/null
+++ b/sound/soc/pxa/em-x270.c
@@ -0,0 +1,102 @@
+/*
+ * em-x270.c -- SoC audio for EM-X270
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+ },
+};
+
+static struct snd_soc_machine em_x270 = {
+ .name = "EM-X270",
+ .dai_link = em_x270_dai,
+ .num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct snd_soc_device em_x270_snd_devdata = {
+ .machine = &em_x270,
+ .platform = &pxa2xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+ int ret;
+
+ if (!machine_is_em_x270())
+ return -ENODEV;
+
+ em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!em_x270_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata);
+ em_x270_snd_devdata.dev = &em_x270_snd_device->dev;
+ ret = platform_device_add(em_x270_snd_device);
+
+ if (ret)
+ platform_device_put(em_x270_snd_device);
+
+ return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+ platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 7e830b21894..65a4e9a8c39 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -48,8 +48,6 @@ static int poodle_spk_func;
static void poodle_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0;
-
/* set up jack connection */
if (poodle_jack_func == POODLE_HP) {
/* set = unmute headphone */
@@ -57,23 +55,23 @@ static void poodle_ext_control(struct snd_soc_codec *codec)
POODLE_LOCOMO_GPIO_MUTE_L, 1);
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_R, 1);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
} else {
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_L, 0);
locomo_gpio_write(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_MUTE_R, 0);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
}
- if (poodle_spk_func == POODLE_SPK_ON)
- spk = 1;
-
/* set the enpoints to their new connetion states */
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+ if (poodle_spk_func == POODLE_SPK_ON)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* signal a DAPM event */
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int poodle_startup(struct snd_pcm_substream *substream)
@@ -104,8 +102,8 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -124,25 +122,25 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -215,8 +213,8 @@ SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
};
-/* Corgi machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+/* Corgi machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to LHPOUT1, RHPOUT1 */
{"Headphone Jack", NULL, "LHPOUT"},
@@ -225,8 +223,6 @@ static const char *audio_map[][3] = {
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Off", "Headphone"};
@@ -250,9 +246,9 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
- snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
+ snd_soc_dapm_disable_pin(codec, "LLINEIN");
+ snd_soc_dapm_disable_pin(codec, "RLINEIN");
+ snd_soc_dapm_enable_pin(codec, "MICIN");
/* Add poodle specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
@@ -263,15 +259,13 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
}
/* Add poodle specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+ ARRAY_SIZE(wm8731_dapm_widgets));
/* Set up poodle specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 97ec2d90547..059af815ea0 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -283,7 +283,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
#ifdef CONFIG_PM
static int pxa2xx_ac97_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
GCR |= GCR_ACLINK_OFF;
clk_disable(ac97_clk);
@@ -291,7 +291,7 @@ static int pxa2xx_ac97_suspend(struct platform_device *pdev,
}
static int pxa2xx_ac97_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
@@ -310,7 +310,8 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev,
#define pxa2xx_ac97_resume NULL
#endif
-static int pxa2xx_ac97_probe(struct platform_device *pdev)
+static int pxa2xx_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
int ret;
@@ -355,7 +356,8 @@ static int pxa2xx_ac97_probe(struct platform_device *pdev)
return ret;
}
-static void pxa2xx_ac97_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
GCR |= GCR_ACLINK_OFF;
free_irq(IRQ_AC97, NULL);
@@ -372,7 +374,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -386,7 +388,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
@@ -400,7 +402,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -418,7 +420,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
* There is only 1 physical AC97 interface for pxa2xx, but it
* has extra fifo's that can be used for aux DACs and ADCs.
*/
-struct snd_soc_cpu_dai pxa_ac97_dai[] = {
+struct snd_soc_dai pxa_ac97_dai[] = {
{
.name = "pxa2xx-ac97",
.id = 0,
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index b8ccfee095c..e390de8edcd 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,7 @@
#define PXA2XX_DAI_AC97_AUX 1
#define PXA2XX_DAI_AC97_MIC 2
-extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
+extern struct snd_soc_dai pxa_ac97_dai[3];
/* platform data */
extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 42507103097..8f96d87f7b4 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -9,15 +9,13 @@
* 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.
- *
- * Revision history
- * 12th Aug 2005 Initial version.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -40,6 +38,7 @@ struct pxa_i2s_port {
u32 fmt;
};
static struct pxa_i2s_port pxa_i2s;
+static struct clk *clk_i2s;
static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
.name = "I2S PCM Stereo out",
@@ -80,7 +79,11 @@ static struct pxa2xx_gpio gpio_bus[] = {
static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ clk_i2s = clk_get(NULL, "I2SCLK");
+ if (IS_ERR(clk_i2s))
+ return PTR_ERR(clk_i2s);
if (!cpu_dai->active) {
SACR0 |= SACR0_RST;
@@ -101,7 +104,7 @@ static int pxa_i2s_wait(void)
return 0;
}
-static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
/* interface format */
@@ -127,7 +130,7 @@ static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
return 0;
}
-static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
if (clk_id != PXA2XX_I2S_SYSCLK)
@@ -143,13 +146,13 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
- pxa_set_cken(CKEN_I2S, 1);
+ clk_enable(clk_i2s);
pxa_i2s_wait();
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -234,13 +237,15 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
SACR0 &= ~SACR0_ENB;
pxa_i2s_wait();
- pxa_set_cken(CKEN_I2S, 0);
+ clk_disable(clk_i2s);
}
+
+ clk_put(clk_i2s);
}
#ifdef CONFIG_PM
static int pxa2xx_i2s_suspend(struct platform_device *dev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -258,7 +263,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev,
}
static int pxa2xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -283,7 +288,7 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
-struct snd_soc_cpu_dai pxa_i2s_dai = {
+struct snd_soc_dai pxa_i2s_dai = {
.name = "pxa2xx-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
index 4435bd9f884..e2def441153 100644
--- a/sound/soc/pxa/pxa2xx-i2s.h
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -15,6 +15,6 @@
/* I2S clock */
#define PXA2XX_I2S_SYSCLK 0
-extern struct snd_soc_cpu_dai pxa_i2s_dai;
+extern struct snd_soc_dai pxa_i2s_dai;
#endif
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 01ad7bf716b..2df03ee5819 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -330,7 +330,7 @@ static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
-int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index d8b8372db00..64385797da5 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -12,9 +12,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 30th Nov 2005 Initial version.
- *
*/
#include <linux/module.h>
@@ -54,60 +51,60 @@ static int spitz_spk_func;
static void spitz_ext_control(struct snd_soc_codec *codec)
{
if (spitz_spk_func == SPITZ_SPK_ON)
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
- snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* set up jack connection */
switch (spitz_jack_func) {
case SPITZ_HP:
/* enable and unmute hp jack, disable mic bias */
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
break;
case SPITZ_MIC:
/* enable mic jack and bias, mute hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
break;
case SPITZ_LINE:
/* enable line jack, disable mic bias and mute hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_enable_pin(codec, "Line Jack");
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
break;
case SPITZ_HEADSET:
/* enable and unmute headset jack enable mic bias, mute L hp */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
break;
case SPITZ_HP_OFF:
/* jack removed, everything off */
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
- snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
+ snd_soc_dapm_disable_pin(codec, "Mic Jack");
+ snd_soc_dapm_disable_pin(codec, "Line Jack");
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
break;
}
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int spitz_startup(struct snd_pcm_substream *substream)
@@ -124,8 +121,8 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@@ -144,25 +141,25 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
- ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -250,7 +247,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
};
/* Spitz machine audio_map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to LOUT1, ROUT1 */
{"Headphone Jack", NULL, "LOUT1"},
@@ -269,8 +266,6 @@ static const char *audio_map[][3] = {
/* line is connected to input 1 - no bias */
{"LINPUT1", NULL, "Line Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -296,13 +291,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
int i, err;
/* NC codec pins */
- snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
- snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "MONO", 0);
+ snd_soc_dapm_disable_pin(codec, "RINPUT1");
+ snd_soc_dapm_disable_pin(codec, "LINPUT2");
+ snd_soc_dapm_disable_pin(codec, "RINPUT2");
+ snd_soc_dapm_disable_pin(codec, "LINPUT3");
+ snd_soc_dapm_disable_pin(codec, "RINPUT3");
+ snd_soc_dapm_disable_pin(codec, "OUT3");
+ snd_soc_dapm_disable_pin(codec, "MONO");
/* Add spitz specific controls */
for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
@@ -313,15 +308,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
}
/* Add spitz specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+ ARRAY_SIZE(wm8750_dapm_widgets));
- /* Set up spitz specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++)
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
+ /* Set up spitz specific audio paths */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 7346d7e5d06..b6edb61a3a3 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -12,9 +12,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 30th Nov 2005 Initial version.
- *
* GPIO's
* 1 - Jack Insertion
* 5 - Hookswitch (headset answer/hang up switch)
@@ -55,29 +52,31 @@ static int tosa_spk_func;
static void tosa_ext_control(struct snd_soc_codec *codec)
{
- int spk = 0, mic_int = 0, hp = 0, hs = 0;
-
/* set up jack connection */
switch (tosa_jack_func) {
case TOSA_HP:
- hp = 1;
+ snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case TOSA_MIC_INT:
- mic_int = 1;
+ snd_soc_dapm_enable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case TOSA_HEADSET:
- hs = 1;
+ snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Headset Jack");
break;
}
if (tosa_spk_func == TOSA_SPK_ON)
- spk = 1;
+ snd_soc_dapm_enable_pin(codec, "Speaker");
+ else
+ snd_soc_dapm_disable_pin(codec, "Speaker");
- snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
- snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
- snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
- snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
}
static int tosa_startup(struct snd_pcm_substream *substream)
@@ -154,7 +153,7 @@ SND_SOC_DAPM_SPK("Speaker", NULL),
};
/* tosa audio map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to HPOUTL, HPOUTR */
{"Headphone Jack", NULL, "HPOUTL"},
@@ -173,8 +172,6 @@ static const char *audio_map[][3] = {
{"Headset Jack", NULL, "HPOUTR"},
{"LINEINR", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Headset Jack"},
-
- {NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -196,8 +193,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
{
int i, err;
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
+ snd_soc_dapm_disable_pin(codec, "OUT3");
+ snd_soc_dapm_disable_pin(codec, "MONOOUT");
/* add tosa specific controls */
for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
@@ -208,17 +205,13 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
}
/* add tosa specific widgets */
- for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
- snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
- }
+ snd_soc_dapm_new_controls(codec, tosa_dapm_widgets,
+ ARRAY_SIZE(tosa_dapm_widgets));
/* set up tosa specific audio path audio_map */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 1f6dbfc4caa..b9f2353effe 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,7 +1,6 @@
config SND_S3C24XX_SOC
tristate "SoC Audio for the Samsung S3C24XX chips"
- depends on ARCH_S3C2410 && SND_SOC
- select SND_PCM
+ depends on ARCH_S3C2410
help
Say Y or M if you want to add support for codecs attached to
the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -16,7 +15,6 @@ config SND_S3C2412_SOC_I2S
config SND_S3C2443_SOC_AC97
tristate
select AC97_BUS
- select SND_AC97_CODEC
select SND_SOC_AC97_BUS
config SND_S3C24XX_SOC_NEO1973_WM8753
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 0e9d1c5f248..4d7a9aa15f1 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -10,10 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 20th Jan 2007 Initial version.
- * 05th Feb 2007 Rename all to Neo1973
- *
*/
#include <linux/module.h>
@@ -26,6 +22,7 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
#include <asm/mach-types.h>
#include <asm/hardware/scoop.h>
@@ -43,6 +40,14 @@
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
+/* Debugging stuff */
+#define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
+#if S3C24XX_SOC_NEO1973_WM8753_DEBUG
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
+#else
+#define DBG(x...)
+#endif
+
/* define the scenarios */
#define NEO_AUDIO_OFF 0
#define NEO_GSM_CALL_AUDIO_HANDSET 1
@@ -61,12 +66,14 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int pll_out = 0, bclk = 0;
int ret = 0;
unsigned long iis_clkrate;
+ DBG("Entered %s\n", __func__);
+
iis_clkrate = s3c24xx_i2s_get_clockrate();
switch (params_rate(params)) {
@@ -101,44 +108,44 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
}
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai,
+ ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+ ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set MCLK division for sample rate */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
S3C2410_IISMOD_32FS);
if (ret < 0)
return ret;
/* set codec BCLK division for sample rate */
- ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
if (ret < 0)
return ret;
/* set prescaler division for sample rate */
- ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
S3C24XX_PRESCALE(4, 4));
if (ret < 0)
return ret;
/* codec PLL input is PCLK/4 */
- ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -149,10 +156,12 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ DBG("Entered %s\n", __func__);
/* disable the PLL */
- return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
}
/*
@@ -167,11 +176,13 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
unsigned int pcmdiv = 0;
int ret = 0;
unsigned long iis_clkrate;
+ DBG("Entered %s\n", __func__);
+
iis_clkrate = s3c24xx_i2s_get_clockrate();
if (params_rate(params) != 8000)
@@ -183,24 +194,24 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
/* todo: gg check mode (DSP_B) against CSR datasheet */
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set codec PCM division for sample rate */
- ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
if (ret < 0)
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -211,10 +222,12 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ DBG("Entered %s\n", __func__);
/* disable the PLL */
- return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
}
static struct snd_soc_ops neo1973_voice_ops = {
@@ -233,79 +246,81 @@ static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
{
+ DBG("Entered %s\n", __func__);
+
switch (neo1973_scenario) {
case NEO_AUDIO_OFF:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_HANDSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_enable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_HEADSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_enable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_GSM_CALL_AUDIO_BLUETOOTH:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_STEREO_TO_SPEAKERS:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_STEREO_TO_HEADPHONES:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_enable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_HANDSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_enable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_HEADSET:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_enable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
case NEO_CAPTURE_BLUETOOTH:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
break;
default:
- snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
- snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
- snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
- snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
+ snd_soc_dapm_disable_pin(codec, "Audio Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Call Mic");
}
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
@@ -315,6 +330,8 @@ static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ DBG("Entered %s\n", __func__);
+
if (neo1973_scenario == ucontrol->value.integer.value[0])
return 0;
@@ -327,6 +344,8 @@ static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
static void lm4857_write_regs(void)
{
+ DBG("Entered %s\n", __func__);
+
if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
printk(KERN_ERR "lm4857: i2c write failed\n");
}
@@ -338,6 +357,8 @@ static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
int shift = (kcontrol->private_value >> 8) & 0x0F;
int mask = (kcontrol->private_value >> 16) & 0xFF;
+ DBG("Entered %s\n", __func__);
+
ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
return 0;
}
@@ -364,6 +385,8 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
{
u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
+ DBG("Entered %s\n", __func__);
+
if (value)
value -= 5;
@@ -376,6 +399,8 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
{
u8 value = ucontrol->value.integer.value[0];
+ DBG("Entered %s\n", __func__);
+
if (value)
value += 5;
@@ -397,8 +422,7 @@ static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
};
-/* example machine audio_mapnections */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route dapm_routes[] = {
/* Connections to the lm4857 amp */
{"Audio Out", NULL, "LOUT1"},
@@ -421,8 +445,6 @@ static const char *audio_map[][3] = {
/* Connect the ALC pins */
{"ACIN", NULL, "ACOP"},
-
- {NULL, NULL, NULL},
};
static const char *lm4857_mode[] = {
@@ -453,13 +475,16 @@ static const struct soc_enum neo_scenario_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
};
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
- SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
- SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
- SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
- lm4857_get_reg, lm4857_set_reg),
+ SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+ SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+ SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+ lm4857_get_reg, lm4857_set_reg, mono_tlv),
SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
lm4857_get_mode, lm4857_set_mode),
SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
@@ -483,21 +508,23 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
{
int i, err;
+ DBG("Entered %s\n", __func__);
+
/* set up NC codec pins */
- snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
- snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
- snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
- snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+ snd_soc_dapm_disable_pin(codec, "LOUT2");
+ snd_soc_dapm_disable_pin(codec, "ROUT2");
+ snd_soc_dapm_disable_pin(codec, "OUT3");
+ snd_soc_dapm_disable_pin(codec, "OUT4");
+ snd_soc_dapm_disable_pin(codec, "LINE1");
+ snd_soc_dapm_disable_pin(codec, "LINE2");
/* set endpoints to default mode */
set_scenario_endpoints(codec, NEO_AUDIO_OFF);
/* Add neo1973 specific widgets */
- for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+ ARRAY_SIZE(wm8753_dapm_widgets));
/* add neo1973 specific controls */
for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
@@ -508,20 +535,18 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
return err;
}
- /* set up neo1973 specific audio path audio_mapnects */
- for (i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
+ /* set up neo1973 specific audio routes */
+ err = snd_soc_dapm_add_routes(codec, dapm_routes,
+ ARRAY_SIZE(dapm_routes));
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
/*
* BT Codec DAI
*/
-static struct snd_soc_cpu_dai bt_dai = {
+static struct snd_soc_dai bt_dai = {
.name = "Bluetooth",
.id = 0,
.type = SND_SOC_DAI_PCM,
@@ -583,6 +608,8 @@ static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
{
int ret;
+ DBG("Entered %s\n", __func__);
+
client_template.adapter = adap;
client_template.addr = addr;
@@ -606,6 +633,8 @@ exit_err:
static int lm4857_i2c_detach(struct i2c_client *client)
{
+ DBG("Entered %s\n", __func__);
+
i2c_detach_client(client);
kfree(client);
return 0;
@@ -613,6 +642,8 @@ static int lm4857_i2c_detach(struct i2c_client *client)
static int lm4857_i2c_attach(struct i2c_adapter *adap)
{
+ DBG("Entered %s\n", __func__);
+
return i2c_probe(adap, &addr_data, lm4857_amp_probe);
}
@@ -620,6 +651,8 @@ static u8 lm4857_state;
static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
{
+ DBG("Entered %s\n", __func__);
+
dev_dbg(&dev->dev, "lm4857_suspend\n");
lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
if (lm4857_state) {
@@ -631,6 +664,8 @@ static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
static int lm4857_resume(struct i2c_client *dev)
{
+ DBG("Entered %s\n", __func__);
+
if (lm4857_state) {
lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
lm4857_write_regs();
@@ -640,6 +675,8 @@ static int lm4857_resume(struct i2c_client *dev)
static void lm4857_shutdown(struct i2c_client *dev)
{
+ DBG("Entered %s\n", __func__);
+
dev_dbg(&dev->dev, "lm4857_shutdown\n");
lm4857_regs[LM4857_CTRL] &= 0xf0;
lm4857_write_regs();
@@ -671,6 +708,8 @@ static int __init neo1973_init(void)
{
int ret;
+ DBG("Entered %s\n", __func__);
+
neo1973_snd_device = platform_device_alloc("soc-audio", -1);
if (!neo1973_snd_device)
return -ENOMEM;
@@ -691,6 +730,8 @@ static int __init neo1973_init(void)
static void __exit neo1973_exit(void)
{
+ DBG("Entered %s\n", __func__);
+
i2c_del_driver(&lm4857_i2c_driver);
platform_device_unregister(neo1973_snd_device);
}
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index c4a46dd589b..ee4676ed128 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -295,7 +295,7 @@ static inline int s3c2412_snd_is_clkmaster(void)
/*
* Set S3C2412 I2S DAI format
*/
-static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
u32 iismod;
@@ -500,7 +500,7 @@ EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
/*
* Set S3C2412 Clock source
*/
-static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
@@ -528,7 +528,7 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Set S3C2412 Clock dividers
*/
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -601,7 +601,8 @@ struct clk *s3c2412_get_iisclk(void)
EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
-static int s3c2412_i2s_probe(struct platform_device *pdev)
+static int s3c2412_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
DBG("Entered %s\n", __func__);
@@ -647,7 +648,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
static int s3c2412_i2s_suspend(struct platform_device *dev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
u32 iismod;
@@ -675,7 +676,7 @@ static int s3c2412_i2s_suspend(struct platform_device *dev,
}
static int s3c2412_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *dai)
+ struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -707,7 +708,7 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-struct snd_soc_cpu_dai s3c2412_i2s_dai = {
+struct snd_soc_dai s3c2412_i2s_dai = {
.name = "s3c2412-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index 27f48e1ffa8..aac08a25e54 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -24,7 +24,7 @@
extern struct clk *s3c2412_get_iisclk(void);
-extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
+extern struct snd_soc_dai s3c2412_i2s_dai;
struct s3c2412_rate_calc {
unsigned int clk_div; /* for prescaler */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index e81d9a6c83d..783349b7fed 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -10,9 +10,6 @@
* 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.
- *
- * Revision history
- * 21st Mar 2007 Initial Version
*/
#include <linux/init.h>
@@ -212,7 +209,8 @@ static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
.dma_size = 4,
};
-static int s3c2443_ac97_probe(struct platform_device *pdev)
+static int s3c2443_ac97_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
int ret;
u32 ac_glbctrl;
@@ -263,7 +261,8 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
return ret;
}
-static void s3c2443_ac97_remove(struct platform_device *pdev)
+static void s3c2443_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
free_irq(IRQ_S3C244x_AC97, NULL);
clk_disable(s3c24xx_ac97.ac97_clk);
@@ -275,7 +274,7 @@ static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
@@ -317,7 +316,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -353,7 +352,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+struct snd_soc_dai s3c2443_ac97_dai[] = {
{
.name = "s3c2443-ac97",
.id = 0,
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index bf03e8ed16c..a96dcadf28b 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -26,6 +26,6 @@
#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
#endif
-extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+extern struct snd_soc_dai s3c2443_ac97_dai[];
#endif /*S3C24XXAC97_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 1ed6afd4545..397524282b5 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -12,11 +12,6 @@
* 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.
- *
- *
- * Revision history
- * 11th Dec 2006 Merged with Simtec driver
- * 10th Nov 2006 Initial version.
*/
#include <linux/init.h>
@@ -180,7 +175,7 @@ static void s3c24xx_snd_rxctrl(int on)
static int s3c24xx_snd_lrsync(void)
{
u32 iiscon;
- unsigned long timeout = jiffies + msecs_to_jiffies(5);
+ int timeout = 50; /* 5ms */
DBG("Entered %s\n", __func__);
@@ -189,8 +184,9 @@ static int s3c24xx_snd_lrsync(void)
if (iiscon & S3C2410_IISCON_LRINDEX)
break;
- if (time_after(jiffies, timeout))
+ if (!timeout--)
return -ETIMEDOUT;
+ udelay(100);
}
return 0;
@@ -209,7 +205,7 @@ static inline int s3c24xx_snd_is_clkmaster(void)
/*
* Set S3C24xx I2S DAI format
*/
-static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
u32 iismod;
@@ -317,7 +313,7 @@ exit_err:
/*
* Set S3C24xx Clock source
*/
-static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -343,7 +339,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
/*
* Set S3C24xx Clock dividers
*/
-static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
u32 reg;
@@ -381,7 +377,8 @@ u32 s3c24xx_i2s_get_clockrate(void)
}
EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
-static int s3c24xx_i2s_probe(struct platform_device *pdev)
+static int s3c24xx_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
DBG("Entered %s\n", __func__);
@@ -414,7 +411,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
static int s3c24xx_i2s_suspend(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
@@ -429,7 +426,7 @@ static int s3c24xx_i2s_suspend(struct platform_device *pdev,
}
static int s3c24xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_cpu_dai *cpu_dai)
+ struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +449,7 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
+struct snd_soc_dai s3c24xx_i2s_dai = {
.name = "s3c24xx-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
index 537b4ecce8a..726d91cf4e1 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -32,6 +32,6 @@
u32 s3c24xx_i2s_get_clockrate(void);
-extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
+extern struct snd_soc_dai s3c24xx_i2s_dai;
#endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 7806ae61461..cef79b34dc6 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -12,10 +12,6 @@
* 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.
- *
- * Revision history
- * 11th Dec 2006 Merged with Simtec driver
- * 10th Nov 2006 Initial version.
*/
#include <linux/module.h>
@@ -433,7 +429,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
static int s3c24xx_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
{
int ret = 0;
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index b4a56302b9a..8515d6ff03f 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -10,9 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 8th Mar 2007 Initial version.
- *
*/
#include <linux/module.h>
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 4c1e013381c..54bd604012a 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -3,7 +3,7 @@ menu "SoC Audio support for SuperH"
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
- depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+ depends on CPU_SUBTYPE_SH7760 && SH_DMABRG
help
Enable this option for SH7760 AC97/I2S audio support.
@@ -13,10 +13,9 @@ config SND_SOC_PCM_SH7760
##
config SND_SOC_SH4_HAC
+ tristate
select AC97_BUS
select SND_SOC_AC97_BUS
- select SND_AC97_CODEC
- tristate
config SND_SOC_SH4_SSI
tristate
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 7a3ce80d672..9faa12622d0 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -326,7 +326,7 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
}
static int camelot_pcm_new(struct snd_card *card,
- struct snd_soc_codec_dai *dai,
+ struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index b7b676b3d67..df7bc345c32 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -266,7 +266,7 @@ static int hac_hw_params(struct snd_pcm_substream *substream,
#define AC97_FMTS \
SNDRV_PCM_FMTBIT_S16_LE
-struct snd_soc_cpu_dai sh4_hac_dai[] = {
+struct snd_soc_dai sh4_hac_dai[] = {
{
.name = "HAC0",
.id = 0,
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 2f91de84c5c..92bfaf4774a 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -20,12 +20,12 @@
#define IPSEL 0xFE400034
/* platform specific structs can be declared here */
-extern struct snd_soc_cpu_dai sh4_hac_dai[2];
+extern struct snd_soc_dai sh4_hac_dai[2];
extern struct snd_soc_platform sh7760_soc_platform;
static int machine_init(struct snd_soc_codec *codec)
{
- snd_soc_dapm_sync_endpoints(codec);
+ snd_soc_dapm_sync(codec);
return 0;
}
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 3388bc3d62d..55c3464163a 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -208,7 +208,7 @@ static int ssi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
+static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
@@ -222,7 +222,7 @@ static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
* This divider is used to generate the SSI_SCK (I2S bitclock) from the
* clock at the HAC_BIT_CLK ("oversampling clock") pin.
*/
-static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
+static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr;
@@ -245,7 +245,7 @@ static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
return 0;
}
-static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
+static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr = SSIREG(SSICR);
@@ -332,7 +332,7 @@ static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
-struct snd_soc_cpu_dai sh4_ssi_dai[] = {
+struct snd_soc_dai sh4_ssi_dai[] = {
{
.name = "SSI0",
.id = 0,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e148db940cf..83f1190293a 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -14,10 +14,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 12th Aug 2005 Initial version.
- * 25th Oct 2005 Working Codec, Interface and Platform registration.
- *
* TODO:
* o Add hw rules to enforce rates, etc.
* o More testing with other codecs/machines.
@@ -112,9 +108,9 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
-static inline const char* get_dai_name(int type)
+static inline const char *get_dai_name(int type)
{
- switch(type) {
+ switch (type) {
case SND_SOC_DAI_AC97_BUS:
case SND_SOC_DAI_AC97:
return "AC97";
@@ -138,8 +134,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
@@ -182,9 +178,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
/* Check that the codec and cpu DAI's are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
- max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
+ max(codec_dai->playback.rate_min,
+ cpu_dai->playback.rate_min);
runtime->hw.rate_max =
- min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
+ min(codec_dai->playback.rate_max,
+ cpu_dai->playback.rate_max);
runtime->hw.channels_min =
max(codec_dai->playback.channels_min,
cpu_dai->playback.channels_min);
@@ -197,9 +195,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
codec_dai->playback.rates & cpu_dai->playback.rates;
} else {
runtime->hw.rate_min =
- max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
+ max(codec_dai->capture.rate_min,
+ cpu_dai->capture.rate_min);
runtime->hw.rate_max =
- min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
+ min(codec_dai->capture.rate_max,
+ cpu_dai->capture.rate_max);
runtime->hw.channels_min =
max(codec_dai->capture.channels_min,
cpu_dai->capture.channels_min);
@@ -229,7 +229,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
goto machine_err;
}
- dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
+ dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
@@ -272,11 +272,11 @@ static void close_delayed_work(struct work_struct *work)
struct snd_soc_device *socdev =
container_of(work, struct snd_soc_device, delayed_work.work);
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_codec_dai *codec_dai;
+ struct snd_soc_dai *codec_dai;
int i;
mutex_lock(&pcm_mutex);
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
dbg("pop wq checking: %s status: %s waiting: %s\n",
@@ -287,12 +287,12 @@ static void close_delayed_work(struct work_struct *work)
/* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) {
- /* power down the codec to D1 if no longer active */
+ /* Reduce power if no longer active */
if (codec->active == 0) {
dbg("pop wq D1 %s %s\n", codec->name,
codec_dai->playback.stream_name);
- snd_soc_dapm_device_event(socdev,
- SNDRV_CTL_POWER_D1);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
}
codec_dai->pop_wait = 0;
@@ -300,12 +300,12 @@ static void close_delayed_work(struct work_struct *work)
codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_STOP);
- /* power down the codec power domain if no longer active */
+ /* Fall into standby if no longer active */
if (codec->active == 0) {
dbg("pop wq D3 %s %s\n", codec->name,
codec_dai->playback.stream_name);
- snd_soc_dapm_device_event(socdev,
- SNDRV_CTL_POWER_D3hot);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
}
}
@@ -323,8 +323,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
mutex_lock(&pcm_mutex);
@@ -365,8 +365,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
SND_SOC_DAPM_STREAM_STOP);
if (codec->active == 0 && codec_dai->pop_wait == 0)
- snd_soc_dapm_device_event(socdev,
- SNDRV_CTL_POWER_D3hot);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
mutex_unlock(&pcm_mutex);
@@ -384,8 +384,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
int ret = 0;
@@ -434,14 +434,14 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
else {
codec_dai->pop_wait = 0;
cancel_delayed_work(&socdev->delayed_work);
- if (codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ snd_soc_dai_digital_mute(codec_dai, 0);
}
} else {
/* no delayed work - do we need to power up codec */
- if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
- snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(codec,
@@ -452,9 +452,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0);
- if (codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+ snd_soc_dai_digital_mute(codec_dai, 0);
} else {
/* codec already powered - power on widgets */
@@ -466,8 +465,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- if (codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 0);
+
+ snd_soc_dai_digital_mute(codec_dai, 0);
}
}
@@ -488,8 +487,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
@@ -514,7 +513,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
if (cpu_dai->ops.hw_params) {
ret = cpu_dai->ops.hw_params(substream, params);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't set interface %s hw params\n",
+ printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
goto interface_err;
}
@@ -523,7 +522,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
if (platform->pcm_ops->hw_params) {
ret = platform->pcm_ops->hw_params(substream, params);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't set platform %s hw params\n",
+ printk(KERN_ERR "asoc: platform %s hw params failed\n",
platform->name);
goto platform_err;
}
@@ -542,7 +541,7 @@ interface_err:
codec_dai->ops.hw_free(substream);
codec_err:
- if(machine->ops && machine->ops->hw_free)
+ if (machine->ops && machine->ops->hw_free)
machine->ops->hw_free(substream);
mutex_unlock(&pcm_mutex);
@@ -558,15 +557,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
mutex_lock(&pcm_mutex);
/* apply codec digital mute */
- if (!codec->active && codec_dai->dai_ops.digital_mute)
- codec_dai->dai_ops.digital_mute(codec_dai, 1);
+ if (!codec->active)
+ snd_soc_dai_digital_mute(codec_dai, 1);
/* free any machine hw params */
if (machine->ops && machine->ops->hw_free)
@@ -593,8 +592,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
- struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret;
if (codec_dai->ops.trigger) {
@@ -631,16 +630,26 @@ static struct snd_pcm_ops soc_pcm_ops = {
/* powers down audio subsystem for suspend */
static int soc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
int i;
+ /* Due to the resume being scheduled into a workqueue we could
+ * suspend before that's finished - wait for it to complete.
+ */
+ snd_power_lock(codec->card);
+ snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
+ snd_power_unlock(codec->card);
+
+ /* we're going to block userspace touching us until resume completes */
+ snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
+
/* mute any active DAC's */
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
if (dai->dai_ops.digital_mute && dai->playback.active)
dai->dai_ops.digital_mute(dai, 1);
}
@@ -652,8 +661,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
if (machine->suspend_pre)
machine->suspend_pre(pdev, state);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
cpu_dai->suspend(pdev, cpu_dai);
if (platform->suspend)
@@ -662,9 +671,9 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
/* close any waiting streams and save state */
run_delayed_work(&socdev->delayed_work);
- codec->suspend_dapm_state = codec->dapm_state;
+ codec->suspend_bias_level = codec->bias_level;
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
char *stream = codec->dai[i].playback.stream_name;
if (stream != NULL)
snd_soc_dapm_stream_event(codec, stream,
@@ -678,8 +687,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
if (codec_dev->suspend)
codec_dev->suspend(pdev, state);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
cpu_dai->suspend(pdev, cpu_dai);
}
@@ -690,21 +699,32 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+/* deferred resume work, so resume can complete before we finished
+ * setting our codec back up, which can be very slow on I2C
+ */
+static void soc_resume_deferred(struct work_struct *work)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct snd_soc_device *socdev = container_of(work,
+ struct snd_soc_device,
+ deferred_resume_work);
+ struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
+ struct platform_device *pdev = to_platform_device(socdev->dev);
int i;
+ /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+ * so userspace apps are blocked from touching us
+ */
+
+ dev_info(socdev->dev, "starting resume work\n");
+
if (machine->resume_pre)
machine->resume_pre(pdev);
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
cpu_dai->resume(pdev, cpu_dai);
}
@@ -712,8 +732,8 @@ static int soc_resume(struct platform_device *pdev)
if (codec_dev->resume)
codec_dev->resume(pdev);
- for(i = 0; i < codec->num_dai; i++) {
- char* stream = codec->dai[i].playback.stream_name;
+ for (i = 0; i < codec->num_dai; i++) {
+ char *stream = codec->dai[i].playback.stream_name;
if (stream != NULL)
snd_soc_dapm_stream_event(codec, stream,
SND_SOC_DAPM_STREAM_RESUME);
@@ -723,15 +743,15 @@ static int soc_resume(struct platform_device *pdev)
SND_SOC_DAPM_STREAM_RESUME);
}
- /* unmute any active DAC's */
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+ /* unmute any active DACs */
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
if (dai->dai_ops.digital_mute && dai->playback.active)
dai->dai_ops.digital_mute(dai, 0);
}
- for(i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < machine->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
cpu_dai->resume(pdev, cpu_dai);
if (platform->resume)
@@ -741,6 +761,22 @@ static int soc_resume(struct platform_device *pdev)
if (machine->resume_post)
machine->resume_post(pdev);
+ dev_info(socdev->dev, "resume work completed\n");
+
+ /* userspace can access us now we are back as we were before */
+ snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ dev_info(socdev->dev, "scheduling resume work\n");
+
+ if (!schedule_work(&socdev->deferred_resume_work))
+ dev_err(socdev->dev, "work item may be lost\n");
+
return 0;
}
@@ -760,33 +796,38 @@ static int soc_probe(struct platform_device *pdev)
if (machine->probe) {
ret = machine->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
return ret;
}
for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->probe) {
- ret = cpu_dai->probe(pdev);
- if(ret < 0)
+ ret = cpu_dai->probe(pdev, cpu_dai);
+ if (ret < 0)
goto cpu_dai_err;
}
}
if (codec_dev->probe) {
ret = codec_dev->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
goto cpu_dai_err;
}
if (platform->probe) {
ret = platform->probe(pdev);
- if(ret < 0)
+ if (ret < 0)
goto platform_err;
}
/* DAPM stream work */
INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+#ifdef CONFIG_PM
+ /* deferred resume work */
+ INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+#endif
+
return 0;
platform_err:
@@ -795,9 +836,9 @@ platform_err:
cpu_dai_err:
for (i--; i >= 0; i--) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->remove)
- cpu_dai->remove(pdev);
+ cpu_dai->remove(pdev, cpu_dai);
}
if (machine->remove)
@@ -824,9 +865,9 @@ static int soc_remove(struct platform_device *pdev)
codec_dev->remove(pdev);
for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
if (cpu_dai->remove)
- cpu_dai->remove(pdev);
+ cpu_dai->remove(pdev, cpu_dai);
}
if (machine->remove)
@@ -852,8 +893,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)
{
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = dai_link->codec_dai;
+ struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
struct snd_soc_pcm_runtime *rtd;
struct snd_pcm *pcm;
char new_name[64];
@@ -868,7 +909,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
codec_dai->codec = socdev->codec;
/* check client and interface hw capabilities */
- sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
+ sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
get_dai_name(cpu_dai->type), num);
if (codec_dai->playback.channels_min)
@@ -879,7 +920,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
capture, &pcm);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+ printk(KERN_ERR "asoc: can't create pcm for codec %s\n",
+ codec->name);
kfree(rtd);
return ret;
}
@@ -928,8 +970,9 @@ static ssize_t codec_reg_show(struct device *dev,
step = codec->reg_cache_step;
count += sprintf(buf, "%s registers\n", codec->name);
- for(i = 0; i < codec->reg_cache_size; i += step)
- count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i));
+ for (i = 0; i < codec->reg_cache_size; i += step)
+ count += sprintf(buf + count, "%2x: %4x\n", i,
+ codec->read(codec, i));
return count;
}
@@ -1072,7 +1115,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
/* create the pcms */
- for(i = 0; i < machine->num_links; i++) {
+ for (i = 0; i < machine->num_links; i++) {
ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm %s\n",
@@ -1102,7 +1145,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
struct snd_soc_machine *machine = socdev->machine;
int ret = 0, i, ac97 = 0, err = 0;
- for(i = 0; i < machine->num_links; i++) {
+ for (i = 0; i < machine->num_links; i++) {
if (socdev->machine->dai_link[i].init) {
err = socdev->machine->dai_link[i].init(codec);
if (err < 0) {
@@ -1111,7 +1154,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
continue;
}
}
- if (socdev->machine->dai_link[i].codec_dai->type ==
+ if (socdev->machine->dai_link[i].codec_dai->type ==
SND_SOC_DAI_AC97_BUS)
ac97 = 1;
}
@@ -1122,7 +1165,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
ret = snd_card_register(codec->card);
if (ret < 0) {
- printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
+ printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
codec->name);
goto out;
}
@@ -1146,7 +1189,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
err = device_create_file(socdev->dev, &dev_attr_codec_reg);
if (err < 0)
- printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+ printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
mutex_unlock(&codec->mutex);
@@ -1166,13 +1209,13 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
#ifdef CONFIG_SND_SOC_AC97_BUS
- struct snd_soc_codec_dai *codec_dai;
+ struct snd_soc_dai *codec_dai;
int i;
#endif
mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
- for(i = 0; i < codec->num_dai; i++) {
+ for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
soc_ac97_dev_unregister(codec);
@@ -1282,7 +1325,8 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
;
val = snd_soc_read(codec, e->reg);
- ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+ ucontrol->value.enumerated.item[0]
+ = (val >> e->shift_l) & (bitmask - 1);
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
(val >> e->shift_r) & (bitmask - 1);
@@ -1576,7 +1620,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
return err;
err = snd_soc_update_bits(codec, reg2, val_mask, val2);
@@ -1584,6 +1629,204 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
+/**
+ * snd_soc_info_volsw_s8 - signed mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int max = (signed char)((kcontrol->private_value >> 16) & 0xff);
+ int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max-min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
+
+/**
+ * snd_soc_get_volsw_s8 - signed mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+ int val = snd_soc_read(codec, reg);
+
+ ucontrol->value.integer.value[0] =
+ ((signed char)(val & 0xff))-min;
+ ucontrol->value.integer.value[1] =
+ ((signed char)((val >> 8) & 0xff))-min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
+
+/**
+ * snd_soc_put_volsw_sgn - signed mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+ unsigned short val;
+
+ val = (ucontrol->value.integer.value[0]+min) & 0xff;
+ val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
+
+ return snd_soc_update_bits(codec, reg, 0xffff, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
+
+/**
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
+
+/**
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @clk_id: DAI specific clock divider ID
+ * @div: new clock divisor.
+ *
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
+ */
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+ int div_id, int div)
+{
+ if (dai->dai_ops.set_clkdiv)
+ return dai->dai_ops.set_clkdiv(dai, div_id, div);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
+
+/**
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ if (dai->dai_ops.set_pll)
+ return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+
+/**
+ * snd_soc_dai_set_fmt - configure DAI hardware audio format.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @fmt: SND_SOC_DAIFMT_ format value.
+ *
+ * Configures the DAI hardware format and clocking.
+ */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ if (dai->dai_ops.set_fmt)
+ return dai->dai_ops.set_fmt(dai, fmt);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
+
+/**
+ * snd_soc_dai_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @mask: DAI specific mask representing used slots.
+ * @slots: Number of slots in use.
+ *
+ * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
+ * specific.
+ */
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int mask, int slots)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
+
+/**
+ * snd_soc_dai_set_tristate - configure DAI system or master clock.
+ * @dai: DAI
+ * @tristate: tristate enable
+ *
+ * Tristates the DAI so that others can use it.
+ */
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ if (dai->dai_ops.set_sysclk)
+ return dai->dai_ops.set_tristate(dai, tristate);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
+
+/**
+ * snd_soc_dai_digital_mute - configure DAI system or master clock.
+ * @dai: DAI
+ * @mute: mute enable
+ *
+ * Mutes the DAI DAC.
+ */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ if (dai->dai_ops.digital_mute)
+ return dai->dai_ops.digital_mute(dai, mute);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
+
static int __devinit snd_soc_init(void)
{
printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
@@ -1592,7 +1835,7 @@ static int __devinit snd_soc_init(void)
static void snd_soc_exit(void)
{
- platform_driver_unregister(&soc_driver);
+ platform_driver_unregister(&soc_driver);
}
module_init(snd_soc_init);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index af3326c6350..2c87061c2a6 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -10,11 +10,6 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 12th Aug 2005 Initial version.
- * 25th Oct 2005 Implemented path power domain.
- * 18th Dec 2005 Implemented machine and stream level power domain.
- *
* Features:
* o Changes power status of internal codec blocks depending on the
* dynamic configuration of codec internal audio paths and active
@@ -50,23 +45,10 @@
#include <sound/initval.h>
/* debug */
-#define DAPM_DEBUG 0
-#if DAPM_DEBUG
+#ifdef DEBUG
#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
-#define dbg(format, arg...) printk(format, ## arg)
#else
#define dump_dapm(codec, action)
-#define dbg(format, arg...)
-#endif
-
-#define POP_DEBUG 0
-#if POP_DEBUG
-#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
-#define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
-#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
-#else
-#define pop_dbg(format, arg...)
-#define pop_wait(time)
#endif
/* dapm power sequences - make this per codec in the future */
@@ -85,6 +67,28 @@ static int dapm_status = 1;
module_param(dapm_status, int, 0);
MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
+static unsigned int pop_time;
+
+static void pop_wait(void)
+{
+ if (pop_time)
+ schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
+}
+
+static void pop_dbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (pop_time) {
+ vprintk(fmt, args);
+ pop_wait();
+ }
+
+ va_end(args);
+}
+
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
@@ -222,11 +226,12 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
change = old != new;
if (change) {
pop_dbg("pop test %s : %s in %d ms\n", widget->name,
- widget->power ? "on" : "off", POP_TIME);
+ widget->power ? "on" : "off", pop_time);
snd_soc_write(codec, widget->reg, new);
- pop_wait(POP_TIME);
+ pop_wait();
}
- dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
+ pr_debug("reg %x old %x new %x change %d\n", widget->reg,
+ old, new, change);
return change;
}
@@ -448,6 +453,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
}
/*
+ * Handler for generic register modifier widget.
+ */
+int dapm_reg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ val = w->on_val;
+ else
+ val = w->off_val;
+
+ snd_soc_update_bits(w->codec, -(w->reg + 1),
+ w->mask << w->shift, val << w->shift);
+
+ return 0;
+}
+
+/*
* Scan each dapm widget for complete audio path.
* A complete path is a route that has valid endpoints i.e.:-
*
@@ -565,8 +589,8 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
/* call any power change event handlers */
if (power_change) {
if (w->event) {
- dbg("power %s event for %s flags %x\n",
- w->power ? "on" : "off", w->name, w->event_flags);
+ pr_debug("power %s event for %s flags %x\n",
+ w->power ? "on" : "off", w->name, w->event_flags);
if (power) {
/* power up event */
if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
@@ -608,7 +632,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
return ret;
}
-#if DAPM_DEBUG
+#ifdef DEBUG
static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
{
struct snd_soc_dapm_widget *w;
@@ -693,8 +717,10 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
path->connect = 0; /* old connection must be powered down */
}
- if (found)
+ if (found) {
dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(widget->codec, "mux power update");
+ }
return 0;
}
@@ -730,8 +756,10 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
break;
}
- if (found)
+ if (found) {
dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(widget->codec, "mixer power update");
+ }
return 0;
}
@@ -768,21 +796,18 @@ static ssize_t dapm_widget_show(struct device *dev,
}
}
- switch(codec->dapm_state){
- case SNDRV_CTL_POWER_D0:
- state = "D0";
+ switch (codec->bias_level) {
+ case SND_SOC_BIAS_ON:
+ state = "On";
break;
- case SNDRV_CTL_POWER_D1:
- state = "D1";
+ case SND_SOC_BIAS_PREPARE:
+ state = "Prepare";
break;
- case SNDRV_CTL_POWER_D2:
- state = "D2";
+ case SND_SOC_BIAS_STANDBY:
+ state = "Standby";
break;
- case SNDRV_CTL_POWER_D3hot:
- state = "D3hot";
- break;
- case SNDRV_CTL_POWER_D3cold:
- state = "D3cold";
+ case SND_SOC_BIAS_OFF:
+ state = "Off";
break;
}
count += sprintf(buf + count, "PM State: %s\n", state);
@@ -792,20 +817,51 @@ static ssize_t dapm_widget_show(struct device *dev,
static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
+/* pop/click delay times */
+static ssize_t dapm_pop_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", pop_time);
+}
+
+static ssize_t dapm_pop_time_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+
+{
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) >= 0)
+ pop_time = val;
+ else
+ printk(KERN_ERR "Unable to parse pop_time setting\n");
+
+ return count;
+}
+
+static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show,
+ dapm_pop_time_store);
+
int snd_soc_dapm_sys_add(struct device *dev)
{
int ret = 0;
- if (dapm_status)
+ if (dapm_status) {
ret = device_create_file(dev, &dev_attr_dapm_widget);
+ if (ret == 0)
+ ret = device_create_file(dev, &dev_attr_dapm_pop_time);
+ }
+
return ret;
}
static void snd_soc_dapm_sys_remove(struct device *dev)
{
- if (dapm_status)
+ if (dapm_status) {
+ device_remove_file(dev, &dev_attr_dapm_pop_time);
device_remove_file(dev, &dev_attr_dapm_widget);
+ }
}
/* free all dapm widgets and resources */
@@ -826,8 +882,25 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
}
}
+static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
+ char *pin, int status)
+{
+ struct snd_soc_dapm_widget *w;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (!strcmp(w->name, pin)) {
+ pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+ w->connected = status;
+ return 0;
+ }
+ }
+
+ pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+ return -EINVAL;
+}
+
/**
- * snd_soc_dapm_sync_endpoints - scan and power dapm paths
+ * snd_soc_dapm_sync - scan and power dapm paths
* @codec: audio codec
*
* Walks all dapm audio paths and powers widgets according to their
@@ -835,27 +908,16 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
*
* Returns 0 for success.
*/
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
+int snd_soc_dapm_sync(struct snd_soc_codec *codec)
{
- return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+ int ret = dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+ dump_dapm(codec, "sync");
+ return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
- const char * control, const char *source)
+static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
+ const char *sink, const char *control, const char *source)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
@@ -957,9 +1019,64 @@ err:
kfree(path);
return ret;
}
+
+/**
+ * snd_soc_dapm_connect_input - connect dapm widgets
+ * @codec: audio codec
+ * @sink: name of target widget
+ * @control: mixer control name
+ * @source: name of source name
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * This function has been deprecated in favour of snd_soc_dapm_add_routes().
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
+ const char *control, const char *source)
+{
+ return snd_soc_dapm_add_route(codec, sink, control, source);
+}
EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
/**
+ * snd_soc_dapm_add_routes - Add routes between DAPM widgets
+ * @codec: codec
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * Returns 0 for success else error. On error all resources can be freed
+ * with a call to snd_soc_card_free().
+ */
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_route *route, int num)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ ret = snd_soc_dapm_add_route(codec, route->sink,
+ route->control, route->source);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to add route %s->%s\n",
+ route->source,
+ route->sink);
+ return ret;
+ }
+ route++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+
+/**
* snd_soc_dapm_new_widgets - add new dapm widgets
* @codec: audio codec
*
@@ -1234,6 +1351,33 @@ int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
/**
+ * snd_soc_dapm_new_controls - create new dapm controls
+ * @codec: audio codec
+ * @widget: widget array
+ * @num: number of widgets
+ *
+ * Creates new DAPM controls based upon the templates.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+ const struct snd_soc_dapm_widget *widget,
+ int num)
+{
+ int i, ret;
+
+ for (i = 0; i < num; i++) {
+ ret = snd_soc_dapm_new_control(codec, widget);
+ if (ret < 0)
+ return ret;
+ widget++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+
+
+/**
* snd_soc_dapm_stream_event - send a stream event to the dapm core
* @codec: audio codec
* @stream: stream name
@@ -1257,8 +1401,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
{
if (!w->sname)
continue;
- dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
- stream, event);
+ pr_debug("widget %s\n %s stream %s event %d\n",
+ w->name, w->sname, stream, event);
if (strstr(w->sname, stream)) {
switch(event) {
case SND_SOC_DAPM_STREAM_START:
@@ -1294,53 +1438,81 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
/**
- * snd_soc_dapm_device_event - send a device event to the dapm core
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
* @socdev: audio device
- * @event: device event
+ * @level: level to configure
*
- * Sends a device event to the dapm core. The core then makes any
- * necessary machine or codec power changes..
+ * Configure the bias (power) levels for the SoC audio device.
*
* Returns 0 for success else error.
*/
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+ enum snd_soc_bias_level level)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_machine *machine = socdev->machine;
+ int ret = 0;
- if (machine->dapm_event)
- machine->dapm_event(machine, event);
- if (codec->dapm_event)
- codec->dapm_event(codec, event);
- return 0;
+ if (machine->set_bias_level)
+ ret = machine->set_bias_level(machine, level);
+ if (ret == 0 && codec->set_bias_level)
+ ret = codec->set_bias_level(codec, level);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
/**
- * snd_soc_dapm_set_endpoint - set audio endpoint status
+ * snd_soc_dapm_enable_pin - enable pin.
+ * @snd_soc_codec: SoC codec
+ * @pin: pin name
+ *
+ * Enables input/output pin and it's parents or children widgets iff there is
+ * a valid audio route and active audio stream.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
+
+/**
+ * snd_soc_dapm_disable_pin - disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Disables input/output pin and it's parents or children widgets.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
+
+/**
+ * snd_soc_dapm_get_pin_status - get audio pin status
* @codec: audio codec
- * @endpoint: audio signal endpoint (or start point)
- * @status: point status
+ * @pin: audio signal pin endpoint (or start point)
*
- * Set audio endpoint status - connected or disconnected.
+ * Get audio pin status - connected or disconnected.
*
- * Returns 0 for success else error.
+ * Returns 1 for connected otherwise 0.
*/
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
- char *endpoint, int status)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
{
struct snd_soc_dapm_widget *w;
list_for_each_entry(w, &codec->dapm_widgets, list) {
- if (!strcmp(w->name, endpoint)) {
- w->connected = status;
- return 0;
- }
+ if (!strcmp(w->name, pin))
+ return w->connected;
}
- return -ENODEV;
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
/**
* snd_soc_dapm_free - free dapm resources