summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/arm/Kconfig15
-rw-r--r--sound/arm/pxa2xx-ac97.c13
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c201
-rw-r--r--sound/arm/pxa2xx-pcm.c12
-rw-r--r--sound/arm/pxa2xx-pcm.h2
-rw-r--r--sound/hda/ext/hdac_ext_bus.c1
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/hda_tegra.c30
-rw-r--r--sound/pci/hda/patch_cirrus.c1
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_realtek.c32
-rw-r--r--sound/pci/hda/patch_sigmatel.c6
-rw-r--r--sound/soc/Kconfig6
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile2
-rw-r--r--sound/soc/atmel/atmel-classd.c679
-rw-r--r--sound/soc/atmel/atmel-classd.h120
-rw-r--r--sound/soc/atmel/atmel_wm8904.c1
-rw-r--r--sound/soc/au1x/db1000.c10
-rw-r--r--sound/soc/au1x/db1200.c14
-rw-r--r--sound/soc/au1x/psc-i2s.c1
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c11
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c12
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1701.c12
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c12
-rw-r--r--sound/soc/codecs/Kconfig23
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ad193x-i2c.c8
-rw-r--r--sound/soc/codecs/ad193x-spi.c16
-rw-r--r--sound/soc/codecs/ad193x.c128
-rw-r--r--sound/soc/codecs/ad193x.h9
-rw-r--r--sound/soc/codecs/ak4613.c497
-rw-r--r--sound/soc/codecs/ak4642.c153
-rw-r--r--sound/soc/codecs/arizona.c49
-rw-r--r--sound/soc/codecs/arizona.h14
-rw-r--r--sound/soc/codecs/da7213.c190
-rw-r--r--sound/soc/codecs/da7213.h8
-rw-r--r--sound/soc/codecs/da7219-aad.c823
-rw-r--r--sound/soc/codecs/da7219-aad.h212
-rw-r--r--sound/soc/codecs/da7219.c1955
-rw-r--r--sound/soc/codecs/da7219.h820
-rw-r--r--sound/soc/codecs/es8328.c2
-rw-r--r--sound/soc/codecs/hdmi.c109
-rw-r--r--sound/soc/codecs/nau8825.c1309
-rw-r--r--sound/soc/codecs/nau8825.h341
-rw-r--r--sound/soc/codecs/rt286.c8
-rw-r--r--sound/soc/codecs/rt298.c26
-rw-r--r--sound/soc/codecs/rt5645.c200
-rw-r--r--sound/soc/codecs/rt5645.h46
-rw-r--r--sound/soc/codecs/sgtl5000.c6
-rw-r--r--sound/soc/codecs/tas2552.c2
-rw-r--r--sound/soc/codecs/tlv320aic3x.c49
-rw-r--r--sound/soc/codecs/wm0010.c23
-rw-r--r--sound/soc/codecs/wm5110.c187
-rw-r--r--sound/soc/codecs/wm8960.c26
-rw-r--r--sound/soc/codecs/wm8962.c10
-rw-r--r--sound/soc/codecs/wm8998.c1430
-rw-r--r--sound/soc/codecs/wm8998.h23
-rw-r--r--sound/soc/davinci/davinci-mcasp.c319
-rw-r--r--sound/soc/dwc/designware_i2s.c142
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c144
-rw-r--r--sound/soc/fsl/fsl_esai.c88
-rw-r--r--sound/soc/fsl/fsl_sai.c38
-rw-r--r--sound/soc/fsl/fsl_spdif.c73
-rw-r--r--sound/soc/fsl/fsl_ssi.c107
-rw-r--r--sound/soc/fsl/imx-spdif.c1
-rw-r--r--sound/soc/fsl/imx-ssi.c19
-rw-r--r--sound/soc/generic/simple-card.c8
-rw-r--r--sound/soc/intel/Kconfig15
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c19
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/broadwell.c9
-rw-r--r--sound/soc/intel/boards/skl_rt286.c259
-rw-r--r--sound/soc/intel/common/Makefile6
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h1
-rw-r--r--sound/soc/intel/common/sst-dsp.c2
-rw-r--r--sound/soc/intel/common/sst-dsp.h2
-rw-r--r--sound/soc/intel/common/sst-firmware.c10
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c20
-rw-r--r--sound/soc/intel/skylake/Makefile3
-rw-r--r--sound/soc/intel/skylake/skl-messages.c44
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c10
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c176
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c7
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c12
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h1
-rw-r--r--sound/soc/intel/skylake/skl-sst.c56
-rw-r--r--sound/soc/intel/skylake/skl-topology.c1252
-rw-r--r--sound/soc/intel/skylake/skl-topology.h36
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h83
-rw-r--r--sound/soc/intel/skylake/skl.c32
-rw-r--r--sound/soc/intel/skylake/skl.h17
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c1
-rw-r--r--sound/soc/kirkwood/armada-370-db.c1
-rw-r--r--sound/soc/mediatek/mt8173-max98090.c11
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5676.c11
-rw-r--r--sound/soc/mediatek/mtk-afe-pcm.c17
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c6
-rw-r--r--sound/soc/omap/rx51.c2
-rw-r--r--sound/soc/pxa/Kconfig2
-rw-r--r--sound/soc/pxa/brownstone.c9
-rw-r--r--sound/soc/pxa/corgi.c11
-rw-r--r--sound/soc/pxa/e740_wm9705.c5
-rw-r--r--sound/soc/pxa/e750_wm9705.c5
-rw-r--r--sound/soc/pxa/e800_wm9712.c5
-rw-r--r--sound/soc/pxa/hx4700.c4
-rw-r--r--sound/soc/pxa/imote2.c11
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c11
-rw-r--r--sound/soc/pxa/palm27x.c9
-rw-r--r--sound/soc/pxa/poodle.c11
-rw-r--r--sound/soc/pxa/pxa-ssp.c1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c49
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c3
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c33
-rw-r--r--sound/soc/pxa/spitz.c5
-rw-r--r--sound/soc/pxa/tosa.c5
-rw-r--r--sound/soc/pxa/ttc-dkb.c12
-rw-r--r--sound/soc/qcom/lpass-cpu.c3
-rw-r--r--sound/soc/rockchip/Kconfig12
-rw-r--r--sound/soc/rockchip/Makefile6
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c48
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h16
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c405
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h63
-rw-r--r--sound/soc/sh/Kconfig1
-rw-r--r--sound/soc/sh/rcar/adg.c306
-rw-r--r--sound/soc/sh/rcar/core.c66
-rw-r--r--sound/soc/sh/rcar/ctu.c16
-rw-r--r--sound/soc/sh/rcar/dma.c2
-rw-r--r--sound/soc/sh/rcar/dvc.c16
-rw-r--r--sound/soc/sh/rcar/gen.c14
-rw-r--r--sound/soc/sh/rcar/mix.c16
-rw-r--r--sound/soc/sh/rcar/rcar_snd.h117
-rw-r--r--sound/soc/sh/rcar/rsnd.h29
-rw-r--r--sound/soc/sh/rcar/src.c27
-rw-r--r--sound/soc/sh/rcar/ssi.c113
-rw-r--r--sound/soc/sh/siu_dai.c85
-rw-r--r--sound/soc/soc-compress.c12
-rw-r--r--sound/soc/soc-core.c29
-rw-r--r--sound/soc/soc-dapm.c61
-rw-r--r--sound/soc/soc-ops.c36
-rw-r--r--sound/soc/soc-pcm.c49
-rw-r--r--sound/soc/soc-utils.c9
-rw-r--r--sound/soc/spear/Kconfig2
-rw-r--r--sound/soc/sti/uniperif_player.c14
-rw-r--r--sound/soc/sti/uniperif_reader.c6
-rw-r--r--sound/soc/sunxi/Kconfig11
-rw-r--r--sound/soc/sunxi/Makefile2
-rw-r--r--sound/soc/sunxi/sun4i-codec.c719
-rw-r--r--sound/soc/ux500/mop500.c1
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c1
-rw-r--r--sound/synth/emux/emux_oss.c3
153 files changed, 14049 insertions, 1443 deletions
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 885683a3b0bd..e0406211716b 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -9,6 +9,14 @@ menuconfig SND_ARM
Drivers that are implemented on ASoC can be found in
"ALSA for SoC audio support" section.
+config SND_PXA2XX_LIB
+ tristate
+ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+ select SND_DMAENGINE_PCM
+
+config SND_PXA2XX_LIB_AC97
+ bool
+
if SND_ARM
config SND_ARMAACI
@@ -21,13 +29,6 @@ config SND_PXA2XX_PCM
tristate
select SND_PCM
-config SND_PXA2XX_LIB
- tristate
- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
-
-config SND_PXA2XX_LIB_AC97
- bool
-
config SND_PXA2XX_AC97
tristate "AC97 driver for the Intel PXA2xx chip"
depends on ARCH_PXA
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 38590b322c54..fbd5dad0c484 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_reset,
};
-static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.filter_data = &pxa2xx_ac97_pcm_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 01f8fdc42b1b..e9b98af6b52c 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -15,8 +16,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
-#include <mach/dma.h>
-
#include "pxa2xx-pcm.h"
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.period_bytes_min = 32,
.period_bytes_max = 8192 - 32,
.periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .periods_max = 256,
.buffer_bytes_max = 128 * 1024,
.fifo_size = 32,
};
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
- u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_slave_config config;
+ int ret;
- /* temporary transition hack */
- switch (rtd->params->addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- dcmd |= DCMD_WIDTH1;
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- dcmd |= DCMD_WIDTH2;
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- dcmd |= DCMD_WIDTH4;
- break;
- default:
- /* can't happen */
- break;
- }
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
- switch (rtd->params->maxburst) {
- case 8:
- dcmd |= DCMD_BURST8;
- break;
- case 16:
- dcmd |= DCMD_BURST16;
- break;
- case 32:
- dcmd |= DCMD_BURST32;
- break;
- }
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (ret)
+ return ret;
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
+ snd_dmaengine_pcm_set_config_from_dai_data(substream,
+ snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
+ &config);
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->addr;
- } else {
- dma_desc->dsadr = rtd->params->addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret)
+ return ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- if (rtd && rtd->params && rtd->params->filter_data) {
- unsigned long req = *(unsigned long *) rtd->params->filter_data;
- DRCMR(req) = 0;
- }
-
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
+ return snd_dmaengine_pcm_trigger(substream, cmd);
}
EXPORT_SYMBOL(pxa2xx_pcm_trigger);
snd_pcm_uframes_t
pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
+ return snd_dmaengine_pcm_pointer(substream);
}
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- unsigned long req;
-
- if (!prtd || !prtd->params)
- return 0;
-
- if (prtd->dma_ch == -1)
- return -EINVAL;
-
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- DCSR(prtd->dma_ch) = 0;
- DCMD(prtd->dma_ch) = 0;
- req = *(unsigned long *) prtd->params->filter_data;
- DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
-
return 0;
}
EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
-void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
- dma_ch, dcsr);
- snd_pcm_stop_xrun(substream);
- }
-}
-EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
-
int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
+ struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!dma_params)
+ return 0;
+
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
-
- ret = -ENOMEM;
- rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
+ return ret;
- rtd->dma_ch = -1;
- runtime->private_data = rtd;
- return 0;
-
- err1:
- kfree(rtd);
- out:
- return ret;
+ return snd_dmaengine_pcm_open_request_chan(substream,
+ pxad_filter_fn,
+ dma_params->filter_data);
}
EXPORT_SYMBOL(__pxa2xx_pcm_open);
int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
-
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
+ return snd_dmaengine_pcm_close_release_chan(substream);
}
EXPORT_SYMBOL(__pxa2xx_pcm_close);
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 83be8e3f095e..83fcfac97739 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
client->playback_params : client->capture_params;
- ret = pxa_request_dma("dma", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- goto err2;
- rtd->dma_ch = ret;
ret = client->startup(substream);
if (!ret)
- goto out;
+ goto err2;
+
+ return 0;
- pxa_free_dma(rtd->dma_ch);
err2:
__pxa2xx_pcm_close(substream);
out:
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
struct pxa2xx_pcm_client *client = substream->private_data;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- pxa_free_dma(rtd->dma_ch);
client->shutdown(substream);
return __pxa2xx_pcm_close(substream);
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index 00330985beec..8fa2b7c9e6b8 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -13,8 +13,6 @@
struct pxa2xx_runtime_data {
int dma_ch;
struct snd_dmaengine_dai_dma_data *params;
- struct pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
};
struct pxa2xx_pcm_client {
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 4449d1a99089..2433f7c81472 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/hdaudio_ext.h>
MODULE_DESCRIPTION("HDA extended core");
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 37f43a1b34ef..a249d5486889 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -3367,10 +3367,8 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
int dev, err;
err = snd_hda_codec_parse_pcms(codec);
- if (err < 0) {
- snd_hda_codec_reset(codec);
+ if (err < 0)
return err;
- }
/* attach a new PCM streams */
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 477742cb70a2..58c0aad37284 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -73,6 +73,7 @@ struct hda_tegra {
struct clk *hda2codec_2x_clk;
struct clk *hda2hdmi_clk;
void __iomem *regs;
+ struct work_struct probe_work;
};
#ifdef CONFIG_PM
@@ -294,7 +295,9 @@ static int hda_tegra_dev_disconnect(struct snd_device *device)
static int hda_tegra_dev_free(struct snd_device *device)
{
struct azx *chip = device->device_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ cancel_work_sync(&hda->probe_work);
if (azx_bus(chip)->chip_init) {
azx_stop_all_streams(chip);
azx_stop_chip(chip);
@@ -426,6 +429,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
/*
* constructor
*/
+
+static void hda_tegra_probe_work(struct work_struct *work);
+
static int hda_tegra_create(struct snd_card *card,
unsigned int driver_caps,
struct hda_tegra *hda)
@@ -452,6 +458,8 @@ static int hda_tegra_create(struct snd_card *card,
chip->single_cmd = false;
chip->snoop = true;
+ INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
+
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
if (err < 0)
return err;
@@ -499,6 +507,21 @@ static int hda_tegra_probe(struct platform_device *pdev)
card->private_data = chip;
dev_set_drvdata(&pdev->dev, card);
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void hda_tegra_probe_work(struct work_struct *work)
+{
+ struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
+ struct azx *chip = &hda->chip;
+ struct platform_device *pdev = to_platform_device(hda->dev);
+ int err;
err = hda_tegra_first_init(chip, pdev);
if (err < 0)
@@ -520,11 +543,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
chip->running = 1;
snd_hda_set_power_save(&chip->bus, power_save * 1000);
- return 0;
-
-out_free:
- snd_card_free(card);
- return err;
+ out_free:
+ return; /* no error return from async probe */
}
static int hda_tegra_remove(struct platform_device *pdev)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 584a0343ab0c..85813de26da8 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -633,6 +633,7 @@ static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ca03c40609fc..2f0ec7c45fc7 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -819,6 +819,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a75b5611d1e4..16b8dcba5c12 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4188,6 +4188,24 @@ static void alc_fixup_disable_aamix(struct hda_codec *codec,
}
}
+/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */
+static void alc_fixup_tpt440_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ codec->power_save_node = 0; /* avoid click noises */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
static void alc_shutup_dell_xps13(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -4562,7 +4580,6 @@ enum {
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_FIXUP_TPT440_DOCK,
- ALC292_FIXUP_TPT440_DOCK2,
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
@@ -5029,17 +5046,7 @@ static const struct hda_fixup alc269_fixups[] = {
},
[ALC292_FIXUP_TPT440_DOCK] = {
.type = HDA_FIXUP_FUNC,
- .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
- .chained = true,
- .chain_id = ALC292_FIXUP_TPT440_DOCK2
- },
- [ALC292_FIXUP_TPT440_DOCK2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x16, 0x21211010 }, /* dock headphone */
- { 0x19, 0x21a11010 }, /* dock mic */
- { }
- },
+ .v.func = alc_fixup_tpt440_dock,
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
@@ -5299,6 +5306,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 9d947aef2c8b..def5cc8dff02 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4520,7 +4520,11 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
return err;
spec = codec->spec;
- codec->power_save_node = 1;
+ /* enable power_save_node only for new 92HD89xx chips, as it causes
+ * click noises on old 92HD73xx chips.
+ */
+ if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670)
+ codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.mixer_nid = 0x1d;
spec->have_spdif_mux = 1;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 225bfda414e9..7ff7d88e46dd 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -9,7 +9,6 @@ menuconfig SND_SOC
select SND_JACK if INPUT=y || INPUT=SND
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- select SND_COMPRESS_OFFLOAD
---help---
If you want ASoC support, you should say Y here and also to the
@@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
bool
select SND_DMAENGINE_PCM
+config SND_SOC_COMPRESS
+ bool
+ select SND_COMPRESS_OFFLOAD
+
config SND_SOC_TOPOLOGY
bool
@@ -58,6 +61,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sti/Kconfig"
+source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 134aca150a50..8eb06db32fa0 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,6 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
snd-soc-core-objs += soc-topology.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 1489cd461aec..2d30464b81ce 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_WM8731
help
Say Y if you want to add support for audio SoC on an
at91sam9x5 based board that is using WM8731 codec.
+
+config SND_ATMEL_SOC_CLASSD
+ tristate "Atmel ASoC driver for boards using CLASSD"
+ depends on ARCH_AT91 || COMPILE_TEST
+ select SND_ATMEL_SOC_DMA
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ CLASSD.
endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index b327e5cc8de3..f6f7db428216 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
+snd-atmel-soc-classd-objs := atmel-classd.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
+obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
new file mode 100644
index 000000000000..8276675730ef
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.c
@@ -0,0 +1,679 @@
+/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.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 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-classd.h"
+
+struct atmel_classd_pdata {
+ bool non_overlap_enable;
+ int non_overlap_time;
+ int pwm_type;
+ const char *card_name;
+};
+
+struct atmel_classd {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct clk *aclk;
+ int irq;
+ const struct atmel_classd_pdata *pdata;
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_classd_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-classd",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
+
+static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_classd_pdata *pdata;
+ const char *pwm_type;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
+ if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+ pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
+ else
+ pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
+
+ ret = of_property_read_u32(np,
+ "atmel,non-overlap-time", &pdata->non_overlap_time);
+ if (ret)
+ pdata->non_overlap_enable = false;
+ else
+ pdata->non_overlap_enable = true;
+
+ ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
+ if (ret)
+ pdata->card_name = "CLASSD";
+
+ return pdata;
+}
+#else
+static inline struct atmel_classd_pdata *
+atmel_classd_dt_init(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
+ | 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)
+
+static const struct snd_pcm_hardware atmel_classd_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = ATMEL_CLASSD_RATES,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
+
+/* cpu dai component */
+static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ regmap_write(dd->regmap, CLASSD_THR, 0x0);
+
+ return clk_prepare_enable(dd->pclk);
+}
+
+static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->pclk);
+}
+
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+ .startup = atmel_classd_cpu_dai_startup,
+ .shutdown = atmel_classd_cpu_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+ .name = "atmel-classd",
+};
+
+/* platform */
+static int
+atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ if (params_physical_width(params) != 16) {
+ dev_err(rtd->platform->dev,
+ "only supports 16-bit audio data\n");
+ return -EINVAL;
+ }
+
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr = dd->phy_base + CLASSD_THR;
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config->dst_maxburst = 1;
+ slave_config->src_maxburst = 1;
+ slave_config->device_fc = false;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_classd_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_classd_platform_configure_dma,
+ .pcm_hardware = &atmel_classd_hw,
+ .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+static const char * const mono_mode_text[] = {
+ "mix", "sat", "left", "right"
+};
+
+static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
+ mono_mode_text);
+
+static const char * const eqcfg_text[] = {
+ "Treble-12dB", "Treble-6dB",
+ "Medium-8dB", "Medium-3dB",
+ "Bass-12dB", "Bass-6dB",
+ "0 dB",
+ "Bass+6dB", "Bass+12dB",
+ "Medium+3dB", "Medium+8dB",
+ "Treble+6dB", "Treble+12dB",
+};
+
+static const unsigned int eqcfg_value[] = {
+ CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
+ CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
+ CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
+ CLASSD_INTPMR_EQCFG_FLAT,
+ CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
+ CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
+ CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
+ CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
+ eqcfg_text, eqcfg_value);
+
+static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
+
+static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
+SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
+ CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
+ 78, 1, classd_digital_tlv),
+
+SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
+ CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
+
+SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
+
+SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
+
+SOC_ENUM("Mono Mode", classd_mono_mode_enum),
+
+SOC_ENUM("EQ", classd_eqcfg_enum),
+};
+
+static const char * const pwm_type[] = {
+ "Single ended", "Differential"
+};
+
+static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+ const struct atmel_classd_pdata *pdata = dd->pdata;
+ u32 mask, val;
+
+ mask = CLASSD_MR_PWMTYP_MASK;
+ val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
+
+ mask |= CLASSD_MR_NON_OVERLAP_MASK;
+ if (pdata->non_overlap_enable) {
+ val |= (CLASSD_MR_NON_OVERLAP_EN
+ << CLASSD_MR_NON_OVERLAP_SHIFT);
+
+ mask |= CLASSD_MR_NOVR_VAL_MASK;
+ switch (pdata->non_overlap_time) {
+ case 5:
+ val |= (CLASSD_MR_NOVR_VAL_5NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 10:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 15:
+ val |= (CLASSD_MR_NOVR_VAL_15NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ case 20:
+ val |= (CLASSD_MR_NOVR_VAL_20NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ break;
+ default:
+ val |= (CLASSD_MR_NOVR_VAL_10NS
+ << CLASSD_MR_NOVR_VAL_SHIFT);
+ dev_warn(codec->dev,
+ "non-overlapping value %d is invalid, the default value 10 is specified\n",
+ pdata->non_overlap_time);
+ break;
+ }
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ dev_info(codec->dev,
+ "PWM modulation type is %s, non-overlapping is %s\n",
+ pwm_type[pdata->pwm_type],
+ pdata->non_overlap_enable?"enabled":"disabled");
+
+ return 0;
+}
+
+static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
+{
+ return dev_get_regmap(dev, NULL);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_classd = {
+ .probe = atmel_classd_codec_probe,
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ .get_regmap = atmel_classd_codec_get_remap,
+};
+
+/* codec dai component */
+static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
+ int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
+
+ if (mute)
+ val = mask;
+ else
+ val = 0;
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
+
+static struct {
+ int rate;
+ int sample_rate;
+ int dsp_clk;
+ unsigned long aclk_rate;
+} const sample_rates[] = {
+ { 8000, CLASSD_INTPMR_FRAME_8K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 16000, CLASSD_INTPMR_FRAME_16K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 32000, CLASSD_INTPMR_FRAME_32K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 48000, CLASSD_INTPMR_FRAME_48K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 96000, CLASSD_INTPMR_FRAME_96K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+ { 22050, CLASSD_INTPMR_FRAME_22K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 44100, CLASSD_INTPMR_FRAME_44K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+ { 88200, CLASSD_INTPMR_FRAME_88K,
+ CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+};
+
+static int
+atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int fs;
+ int i, best, best_val, cur_val, ret;
+ u32 mask, val;
+
+ fs = params_rate(params);
+
+ best = 0;
+ best_val = abs(fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+
+ dev_dbg(codec->dev,
+ "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
+ sample_rates[best].rate, sample_rates[best].aclk_rate);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+
+ ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+ if (ret)
+ return ret;
+
+ mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
+ val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
+ | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
+
+ snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
+
+ ret = clk_prepare_enable(dd->aclk);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(dd->gclk);
+}
+
+static void
+atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->aclk);
+}
+
+static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ snd_soc_update_bits(codec, CLASSD_MR,
+ CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
+ (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
+
+ return 0;
+}
+
+static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 mask, val;
+
+ mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+ | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
+ .digital_mute = atmel_classd_codec_dai_digital_mute,
+ .startup = atmel_classd_codec_dai_startup,
+ .shutdown = atmel_classd_codec_dai_shutdown,
+ .hw_params = atmel_classd_codec_dai_hw_params,
+ .prepare = atmel_classd_codec_dai_prepare,
+ .trigger = atmel_classd_codec_dai_trigger,
+};
+
+#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
+
+static struct snd_soc_dai_driver atmel_classd_codec_dai = {
+ .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = ATMEL_CLASSD_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &atmel_classd_codec_dai_ops,
+};
+
+/* ASoC sound card */
+static int atmel_classd_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+
+ dai_link->name = "CLASSD";
+ dai_link->stream_name = "CLASSD PCM";
+ dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME;
+ dai_link->cpu_dai_name = dev_name(dev);
+ dai_link->codec_name = dev_name(dev);
+ dai_link->platform_name = dev_name(dev);
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->name = dd->pdata->card_name;
+ card->dev = dev;
+
+ return 0;
+};
+
+/* regmap configuration */
+static const struct reg_default atmel_classd_reg_defaults[] = {
+ { CLASSD_INTPMR, 0x00301212 },
+};
+
+#define ATMEL_CLASSD_REG_MAX 0xE4
+static const struct regmap_config atmel_classd_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_CLASSD_REG_MAX,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = atmel_classd_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults),
+};
+
+static int atmel_classd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_classd *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_classd_pdata *pdata;
+ struct snd_soc_card *card;
+ int ret;
+
+ pdata = dev_get_platdata(dev);
+ if (!pdata) {
+ pdata = atmel_classd_dt_init(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->pdata = pdata;
+
+ dd->irq = platform_get_irq(pdev, 0);
+ if (dd->irq < 0) {
+ ret = dd->irq;
+ dev_err(dev, "failed to could not get irq: %d\n", ret);
+ return ret;
+ }
+
+ dd->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dd->pclk)) {
+ ret = PTR_ERR(dd->pclk);
+ dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(dd->gclk)) {
+ ret = PTR_ERR(dd->gclk);
+ dev_err(dev, "failed to get GCK clock: %d\n", ret);
+ return ret;
+ }
+
+ dd->aclk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(dd->aclk)) {
+ ret = PTR_ERR(dd->aclk);
+ dev_err(dev, "failed to get audio clock: %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no memory resource\n");
+ return -ENXIO;
+ }
+
+ io_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(io_base)) {
+ ret = PTR_ERR(io_base);
+ dev_err(dev, "failed to remap register memory: %d\n", ret);
+ return ret;
+ }
+
+ dd->phy_base = res->start;
+
+ dd->regmap = devm_regmap_init_mmio(dev, io_base,
+ &atmel_classd_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ ret = PTR_ERR(dd->regmap);
+ dev_err(dev, "failed to init register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_classd_cpu_dai_component,
+ &atmel_classd_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_classd_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
+ &atmel_classd_codec_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register codec: %d\n", ret);
+ return ret;
+ }
+
+ /* register sound card */
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, dd);
+ platform_set_drvdata(pdev, card);
+
+ ret = atmel_classd_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int atmel_classd_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver atmel_classd_driver = {
+ .driver = {
+ .name = "atmel-classd",
+ .of_match_table = of_match_ptr(atmel_classd_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_classd_probe,
+ .remove = atmel_classd_remove,
+};
+module_platform_driver(atmel_classd_driver);
+
+MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h
new file mode 100644
index 000000000000..73f8fdd1ca83
--- /dev/null
+++ b/sound/soc/atmel/atmel-classd.h
@@ -0,0 +1,120 @@
+#ifndef __ATMEL_CLASSD_H_
+#define __ATMEL_CLASSD_H_
+
+#define CLASSD_CR 0x00000000
+#define CLASSD_CR_RESET 0x1
+
+#define CLASSD_MR 0x00000004
+
+#define CLASSD_MR_LEN_DIS 0x0
+#define CLASSD_MR_LEN_EN 0x1
+#define CLASSD_MR_LEN_MASK (0x1 << 0)
+#define CLASSD_MR_LEN_SHIFT (0)
+
+#define CLASSD_MR_LMUTE_DIS 0x0
+#define CLASSD_MR_LMUTE_EN 0x1
+#define CLASSD_MR_LMUTE_SHIFT (0x1)
+#define CLASSD_MR_LMUTE_MASK (0x1 << 1)
+
+#define CLASSD_MR_REN_DIS 0x0
+#define CLASSD_MR_REN_EN 0x1
+#define CLASSD_MR_REN_MASK (0x1 << 4)
+#define CLASSD_MR_REN_SHIFT (4)
+
+#define CLASSD_MR_RMUTE_DIS 0x0
+#define CLASSD_MR_RMUTE_EN 0x1
+#define CLASSD_MR_RMUTE_SHIFT (0x5)
+#define CLASSD_MR_RMUTE_MASK (0x1 << 5)
+
+#define CLASSD_MR_PWMTYP_SINGLE 0x0
+#define CLASSD_MR_PWMTYP_DIFF 0x1
+#define CLASSD_MR_PWMTYP_MASK (0x1 << 8)
+#define CLASSD_MR_PWMTYP_SHIFT (8)
+
+#define CLASSD_MR_NON_OVERLAP_DIS 0x0
+#define CLASSD_MR_NON_OVERLAP_EN 0x1
+#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16)
+#define CLASSD_MR_NON_OVERLAP_SHIFT (16)
+
+#define CLASSD_MR_NOVR_VAL_5NS 0x0
+#define CLASSD_MR_NOVR_VAL_10NS 0x1
+#define CLASSD_MR_NOVR_VAL_15NS 0x2
+#define CLASSD_MR_NOVR_VAL_20NS 0x3
+#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20)
+#define CLASSD_MR_NOVR_VAL_SHIFT (20)
+
+#define CLASSD_INTPMR 0x00000008
+
+#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0)
+#define CLASSD_INTPMR_ATTL_SHIFT (0)
+#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8)
+#define CLASSD_INTPMR_ATTR_SHIFT (8)
+
+#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0
+#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1
+#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16)
+#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16)
+
+#define CLASSD_INTPMR_DEEMP_DIS 0x0
+#define CLASSD_INTPMR_DEEMP_EN 0x1
+#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18)
+#define CLASSD_INTPMR_DEEMP_SHIFT (18)
+
+#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0
+#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1
+#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19)
+#define CLASSD_INTPMR_SWAP_SHIFT (19)
+
+#define CLASSD_INTPMR_FRAME_8K 0x0
+#define CLASSD_INTPMR_FRAME_16K 0x1
+#define CLASSD_INTPMR_FRAME_32K 0x2
+#define CLASSD_INTPMR_FRAME_48K 0x3
+#define CLASSD_INTPMR_FRAME_96K 0x4
+#define CLASSD_INTPMR_FRAME_22K 0x5
+#define CLASSD_INTPMR_FRAME_44K 0x6
+#define CLASSD_INTPMR_FRAME_88K 0x7
+#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20)
+#define CLASSD_INTPMR_FRAME_SHIFT (20)
+
+#define CLASSD_INTPMR_EQCFG_FLAT 0x0
+#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1
+#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2
+#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3
+#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4
+#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5
+#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6
+#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7
+#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8
+#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9
+#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa
+#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb
+#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc
+#define CLASSD_INTPMR_EQCFG_SHIFT (24)
+
+#define CLASSD_INTPMR_MONO_DIS 0x0
+#define CLASSD_INTPMR_MONO_EN 0x1
+#define CLASSD_INTPMR_MONO_MASK (0x1 << 28)
+#define CLASSD_INTPMR_MONO_SHIFT (28)
+
+#define CLASSD_INTPMR_MONO_MODE_MIX 0x0
+#define CLASSD_INTPMR_MONO_MODE_SAT 0x1
+#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2
+#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3
+#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29)
+#define CLASSD_INTPMR_MONO_MODE_SHIFT (29)
+
+#define CLASSD_INTSR 0x0000000c
+
+#define CLASSD_THR 0x00000010
+
+#define CLASSD_IER 0x00000014
+
+#define CLASSD_IDR 0x00000018
+
+#define CLASSD_IMR 0x0000001c
+
+#define CLASSD_ISR 0x00000020
+
+#define CLASSD_WPMR 0x000000e4
+
+#endif
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index aa354e1c6ff7..1933bcd46cca 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
{ .compatible = "atmel,asoc-wm8904", },
{ }
};
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
#endif
static struct platform_driver atmel_asoc_wm8904_driver = {
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
index 452f404abfd2..e97c32798e98 100644
--- a/sound/soc/au1x/db1000.c
+++ b/sound/soc/au1x/db1000.c
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &db1000_ac97;
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = db1000_audio_probe,
- .remove = db1000_audio_remove,
};
module_platform_driver(db1000_audio_driver);
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index 58c3164802b8..5c73061d912a 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -129,6 +129,8 @@ static struct snd_soc_dai_link db1300_i2s_dai = {
.cpu_dai_name = "au1xpsc_i2s.2",
.platform_name = "au1xpsc-pcm.2",
.codec_name = "wm8731.0-001b",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &db1200_i2s_wm8731_ops,
};
@@ -146,6 +148,8 @@ static struct snd_soc_dai_link db1550_i2s_dai = {
.cpu_dai_name = "au1xpsc_i2s.3",
.platform_name = "au1xpsc-pcm.3",
.codec_name = "wm8731.0-001b",
+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
.ops = &db1200_i2s_wm8731_ops,
};
@@ -174,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
card = db1200_cards[pid->driver_data];
card->dev = &pdev->dev;
- return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- snd_soc_unregister_card(card);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver db1200_audio_driver = {
@@ -191,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
},
.id_table = db1200_pids,
.probe = db1200_audio_probe,
- .remove = db1200_audio_remove,
};
module_platform_driver(db1200_audio_driver);
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 38e853add96e..0bf9d62b91a0 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -296,7 +296,6 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
struct resource *iores, *dmares;
unsigned long sel;
- int ret;
struct au1xpsc_audio_data *wd;
wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data),
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index 5bf1501e5e3c..864df2616e10 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
-static int bf5xx_ad1836_driver_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver bf5xx_ad1836_driver = {
.driver = {
.name = "bfin-snd-ad1836",
.pm = &snd_soc_pm_ops,
},
.probe = bf5xx_ad1836_driver_probe,
- .remove = bf5xx_ad1836_driver_remove,
};
module_platform_driver(bf5xx_ad1836_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
index 523baf5820d7..72ac78988426 100644
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1373);
-}
-
-static int bfin_eval_adau1373_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
}
static struct platform_driver bfin_eval_adau1373_driver = {
@@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1373_probe,
- .remove = bfin_eval_adau1373_remove,
};
module_platform_driver(bfin_eval_adau1373_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c
index f9e926dfd4ef..5c67f72cf9a9 100644
--- a/sound/soc/blackfin/bfin-eval-adau1701.c
+++ b/sound/soc/blackfin/bfin-eval-adau1701.c
@@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adau1701);
-}
-
-static int bfin_eval_adau1701_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
}
static struct platform_driver bfin_eval_adau1701_driver = {
@@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1701_probe,
- .remove = bfin_eval_adau1701_remove,
};
module_platform_driver(bfin_eval_adau1701_driver);
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
index 27eee66afdb2..1037477d10b2 100644
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ b/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- return snd_soc_register_card(&bfin_eval_adav80x);
-}
-
-static int bfin_eval_adav80x_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
}
static const struct platform_device_id bfin_eval_adav80x_ids[] = {
@@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adav80x_probe,
- .remove = bfin_eval_adav80x_remove,
.id_table = bfin_eval_adav80x_ids,
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0c9733ecd17f..cfdafc4c11ea 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4554
+ select SND_SOC_AK4613 if I2C
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
@@ -57,6 +58,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
+ select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DMIC
@@ -79,7 +81,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
- select SND_SOC_HDMI_CODEC
+ select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
@@ -171,6 +173,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8996 if I2C
select SND_SOC_WM8997 if MFD_WM8997
+ select SND_SOC_WM8998 if MFD_WM8998
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -195,9 +198,11 @@ config SND_SOC_ARIZONA
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM8997=y
+ default y if SND_SOC_WM8998=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
+ default m if SND_SOC_WM8998=m
config SND_SOC_WM_HUBS
tristate
@@ -319,6 +324,10 @@ config SND_SOC_AK4535
config SND_SOC_AK4554
tristate "AKM AK4554 CODEC"
+config SND_SOC_AK4613
+ tristate "AKM AK4613 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
@@ -430,6 +439,9 @@ config SND_SOC_DA7210
config SND_SOC_DA7213
tristate
+config SND_SOC_DA7219
+ tristate
+
config SND_SOC_DA732X
tristate
@@ -442,9 +454,6 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC
tristate
-config SND_SOC_HDMI_CODEC
- tristate "HDMI stub CODEC"
-
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -865,6 +874,9 @@ config SND_SOC_WM8996
config SND_SOC_WM8997
tristate
+config SND_SOC_WM8998
+ tristate
+
config SND_SOC_WM9081
tristate
@@ -896,6 +908,9 @@ config SND_SOC_MC13783
config SND_SOC_ML26124
tristate
+config SND_SOC_NAU8825
+ tristate
+
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4a32077954ae..f632fc42f59f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
@@ -49,6 +50,7 @@ snd-soc-cs4349-objs := cs4349.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
+snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-bt-sco-objs := bt-sco.o
@@ -72,7 +74,7 @@ snd-soc-max98925-objs := max98925.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
-snd-soc-hdmi-codec-objs := hdmi.o
+snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
@@ -176,6 +178,7 @@ snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm8997-objs := wm8997.o
+snd-soc-wm8998-objs := wm8998.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
@@ -216,6 +219,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
@@ -241,6 +245,7 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
+obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
@@ -264,7 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
-obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
@@ -364,6 +369,7 @@ obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
+obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index df3a1a415825..171313664bc8 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -15,8 +15,8 @@
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", 0 },
- { "ad1937", 0 },
+ { "ad1936", AD193X },
+ { "ad1937", AD193X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
@@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client,
config.val_bits = 8;
config.reg_bits = 8;
- return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
+ return ad193x_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
index 390cef9b9dc2..431f95da1de1 100644
--- a/sound/soc/codecs/ad193x-spi.c
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -16,6 +16,7 @@
static int ad193x_spi_probe(struct spi_device *spi)
{
+ const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
config = ad193x_regmap_config;
@@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi)
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
- return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+ (enum ad193x_type)id->driver_data);
}
static int ad193x_spi_remove(struct spi_device *spi)
@@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi)
return 0;
}
+static const struct spi_device_id ad193x_spi_id[] = {
+ { "ad193x", AD193X },
+ { "ad1933", AD1933 },
+ { "ad1934", AD1934 },
+ { "ad1938", AD193X },
+ { "ad1939", AD193X },
+ { "adau1328", AD193X },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad193x_spi_id);
+
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
@@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = {
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
+ .id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 17c953595660..3a3f3f2343d7 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -23,6 +23,7 @@
/* codec private data */
struct ad193x_priv {
struct regmap *regmap;
+ enum ad193x_type type;
int sysclk;
};
@@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
- AD193X_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
- AD193X_ADCR2_MUTE, 1, 1),
-
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
AD193X_DACR1_MUTE, 1, 1),
@@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
AD193X_DACR4_MUTE, 1, 1),
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+ AD193X_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+ AD193X_ADCR2_MUTE, 1, 1),
+
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
AD193X_ADC_HIGHPASS_FILTER, 1, 0),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
@@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
{ "DAC Output", NULL, "DAC" },
{ "DAC Output", NULL, "VMID" },
- { "ADC", NULL, "SYSCLK" },
- { "DAC", NULL, "ADC_PWR" },
- { "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", NULL, "DAC Output" },
{ "DAC2OUT", NULL, "DAC Output" },
{ "DAC3OUT", NULL, "DAC Output" },
{ "DAC4OUT", NULL, "DAC Output" },
+ { "SYSCLK", NULL, "PLL_PWR" },
+};
+
+static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
+ { "ADC", NULL, "SYSCLK" },
+ { "ADC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
- { "SYSCLK", NULL, "PLL_PWR" },
};
+static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
+{
+ switch (ad193x->type) {
+ case AD1933:
+ case AD1934:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
/*
* DAI ops entries
*/
@@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_CHAN_MASK,
+ channels << AD193X_ADC_CHAN_SHFT);
return 0;
}
@@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
break;
default:
- return -EINVAL;
+ if (ad193x_has_adc(ad193x))
+ return -EINVAL;
+ break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_SERFMT_MASK, adc_serfmt);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_FMT_MASK, adc_fmt);
+ if (ad193x_has_adc(ad193x)) {
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_SERFMT_MASK, adc_serfmt);
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+ AD193X_ADC_FMT_MASK, adc_fmt);
+ }
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_FMT_MASK, dac_fmt);
@@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
AD193X_DAC_WORD_LEN_MASK,
word_len << AD193X_DAC_WORD_LEN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_WORD_LEN_MASK, word_len);
+ if (ad193x_has_adc(ad193x))
+ regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+ AD193X_ADC_WORD_LEN_MASK, word_len);
return 0;
}
@@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = {
static int ad193x_codec_probe(struct snd_soc_codec *codec)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ int num, ret;
/* default setting for ad193x */
@@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
/* dac in tdm mode */
regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
- /* high-pass filter enable */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* high-pass filter enable */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
+ /* sata delay=1, adc aux mode */
+ regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+ }
+
/* pll input: mclki/xi */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+ /* adc only */
+ if (ad193x_has_adc(ad193x)) {
+ /* add adc controls */
+ num = ARRAY_SIZE(ad193x_adc_snd_controls);
+ ret = snd_soc_add_codec_controls(codec,
+ ad193x_adc_snd_controls,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc widgets */
+ num = ARRAY_SIZE(ad193x_adc_widgets);
+ ret = snd_soc_dapm_new_controls(dapm,
+ ad193x_adc_widgets,
+ num);
+ if (ret)
+ return ret;
+
+ /* add adc routes */
+ num = ARRAY_SIZE(ad193x_adc_audio_paths);
+ ret = snd_soc_dapm_add_routes(dapm,
+ ad193x_adc_audio_paths,
+ num);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -356,18 +418,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
.num_dapm_routes = ARRAY_SIZE(audio_paths),
};
-static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
- .volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
-int ad193x_probe(struct device *dev, struct regmap *regmap)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type)
{
struct ad193x_priv *ad193x;
@@ -379,6 +436,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
ad193x->regmap = regmap;
+ ad193x->type = type;
dev_set_drvdata(dev, ad193x);
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index ab9a998f15be..8b1e65f928d2 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -13,8 +13,15 @@
struct device;
+enum ad193x_type {
+ AD193X,
+ AD1933,
+ AD1934,
+};
+
extern const struct regmap_config ad193x_regmap_config;
-int ad193x_probe(struct device *dev, struct regmap *regmap);
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644
index 000000000000..07a266460ec3
--- /dev/null
+++ b/sound/soc/codecs/ak4613.c
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * 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/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1 0x00 /* Power Management 1 */
+#define PW_MGMT2 0x01 /* Power Management 2 */
+#define PW_MGMT3 0x02 /* Power Management 3 */
+#define CTRL1 0x03 /* Control 1 */
+#define CTRL2 0x04 /* Control 2 */
+#define DEMP1 0x05 /* De-emphasis1 */
+#define DEMP2 0x06 /* De-emphasis2 */
+#define OFD 0x07 /* Overflow Detect */
+#define ZRD 0x08 /* Zero Detect */
+#define ICTRL 0x09 /* Input Control */
+#define OCTRL 0x0a /* Output Control */
+#define LOUT1 0x0b /* LOUT1 Volume Control */
+#define ROUT1 0x0c /* ROUT1 Volume Control */
+#define LOUT2 0x0d /* LOUT2 Volume Control */
+#define ROUT2 0x0e /* ROUT2 Volume Control */
+#define LOUT3 0x0f /* LOUT3 Volume Control */
+#define ROUT3 0x10 /* ROUT3 Volume Control */
+#define LOUT4 0x11 /* LOUT4 Volume Control */
+#define ROUT4 0x12 /* ROUT4 Volume Control */
+#define LOUT5 0x13 /* LOUT5 Volume Control */
+#define ROUT5 0x14 /* ROUT5 Volume Control */
+#define LOUT6 0x15 /* LOUT6 Volume Control */
+#define ROUT6 0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN BIT(0)
+#define PMDAC BIT(1)
+#define PMADC BIT(2)
+#define PMVR BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL 0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL 0x3f
+
+/* CTRL1 */
+#define DIF0 BIT(3)
+#define DIF1 BIT(4)
+#define DIF2 BIT(5)
+#define TDM0 BIT(6)
+#define TDM1 BIT(7)
+#define NO_FMT (0xff)
+#define FMT_MASK (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED (0 << 2)
+#define DFS_DOUBLE_SPEED (1 << 2)
+#define DFS_QUAD_SPEED (2 << 2)
+
+struct ak4613_priv {
+ struct mutex lock;
+
+ unsigned int fmt;
+ u8 fmt_ctrl;
+ int cnt;
+};
+
+struct ak4613_formats {
+ unsigned int width;
+ unsigned int fmt;
+};
+
+struct ak4613_interface {
+ struct ak4613_formats capture;
+ struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+ 0, 0xFF, 1, out_tlv),
+ SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+ 0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+ { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 },
+ { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 },
+ { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 },
+ { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 },
+ { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+ { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+ /* capture */ /* playback */
+ [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) },
+ [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) },
+ [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) },
+ [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) },
+ [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x16,
+ .reg_defaults = ak4613_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+ { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+ { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT4"),
+ SND_SOC_DAPM_OUTPUT("LOUT5"),
+ SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT4"),
+ SND_SOC_DAPM_OUTPUT("ROUT5"),
+ SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+ SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+ SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+ SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+ SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+ {"LOUT1", NULL, "DAC1"},
+ {"LOUT2", NULL, "DAC2"},
+ {"LOUT3", NULL, "DAC3"},
+ {"LOUT4", NULL, "DAC4"},
+ {"LOUT5", NULL, "DAC5"},
+ {"LOUT6", NULL, "DAC6"},
+
+ {"ROUT1", NULL, "DAC1"},
+ {"ROUT2", NULL, "DAC2"},
+ {"ROUT3", NULL, "DAC3"},
+ {"ROUT4", NULL, "DAC4"},
+ {"ROUT5", NULL, "DAC5"},
+ {"ROUT6", NULL, "DAC6"},
+
+ {"DAC1", NULL, "Playback"},
+ {"DAC2", NULL, "Playback"},
+ {"DAC3", NULL, "Playback"},
+ {"DAC4", NULL, "Playback"},
+ {"DAC5", NULL, "Playback"},
+ {"DAC6", NULL, "Playback"},
+
+ {"Capture", NULL, "ADC1"},
+ {"Capture", NULL, "ADC2"},
+
+ {"ADC1", NULL, "LIN1"},
+ {"ADC2", NULL, "LIN2"},
+
+ {"ADC1", NULL, "RIN1"},
+ {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct device *dev = codec->dev;
+
+ mutex_lock(&priv->lock);
+ priv->cnt--;
+ if (priv->cnt < 0) {
+ dev_err(dev, "unexpected counter error\n");
+ priv->cnt = 0;
+ }
+ if (!priv->cnt)
+ priv->fmt_ctrl = NO_FMT;
+ mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (fmt) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_I2S:
+ priv->fmt = fmt;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+ const struct ak4613_formats *fmts;
+ struct device *dev = codec->dev;
+ unsigned int width = params_width(params);
+ unsigned int fmt = priv->fmt;
+ unsigned int rate;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int i, ret;
+ u8 fmt_ctrl, ctrl2;
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ ctrl2 = DFS_NORMAL_SPEED;
+ break;
+ case 88200:
+ case 96000:
+ ctrl2 = DFS_DOUBLE_SPEED;
+ break;
+ case 176400:
+ case 192000:
+ ctrl2 = DFS_QUAD_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME
+ *
+ * It doesn't support TDM at this point
+ */
+ fmt_ctrl = NO_FMT;
+ for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+ fmts = (is_play) ? &ak4613_iface[i].playback :
+ &ak4613_iface[i].capture;
+
+ if (fmts->fmt != fmt)
+ continue;
+
+ if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ if (fmts->width != width)
+ continue;
+ } else {
+ if (fmts->width < width)
+ continue;
+ }
+
+ fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+ break;
+ }
+
+ ret = -EINVAL;
+ if (fmt_ctrl == NO_FMT)
+ goto hw_params_end;
+
+ mutex_lock(&priv->lock);
+ if ((priv->fmt_ctrl == NO_FMT) ||
+ (priv->fmt_ctrl == fmt_ctrl)) {
+ priv->fmt_ctrl = fmt_ctrl;
+ priv->cnt++;
+ ret = 0;
+ }
+ mutex_unlock(&priv->lock);
+
+ if (ret < 0)
+ goto hw_params_end;
+
+ snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+ snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+ if (ret < 0)
+ dev_warn(dev, "unsupported data width/format combination\n");
+
+ return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 mgmt1 = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ mgmt1 |= RSTN;
+ /* fall through */
+ case SND_SOC_BIAS_PREPARE:
+ mgmt1 |= PMADC | PMDAC;
+ /* fall through */
+ case SND_SOC_BIAS_STANDBY:
+ mgmt1 |= PMVR;
+ /* fall through */
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+ .shutdown = ak4613_dai_shutdown,
+ .set_fmt = ak4613_dai_set_fmt,
+ .hw_params = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_64000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+ .name = "ak4613-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4613_PCM_RATE,
+ .formats = AK4613_PCM_FMTBIT,
+ },
+ .ops = &ak4613_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_mark_dirty(regmap);
+ return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+ .resume = ak4613_resume,
+ .set_bias_level = ak4613_set_bias_level,
+ .controls = ak4613_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4613_snd_controls),
+ .dapm_widgets = ak4613_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
+ .dapm_routes = ak4613_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+ const struct regmap_config *regmap_cfg;
+ struct regmap *regmap;
+ struct ak4613_priv *priv;
+
+ regmap_cfg = NULL;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(ak4613_of_match, dev);
+ if (of_id)
+ regmap_cfg = of_id->data;
+ } else {
+ regmap_cfg = (const struct regmap_config *)id->driver_data;
+ }
+
+ if (!regmap_cfg)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->fmt_ctrl = NO_FMT;
+ priv->cnt = 0;
+
+ mutex_init(&priv->lock);
+
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+ &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+ .driver = {
+ .name = "ak4613-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4613_of_match,
+ },
+ .probe = ak4613_i2c_probe,
+ .remove = ak4613_i2c_remove,
+ .id_table = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4a90143d0e90..cda27c22812a 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -23,6 +23,8 @@
* AK4648 is tested.
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -128,11 +130,8 @@
#define I2S (3 << 0)
/* MD_CTL2 */
-#define FS0 (1 << 0)
-#define FS1 (1 << 1)
-#define FS2 (1 << 2)
-#define FS3 (1 << 5)
-#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val) ((val & 0x3) << 6)
/* MD_CTL3 */
#define BST1 (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
struct ak4642_priv {
const struct ak4642_drvdata *drvdata;
+ struct clk *mcko;
};
/*
@@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+ u32 frequency)
+{
+ u32 fs_list[] = {
+ [0] = 8000,
+ [1] = 12000,
+ [2] = 16000,
+ [3] = 24000,
+ [4] = 7350,
+ [5] = 11025,
+ [6] = 14700,
+ [7] = 22050,
+ [10] = 32000,
+ [11] = 48000,
+ [14] = 29400,
+ [15] = 44100,
+ };
+ u32 ps_list[] = {
+ [0] = 256,
+ [1] = 128,
+ [2] = 64,
+ [3] = 32
+ };
+ int ps, fs;
+
+ for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+ for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+ if (frequency == ps_list[ps] * fs_list[fs]) {
+ snd_soc_write(codec, MD_CTL2,
+ PSs(ps) | FSs(fs));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- u8 rate;
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ u32 rate = clk_get_rate(priv->mcko);
- switch (params_rate(params)) {
- case 7350:
- rate = FS2;
- break;
- case 8000:
- rate = 0;
- break;
- case 11025:
- rate = FS2 | FS0;
- break;
- case 12000:
- rate = FS0;
- break;
- case 14700:
- rate = FS2 | FS1;
- break;
- case 16000:
- rate = FS1;
- break;
- case 22050:
- rate = FS2 | FS1 | FS0;
- break;
- case 24000:
- rate = FS1 | FS0;
- break;
- case 29400:
- rate = FS3 | FS2 | FS1;
- break;
- case 32000:
- rate = FS3 | FS1;
- break;
- case 44100:
- rate = FS3 | FS2 | FS1 | FS0;
- break;
- case 48000:
- rate = FS3 | FS1 | FS0;
- break;
- default:
- return -EINVAL;
- }
- snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+ if (!rate)
+ rate = params_rate(params) * 256;
- return 0;
+ return ak4642_set_mcko(codec, rate);
}
static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
return 0;
}
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+ struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (priv->mcko)
+ ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+ return 0;
+}
+
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+ .probe = ak4642_probe,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
@@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
.extended_frequencies = 1,
};
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk *clk;
+ const char *clk_name = np->name;
+ const char *parent_clk_name = NULL;
+ u32 rate;
+
+ if (of_property_read_u32(np, "clock-frequency", &rate))
+ return NULL;
+
+ if (of_property_read_bool(np, "clocks"))
+ parent_clk_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+ (parent_clk_name) ? 0 : CLK_IS_ROOT,
+ rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
static const struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct device_node *np = i2c->dev.of_node;
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
const struct ak4642_drvdata *drvdata = NULL;
struct regmap *regmap;
struct ak4642_priv *priv;
+ struct clk *mcko = NULL;
if (np) {
const struct of_device_id *of_id;
- of_id = of_match_device(ak4642_of_match, &i2c->dev);
+ mcko = ak4642_of_parse_mcko(dev);
+ if (IS_ERR(mcko))
+ mcko = NULL;
+
+ of_id = of_match_device(ak4642_of_match, dev);
if (of_id)
drvdata = of_id->data;
} else {
@@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
}
if (!drvdata) {
- dev_err(&i2c->dev, "Unknown device type\n");
+ dev_err(dev, "Unknown device type\n");
return -EINVAL;
}
- priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->drvdata = drvdata;
+ priv->mcko = mcko;
i2c_set_clientdata(i2c, priv);
@@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(&i2c->dev,
+ return snd_soc_register_codec(dev,
&soc_codec_dev_ak4642, &ak4642_dai, 1);
}
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 8a2221ab3d10..9929efc6b9aa 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
0x4f5, 0x0da);
}
break;
+ default:
+ break;
}
return 0;
@@ -314,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"Tone Generator 2",
"Haptics",
"AEC",
+ "AEC2",
"Mic Mute Mixer",
"Noise Generator",
"IN1L",
@@ -421,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x05,
0x06, /* Haptics */
0x08, /* AEC */
+ 0x09, /* AEC2 */
0x0c, /* Noise mixer */
0x0d, /* Comfort noise */
0x10, /* IN1L */
@@ -525,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values);
const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ "12kHz", "24kHz", "48kHz", "96kHz", "192kHz",
+ "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz",
+ "4kHz", "8kHz", "16kHz", "32kHz",
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_text);
+
+const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13,
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) {
+ if (arizona_sample_rate_val[i] == rate_val)
+ return arizona_sample_rate_text[i];
+ }
+
+ return "Illegal";
+}
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
+
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
};
@@ -689,6 +719,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
ARIZONA_IN_VU, val);
}
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+ unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+ unsigned int val = snd_soc_read(codec, reg);
+
+ return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
@@ -725,6 +764,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
if (reg == 0)
arizona_in_set_vu(codec, 0);
+ break;
+ default:
+ break;
}
return 0;
@@ -806,6 +848,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
break;
}
break;
+ default:
+ break;
}
return 0;
@@ -1868,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
if (fll->arizona->rev < 3 || sync)
return init_ratio;
break;
+ case WM8998:
+ case WM1814:
+ if (sync)
+ return init_ratio;
+ break;
default:
return init_ratio;
}
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index ada0a418ff4b..fea8b8ae8e1a 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -93,12 +93,17 @@ struct arizona_priv {
bool dvfs_cached;
};
-#define ARIZONA_NUM_MIXER_INPUTS 103
+#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+#define ARIZONA_GAINMUX_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
+ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ arizona_mixer_tlv)
+
#define ARIZONA_MIXER_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
@@ -209,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
.num_regs = 1 }) }
#define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const struct soc_enum arizona_isrc_fsl[];
extern const struct soc_enum arizona_isrc_fsh[];
@@ -294,4 +303,7 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
#endif
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index a9c86efb3187..7278f93460c1 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
* option) any later version.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- if ((freq == 32768) ||
- ((freq >= 5000000) && (freq <= 54000000))) {
- da7213->mclk_rate = freq;
- return 0;
- } else {
- dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
- freq);
- return -EINVAL;
- }
+ da7213->mclk_squarer_en = false;
+ break;
+ case DA7213_CLKSRC_MCLK_SQR:
+ da7213->mclk_squarer_en = true;
break;
default:
dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
return -EINVAL;
}
+
+ da7213->clk_src = clk_id;
+
+ if (da7213->mclk) {
+ freq = clk_round_rate(da7213->mclk, freq);
+ ret = clk_set_rate(da7213->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7213->mclk_rate = freq;
+
+ return 0;
}
/* Supported PLL input frequencies are 5MHz - 54MHz. */
@@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = {
static int da7213_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* MCLK */
+ if (da7213->mclk) {
+ ret = clk_prepare_enable(da7213->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1382,15 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
/* Disable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
+
+ /* MCLK */
+ if (da7213->mclk)
+ clk_disable_unprepare(da7213->mclk);
break;
}
return 0;
}
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+ { .compatible = "dlg,da7213", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+
+static enum da7213_micbias_voltage
+ da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1600:
+ return DA7213_MICBIAS_1_6V;
+ case 2200:
+ return DA7213_MICBIAS_2_2V;
+ case 2500:
+ return DA7213_MICBIAS_2_5V;
+ case 3000:
+ return DA7213_MICBIAS_3_0V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level\n");
+ return DA7213_MICBIAS_2_2V;
+ }
+}
+
+static enum da7213_dmic_data_sel
+ da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7213_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC data select type\n");
+ return DA7213_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7213_dmic_samplephase
+ da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC sample phase\n");
+ return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7213_dmic_clk_rate
+ da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7213_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7213_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+ return DA7213_DMIC_CLK_1_5MHZ;
+ }
+}
+
+static struct da7213_platform_data
+ *da7213_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7213_platform_data *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0)
+ pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
+
+ if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0)
+ pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str))
+ pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str);
+ else
+ pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str))
+ pdata->dmic_samplephase =
+ da7213_of_dmic_samplephase(codec, of_str);
+ else
+ pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0)
+ pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32);
+ else
+ pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
+
+ return pdata;
+}
+
+
static int da7213_probe(struct snd_soc_codec *codec)
{
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
- struct da7213_platform_data *pdata = da7213->pdata;
/* Default to using ALC auto offset calibration mode. */
snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
@@ -1450,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, DA7213_LINE_CTRL,
DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7213->pdata = da7213_of_to_pdata(codec);
+ else
+ da7213->pdata = dev_get_platdata(codec->dev);
+
/* Set platform data values */
if (da7213->pdata) {
+ struct da7213_platform_data *pdata = da7213->pdata;
u8 micbias_lvl = 0, dmic_cfg = 0;
/* Set Mic Bias voltages */
@@ -1503,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_DATA_SEL_MASK |
DA7213_DMIC_SAMPLEPHASE_MASK |
DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
+ }
- /* Set MCLK squaring */
- da7213->mclk_squarer_en = pdata->mclk_squaring;
+ /* Check if MCLK provided */
+ da7213->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7213->mclk)) {
+ if (PTR_ERR(da7213->mclk) != -ENOENT)
+ return PTR_ERR(da7213->mclk);
+ else
+ da7213->mclk = NULL;
}
+
return 0;
}
@@ -1537,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da7213_priv *da7213;
- struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
@@ -1545,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
if (!da7213)
return -ENOMEM;
- if (pdata)
- da7213->pdata = pdata;
-
i2c_set_clientdata(i2c, da7213);
da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
@@ -1582,6 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
+ .of_match_table = of_match_ptr(da7213_of_match),
},
.probe = da7213_i2c_probe,
.remove = da7213_remove,
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index 9cb9ddd01282..030fd691b076 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -13,6 +13,7 @@
#ifndef _DA7213_H
#define _DA7213_H
+#include <linux/clk.h>
#include <linux/regmap.h>
#include <sound/da7213.h>
@@ -504,14 +505,17 @@
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
-enum clk_src {
- DA7213_CLKSRC_MCLK
+enum da7213_clk_src {
+ DA7213_CLKSRC_MCLK = 0,
+ DA7213_CLKSRC_MCLK_SQR,
};
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
+ struct clk *mclk;
unsigned int mclk_rate;
+ int clk_src;
bool master;
bool mclk_squarer_en;
bool srm_en;
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
new file mode 100644
index 000000000000..9459593eef13
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.c
@@ -0,0 +1,823 @@
+/*
+ * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * Detection control
+ */
+
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219->aad->jack = jack;
+ da7219->aad->jack_inserted = false;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
+
+ /* Enable/Disable jack detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ (jack ? DA7219_ACCDET_EN_MASK : 0));
+}
+EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
+
+/*
+ * Button/HPTest work
+ */
+
+static void da7219_aad_btn_det_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, btn_det_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ u8 statusa, micbias_ctrl;
+ bool micbias_up = false;
+ int retries = 0;
+
+ /* Drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ /* Make sure mic bias is up */
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ do {
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+ if (statusa & DA7219_MICBIAS_UP_STS_MASK)
+ micbias_up = true;
+ else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
+ msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
+ } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
+
+ if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
+ dev_warn(codec->dev, "Mic bias status check timed out");
+
+ /*
+ * Mic bias pulse required to enable mic, must be done before enabling
+ * button detection to prevent erroneous button readings.
+ */
+ if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
+ /* Pulse higher level voltage */
+ micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
+ snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_LEVEL_MASK,
+ da7219_aad->micbias_pulse_lvl);
+ msleep(da7219_aad->micbias_pulse_time);
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+
+ }
+
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK,
+ da7219_aad->btn_cfg);
+}
+
+static void da7219_aad_hptest_work(struct work_struct *work)
+{
+ struct da7219_aad_priv *da7219_aad =
+ container_of(work, struct da7219_aad_priv, hptest_work);
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u16 tonegen_freq_hptest;
+ u8 accdet_cfg8;
+ int report = 0;
+
+ /* Lock DAPM and any Kcontrols that are affected by this test */
+ snd_soc_dapm_mutex_lock(dapm);
+ mutex_lock(&da7219->lock);
+
+ /* Bypass cache so it saves current settings */
+ regcache_cache_bypass(da7219->regmap, true);
+
+ /* Make sure Tone Generator is disabled */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ /* Enable HPTest block, 1KOhms check */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
+ DA7219_HPTEST_EN_MASK |
+ DA7219_HPTEST_RES_SEL_1KOHMS);
+
+ /* Set gains to 0db */
+ snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+ snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+
+ /* Disable DAC filters, EQs and soft mute */
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+ 0);
+ snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_MASK, 0);
+
+ /* Enable HP left & right paths */
+ snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+ DA7219_CP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
+ DA7219_DAC_L_SRC_TONEGEN |
+ DA7219_DAC_R_SRC_TONEGEN);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
+ DA7219_DAC_L_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
+ DA7219_DAC_R_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK,
+ DA7219_MIXOUT_L_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK,
+ DA7219_MIXOUT_R_MIX_SELECT_MASK);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_OUTFILT_ST_1L_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1L);
+ snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+ DA7219_OUTFILT_ST_1R_SRC_MASK,
+ DA7219_DMIX_ST_SRC_OUTFILT1R);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_MASK,
+ DA7219_MIXOUT_L_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_MASK,
+ DA7219_MIXOUT_R_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_write(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+
+ /* Configure & start Tone Generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+ regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
+ snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
+ DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
+ DA7219_SWG_SEL_SRAMP |
+ DA7219_TONE_GEN_GAIN_MINUS_15DB);
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Grab comparator reading */
+ accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
+ if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
+ report |= SND_JACK_HEADPHONE;
+ else
+ report |= SND_JACK_LINEOUT;
+
+ /* Stop tone generator */
+ snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+ msleep(DA7219_AAD_HPTEST_PERIOD);
+
+ /* Restore original settings from cache */
+ regcache_mark_dirty(da7219->regmap);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
+ DA7219_DROUTING_ST_OUTFILT_1R);
+ regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_R_SELECT);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
+ DA7219_DIG_ROUTING_DAC);
+ regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
+ DA7219_DAC_FILTERS5);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
+ DA7219_DAC_FILTERS1);
+ regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
+ DA7219_TONE_GEN_ON_PER);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+ DA7219_TONE_GEN_FREQ1_U);
+ regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
+ DA7219_TONE_GEN_CFG2);
+
+ regcache_cache_bypass(da7219->regmap, false);
+
+ /* Disable HPTest block */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+ DA7219_HPTEST_EN_MASK, 0);
+
+ /* Drive Headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+ DA7219_HP_L_AMP_OE_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+ DA7219_HP_R_AMP_OE_MASK);
+
+ mutex_unlock(&da7219->lock);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ /*
+ * Only send report if jack hasn't been removed during process,
+ * otherwise it's invalid and we drop it.
+ */
+ if (da7219_aad->jack_inserted)
+ snd_soc_jack_report(da7219_aad->jack, report,
+ SND_JACK_HEADSET | SND_JACK_LINEOUT);
+}
+
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
+{
+ struct da7219_aad_priv *da7219_aad = data;
+ struct snd_soc_codec *codec = da7219_aad->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 events[DA7219_AAD_IRQ_REG_MAX];
+ u8 statusa;
+ int i, report = 0, mask = 0;
+
+ /* Read current IRQ events */
+ regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
+ return IRQ_NONE;
+
+ /* Read status register for jack insertion & type status */
+ statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+
+ /* Clear events */
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+ events, DA7219_AAD_IRQ_REG_MAX);
+
+ dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+ events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
+ statusa);
+
+ if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
+ /* Jack Insertion */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_INSERTED_MASK) {
+ report |= SND_JACK_MECHANICAL;
+ mask |= SND_JACK_MECHANICAL;
+ da7219_aad->jack_inserted = true;
+ }
+
+ /* Jack type detection */
+ if (events[DA7219_AAD_IRQ_REG_A] &
+ DA7219_E_JACK_DETECT_COMPLETE_MASK) {
+ /*
+ * If 4-pole, then enable button detection, else perform
+ * HP impedance test to determine output type to report.
+ *
+ * We schedule work here as the tasks themselves can
+ * take time to complete, and in particular for hptest
+ * we want to be able to check if the jack was removed
+ * during the procedure as this will invalidate the
+ * result. By doing this as work, the IRQ thread can
+ * handle a removal, and we can check at the end of
+ * hptest if we have a valid result or not.
+ */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ report |= SND_JACK_HEADSET;
+ mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
+ schedule_work(&da7219_aad->btn_det_work);
+ } else {
+ schedule_work(&da7219_aad->hptest_work);
+ }
+ }
+
+ /* Button support for 4-pole jack */
+ if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Press */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
+ report |= SND_JACK_BTN_0 >> i;
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+ /* Button Release */
+ if (events[DA7219_AAD_IRQ_REG_B] &
+ (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
+ report &= ~(SND_JACK_BTN_0 >> i);
+ mask |= SND_JACK_BTN_0 >> i;
+ }
+ }
+ }
+ } else {
+ /* Jack removal */
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
+ report = 0;
+ mask |= DA7219_AAD_REPORT_ALL_MASK;
+ da7219_aad->jack_inserted = false;
+
+ /* Un-drive headphones/lineout */
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK, 0);
+
+ /* Ensure button detection disabled */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ /* Disable mic bias */
+ snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ /* Cancel any pending work */
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+ }
+ }
+
+ snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT to pdata conversion
+ */
+
+static enum da7219_aad_micbias_pulse_lvl
+ da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2800:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
+ case 2900:
+ return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias pulse level");
+ return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+ }
+}
+
+static enum da7219_aad_btn_cfg
+ da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 2:
+ return DA7219_AAD_BTN_CFG_2MS;
+ case 5:
+ return DA7219_AAD_BTN_CFG_5MS;
+ case 10:
+ return DA7219_AAD_BTN_CFG_10MS;
+ case 50:
+ return DA7219_AAD_BTN_CFG_50MS;
+ case 100:
+ return DA7219_AAD_BTN_CFG_100MS;
+ case 200:
+ return DA7219_AAD_BTN_CFG_200MS;
+ case 500:
+ return DA7219_AAD_BTN_CFG_500MS;
+ default:
+ dev_warn(codec->dev, "Invalid button config");
+ return DA7219_AAD_BTN_CFG_10MS;
+ }
+}
+
+static enum da7219_aad_mic_det_thr
+ da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 200:
+ return DA7219_AAD_MIC_DET_THR_200_OHMS;
+ case 500:
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ case 750:
+ return DA7219_AAD_MIC_DET_THR_750_OHMS;
+ case 1000:
+ return DA7219_AAD_MIC_DET_THR_1000_OHMS;
+ default:
+ dev_warn(codec->dev, "Invalid mic detect threshold");
+ return DA7219_AAD_MIC_DET_THR_500_OHMS;
+ }
+}
+
+static enum da7219_aad_jack_ins_deb
+ da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7219_AAD_JACK_INS_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_INS_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ case 50:
+ return DA7219_AAD_JACK_INS_DEB_50MS;
+ case 100:
+ return DA7219_AAD_JACK_INS_DEB_100MS;
+ case 200:
+ return DA7219_AAD_JACK_INS_DEB_200MS;
+ case 500:
+ return DA7219_AAD_JACK_INS_DEB_500MS;
+ case 1000:
+ return DA7219_AAD_JACK_INS_DEB_1S;
+ default:
+ dev_warn(codec->dev, "Invalid jack insert debounce");
+ return DA7219_AAD_JACK_INS_DEB_20MS;
+ }
+}
+
+static enum da7219_aad_jack_det_rate
+ da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "32ms_64ms")) {
+ return DA7219_AAD_JACK_DET_RATE_32_64MS;
+ } else if (!strcmp(str, "64ms_128ms")) {
+ return DA7219_AAD_JACK_DET_RATE_64_128MS;
+ } else if (!strcmp(str, "128ms_256ms")) {
+ return DA7219_AAD_JACK_DET_RATE_128_256MS;
+ } else if (!strcmp(str, "256ms_512ms")) {
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ } else {
+ dev_warn(codec->dev, "Invalid jack detect rate");
+ return DA7219_AAD_JACK_DET_RATE_256_512MS;
+ }
+}
+
+static enum da7219_aad_jack_rem_deb
+ da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ case 5:
+ return DA7219_AAD_JACK_REM_DEB_5MS;
+ case 10:
+ return DA7219_AAD_JACK_REM_DEB_10MS;
+ case 20:
+ return DA7219_AAD_JACK_REM_DEB_20MS;
+ default:
+ dev_warn(codec->dev, "Invalid jack removal debounce");
+ return DA7219_AAD_JACK_REM_DEB_1MS;
+ }
+}
+
+static enum da7219_aad_btn_avg
+ da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_BTN_AVG_1;
+ case 2:
+ return DA7219_AAD_BTN_AVG_2;
+ case 4:
+ return DA7219_AAD_BTN_AVG_4;
+ case 8:
+ return DA7219_AAD_BTN_AVG_8;
+ default:
+ dev_warn(codec->dev, "Invalid button average value");
+ return DA7219_AAD_BTN_AVG_2;
+ }
+}
+
+static enum da7219_aad_adc_1bit_rpt
+ da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1:
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ case 2:
+ return DA7219_AAD_ADC_1BIT_RPT_2;
+ case 4:
+ return DA7219_AAD_ADC_1BIT_RPT_4;
+ case 8:
+ return DA7219_AAD_ADC_1BIT_RPT_8;
+ default:
+ dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+ return DA7219_AAD_ADC_1BIT_RPT_1;
+ }
+}
+
+static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct da7219_aad_pdata *aad_pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ if (!aad_np)
+ return NULL;
+
+ aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
+ if (!aad_pdata)
+ goto out;
+
+ aad_pdata->irq = irq_of_parse_and_map(np, 0);
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_lvl =
+ da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+ else
+ aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+
+ if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &of_val32) >= 0)
+ aad_pdata->micbias_pulse_time = of_val32;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+ else
+ aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
+
+ if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+ aad_pdata->mic_det_thr =
+ da7219_aad_of_mic_det_thr(codec, of_val32);
+ else
+ aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+ aad_pdata->jack_ins_deb =
+ da7219_aad_of_jack_ins_deb(codec, of_val32);
+ else
+ aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
+
+ if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+ aad_pdata->jack_det_rate =
+ da7219_aad_of_jack_det_rate(codec, of_str);
+ else
+ aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
+
+ if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+ aad_pdata->jack_rem_deb =
+ da7219_aad_of_jack_rem_deb(codec, of_val32);
+ else
+ aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
+
+ if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->a_d_btn_thr = 0xA;
+
+ if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->d_b_btn_thr = 0x16;
+
+ if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->b_c_btn_thr = 0x21;
+
+ if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) of_val32;
+ else
+ aad_pdata->c_mic_btn_thr = 0x3E;
+
+ if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+ else
+ aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
+
+ if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+ aad_pdata->adc_1bit_rpt =
+ da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+ else
+ aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+
+out:
+ of_node_put(aad_np);
+
+ return aad_pdata;
+}
+
+static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if ((pdata) && (pdata->aad_pdata)) {
+ struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
+ u8 cfg, mask;
+
+ da7219_aad->irq = aad_pdata->irq;
+
+ switch (aad_pdata->micbias_pulse_lvl) {
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
+ case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
+ da7219_aad->micbias_pulse_lvl =
+ (aad_pdata->micbias_pulse_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
+
+ switch (aad_pdata->btn_cfg) {
+ case DA7219_AAD_BTN_CFG_2MS:
+ case DA7219_AAD_BTN_CFG_5MS:
+ case DA7219_AAD_BTN_CFG_10MS:
+ case DA7219_AAD_BTN_CFG_50MS:
+ case DA7219_AAD_BTN_CFG_100MS:
+ case DA7219_AAD_BTN_CFG_200MS:
+ case DA7219_AAD_BTN_CFG_500MS:
+ da7219_aad->btn_cfg = (aad_pdata->btn_cfg <<
+ DA7219_BUTTON_CONFIG_SHIFT);
+ }
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->mic_det_thr) {
+ case DA7219_AAD_MIC_DET_THR_200_OHMS:
+ case DA7219_AAD_MIC_DET_THR_500_OHMS:
+ case DA7219_AAD_MIC_DET_THR_750_OHMS:
+ case DA7219_AAD_MIC_DET_THR_1000_OHMS:
+ cfg |= (aad_pdata->mic_det_thr <<
+ DA7219_MIC_DET_THRESH_SHIFT);
+ mask |= DA7219_MIC_DET_THRESH_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->jack_ins_deb) {
+ case DA7219_AAD_JACK_INS_DEB_5MS:
+ case DA7219_AAD_JACK_INS_DEB_10MS:
+ case DA7219_AAD_JACK_INS_DEB_20MS:
+ case DA7219_AAD_JACK_INS_DEB_50MS:
+ case DA7219_AAD_JACK_INS_DEB_100MS:
+ case DA7219_AAD_JACK_INS_DEB_200MS:
+ case DA7219_AAD_JACK_INS_DEB_500MS:
+ case DA7219_AAD_JACK_INS_DEB_1S:
+ cfg |= (aad_pdata->jack_ins_deb <<
+ DA7219_JACKDET_DEBOUNCE_SHIFT);
+ mask |= DA7219_JACKDET_DEBOUNCE_MASK;
+ }
+ switch (aad_pdata->jack_det_rate) {
+ case DA7219_AAD_JACK_DET_RATE_32_64MS:
+ case DA7219_AAD_JACK_DET_RATE_64_128MS:
+ case DA7219_AAD_JACK_DET_RATE_128_256MS:
+ case DA7219_AAD_JACK_DET_RATE_256_512MS:
+ cfg |= (aad_pdata->jack_det_rate <<
+ DA7219_JACK_DETECT_RATE_SHIFT);
+ mask |= DA7219_JACK_DETECT_RATE_MASK;
+ }
+ switch (aad_pdata->jack_rem_deb) {
+ case DA7219_AAD_JACK_REM_DEB_1MS:
+ case DA7219_AAD_JACK_REM_DEB_5MS:
+ case DA7219_AAD_JACK_REM_DEB_10MS:
+ case DA7219_AAD_JACK_REM_DEB_20MS:
+ cfg |= (aad_pdata->jack_rem_deb <<
+ DA7219_JACKDET_REM_DEB_SHIFT);
+ mask |= DA7219_JACKDET_REM_DEB_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+ aad_pdata->a_d_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+ aad_pdata->d_b_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+ aad_pdata->b_c_btn_thr);
+ snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+ aad_pdata->c_mic_btn_thr);
+
+ cfg = 0;
+ mask = 0;
+ switch (aad_pdata->btn_avg) {
+ case DA7219_AAD_BTN_AVG_1:
+ case DA7219_AAD_BTN_AVG_2:
+ case DA7219_AAD_BTN_AVG_4:
+ case DA7219_AAD_BTN_AVG_8:
+ cfg |= (aad_pdata->btn_avg <<
+ DA7219_BUTTON_AVERAGE_SHIFT);
+ mask |= DA7219_BUTTON_AVERAGE_MASK;
+ }
+ switch (aad_pdata->adc_1bit_rpt) {
+ case DA7219_AAD_ADC_1BIT_RPT_1:
+ case DA7219_AAD_ADC_1BIT_RPT_2:
+ case DA7219_AAD_ADC_1BIT_RPT_4:
+ case DA7219_AAD_ADC_1BIT_RPT_8:
+ cfg |= (aad_pdata->adc_1bit_rpt <<
+ DA7219_ADC_1_BIT_REPEAT_SHIFT);
+ mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
+ }
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+ }
+}
+
+
+/*
+ * Init/Exit
+ */
+
+int da7219_aad_init(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+ int ret;
+
+ da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
+ if (!da7219_aad)
+ return -ENOMEM;
+
+ da7219->aad = da7219_aad;
+ da7219_aad->codec = codec;
+
+ /* Handle any DT/platform data */
+ if ((codec->dev->of_node) && (da7219->pdata))
+ da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+
+ da7219_aad_handle_pdata(codec);
+
+ /* Disable button detection */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_BUTTON_CONFIG_MASK, 0);
+
+ INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
+ INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
+
+ ret = request_threaded_irq(da7219_aad->irq, NULL,
+ da7219_aad_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7219-aad", da7219_aad);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask AAD IRQs */
+ memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ &mask, DA7219_AAD_IRQ_REG_MAX);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da7219_aad_init);
+
+void da7219_aad_exit(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ u8 mask[DA7219_AAD_IRQ_REG_MAX];
+
+ /* Mask off AAD IRQs */
+ memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
+ regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+ mask, DA7219_AAD_IRQ_REG_MAX);
+
+ free_irq(da7219_aad->irq, da7219_aad);
+
+ cancel_work_sync(&da7219_aad->btn_det_work);
+ cancel_work_sync(&da7219_aad->hptest_work);
+}
+EXPORT_SYMBOL_GPL(da7219_aad_exit);
+
+MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
new file mode 100644
index 000000000000..4fccf677cd06
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.h
@@ -0,0 +1,212 @@
+/*
+ * da7219-aad.h - DA7322 ASoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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 __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_ACCDET_STATUS_A 0xC0
+#define DA7219_ACCDET_STATUS_B 0xC1
+#define DA7219_ACCDET_IRQ_EVENT_A 0xC2
+#define DA7219_ACCDET_IRQ_EVENT_B 0xC3
+#define DA7219_ACCDET_IRQ_MASK_A 0xC4
+#define DA7219_ACCDET_IRQ_MASK_B 0xC5
+#define DA7219_ACCDET_CONFIG_1 0xC6
+#define DA7219_ACCDET_CONFIG_2 0xC7
+#define DA7219_ACCDET_CONFIG_3 0xC8
+#define DA7219_ACCDET_CONFIG_4 0xC9
+#define DA7219_ACCDET_CONFIG_5 0xCA
+#define DA7219_ACCDET_CONFIG_6 0xCB
+#define DA7219_ACCDET_CONFIG_7 0xCC
+#define DA7219_ACCDET_CONFIG_8 0xCD
+
+
+/*
+ * Bit Fields
+ */
+
+/* DA7219_ACCDET_STATUS_A = 0xC0 */
+#define DA7219_JACK_INSERTION_STS_SHIFT 0
+#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0)
+#define DA7219_JACK_TYPE_STS_SHIFT 1
+#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1)
+#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2
+#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2)
+#define DA7219_MICBIAS_UP_STS_SHIFT 3
+#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3)
+
+/* DA7219_ACCDET_STATUS_B = 0xC1 */
+#define DA7219_BUTTON_TYPE_STS_SHIFT 0
+#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */
+#define DA7219_E_JACK_INSERTED_SHIFT 0
+#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_E_JACK_REMOVED_SHIFT 1
+#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */
+#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */
+#define DA7219_M_JACK_INSERTED_SHIFT 0
+#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0)
+#define DA7219_M_JACK_REMOVED_SHIFT 1
+#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1)
+#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2
+#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */
+#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0
+#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0)
+#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1
+#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1)
+#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2
+#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2)
+#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3
+#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3)
+#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4
+#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4)
+#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5
+#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5)
+#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6
+#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6)
+#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7
+#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_1 = 0xC6 */
+#define DA7219_ACCDET_EN_SHIFT 0
+#define DA7219_ACCDET_EN_MASK (0x1 << 0)
+#define DA7219_BUTTON_CONFIG_SHIFT 1
+#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1)
+#define DA7219_MIC_DET_THRESH_SHIFT 4
+#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4)
+#define DA7219_JACK_TYPE_DET_EN_SHIFT 6
+#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6)
+#define DA7219_PIN_ORDER_DET_EN_SHIFT 7
+#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_2 = 0xC7 */
+#define DA7219_ACCDET_PAUSE_SHIFT 0
+#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0)
+#define DA7219_JACKDET_DEBOUNCE_SHIFT 1
+#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1)
+#define DA7219_JACK_DETECT_RATE_SHIFT 4
+#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4)
+#define DA7219_JACKDET_REM_DEB_SHIFT 6
+#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6)
+
+/* DA7219_ACCDET_CONFIG_3 = 0xC8 */
+#define DA7219_A_D_BUTTON_THRESH_SHIFT 0
+#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_4 = 0xC9 */
+#define DA7219_D_B_BUTTON_THRESH_SHIFT 0
+#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_5 = 0xCA */
+#define DA7219_B_C_BUTTON_THRESH_SHIFT 0
+#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_6 = 0xCB */
+#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0
+#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_7 = 0xCC */
+#define DA7219_BUTTON_AVERAGE_SHIFT 0
+#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0)
+#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2
+#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2)
+#define DA7219_PIN_ORDER_FORCE_SHIFT 4
+#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4)
+#define DA7219_JACK_TYPE_FORCE_SHIFT 5
+#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5)
+
+/* DA7219_ACCDET_CONFIG_8 = 0xCD */
+#define DA7219_HPTEST_EN_SHIFT 0
+#define DA7219_HPTEST_EN_MASK (0x1 << 0)
+#define DA7219_HPTEST_RES_SEL_SHIFT 1
+#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1)
+#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1)
+#define DA7219_HPTEST_COMP_SHIFT 4
+#define DA7219_HPTEST_COMP_MASK (0x1 << 4)
+
+
+#define DA7219_AAD_MAX_BUTTONS 4
+#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \
+ SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+#define DA7219_AAD_MICBIAS_CHK_DELAY 10
+#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
+
+#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28
+#define DA7219_AAD_HPTEST_PERIOD 65
+
+enum da7219_aad_event_regs {
+ DA7219_AAD_IRQ_REG_A = 0,
+ DA7219_AAD_IRQ_REG_B,
+ DA7219_AAD_IRQ_REG_MAX,
+};
+
+/* Private data */
+struct da7219_aad_priv {
+ struct snd_soc_codec *codec;
+ int irq;
+
+ u8 micbias_pulse_lvl;
+ u32 micbias_pulse_time;
+
+ u8 btn_cfg;
+
+ struct work_struct btn_det_work;
+ struct work_struct hptest_work;
+
+ struct snd_soc_jack *jack;
+ bool jack_inserted;
+};
+
+/* AAD control */
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+/* Init/Exit */
+int da7219_aad_init(struct snd_soc_codec *codec);
+void da7219_aad_exit(struct snd_soc_codec *codec);
+
+#endif /* __DA7219_AAD_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
new file mode 100644
index 000000000000..f238c1e8a69c
--- /dev/null
+++ b/sound/soc/codecs/da7219.c
@@ -0,0 +1,1955 @@
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/clk.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.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 <sound/da7219.h>
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv,
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -77.25dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0);
+
+/* Input Enums */
+static const char * const da7219_alc_attack_rate_txt[] = {
+ "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+ "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+ "30024/fs"
+};
+
+static const struct soc_enum da7219_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT,
+ DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt);
+
+static const char * const da7219_alc_release_rate_txt[] = {
+ "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+ "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs"
+};
+
+static const struct soc_enum da7219_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT,
+ DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt);
+
+static const char * const da7219_alc_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7219_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT,
+ DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt);
+
+static const char * const da7219_alc_env_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7219_alc_env_attack_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const struct soc_enum da7219_alc_env_release_rate =
+ SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT,
+ DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const char * const da7219_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs"
+};
+
+static const struct soc_enum da7219_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTICLIP_STEP_SHIFT,
+ DA7219_ALC_ANTICLIP_STEP_MAX,
+ da7219_alc_anticlip_step_txt);
+
+/* Input/Output Enums */
+static const char * const da7219_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16"
+};
+
+static const struct soc_enum da7219_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT,
+ DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt);
+
+static const char * const da7219_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice"
+};
+
+static const unsigned int da7219_hpf_mode_val[] = {
+ DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7219_adc_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const struct soc_enum da7219_dac_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+ DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+ da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const char * const da7219_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz"
+};
+
+static const struct soc_enum da7219_adc_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_AUDIO_HPF_CORNER_SHIFT,
+ DA7219_AUDIO_HPF_CORNER_MAX,
+ da7219_audio_hpf_corner_txt);
+
+static const char * const da7219_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da7219_adc_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+ DA7219_ADC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+ DA7219_DAC_VOICE_HPF_CORNER_SHIFT,
+ DA7219_VOICE_HPF_CORNER_MAX,
+ da7219_voice_hpf_corner_txt);
+
+static const char * const da7219_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7219_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT,
+ DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt);
+
+static const char * const da7219_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7219_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT,
+ DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7219_dac_softmute_rate_txt[] = {
+ "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples",
+ "32 Samples", "64 Samples"
+};
+
+static const struct soc_enum da7219_dac_softmute_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT,
+ DA7219_DAC_SOFTMUTE_RATE_MAX,
+ da7219_dac_softmute_rate_txt);
+
+static const char * const da7219_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7219_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_SETUP_TIME_SHIFT,
+ DA7219_DAC_NG_SETUP_TIME_MAX,
+ da7219_dac_ng_setup_time_txt);
+
+static const char * const da7219_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampup_txt);
+
+static const char * const da7219_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+ DA7219_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7219_DAC_NG_RAMP_RATE_MAX,
+ da7219_dac_ng_rampdown_txt);
+
+
+static const char * const da7219_cp_track_mode_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7219_cp_track_mode_val[] = {
+ DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL,
+ DA7219_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7219_cp_track_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT,
+ DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX,
+ da7219_cp_track_mode_txt,
+ da7219_cp_track_mode_val);
+
+
+/*
+ * Control Functions
+ */
+
+/* Locked Kcontrol calls */
+static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+/* ALC */
+static void da7219_alc_calib(struct snd_soc_codec *codec)
+{
+ u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
+
+ /* Save current state of mic control register */
+ mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL);
+
+ /* Save current state of input mixer control register */
+ mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+
+ /* Save current state of input ADC control register */
+ adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+ DA7219_MIC_1_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK,
+ DA7219_MIC_1_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_MASK |
+ DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
+ DA7219_MIXIN_L_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
+ DA7219_ADC_L_EN_MASK);
+
+ /* Perform auto calibration */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_AUTO_CALIB_EN_MASK,
+ DA7219_ALC_AUTO_CALIB_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1);
+ } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
+ dev_warn(codec->dev,
+ "ALC auto calibration failed with overflow\n");
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK, 0);
+ } else {
+ /* Enable DC offset cancellation, hybrid mode */
+ snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK,
+ DA7219_ALC_OFFSET_EN_MASK |
+ DA7219_ALC_SYNC_MODE_MASK);
+ }
+
+ /* Restore input filter control register to original state */
+ snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+}
+
+static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ /*
+ * If ALC in operation and value of control has been updated,
+ * make sure calibrated offsets are updated.
+ */
+ if ((ret == 1) && (da7219->alc_en))
+ da7219_alc_calib(codec);
+
+ return ret;
+}
+
+static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
+ da7219_alc_calib(codec);
+ da7219->alc_en = true;
+ } else {
+ da7219->alc_en = false;
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to host endianness here.
+ */
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int reg = mixer_ctrl->reg;
+ u16 val;
+ int ret;
+
+ /*
+ * Frequency value spans two 8-bit registers, lower then upper byte.
+ * Therefore we need to convert to little endian here to align with
+ * HW registers.
+ */
+ val = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+ mutex_lock(&da7219->lock);
+ ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7219->lock);
+
+ return ret;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7219_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN,
+ DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_mic_gain_tlv),
+ SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN,
+ DA7219_MIXIN_L_AMP_GAIN_SHIFT,
+ DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_mixin_gain_put,
+ da7219_mixin_gain_tlv),
+ SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ADC */
+ SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN,
+ DA7219_ADC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_adc_dig_gain_tlv),
+ SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+ SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL,
+ DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7219_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7219_alc_hold_time),
+ SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate),
+ SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE,
+ DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN,
+ DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX,
+ DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+ DA7219_INVERT, da7219_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS,
+ DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+ DA7219_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL,
+ DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT),
+ SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT,
+ snd_soc_get_volsw, da7219_alc_sw_put),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode),
+ SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner),
+ SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner),
+
+ /* Sidetone Filter */
+ SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN,
+ DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_sidetone_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT),
+
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2,
+ DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put, da7219_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1,
+ DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L,
+ DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L,
+ DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+ da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER,
+ DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER,
+ DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+ DA7219_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate),
+
+ /* DAC High-Pass Filter */
+ SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode,
+ da7219_enum_locked_get, da7219_enum_locked_put),
+ SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner),
+ SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner),
+
+ /* DAC 5-Band Equaliser */
+ SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2,
+ DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3,
+ DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+ DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+ SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4,
+ DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Softmute */
+ SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate),
+ SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5,
+ DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_NO_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH,
+ DA7219_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH,
+ DA7219_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+ da7219_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* DACs */
+ SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN,
+ DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT,
+ DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_dac_dig_gain_tlv),
+ SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL,
+ DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode),
+ SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1,
+ DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX,
+ DA7219_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN,
+ DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT,
+ DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT,
+ da7219_volsw_locked_get, da7219_volsw_locked_put,
+ da7219_hp_gain_tlv),
+ SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+ DA7219_INVERT, da7219_volsw_locked_get,
+ da7219_volsw_locked_put),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL,
+ DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7219_out_sel_txt[] = {
+ "ADC", "Tone Generator", "DAIL", "DAIR"
+};
+
+static const struct soc_enum da7219_out_dail_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dail_sel_mux =
+ SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel);
+
+static const struct soc_enum da7219_out_dair_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+ DA7219_DAI_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dair_sel_mux =
+ SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel);
+
+static const struct soc_enum da7219_out_dacl_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_L_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacl_sel_mux =
+ SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel);
+
+static const struct soc_enum da7219_out_dacr_sel =
+ SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+ DA7219_DAC_R_SRC_SHIFT,
+ DA7219_OUT_SRC_MAX,
+ da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacr_sel_mux =
+ SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+static const struct snd_kcontrol_new da7219_mixin_controls[] = {
+ SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT,
+ DA7219_MIXIN_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_l_controls[] = {
+ SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT,
+ DA7219_MIXOUT_L_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_r_controls[] = {
+ SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT,
+ DA7219_MIXOUT_R_MIX_SELECT_SHIFT,
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+#define DA7219_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \
+ DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
+ DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7219_dai_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 pll_ctrl, pll_status;
+ int i = 0;
+ bool srm_lock = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (da7219->master)
+ /* Enable DAI clks for master mode */
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK,
+ DA7219_DAI_CLK_EN_MASK);
+
+ /* PC synchronised to DAI */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK, 0);
+
+ /* Slave mode, if SRM not enabled no need for status checks */
+ pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL);
+ if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ do {
+ pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+ if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
+ srm_lock = true;
+ } else {
+ ++i;
+ msleep(50);
+ }
+ } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock));
+
+ if (!srm_lock)
+ dev_warn(codec->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT,
+ DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Disable DAI clks if in master mode */
+ if (da7219->master)
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_CLK_EN_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL,
+ DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
+ DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1,
+ DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL,
+ DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT),
+
+ /* Input Mixer Supply */
+ SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+
+ /* Input Mixer */
+ SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0,
+ da7219_mixin_controls,
+ ARRAY_SIZE(da7219_mixin_controls)),
+
+ /* Input Muxes */
+ SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dail_sel_mux),
+ SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dair_sel_mux),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT,
+ DA7219_NO_INVERT, da7219_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Muxes */
+ SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacl_sel_mux),
+ SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0,
+ &da7219_out_dacr_sel_mux),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_l_controls,
+ ARRAY_SIZE(da7219_mixout_l_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7219_mixout_r_controls,
+ ARRAY_SIZE(da7219_mixout_r_controls)),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7219_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0,
+ 0, da7219_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
+ DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
+ DA7219_NO_INVERT),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
+ DA7219_NO_INVERT, NULL, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mux Routes
+ */
+
+#define DA7219_OUT_DAI_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIOUT"}, \
+ {name, "DAIR", "DAIOUT"}
+
+#define DA7219_OUT_DAC_MUX_ROUTES(name) \
+ {name, "ADC", "Mixer In"}, \
+ {name, "Tone Generator", "Tone Generator"}, \
+ {name, "DAIL", "DAIIN"}, \
+ {name, "DAIR", "DAIIN"}
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7219_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Mixer Out FilterL"}, \
+ {name, "Out FilterR Switch", "Mixer Out FilterR"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7219_audio_map[] = {
+ /* Input paths */
+ {"MIC", NULL, "Mic Bias"},
+ {"Mic PGA", NULL, "MIC"},
+ {"Mixin PGA", NULL, "Mic PGA"},
+ {"ADC", NULL, "Mixin PGA"},
+
+ {"Sidetone Filter", NULL, "ADC"},
+ {"Mixer In", NULL, "Mixer In Supply"},
+ {"Mixer In", "Mic Switch", "ADC"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
+ DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"),
+
+ {"DAIOUT", NULL, "Out DAIL Mux"},
+ {"DAIOUT", NULL, "Out DAIR Mux"},
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"),
+ DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"),
+
+ {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"},
+ {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"},
+
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"DACL", NULL, "ST Mixer Out FilterL"},
+ {"DACR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "DACL"},
+ {"Mixout Right PGA", NULL, "DACR"},
+
+ {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+ {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+
+ {"HPL", NULL, "Headphone Left PGA"},
+ {"HPR", NULL, "Headphone Right PGA"},
+
+ {"HPL", NULL, "Charge Pump"},
+ {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+ return 0;
+
+ if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case DA7219_CLKSRC_MCLK_SQR:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK,
+ DA7219_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7219_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ da7219->clk_src = clk_id;
+
+ if (da7219->mclk) {
+ freq = clk_round_rate(da7219->mclk, freq);
+ ret = clk_set_rate(da7219->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7219->mclk_rate = freq;
+
+ return 0;
+}
+
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ u8 pll_ctrl, indiv_bits, indiv;
+ u8 pll_frac_top, pll_frac_bot, pll_integer;
+ u32 freq_ref;
+ u64 frac_div;
+
+ /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7219->mclk_rate == 32768) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate < 2000000) {
+ dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ } else if (da7219->mclk_rate <= 5000000) {
+ indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+ indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 10000000) {
+ indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
+ indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 20000000) {
+ indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
+ indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 40000000) {
+ indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
+ indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7219->mclk_rate <= 54000000) {
+ indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
+ indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+ } else {
+ dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ da7219->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7219->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7219_SYSCLK_MCLK:
+ pll_ctrl |= DA7219_PLL_MODE_BYPASS;
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK |
+ DA7219_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7219_SYSCLK_PLL:
+ pll_ctrl |= DA7219_PLL_MODE_NORMAL;
+ break;
+ case DA7219_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7219_PLL_MODE_SRM;
+ break;
+ case DA7219_SYSCLK_PLL_32KHZ:
+ pll_ctrl |= DA7219_PLL_MODE_32KHZ;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid PLL config\n");
+ return -EINVAL;
+ }
+
+ /* Calculate dividers for PLL */
+ pll_integer = fout / freq_ref;
+ frac_div = (u64)(fout % freq_ref) * 8192ULL;
+ do_div(frac_div, freq_ref);
+ pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
+ snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+ DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ da7219->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7219->master = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7219_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7219_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default 64 BCLKs per WCLK is supported */
+ dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK |
+ DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
+ dai_clk_mode);
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ u8 dai_bclks_per_wclk;
+ u16 offset;
+ u32 frame_size;
+
+ /* No channels enabled so disable TDM, revert to 64-bit frames */
+ if (!tx_mask) {
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ DA7219_DAI_BCLKS_PER_WCLK_64);
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+ dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ DA7219_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /* Check we have a valid offset given */
+ if (rx_mask > DA7219_DAI_OFFSET_MAX) {
+ dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+ DA7219_DAI_OFFSET_MAX);
+ return -EINVAL;
+ }
+
+ /* Calculate & validate frame size based on slot info provided. */
+ frame_size = slots * slot_width;
+ switch (frame_size) {
+ case 32:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ dai_bclks_per_wclk);
+
+ offset = cpu_to_le16(rx_mask);
+ regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
+ &offset, sizeof(offset));
+
+ snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_TDM_CH_EN_MASK |
+ DA7219_DAI_TDM_MODE_EN_MASK,
+ (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+ DA7219_DAI_TDM_MODE_EN_MASK);
+
+ return 0;
+}
+
+static int da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 dai_ctrl = 0, fs;
+ unsigned int channels;
+
+ switch (params_width(params)) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(codec->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7219_SR_8000;
+ break;
+ case 11025:
+ fs = DA7219_SR_11025;
+ break;
+ case 12000:
+ fs = DA7219_SR_12000;
+ break;
+ case 16000:
+ fs = DA7219_SR_16000;
+ break;
+ case 22050:
+ fs = DA7219_SR_22050;
+ break;
+ case 24000:
+ fs = DA7219_SR_24000;
+ break;
+ case 32000:
+ fs = DA7219_SR_32000;
+ break;
+ case 44100:
+ fs = DA7219_SR_44100;
+ break;
+ case 48000:
+ fs = DA7219_SR_48000;
+ break;
+ case 88200:
+ fs = DA7219_SR_88200;
+ break;
+ case 96000:
+ fs = DA7219_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+ DA7219_DAI_WORD_LENGTH_MASK |
+ DA7219_DAI_CH_NUM_MASK,
+ dai_ctrl);
+ snd_soc_write(codec, DA7219_SR, fs);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7219_dai_ops = {
+ .hw_params = da7219_hw_params,
+ .set_sysclk = da7219_set_dai_sysclk,
+ .set_pll = da7219_set_dai_pll,
+ .set_fmt = da7219_set_dai_fmt,
+ .set_tdm_slot = da7219_set_dai_tdm_slot,
+};
+
+#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver da7219_dai = {
+ .name = "da7219-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = DA7219_DAI_CH_NUM_MAX,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7219_FORMATS,
+ },
+ .ops = &da7219_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+};
+
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7219_of_match[] = {
+ { .compatible = "dlg,da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7219_of_match);
+
+static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
+ u32 val)
+{
+ switch (val) {
+ case 1050:
+ return DA7219_LDO_LVL_SEL_1_05V;
+ case 1100:
+ return DA7219_LDO_LVL_SEL_1_10V;
+ case 1200:
+ return DA7219_LDO_LVL_SEL_1_20V;
+ case 1400:
+ return DA7219_LDO_LVL_SEL_1_40V;
+ default:
+ dev_warn(codec->dev, "Invalid LDO level");
+ return DA7219_LDO_LVL_SEL_1_05V;
+ }
+}
+
+static enum da7219_micbias_voltage
+ da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1800:
+ return DA7219_MICBIAS_1_8V;
+ case 2000:
+ return DA7219_MICBIAS_2_0V;
+ case 2200:
+ return DA7219_MICBIAS_2_2V;
+ case 2400:
+ return DA7219_MICBIAS_2_4V;
+ case 2600:
+ return DA7219_MICBIAS_2_6V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level");
+ return DA7219_MICBIAS_2_2V;
+ }
+}
+
+static enum da7219_mic_amp_in_sel
+ da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7219_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(codec->dev, "Invalid mic input type selection");
+ return DA7219_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct device_node *np = codec->dev->of_node;
+ struct da7219_pdata *pdata;
+ const char *of_str;
+ u32 of_val32;
+
+ pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
+ pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
+
+ if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
+
+ if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+ else
+ pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7219_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* MCLK */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK,
+ DA7219_BIAS_EN_MASK);
+
+ /* Enable Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK,
+ DA7219_LDO_EN_MASK);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable if jack detection not active */
+ if (!da7219->aad->jack) {
+ /* Bypass Internal Digital LDO */
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_EN_MASK, 0);
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7219_REFERENCES,
+ DA7219_BIAS_EN_MASK, 0);
+ }
+
+ /* MCLK */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
+ [DA7219_SUPPLY_VDD] = "VDD",
+ [DA7219_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7219_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7219_handle_supplies(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddio;
+ u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
+ da7219->supplies[i].supply = da7219_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
+ da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to get supplies");
+ return ret;
+ }
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1200000)
+ dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2800000)
+ io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies");
+ return ret;
+ }
+
+ /* Ensure device in active mode */
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+
+ /* Update IO voltage level range */
+ snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+
+ return 0;
+}
+
+static void da7219_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_pdata *pdata = da7219->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0;
+
+ /* Internal LDO */
+ switch (pdata->ldo_lvl_sel) {
+ case DA7219_LDO_LVL_SEL_1_05V:
+ case DA7219_LDO_LVL_SEL_1_10V:
+ case DA7219_LDO_LVL_SEL_1_20V:
+ case DA7219_LDO_LVL_SEL_1_40V:
+ snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+ DA7219_LDO_LEVEL_SELECT_MASK,
+ (pdata->ldo_lvl_sel <<
+ DA7219_LDO_LEVEL_SELECT_SHIFT));
+ break;
+ }
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias_lvl) {
+ case DA7219_MICBIAS_1_8V:
+ case DA7219_MICBIAS_2_0V:
+ case DA7219_MICBIAS_2_2V:
+ case DA7219_MICBIAS_2_4V:
+ case DA7219_MICBIAS_2_6V:
+ micbias_lvl |= (pdata->micbias_lvl <<
+ DA7219_MICBIAS1_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+ /* Mic */
+ switch (pdata->mic_amp_in_sel) {
+ case DA7219_MIC_AMP_IN_SEL_DIFF:
+ case DA7219_MIC_AMP_IN_SEL_SE_P:
+ case DA7219_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_write(codec, DA7219_MIC_1_SELECT,
+ pdata->mic_amp_in_sel);
+ break;
+ }
+ }
+}
+
+static int da7219_probe(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ mutex_init(&da7219->lock);
+
+ /* Regulator configuration */
+ ret = da7219_handle_supplies(codec);
+ if (ret)
+ return ret;
+
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7219->pdata = da7219_of_to_pdata(codec);
+ else
+ da7219->pdata = dev_get_platdata(codec->dev);
+
+ da7219_handle_pdata(codec);
+
+ /* Check if MCLK provided */
+ da7219->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7219->mclk)) {
+ if (PTR_ERR(da7219->mclk) != -ENOENT)
+ return PTR_ERR(da7219->mclk);
+ else
+ da7219->mclk = NULL;
+ }
+
+ /* Default PC counter to free-running */
+ snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+ DA7219_PC_FREERUN_MASK);
+
+ /* Default gain ramping */
+ snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+ DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+ DA7219_ADC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+ DA7219_DAC_L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+ DA7219_DAC_R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_RAMP_EN_MASK,
+ DA7219_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_RAMP_EN_MASK,
+ DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+ /* Initialise AAD block */
+ return da7219_aad_init(codec);
+}
+
+static int da7219_remove(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ da7219_aad_exit(codec);
+
+ /* Supplies */
+ return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* Put device into standby mode if jack detection disabled */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+
+ return 0;
+}
+
+static int da7219_resume(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ /* Put device into active mode if previously pushed to standby */
+ if (!da7219->aad->jack)
+ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE,
+ DA7219_SYSTEM_ACTIVE_MASK);
+
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
+ .probe = da7219_probe,
+ .remove = da7219_remove,
+ .suspend = da7219_suspend,
+ .resume = da7219_resume,
+ .set_bias_level = da7219_set_bias_level,
+
+ .controls = da7219_snd_controls,
+ .num_controls = ARRAY_SIZE(da7219_snd_controls),
+
+ .dapm_widgets = da7219_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
+ .dapm_routes = da7219_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7219_reg_defaults[] = {
+ { DA7219_MIC_1_SELECT, 0x00 },
+ { DA7219_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7219_SR_24_48, 0x00 },
+ { DA7219_SR, 0x0A },
+ { DA7219_CIF_I2C_ADDR_CFG, 0x02 },
+ { DA7219_PLL_CTRL, 0x10 },
+ { DA7219_PLL_FRAC_TOP, 0x00 },
+ { DA7219_PLL_FRAC_BOT, 0x00 },
+ { DA7219_PLL_INTEGER, 0x20 },
+ { DA7219_DIG_ROUTING_DAI, 0x10 },
+ { DA7219_DAI_CLK_MODE, 0x01 },
+ { DA7219_DAI_CTRL, 0x28 },
+ { DA7219_DAI_TDM_CTRL, 0x40 },
+ { DA7219_DIG_ROUTING_DAC, 0x32 },
+ { DA7219_DAI_OFFSET_LOWER, 0x00 },
+ { DA7219_DAI_OFFSET_UPPER, 0x00 },
+ { DA7219_REFERENCES, 0x00 },
+ { DA7219_MIXIN_L_SELECT, 0x00 },
+ { DA7219_MIXIN_L_GAIN, 0x03 },
+ { DA7219_ADC_L_GAIN, 0x6F },
+ { DA7219_ADC_FILTERS1, 0x80 },
+ { DA7219_MIC_1_GAIN, 0x01 },
+ { DA7219_SIDETONE_CTRL, 0x40 },
+ { DA7219_SIDETONE_GAIN, 0x0E },
+ { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7219_DAC_FILTERS5, 0x00 },
+ { DA7219_DAC_FILTERS2, 0x88 },
+ { DA7219_DAC_FILTERS3, 0x88 },
+ { DA7219_DAC_FILTERS4, 0x08 },
+ { DA7219_DAC_FILTERS1, 0x80 },
+ { DA7219_DAC_L_GAIN, 0x6F },
+ { DA7219_DAC_R_GAIN, 0x6F },
+ { DA7219_CP_CTRL, 0x20 },
+ { DA7219_HP_L_GAIN, 0x39 },
+ { DA7219_HP_R_GAIN, 0x39 },
+ { DA7219_MIXOUT_L_SELECT, 0x00 },
+ { DA7219_MIXOUT_R_SELECT, 0x00 },
+ { DA7219_MICBIAS_CTRL, 0x03 },
+ { DA7219_MIC_1_CTRL, 0x40 },
+ { DA7219_MIXIN_L_CTRL, 0x40 },
+ { DA7219_ADC_L_CTRL, 0x40 },
+ { DA7219_DAC_L_CTRL, 0x40 },
+ { DA7219_DAC_R_CTRL, 0x40 },
+ { DA7219_HP_L_CTRL, 0x40 },
+ { DA7219_HP_R_CTRL, 0x40 },
+ { DA7219_MIXOUT_L_CTRL, 0x10 },
+ { DA7219_MIXOUT_R_CTRL, 0x10 },
+ { DA7219_CHIP_ID1, 0x23 },
+ { DA7219_CHIP_ID2, 0x93 },
+ { DA7219_CHIP_REVISION, 0x00 },
+ { DA7219_LDO_CTRL, 0x00 },
+ { DA7219_IO_CTRL, 0x00 },
+ { DA7219_GAIN_RAMP_CTRL, 0x00 },
+ { DA7219_PC_COUNT, 0x02 },
+ { DA7219_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7219_DIG_CTRL, 0x00 },
+ { DA7219_ALC_CTRL2, 0x00 },
+ { DA7219_ALC_CTRL3, 0x00 },
+ { DA7219_ALC_NOISE, 0x3F },
+ { DA7219_ALC_TARGET_MIN, 0x3F },
+ { DA7219_ALC_TARGET_MAX, 0x00 },
+ { DA7219_ALC_GAIN_LIMITS, 0xFF },
+ { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7219_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7219_ALC_ANTICLIP_LEVEL, 0x00 },
+ { DA7219_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7219_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7219_DAC_NG_ON_THRESH, 0x00 },
+ { DA7219_DAC_NG_CTRL, 0x00 },
+ { DA7219_TONE_GEN_CFG1, 0x00 },
+ { DA7219_TONE_GEN_CFG2, 0x00 },
+ { DA7219_TONE_GEN_CYCLES, 0x00 },
+ { DA7219_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7219_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7219_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7219_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7219_TONE_GEN_ON_PER, 0x02 },
+ { DA7219_TONE_GEN_OFF_PER, 0x01 },
+ { DA7219_ACCDET_IRQ_MASK_A, 0x00 },
+ { DA7219_ACCDET_IRQ_MASK_B, 0x00 },
+ { DA7219_ACCDET_CONFIG_1, 0xD6 },
+ { DA7219_ACCDET_CONFIG_2, 0x34 },
+ { DA7219_ACCDET_CONFIG_3, 0x0A },
+ { DA7219_ACCDET_CONFIG_4, 0x16 },
+ { DA7219_ACCDET_CONFIG_5, 0x21 },
+ { DA7219_ACCDET_CONFIG_6, 0x3E },
+ { DA7219_ACCDET_CONFIG_7, 0x01 },
+ { DA7219_SYSTEM_ACTIVE, 0x00 },
+};
+
+static bool da7219_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7219_MIC_1_GAIN_STATUS:
+ case DA7219_MIXIN_L_GAIN_STATUS:
+ case DA7219_ADC_L_GAIN_STATUS:
+ case DA7219_DAC_L_GAIN_STATUS:
+ case DA7219_DAC_R_GAIN_STATUS:
+ case DA7219_HP_L_GAIN_STATUS:
+ case DA7219_HP_R_GAIN_STATUS:
+ case DA7219_CIF_CTRL:
+ case DA7219_PLL_SRM_STS:
+ case DA7219_ALC_CTRL1:
+ case DA7219_SYSTEM_MODES_INPUT:
+ case DA7219_SYSTEM_MODES_OUTPUT:
+ case DA7219_ALC_OFFSET_AUTO_M_L:
+ case DA7219_ALC_OFFSET_AUTO_U_L:
+ case DA7219_TONE_GEN_CFG1:
+ case DA7219_ACCDET_STATUS_A:
+ case DA7219_ACCDET_STATUS_B:
+ case DA7219_ACCDET_IRQ_EVENT_A:
+ case DA7219_ACCDET_IRQ_EVENT_B:
+ case DA7219_ACCDET_CONFIG_8:
+ case DA7219_SYSTEM_STATUS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_config da7219_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7219_SYSTEM_ACTIVE,
+ .reg_defaults = da7219_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults),
+ .volatile_reg = da7219_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7219_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7219_priv *da7219;
+ int ret;
+
+ da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
+ GFP_KERNEL);
+ if (!da7219)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7219);
+
+ da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
+ if (IS_ERR(da7219->regmap)) {
+ ret = PTR_ERR(da7219->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
+ &da7219_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int da7219_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da7219_i2c_id[] = {
+ { "da7219", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
+
+static struct i2c_driver da7219_i2c_driver = {
+ .driver = {
+ .name = "da7219",
+ .of_match_table = of_match_ptr(da7219_of_match),
+ },
+ .probe = da7219_i2c_probe,
+ .remove = da7219_i2c_remove,
+ .id_table = da7219_i2c_id,
+};
+
+module_i2c_driver(da7219_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7219 Codec Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
new file mode 100644
index 000000000000..b514268c6c56
--- /dev/null
+++ b/sound/soc/codecs/da7219.h
@@ -0,0 +1,820 @@
+/*
+ * da7219.h - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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 __DA7219_H
+#define __DA7219_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_MIC_1_GAIN_STATUS 0x6
+#define DA7219_MIXIN_L_GAIN_STATUS 0x8
+#define DA7219_ADC_L_GAIN_STATUS 0xA
+#define DA7219_DAC_L_GAIN_STATUS 0xC
+#define DA7219_DAC_R_GAIN_STATUS 0xD
+#define DA7219_HP_L_GAIN_STATUS 0xE
+#define DA7219_HP_R_GAIN_STATUS 0xF
+#define DA7219_MIC_1_SELECT 0x10
+#define DA7219_CIF_TIMEOUT_CTRL 0x12
+#define DA7219_CIF_CTRL 0x13
+#define DA7219_SR_24_48 0x16
+#define DA7219_SR 0x17
+#define DA7219_CIF_I2C_ADDR_CFG 0x1B
+#define DA7219_PLL_CTRL 0x20
+#define DA7219_PLL_FRAC_TOP 0x22
+#define DA7219_PLL_FRAC_BOT 0x23
+#define DA7219_PLL_INTEGER 0x24
+#define DA7219_PLL_SRM_STS 0x25
+#define DA7219_DIG_ROUTING_DAI 0x2A
+#define DA7219_DAI_CLK_MODE 0x2B
+#define DA7219_DAI_CTRL 0x2C
+#define DA7219_DAI_TDM_CTRL 0x2D
+#define DA7219_DIG_ROUTING_DAC 0x2E
+#define DA7219_ALC_CTRL1 0x2F
+#define DA7219_DAI_OFFSET_LOWER 0x30
+#define DA7219_DAI_OFFSET_UPPER 0x31
+#define DA7219_REFERENCES 0x32
+#define DA7219_MIXIN_L_SELECT 0x33
+#define DA7219_MIXIN_L_GAIN 0x34
+#define DA7219_ADC_L_GAIN 0x36
+#define DA7219_ADC_FILTERS1 0x38
+#define DA7219_MIC_1_GAIN 0x39
+#define DA7219_SIDETONE_CTRL 0x3A
+#define DA7219_SIDETONE_GAIN 0x3B
+#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C
+#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D
+#define DA7219_DAC_FILTERS5 0x40
+#define DA7219_DAC_FILTERS2 0x41
+#define DA7219_DAC_FILTERS3 0x42
+#define DA7219_DAC_FILTERS4 0x43
+#define DA7219_DAC_FILTERS1 0x44
+#define DA7219_DAC_L_GAIN 0x45
+#define DA7219_DAC_R_GAIN 0x46
+#define DA7219_CP_CTRL 0x47
+#define DA7219_HP_L_GAIN 0x48
+#define DA7219_HP_R_GAIN 0x49
+#define DA7219_MIXOUT_L_SELECT 0x4B
+#define DA7219_MIXOUT_R_SELECT 0x4C
+#define DA7219_SYSTEM_MODES_INPUT 0x50
+#define DA7219_SYSTEM_MODES_OUTPUT 0x51
+#define DA7219_MICBIAS_CTRL 0x62
+#define DA7219_MIC_1_CTRL 0x63
+#define DA7219_MIXIN_L_CTRL 0x65
+#define DA7219_ADC_L_CTRL 0x67
+#define DA7219_DAC_L_CTRL 0x69
+#define DA7219_DAC_R_CTRL 0x6A
+#define DA7219_HP_L_CTRL 0x6B
+#define DA7219_HP_R_CTRL 0x6C
+#define DA7219_MIXOUT_L_CTRL 0x6E
+#define DA7219_MIXOUT_R_CTRL 0x6F
+#define DA7219_CHIP_ID1 0x81
+#define DA7219_CHIP_ID2 0x82
+#define DA7219_CHIP_REVISION 0x83
+#define DA7219_LDO_CTRL 0x90
+#define DA7219_IO_CTRL 0x91
+#define DA7219_GAIN_RAMP_CTRL 0x92
+#define DA7219_PC_COUNT 0x94
+#define DA7219_CP_VOL_THRESHOLD1 0x95
+#define DA7219_CP_DELAY 0x96
+#define DA7219_DIG_CTRL 0x99
+#define DA7219_ALC_CTRL2 0x9A
+#define DA7219_ALC_CTRL3 0x9B
+#define DA7219_ALC_NOISE 0x9C
+#define DA7219_ALC_TARGET_MIN 0x9D
+#define DA7219_ALC_TARGET_MAX 0x9E
+#define DA7219_ALC_GAIN_LIMITS 0x9F
+#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA7219_ALC_ANTICLIP_CTRL 0xA1
+#define DA7219_ALC_ANTICLIP_LEVEL 0xA2
+#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3
+#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4
+#define DA7219_DAC_NG_SETUP_TIME 0xAF
+#define DA7219_DAC_NG_OFF_THRESH 0xB0
+#define DA7219_DAC_NG_ON_THRESH 0xB1
+#define DA7219_DAC_NG_CTRL 0xB2
+#define DA7219_TONE_GEN_CFG1 0xB4
+#define DA7219_TONE_GEN_CFG2 0xB5
+#define DA7219_TONE_GEN_CYCLES 0xB6
+#define DA7219_TONE_GEN_FREQ1_L 0xB7
+#define DA7219_TONE_GEN_FREQ1_U 0xB8
+#define DA7219_TONE_GEN_FREQ2_L 0xB9
+#define DA7219_TONE_GEN_FREQ2_U 0xBA
+#define DA7219_TONE_GEN_ON_PER 0xBB
+#define DA7219_TONE_GEN_OFF_PER 0xBC
+#define DA7219_SYSTEM_STATUS 0xE0
+#define DA7219_SYSTEM_ACTIVE 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7219_SWITCH_EN_MAX 0x1
+
+/* DA7219_MIC_1_GAIN_STATUS = 0x6 */
+#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0)
+#define DA7219_MIC_1_AMP_GAIN_MAX 0x7
+
+/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0)
+
+/* DA7219_ADC_L_GAIN_STATUS = 0xA */
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_L_GAIN_STATUS = 0xC */
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_DAC_R_GAIN_STATUS = 0xD */
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0)
+
+/* DA7219_HP_L_GAIN_STATUS = 0xE */
+#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_HP_R_GAIN_STATUS = 0xF */
+#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0)
+
+/* DA7219_MIC_1_SELECT = 0x10 */
+#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */
+#define DA7219_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7219_CIF_CTRL = 0x13 */
+#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7219_SR_24_48 = 0x16 */
+#define DA7219_SR_24_48_SHIFT 0
+#define DA7219_SR_24_48_MASK (0x1 << 0)
+
+/* DA7219_SR = 0x17 */
+#define DA7219_SR_SHIFT 0
+#define DA7219_SR_MASK (0xF << 0)
+#define DA7219_SR_8000 (0x01 << 0)
+#define DA7219_SR_11025 (0x02 << 0)
+#define DA7219_SR_12000 (0x03 << 0)
+#define DA7219_SR_16000 (0x05 << 0)
+#define DA7219_SR_22050 (0x06 << 0)
+#define DA7219_SR_24000 (0x07 << 0)
+#define DA7219_SR_32000 (0x09 << 0)
+#define DA7219_SR_44100 (0x0A << 0)
+#define DA7219_SR_48000 (0x0B << 0)
+#define DA7219_SR_88200 (0x0E << 0)
+#define DA7219_SR_96000 (0x0F << 0)
+
+/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */
+#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0
+#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0)
+
+/* DA7219_PLL_CTRL = 0x20 */
+#define DA7219_PLL_INDIV_SHIFT 2
+#define DA7219_PLL_INDIV_MASK (0x7 << 2)
+#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2)
+#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2)
+#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2)
+#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2)
+#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2)
+#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
+#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
+#define DA7219_PLL_MODE_SHIFT 6
+#define DA7219_PLL_MODE_MASK (0x3 << 6)
+#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7219_PLL_MODE_SRM (0x2 << 6)
+#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
+
+/* DA7219_PLL_FRAC_TOP = 0x22 */
+#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7219_PLL_FRAC_BOT = 0x23 */
+#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7219_PLL_INTEGER = 0x24 */
+#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7219_PLL_SRM_STS = 0x25 */
+#define DA7219_PLL_SRM_STATE_SHIFT 0
+#define DA7219_PLL_SRM_STATE_MASK (0xF << 0)
+#define DA7219_PLL_SRM_STATUS_SHIFT 4
+#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4)
+#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7)
+
+/* DA7219_DIG_ROUTING_DAI = 0x2A */
+#define DA7219_DAI_L_SRC_SHIFT 0
+#define DA7219_DAI_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAI_R_SRC_SHIFT 4
+#define DA7219_DAI_R_SRC_MASK (0x3 << 4)
+#define DA7219_OUT_SRC_MAX 4
+
+/* DA7219_DAI_CLK_MODE = 0x2B */
+#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7219_DAI_CLK_POL_SHIFT 2
+#define DA7219_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7219_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7219_DAI_WCLK_POL_SHIFT 3
+#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7219_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7219_DAI_CLK_EN_SHIFT 7
+#define DA7219_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_CTRL = 0x2C */
+#define DA7219_DAI_FORMAT_SHIFT 0
+#define DA7219_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7219_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7219_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7219_DAI_WORD_LENGTH_SHIFT 2
+#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7219_DAI_CH_NUM_SHIFT 4
+#define DA7219_DAI_CH_NUM_MASK (0x3 << 4)
+#define DA7219_DAI_CH_NUM_MAX 2
+#define DA7219_DAI_EN_SHIFT 7
+#define DA7219_DAI_EN_MASK (0x1 << 7)
+
+/* DA7219_DAI_TDM_CTRL = 0x2D */
+#define DA7219_DAI_TDM_CH_EN_SHIFT 0
+#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0)
+#define DA7219_DAI_OE_SHIFT 6
+#define DA7219_DAI_OE_MASK (0x1 << 6)
+#define DA7219_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+#define DA7219_DAI_TDM_MAX_SLOTS 2
+
+/* DA7219_DIG_ROUTING_DAC = 0x2E */
+#define DA7219_DAC_L_SRC_SHIFT 0
+#define DA7219_DAC_L_SRC_MASK (0x3 << 0)
+#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0)
+#define DA7219_DAC_L_MONO_SHIFT 3
+#define DA7219_DAC_L_MONO_MASK (0x1 << 3)
+#define DA7219_DAC_R_SRC_SHIFT 4
+#define DA7219_DAC_R_SRC_MASK (0x3 << 4)
+#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4)
+#define DA7219_DAC_R_MONO_SHIFT 7
+#define DA7219_DAC_R_MONO_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL1 = 0x2F */
+#define DA7219_ALC_OFFSET_EN_SHIFT 0
+#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0)
+#define DA7219_ALC_SYNC_MODE_SHIFT 1
+#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1)
+#define DA7219_ALC_EN_SHIFT 3
+#define DA7219_ALC_EN_MASK (0x1 << 3)
+#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4
+#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4)
+#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5
+#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5)
+
+/* DA7219_DAI_OFFSET_LOWER = 0x30 */
+#define DA7219_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7219_DAI_OFFSET_UPPER = 0x31 */
+#define DA7219_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+#define DA7219_DAI_OFFSET_MAX 0x2FF
+
+/* DA7219_REFERENCES = 0x32 */
+#define DA7219_BIAS_EN_SHIFT 3
+#define DA7219_BIAS_EN_MASK (0x1 << 3)
+#define DA7219_VMID_FAST_CHARGE_SHIFT 4
+#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4)
+
+/* DA7219_MIXIN_L_SELECT = 0x33 */
+#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXIN_L_GAIN = 0x34 */
+#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0
+#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0)
+#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF
+
+/* DA7219_ADC_L_GAIN = 0x36 */
+#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7219_ADC_FILTERS1 = 0x38 */
+#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_VOICE_HPF_CORNER_MAX 8
+#define DA7219_ADC_VOICE_EN_SHIFT 3
+#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_AUDIO_HPF_CORNER_MAX 4
+#define DA7219_ADC_HPF_EN_SHIFT 7
+#define DA7219_ADC_HPF_EN_MASK (0x1 << 7)
+#define DA7219_HPF_MODE_SHIFT 0
+#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MAX 3
+
+/* DA7219_MIC_1_GAIN = 0x39 */
+#define DA7219_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7219_SIDETONE_CTRL = 0x3A */
+#define DA7219_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_SIDETONE_EN_SHIFT 7
+#define DA7219_SIDETONE_EN_MASK (0x1 << 7)
+
+/* DA7219_SIDETONE_GAIN = 0x3B */
+#define DA7219_SIDETONE_GAIN_SHIFT 0
+#define DA7219_SIDETONE_GAIN_MASK (0xF << 0)
+#define DA7219_SIDETONE_GAIN_MAX 0xE
+
+/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */
+#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0
+#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1
+#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2
+#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1)
+
+/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */
+#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7219_DAC_FILTERS5 = 0x40 */
+#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4)
+#define DA7219_DAC_SOFTMUTE_RATE_MAX 7
+#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7
+#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS2 = 0x41 */
+#define DA7219_DAC_EQ_BAND1_SHIFT 0
+#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND2_SHIFT 4
+#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4)
+#define DA7219_DAC_EQ_BAND_MAX 0xF
+
+/* DA7219_DAC_FILTERS3 = 0x42 */
+#define DA7219_DAC_EQ_BAND3_SHIFT 0
+#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0)
+#define DA7219_DAC_EQ_BAND4_SHIFT 4
+#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7219_DAC_FILTERS4 = 0x43 */
+#define DA7219_DAC_EQ_BAND5_SHIFT 0
+#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0)
+#define DA7219_DAC_EQ_EN_SHIFT 7
+#define DA7219_DAC_EQ_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_FILTERS1 = 0x44 */
+#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0
+#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7219_DAC_VOICE_EN_SHIFT 3
+#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3)
+#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7219_DAC_HPF_EN_SHIFT 7
+#define DA7219_DAC_HPF_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_GAIN = 0x45 */
+#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F
+#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0)
+
+/* DA7219_DAC_R_GAIN = 0x46 */
+#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7219_CP_CTRL = 0x47 */
+#define DA7219_CP_MCHANGE_SHIFT 4
+#define DA7219_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7219_CP_MCHANGE_REL_MASK 0x3
+#define DA7219_CP_MCHANGE_MAX 3
+#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7219_CP_MCHANGE_DAC_VOL 0x2
+#define DA7219_CP_MCHANGE_SIG_MAG 0x3
+#define DA7219_CP_EN_SHIFT 7
+#define DA7219_CP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_GAIN = 0x48 */
+#define DA7219_HP_L_AMP_GAIN_SHIFT 0
+#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7219_HP_AMP_GAIN_MAX 0x3F
+#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0)
+
+/* DA7219_HP_R_GAIN = 0x49 */
+#define DA7219_HP_R_AMP_GAIN_SHIFT 0
+#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7219_MIXOUT_L_SELECT = 0x4B */
+#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXOUT_R_SELECT = 0x4C */
+#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0
+#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_SYSTEM_MODES_INPUT = 0x50 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_ADC_MODE_SHIFT 1
+#define DA7219_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */
+#define DA7219_MODE_SUBMIT_SHIFT 0
+#define DA7219_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7219_DAC_MODE_SHIFT 1
+#define DA7219_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7219_MICBIAS_CTRL = 0x62 */
+#define DA7219_MICBIAS1_LEVEL_SHIFT 0
+#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0)
+#define DA7219_MICBIAS1_EN_SHIFT 3
+#define DA7219_MICBIAS1_EN_MASK (0x1 << 3)
+
+/* DA7219_MIC_1_CTRL = 0x63 */
+#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIC_1_AMP_EN_SHIFT 7
+#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXIN_L_CTRL = 0x65 */
+#define DA7219_MIXIN_L_MIX_EN_SHIFT 3
+#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3)
+#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_MIXIN_L_AMP_EN_SHIFT 7
+#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_ADC_L_CTRL = 0x67 */
+#define DA7219_ADC_L_BIAS_SHIFT 0
+#define DA7219_ADC_L_BIAS_MASK (0x3 << 0)
+#define DA7219_ADC_L_RAMP_EN_SHIFT 5
+#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_ADC_L_MUTE_EN_SHIFT 6
+#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_ADC_L_EN_SHIFT 7
+#define DA7219_ADC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_L_CTRL = 0x69 */
+#define DA7219_DAC_L_RAMP_EN_SHIFT 5
+#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_L_MUTE_EN_SHIFT 6
+#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_L_EN_SHIFT 7
+#define DA7219_DAC_L_EN_MASK (0x1 << 7)
+
+/* DA7219_DAC_R_CTRL = 0x6A */
+#define DA7219_DAC_R_RAMP_EN_SHIFT 5
+#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_DAC_R_MUTE_EN_SHIFT 6
+#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_DAC_R_EN_SHIFT 7
+#define DA7219_DAC_R_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_L_CTRL = 0x6B */
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_L_AMP_OE_SHIFT 3
+#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_L_AMP_EN_SHIFT 7
+#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_HP_R_CTRL = 0x6C */
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7219_HP_R_AMP_OE_SHIFT 3
+#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7219_HP_R_AMP_EN_SHIFT 7
+#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_L_CTRL = 0x6E */
+#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_MIXOUT_R_CTRL = 0x6F */
+#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7219_CHIP_ID1 = 0x81 */
+#define DA7219_CHIP_ID1_SHIFT 0
+#define DA7219_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7219_CHIP_ID2 = 0x82 */
+#define DA7219_CHIP_ID2_SHIFT 0
+#define DA7219_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7219_CHIP_REVISION = 0x83 */
+#define DA7219_CHIP_MINOR_SHIFT 0
+#define DA7219_CHIP_MINOR_MASK (0xF << 0)
+#define DA7219_CHIP_MAJOR_SHIFT 4
+#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7219_LDO_CTRL = 0x90 */
+#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
+#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
+#define DA7219_LDO_EN_SHIFT 7
+#define DA7219_LDO_EN_MASK (0x1 << 7)
+
+/* DA7219_IO_CTRL = 0x91 */
+#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1
+
+/* DA7219_GAIN_RAMP_CTRL = 0x92 */
+#define DA7219_GAIN_RAMP_RATE_SHIFT 0
+#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_MAX 4
+
+/* DA7219_PC_COUNT = 0x94 */
+#define DA7219_PC_FREERUN_SHIFT 0
+#define DA7219_PC_FREERUN_MASK (0x1 << 0)
+#define DA7219_PC_RESYNC_AUTO_SHIFT 1
+#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */
+#define DA7219_CP_THRESH_VDD2_SHIFT 0
+#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7219_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7219_DIG_CTRL = 0x99 */
+#define DA7219_DAC_L_INV_SHIFT 3
+#define DA7219_DAC_L_INV_MASK (0x1 << 3)
+#define DA7219_DAC_R_INV_SHIFT 7
+#define DA7219_DAC_R_INV_MASK (0x1 << 7)
+
+/* DA7219_ALC_CTRL2 = 0x9A */
+#define DA7219_ALC_ATTACK_SHIFT 0
+#define DA7219_ALC_ATTACK_MASK (0xF << 0)
+#define DA7219_ALC_ATTACK_MAX 13
+#define DA7219_ALC_RELEASE_SHIFT 4
+#define DA7219_ALC_RELEASE_MASK (0xF << 4)
+#define DA7219_ALC_RELEASE_MAX 11
+
+/* DA7219_ALC_CTRL3 = 0x9B */
+#define DA7219_ALC_HOLD_SHIFT 0
+#define DA7219_ALC_HOLD_MASK (0xF << 0)
+#define DA7219_ALC_HOLD_MAX 16
+#define DA7219_ALC_INTEG_ATTACK_SHIFT 4
+#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4)
+#define DA7219_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6)
+#define DA7219_ALC_INTEG_MAX 4
+
+/* DA7219_ALC_NOISE = 0x9C */
+#define DA7219_ALC_NOISE_SHIFT 0
+#define DA7219_ALC_NOISE_MASK (0x3F << 0)
+#define DA7219_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7219_ALC_TARGET_MIN = 0x9D */
+#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7219_ALC_TARGET_MAX = 0x9E */
+#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7219_ALC_GAIN_LIMITS = 0x9F */
+#define DA7219_ALC_ATTEN_MAX_SHIFT 0
+#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7219_ALC_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4)
+#define DA7219_ALC_ATTEN_GAIN_MAX 0xF
+
+/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7219_ALC_ANA_GAIN_MIN 0x1
+#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+#define DA7219_ALC_ANA_GAIN_MAX 0x7
+
+/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7219_ALC_ANTICLIP_STEP_MAX 4
+#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7
+#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7)
+
+/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0
+#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */
+#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */
+#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0
+#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0)
+
+/* DA7219_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7219_DAC_NG_SETUP_TIME_MAX 4
+#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7219_DAC_NG_RAMP_RATE_MAX 2
+
+/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */
+#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7219_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7219_DAC_NG_ON_THRESH = 0xB1 */
+#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7219_DAC_NG_CTRL = 0xB2 */
+#define DA7219_DAC_NG_EN_SHIFT 7
+#define DA7219_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG1 = 0xB4 */
+#define DA7219_DTMF_REG_SHIFT 0
+#define DA7219_DTMF_REG_MASK (0xF << 0)
+#define DA7219_DTMF_REG_MAX 16
+#define DA7219_DTMF_EN_SHIFT 4
+#define DA7219_DTMF_EN_MASK (0x1 << 4)
+#define DA7219_START_STOPN_SHIFT 7
+#define DA7219_START_STOPN_MASK (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG2 = 0xB5 */
+#define DA7219_SWG_SEL_SHIFT 0
+#define DA7219_SWG_SEL_MASK (0x3 << 0)
+#define DA7219_SWG_SEL_MAX 4
+#define DA7219_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7219_TONE_GEN_GAIN_SHIFT 4
+#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7219_TONE_GEN_GAIN_MAX 0xF
+#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7219_TONE_GEN_CYCLES = 0xB6 */
+#define DA7219_BEEP_CYCLES_SHIFT 0
+#define DA7219_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7219_FREQ1_L_SHIFT 0
+#define DA7219_FREQ1_L_MASK (0xFF << 0)
+#define DA7219_FREQ_MAX 0xFFFF
+
+/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7219_FREQ1_U_SHIFT 0
+#define DA7219_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7219_FREQ2_L_SHIFT 0
+#define DA7219_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7219_FREQ2_U_SHIFT 0
+#define DA7219_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7219_TONE_GEN_ON_PER = 0xBB */
+#define DA7219_BEEP_ON_PER_SHIFT 0
+#define DA7219_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7219_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7219_TONE_GEN_OFF_PER = 0xBC */
+#define DA7219_BEEP_OFF_PER_SHIFT 0
+#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0)
+
+/* DA7219_SYSTEM_STATUS = 0xE0 */
+#define DA7219_SC1_BUSY_SHIFT 0
+#define DA7219_SC1_BUSY_MASK (0x1 << 0)
+#define DA7219_SC2_BUSY_SHIFT 1
+#define DA7219_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7219_SYSTEM_ACTIVE = 0xFD */
+#define DA7219_SYSTEM_ACTIVE_SHIFT 0
+#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7219_NO_INVERT 0
+#define DA7219_INVERT 1
+
+/* Byte related defines */
+#define DA7219_BYTE_SHIFT 8
+#define DA7219_BYTE_MASK 0xFF
+
+/* PLL Output Frequencies */
+#define DA7219_PLL_FREQ_OUT_90316 90316800
+#define DA7219_PLL_FREQ_OUT_98304 98304000
+
+/* PLL Frequency Dividers */
+#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1
+#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2
+#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
+#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
+#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+
+/* SRM */
+#define DA7219_SRM_CHECK_RETRIES 8
+
+enum da7219_clk_src {
+ DA7219_CLKSRC_MCLK = 0,
+ DA7219_CLKSRC_MCLK_SQR,
+};
+
+enum da7219_sys_clk {
+ DA7219_SYSCLK_MCLK = 0,
+ DA7219_SYSCLK_PLL,
+ DA7219_SYSCLK_PLL_SRM,
+ DA7219_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7219_supplies {
+ DA7219_SUPPLY_VDD = 0,
+ DA7219_SUPPLY_VDDMIC,
+ DA7219_SUPPLY_VDDIO,
+ DA7219_NUM_SUPPLIES,
+};
+
+struct da7219_aad_priv;
+
+/* Private data */
+struct da7219_priv {
+ struct da7219_aad_priv *aad;
+ struct da7219_pdata *pdata;
+
+ struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct mutex lock;
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ int clk_src;
+
+ bool master;
+ bool alc_en;
+};
+
+#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 6a091016e0fc..969e337dc17c 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.integer.value[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
deleted file mode 100644
index bd42ad34e004..000000000000
--- a/sound/soc/codecs/hdmi.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-codec"
-
-static const struct snd_soc_dapm_widget hdmi_widgets[] = {
- SND_SOC_DAPM_INPUT("RX"),
- SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai = {
- .name = "hdmi-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
- .sig_bits = 24,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
- { .compatible = "linux,hdmi-audio", },
- { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-static struct snd_soc_codec_driver hdmi_codec = {
- .dapm_widgets = hdmi_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
- .ignore_pmdown_time = true,
-};
-
-static int hdmi_codec_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
- &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
- },
-
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644
index 000000000000..7fc7b4e3f444
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,1309 @@
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#include "nau8825.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+struct nau8825_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8825_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+ { 48, 0xd },
+ { 96, 0xe },
+ { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8825_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+static const struct reg_default nau8825_reg_defaults[] = {
+ { NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_CLK_DIVIDER, 0x0050 },
+ { NAU8825_REG_FLL1, 0x0 },
+ { NAU8825_REG_FLL2, 0x3126 },
+ { NAU8825_REG_FLL3, 0x0008 },
+ { NAU8825_REG_FLL4, 0x0010 },
+ { NAU8825_REG_FLL5, 0x0 },
+ { NAU8825_REG_FLL6, 0x6000 },
+ { NAU8825_REG_FLL_VCO_RSV, 0xf13c },
+ { NAU8825_REG_HSD_CTRL, 0x000c },
+ { NAU8825_REG_JACK_DET_CTRL, 0x0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0 },
+ { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff },
+ { NAU8825_REG_SAR_CTRL, 0x0015 },
+ { NAU8825_REG_KEYDET_CTRL, 0x0110 },
+ { NAU8825_REG_VDET_THRESHOLD_1, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_2, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_3, 0x0 },
+ { NAU8825_REG_VDET_THRESHOLD_4, 0x0 },
+ { NAU8825_REG_GPIO34_CTRL, 0x0 },
+ { NAU8825_REG_GPIO12_CTRL, 0x0 },
+ { NAU8825_REG_TDM_CTRL, 0x0 },
+ { NAU8825_REG_I2S_PCM_CTRL1, 0x000b },
+ { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 },
+ { NAU8825_REG_LEFT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 },
+ { NAU8825_REG_BIQ_CTRL, 0x0 },
+ { NAU8825_REG_BIQ_COF1, 0x0 },
+ { NAU8825_REG_BIQ_COF2, 0x0 },
+ { NAU8825_REG_BIQ_COF3, 0x0 },
+ { NAU8825_REG_BIQ_COF4, 0x0 },
+ { NAU8825_REG_BIQ_COF5, 0x0 },
+ { NAU8825_REG_BIQ_COF6, 0x0 },
+ { NAU8825_REG_BIQ_COF7, 0x0 },
+ { NAU8825_REG_BIQ_COF8, 0x0 },
+ { NAU8825_REG_BIQ_COF9, 0x0 },
+ { NAU8825_REG_BIQ_COF10, 0x0 },
+ { NAU8825_REG_ADC_RATE, 0x0010 },
+ { NAU8825_REG_DAC_CTRL1, 0x0001 },
+ { NAU8825_REG_DAC_CTRL2, 0x0 },
+ { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 },
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+ { NAU8825_REG_MUTE_CTRL, 0x0 },
+ { NAU8825_REG_HSVOL_CTRL, 0x0 },
+ { NAU8825_REG_DACL_CTRL, 0x02cf },
+ { NAU8825_REG_DACR_CTRL, 0x00cf },
+ { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff },
+ { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 },
+ { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 },
+ { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 },
+ { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 },
+ { NAU8825_REG_IMM_MODE_CTRL, 0x0 },
+ { NAU8825_REG_CLASSG_CTRL, 0x0 },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
+ { NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_BIAS_ADJ, 0x0 },
+ { NAU8825_REG_TRIM_SETTINGS, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0 },
+ { NAU8825_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8825_REG_RDAC, 0x0008 },
+ { NAU8825_REG_MIC_BIAS, 0x0006 },
+ { NAU8825_REG_BOOST, 0x0 },
+ { NAU8825_REG_FEPGA, 0x0 },
+ { NAU8825_REG_POWER_UP_CONTROL, 0x0 },
+ { NAU8825_REG_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
+ case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+ case NAU8825_REG_INTERRUPT_MASK:
+ case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
+ case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+ case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+ case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+ case NAU8825_REG_IMM_MODE_CTRL:
+ case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+ case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_BIAS_ADJ:
+ case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+ case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+ case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+ case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8825_REG_RESET:
+ case NAU8825_REG_IRQ_STATUS:
+ case NAU8825_REG_INT_CLR_KEY_STATUS:
+ case NAU8825_REG_IMM_RMS_L:
+ case NAU8825_REG_IMM_RMS_R:
+ case NAU8825_REG_I2C_DEVICE_ID:
+ case NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
+ case NAU8825_REG_GENERAL_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char * const nau8825_adc_decimation[] = {
+ "32", "64", "128", "256"
+};
+
+static const struct soc_enum nau8825_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT,
+ ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation);
+
+static const char * const nau8825_dac_oversampl[] = {
+ "64", "256", "128", "", "32"
+};
+
+static const struct soc_enum nau8825_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT,
+ ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400);
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 0, 0xff, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+ 12, 8, 0x0f, 0, sidetone_vol_tlv),
+ SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL,
+ 6, 0, 0x3f, 1, dac_vol_tlv),
+ SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL,
+ 8, 37, 0, fepga_gain_tlv),
+ SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL,
+ 0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+ SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+};
+
+/* DAC Mux 0x33[9] and 0x34[9] */
+static const char * const nau8825_dac_src[] = {
+ "DACL", "DACR",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacl_enum, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8825_dacr_enum, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src);
+
+static const struct snd_kcontrol_new nau8825_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum);
+
+static const struct snd_kcontrol_new nau8825_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
+ 15, 1),
+
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
+
+ SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
+ 0),
+
+ /* ADC for button press detection */
+ SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_ADC_EN_SFT, 0),
+
+ SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
+ SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
+ SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR_SFT, 0),
+ SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACL_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
+
+ SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+ NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+ NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+ NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+ {"Frontend PGA", NULL, "MIC"},
+ {"ADC", NULL, "Frontend PGA"},
+ {"ADC", NULL, "ADC Clock"},
+ {"ADC", NULL, "ADC Power"},
+ {"AIFTX", NULL, "ADC"},
+
+ {"DDACL", NULL, "Playback"},
+ {"DDACR", NULL, "Playback"},
+ {"DDACL", NULL, "DDAC Clock"},
+ {"DDACR", NULL, "DDAC Clock"},
+ {"DACL Mux", "DACL", "DDACL"},
+ {"DACL Mux", "DACR", "DDACR"},
+ {"DACR Mux", "DACL", "DDACL"},
+ {"DACR Mux", "DACR", "DDACR"},
+ {"HP amp L", NULL, "DACL Mux"},
+ {"HP amp R", NULL, "DACR Mux"},
+ {"HP amp L", NULL, "HP amp power"},
+ {"HP amp R", NULL, "HP amp power"},
+ {"ADACL", NULL, "HP amp L"},
+ {"ADACR", NULL, "HP amp R"},
+ {"ADACL", NULL, "ADACL Clock"},
+ {"ADACR", NULL, "ADACR Clock"},
+ {"Output Driver L Stage 1", NULL, "ADACL"},
+ {"Output Driver R Stage 1", NULL, "ADACR"},
+ {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+ {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+ {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+ {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+ {"Output DACL", NULL, "Output Driver L Stage 3"},
+ {"Output DACR", NULL, "Output Driver R Stage 3"},
+ {"HPOL", NULL, "Output DACL"},
+ {"HPOR", NULL, "Output DACR"},
+ {"HPOL", NULL, "Charge Pump"},
+ {"HPOR", NULL, "Charge Pump"},
+};
+
+static int nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8825_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8825_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8825_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8825_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK, val_len);
+
+ return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8825_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8825_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8825_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8825_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8825_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8825_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+ NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
+ NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+ ctrl1_val);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+ .hw_params = nau8825_hw_params,
+ .set_fmt = nau8825_set_dai_fmt,
+};
+
+#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8825_dai = {
+ .name = "nau8825-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = NAU8825_RATES,
+ .formats = NAU8825_FORMATS,
+ },
+ .ops = &nau8825_dai_ops,
+};
+
+/**
+ * nau8825_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct regmap *regmap = nau8825->regmap;
+
+ nau8825->jack = jack;
+
+ /* Ground HP Outputs[1:0], needed for headset auto detection
+ * Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
+ */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
+
+
+static bool nau8825_is_jack_inserted(struct regmap *regmap)
+{
+ int status;
+
+ regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
+ return !(status & NAU8825_GPIO2JD1);
+}
+
+static void nau8825_restart_jack_detection(struct regmap *regmap)
+{
+ /* this will restart the entire jack detection process including MIC/GND
+ * switching and create interrupts. We have to go from 0 to 1 and back
+ * to 0 to restart.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_RESTART, 0);
+}
+
+static void nau8825_eject_jack(struct nau8825 *nau8825)
+{
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ struct regmap *regmap = nau8825->regmap;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+ /* ground HPL/HPR, MICGRND1/2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int nau8825_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+static int nau8825_jack_insert(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ struct snd_soc_dapm_context *dapm = nau8825->dapm;
+ int jack_status_reg, mic_detected;
+ int type = 0;
+
+ regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
+ mic_detected = (jack_status_reg >> 10) & 3;
+
+ switch (mic_detected) {
+ case 0:
+ /* no mic */
+ type = SND_JACK_HEADPHONE;
+ break;
+ case 1:
+ dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 1 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKR2);
+ /* Attach SARADC to MICGND1 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKR2);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ case 2:
+ case 3:
+ dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+ type = SND_JACK_HEADSET;
+
+ /* Unground MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+ 2 << 2);
+ /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+ NAU8825_MICBIAS_JKSLV);
+ /* Attach SARADC to MICGND2 */
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_INPUT_MASK,
+ NAU8825_SAR_INPUT_JKSLV);
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+ break;
+ }
+
+ if (type & SND_JACK_HEADPHONE) {
+ /* Unground HPL/R */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
+ }
+
+ return type;
+}
+
+#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8825_interrupt(int irq, void *data)
+{
+ struct nau8825 *nau8825 = (struct nau8825 *)data;
+ struct regmap *regmap = nau8825->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+
+ if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
+ NAU8825_JACK_EJECTION_DETECTED) {
+
+ nau8825_eject_jack(nau8825);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK;
+ } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) {
+ int key_status;
+
+ regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS,
+ &key_status);
+
+ /* upper 8 bits of the register are for short pressed keys,
+ * lower 8 bits - for long pressed buttons
+ */
+ nau8825->button_pressed = nau8825_button_decode(
+ key_status >> 8);
+
+ event |= nau8825->button_pressed;
+ event_mask |= NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) {
+ event_mask = NAU8825_BUTTONS;
+ clear_irq = NAU8825_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
+ if (nau8825_is_jack_inserted(regmap)) {
+ event |= nau8825_jack_insert(nau8825);
+ } else {
+ dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
+ nau8825_eject_jack(nau8825);
+ }
+
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8825->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static void nau8825_setup_buttons(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_TRACKING_GAIN_MASK,
+ nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_COMPARE_TIME_MASK,
+ nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+ NAU8825_SAR_SAMPLING_TIME_MASK,
+ nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_LEVELS_NR_MASK,
+ (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_HYSTERESIS_MASK,
+ nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+ NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK,
+ nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1,
+ (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2,
+ (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3,
+ (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]);
+ regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4,
+ (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]);
+
+ /* Enable short press and release interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN,
+ 0);
+}
+
+static void nau8825_init_regs(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable Bias/Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN);
+
+ /* VMID Tieoff */
+ regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_VMID_SEL_MASK,
+ nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8825_REG_BOOST,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+ NAU8825_SHORT_SHUTDOWN_EN);
+
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_OUTPUT_EN,
+ nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_EN,
+ nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN);
+ regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+ NAU8825_JKDET_PULL_UP,
+ nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_POLARITY,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY);
+
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_INSERT_DEBOUNCE_MASK,
+ nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_EJECT_DEBOUNCE_MASK,
+ nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+
+ /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
+
+ regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+ NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+
+ if (nau8825->sar_threshold_num)
+ nau8825_setup_buttons(nau8825);
+
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
+ NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+}
+
+static const struct regmap_config nau8825_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 16,
+
+ .max_register = NAU8825_REG_MAX,
+ .readable_reg = nau8825_readable_reg,
+ .writeable_reg = nau8825_writeable_reg,
+ .volatile_reg = nau8825_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8825_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
+};
+
+static int nau8825_codec_probe(struct snd_soc_codec *codec)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ nau8825->dapm = dapm;
+
+ /* The interrupt clock is gated by x1[10:8],
+ * one of them needs to be enabled all the time for
+ * interrupts to happen.
+ */
+ snd_soc_dapm_force_enable_pin(dapm, "DDACR");
+ snd_soc_dapm_sync(dapm);
+
+ /* Unmask interruptions. Handler uses dapm object so we can enable
+ * interruptions only after dapm is fully initialized.
+ */
+ regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+ nau8825_restart_jack_detection(nau8825->regmap);
+
+ return 0;
+}
+
+/**
+ * nau8825_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
+ struct nau8825_fll *fll_param)
+{
+ u64 fvco;
+ unsigned int fref, i;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(mclk_src_scaling))
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[i].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8825_fll_apply(struct nau8825 *nau8825,
+ struct nau8825_fll *fll_param)
+{
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
+ NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+ /* select divided VCO input */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+ /* FLL sigma delta modulator enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ struct nau8825_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8825_fll_apply(nau8825, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ return 0;
+}
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
+ unsigned int freq)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ switch (clk_id) {
+ case NAU8825_CLK_MCLK:
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+
+ /* We selected MCLK source but the clock itself managed externally */
+ if (!nau8825->mclk)
+ break;
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ nau8825->mclk_freq = freq;
+
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ }
+
+ break;
+ case NAU8825_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
+ default:
+ dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+ return 0;
+}
+
+static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ return nau8825_configure_sysclk(nau8825, clk_id, freq);
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ if (nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ ret = regcache_sync(nau8825->regmap);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (nau8825->mclk_freq)
+ clk_disable_unprepare(nau8825->mclk);
+
+ regcache_mark_dirty(nau8825->regmap);
+ break;
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver nau8825_codec_driver = {
+ .probe = nau8825_codec_probe,
+ .set_sysclk = nau8825_set_sysclk,
+ .set_pll = nau8825_set_pll,
+ .set_bias_level = nau8825_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = nau8825_controls,
+ .num_controls = ARRAY_SIZE(nau8825_controls),
+ .dapm_widgets = nau8825_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+ .dapm_routes = nau8825_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static void nau8825_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+}
+
+static void nau8825_print_device_properties(struct nau8825 *nau8825)
+{
+ int i;
+ struct device *dev = nau8825->dev;
+
+ dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable);
+ dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable);
+ dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up);
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num);
+ for (i = 0; i < nau8825->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%d\n", i,
+ nau8825->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce);
+ dev_dbg(dev, "jack-insert-debounce: %d\n",
+ nau8825->jack_insert_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8825->jack_eject_debounce);
+}
+
+static int nau8825_read_device_properties(struct device *dev,
+ struct nau8825 *nau8825) {
+
+ nau8825->jkdet_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-enable");
+ nau8825->jkdet_pull_enable = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-enable");
+ nau8825->jkdet_pull_up = device_property_read_bool(dev,
+ "nuvoton,jkdet-pull-up");
+ device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8825->jkdet_polarity);
+ device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8825->micbias_voltage);
+ device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8825->vref_impedance);
+ device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8825->sar_threshold_num);
+ device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8825->sar_threshold, nau8825->sar_threshold_num);
+ device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8825->sar_hysteresis);
+ device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8825->sar_voltage);
+ device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8825->sar_compare_time);
+ device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8825->sar_sampling_time);
+ device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8825->key_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+ &nau8825->jack_insert_debounce);
+ device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8825->jack_eject_debounce);
+
+ nau8825->mclk = devm_clk_get(dev, "mclk");
+ if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+ /* The MCLK is managed externally or not used at all */
+ nau8825->mclk = NULL;
+ dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ } else if (IS_ERR(nau8825->mclk)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_setup_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+ int ret;
+
+ /* IRQ Output Enable */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+ /* Enable DDACR needed for interrupts
+ * It is the same as force_enable_pin("DDACR") we do later
+ */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
+
+ /* Chip needs one FSCLK cycle in order to generate interrupts,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
+ nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8825", nau8825);
+
+ if (ret) {
+ dev_err(nau8825->dev, "Cannot request irq %d (%d)\n",
+ nau8825->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
+ int ret, value;
+
+ if (!nau8825) {
+ nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL);
+ if (!nau8825)
+ return -ENOMEM;
+ ret = nau8825_read_device_properties(dev, nau8825);
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, nau8825);
+
+ nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config);
+ if (IS_ERR(nau8825->regmap))
+ return PTR_ERR(nau8825->regmap);
+ nau8825->dev = dev;
+ nau8825->irq = i2c->irq;
+
+ nau8825_print_device_properties(nau8825);
+
+ nau8825_reset_chip(nau8825->regmap);
+ ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8825: %d\n",
+ ret);
+ return ret;
+ }
+ if ((value & NAU8825_SOFTWARE_ID_MASK) !=
+ NAU8825_SOFTWARE_ID_NAU8825) {
+ dev_err(dev, "Not a NAU8825 chip\n");
+ return -ENODEV;
+ }
+
+ nau8825_init_regs(nau8825);
+
+ if (i2c->irq)
+ nau8825_setup_irq(nau8825);
+
+ return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+ &nau8825_dai, 1);
+}
+
+static int nau8825_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id nau8825_i2c_ids[] = {
+ { "nau8825", 0 },
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8825_of_ids[] = {
+ { .compatible = "nuvoton,nau8825", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8825_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8825_acpi_match[] = {
+ { "10508825", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+#endif
+
+static struct i2c_driver nau8825_driver = {
+ .driver = {
+ .name = "nau8825",
+ .of_match_table = of_match_ptr(nau8825_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ },
+ .probe = nau8825_i2c_probe,
+ .remove = nau8825_i2c_remove,
+ .id_table = nau8825_i2c_ids,
+};
+module_i2c_driver(nau8825_driver);
+
+MODULE_DESCRIPTION("ASoC nau8825 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 000000000000..dff8edb83bfd
--- /dev/null
+++ b/sound/soc/codecs/nau8825.h
@@ -0,0 +1,341 @@
+/*
+ * NAU8825 ALSA SoC audio driver
+ *
+ * Copyright 2015 Google Inc.
+ * Author: Anatol Pomozov <anatol.pomozov@chrominium.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 __NAU8825_H__
+#define __NAU8825_H__
+
+#define NAU8825_REG_RESET 0x00
+#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_CLK_DIVIDER 0x03
+#define NAU8825_REG_FLL1 0x04
+#define NAU8825_REG_FLL2 0x05
+#define NAU8825_REG_FLL3 0x06
+#define NAU8825_REG_FLL4 0x07
+#define NAU8825_REG_FLL5 0x08
+#define NAU8825_REG_FLL6 0x09
+#define NAU8825_REG_FLL_VCO_RSV 0x0a
+#define NAU8825_REG_HSD_CTRL 0x0c
+#define NAU8825_REG_JACK_DET_CTRL 0x0d
+#define NAU8825_REG_INTERRUPT_MASK 0x0f
+#define NAU8825_REG_IRQ_STATUS 0x10
+#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11
+#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12
+#define NAU8825_REG_SAR_CTRL 0x13
+#define NAU8825_REG_KEYDET_CTRL 0x14
+#define NAU8825_REG_VDET_THRESHOLD_1 0x15
+#define NAU8825_REG_VDET_THRESHOLD_2 0x16
+#define NAU8825_REG_VDET_THRESHOLD_3 0x17
+#define NAU8825_REG_VDET_THRESHOLD_4 0x18
+#define NAU8825_REG_GPIO34_CTRL 0x19
+#define NAU8825_REG_GPIO12_CTRL 0x1a
+#define NAU8825_REG_TDM_CTRL 0x1b
+#define NAU8825_REG_I2S_PCM_CTRL1 0x1c
+#define NAU8825_REG_I2S_PCM_CTRL2 0x1d
+#define NAU8825_REG_LEFT_TIME_SLOT 0x1e
+#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f
+#define NAU8825_REG_BIQ_CTRL 0x20
+#define NAU8825_REG_BIQ_COF1 0x21
+#define NAU8825_REG_BIQ_COF2 0x22
+#define NAU8825_REG_BIQ_COF3 0x23
+#define NAU8825_REG_BIQ_COF4 0x24
+#define NAU8825_REG_BIQ_COF5 0x25
+#define NAU8825_REG_BIQ_COF6 0x26
+#define NAU8825_REG_BIQ_COF7 0x27
+#define NAU8825_REG_BIQ_COF8 0x28
+#define NAU8825_REG_BIQ_COF9 0x29
+#define NAU8825_REG_BIQ_COF10 0x2a
+#define NAU8825_REG_ADC_RATE 0x2b
+#define NAU8825_REG_DAC_CTRL1 0x2c
+#define NAU8825_REG_DAC_CTRL2 0x2d
+#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f
+#define NAU8825_REG_ADC_DGAIN_CTRL 0x30
+#define NAU8825_REG_MUTE_CTRL 0x31
+#define NAU8825_REG_HSVOL_CTRL 0x32
+#define NAU8825_REG_DACL_CTRL 0x33
+#define NAU8825_REG_DACR_CTRL 0x34
+#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38
+#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39
+#define NAU8825_REG_ADC_DRC_SLOPES 0x3a
+#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b
+#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45
+#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46
+#define NAU8825_REG_DAC_DRC_SLOPES 0x47
+#define NAU8825_REG_DAC_DRC_ATKDCY 0x48
+#define NAU8825_REG_IMM_MODE_CTRL 0x4c
+#define NAU8825_REG_IMM_RMS_L 0x4d
+#define NAU8825_REG_IMM_RMS_R 0x4e
+#define NAU8825_REG_CLASSG_CTRL 0x50
+#define NAU8825_REG_OPT_EFUSE_CTRL 0x51
+#define NAU8825_REG_MISC_CTRL 0x55
+#define NAU8825_REG_I2C_DEVICE_ID 0x58
+#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
+#define NAU8825_REG_BIAS_ADJ 0x66
+#define NAU8825_REG_TRIM_SETTINGS 0x68
+#define NAU8825_REG_ANALOG_CONTROL_1 0x69
+#define NAU8825_REG_ANALOG_CONTROL_2 0x6a
+#define NAU8825_REG_ANALOG_ADC_1 0x71
+#define NAU8825_REG_ANALOG_ADC_2 0x72
+#define NAU8825_REG_RDAC 0x73
+#define NAU8825_REG_MIC_BIAS 0x74
+#define NAU8825_REG_BOOST 0x76
+#define NAU8825_REG_FEPGA 0x77
+#define NAU8825_REG_POWER_UP_CONTROL 0x7f
+#define NAU8825_REG_CHARGE_PUMP 0x80
+#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
+#define NAU8825_REG_GENERAL_STATUS 0x82
+#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+
+/* ENA_CTRL (0x1) */
+#define NAU8825_ENABLE_DACR_SFT 10
+#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
+#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_SAR_SFT 1
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8825_CLK_SRC_SFT 15
+#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
+
+/* FLL1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+
+/* FLL4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
+
+/* FLL5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
+
+/* FLL6 (0x9) */
+#define NAU8825_DCO_EN_MASK (0x1 << 15)
+#define NAU8825_DCO_EN (0x1 << 15)
+#define NAU8825_DCO_DIS (0x0 << 15)
+#define NAU8825_SDM_EN_MASK (0x1 << 14)
+#define NAU8825_SDM_EN (0x1 << 14)
+#define NAU8825_SDM_DIS (0x0 << 14)
+
+/* HSD_CTRL (0xc) */
+#define NAU8825_HSD_AUTO_MODE (1 << 6)
+/* 0 - short to GND, 1 - open */
+#define NAU8825_SPKR_DWN1R (1 << 1)
+#define NAU8825_SPKR_DWN1L (1 << 0)
+
+/* JACK_DET_CTRL (0xd) */
+#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
+#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
+#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
+#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
+#define NAU8825_IRQ_EJECT_EN (1 << 2)
+
+/* IRQ_STATUS (0x10) */
+#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
+#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9)
+#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8)
+#define NAU8825_KEY_IRQ_MASK (0x7 << 5)
+#define NAU8825_KEY_RELEASE_IRQ (1 << 7)
+#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6)
+#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5)
+#define NAU8825_MIC_DETECTION_IRQ (1 << 4)
+#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
+#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
+#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0)
+#define NAU8825_JACK_INSERTION_DETECTED (1 << 0)
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
+#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+
+/* SAR_CTRL (0x13) */
+#define NAU8825_SAR_ADC_EN_SFT 12
+#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT)
+#define NAU8825_SAR_INPUT_MASK (1 << 11)
+#define NAU8825_SAR_INPUT_JKSLV (1 << 11)
+#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
+#define NAU8825_SAR_TRACKING_GAIN_SFT 8
+#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_COMPARE_TIME_SFT 2
+#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8825_SAR_SAMPLING_TIME_SFT 0
+#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* KEYDET_CTRL (0x14) */
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8825_KEYDET_LEVELS_NR_SFT 8
+#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8825_KEYDET_HYSTERESIS_SFT 0
+#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8825_I2S_BP_SFT 7
+#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_PCMB_SFT 6
+#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_DL_SFT 2
+#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DF_SFT 0
+#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT)
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_MS_SFT 3
+#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SYNC_DOWN_SFT 0
+#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8825_ADC_SYNC_DOWN_32 0
+#define NAU8825_ADC_SYNC_DOWN_64 1
+#define NAU8825_ADC_SYNC_DOWN_128 2
+#define NAU8825_ADC_SYNC_DOWN_256 3
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8825_DAC_CLIP_OFF (1 << 7)
+#define NAU8825_DAC_OVERSAMPLE_SFT 0
+#define NAU8825_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8825_DAC_OVERSAMPLE_64 0
+#define NAU8825_DAC_OVERSAMPLE_256 1
+#define NAU8825_DAC_OVERSAMPLE_128 2
+#define NAU8825_DAC_OVERSAMPLE_32 4
+
+/* MUTE_CTRL (0x31) */
+#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
+#define NAU8825_DAC_SOFT_MUTE (1 << 9)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8825_HP_MUTE (1 << 15)
+
+/* DACL_CTRL (0x33) */
+#define NAU8825_DACL_CH_SEL_SFT 9
+
+/* DACR_CTRL (0x34) */
+#define NAU8825_DACR_CH_SEL_SFT 9
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8825_GPIO2JD1 (1 << 7)
+#define NAU8825_SOFTWARE_ID_MASK 0x3
+#define NAU8825_SOFTWARE_ID_NAU8825 0x0
+
+/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_VMID (1 << 6)
+#define NAU8825_BIAS_VMID_SEL_SFT 4
+#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
+#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
+#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8)
+#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8)
+#define NAU8825_ADC_VREFSEL_VMID (1 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
+#define NAU8825_POWERUP_ADCL (1 << 6)
+
+/* MIC_BIAS (0x74) */
+#define NAU8825_MICBIAS_JKSLV (1 << 14)
+#define NAU8825_MICBIAS_JKR2 (1 << 12)
+#define NAU8825_MICBIAS_POWERUP_SFT 8
+#define NAU8825_MICBIAS_VOLTAGE_SFT 0
+#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8825_PRECHARGE_DIS (1 << 13)
+#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_G_DIS (1 << 8)
+#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8825_POWERUP_INTEGR_R (1 << 5)
+#define NAU8825_POWERUP_INTEGR_L (1 << 4)
+#define NAU8825_POWERUP_DRV_IN_R (1 << 3)
+#define NAU8825_POWERUP_DRV_IN_L (1 << 2)
+#define NAU8825_POWERUP_HP_DRV_R (1 << 1)
+#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8825_JAMNODCLOW (1 << 10)
+#define NAU8825_POWER_DOWN_DACR (1 << 9)
+#define NAU8825_POWER_DOWN_DACL (1 << 8)
+#define NAU8825_CHANRGE_PUMP_EN (1 << 5)
+
+
+/* System Clock Source */
+enum {
+ NAU8825_CLK_MCLK = 0,
+ NAU8825_CLK_INTERNAL,
+};
+
+struct nau8825 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct clk *mclk;
+ int irq;
+ int mclk_freq; /* 0 - mclk is disabled */
+ int button_pressed;
+ int micbias_voltage;
+ int vref_impedance;
+ bool jkdet_enable;
+ bool jkdet_pull_enable;
+ bool jkdet_pull_up;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_insert_debounce;
+ int jack_eject_debounce;
+};
+
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
+
+#endif /* __NAU8825_H__ */
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 1fbdb4fe0ca9..af2ed774b552 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -37,7 +37,7 @@
#define RT288_VENDOR_ID 0x10ec0288
struct rt286_priv {
- const struct reg_default *index_cache;
+ struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
struct snd_soc_codec *codec;
@@ -1160,7 +1160,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
- rt286->index_cache = rt286_index_def;
+ rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def,
+ sizeof(rt286_index_def), GFP_KERNEL);
+ if (!rt286->index_cache)
+ return -ENOMEM;
+
rt286->index_cache_size = INDEX_CACHE_SIZE;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 2a6c2ba0f0d8..603e9334b971 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -49,24 +49,24 @@ struct rt298_priv {
};
static struct reg_default rt298_index_def[] = {
- { 0x01, 0xaaaa },
- { 0x02, 0x8aaa },
+ { 0x01, 0xa5a8 },
+ { 0x02, 0x8e95 },
{ 0x03, 0x0002 },
- { 0x04, 0xaf01 },
- { 0x08, 0x000d },
- { 0x09, 0xd810 },
- { 0x0a, 0x0120 },
+ { 0x04, 0xaf67 },
+ { 0x08, 0x200f },
+ { 0x09, 0xd010 },
+ { 0x0a, 0x0100 },
{ 0x0b, 0x0000 },
{ 0x0d, 0x2800 },
- { 0x0f, 0x0000 },
- { 0x19, 0x0a17 },
+ { 0x0f, 0x0022 },
+ { 0x19, 0x0217 },
{ 0x20, 0x0020 },
{ 0x33, 0x0208 },
{ 0x46, 0x0300 },
- { 0x49, 0x0004 },
- { 0x4f, 0x50e9 },
- { 0x50, 0x2000 },
- { 0x63, 0x2902 },
+ { 0x49, 0x4004 },
+ { 0x4f, 0x50c9 },
+ { 0x50, 0x3000 },
+ { 0x63, 0x1b02 },
{ 0x67, 0x1111 },
{ 0x68, 0x1016 },
{ 0x69, 0x273f },
@@ -1213,7 +1213,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
mdelay(10);
if (!rt298->pdata.gpio2_en)
- regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x4000);
+ regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40);
else
regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 4972bf3efa91..28132375e427 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -42,6 +42,8 @@
#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+#define RT5645_HWEQ_NUM 57
+
static const struct regmap_range_cfg rt5645_ranges[] = {
{
.name = "PR",
@@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
+struct rt5645_eq_param_s {
+ unsigned short reg;
+ unsigned short val;
+};
+
static const char *const rt5645_supply_names[] = {
"avdd",
"cpvdd",
@@ -240,6 +247,7 @@ struct rt5645_priv {
struct snd_soc_jack *btn_jack;
struct delayed_work jack_detect_work;
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+ struct rt5645_eq_param_s *eq_param;
int codec_type;
int sysclk;
@@ -469,6 +477,94 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
);
+/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */
+static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv,
+ 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
+);
+
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
+
+ return 0;
+}
+
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+ eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+ }
+
+ return 0;
+}
+
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+ if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+ (reg == RT5645_EQ_CTRL2))
+ return true;
+
+ return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ struct rt5645_eq_param_s *eq_param =
+ (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+ eq_param[i].val = be16_to_cpu(eq_param[i].val);
+ }
+
+ /* The final setting of the table should be RT5645_EQ_CTRL2 */
+ for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+ if (eq_param[i].reg == 0)
+ continue;
+ else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+ return 0;
+ else
+ break;
+ }
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (!rt5645_validate_hweq(eq_param[i].reg) &&
+ eq_param[i].reg != 0)
+ return 0;
+ else if (eq_param[i].reg == 0)
+ break;
+ }
+
+ memcpy(rt5645->eq_param, eq_param,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
+
+ return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt5645_hweq_info, \
+ .get = rt5645_hweq_get, \
+ .put = rt5645_hweq_put \
+}
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -476,6 +572,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* ClassD modulator Speaker Gain Ratio */
+ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
+ RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv),
+
/* Headphone Output Volume */
SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
@@ -519,16 +619,17 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
- SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1,
+ SOC_DOUBLE_TLV("ADC Boost Capture Volume", RT5645_ADC_BST_VOL1,
RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0,
adc_bst_tlv),
- SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1,
- RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0,
+ SOC_DOUBLE_TLV("Mono ADC Boost Capture Volume", RT5645_ADC_BST_VOL2,
+ RT5645_MONO_ADC_L_BST_SFT, RT5645_MONO_ADC_R_BST_SFT, 3, 0,
adc_bst_tlv),
/* I2S2 function select */
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
+ RT5645_HWEQ("Speaker HWEQ"),
};
/**
@@ -619,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
+static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+{
+ struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+ if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+ regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+ rt5645->eq_param[i].val);
+ else
+ break;
+ }
+
+ return 0;
+}
+
/**
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
* @codec: SoC audio codec device.
@@ -732,14 +849,14 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
static const struct snd_kcontrol_new rt5645_dac_l_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
RT5645_M_ADCMIX_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
RT5645_M_DAC1_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5645_dac_r_mix[] = {
SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
RT5645_M_ADCMIX_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
RT5645_M_DAC1_R_SFT, 1, 1),
};
@@ -1381,7 +1498,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
- mdelay(5);
+ msleep(40);
rt5645->hp_on = true;
} else {
/* depop parameters */
@@ -1523,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ rt5645_enable_hweq(codec);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L,
@@ -1531,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L, 0);
@@ -2733,6 +2852,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
+ if (rt5645->en_button_func &&
+ snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(0));
break;
case SND_SOC_BIAS_OFF:
@@ -2829,13 +2952,15 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_sync(dapm);
rt5645->jack_type = SND_JACK_HEADPHONE;
}
-
- snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001);
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
} else { /* jack out */
rt5645->jack_type = 0;
+ regmap_update_bits(rt5645->regmap, RT5645_HP_VOL,
+ RT5645_L_MUTE | RT5645_R_MUTE,
+ RT5645_L_MUTE | RT5645_R_MUTE);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2,
RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
@@ -2848,6 +2973,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_disable_pin(dapm, "LDO2");
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
+ if (rt5645->pdata.jd_invert)
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
}
return rt5645->jack_type;
@@ -2880,8 +3008,6 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec,
rt5645->en_button_func = true;
regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
- regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
- RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
}
@@ -3041,6 +3167,9 @@ static int rt5645_probe(struct snd_soc_codec *codec)
snd_soc_dapm_sync(dapm);
}
+ rt5645->eq_param = devm_kzalloc(codec->dev,
+ RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+
return 0;
}
@@ -3101,7 +3230,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.rates = RT5645_STEREO_RATES,
.formats = RT5645_FORMATS,
},
@@ -3205,9 +3334,49 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
},
},
+ {
+ .ident = "Google Ultima",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
+ },
+ },
+ {
+ .ident = "Google Reks",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
+ },
+ },
{ }
};
+static struct rt5645_platform_data buddy_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+ .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+ .jd_mode = 3,
+ .jd_invert = true,
+};
+
+static int buddy_quirk_cb(const struct dmi_system_id *id)
+{
+ rt5645_pdata = &buddy_platform_data;
+
+ return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_broadwell[] = {
+ {
+ .ident = "Chrome Buddy",
+ .callback = buddy_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+ },
+ },
+ { }
+};
+
+
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
{
rt5645->pdata.in2_diff = device_property_read_bool(dev,
@@ -3240,7 +3409,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
- else if (dmi_check_system(dmi_platform_intel_braswell))
+ else if (dmi_check_system(dmi_platform_intel_braswell) ||
+ dmi_check_system(dmi_platform_intel_broadwell))
rt5645->pdata = *rt5645_pdata;
else
rt5645_parse_dt(rt5645, &i2c->dev);
@@ -3468,6 +3638,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
RT5645_CBJ_MN_JD);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN,
0);
+ msleep(20);
+ regmap_write(rt5645->regmap, RT5645_RESET, 0);
}
static struct i2c_driver rt5645_i2c_driver = {
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 0e4cfc6ac649..093e46d559fb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -39,8 +39,8 @@
#define RT5645_STO1_ADC_DIG_VOL 0x1c
#define RT5645_MONO_ADC_DIG_VOL 0x1d
#define RT5645_ADC_BST_VOL1 0x1e
-/* Mixer - D-D */
#define RT5645_ADC_BST_VOL2 0x20
+/* Mixer - D-D */
#define RT5645_STO1_ADC_MIXER 0x27
#define RT5645_MONO_ADC_MIXER 0x28
#define RT5645_AD_DA_MIXER 0x29
@@ -315,12 +315,14 @@
#define RT5645_STO1_ADC_R_BST_SFT 12
#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10)
#define RT5645_STO1_ADC_COMP_SFT 10
-#define RT5645_STO2_ADC_L_BST_MASK (0x3 << 8)
-#define RT5645_STO2_ADC_L_BST_SFT 8
-#define RT5645_STO2_ADC_R_BST_MASK (0x3 << 6)
-#define RT5645_STO2_ADC_R_BST_SFT 6
-#define RT5645_STO2_ADC_COMP_MASK (0x3 << 4)
-#define RT5645_STO2_ADC_COMP_SFT 4
+
+/* ADC Boost Volume Control (0x20) */
+#define RT5645_MONO_ADC_L_BST_MASK (0x3 << 14)
+#define RT5645_MONO_ADC_L_BST_SFT 14
+#define RT5645_MONO_ADC_R_BST_MASK (0x3 << 12)
+#define RT5645_MONO_ADC_R_BST_SFT 12
+#define RT5645_MONO_ADC_COMP_MASK (0x3 << 10)
+#define RT5645_MONO_ADC_COMP_SFT 10
/* Stereo2 ADC Mixer Control (0x26) */
#define RT5645_STO2_ADC_SRC_MASK (0x1 << 15)
@@ -619,14 +621,14 @@
#define RT5645_G_OM_L_SM_L_SFT 6
#define RT5645_M_BST1_L_SM_L (0x1 << 5)
#define RT5645_M_BST1_L_SM_L_SFT 5
+#define RT5645_M_BST3_L_SM_L (0x1 << 4)
+#define RT5645_M_BST3_L_SM_L_SFT 4
#define RT5645_M_IN_L_SM_L (0x1 << 3)
#define RT5645_M_IN_L_SM_L_SFT 3
-#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
-#define RT5645_M_DAC_L1_SM_L_SFT 1
#define RT5645_M_DAC_L2_SM_L (0x1 << 2)
#define RT5645_M_DAC_L2_SM_L_SFT 2
-#define RT5645_M_BST3_L_SM_L (0x1 << 4)
-#define RT5645_M_BST3_L_SM_L_SFT 4
+#define RT5645_M_DAC_L1_SM_L (0x1 << 1)
+#define RT5645_M_DAC_L1_SM_L_SFT 1
/* SPK Right Mixer Control (0x47) */
#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14)
@@ -641,14 +643,14 @@
#define RT5645_G_OM_R_SM_R_SFT 6
#define RT5645_M_BST2_R_SM_R (0x1 << 5)
#define RT5645_M_BST2_R_SM_R_SFT 5
+#define RT5645_M_BST3_R_SM_R (0x1 << 4)
+#define RT5645_M_BST3_R_SM_R_SFT 4
#define RT5645_M_IN_R_SM_R (0x1 << 3)
#define RT5645_M_IN_R_SM_R_SFT 3
-#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
-#define RT5645_M_DAC_R1_SM_R_SFT 1
#define RT5645_M_DAC_R2_SM_R (0x1 << 2)
#define RT5645_M_DAC_R2_SM_R_SFT 2
-#define RT5645_M_BST3_R_SM_R (0x1 << 4)
-#define RT5645_M_BST3_R_SM_R_SFT 4
+#define RT5645_M_DAC_R1_SM_R (0x1 << 1)
+#define RT5645_M_DAC_R1_SM_R_SFT 1
/* SPOLMIX Control (0x48) */
#define RT5645_M_DAC_L1_SPM_L (0x1 << 15)
@@ -668,13 +670,17 @@
#define RT5645_M_SV_R_SPM_R (0x1 << 0)
#define RT5645_M_SV_R_SPM_R_SFT 0
+/* SPOMIX Ratio Control (0x4a) */
+#define RT5645_SPK_G_CLSD_MASK (0x7 << 0)
+#define RT5645_SPK_G_CLSD_SFT 0
+
/* Mono Output Mixer Control (0x4c) */
+#define RT5645_G_MONOMIX_MASK (0x1 << 10)
+#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_OV_L_MM (0x1 << 9)
#define RT5645_M_OV_L_MM_SFT 9
#define RT5645_M_DAC_L2_MA (0x1 << 8)
#define RT5645_M_DAC_L2_MA_SFT 8
-#define RT5645_G_MONOMIX_MASK (0x1 << 10)
-#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_BST2_MM (0x1 << 4)
#define RT5645_M_BST2_MM_SFT 4
#define RT5645_M_DAC_R1_MM (0x1 << 3)
@@ -777,8 +783,6 @@
#define RT5645_PWR_CLS_D_R_BIT 9
#define RT5645_PWR_CLS_D_L (0x1 << 8)
#define RT5645_PWR_CLS_D_L_BIT 8
-#define RT5645_PWR_ADC_R (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT 1
#define RT5645_PWR_DAC_L2 (0x1 << 7)
#define RT5645_PWR_DAC_L2_BIT 7
#define RT5645_PWR_DAC_R2 (0x1 << 6)
@@ -1626,6 +1630,10 @@
#define RT5645_OT_P_NOR (0x0 << 10)
#define RT5645_OT_P_INV (0x1 << 10)
#define RT5645_IRQ_JD_1_1_EN (0x1 << 9)
+#define RT5645_JD_1_1_MASK (0x1 << 7)
+#define RT5645_JD_1_1_SFT 7
+#define RT5645_JD_1_1_NOR (0x0 << 7)
+#define RT5645_JD_1_1_INV (0x1 << 7)
/* IRQ Control 2 (0xbe) */
#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15)
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index bfda25ef0dd4..f540f82b1f27 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1376,8 +1376,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
- SGTL5000_BIAS_R_MASK,
- sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT);
+ SGTL5000_BIAS_VOLT_MASK,
+ sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT);
/*
* disable DAP
* TODO:
@@ -1549,7 +1549,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
else {
sgtl5000->micbias_voltage = 0;
dev_err(&client->dev,
- "Unsuitable MicBias resistor\n");
+ "Unsuitable MicBias voltage\n");
}
} else {
sgtl5000->micbias_voltage = 0;
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index e3a0bca28bcf..cc1d3981fa4b 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -549,7 +549,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
/*
* DAC digital volumes. From -7 to 24 dB in 1 dB steps
*/
-static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0);
static const char * const tas2552_din_source_select[] = {
"Muted",
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 1a82b19b2644..a564759845f9 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -80,6 +80,7 @@ struct aic3x_priv {
unsigned int sysclk;
unsigned int dai_fmt;
unsigned int tdm_delay;
+ unsigned int slot_width;
struct list_head list;
int master;
int gpio_reset;
@@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
int clk;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = params_width(params);
/* select data word length */
data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
- switch (params_width(params)) {
+ switch (width) {
case 16:
break;
case 20:
@@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int delay = 0;
+ int width = aic3x->slot_width;
+
+ if (!width)
+ width = substream->runtime->sample_bits;
/* TDM slot selection only valid in DSP_A/_B mode */
if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
- delay += (aic3x->tdm_delay + 1);
+ delay += (aic3x->tdm_delay*width + 1);
else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
- delay += aic3x->tdm_delay;
+ delay += aic3x->tdm_delay*width;
/* Configure data delay */
snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
@@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- aic3x->tdm_delay = lsb * slot_width;
+ switch (slot_width) {
+ case 16:
+ case 20:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+
+ aic3x->tdm_delay = lsb;
+ aic3x->slot_width = slot_width;
/* DOUT in high-impedance on inactive bit clocks */
snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
@@ -1509,14 +1531,17 @@ static int aic3x_init(struct snd_soc_codec *codec)
snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
- /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
- /* Line2 Line Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+ /* On tlv320aic3104, these registers are reserved and must not be written */
+ if (aic3x->model != AIC3X_MODEL_3104) {
+ /* Line2 to HP Bypass default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+ /* Line2 Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+ }
switch (aic3x->model) {
case AIC3X_MODEL_3X:
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index f2c6ad4b8fde..581ec1502228 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -577,7 +577,6 @@ static int wm0010_boot(struct snd_soc_codec *codec)
struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
unsigned long flags;
int ret;
- const struct firmware *fw;
struct spi_message m;
struct spi_transfer t;
struct dfw_pllrec pll_rec;
@@ -623,14 +622,6 @@ static int wm0010_boot(struct snd_soc_codec *codec)
wm0010->state = WM0010_OUT_OF_RESET;
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
- /* First the bootloader */
- ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
- ret);
- goto abort;
- }
-
if (!wait_for_completion_timeout(&wm0010->boot_completion,
msecs_to_jiffies(20)))
dev_err(codec->dev, "Failed to get interrupt from DSP\n");
@@ -673,7 +664,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)
img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img_swap)
- goto abort;
+ goto abort_out;
/* We need to re-order for 0010 */
byte_swap_64((u64 *)&pll_rec, img_swap, len);
@@ -688,16 +679,16 @@ static int wm0010_boot(struct snd_soc_codec *codec)
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);
- if (ret != 0) {
+ if (ret) {
dev_err(codec->dev, "First PLL write failed: %d\n", ret);
- goto abort;
+ goto abort_swap;
}
/* Use a second send of the message to get the return status */
ret = spi_sync(spi, &m);
- if (ret != 0) {
+ if (ret) {
dev_err(codec->dev, "Second PLL write failed: %d\n", ret);
- goto abort;
+ goto abort_swap;
}
p = (u32 *)out;
@@ -730,6 +721,10 @@ static int wm0010_boot(struct snd_soc_codec *codec)
return 0;
+abort_swap:
+ kfree(img_swap);
+abort_out:
+ kfree(out);
abort:
/* Put the chip back into reset */
wm0010_halt(codec);
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 9756578fc752..c04c0bc6f58a 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -38,6 +38,12 @@
struct wm5110_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
+
+ unsigned int in_value;
+ int in_pre_pending;
+ int in_post_pending;
+
+ unsigned int in_pga_cache[6];
};
static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -428,6 +434,127 @@ err:
return ret;
}
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_card *card = dapm->card;
+ int ret;
+
+ /*
+ * PGA Volume is also used as part of the enable sequence, so
+ * usage of it should be avoided whilst that is running.
+ */
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+ mutex_unlock(&card->dapm_mutex);
+
+ return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ unsigned int reg, mask;
+ struct reg_sequence analog_seq[] = {
+ { 0x80, 0x3 },
+ { 0x35d, 0 },
+ { 0x80, 0x0 },
+ };
+
+ reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+ mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+ switch (event) {
+ case SND_SOC_DAPM_WILL_PMU:
+ wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+ wm5110->in_pre_pending++;
+ wm5110->in_post_pending++;
+ return 0;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+ snd_soc_update_bits(codec, reg, mask,
+ 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+ wm5110->in_pre_pending--;
+ if (wm5110->in_pre_pending == 0) {
+ analog_seq[1].def = wm5110->in_value;
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+
+ msleep(55);
+
+ wm5110->in_value = 0;
+ }
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, reg, mask,
+ wm5110->in_pga_cache[w->shift]);
+
+ wm5110->in_post_pending--;
+ if (wm5110->in_post_pending == 0)
+ regmap_multi_reg_write_bypassed(arizona->regmap,
+ analog_seq,
+ ARRAY_SIZE(analog_seq));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ switch (arizona->rev) {
+ case 0 ... 4:
+ if (arizona_input_analog(codec, w->shift))
+ wm5110_in_analog_ev(w, kcontrol, event);
+
+ break;
+ default:
+ break;
+ }
+
+ return arizona_in_ev(w, kcontrol, event);
+}
+
static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
- ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
- ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
- ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+ wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
@@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
- 0, NULL, 0, arizona_in_ev,
+ 0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_WILL_PMU),
SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index e3b7d0c57411..dbd88408861a 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -211,28 +211,38 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
return wm8960_set_deemph(codec);
}
-static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
-static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
-static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
+static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1);
+static const unsigned int micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0),
+};
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
- 0, 63, 0, adc_tlv),
+ 0, 63, 0, inpga_tlv),
SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
6, 1, 0),
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
7, 1, 0),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
- WM8960_INBMIX1, 4, 7, 0, boost_tlv),
+ WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
- WM8960_INBMIX1, 1, 7, 0, boost_tlv),
+ WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
- WM8960_INBMIX2, 4, 7, 0, boost_tlv),
+ WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
- WM8960_INBMIX2, 1, 7, 0, boost_tlv),
+ WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume",
+ WM8960_RINPATH, 4, 3, 0, micboost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume",
+ WM8960_LINPATH, 4, 3, 0, micboost_tlv),
SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
0, 255, 0, dac_tlv),
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index b4eb975da981..39ebd7bf4f53 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2944,7 +2944,8 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute)
WM8962_DAC_MUTE, val);
}
-#define WM8962_RATES SNDRV_PCM_RATE_8000_96000
+#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -3759,7 +3760,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)
- goto err_enable;
+ goto err_pm_runtime;
regcache_cache_only(wm8962->regmap, true);
@@ -3768,6 +3769,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
return 0;
+err_pm_runtime:
+ pm_runtime_disable(&i2c->dev);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies);
err:
@@ -3777,6 +3780,7 @@ err:
static int wm8962_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
+ pm_runtime_disable(&client->dev);
return 0;
}
@@ -3804,6 +3808,8 @@ static int wm8962_runtime_resume(struct device *dev)
wm8962_reset(wm8962);
+ regcache_mark_dirty(wm8962->regmap);
+
/* SYSCLK defaults to on; make sure it is off so we can safely
* write to registers if the device is declocked.
*/
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
new file mode 100644
index 000000000000..8782dfb628ab
--- /dev/null
+++ b/sound/soc/codecs/wm8998.c
@@ -0,0 +1,1430 @@
+/*
+ * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm8998.h"
+
+struct wm8998_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE1);
+ val &= ARIZONA_ASRC_RATE1_MASK;
+ val >>= ARIZONA_ASRC_RATE1_SHIFT;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ val = snd_soc_read(codec,
+ ARIZONA_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate1 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate1 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+
+ val = snd_soc_read(codec, ARIZONA_ASRC_RATE2);
+ val &= ARIZONA_ASRC_RATE2_MASK;
+ val >>= ARIZONA_ASRC_RATE2_SHIFT;
+
+ switch (val) {
+ case 8:
+ case 9:
+ val -= 0x8;
+ val = snd_soc_read(codec,
+ ARIZONA_ASYNC_SAMPLE_RATE_1 + val);
+ if (val >= 0x11) {
+ dev_warn(codec->dev,
+ "Unsupported ASRC rate2 (%s)\n",
+ arizona_sample_rate_val_to_name(val));
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev,
+ "Illegal ASRC rate2 selector (0x%x)\n",
+ val);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode;
+ unsigned int mode_val, src_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ /* L and R registers have same shift and mask */
+ inmode = arizona->pdata.inmode[2 * mux];
+ src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
+
+ switch (arizona->pdata.inmode[0]) {
+ case ARIZONA_INMODE_DMIC:
+ if (mux)
+ mode_val = 0; /* B always analogue */
+ else
+ mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_MODE_MASK, mode_val);
+
+ /* IN1A is digital so L and R must change together */
+ /* src_val setting same for both registers */
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK, src_val);
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_MASK |
+ ARIZONA_IN1R_SRC_SE_MASK, src_val);
+ break;
+ default:
+ /* both analogue */
+ snd_soc_update_bits(codec,
+ e->reg,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK,
+ src_val);
+ break;
+ }
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = wm8998->core.arizona;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int mux, inmode, src_val, mode_val;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ inmode = arizona->pdata.inmode[1 + (2 * mux)];
+ if (inmode & ARIZONA_INMODE_DMIC)
+ mode_val = 1 << ARIZONA_IN2_MODE_SHIFT;
+ else
+ mode_val = 0;
+
+ src_val = mux << ARIZONA_IN2L_SRC_SHIFT;
+ if (inmode & ARIZONA_INMODE_SE)
+ src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT;
+
+ snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_MODE_MASK, mode_val);
+
+ snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK,
+ src_val);
+
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+}
+
+static const char * const wm8998_inmux_texts[] = {
+ "A",
+ "B",
+};
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
+ ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_SRC_SHIFT,
+ wm8998_inmux_texts);
+
+static const struct snd_kcontrol_new wm8998_in1mux[2] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+};
+
+static const struct snd_kcontrol_new wm8998_in2mux =
+ SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum,
+ snd_soc_dapm_get_enum_double, wm8998_in2mux_put);
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM8998_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0)
+
+static const struct snd_kcontrol_new wm8998_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L),
+WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R),
+WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char * const wm8998_aec_loopback_texts[] = {
+ "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT",
+ "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR",
+};
+
+static const unsigned int wm8998_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
+ ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
+ ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ wm8998_aec_loopback_texts,
+ wm8998_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
+ SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),
+ SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1AL"),
+SND_SOC_DAPM_INPUT("IN1AR"),
+SND_SOC_DAPM_INPUT("IN1BL"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2A"),
+SND_SOC_DAPM_INPUT("IN2B"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]),
+SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8998_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+ ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+ ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL,
+ ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+ARIZONA_MUX_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MUX_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MUX_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MUX_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"),
+ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDATL"),
+SND_SOC_DAPM_OUTPUT("SPKDATR"),
+SND_SOC_DAPM_OUTPUT("SPDIF"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC1 Loopback" }, \
+ { name, "AEC2", "AEC2 Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2 PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "AIF2RX3", "AIF2RX3" }, \
+ { name, "AIF2RX4", "AIF2RX4" }, \
+ { name, "AIF2RX5", "AIF2RX5" }, \
+ { name, "AIF2RX6", "AIF2RX6" }, \
+ { name, "AIF3RX1", "AIF3RX1" }, \
+ { name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ASRC1L", "ASRC1L" }, \
+ { name, "ASRC1R", "ASRC1R" }, \
+ { name, "ASRC2L", "ASRC2L" }, \
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "AIF3 Capture", NULL, "DBVDD3" },
+ { "AIF3 Playback", NULL, "DBVDD3" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT2L", NULL, "CPVDD" },
+ { "OUT2R", NULL, "CPVDD" },
+ { "OUT3", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDDL" },
+ { "OUT4R", NULL, "SPKVDDR" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT2L", NULL, "SYSCLK" },
+ { "OUT2R", NULL, "SYSCLK" },
+ { "OUT3", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+ { "OUT4R", NULL, "SYSCLK" },
+ { "OUT5L", NULL, "SYSCLK" },
+ { "OUT5R", NULL, "SYSCLK" },
+
+ { "IN1AL", NULL, "SYSCLK" },
+ { "IN1AR", NULL, "SYSCLK" },
+ { "IN1BL", NULL, "SYSCLK" },
+ { "IN1BR", NULL, "SYSCLK" },
+ { "IN2A", NULL, "SYSCLK" },
+ { "IN2B", NULL, "SYSCLK" },
+
+ { "SPD1", NULL, "SYSCLK" },
+ { "SPD1", NULL, "SPD1TX1" },
+ { "SPD1", NULL, "SPD1TX2" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+ { "AIF2 Capture", NULL, "AIF2TX3" },
+ { "AIF2 Capture", NULL, "AIF2TX4" },
+ { "AIF2 Capture", NULL, "AIF2TX5" },
+ { "AIF2 Capture", NULL, "AIF2TX6" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+ { "AIF2RX3", NULL, "AIF2 Playback" },
+ { "AIF2RX4", NULL, "AIF2 Playback" },
+ { "AIF2RX5", NULL, "AIF2 Playback" },
+ { "AIF2RX6", NULL, "AIF2 Playback" },
+
+ { "AIF3 Capture", NULL, "AIF3TX1" },
+ { "AIF3 Capture", NULL, "AIF3TX2" },
+
+ { "AIF3RX1", NULL, "AIF3 Playback" },
+ { "AIF3RX2", NULL, "AIF3 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+
+ { "SLIMRX3", NULL, "Slim2 Playback" },
+ { "SLIMRX4", NULL, "Slim2 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "A", "IN1AL" },
+ { "IN1R Mux", "A", "IN1AR" },
+ { "IN1L Mux", "B", "IN1BL" },
+ { "IN1R Mux", "B", "IN1BR" },
+
+ { "IN2 Mux", "A", "IN2A" },
+ { "IN2 Mux", "B", "IN2B" },
+
+ { "IN1L PGA", NULL, "IN1L Mux" },
+ { "IN1R PGA", NULL, "IN1R Mux" },
+ { "IN2 PGA", NULL, "IN2 Mux" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+ ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+ ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+ ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+ ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+ ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+ ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+ ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+ ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"),
+
+ ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+ ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+ ARIZONA_MUX_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MUX_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MUX_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MUX_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC1 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC1 Loopback", "HPOUTR", "OUT1R" },
+ { "AEC2 Loopback", "HPOUTL", "OUT1L" },
+ { "AEC2 Loopback", "HPOUTR", "OUT1R" },
+ { "HPOUTL", NULL, "OUT1L" },
+ { "HPOUTR", NULL, "OUT1R" },
+
+ { "AEC1 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC1 Loopback", "LINEOUTR", "OUT2R" },
+ { "AEC2 Loopback", "LINEOUTL", "OUT2L" },
+ { "AEC2 Loopback", "LINEOUTR", "OUT2R" },
+ { "LINEOUTL", NULL, "OUT2L" },
+ { "LINEOUTR", NULL, "OUT2R" },
+
+ { "AEC1 Loopback", "EPOUT", "OUT3" },
+ { "AEC2 Loopback", "EPOUT", "OUT3" },
+ { "EPOUT", NULL, "OUT3" },
+
+ { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+ { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+ { "SPKOUTLN", NULL, "OUT4L" },
+ { "SPKOUTLP", NULL, "OUT4L" },
+
+ { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+ { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+ { "SPKOUTRN", NULL, "OUT4R" },
+ { "SPKOUTRP", NULL, "OUT4R" },
+
+ { "SPDIF", NULL, "SPD1" },
+
+ { "AEC1 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC1 Loopback", "SPKDATR", "OUT5R" },
+ { "AEC2 Loopback", "SPKDATL", "OUT5L" },
+ { "AEC2 Loopback", "SPKDATR", "OUT5R" },
+ { "SPKDATL", NULL, "OUT5L" },
+ { "SPKDATR", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+};
+
+#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8998_dai[] = {
+ {
+ .name = "wm8998-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "wm8998-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8998-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8998_RATES,
+ .formats = WM8998_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8998_FLL1:
+ return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout);
+ case WM8998_FLL2:
+ return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout);
+ case WM8998_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref,
+ Fout);
+ case WM8998_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wm8998_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ priv->core.arizona->dapm = dapm;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ return 0;
+}
+
+static int wm8998_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8998_DIG_VU 0x0200
+
+static unsigned int wm8998_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_2L,
+ ARIZONA_DAC_DIGITAL_VOLUME_2R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4R,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm8998_get_regmap(struct device *dev)
+{
+ struct wm8998_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
+ .probe = wm8998_codec_probe,
+ .remove = wm8998_codec_remove,
+ .get_regmap = wm8998_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8998_set_fll,
+
+ .controls = wm8998_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+ .dapm_widgets = wm8998_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
+ .dapm_routes = wm8998_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+};
+
+static int wm8998_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8998_priv *wm8998;
+ int i;
+
+ wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
+ GFP_KERNEL);
+ if (!wm8998)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8998);
+
+ wm8998->core.arizona = arizona;
+ wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
+
+ for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
+ wm8998->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8998->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8998->fll[1]);
+
+ for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++)
+ arizona_init_dai(&wm8998->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8998_digital_vu[i],
+ WM8998_DIG_VU, WM8998_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+ wm8998_dai, ARRAY_SIZE(wm8998_dai));
+}
+
+static int wm8998_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8998_codec_driver = {
+ .driver = {
+ .name = "wm8998-codec",
+ },
+ .probe = wm8998_probe,
+ .remove = wm8998_remove,
+};
+
+module_platform_driver(wm8998_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8998 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wm8998-codec");
diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h
new file mode 100644
index 000000000000..1e8647252162
--- /dev/null
+++ b/sound/soc/codecs/wm8998.h
@@ -0,0 +1,23 @@
+/*
+ * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8998_H
+#define _WM8998_H
+
+#include "arizona.h"
+
+#define WM8998_FLL1 1
+#define WM8998_FLL2 2
+#define WM8998_FLL1_REFCLK 3
+#define WM8998_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index add6bb99661d..4495a40a9468 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -80,12 +80,13 @@ struct davinci_mcasp {
/* McASP specific data */
int tdm_slots;
+ u32 tdm_mask[2];
+ int slot_width;
u8 op_mode;
u8 num_serializer;
u8 *serial_dir;
u8 version;
u8 bclk_div;
- u16 bclk_lrclk_ratio;
int streams;
u32 irq_request[2];
int dma_request[2];
@@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
mcasp->bclk_div = div;
break;
- case 2: /* BCLK/LRCLK ratio */
- mcasp->bclk_lrclk_ratio = div;
+ case 2: /*
+ * BCLK/LRCLK ratio descries how many bit-clock cycles
+ * fit into one frame. The clock ratio is given for a
+ * full period of data (for I2S format both left and
+ * right channels), so it has to be divided by number
+ * of tdm-slots (for I2S - divided by 2).
+ * Instead of storing this ratio, we calculate a new
+ * tdm_slot width by dividing the the ratio by the
+ * number of configured tdm slots.
+ */
+ mcasp->slot_width = div / mcasp->tdm_slots;
+ if (div % mcasp->tdm_slots)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+ __func__, div, mcasp->tdm_slots);
break;
default:
@@ -596,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
return 0;
}
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+ int serializers)
+{
+ struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+ unsigned int *list = (unsigned int *) cl->list;
+ int slots = mcasp->tdm_slots;
+ int i, count = 0;
+
+ if (mcasp->tdm_mask[stream])
+ slots = hweight32(mcasp->tdm_mask[stream]);
+
+ for (i = 2; i <= slots; i++)
+ list[count++] = i;
+
+ for (i = 2; i <= serializers; i++)
+ list[count++] = i*slots;
+
+ cl->count = count;
+
+ return 0;
+}
+
+static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
+{
+ int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ if (mcasp->serial_dir[i] == TX_MODE)
+ tx_serializers++;
+ else if (mcasp->serial_dir[i] == RX_MODE)
+ rx_serializers++;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
+ tx_serializers);
+ if (ret)
+ return ret;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+ rx_serializers);
+
+ return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(mcasp->dev,
+ "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(mcasp->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (slot_width &&
+ (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+ dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ mcasp->tdm_slots = slots;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+ mcasp->slot_width = slot_width;
+
+ return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int word_length)
+ int sample_width)
{
u32 fmt;
- u32 tx_rotate = (word_length / 4) & 0x7;
- u32 mask = (1ULL << word_length) - 1;
+ u32 tx_rotate = (sample_width / 4) & 0x7;
+ u32 mask = (1ULL << sample_width) - 1;
+ u32 slot_width = sample_width;
+
/*
* For captured data we should not rotate, inversion and masking is
* enoguh to get the data to the right position:
@@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
u32 rx_rotate = 0;
/*
- * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
- * callback, take it into account here. That allows us to for example
- * send 32 bits per channel to the codec, while only 16 of them carry
- * audio payload.
- * The clock ratio is given for a full period of data (for I2S format
- * both left and right channels), so it has to be divided by number of
- * tdm-slots (for I2S - divided by 2).
+ * Setting the tdm slot width either with set_clkdiv() or
+ * set_tdm_slot() allows us to for example send 32 bits per
+ * channel to the codec, while only 16 of them carry audio
+ * payload.
*/
- if (mcasp->bclk_lrclk_ratio) {
- u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
-
+ if (mcasp->slot_width) {
/*
- * When we have more bclk then it is needed for the data, we
- * need to use the rotation to move the received samples to have
- * correct alignment.
+ * When we have more bclk then it is needed for the
+ * data, we need to use the rotation to move the
+ * received samples to have correct alignment.
*/
- rx_rotate = (slot_length - word_length) / 4;
- word_length = slot_length;
+ slot_width = mcasp->slot_width;
+ rx_rotate = (slot_width - sample_width) / 4;
}
/* mapping of the XSSZ bit-field as described in the datasheet */
- fmt = (word_length >> 1) - 1;
+ fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
@@ -663,7 +752,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
u8 rx_ser = 0;
u8 slots = mcasp->tdm_slots;
u8 max_active_serializers = (channels + slots - 1) / slots;
- int active_serializers, numevt, n;
+ int active_serializers, numevt;
u32 reg;
/* Default configuration */
if (mcasp->version < MCASP_VERSION_3)
@@ -745,9 +834,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
* The number of words for numevt need to be in steps of active
* serializers.
*/
- n = numevt % active_serializers;
- if (n)
- numevt += (active_serializers - n);
+ numevt = (numevt / active_serializers) * active_serializers;
+
while (period_words % numevt && numevt > 0)
numevt -= active_serializers;
if (numevt <= 0)
@@ -777,33 +865,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
/*
* If more than one serializer is needed, then use them with
- * their specified tdm_slots count. Otherwise, one serializer
- * can cope with the transaction using as many slots as channels
- * in the stream, requires channels symmetry
+ * all the specified tdm_slots. Otherwise, one serializer can
+ * cope with the transaction using just as many slots as there
+ * are channels in the stream.
*/
- active_serializers = (channels + total_slots - 1) / total_slots;
- if (active_serializers == 1)
- active_slots = channels;
- else
- active_slots = total_slots;
-
- for (i = 0; i < active_slots; i++)
- mask |= (1 << i);
+ if (mcasp->tdm_mask[stream]) {
+ active_slots = hweight32(mcasp->tdm_mask[stream]);
+ active_serializers = (channels + active_slots - 1) /
+ active_slots;
+ if (active_serializers == 1) {
+ active_slots = channels;
+ for (i = 0; i < total_slots; i++) {
+ if ((1 << i) & mcasp->tdm_mask[stream]) {
+ mask |= (1 << i);
+ if (--active_slots <= 0)
+ break;
+ }
+ }
+ }
+ } else {
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+ }
mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(total_slots), FSXMOD(0x1FF));
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(total_slots), FSRMOD(0x1FF));
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(total_slots), FSRMOD(0x1FF));
+ }
return 0;
}
@@ -923,6 +1028,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int sbits = params_width(params);
int ppm, div;
+ if (mcasp->slot_width)
+ sbits = mcasp->slot_width;
+
div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
&ppm);
if (ppm)
@@ -1028,6 +1136,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval range;
int i;
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
snd_interval_any(&range);
range.empty = 1;
@@ -1070,10 +1181,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
if (snd_mask_test(fmt, i)) {
- uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
+ uint sbits = snd_pcm_format_width(i);
int ppm;
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
+ &ppm);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set(&nfmt, i);
count++;
@@ -1095,6 +1210,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir;
+ int tdm_slots = mcasp->tdm_slots;
+
+ if (mcasp->tdm_mask[substream->stream])
+ tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
mcasp->substreams[substream->stream] = substream;
@@ -1115,7 +1234,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
max_channels++;
}
ruledata->serializers = max_channels;
- max_channels *= mcasp->tdm_slots;
+ max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
* limnit based on the seirializers * tdm_slots, we need to use that as
@@ -1125,15 +1244,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
*/
if (mcasp->channels && mcasp->channels < max_channels)
max_channels = mcasp->channels;
+ /*
+ * But we can always allow channels upto the amount of
+ * the available tdm_slots.
+ */
+ if (max_channels < tdm_slots)
+ max_channels = tdm_slots;
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
2, max_channels);
- if (mcasp->chconstr[substream->stream].count)
- snd_pcm_hw_constraint_list(substream->runtime,
- 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &mcasp->chconstr[substream->stream]);
+ snd_pcm_hw_constraint_list(substream->runtime,
+ 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mcasp->chconstr[substream->stream]);
+
+ if (mcasp->slot_width)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ 8, mcasp->slot_width);
/*
* If we rely on implicit BCLK divider setting we should
@@ -1185,6 +1314,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.set_fmt = davinci_mcasp_set_dai_fmt,
.set_clkdiv = davinci_mcasp_set_clkdiv,
.set_sysclk = davinci_mcasp_set_sysclk,
+ .set_tdm_slot = davinci_mcasp_set_tdm_slot,
};
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
@@ -1299,6 +1429,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
.ops = &davinci_mcasp_dai_ops,
.symmetric_samplebits = 1,
+ .symmetric_rates = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1514,59 +1645,6 @@ nodata:
return pdata;
}
-/* All serializers must have equal number of channels */
-static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
- struct snd_pcm_hw_constraint_list *cl,
- int serializers)
-{
- unsigned int *list;
- int i, count = 0;
-
- if (serializers <= 1)
- return 0;
-
- list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
- (mcasp->tdm_slots + serializers - 2),
- GFP_KERNEL);
- if (!list)
- return -ENOMEM;
-
- for (i = 2; i <= mcasp->tdm_slots; i++)
- list[count++] = i;
-
- for (i = 2; i <= serializers; i++)
- list[count++] = i*mcasp->tdm_slots;
-
- cl->count = count;
- cl->list = list;
-
- return 0;
-}
-
-
-static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
-{
- int rx_serializers = 0, tx_serializers = 0, ret, i;
-
- for (i = 0; i < mcasp->num_serializer; i++)
- if (mcasp->serial_dir[i] == TX_MODE)
- tx_serializers++;
- else if (mcasp->serial_dir[i] == RX_MODE)
- rx_serializers++;
-
- ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
- SNDRV_PCM_STREAM_PLAYBACK],
- tx_serializers);
- if (ret)
- return ret;
-
- ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
- SNDRV_PCM_STREAM_CAPTURE],
- rx_serializers);
-
- return ret;
-}
-
enum {
PCM_EDMA,
PCM_SDMA,
@@ -1685,7 +1763,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "common");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_common_irq_handler,
@@ -1702,7 +1780,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "rx");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_rx_irq_handler,
@@ -1717,7 +1795,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "tx");
if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx",
dev_name(&pdev->dev));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
davinci_mcasp_tx_irq_handler,
@@ -1783,7 +1861,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
}
- ret = davinci_mcasp_init_ch_constraints(mcasp);
+ /* Allocate memory for long enough list for all possible
+ * scenarios. Maximum number tdm slots is 32 and there cannot
+ * be more serializers than given in the configuration. The
+ * serializer directions could be taken into account, but it
+ * would make code much more complex and save only couple of
+ * bytes.
+ */
+ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+ devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+ (32 + mcasp->num_serializer - 2),
+ GFP_KERNEL);
+
+ if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+ !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
+ return -ENOMEM;
+
+ ret = davinci_mcasp_set_ch_constraints(mcasp);
if (ret)
goto err;
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index a3e97b46b64e..6e6a70c5c2bd 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -131,23 +131,32 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, TOR(i), 0);
+ i2s_read_reg(dev->i2s_base, TOR(i));
} else {
for (i = 0; i < 4; i++)
- i2s_write_reg(dev->i2s_base, ROR(i), 0);
+ i2s_read_reg(dev->i2s_base, ROR(i));
}
}
static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
{
-
+ u32 i, irq;
i2s_write_reg(dev->i2s_base, IER, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < 4; i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
+ }
i2s_write_reg(dev->i2s_base, ITER, 1);
- else
+ } else {
+ for (i = 0; i < 4; i++) {
+ irq = i2s_read_reg(dev->i2s_base, IMR(i));
+ i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
+ }
i2s_write_reg(dev->i2s_base, IRER, 1);
+ }
i2s_write_reg(dev->i2s_base, CER, 1);
}
@@ -273,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
config->sample_rate = params_rate(params);
- if (dev->i2s_clk_cfg) {
- ret = dev->i2s_clk_cfg(config);
- if (ret < 0) {
- dev_err(dev->dev, "runtime audio clk config fail\n");
- return ret;
- }
- } else {
- u32 bitclk = config->sample_rate * config->data_width * 2;
-
- ret = clk_set_rate(dev->clk, bitclk);
- if (ret) {
- dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
- ret);
- return ret;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (dev->i2s_clk_cfg) {
+ ret = dev->i2s_clk_cfg(config);
+ if (ret < 0) {
+ dev_err(dev->dev, "runtime audio clk config fail\n");
+ return ret;
+ }
+ } else {
+ u32 bitclk = config->sample_rate *
+ config->data_width * 2;
+
+ ret = clk_set_rate(dev->clk, bitclk);
+ if (ret) {
+ dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+ ret);
+ return ret;
+ }
}
}
-
return 0;
}
@@ -339,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (dev->capability & DW_I2S_SLAVE)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (dev->capability & DW_I2S_MASTER)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static struct snd_soc_dai_ops dw_i2s_dai_ops = {
.startup = dw_i2s_startup,
.shutdown = dw_i2s_shutdown,
.hw_params = dw_i2s_hw_params,
.prepare = dw_i2s_prepare,
.trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
};
static const struct snd_soc_component_driver dw_i2s_component = {
@@ -357,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_disable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable(dev->clk);
return 0;
}
@@ -365,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- clk_enable(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_enable(dev->clk);
return 0;
}
@@ -443,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
dw_i2s_dai->capture.rates = rates;
}
+ if (COMP1_MODE_EN(comp1)) {
+ dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+ dev->capability |= DW_I2S_MASTER;
+ } else {
+ dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+ dev->capability |= DW_I2S_SLAVE;
+ }
+
return 0;
}
@@ -529,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
struct resource *res;
int ret;
struct snd_soc_dai_driver *dw_i2s_dai;
+ const char *clk_id;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
@@ -550,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev)
return PTR_ERR(dev->i2s_base);
dev->dev = &pdev->dev;
+
if (pdata) {
+ dev->capability = pdata->cap;
+ clk_id = NULL;
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
- if (ret < 0)
- return ret;
+ } else {
+ clk_id = "i2sclk";
+ ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ }
+ if (ret < 0)
+ return ret;
- dev->capability = pdata->cap;
- dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
- if (!dev->i2s_clk_cfg) {
- dev_err(&pdev->dev, "no clock configure method\n");
- return -ENODEV;
+ if (dev->capability & DW_I2S_MASTER) {
+ if (pdata) {
+ dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+ if (!dev->i2s_clk_cfg) {
+ dev_err(&pdev->dev, "no clock configure method\n");
+ return -ENODEV;
+ }
}
+ dev->clk = devm_clk_get(&pdev->dev, clk_id);
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- } else {
- ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+
+ ret = clk_prepare_enable(dev->clk);
if (ret < 0)
return ret;
-
- dev->clk = devm_clk_get(&pdev->dev, "i2sclk");
}
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
-
- ret = clk_prepare_enable(dev->clk);
- if (ret < 0)
- return ret;
dev_set_drvdata(&pdev->dev, dev);
ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
@@ -597,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
return 0;
err_clk_disable:
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return ret;
}
@@ -605,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev)
{
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- clk_disable_unprepare(dev->clk);
+ if (dev->capability & DW_I2S_MASTER)
+ clk_disable_unprepare(dev->clk);
return 0;
}
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 5aeb6ed4827e..1b05d1c5d9fd 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -14,6 +14,9 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+#include <sound/ac97_codec.h>
+#endif
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
+static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
+{
+ return priv->dai_fmt == SND_SOC_DAIFMT_AC97;
+}
+
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
* set_bias_level(), bypass the remaining settings in hw_params().
* Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
*/
- if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
+ if ((priv->card.set_bias_level &&
+ priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
+ fsl_asoc_card_is_ac97(priv))
return 0;
/* Specific configurations of DAIs starts from here */
@@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
ext_port--;
/*
- * Use asynchronous mode (6 wires) for all cases.
+ * Use asynchronous mode (6 wires) for all cases except AC97.
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
@@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
default:
- return -EINVAL;
+ if (!fsl_asoc_card_is_ac97(priv))
+ return -EINVAL;
+ }
+
+ if (fsl_asoc_card_is_ac97(priv)) {
+ int_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSDIR;
}
/* Asynchronous mode can not be set along with RCLKDIR */
- ret = imx_audmux_v2_configure_port(int_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
- if (ret) {
- dev_err(dev, "audmux internal port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port);
+
+ ret = imx_audmux_v2_configure_port(int_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux internal port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
@@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
return ret;
}
- ret = imx_audmux_v2_configure_port(ext_port, 0,
- IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
- if (ret) {
- dev_err(dev, "audmux external port setup failed\n");
- return ret;
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ unsigned int pdcr =
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port);
+
+ ret = imx_audmux_v2_configure_port(ext_port, 0,
+ pdcr);
+ if (ret) {
+ dev_err(dev, "audmux external port setup failed\n");
+ return ret;
+ }
}
ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
@@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
struct device *dev = card->dev;
int ret;
+ if (fsl_asoc_card_is_ac97(priv)) {
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+ struct snd_soc_codec *codec = card->rtd[0].codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Use slots 3/4 for S/PDIF so SSI won't try to enable
+ * other slots and send some samples there
+ * due to SLOTREQ bits for S/PDIF received from codec
+ */
+ snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+ AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4);
+#endif
+
+ return 0;
+ }
+
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret) {
@@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct i2c_client *codec_dev;
- struct clk *codec_clk;
const char *codec_dai_name;
u32 width;
int ret;
@@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Give a chance to old DT binding */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
- codec_np = of_parse_phandle(np, "audio-codec", 0);
- if (!cpu_np || !codec_np) {
- dev_err(&pdev->dev, "phandle missing or invalid\n");
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
@@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
- codec_dev = of_find_i2c_device_by_node(codec_np);
- if (!codec_dev) {
- dev_err(&pdev->dev, "failed to find codec platform device\n");
- ret = -EINVAL;
- goto fail;
- }
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (codec_np)
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ else
+ codec_dev = NULL;
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
if (asrc_np)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
- codec_clk = clk_get(&codec_dev->dev, NULL);
- if (!IS_ERR(codec_clk)) {
- priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
- clk_put(codec_clk);
+ if (codec_dev) {
+ struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
+
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
}
/* Default sample rate and format, will be updated in hw_params() */
@@ -486,9 +533,20 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
+ codec_dai_name = "ac97-hifi";
+ priv->card.set_bias_level = NULL;
+ priv->dai_fmt = SND_SOC_DAIFMT_AC97;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto asrc_fail;
+ }
+
+ if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+ dev_err(&pdev->dev, "failed to find codec device\n");
+ ret = -EINVAL;
+ goto asrc_fail;
}
/* Common settings for corresponding Freescale CPU DAI driver */
@@ -507,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
}
- sprintf(priv->name, "%s-audio", codec_dev->name);
+ snprintf(priv->name, sizeof(priv->name), "%s-audio",
+ fsl_asoc_card_is_ac97(priv) ? "ac97" :
+ codec_dev->name);
/* Initialize sound card */
priv->pdev = pdev;
@@ -531,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Normal DAI Link */
priv->dai_link[0].cpu_of_node = cpu_np;
- priv->dai_link[0].codec_of_node = codec_np;
priv->dai_link[0].codec_dai_name = codec_dai_name;
+
+ if (!fsl_asoc_card_is_ac97(priv))
+ priv->dai_link[0].codec_of_node = codec_np;
+ else {
+ u32 idx;
+
+ ret = of_property_read_u32(cpu_np, "cell-index", &idx);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "cannot get CPU index property\n");
+ goto asrc_fail;
+ }
+
+ priv->dai_link[0].codec_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "ac97-codec.%u",
+ (unsigned int)idx);
+ }
+
priv->dai_link[0].platform_of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
@@ -543,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->dai_link[1].platform_of_node = asrc_np;
priv->dai_link[2].codec_dai_name = codec_dai_name;
priv->dai_link[2].codec_of_node = codec_np;
+ priv->dai_link[2].codec_name =
+ priv->dai_link[0].codec_name;
priv->dai_link[2].cpu_of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
@@ -578,20 +658,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_fail:
of_node_put(asrc_np);
-fail:
of_node_put(codec_np);
+fail:
of_node_put(cpu_np);
return ret;
}
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-ac97", },
{ .compatible = "fsl,imx-audio-cs42888", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{ .compatible = "fsl,imx-audio-wm8960", },
{}
};
+MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
static struct platform_driver fsl_asoc_card_driver = {
.probe = fsl_asoc_card_probe,
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 837979ea5c92..59f234e51971 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = {
.name = "fsl-esai",
};
+static const struct reg_default fsl_esai_reg_defaults[] = {
+ {0x8, 0x00000000},
+ {0x10, 0x00000000},
+ {0x18, 0x00000000},
+ {0x98, 0x00000000},
+ {0xd0, 0x00000000},
+ {0xd4, 0x00000000},
+ {0xd8, 0x00000000},
+ {0xdc, 0x00000000},
+ {0xe0, 0x00000000},
+ {0xe4, 0x0000ffff},
+ {0xe8, 0x0000ffff},
+ {0xec, 0x0000ffff},
+ {0xf0, 0x0000ffff},
+ {0xf8, 0x00000000},
+ {0xfc, 0x00000000},
+};
+
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ESAI_ETDR:
+ case REG_ESAI_ERDR:
+ case REG_ESAI_ESR:
+ case REG_ESAI_TFSR:
+ case REG_ESAI_RFSR:
+ case REG_ESAI_TX0:
+ case REG_ESAI_TX1:
+ case REG_ESAI_TX2:
+ case REG_ESAI_TX3:
+ case REG_ESAI_TX4:
+ case REG_ESAI_TX5:
+ case REG_ESAI_RX0:
+ case REG_ESAI_RX1:
+ case REG_ESAI_RX2:
+ case REG_ESAI_RX3:
+ case REG_ESAI_SAISR:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = {
.val_bits = 32,
.max_register = REG_ESAI_PCRC,
+ .reg_defaults = fsl_esai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
.readable_reg = fsl_esai_readable_reg,
+ .volatile_reg = fsl_esai_volatile_reg,
.writeable_reg = fsl_esai_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static int fsl_esai_probe(struct platform_device *pdev)
@@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
+#ifdef CONFIG_PM_SLEEP
+static int fsl_esai_suspend(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+
+ regcache_cache_only(esai->regmap, true);
+ regcache_mark_dirty(esai->regmap);
+
+ return 0;
+}
+
+static int fsl_esai_resume(struct device *dev)
+{
+ struct fsl_esai *esai = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(esai->regmap, false);
+
+ /* FIFO reset for safety */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
+ ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+ ret = regcache_sync(esai->regmap);
+ if (ret)
+ return ret;
+
+ /* FIFO reset done */
+ regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+ regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_esai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+};
+
static struct platform_driver fsl_esai_driver = {
.probe = fsl_esai_probe,
.driver = {
.name = "fsl-esai-dai",
+ .pm = &fsl_esai_pm_ops,
.of_match_table = fsl_esai_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index a18fd92c4a85..a4435f5e3be9 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -27,13 +27,13 @@
#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
FSL_SAI_CSR_FEIE)
-static u32 fsl_sai_rates[] = {
+static const unsigned int fsl_sai_rates[] = {
8000, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000,
88200, 96000, 176400, 192000
};
-static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
.count = ARRAY_SIZE(fsl_sai_rates),
.list = fsl_sai_rates,
};
@@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case FSL_SAI_TCSR:
+ case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
case FSL_SAI_TDR:
@@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = {
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
};
static int fsl_sai_probe(struct platform_device *pdev)
@@ -801,11 +804,42 @@ static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,imx6sx-sai", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_suspend(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, true);
+ regcache_mark_dirty(sai->regmap);
+
+ return 0;
+}
+
+static int fsl_sai_resume(struct device *dev)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, false);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ msleep(1);
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
.driver = {
.name = "fsl-sai",
+ .pm = &fsl_sai_pm_ops,
.of_match_table = fsl_sai_ids,
},
};
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index ab729f2426fe..3d59bb6719f2 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -108,6 +108,8 @@ struct fsl_spdif_priv {
struct clk *sysclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
+ /* regcache for SRPC */
+ u32 regcache_srpc;
};
/* DPLL locked and lock loss interrupt handler */
@@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
struct regmap *regmap = spdif_priv->regmap;
u32 val, cycle = 1000;
+ regcache_cache_bypass(regmap, true);
+
regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
/*
@@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
regmap_read(regmap, REG_SPDIF_SCR, &val);
} while ((val & SCR_SOFT_RESET) && cycle--);
+ regcache_cache_bypass(regmap, false);
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
+
if (cycle)
return 0;
else
@@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
};
/* FSL SPDIF REGMAP */
+static const struct reg_default fsl_spdif_reg_defaults[] = {
+ {0x0, 0x00000400},
+ {0x4, 0x00000000},
+ {0xc, 0x00000000},
+ {0x34, 0x00000000},
+ {0x38, 0x00000000},
+ {0x50, 0x00020f00},
+};
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
{
@@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
}
}
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIS:
+ case REG_SPDIF_SRL:
+ case REG_SPDIF_SRR:
+ case REG_SPDIF_SRCSH:
+ case REG_SPDIF_SRCSL:
+ case REG_SPDIF_SRU:
+ case REG_SPDIF_SRQ:
+ case REG_SPDIF_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_SRFM:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.val_bits = 32,
.max_register = REG_SPDIF_STC,
+ .reg_defaults = fsl_spdif_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
.readable_reg = fsl_spdif_readable_reg,
+ .volatile_reg = fsl_spdif_volatile_reg,
.writeable_reg = fsl_spdif_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
@@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int fsl_spdif_suspend(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
+ &spdif_priv->regcache_srpc);
+
+ regcache_cache_only(spdif_priv->regmap, true);
+ regcache_mark_dirty(spdif_priv->regmap);
+
+ return 0;
+}
+
+static int fsl_spdif_resume(struct device *dev)
+{
+ struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdif_priv->regmap, false);
+
+ regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ spdif_priv->regcache_srpc);
+
+ return regcache_sync(spdif_priv->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_spdif_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+};
+
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", },
{ .compatible = "fsl,vf610-spdif", },
@@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = {
.driver = {
.name = "fsl-spdif-dai",
.of_match_table = fsl_spdif_dt_ids,
+ .pm = &fsl_spdif_pm,
},
.probe = fsl_spdif_probe,
};
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 8ec6fb208ea0..95d2392303eb 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val {
struct fsl_ssi_reg_val rx;
struct fsl_ssi_reg_val tx;
};
+
+static const struct reg_default fsl_ssi_reg_defaults[] = {
+ {0x10, 0x00000000},
+ {0x18, 0x00003003},
+ {0x1c, 0x00000200},
+ {0x20, 0x00000200},
+ {0x24, 0x00040000},
+ {0x28, 0x00040000},
+ {0x38, 0x00000000},
+ {0x48, 0x00000000},
+ {0x4c, 0x00000000},
+ {0x54, 0x00000000},
+ {0x58, 0x00000000},
+};
+
+static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SACCEN:
+ case CCSR_SSI_SACCDIS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_STX0:
+ case CCSR_SSI_STX1:
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SISR:
+ case CCSR_SSI_SFCSR:
+ case CCSR_SSI_SACADD:
+ case CCSR_SSI_SACDAT:
+ case CCSR_SSI_SATAG:
+ case CCSR_SSI_SACCST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CCSR_SSI_SRX0:
+ case CCSR_SSI_SRX1:
+ case CCSR_SSI_SACCST:
+ return false;
+ default:
+ return true;
+ }
+}
+
static const struct regmap_config fsl_ssi_regconfig = {
.max_register = CCSR_SSI_SACCDIS,
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .reg_defaults = fsl_ssi_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+ .readable_reg = fsl_ssi_readable_reg,
+ .volatile_reg = fsl_ssi_volatile_reg,
+ .writeable_reg = fsl_ssi_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
};
struct fsl_ssi_soc_data {
@@ -176,6 +239,9 @@ struct fsl_ssi_private {
unsigned int baudclk_streams;
unsigned int bitclk_freq;
+ /*regcache for SFCSR*/
+ u32 regcache_sfcsr;
+
/* DMA params */
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -249,7 +315,8 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
{
- return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+ return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+ SND_SOC_DAIFMT_AC97;
}
static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
@@ -947,7 +1014,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
CCSR_SSI_SCR_TCH_EN);
}
- if (fmt & SND_SOC_DAIFMT_AC97)
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
fsl_ssi_setup_ac97(ssi_private);
return 0;
@@ -1513,10 +1580,46 @@ static int fsl_ssi_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int fsl_ssi_suspend(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regmap_read(regs, CCSR_SSI_SFCSR,
+ &ssi_private->regcache_sfcsr);
+
+ regcache_cache_only(regs, true);
+ regcache_mark_dirty(regs);
+
+ return 0;
+}
+
+static int fsl_ssi_resume(struct device *dev)
+{
+ struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+ struct regmap *regs = ssi_private->regs;
+
+ regcache_cache_only(regs, false);
+
+ regmap_update_bits(regs, CCSR_SSI_SFCSR,
+ CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
+ CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
+ ssi_private->regcache_sfcsr);
+
+ return regcache_sync(regs);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_ssi_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+};
+
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
.of_match_table = fsl_ssi_ids,
+ .pm = &fsl_ssi_pm,
},
.probe = fsl_ssi_probe,
.remove = fsl_ssi_remove,
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index 33da26a12457..a407e833c612 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
static struct platform_driver imx_spdif_driver = {
.driver = {
.name = "imx-spdif",
+ .pm = &snd_soc_pm_ops,
.of_match_table = imx_spdif_dt_ids,
},
.probe = imx_spdif_audio_probe,
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 48b2d24dd1f0..b95132e2f9dc 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -95,7 +95,8 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* data on rising edge of bclk, frame low 1clk before data */
- strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
+ SSI_STCR_TEFS;
scr |= SSI_SCR_NET;
if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
scr &= ~SSI_I2S_MODE_MASK;
@@ -104,33 +105,31 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_LEFT_J:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_DSP_B:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL;
break;
case SND_SOC_DAIFMT_DSP_A:
/* data on rising edge of bclk, frame high 1clk before data */
- strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS;
+ strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL |
+ SSI_STCR_TEFS;
break;
}
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
- strcr |= SSI_STCR_TFSI;
- strcr &= ~SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI;
break;
case SND_SOC_DAIFMT_IB_NF:
- strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ strcr ^= SSI_STCR_TSCKP;
break;
case SND_SOC_DAIFMT_NB_IF:
- strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ strcr ^= SSI_STCR_TFSI;
break;
case SND_SOC_DAIFMT_NB_NF:
- strcr &= ~SSI_STCR_TFSI;
- strcr |= SSI_STCR_TSCKP;
break;
}
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 3ff76d419436..54c33204541f 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
}
if (set->slots) {
- ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+ ret = snd_soc_dai_set_tdm_slot(dai,
+ set->tx_slot_mask,
+ set->rx_slot_mask,
set->slots,
set->slot_width);
if (ret && ret != -ENOTSUPP) {
@@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
return ret;
/* Parse TDM slot */
- ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+ ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
+ &dai->rx_slot_mask,
+ &dai->slots, &dai->slot_width);
if (ret)
return ret;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 05fde5e6e257..7b778ab85f8b 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -12,6 +12,7 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM
tristate
+ select SND_SOC_COMPRESS
config SND_SST_IPC
tristate
@@ -138,4 +139,18 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
config SND_SOC_INTEL_SKYLAKE
tristate
select SND_HDA_EXT_CORE
+ select SND_SOC_TOPOLOGY
select SND_SOC_INTEL_SST
+
+config SND_SOC_INTEL_SKL_RT286_MACH
+ tristate "ASoC Audio driver for SKL with RT286 I2S mode"
+ depends on X86 && ACPI
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SKYLAKE
+ select SND_SOC_RT286
+ select SND_SOC_DMIC
+ help
+ This adds support for ASoC machine driver for Skylake platforms
+ with RT286 I2S audio codec.
+ Say Y if you have such a device
+ If unsure select "N".
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 683e50116152..0487cfaac538 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream,
kfree(stream);
}
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
- struct snd_pcm_substream *substream)
-{
- struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
- struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- u32 str_id = stream->stream_info.str_id;
- unsigned int pipe_id;
-
- pipe_id = map[str_id].device_id;
-
- dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
- pipe_id, str_id);
- return pipe_id;
-}
-
static int sst_media_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -529,7 +512,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
{
.name = "compress-cpu-dai",
- .compress_dai = 1,
+ .compress_new = snd_soc_new_compress,
.ops = &sst_compr_dai_ops,
.playback = {
.stream_name = "Compress Playback",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index cb94895c9edb..371c4565cad8 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-skl_rt286-objs := skl_rt286.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
@@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index 8bafaf6ceab1..3f8a1e10bed0 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev)
{
broadwell_rt286.dev = &pdev->dev;
- return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&broadwell_rt286);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
}
static struct platform_driver broadwell_audio = {
.probe = broadwell_audio_probe,
- .remove = broadwell_audio_remove,
.driver = {
.name = "broadwell-audio",
},
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
new file mode 100644
index 000000000000..a73a431bd8b7
--- /dev/null
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -0,0 +1,259 @@
+/*
+ * Intel Skylake I2S Machine Driver
+ *
+ * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ * Intel Broadwell Wildcatpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack skylake_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin skylake_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new skylake_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget skylake_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route skylake_rt286_map[] = {
+ /* speaker */
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ /* HP jack connectors - unknown if we have jack deteck */
+ {"Headphone Jack", NULL, "HPO Pin"},
+
+ /* other jacks */
+ {"MIC1", NULL, "Mic Jack"},
+
+ /* digital mics */
+ {"DMIC1 Pin", NULL, "DMIC2"},
+ {"DMIC AIF", NULL, "SoC DMIC"},
+
+ /* CODEC BE connections */
+ { "AIF1 Playback", NULL, "ssp0 Tx"},
+ { "ssp0 Tx", NULL, "codec0_out"},
+ { "ssp0 Tx", NULL, "codec1_out"},
+
+ { "codec0_in", NULL, "ssp0 Rx" },
+ { "codec1_in", NULL, "ssp0 Rx" },
+ { "ssp0 Rx", NULL, "AIF1 Capture" },
+
+ { "dmic01_hifi", NULL, "DMIC01 Rx" },
+ { "DMIC01 Rx", NULL, "Capture" },
+
+ { "hif1", NULL, "iDisp Tx"},
+ { "iDisp Tx", NULL, "iDisp_out"},
+
+};
+
+static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &skylake_headset,
+ skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
+
+ if (ret)
+ return ret;
+
+ rt286_mic_detect(codec, &skylake_headset);
+
+ return 0;
+}
+
+
+static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The output is 48KHz, stereo, 16bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+ return 0;
+}
+
+static int skylake_rt286_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->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops skylake_rt286_ops = {
+ .hw_params = skylake_rt286_hw_params,
+};
+
+/* skylake digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link skylake_rt286_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "Skl Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Skl Audio Capture Port",
+ .stream_name = "Audio Record",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:1f.3",
+ .nonatomic = 1,
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Skl Audio Reference cap",
+ .stream_name = "refcap",
+ .cpu_dai_name = "Reference Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "SSP0-Codec",
+ .be_id = 0,
+ .cpu_dai_name = "SSP0 Pin",
+ .platform_name = "0000:00:1f.3",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT343A:00",
+ .codec_dai_name = "rt286-aif1",
+ .init = skylake_rt286_codec_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = skylake_ssp0_fixup,
+ .ops = &skylake_rt286_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "dmic01",
+ .be_id = 1,
+ .cpu_dai_name = "DMIC01 Pin",
+ .codec_name = "dmic-codec",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "0000:00:1f.3",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+};
+
+/* skylake audio machine driver for SPT + RT286S */
+static struct snd_soc_card skylake_rt286 = {
+ .name = "skylake-rt286",
+ .owner = THIS_MODULE,
+ .dai_link = skylake_rt286_dais,
+ .num_links = ARRAY_SIZE(skylake_rt286_dais),
+ .controls = skylake_controls,
+ .num_controls = ARRAY_SIZE(skylake_controls),
+ .dapm_widgets = skylake_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
+ .dapm_routes = skylake_rt286_map,
+ .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
+ .fully_routed = true,
+};
+
+static int skylake_audio_probe(struct platform_device *pdev)
+{
+ skylake_rt286.dev = &pdev->dev;
+
+ return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
+}
+
+static struct platform_driver skylake_audio = {
+ .probe = skylake_audio_probe,
+ .driver = {
+ .name = "skl_alc286s_i2s",
+ },
+};
+
+module_platform_driver(skylake_audio)
+
+/* Module information */
+MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Skylake");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_alc286s_i2s");
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index f24154ca4e98..d9105584c51f 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -1,7 +1,11 @@
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
+ifneq ($(CONFIG_DW_DMAC_CORE),)
+snd-soc-sst-dsp-objs += sst-firmware.o
+endif
+
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
index cbd568eac033..2151652d37b7 100644
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ b/sound/soc/intel/common/sst-dsp-priv.h
@@ -314,6 +314,7 @@ struct sst_dsp {
int sst_state;
struct skl_cl_dev cl_dev;
u32 intr_status;
+ const struct firmware *fw;
};
/* Size optimised DRAM/IRAM memcpy */
diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c
index a627236dd1f5..c9452e02e0dd 100644
--- a/sound/soc/intel/common/sst-dsp.c
+++ b/sound/soc/intel/common/sst-dsp.c
@@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
{
@@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst)
sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
+#endif
/* Module information */
MODULE_AUTHOR("Liam Girdwood");
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h
index 1f45f18715c0..859f0de00339 100644
--- a/sound/soc/intel/common/sst-dsp.h
+++ b/sound/soc/intel/common/sst-dsp.h
@@ -216,10 +216,12 @@ struct sst_pdata {
void *dsp;
};
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
void sst_dsp_free(struct sst_dsp *sst);
+#endif
/* SHIM Read / Write */
void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index ebcca6dc48d1..1636a1eeb002 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -26,7 +26,6 @@
#include <linux/acpi.h>
/* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
#include <linux/dma/dw.h>
#include <asm/page.h>
@@ -169,12 +168,6 @@ err:
return ret;
}
-static struct dw_dma_platform_data dw_pdata = {
- .is_private = 1,
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
-};
-
static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
int irq)
{
@@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
return ERR_PTR(err);
chip->dev = dev;
- err = dw_dma_probe(chip, &dw_pdata);
+
+ err = dw_dma_probe(chip, NULL);
if (err)
return ERR_PTR(err);
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index f6efa9d4acad..b27f25f70730 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -302,6 +302,10 @@ struct sst_hsw {
struct sst_hsw_ipc_dx_reply dx;
void *dx_context;
dma_addr_t dx_context_paddr;
+ enum sst_hsw_device_id dx_dev;
+ enum sst_hsw_device_mclk dx_mclk;
+ enum sst_hsw_device_mode dx_mode;
+ u32 dx_clock_divider;
/* boot */
wait_queue_head_t boot_wait;
@@ -1400,10 +1404,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
trace_ipc_request("set device config", dev);
- config.ssp_interface = dev;
- config.clock_frequency = mclk;
- config.mode = mode;
- config.clock_divider = clock_divider;
+ hsw->dx_dev = config.ssp_interface = dev;
+ hsw->dx_mclk = config.clock_frequency = mclk;
+ hsw->dx_mode = config.mode = mode;
+ hsw->dx_clock_divider = config.clock_divider = clock_divider;
if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
config.channels = 4;
else
@@ -1704,10 +1708,10 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
return -EIO;
}
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ /* Set ADSP SSP port settings - sadly the FW does not store SSP port
+ settings as part of the PM context. */
+ ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk,
+ hsw->dx_mode, hsw->dx_clock_divider);
if (ret < 0)
dev_err(dev, "error: SSP re-initialization failed\n");
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index 27db22178204..914b6dab9bea 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -1,4 +1,5 @@
-snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o
+snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
+skl-topology.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 826d4fd8930a..50a109503a3f 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
return 0;
}
+#define NOTIFICATION_PARAM_ID 3
+#define NOTIFICATION_MASK 0xf
+
+/* disable notfication for underruns/overruns from firmware module */
+static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+{
+ struct notification_mask mask;
+ struct skl_ipc_large_config_msg msg = {0};
+
+ mask.notify = NOTIFICATION_MASK;
+ mask.enable = enable;
+
+ msg.large_param_id = NOTIFICATION_PARAM_ID;
+ msg.param_data_size = sizeof(mask);
+
+ skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
+}
+
int skl_init_dsp(struct skl *skl)
{
void __iomem *mmio_base;
@@ -79,7 +97,10 @@ int skl_init_dsp(struct skl *skl)
ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
loader_ops, &skl->skl_sst);
+ if (ret < 0)
+ return ret;
+ skl_dsp_enable_notification(skl->skl_sst, false);
dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
return ret;
@@ -122,6 +143,7 @@ int skl_suspend_dsp(struct skl *skl)
int skl_resume_dsp(struct skl *skl)
{
struct skl_sst *ctx = skl->skl_sst;
+ int ret;
/* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap)
@@ -131,7 +153,12 @@ int skl_resume_dsp(struct skl *skl)
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
- return skl_dsp_wake(ctx->dsp);
+ ret = skl_dsp_wake(ctx->dsp);
+ if (ret < 0)
+ return ret;
+
+ skl_dsp_enable_notification(skl->skl_sst, false);
+ return ret;
}
enum skl_bitdepth skl_get_bit_depth(int params)
@@ -294,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
(mconfig->formats_config.caps_size) / 4;
}
+#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
/*
* Calculate the gatewat settings required for copier module, type of
* gateway and index of gateway to use
@@ -303,6 +331,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
struct skl_cpr_cfg *cpr_mconfig)
{
union skl_connector_node_id node_id = {0};
+ union skl_ssp_dma_node ssp_node = {0};
struct skl_pipe_params *params = mconfig->pipe->p_params;
switch (mconfig->dev_type) {
@@ -320,9 +349,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
(SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
SKL_DMA_I2S_LINK_OUTPUT_CLASS :
SKL_DMA_I2S_LINK_INPUT_CLASS;
- node_id.node.vindex = params->host_dma_id +
- (mconfig->time_slot << 1) +
- (mconfig->vbus_id << 3);
+ ssp_node.dma_node.time_slot_index = mconfig->time_slot;
+ ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
+ node_id.node.vindex = ssp_node.val;
break;
case SKL_DEVICE_DMIC:
@@ -339,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
node_id.node.vindex = params->link_dma_id;
break;
- default:
+ case SKL_DEVICE_HDAHOST:
node_id.node.dma_type =
(SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
SKL_DMA_HDA_HOST_OUTPUT_CLASS :
SKL_DMA_HDA_HOST_INPUT_CLASS;
node_id.node.vindex = params->host_dma_id;
break;
+
+ default:
+ cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+ cpr_mconfig->cpr_feature_mask = 0;
+ return;
}
cpr_mconfig->gtw_cfg.node_id = node_id.val;
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 13036b19d7e5..b0c7bd113aac 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-void __iomem *skl_nhlt_init(struct device *dev)
+void *skl_nhlt_init(struct device *dev)
{
acpi_handle handle;
union acpi_object *obj;
@@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev)
if (obj && obj->type == ACPI_TYPE_BUFFER) {
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
- return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length);
+ return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+ MEMREMAP_WB);
}
dev_err(dev, "device specific method to extract NHLT blob failed\n");
return NULL;
}
-void skl_nhlt_free(void __iomem *addr)
+void skl_nhlt_free(void *addr)
{
- iounmap(addr);
- addr = NULL;
+ memunmap(addr);
}
static struct nhlt_specific_cfg *skl_get_specific_cfg(
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7d617bf493bc..a2f94ce1679d 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -24,6 +24,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "skl.h"
+#include "skl-topology.h"
#define HDA_MONO 1
#define HDA_STEREO 2
@@ -115,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
ret = pm_runtime_get_sync(dai->dev);
- if (ret)
+ if (ret < 0)
return ret;
stream = snd_hdac_ext_stream_assign(ebus, substream,
@@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct skl_pipe_params p_params = {0};
+ struct skl_module_cfg *m_cfg;
int ret, dma_id;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
dma_id = hdac_stream(stream)->stream_tag - 1;
dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.host_dma_id = dma_id;
+ p_params.stream = substream->stream;
+
+ m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
+ if (m_cfg)
+ skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
+
return 0;
}
@@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
}
+static int skl_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct skl_pipe_params p_params = {0};
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ skl_tplg_be_update_params(dai, &p_params);
+
+ return 0;
+}
+
+static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_module_cfg *mconfig;
+
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+ if (!mconfig)
+ return -EIO;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ return skl_run_pipe(ctx, mconfig->pipe);
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ return skl_stop_pipe(ctx, mconfig->pipe);
+
+ default:
+ return 0;
+ }
+}
+
static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int dma_id;
+ struct skl_pipe_params p_params = {0};
- pr_debug("%s\n", __func__);
link_dev = snd_hdac_ext_stream_assign(ebus, substream,
HDAC_EXT_STREAM_TYPE_LINK);
if (!link_dev)
@@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
if (dma_params)
dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
- dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ p_params.s_fmt = snd_pcm_format_width(params_format(params));
+ p_params.ch = params_channels(params);
+ p_params.s_freq = params_rate(params);
+ p_params.stream = substream->stream;
+ p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ skl_tplg_be_update_params(dai, &p_params);
return 0;
}
@@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
unsigned int format_val = 0;
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_pcm_hw_params *params;
- struct snd_interval *channels, *rate;
struct hdac_ext_link *link;
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
if (link_dev->link_prepared) {
dev_dbg(dai->dev, "already stream is prepared - returning\n");
return 0;
}
- params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL);
- if (params == NULL)
- return -ENOMEM;
-
- channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- channels->min = channels->max = substream->runtime->channels;
- rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- rate->min = rate->max = substream->runtime->rate;
- snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- substream->runtime->format);
-
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_hda_be_startup(struct snd_pcm_substream *substream,
+static int skl_be_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return pm_runtime_get_sync(dai->dev);
}
-static void skl_hda_be_shutdown(struct snd_pcm_substream *substream,
+static void skl_be_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pm_runtime_mark_last_busy(dai->dev);
@@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
.prepare = skl_pcm_prepare,
.hw_params = skl_pcm_hw_params,
.hw_free = skl_pcm_hw_free,
+ .trigger = skl_pcm_trigger,
};
static struct snd_soc_dai_ops skl_dmic_dai_ops = {
- .startup = skl_hda_be_startup,
- .shutdown = skl_hda_be_shutdown,
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
+ .startup = skl_be_startup,
+ .hw_params = skl_be_hw_params,
+ .shutdown = skl_be_shutdown,
};
static struct snd_soc_dai_ops skl_link_dai_ops = {
- .startup = skl_hda_be_startup,
+ .startup = skl_be_startup,
.prepare = skl_link_pcm_prepare,
.hw_params = skl_link_hw_params,
.hw_free = skl_link_hw_free,
.trigger = skl_link_pcm_trigger,
- .shutdown = skl_hda_be_shutdown,
+ .shutdown = skl_be_shutdown,
};
static struct snd_soc_dai_driver skl_platform_dai[] = {
@@ -488,6 +540,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
/* BE CPU Dais */
{
+ .name = "SSP0 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
.name = "iDisp Pin",
.ops = &skl_link_dai_ops,
.playback = {
@@ -510,17 +580,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
- .name = "DMIC23 Pin",
- .ops = &skl_dmic_dai_ops,
- .capture = {
- .stream_name = "DMIC23 Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
.name = "HD-Codec Pin",
.ops = &skl_link_dai_ops,
.playback = {
@@ -538,28 +597,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
-{
- .name = "HD-Codec-SPK Pin",
- .ops = &skl_link_dai_ops,
- .playback = {
- .stream_name = "HD-Codec-SPK Tx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "HD-Codec-AMIC Pin",
- .ops = &skl_link_dai_ops,
- .capture = {
- .stream_name = "HD-Codec-AMIC Rx",
- .channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
};
static int skl_platform_open(struct snd_pcm_substream *substream)
@@ -577,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream)
return 0;
}
-static int skl_pcm_trigger(struct snd_pcm_substream *substream,
+static int skl_coupled_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -651,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_dsp_trigger(struct snd_pcm_substream *substream,
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -708,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
if (ebus->ppcap)
- return skl_dsp_trigger(substream, cmd);
+ return skl_decoupled_trigger(substream, cmd);
else
- return skl_pcm_trigger(substream, cmd);
+ return skl_coupled_trigger(substream, cmd);
}
/* calculate runtime delay from LPIB */
@@ -877,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval;
}
+static int skl_platform_soc_probe(struct snd_soc_platform *platform)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+
+ if (ebus->ppcap)
+ return skl_tplg_init(platform, ebus);
+
+ return 0;
+}
static struct snd_soc_platform_driver skl_platform_drv = {
+ .probe = skl_platform_soc_probe,
.ops = &skl_platform_ops,
.pcm_new = skl_pcm_new,
.pcm_free = skl_pcm_free,
@@ -890,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = {
int skl_platform_register(struct device *dev)
{
int ret;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ INIT_LIST_HEAD(&skl->ppl_list);
+ INIT_LIST_HEAD(&skl->dapm_path_list);
ret = snd_soc_register_platform(dev, &skl_platform_drv);
if (ret) {
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 94875b008b0b..1bfb7f63b572 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx)
/* poll with timeout to check if operation successful */
return sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK,
+ SKL_ADSPCS_CPA_MASK,
0,
SKL_DSP_PD_TO,
"Power down");
@@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
ctx->intr_status = val;
+ if (val == 0xffffffff) {
+ spin_unlock(&ctx->spinlock);
+ return IRQ_NONE;
+ }
+
if (val & SKL_ADSPIS_IPC) {
skl_ipc_int_disable(ctx);
result = IRQ_WAKE_THREAD;
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
index 937a0a3a63a0..3345ea0d4414 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx)
SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
}
+void skl_ipc_op_int_disable(struct sst_dsp *ctx)
+{
+ /* disable IPC DONE interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+ /* Disable IPC BUSY interrupt */
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+ SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+}
+
bool skl_ipc_int_status(struct sst_dsp *ctx)
{
return sst_dsp_shim_read_unlocked(ctx,
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index 9f5f67202858..f1a154e45dc3 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
void skl_ipc_int_enable(struct sst_dsp *dsp);
void skl_ipc_op_int_enable(struct sst_dsp *ctx);
+void skl_ipc_op_int_disable(struct sst_dsp *ctx);
void skl_ipc_int_disable(struct sst_dsp *dsp);
bool skl_ipc_int_status(struct sst_dsp *dsp);
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index c18ea51b7484..3b83dc99f1d4 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
static int skl_load_base_firmware(struct sst_dsp *ctx)
{
int ret = 0, i;
- const struct firmware *fw = NULL;
struct skl_sst *skl = ctx->thread_context;
u32 reg;
- ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev);
+ skl->boot_complete = false;
+ init_waitqueue_head(&skl->boot_wait);
+
+ if (ctx->fw == NULL) {
+ ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+ skl_dsp_disable_core(ctx);
+ return -EIO;
+ }
+ }
+
+ ret = skl_dsp_boot(ctx);
if (ret < 0) {
- dev_err(ctx->dev, "Request firmware failed %d\n", ret);
- skl_dsp_disable_core(ctx);
- return -EIO;
+ dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+ goto skl_load_base_firmware_failed;
+ }
+
+ ret = skl_cldma_prepare(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+ goto skl_load_base_firmware_failed;
}
/* enable Interrupt */
@@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
goto skl_load_base_firmware_failed;
}
- ret = skl_transfer_firmware(ctx, fw->data, fw->size);
+ ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
goto skl_load_base_firmware_failed;
@@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
}
- release_firmware(fw);
-
return 0;
skl_load_base_firmware_failed:
skl_dsp_disable_core(ctx);
- release_firmware(fw);
+ release_firmware(ctx->fw);
+ ctx->fw = NULL;
return ret;
}
@@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ /* disable Interrupt */
+ ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+ skl_cldma_int_disable(ctx);
+ skl_ipc_op_int_disable(ctx);
+ skl_ipc_int_disable(ctx);
+
return ret;
}
@@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
- skl->boot_complete = false;
- init_waitqueue_head(&skl->boot_wait);
-
- ret = skl_dsp_boot(sst);
- if (ret < 0) {
- dev_err(skl->dev, "Boot dsp core failed ret: %d", ret);
- goto free_ipc;
- }
-
- ret = skl_cldma_prepare(sst);
- if (ret < 0) {
- dev_err(dev, "CL dma prepare failed : %d", ret);
- goto free_ipc;
- }
-
-
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
dev_err(dev, "Load base fw failed : %d", ret);
@@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
return 0;
-free_ipc:
skl_ipc_free(&skl->ipc);
return ret;
}
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
new file mode 100644
index 000000000000..a7854c8fc523
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -0,0 +1,1252 @@
+/*
+ * skl-topology.c - Implements Platform component ALSA controls/widget
+ * handlers.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl-topology.h"
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define SKL_CH_FIXUP_MASK (1 << 0)
+#define SKL_RATE_FIXUP_MASK (1 << 1)
+#define SKL_FMT_FIXUP_MASK (1 << 2)
+
+/*
+ * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
+ * ignore. This helpers checks if the SKL driver handles this widget type
+ */
+static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_switch:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/*
+ * Each pipelines needs memory to be allocated. Check if we have free memory
+ * from available pool. Then only add this to pool
+ * This is freed when pipe is deleted
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mem + mconfig->pipe->memory_pages >
+ skl->resource.max_mem) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id,
+ mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d mem %d\n",
+ skl->resource.max_mem, skl->resource.mem);
+ return false;
+ }
+
+ skl->resource.mem += mconfig->pipe->memory_pages;
+ return true;
+}
+
+/*
+ * Pipeline needs needs DSP CPU resources for computation, this is
+ * quantified in MCPS (Million Clocks Per Second) required for module/pipe
+ *
+ * Each pipelines needs mcps to be allocated. Check if we have mcps for this
+ * pipe. This adds the mcps to driver counter
+ * This is removed on pipeline delete
+ */
+static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+ struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+
+ if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
+ dev_err(ctx->dev,
+ "%s: module_id %d instance %d\n", __func__,
+ mconfig->id.module_id, mconfig->id.instance_id);
+ dev_err(ctx->dev,
+ "exceeds ppl memory available %d > mem %d\n",
+ skl->resource.max_mcps, skl->resource.mcps);
+ return false;
+ }
+
+ skl->resource.mcps += mconfig->mcps;
+ return true;
+}
+
+/*
+ * Free the mcps when tearing down
+ */
+static void
+skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mcps -= mconfig->mcps;
+}
+
+/*
+ * Free the memory when tearing down
+ */
+static void
+skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ skl->resource.mem -= mconfig->pipe->memory_pages;
+}
+
+
+static void skl_dump_mconfig(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ dev_dbg(ctx->dev, "Dumping config\n");
+ dev_dbg(ctx->dev, "Input Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->in_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "Output Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+ dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n",
+ mcfg->out_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+}
+
+static void skl_tplg_update_params(struct skl_module_fmt *fmt,
+ struct skl_pipe_params *params, int fixup)
+{
+ if (fixup & SKL_RATE_FIXUP_MASK)
+ fmt->s_freq = params->s_freq;
+ if (fixup & SKL_CH_FIXUP_MASK)
+ fmt->channels = params->ch;
+ if (fixup & SKL_FMT_FIXUP_MASK)
+ fmt->valid_bit_depth = params->s_fmt;
+}
+
+/*
+ * A pipeline may have modules which impact the pcm parameters, like SRC,
+ * channel converter, format converter.
+ * We need to calculate the output params by applying the 'fixup'
+ * Topology will tell driver which type of fixup is to be applied by
+ * supplying the fixup mask, so based on that we calculate the output
+ *
+ * Now In FE the pcm hw_params is source/target format. Same is applicable
+ * for BE with its hw_params invoked.
+ * here based on FE, BE pipeline and direction we calculate the input and
+ * outfix and then apply that for a module
+ */
+static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
+ struct skl_pipe_params *params, bool is_fe)
+{
+ int in_fixup, out_fixup;
+ struct skl_module_fmt *in_fmt, *out_fmt;
+
+ in_fmt = &m_cfg->in_fmt;
+ out_fmt = &m_cfg->out_fmt;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (is_fe) {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ } else {
+ if (is_fe) {
+ out_fixup = m_cfg->params_fixup;
+ in_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ } else {
+ in_fixup = m_cfg->params_fixup;
+ out_fixup = (~m_cfg->converter) &
+ m_cfg->params_fixup;
+ }
+ }
+
+ skl_tplg_update_params(in_fmt, params, in_fixup);
+ skl_tplg_update_params(out_fmt, params, out_fixup);
+}
+
+/*
+ * A module needs input and output buffers, which are dependent upon pcm
+ * params, so once we have calculate params, we need buffer calculation as
+ * well.
+ */
+static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
+ struct skl_module_cfg *mcfg)
+{
+ int multiplier = 1;
+
+ if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
+ multiplier = 5;
+
+ mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
+ (mcfg->in_fmt.channels) *
+ (mcfg->in_fmt.bit_depth >> 3) *
+ multiplier;
+
+ mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
+ (mcfg->out_fmt.channels) *
+ (mcfg->out_fmt.bit_depth >> 3) *
+ multiplier;
+}
+
+static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
+ struct skl_sst *ctx)
+{
+ struct skl_module_cfg *m_cfg = w->priv;
+ struct skl_pipe_params *params = m_cfg->pipe->p_params;
+ int p_conn_type = m_cfg->pipe->conn_type;
+ bool is_fe;
+
+ if (!m_cfg->params_fixup)
+ return;
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+
+ if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
+ is_fe = true;
+ else
+ is_fe = false;
+
+ skl_tplg_update_params_fixup(m_cfg, params, is_fe);
+ skl_tplg_update_buffer_size(ctx, m_cfg);
+
+ dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
+ w->name);
+
+ skl_dump_mconfig(ctx, m_cfg);
+}
+
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
+ * to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_alloc_pipe_widget(struct device *dev,
+ struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
+{
+ struct skl_module_cfg *src_module = NULL;
+ struct snd_soc_dapm_path *p = NULL;
+ struct skl_pipe_module *p_module = NULL;
+
+ p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
+ if (!p_module)
+ return -ENOMEM;
+
+ p_module->w = w;
+ list_add_tail(&p_module->node, &pipe->w_list);
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if ((p->sink->priv == NULL)
+ && (!is_skl_dsp_widget_type(w)))
+ continue;
+
+ if ((p->sink->priv != NULL) && p->connect
+ && is_skl_dsp_widget_type(p->sink)) {
+
+ src_module = p->sink->priv;
+ if (pipe->ppl_id == src_module->pipe->ppl_id)
+ skl_tplg_alloc_pipe_widget(dev,
+ p->sink, pipe);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Inside a pipe instance, we can have various modules. These modules need
+ * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
+ * skl_init_module() routine, so invoke that for all modules in a pipeline
+ */
+static int
+skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
+{
+ struct skl_pipe_module *w_module;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ list_for_each_entry(w_module, &pipe->w_list, node) {
+ w = w_module->w;
+ mconfig = w->priv;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * apply fix/conversion to module params based on
+ * FE/BE params
+ */
+ skl_tplg_update_module_params(w, ctx);
+ ret = skl_init_module(ctx, mconfig, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ * - check the resources
+ * - Create the pipeline
+ * - Initialize the modules in pipeline
+ * - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret;
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -EBUSY;
+
+ if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * Create a list of modules for pipe.
+ * This list contains modules from source to sink
+ */
+ ret = skl_create_pipeline(ctx, mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * we create a w_list of all widgets in that pipe. This list is not
+ * freed on PMD event as widgets within a pipe are static. This
+ * saves us cycles to get widgets in pipe every time.
+ *
+ * So if we have already initialized all the widgets of a pipeline
+ * we skip, so check for list_empty and create the list if empty
+ */
+ if (list_empty(&s_pipe->w_list)) {
+ ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Init all pipe modules from source to sink */
+ ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Bind modules from source to sink */
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_bind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ * - Bind to sink pipeline
+ * Since the sink pipes can be running and we don't get mixer event on
+ * connect for already running mixer, we need to find the sink pipes
+ * here and bind to them. This way dynamic connect works.
+ * - Start sink pipeline, if not running
+ * - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_dapm_path_list *path_list;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ /*
+ * find which sink it is connected to, bind with the sink,
+ * if sink is not started, start sink pipe first, then start
+ * this pipe
+ */
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+ dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->sink->priv != NULL) &&
+ is_skl_dsp_widget_type(p->sink)) {
+
+ sink = p->sink;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+
+ /* Bind source to sink, mixin is always source */
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ /* Start sinks pipe first */
+ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+ }
+
+ path_list = kzalloc(
+ sizeof(struct skl_dapm_path_list),
+ GFP_KERNEL);
+ if (path_list == NULL)
+ return -ENOMEM;
+
+ /* Add connected path to one global list */
+ path_list->dapm_path = p;
+ list_add_tail(&path_list->node, &skl->dapm_path_list);
+ break;
+ }
+ }
+
+ /* Start source pipe last after starting all sinks */
+ ret = skl_run_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ * - Check if this pipe is running
+ * - if not, then
+ * - bind this pipeline to its source pipeline
+ * if source pipe is already running, this means it is a dynamic
+ * connection and we need to bind only to that pipe
+ * - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret = 0;
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int src_pipe_started = 0;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /*
+ * If source pipe is already started, that means source is driving
+ * one more sink before this sink got connected, Since source is
+ * started, bind this sink to source and start this pipe.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+ dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->source->priv != NULL) &&
+ is_skl_dsp_widget_type(p->source)) {
+ source = p->source;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+ src_pipe_started = 1;
+
+ /*
+ * check pipe state, then no need to bind or start
+ * the pipe
+ */
+ if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+ src_pipe_started = 0;
+ }
+ }
+
+ if (src_pipe_started) {
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ * - Stop the pipe
+ * - find the source connections and remove that from dapm_path_list
+ * - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /* Stop the pipe */
+ ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ /*
+ * This list, dapm_path_list handling here does not need any locks
+ * as we are under dapm lock while handling widget events.
+ * List can be manipulated safely only under dapm widgets handler
+ * routines
+ */
+ list_for_each_entry_safe(path_list, tmp_list,
+ &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->sink == sink) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ source = path_list->dapm_path->source;
+ src_mconfig = source->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * If path_found == 1, that means pmd for source pipe has
+ * not occurred, source is connected to some other sink.
+ * so its responsibility of sink to unbind itself from source.
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ * - Free the mcps used
+ * - Free the mem used
+ * - Unbind the modules within the pipeline
+ * - Delete the pipeline (modules are not required to be explicitly
+ * deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ int ret = 0;
+
+ skl_tplg_free_pipe_mcps(skl, mconfig);
+
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_unbind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ ret = skl_delete_pipe(ctx, mconfig->pipe);
+ skl_tplg_free_pipe_mem(skl, mconfig);
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ * - Free the mcps used
+ * - Stop the pipeline
+ * - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_path_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ skl_tplg_free_pipe_mcps(skl, src_mconfig);
+ /* Stop the pipe since this is a mixin module */
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->source == source) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ sink = path_list->dapm_path->sink;
+ sink_mconfig = sink->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * This is a connector and if path is found that means
+ * unbind between source and sink has not happened yet
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMU:
+ return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_PRE_PMD:
+ return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * The FE params are passed by hw_params of the DAI.
+ * On hw_params, the params are stored in Gateway module of the FE and we
+ * need to calculate the format in DSP module configuration, that
+ * conversion is done here
+ */
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct skl_module_fmt *format = NULL;
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ format = &mconfig->in_fmt;
+ else
+ format = &mconfig->out_fmt;
+
+ /* set the hw_params */
+ format->s_freq = params->s_freq;
+ format->channels = params->ch;
+ format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+ /*
+ * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+ * container so update bit depth accordingly
+ */
+ switch (format->valid_bit_depth) {
+ case SKL_DEPTH_16BIT:
+ format->bit_depth = format->valid_bit_depth;
+ break;
+
+ case SKL_DEPTH_24BIT:
+ format->bit_depth = SKL_DEPTH_32BIT;
+ break;
+
+ default:
+ dev_err(dev, "Invalid bit depth %x for pipe\n",
+ format->valid_bit_depth);
+ return -EINVAL;
+ }
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mconfig->ibs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ } else {
+ mconfig->obs = (format->s_freq / 1000) *
+ (format->channels) *
+ (format->bit_depth >> 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Query the module config for the FE DAI
+ * This is used to find the hw_params set for that DAI and apply to FE
+ * pipeline
+ */
+struct skl_module_cfg *
+skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && p->sink->power &&
+ is_skl_dsp_widget_type(p->sink))
+ continue;
+
+ if (p->sink->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->sink->name);
+ return p->sink->priv;
+ }
+ }
+ } else {
+ w = dai->capture_widget;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && p->source->power &&
+ is_skl_dsp_widget_type(p->source))
+ continue;
+
+ if (p->source->priv) {
+ dev_dbg(dai->dev, "set params for %s\n",
+ p->source->name);
+ return p->source->priv;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static u8 skl_tplg_be_link_type(int dev_type)
+{
+ int ret;
+
+ switch (dev_type) {
+ case SKL_DEVICE_BT:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_DMIC:
+ ret = NHLT_LINK_DMIC;
+ break;
+
+ case SKL_DEVICE_I2S:
+ ret = NHLT_LINK_SSP;
+ break;
+
+ case SKL_DEVICE_HDALINK:
+ ret = NHLT_LINK_HDA;
+ break;
+
+ default:
+ ret = NHLT_LINK_INVALID;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Fill the BE gateway parameters
+ * The BE gateway expects a blob of parameters which are kept in the ACPI
+ * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
+ * The port can have multiple settings so pick based on the PCM
+ * parameters
+ */
+static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
+ struct skl_module_cfg *mconfig,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mconfig->pipe;
+ struct nhlt_specific_cfg *cfg;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+
+ memcpy(pipe->p_params, params, sizeof(*params));
+
+ /* update the blob based on virtual bus_id*/
+ cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
+ params->s_fmt, params->ch,
+ params->s_freq, params->stream);
+ if (cfg) {
+ mconfig->formats_config.caps_size = cfg->size;
+ mconfig->formats_config.caps = (u32 *) &cfg->caps;
+ } else {
+ dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
+ mconfig->vbus_id, link_type,
+ params->stream);
+ dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
+ params->ch, params->s_freq, params->s_fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->source) &&
+ p->source->priv) {
+
+ if (!p->source->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->source->priv,
+ params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+ } else {
+ ret = skl_tplg_be_set_src_pipe_params(
+ dai, p->source, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
+ struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_path *p = NULL;
+ int ret = -EIO;
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connect && is_skl_dsp_widget_type(p->sink) &&
+ p->sink->priv) {
+
+ if (!p->sink->power) {
+ ret = skl_tplg_be_fill_pipe_params(
+ dai, p->sink->priv, params);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EBUSY;
+ }
+
+ } else {
+ ret = skl_tplg_be_set_sink_pipe_params(
+ dai, p->sink, params);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * BE hw_params can be a source parameters (capture) or sink parameters
+ * (playback). Based on sink and source we need to either find the source
+ * list or the sink list and set the pipeline parameters
+ */
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params)
+{
+ struct snd_soc_dapm_widget *w;
+
+ if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+
+ return skl_tplg_be_set_src_pipe_params(dai, w, params);
+
+ } else {
+ w = dai->capture_widget;
+
+ return skl_tplg_be_set_sink_pipe_params(dai, w, params);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
+ {SKL_MIXER_EVENT, skl_tplg_mixer_event},
+ {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+ {SKL_PGA_EVENT, skl_tplg_pga_event},
+};
+
+/*
+ * The topology binary passes the pin info for a module so initialize the pin
+ * info passed into module instance
+ */
+static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
+ struct skl_module_pin *m_pin,
+ bool is_dynamic, int max_pin)
+{
+ int i;
+
+ for (i = 0; i < max_pin; i++) {
+ m_pin[i].id.module_id = dfw_pin[i].module_id;
+ m_pin[i].id.instance_id = dfw_pin[i].instance_id;
+ m_pin[i].in_use = false;
+ m_pin[i].is_dynamic = is_dynamic;
+ }
+}
+
+/*
+ * Add pipeline from topology binary into driver pipeline list
+ *
+ * If already added we return that instance
+ * Otherwise we create a new instance and add into driver list
+ */
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
+ struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+{
+ struct skl_pipeline *ppl;
+ struct skl_pipe *pipe;
+ struct skl_pipe_params *params;
+
+ list_for_each_entry(ppl, &skl->ppl_list, node) {
+ if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
+ return ppl->pipe;
+ }
+
+ ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
+ if (!ppl)
+ return NULL;
+
+ pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
+ if (!pipe)
+ return NULL;
+
+ params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return NULL;
+
+ pipe->ppl_id = dfw_pipe->pipe_id;
+ pipe->memory_pages = dfw_pipe->memory_pages;
+ pipe->pipe_priority = dfw_pipe->pipe_priority;
+ pipe->conn_type = dfw_pipe->conn_type;
+ pipe->state = SKL_PIPE_INVALID;
+ pipe->p_params = params;
+ INIT_LIST_HEAD(&pipe->w_list);
+
+ ppl->pipe = pipe;
+ list_add(&ppl->node, &skl->ppl_list);
+
+ return ppl->pipe;
+}
+
+/*
+ * Topology core widget load callback
+ *
+ * This is used to save the private data for each widget which gives
+ * information to the driver about module and pipeline parameters which DSP
+ * FW expects like ids, resource values, formats etc
+ */
+static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ int ret;
+ struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl_module_cfg *mconfig;
+ struct skl_pipe *pipe;
+ struct skl_dfw_module *dfw_config =
+ (struct skl_dfw_module *)tplg_w->priv.data;
+
+ if (!tplg_w->priv.size)
+ goto bind_event;
+
+ mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
+
+ if (!mconfig)
+ return -ENOMEM;
+
+ w->priv = mconfig;
+ mconfig->id.module_id = dfw_config->module_id;
+ mconfig->id.instance_id = dfw_config->instance_id;
+ mconfig->mcps = dfw_config->max_mcps;
+ mconfig->ibs = dfw_config->ibs;
+ mconfig->obs = dfw_config->obs;
+ mconfig->core_id = dfw_config->core_id;
+ mconfig->max_in_queue = dfw_config->max_in_queue;
+ mconfig->max_out_queue = dfw_config->max_out_queue;
+ mconfig->is_loadable = dfw_config->is_loadable;
+ mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
+ mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
+ mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
+ mconfig->in_fmt.valid_bit_depth =
+ dfw_config->in_fmt.valid_bit_depth;
+ mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
+ mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
+ mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
+ mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
+ mconfig->out_fmt.valid_bit_depth =
+ dfw_config->out_fmt.valid_bit_depth;
+ mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+ mconfig->params_fixup = dfw_config->params_fixup;
+ mconfig->converter = dfw_config->converter;
+ mconfig->m_type = dfw_config->module_type;
+ mconfig->vbus_id = dfw_config->vbus_id;
+
+ pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
+ if (pipe)
+ mconfig->pipe = pipe;
+
+ mconfig->dev_type = dfw_config->dev_type;
+ mconfig->hw_conn_type = dfw_config->hw_conn_type;
+ mconfig->time_slot = dfw_config->time_slot;
+ mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
+
+ mconfig->m_in_pin = devm_kzalloc(bus->dev,
+ (mconfig->max_in_queue) *
+ sizeof(*mconfig->m_in_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_in_pin)
+ return -ENOMEM;
+
+ mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
+ sizeof(*mconfig->m_out_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_out_pin)
+ return -ENOMEM;
+
+ skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
+ dfw_config->is_dynamic_in_pin,
+ mconfig->max_in_queue);
+
+ skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
+ dfw_config->is_dynamic_out_pin,
+ mconfig->max_out_queue);
+
+
+ if (mconfig->formats_config.caps_size == 0)
+ goto bind_event;
+
+ mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
+ mconfig->formats_config.caps_size, GFP_KERNEL);
+
+ if (mconfig->formats_config.caps == NULL)
+ return -ENOMEM;
+
+ memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
+ dfw_config->caps.caps_size);
+
+bind_event:
+ if (tplg_w->event_type == 0) {
+ dev_dbg(bus->dev, "ASoC: No event handler required\n");
+ return 0;
+ }
+
+ ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
+ ARRAY_SIZE(skl_tplg_widget_ops),
+ tplg_w->event_type);
+
+ if (ret) {
+ dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
+ __func__, tplg_w->event_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_tplg_ops skl_tplg_ops = {
+ .widget_load = skl_tplg_widget_load,
+};
+
+/* This will be read from topology manifest, currently defined here */
+#define SKL_MAX_MCPS 30000000
+#define SKL_FW_MAX_MEM 1000000
+
+/*
+ * SKL topology init routine
+ */
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
+{
+ int ret;
+ const struct firmware *fw;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg fw %s load failed with %d\n",
+ "dfw_sst.bin", ret);
+ return ret;
+ }
+
+ /*
+ * The complete tplg for SKL is loaded as index 0, we don't use
+ * any other index
+ */
+ ret = snd_soc_tplg_component_load(&platform->component,
+ &skl_tplg_ops, fw, 0);
+ if (ret < 0) {
+ dev_err(bus->dev, "tplg component load failed%d\n", ret);
+ return -EINVAL;
+ }
+
+ skl->resource.max_mcps = SKL_MAX_MCPS;
+ skl->resource.max_mem = SKL_FW_MAX_MEM;
+
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index 8c7767baa94f..76053a8de41c 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -129,6 +129,11 @@ struct skl_src_module_cfg {
enum skl_s_freq src_cfg;
} __packed;
+struct notification_mask {
+ u32 notify;
+ u32 enable;
+} __packed;
+
struct skl_up_down_mixer_cfg {
struct skl_base_cfg base_cfg;
enum skl_ch_cfg out_ch_cfg;
@@ -153,8 +158,7 @@ enum skl_dma_type {
union skl_ssp_dma_node {
u8 val;
struct {
- u8 dual_mono:1;
- u8 time_slot:3;
+ u8 time_slot_index:4;
u8 i2s_instance:4;
} dma_node;
};
@@ -263,6 +267,34 @@ struct skl_module_cfg {
struct skl_specific_cfg formats_config;
};
+struct skl_pipeline {
+ struct skl_pipe *pipe;
+ struct list_head node;
+};
+
+struct skl_dapm_path_list {
+ struct snd_soc_dapm_path *dapm_path;
+ struct list_head node;
+};
+
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+ return ebus_to_skl(ebus);
+}
+
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params);
+void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
+ struct skl_pipe_params *params, int stream);
+int skl_tplg_init(struct snd_soc_platform *platform,
+ struct hdac_ext_bus *ebus);
+struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
+ struct snd_soc_dai *dai, int stream);
+int skl_tplg_update_pipe_params(struct device *dev,
+ struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+
int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
index a50689825bca..2bc396d54cbe 100644
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ b/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -19,6 +19,29 @@
#ifndef __HDA_TPLG_INTERFACE_H__
#define __HDA_TPLG_INTERFACE_H__
+/*
+ * Default types range from 0~12. type can range from 0 to 0xff
+ * SST types start at higher to avoid any overlapping in future
+ */
+#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100
+#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101
+#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103
+
+#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
+#define MAX_IN_QUEUE 8
+#define MAX_OUT_QUEUE 8
+
+/* Event types goes here */
+/* Reserve event type 0 for no event handlers */
+enum skl_event_types {
+ SKL_EVENT_NONE = 0,
+ SKL_MIXER_EVENT,
+ SKL_MUX_EVENT,
+ SKL_VMIXER_EVENT,
+ SKL_PGA_EVENT
+};
+
/**
* enum skl_ch_cfg - channel configuration
*
@@ -83,6 +106,66 @@ enum skl_dev_type {
SKL_DEVICE_I2S = 0x2,
SKL_DEVICE_SLIMBUS = 0x3,
SKL_DEVICE_HDALINK = 0x4,
+ SKL_DEVICE_HDAHOST = 0x5,
SKL_DEVICE_NONE
};
+
+struct skl_dfw_module_pin {
+ u16 module_id;
+ u16 instance_id;
+} __packed;
+
+struct skl_dfw_module_fmt {
+ u32 channels;
+ u32 freq;
+ u32 bit_depth;
+ u32 valid_bit_depth;
+ u32 ch_cfg;
+} __packed;
+
+struct skl_dfw_module_caps {
+ u32 caps_size;
+ u32 caps[HDA_SST_CFG_MAX];
+};
+
+struct skl_dfw_pipe {
+ u8 pipe_id;
+ u8 pipe_priority;
+ u16 conn_type;
+ u32 memory_pages;
+} __packed;
+
+struct skl_dfw_module {
+ u16 module_id;
+ u16 instance_id;
+ u32 max_mcps;
+ u8 core_id;
+ u8 max_in_queue;
+ u8 max_out_queue;
+ u8 is_loadable;
+ u8 conn_type;
+ u8 dev_type;
+ u8 hw_conn_type;
+ u8 time_slot;
+ u32 obs;
+ u32 ibs;
+ u32 params_fixup;
+ u32 converter;
+ u32 module_type;
+ u32 vbus_id;
+ u8 is_dynamic_in_pin;
+ u8 is_dynamic_out_pin;
+ struct skl_dfw_pipe pipe;
+ struct skl_dfw_module_fmt in_fmt;
+ struct skl_dfw_module_fmt out_fmt;
+ struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
+ struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
+ struct skl_dfw_module_caps caps;
+} __packed;
+
+struct skl_dfw_algo_data {
+ u32 max;
+ char *params;
+} __packed;
+
#endif
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 348d094e81d6..5319529aedf7 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -166,12 +166,20 @@ static int skl_runtime_suspend(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+ int ret;
dev_dbg(bus->dev, "in %s\n", __func__);
/* enable controller wake up event */
snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
+ snd_hdac_ext_bus_link_power_down_all(ebus);
+
+ ret = skl_suspend_dsp(skl);
+ if (ret < 0)
+ return ret;
+
snd_hdac_bus_stop_chip(bus);
snd_hdac_bus_enter_link_reset(bus);
@@ -183,7 +191,7 @@ static int skl_runtime_resume(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl *hda = ebus_to_skl(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
int status;
dev_dbg(bus->dev, "in %s\n", __func__);
@@ -191,12 +199,12 @@ static int skl_runtime_resume(struct device *dev)
/* Read STATESTS before controller reset */
status = snd_hdac_chip_readw(bus, STATESTS);
- skl_init_pci(hda);
+ skl_init_pci(skl);
snd_hdac_bus_init_chip(bus, true);
/* disable controller Wake Up event */
snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
- return 0;
+ return skl_resume_dsp(skl);
}
#endif /* CONFIG_PM */
@@ -453,21 +461,28 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
+ skl->nhlt = skl_nhlt_init(bus->dev);
+
+ if (skl->nhlt == NULL)
+ goto out_free;
+
pci_set_drvdata(skl->pci, ebus);
/* check if dsp is there */
if (ebus->ppcap) {
- /* TODO register with dsp IPC */
- dev_dbg(bus->dev, "Register dsp\n");
+ err = skl_init_dsp(skl);
+ if (err < 0) {
+ dev_dbg(bus->dev, "error failed to register dsp\n");
+ goto out_free;
+ }
}
-
if (ebus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus);
/* create device for soc dmic */
err = skl_dmic_device_register(skl);
if (err < 0)
- goto out_free;
+ goto out_dsp_free;
/* register platform dai and controls */
err = skl_platform_register(bus->dev);
@@ -491,6 +506,8 @@ out_unregister:
skl_platform_unregister(bus->dev);
out_dmic_free:
skl_dmic_device_unregister(skl);
+out_dsp_free:
+ skl_free_dsp(skl);
out_free:
skl->init_failed = 1;
skl_free(ebus);
@@ -507,6 +524,7 @@ static void skl_remove(struct pci_dev *pci)
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);
skl_platform_unregister(&pci->dev);
+ skl_free_dsp(skl);
skl_dmic_device_unregister(skl);
skl_free(ebus);
dev_set_drvdata(&pci->dev, NULL);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index f7fdbb02947f..dd2e79ae45a8 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -48,6 +48,13 @@
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+struct skl_dsp_resource {
+ u32 max_mcps;
+ u32 max_mem;
+ u32 mcps;
+ u32 mem;
+};
+
struct skl {
struct hdac_ext_bus ebus;
struct pci_dev *pci;
@@ -55,8 +62,12 @@ struct skl {
unsigned int init_failed:1; /* delayed init failed */
struct platform_device *dmic_dev;
- void __iomem *nhlt; /* nhlt ptr */
+ void *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
+
+ struct skl_dsp_resource resource;
+ struct list_head ppl_list;
+ struct list_head dapm_path_list;
};
#define skl_to_ebus(s) (&(s)->ebus)
@@ -72,8 +83,8 @@ struct skl_dma_params {
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
-void __iomem *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void __iomem *addr);
+void *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(void *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index b05fb1c1a848..794a3499e567 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = {
{ .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, jz4740_of_matches);
#endif
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c
index de7563bdc5c2..e0304d544f26 100644
--- a/sound/soc/kirkwood/armada-370-db.c
+++ b/sound/soc/kirkwood/armada-370-db.c
@@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = {
{ .compatible = "marvell,a370db-audio" },
{ },
};
+MODULE_DEVICE_TABLE(of, a370db_dt_ids);
static struct platform_driver a370db_driver = {
.driver = {
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c
index 684e8a78bed0..71a1a35047ba 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173-max98090.c
@@ -179,21 +179,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
}
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
-static int mt8173_max98090_dev_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static const struct of_device_id mt8173_max98090_dt_match[] = {
{ .compatible = "mediatek,mt8173-max98090", },
{ }
@@ -209,7 +201,6 @@ static struct platform_driver mt8173_max98090_driver = {
#endif
},
.probe = mt8173_max98090_dev_probe,
- .remove = mt8173_max98090_dev_remove,
};
module_platform_driver(mt8173_max98090_driver);
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
index 86cf9752f18a..50ba538eccb3 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -246,21 +246,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
-static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
{ .compatible = "mediatek,mt8173-rt5650-rt5676", },
{ }
@@ -276,7 +268,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
#endif
},
.probe = mt8173_rt5650_rt5676_dev_probe,
- .remove = mt8173_rt5650_rt5676_dev_remove,
};
module_platform_driver(mt8173_rt5650_rt5676_driver);
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c
index d190fe017559..f5baf3c38863 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mtk-afe-pcm.c
@@ -549,6 +549,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
memif->substream = substream;
snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+
+ /*
+ * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+ * smaller than period_size due to AFE's internal buffer.
+ * This easily leads to overrun when avail_min is period_size.
+ * One more period can hold the possible unread buffer.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS,
+ 3,
+ mtk_afe_hardware.periods_max);
+ if (ret < 0) {
+ dev_err(afe->dev, "hw_constraint_minmax failed\n");
+ return ret;
+ }
+ }
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 6e6fce6a14ba..2b23ffbac6b1 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
@@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
static int mxs_sgtl5000_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
mxs_saif_put_mclk(0);
- snd_soc_unregister_card(card);
-
return 0;
}
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 3bebfb1d3a6f..99538900a253 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -297,7 +297,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
return err;
}
- snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+ snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
err = omap_mcbsp_st_add_controls(rtd, 2);
if (err < 0) {
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 39cea80846c3..f2bf8661dd21 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,7 +1,6 @@
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
depends on ARCH_PXA
- select SND_ARM
select SND_PXA2XX_LIB
help
Say Y or M if you want to add support for codecs attached to
@@ -25,7 +24,6 @@ config SND_PXA2XX_AC97
config SND_PXA2XX_SOC_AC97
tristate
select AC97_BUS
- select SND_ARM
select SND_PXA2XX_LIB_AC97
select SND_SOC_AC97_BUS
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 2b26318bc200..6147e86e9b0f 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev)
int ret;
brownstone.dev = &pdev->dev;
- ret = snd_soc_register_card(&brownstone);
+ ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int brownstone_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&brownstone);
- return 0;
-}
-
static struct platform_driver mmp_driver = {
.driver = {
.name = "brownstone-audio",
.pm = &snd_soc_pm_ops,
},
.probe = brownstone_probe,
- .remove = brownstone_remove,
};
module_platform_driver(mmp_driver);
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 3580d10c9f28..c97dc13d3608 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int corgi_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver corgi_driver = {
.driver = {
.name = "corgi-audio",
.pm = &snd_soc_pm_ops,
},
.probe = corgi_probe,
- .remove = corgi_remove,
};
module_platform_driver(corgi_driver);
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
index d72e124a3676..1de876529aa1 100644
--- a/sound/soc/pxa/e740_wm9705.c
+++ b/sound/soc/pxa/e740_wm9705.c
@@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev)
static int e740_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index 48f2d7c2e68c..b7eb7cd5df7d 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev)
static int e750_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 45d4bd46fff6..41bf71466a7b 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev)
static int e800_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
index 9f8be7cd567e..ecbf2873b7ff 100644
--- a/sound/soc/pxa/hx4700.c
+++ b/sound/soc/pxa/hx4700.c
@@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev)
return ret;
snd_soc_card_hx4700.dev = &pdev->dev;
- ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
if (ret)
gpio_free_array(hx4700_audio_gpios,
ARRAY_SIZE(hx4700_audio_gpios));
@@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
static int hx4700_audio_remove(struct platform_device *pdev)
{
- snd_soc_unregister_card(&snd_soc_card_hx4700);
-
gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
index 29fabbfd21f1..9d0e40771ef5 100644
--- a/sound/soc/pxa/imote2.c
+++ b/sound/soc/pxa/imote2.c
@@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int imote2_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver imote2_driver = {
.driver = {
.name = "imote2-audio",
.pm = &snd_soc_pm_ops,
},
.probe = imote2_probe,
- .remove = imote2_remove,
};
module_platform_driver(imote2_driver);
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index a9615a574546..29bc60e85e92 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return -ENODEV;
mioa701.dev = &pdev->dev;
- rc = snd_soc_register_card(&mioa701);
+ rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
if (!rc)
dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
"lead to overheating and possible destruction of your device."
@@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
return rc;
}
-static int mioa701_wm9713_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver mioa701_wm9713_driver = {
.probe = mioa701_wm9713_probe,
- .remove = mioa701_wm9713_remove,
.driver = {
.name = "mioa701-wm9713",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index c20bbc042425..4e74d9573f03 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
palm27x_asoc.dev = &pdev->dev;
- ret = snd_soc_register_card(&palm27x_asoc);
+ ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int palm27x_asoc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&palm27x_asoc);
- return 0;
-}
-
static struct platform_driver palm27x_wm9712_driver = {
.probe = palm27x_asoc_probe,
- .remove = palm27x_asoc_remove,
.driver = {
.name = "palm27x-asoc",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 80b457ac522a..84d0e2e50808 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
-static int poodle_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- return 0;
-}
-
static struct platform_driver poodle_driver = {
.driver = {
.name = "poodle-audio",
.pm = &snd_soc_pm_ops,
},
.probe = poodle_probe,
- .remove = poodle_remove,
};
module_platform_driver(poodle_driver);
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3da485ec1de7..da03fad1b9cd 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = {
{ .compatible = "mrvl,pxa-ssp-dai" },
{}
};
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
#endif
static int asoc_ssp_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 1f6054650991..f3de615aacd7 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
@@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.reset = pxa2xx_ac97_cold_reset,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 11,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.filter_data = &pxa2xx_ac97_pcm_stereo_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 12,
+};
+
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.addr = __PREG(PCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.filter_data = &pxa2xx_ac97_pcm_stereo_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 10,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 9,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.addr = __PREG(MODR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req,
};
-static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
+ .prio = PXAD_PRIO_LOWEST,
+ .drcmr = 8,
+};
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.addr = __PREG(MCDR),
.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req,
};
-static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
@@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_48000)
static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_params,
+ .startup = pxa2xx_ac97_hifi_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_aux_params,
+ .startup = pxa2xx_ac97_aux_startup,
};
static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
- .hw_params = pxa2xx_ac97_hw_mic_params,
+ .startup = pxa2xx_ac97_mic_startup,
};
/*
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 6b4e40036910..0389cf7b4b1e 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
/* Along with FIFO servicing */
SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+ snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
+ &pxa2xx_i2s_pcm_stereo_in);
+
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 831ee37d2e3e..9f390398d518 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -15,8 +15,6 @@
#include <linux/dmaengine.h>
#include <linux/of.h>
-#include <mach/dma.h>
-
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
@@ -27,11 +25,8 @@
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_dmaengine_dai_dma_data *dma;
- int ret;
dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
if (!dma)
return 0;
- /* this may get called several times by oss emulation
- * with different params */
- if (prtd->params == NULL) {
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- } else if (prtd->params != dma) {
- pxa_free_dma(prtd->dma_ch);
- prtd->params = dma;
- ret = pxa_request_dma("name", DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- return ret;
- prtd->dma_ch = ret;
- }
-
return __pxa2xx_pcm_hw_params(substream, params);
}
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
__pxa2xx_pcm_hw_free(substream);
- if (prtd->dma_ch >= 0) {
- pxa_free_dma(prtd->dma_ch);
- prtd->dma_ch = -1;
- prtd->params = NULL;
- }
-
return 0;
}
@@ -132,6 +100,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = {
{ .compatible = "mrvl,pxa-pcm-audio" },
{ }
};
+MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
#endif
static struct platform_driver pxa_pcm_driver = {
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 461123ad5ff2..b00222620fd0 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -322,9 +322,6 @@ err1:
static int spitz_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
gpio_free(spitz_mic_gpio);
return 0;
}
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index f59f566551ef..49518dd642aa 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev)
static int tosa_remove(struct platform_device *pdev)
{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
gpio_free(TOSA_GPIO_L_MUTE);
- snd_soc_unregister_card(card);
return 0;
}
diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c
index 1753c7d9e760..65c20f779177 100644
--- a/sound/soc/pxa/ttc-dkb.c
+++ b/sound/soc/pxa/ttc-dkb.c
@@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev)
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
@@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev)
return ret;
}
-static int ttc_dkb_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
-}
-
static struct platform_driver ttc_dkb_driver = {
.driver = {
.name = "ttc-dkb-audio",
.pm = &snd_soc_pm_ops,
},
.probe = ttc_dkb_probe,
- .remove = ttc_dkb_remove,
};
module_platform_driver(ttc_dkb_driver);
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 97bc2023f08a..e5101e0d2d37 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
dev_err(&pdev->dev,
"%s() error getting mi2s-bit-clk: %ld\n",
- __func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+ __func__,
+ PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
}
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 58bae8e2cf5f..f1e0c703e0d2 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -15,9 +15,17 @@ config SND_SOC_ROCKCHIP_I2S
Rockchip I2S device. The device supports upto maximum of
8 channels each for play and record.
+config SND_SOC_ROCKCHIP_SPDIF
+ tristate "Rockchip SPDIF Device Driver"
+ depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for SPDIF driver for
+ Rockchip SPDIF transceiver device.
+
config SND_SOC_ROCKCHIP_MAX98090
tristate "ASoC support for Rockchip boards using a MAX98090 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
@@ -27,7 +35,7 @@ config SND_SOC_ROCKCHIP_MAX98090
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_RT5645
help
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 1bc1dc3c729a..c0bf560125f3 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -1,7 +1,9 @@
# ROCKCHIP Platform Support
-snd-soc-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-spdif-objs := rockchip_spdif.o
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index b93610212e3d..58ee64594f07 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int val = 0;
switch (params_format(params)) {
@@ -245,13 +246,46 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
- regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
+ switch (params_channels(params)) {
+ case 8:
+ val |= I2S_CHN_8;
+ break;
+ case 6:
+ val |= I2S_CHN_6;
+ break;
+ case 4:
+ val |= I2S_CHN_4;
+ break;
+ case 2:
+ val |= I2S_CHN_2;
+ break;
+ default:
+ dev_err(i2s->dev, "invalid channel: %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_update_bits(i2s->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ val);
+ else
+ regmap_update_bits(i2s->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ val);
+
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
I2S_DMACR_RDL(16));
+ val = I2S_CKR_TRCM_TXRX;
+ if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
+ val = I2S_CKR_TRCM_TXSHARE;
+
+ regmap_update_bits(i2s->regmap, I2S_CKR,
+ I2S_CKR_TRCM_MASK,
+ val);
return 0;
}
@@ -415,10 +449,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
static int rockchip_i2s_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s;
struct resource *res;
void __iomem *regs;
int ret;
+ int val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s) {
@@ -475,6 +511,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ /* refine capture channels */
+ if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ rockchip_i2s_dai.capture.channels_max = val;
+ else
+ rockchip_i2s_dai.capture.channels_max = 2;
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
&rockchip_i2s_dai, 1);
diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h
index 93f456f518a9..dc6e2c74d088 100644
--- a/sound/soc/rockchip/rockchip_i2s.h
+++ b/sound/soc/rockchip/rockchip_i2s.h
@@ -49,6 +49,9 @@
* RXCR
* receive operation control register
*/
+#define I2S_RXCR_CSR_SHIFT 15
+#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
@@ -75,6 +78,12 @@
* CKR
* clock generation register
*/
+#define I2S_CKR_TRCM_SHIFT 28
+#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
@@ -207,6 +216,13 @@ enum {
ROCKCHIP_DIV_BCLK,
};
+/* channel select */
+#define I2S_CSR_SHIFT 15
+#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
+
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
new file mode 100644
index 000000000000..a38a3029062c
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -0,0 +1,405 @@
+/* sound/soc/rockchip/rk_spdif.c
+ *
+ * ALSA SoC Audio Layer - Rockchip I2S Controller driver
+ *
+ * Copyright (c) 2014 Rockchip Electronics Co. Ltd.
+ * Author: Jianqun <jay.xu@rock-chips.com>
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "rockchip_spdif.h"
+
+enum rk_spdif_type {
+ RK_SPDIF_RK3066,
+ RK_SPDIF_RK3188,
+ RK_SPDIF_RK3288,
+};
+
+#define RK3288_GRF_SOC_CON2 0x24c
+
+struct rk_spdif_dev {
+ struct device *dev;
+
+ struct clk *mclk;
+ struct clk *hclk;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+ struct regmap *regmap;
+};
+
+static const struct of_device_id rk_spdif_match[] = {
+ { .compatible = "rockchip,rk3066-spdif",
+ .data = (void *) RK_SPDIF_RK3066 },
+ { .compatible = "rockchip,rk3188-spdif",
+ .data = (void *) RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3288-spdif",
+ .data = (void *) RK_SPDIF_RK3288 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
+static int rk_spdif_runtime_suspend(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static int rk_spdif_runtime_resume(struct device *dev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
+ int srate, mclk;
+ int ret;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 32000:
+ case 48000:
+ case 96000:
+ mclk = 96000 * 128; /* 12288000 hz */
+ break;
+ case 44100:
+ mclk = 44100 * 256; /* 11289600 hz */
+ break;
+ case 192000:
+ mclk = 192000 * 128; /* 24576000 hz */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val |= SPDIF_CFGR_VDW_16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val |= SPDIF_CFGR_VDW_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val |= SPDIF_CFGR_VDW_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set clock and calculate divider */
+ ret = clk_set_rate(spdif->mclk, mclk);
+ if (ret != 0) {
+ dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
+ ret);
+ return ret;
+ }
+
+ val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
+ ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
+ SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
+ SDPIF_CFGR_VDW_MASK,
+ val);
+
+ return ret;
+}
+
+static int rk_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_ENABLE);
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_START);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+ SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_DISABLE);
+
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+ SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_STOP);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif->playback_dma_data;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .hw_params = rk_spdif_hw_params,
+ .trigger = rk_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver rk_spdif_dai = {
+ .probe = rk_spdif_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &rk_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver rk_spdif_component = {
+ .name = "rockchip-spdif",
+};
+
+static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_DMACR:
+ case SPDIF_INTCR:
+ case SPDIF_XFER:
+ case SPDIF_SMPDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_CFGR:
+ case SPDIF_SDBLR:
+ case SPDIF_INTCR:
+ case SPDIF_INTSR:
+ case SPDIF_XFER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIF_INTSR:
+ case SPDIF_SDBLR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rk_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SPDIF_SMPDR,
+ .writeable_reg = rk_spdif_wr_reg,
+ .readable_reg = rk_spdif_rd_reg,
+ .volatile_reg = rk_spdif_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rk_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct rk_spdif_dev *spdif;
+ const struct of_device_id *match;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ match = of_match_node(rk_spdif_match, np);
+ if ((int) match->data == RK_SPDIF_RK3288) {
+ struct regmap *grf;
+
+ grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev,
+ "rockchip_spdif missing 'rockchip,grf' \n");
+ return PTR_ERR(grf);
+ }
+
+ /* Select the 8 channel SPDIF solution on RK3288 as
+ * the 2 channel one does not appear to work
+ */
+ regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16);
+ }
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+ if (!spdif)
+ return -ENOMEM;
+
+ spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(spdif->hclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n");
+ return PTR_ERR(spdif->hclk);
+ }
+ ret = clk_prepare_enable(spdif->hclk);
+ if (ret) {
+ dev_err(spdif->dev, "hclock enable failed %d\n", ret);
+ return ret;
+ }
+
+ spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(spdif->mclk)) {
+ dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n");
+ return PTR_ERR(spdif->mclk);
+ }
+
+ ret = clk_prepare_enable(spdif->mclk);
+ if (ret) {
+ dev_err(spdif->dev, "clock enable failed %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
+ &rk_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap)) {
+ dev_err(&pdev->dev,
+ "Failed to initialise managed register map\n");
+ return PTR_ERR(spdif->regmap);
+ }
+
+ spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
+ spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ spdif->playback_dma_data.maxburst = 4;
+
+ spdif->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_request_idle(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rk_spdif_component,
+ &rk_spdif_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_pm_runtime;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM\n");
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int rk_spdif_remove(struct platform_device *pdev)
+{
+ struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ rk_spdif_runtime_suspend(&pdev->dev);
+
+ clk_disable_unprepare(spdif->mclk);
+ clk_disable_unprepare(spdif->hclk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rk_spdif_pm_ops = {
+ SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver rk_spdif_driver = {
+ .probe = rk_spdif_probe,
+ .remove = rk_spdif_remove,
+ .driver = {
+ .name = "rockchip-spdif",
+ .of_match_table = of_match_ptr(rk_spdif_match),
+ .pm = &rk_spdif_pm_ops,
+ },
+};
+module_platform_driver(rk_spdif_driver);
+
+MODULE_ALIAS("platform:rockchip-spdif");
+MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface");
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
new file mode 100644
index 000000000000..07f86a21046a
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -0,0 +1,63 @@
+/*
+ * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
+ *
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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 _ROCKCHIP_SPDIF_H
+#define _ROCKCHIP_SPDIF_H
+
+/*
+ * CFGR
+ * transfer configuration register
+*/
+#define SPDIF_CFGR_CLK_DIV_SHIFT (16)
+#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+
+#define SPDIF_CFGR_HALFWORD_SHIFT 2
+#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+
+#define SPDIF_CFGR_VDW_SHIFT 0
+#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
+#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
+
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10)
+
+/*
+ * DMACR
+ * DMA control register
+*/
+#define SPDIF_DMACR_TDE_SHIFT 5
+#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+
+#define SPDIF_DMACR_TDL_SHIFT 0
+#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT)
+
+/*
+ * XFER
+ * Transfer control register
+*/
+#define SPDIF_XFER_TXS_SHIFT 0
+#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT)
+
+#define SPDIF_CFGR (0x0000)
+#define SPDIF_SDBLR (0x0004)
+#define SPDIF_DMACR (0x0008)
+#define SPDIF_INTCR (0x000c)
+#define SPDIF_INTSR (0x0010)
+#define SPDIF_XFER (0x0018)
+#define SPDIF_SMPDR (0x0020)
+
+#endif /* _ROCKCHIP_SPDIF_H */
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 07114b0b0dc1..6ca90aaf141f 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on DMA_OF
+ depends on COMMON_CLK
select SND_SIMPLE_CARD
select REGMAP_MMIO
help
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index fefc881dbac2..2a5b3a293cd2 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -7,7 +7,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
-#include <linux/sh_clk.h>
+#include <linux/clk-provider.h>
#include "rsnd.h"
#define CLKA 0
@@ -16,12 +16,26 @@
#define CLKI 3
#define CLKMAX 4
+#define CLKOUT 0
+#define CLKOUT1 1
+#define CLKOUT2 2
+#define CLKOUT3 3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+ .name = "adg",
+};
+
struct rsnd_adg {
struct clk *clk[CLKMAX];
+ struct clk *clkout[CLKOUTMAX];
+ struct clk_onecell_data onecell;
+ struct rsnd_mod mod;
- int rbga_rate_for_441khz_div_6; /* RBGA */
- int rbgb_rate_for_48khz_div_6; /* RBGB */
- u32 ckr;
+ int rbga_rate_for_441khz; /* RBGA */
+ int rbgb_rate_for_48khz; /* RBGB */
};
#define for_each_rsnd_clk(pos, adg, i) \
@@ -29,17 +43,36 @@ struct rsnd_adg {
(i < CLKMAX) && \
((pos) = adg->clk[i]); \
i++)
+#define for_each_rsnd_clkout(pos, adg, i) \
+ for (i = 0; \
+ (i < CLKOUTMAX) && \
+ ((pos) = adg->clkout[i]); \
+ i++)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+ int i, ratio;
+
+ if (!div)
+ return 0;
+
+ for (i = 3; i >= 0; i--) {
+ ratio = 2 << (i * 2);
+ if (0 == (div % ratio))
+ return (u32)((i << 8) | ((div / ratio) - 1));
+ }
+
+ return ~0;
+}
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
int id = rsnd_mod_id(mod);
int ws = id;
- if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
switch (id) {
case 1:
case 2:
@@ -60,6 +93,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int id = rsnd_mod_id(mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
@@ -69,21 +105,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
val = val << shift;
mask = 0xffff << shift;
- rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+ rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
return 0;
}
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
u32 timsel)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int is_play = rsnd_io_is_play(io);
- int id = rsnd_mod_id(mod);
+ int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, ws;
u32 in, out;
+ rsnd_mod_confirm_src(src_mod);
+
ws = rsnd_adg_ssi_ws_timing_gen2(io);
in = (is_play) ? timsel : ws;
@@ -95,37 +136,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
switch (id / 2) {
case 0:
- rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
break;
case 1:
- rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
break;
case 2:
- rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
break;
case 3:
- rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
break;
case 4:
- rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in);
- rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+ rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in);
+ rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
break;
}
return 0;
}
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
unsigned int dst_rate)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, step, ret;
u32 val, en;
@@ -134,10 +176,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+ adg->rbga_rate_for_441khz, /* 0011: RBGA */
+ adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
+ rsnd_mod_confirm_src(src_mod);
+
min = ~0;
val = 0;
en = 0;
@@ -175,25 +219,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
return -EIO;
}
- ret = rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
if (ret < 0) {
dev_err(dev, "timsel error\n");
return ret;
}
- rsnd_mod_bset(mod, DIV_EN, en, en);
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
return 0;
}
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io)
{
u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
- return rsnd_adg_set_src_timsel_gen2(mod, io, val);
+ rsnd_mod_confirm_src(src_mod);
+
+ return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
}
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
@@ -202,6 +248,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
unsigned int dst_rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, shift;
u32 mask, val;
@@ -211,8 +258,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
0, /* 011: MLBCLK (not used) */
- adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
- adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+ adg->rbga_rate_for_441khz, /* 100: RBGA */
+ adg->rbgb_rate_for_48khz, /* 101: RBGB */
};
/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
@@ -238,13 +285,13 @@ find_rate:
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
break;
}
@@ -257,12 +304,17 @@ find_rate:
return 0;
}
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
- int id = rsnd_mod_id(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ int id = rsnd_mod_id(ssi_mod);
int shift = (id % 4) * 8;
u32 mask = 0xFF << shift;
+ rsnd_mod_confirm_ssi(ssi_mod);
+
val = val << shift;
/*
@@ -274,13 +326,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
switch (id / 4) {
case 0:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
break;
case 1:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
break;
case 2:
- rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+ rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
break;
}
}
@@ -326,14 +378,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
}
/*
- * find 1/6 clock from BRGA/BRGB
+ * find divided clock from BRGA/BRGB
*/
- if (rate == adg->rbga_rate_for_441khz_div_6) {
+ if (rate == adg->rbga_rate_for_441khz) {
data = 0x10;
goto found_clock;
}
- if (rate == adg->rbgb_rate_for_48khz_div_6) {
+ if (rate == adg->rbgb_rate_for_48khz) {
data = 0x20;
goto found_clock;
}
@@ -342,29 +394,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
found_clock:
- /* see rsnd_adg_ssi_clk_init() */
- rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr);
- rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */
- rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */
-
/*
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, data);
- dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
- rsnd_mod_id(mod), i, rate);
+ dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ data, rate);
return 0;
}
-static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+ static const char * const clk_name[] = {
+ [CLKA] = "clk_a",
+ [CLKB] = "clk_b",
+ [CLKC] = "clk_c",
+ [CLKI] = "clk_i",
+ };
+ int i;
+
+ for (i = 0; i < CLKMAX; i++) {
+ clk = devm_clk_get(dev, clk_name[i]);
+ adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+ }
+
+ for_each_rsnd_clk(clk, adg, i)
+ dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+ struct rsnd_adg *adg)
{
struct clk *clk;
- unsigned long rate;
- u32 ckr;
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+ u32 ckr, rbgx, rbga, rbgb;
+ u32 rate, req_rate, div;
+ uint32_t count = 0;
+ unsigned long req_48kHz_rate, req_441kHz_rate;
int i;
+ const char *parent_clk_name = NULL;
+ static const char * const clkout_name[] = {
+ [CLKOUT] = "audio_clkout",
+ [CLKOUT1] = "audio_clkout1",
+ [CLKOUT2] = "audio_clkout2",
+ [CLKOUT3] = "audio_clkout3",
+ };
int brg_table[] = {
[CLKA] = 0x0,
[CLKB] = 0x1,
@@ -372,19 +455,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
[CLKI] = 0x2,
};
+ of_property_read_u32(np, "#clock-cells", &count);
+
+ /*
+ * ADG supports BRRA/BRRB output only
+ * this means all clkout0/1/2/3 will be same rate
+ */
+ of_property_read_u32(np, "clock-frequency", &req_rate);
+ req_48kHz_rate = 0;
+ req_441kHz_rate = 0;
+ if (0 == (req_rate % 44100))
+ req_441kHz_rate = req_rate;
+ if (0 == (req_rate % 48000))
+ req_48kHz_rate = req_rate;
+
/*
* This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
* have 44.1kHz or 48kHz base clocks for now.
*
* SSI itself can divide parent clock by 1/1 - 1/16
- * So, BRGA outputs 44.1kHz base parent clock 1/32,
- * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
* see
* rsnd_adg_ssi_clk_try_start()
+ * rsnd_ssi_master_clk_start()
*/
ckr = 0;
- adg->rbga_rate_for_441khz_div_6 = 0;
- adg->rbgb_rate_for_48khz_div_6 = 0;
+ rbga = 2; /* default 1/6 */
+ rbgb = 2; /* default 1/6 */
+ adg->rbga_rate_for_441khz = 0;
+ adg->rbgb_rate_for_48khz = 0;
for_each_rsnd_clk(clk, adg, i) {
rate = clk_get_rate(clk);
@@ -392,19 +490,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
continue;
/* RBGA */
- if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
- adg->rbga_rate_for_441khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 20;
+ if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+ div = 6;
+ if (req_441kHz_rate)
+ div = rate / req_441kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbga = rbgx;
+ adg->rbga_rate_for_441khz = rate / div;
+ ckr |= brg_table[i] << 20;
+ if (req_441kHz_rate)
+ parent_clk_name = __clk_get_name(clk);
+ }
}
/* RBGB */
- if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
- adg->rbgb_rate_for_48khz_div_6 = rate / 6;
- ckr |= brg_table[i] << 16;
+ if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+ div = 6;
+ if (req_48kHz_rate)
+ div = rate / req_48kHz_rate;
+ rbgx = rsnd_adg_calculate_rbgx(div);
+ if (BRRx_MASK(rbgx) == rbgx) {
+ rbgb = rbgx;
+ adg->rbgb_rate_for_48khz = rate / div;
+ ckr |= brg_table[i] << 16;
+ if (req_48kHz_rate) {
+ parent_clk_name = __clk_get_name(clk);
+ ckr |= 0x80000000;
+ }
+ }
}
}
- adg->ckr = ckr;
+ /*
+ * ADG supports BRRA/BRRB output only.
+ * this means all clkout0/1/2/3 will be * same rate
+ */
+
+ /*
+ * for clkout
+ */
+ if (!count) {
+ clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT, req_rate);
+ if (!IS_ERR(clk)) {
+ adg->clkout[CLKOUT] = clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ }
+ }
+ /*
+ * for clkout0/1/2/3
+ */
+ else {
+ for (i = 0; i < CLKOUTMAX; i++) {
+ clk = clk_register_fixed_rate(dev, clkout_name[i],
+ parent_clk_name,
+ (parent_clk_name) ?
+ 0 : CLK_IS_ROOT,
+ req_rate);
+ if (!IS_ERR(clk)) {
+ adg->onecell.clks = adg->clkout;
+ adg->onecell.clk_num = CLKOUTMAX;
+
+ adg->clkout[i] = clk;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get,
+ &adg->onecell);
+ }
+ }
+ }
+
+ rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_write(adg_mod, BRRA, rbga);
+ rsnd_mod_write(adg_mod, BRRB, rbgb);
+
+ for_each_rsnd_clkout(clk, adg, i)
+ dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ ckr, rbga, rbgb);
}
int rsnd_adg_probe(struct platform_device *pdev,
@@ -413,8 +578,6 @@ int rsnd_adg_probe(struct platform_device *pdev,
{
struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv);
- struct clk *clk;
- int i;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
if (!adg) {
@@ -422,15 +585,16 @@ int rsnd_adg_probe(struct platform_device *pdev,
return -ENOMEM;
}
- adg->clk[CLKA] = devm_clk_get(dev, "clk_a");
- adg->clk[CLKB] = devm_clk_get(dev, "clk_b");
- adg->clk[CLKC] = devm_clk_get(dev, "clk_c");
- adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
-
- for_each_rsnd_clk(clk, adg, i)
- dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+ /*
+ * ADG is special module.
+ * Use ADG mod without rsnd_mod_init() to make debug easy
+ * for rsnd_write/rsnd_read
+ */
+ adg->mod.ops = &adg_ops;
+ adg->mod.priv = priv;
- rsnd_adg_ssi_clk_init(priv, adg);
+ rsnd_adg_get_clkin(priv, adg);
+ rsnd_adg_get_clkout(priv, adg);
priv->adg = adg;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index f3feed5ce9b6..deed48ef28b8 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = {
static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+ { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
{},
};
MODULE_DEVICE_TABLE(of, rsnd_of_match);
@@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
#define rsnd_info_id(priv, io, name) \
((io)->info->name - priv->info->name##_info)
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+ if (mod->type != type) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "%s[%d] is not your expected module\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+ }
+}
+
/*
* rsnd_mod functions
*/
@@ -288,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
/*
* rsnd_dai functions
*/
-#define __rsnd_mod_call(mod, io, func, param...) \
+#define rsnd_mod_call(mod, io, func, param...) \
({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct device *dev = rsnd_priv_to_dev(priv); \
@@ -296,24 +308,17 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
- int called = 0; \
- if (val == __rsnd_mod_call_##func) { \
- called = 1; \
- ret = (mod)->ops->func(mod, io, param); \
- } \
+ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
mod->status = (mod->status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
- dev_dbg(dev, "%s[%d] 0x%08x %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \
- called ? #func : ""); \
+ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), \
+ mod->status, call ? #func : ""); \
+ if (call) \
+ ret = (mod)->ops->func(mod, io, param); \
ret; \
})
-#define rsnd_mod_call(mod, io, func, param...) \
- (!(mod) ? -ENODEV : \
- !((mod)->ops->func) ? 0 : \
- __rsnd_mod_call(mod, io, func, param))
-
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
@@ -322,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
- ret = rsnd_mod_call(mod, io, fn, param); \
- if (ret < 0) \
- break; \
+ ret |= rsnd_mod_call(mod, io, fn, param); \
} \
ret; \
})
@@ -490,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
case SNDRV_PCM_TRIGGER_STOP:
ret = rsnd_dai_call(stop, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
- ret = rsnd_dai_call(quit, io, priv);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_dai_call(quit, io, priv);
- ret = rsnd_platform_call(priv, dai, stop, ssi_id);
- if (ret < 0)
- goto dai_trigger_end;
+ ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
rsnd_dai_stream_quit(io);
break;
@@ -1224,20 +1221,11 @@ static int rsnd_probe(struct platform_device *pdev)
};
int ret, i;
- info = NULL;
- of_data = NULL;
- if (of_id) {
- info = devm_kzalloc(&pdev->dev,
- sizeof(struct rcar_snd_info), GFP_KERNEL);
- of_data = of_id->data;
- } else {
- info = pdev->dev.platform_data;
- }
-
- if (!info) {
- dev_err(dev, "driver needs R-Car sound information\n");
- return -ENODEV;
- }
+ info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ of_data = of_id->data;
/*
* init priv data
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
index 05498bba5874..3cb214ab848b 100644
--- a/sound/soc/sh/rcar/ctu.c
+++ b/sound/soc/sh/rcar/ctu.c
@@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_ctu_initialize_lock(mod);
@@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
id = 0;
- return &((struct rsnd_ctu *)(priv->ctu) + id)->mod;
+ return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
}
static void rsnd_of_parse_ctu(struct platform_device *pdev,
@@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "CTU is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_ctu(pdev, of_data, priv);
@@ -150,7 +148,7 @@ int rsnd_ctu_probe(struct platform_device *pdev,
ctu->info = &info->ctu_info[i];
- ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i);
if (ret)
return ret;
@@ -166,6 +164,6 @@ void rsnd_ctu_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ctu(ctu, priv, i) {
- rsnd_mod_quit(&ctu->mod);
+ rsnd_mod_quit(rsnd_mod_get(ctu));
}
}
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index bfbb8a5e93bd..5d084d040961 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
- if (is_ssi && rsnd_ssi_use_busif(io, mod))
+ if (is_ssi && rsnd_ssi_use_busif(io))
is_ssi++;
return (is_from) ?
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 57796387d482..58f690900e6d 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_dvc_soft_reset(mod);
@@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
id = 0;
- return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+ return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
}
static void rsnd_of_parse_dvc(struct platform_device *pdev,
@@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "CMD is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_dvc(pdev, of_data, priv);
@@ -361,7 +359,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
dvc->info = &info->dvc_info[i];
- ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
if (ret)
return ret;
@@ -377,6 +375,6 @@ void rsnd_dvc_remove(struct platform_device *pdev,
int i;
for_each_rsnd_dvc(dvc, priv, i) {
- rsnd_mod_quit(&dvc->mod);
+ rsnd_mod_quit(rsnd_mod_get(dvc));
}
}
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index f04d17bc6e3d..76da7620904c 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -22,13 +22,15 @@
#include "rsnd.h"
struct rsnd_gen {
- void __iomem *base[RSND_BASE_MAX];
-
struct rsnd_gen_ops *ops;
+ /* RSND_BASE_MAX base */
+ void __iomem *base[RSND_BASE_MAX];
+ phys_addr_t res[RSND_BASE_MAX];
struct regmap *regmap[RSND_BASE_MAX];
+
+ /* RSND_REG_MAX base */
struct regmap_field *regs[RSND_REG_MAX];
- phys_addr_t res[RSND_REG_MAX];
};
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
@@ -79,11 +81,11 @@ u32 rsnd_read(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return 0;
+ regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
- regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
-
return val;
}
@@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
+ /* RSND_BASE_MAX base */
gen->base[reg_id] = base;
gen->regmap[reg_id] = regmap;
gen->res[reg_id] = res->start;
@@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
if (IS_ERR(regs))
return PTR_ERR(regs);
+ /* RSND_REG_MAX base */
gen->regs[conf[i].idx] = regs;
}
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
index 0d5c102db6f5..953dd0be9b60 100644
--- a/sound/soc/sh/rcar/mix.c
+++ b/sound/soc/sh/rcar/mix.c
@@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_mix_soft_reset(mod);
@@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
return 0;
}
@@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
id = 0;
- return &((struct rsnd_mix *)(priv->mix) + id)->mod;
+ return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
}
static void rsnd_of_parse_mix(struct platform_device *pdev,
@@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev,
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
- if (rsnd_is_gen1(priv)) {
- dev_warn(dev, "MIX is not supported on Gen1\n");
- return -EINVAL;
- }
+ if (rsnd_is_gen1(priv))
+ return 0;
rsnd_of_parse_mix(pdev, of_data, priv);
@@ -179,7 +177,7 @@ int rsnd_mix_probe(struct platform_device *pdev,
mix->info = &info->mix_info[i];
- ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops,
+ ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i);
if (ret)
return ret;
@@ -195,6 +193,6 @@ void rsnd_mix_remove(struct platform_device *pdev,
int i;
for_each_rsnd_mix(mix, priv, i) {
- rsnd_mod_quit(&mix->mod);
+ rsnd_mod_quit(rsnd_mod_get(mix));
}
}
diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h
new file mode 100644
index 000000000000..d8e33d38da43
--- /dev/null
+++ b/sound/soc/sh/rcar/rcar_snd.h
@@ -0,0 +1,117 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef RCAR_SND_H
+#define RCAR_SND_H
+
+
+#define RSND_GEN1_SRU 0
+#define RSND_GEN1_ADG 1
+#define RSND_GEN1_SSI 2
+
+#define RSND_GEN2_SCU 0
+#define RSND_GEN2_ADG 1
+#define RSND_GEN2_SSIU 2
+#define RSND_GEN2_SSI 3
+
+#define RSND_BASE_MAX 4
+
+/*
+ * flags
+ *
+ * 0xAB000000
+ *
+ * A : clock sharing settings
+ * B : SSI direction
+ */
+#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
+#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
+
+#define RSND_SSI(_dma_id, _irq, _flags) \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
+#define RSND_SSI_UNUSED \
+{ .dma_id = -1, .irq = -1, .flags = 0 }
+
+struct rsnd_ssi_platform_info {
+ int dma_id;
+ int irq;
+ u32 flags;
+};
+
+#define RSND_SRC(rate, _dma_id) \
+{ .convert_rate = rate, .dma_id = _dma_id, }
+#define RSND_SRC_UNUSED \
+{ .convert_rate = 0, .dma_id = -1, }
+
+struct rsnd_src_platform_info {
+ u32 convert_rate; /* sampling rate convert */
+ int dma_id; /* for Gen2 SCU */
+ int irq;
+};
+
+/*
+ * flags
+ */
+struct rsnd_ctu_platform_info {
+ u32 flags;
+};
+
+struct rsnd_mix_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dvc_platform_info {
+ u32 flags;
+};
+
+struct rsnd_dai_path_info {
+ struct rsnd_ssi_platform_info *ssi;
+ struct rsnd_src_platform_info *src;
+ struct rsnd_ctu_platform_info *ctu;
+ struct rsnd_mix_platform_info *mix;
+ struct rsnd_dvc_platform_info *dvc;
+};
+
+struct rsnd_dai_platform_info {
+ struct rsnd_dai_path_info playback;
+ struct rsnd_dai_path_info capture;
+};
+
+/*
+ * flags
+ *
+ * 0x0000000A
+ *
+ * A : generation
+ */
+#define RSND_GEN_MASK (0xF << 0)
+#define RSND_GEN1 (1 << 0) /* fixme */
+#define RSND_GEN2 (2 << 0) /* fixme */
+
+struct rcar_snd_info {
+ u32 flags;
+ struct rsnd_ssi_platform_info *ssi_info;
+ int ssi_info_nr;
+ struct rsnd_src_platform_info *src_info;
+ int src_info_nr;
+ struct rsnd_ctu_platform_info *ctu_info;
+ int ctu_info_nr;
+ struct rsnd_mix_platform_info *mix_info;
+ int mix_info_nr;
+ struct rsnd_dvc_platform_info *dvc_info;
+ int dvc_info_nr;
+ struct rsnd_dai_platform_info *dai_info;
+ int dai_info_nr;
+ int (*start)(int id);
+ int (*stop)(int id);
+};
+
+#endif
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 7a0e52b4640a..085329878525 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -21,10 +21,11 @@
#include <linux/of_irq.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
-#include <sound/rcar_snd.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include "rcar_snd.h"
+
/*
* pseudo register
*
@@ -214,6 +215,7 @@ struct rsnd_dma {
};
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
@@ -225,8 +227,6 @@ int rsnd_dma_probe(struct platform_device *pdev,
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
/*
* R-Car sound mod
*/
@@ -330,8 +330,9 @@ struct rsnd_mod {
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
-#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
-#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk)
+#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
+#define rsnd_mod_get(ip) (&(ip)->mod)
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
@@ -571,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev,
void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+
+#define rsnd_ssi_is_pin_sharing(io) \
+ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
/*
* R-Car SRC
@@ -627,4 +631,15 @@ void rsnd_dvc_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+#ifdef DEBUG
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
+
#endif
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 89a18e102feb..261b50217c48 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
/*
* SSI_MODE1
*/
- if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+ if (rsnd_ssi_is_pin_sharing(io)) {
int shift = -1;
switch (ssi_id) {
case 1:
@@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- rsnd_mod_hw_start(mod);
+ rsnd_mod_power_on(mod);
rsnd_src_soft_reset(mod);
@@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- rsnd_mod_hw_stop(mod);
+ rsnd_mod_power_off(mod);
if (src->err)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
@@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
}
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -932,12 +931,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
- * Gen1 is not supported
- */
- if (rsnd_is_gen1(priv))
- return 0;
-
- /*
* SRC sync convert needs clock master
*/
if (!rsnd_rdai_is_clk_master(rdai))
@@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
.start = rsnd_src_start_gen2,
.stop = rsnd_src_stop_gen2,
.hw_params = rsnd_src_hw_params,
- .pcm_new = rsnd_src_pcm_new,
+ .pcm_new = rsnd_src_pcm_new_gen2,
};
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -983,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
id = 0;
- return &((struct rsnd_src *)(priv->src) + id)->mod;
+ return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
}
static void rsnd_of_parse_src(struct platform_device *pdev,
@@ -1043,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev,
int i, nr, ret;
ops = NULL;
- if (rsnd_is_gen1(priv))
+ if (rsnd_is_gen1(priv)) {
ops = &rsnd_src_gen1_ops;
+ dev_warn(dev, "Gen1 support will be removed soon\n");
+ }
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
if (!ops) {
@@ -1078,7 +1073,7 @@ int rsnd_src_probe(struct platform_device *pdev,
src->info = &info->src_info[i];
- ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
+ ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
if (ret)
return ret;
}
@@ -1093,6 +1088,6 @@ void rsnd_src_remove(struct platform_device *pdev,
int i;
for_each_rsnd_src(src, priv, i) {
- rsnd_mod_quit(&src->mod);
+ rsnd_mod_quit(rsnd_mod_get(src));
}
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index d45b9a7e324e..1427ec21bd7e 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -79,7 +79,6 @@ struct rsnd_ssi {
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
-#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
@@ -87,8 +86,9 @@ struct rsnd_ssi {
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
+ struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int use_busif = 0;
@@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
- int i, j, ret;
- int adg_clk_div_table[] = {
- 1, 6, /* see adg.c */
- };
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
@@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* Find best clock, and try to start ADG
*/
- for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
- for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
- /*
- * this driver is assuming that
- * system word is 64fs (= 2 x 32bit)
- * see rsnd_ssi_init()
- */
- main_rate = rate / adg_clk_div_table[i]
- * 32 * 2 * ssi_clk_mul_table[j];
-
- ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
- if (0 == ret) {
- ssi->cr_clk = FORCE | SWL_32 |
- SCKD | SWSD | CKDV(j);
-
- dev_dbg(dev, "%s[%d] outputs %u Hz\n",
- rsnd_mod_name(&ssi->mod),
- rsnd_mod_id(&ssi->mod), rate);
-
- return 0;
- }
+ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
+ * this driver is assuming that
+ * system word is 64fs (= 2 x 32bit)
+ * see rsnd_ssi_init()
+ */
+ main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+ if (0 == ret) {
+ ssi->cr_clk = FORCE | SWL_32 |
+ SCKD | SWSD | CKDV(j);
+
+ dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+ rsnd_mod_name(mod),
+ rsnd_mod_id(mod), rate);
+
+ return 0;
}
}
@@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
ssi->cr_clk = 0;
- rsnd_adg_ssi_clk_stop(&ssi->mod);
+ rsnd_adg_ssi_clk_stop(mod);
}
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
@@ -182,11 +179,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
u32 cr_mode;
u32 cr;
if (0 == ssi->usrcnt) {
- rsnd_mod_hw_start(&ssi->mod);
+ rsnd_mod_power_on(mod);
if (rsnd_rdai_is_clk_master(rdai)) {
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -198,7 +196,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
}
}
- if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
+ if (rsnd_ssi_is_dma_mode(mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
@@ -210,24 +208,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
cr_mode |
EN;
- rsnd_mod_write(&ssi->mod, SSICR, cr);
+ rsnd_mod_write(mod, SSICR, cr);
/* enable WS continue */
if (rsnd_rdai_is_clk_master(rdai))
- rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ rsnd_mod_write(mod, SSIWSR, CONT);
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
@@ -247,15 +246,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
cr = ssi->cr_own |
ssi->cr_clk;
- rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
- rsnd_ssi_status_check(&ssi->mod, DIRQ);
+ rsnd_mod_write(mod, SSICR, cr | EN);
+ rsnd_ssi_status_check(mod, DIRQ);
/*
* disable SSI,
* and, wait idle state
*/
- rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */
- rsnd_ssi_status_check(&ssi->mod, IIRQ);
+ rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+ rsnd_ssi_status_check(mod, IIRQ);
if (rsnd_rdai_is_clk_master(rdai)) {
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -266,13 +265,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
rsnd_ssi_master_clk_stop(ssi);
}
- rsnd_mod_hw_stop(&ssi->mod);
+ rsnd_mod_power_off(mod);
ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
- rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
/*
@@ -371,7 +370,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
/* It will be removed on rsnd_ssi_hw_stop */
ssi->chan = chan;
if (ssi_parent)
- return rsnd_ssi_hw_params(&ssi_parent->mod, io,
+ return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
substream, params);
return 0;
@@ -379,12 +378,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
{
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
/* under/over flow error */
if (status & (UIRQ | OIRQ)) {
ssi->err++;
/* clear error status */
- rsnd_mod_write(&ssi->mod, SSISR, 0);
+ rsnd_mod_write(mod, SSISR, 0);
}
}
@@ -394,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod,
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod));
+ rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io));
rsnd_ssi_hw_start(ssi, io);
@@ -554,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
/* PIO will request IRQ again */
- devm_free_irq(dev, irq, ssi);
+ devm_free_irq(dev, irq, mod);
return 0;
}
@@ -613,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
int is_play = rsnd_io_is_play(io);
char *name;
- if (rsnd_ssi_use_busif(io, mod))
+ if (rsnd_ssi_use_busif(io))
name = is_play ? "rxu" : "txu";
else
name = is_play ? "rx" : "tx";
@@ -656,10 +657,10 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
id = 0;
- return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+ return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
}
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@@ -668,10 +669,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
{
- if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
+ struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
+ if (!__rsnd_ssi_is_pin_sharing(mod))
return;
- switch (rsnd_mod_id(&ssi->mod)) {
+ switch (rsnd_mod_id(mod)) {
case 1:
case 2:
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
@@ -697,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
struct device *dev = &pdev->dev;
int nr, i;
- if (!of_data)
- return;
-
node = rsnd_ssi_of_node(priv);
if (!node)
return;
@@ -794,7 +794,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops;
- ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
+ ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+ RSND_MOD_SSI, i);
if (ret)
return ret;
@@ -811,6 +812,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ssi(ssi, priv, i) {
- rsnd_mod_quit(&ssi->mod);
+ rsnd_mod_quit(rsnd_mod_get(ssi));
}
}
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
index abb0d956231c..76b2ab8c2b4a 100644
--- a/sound/soc/sh/siu_dai.c
+++ b/sound/soc/sh/siu_dai.c
@@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev)
struct siu_info *info;
int ret;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
siu_i2s_data = info;
@@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev)
ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
if (ret)
- goto ereqfw;
+ return ret;
/*
* Loaded firmware is "const" - read only, but we have to modify it in
@@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev)
release_firmware(fw_entry);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto egetres;
- }
+ if (!res)
+ return -ENODEV;
- region = request_mem_region(res->start, resource_size(res),
- pdev->name);
+ region = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name);
if (!region) {
dev_err(&pdev->dev, "SIU region already claimed\n");
- ret = -EBUSY;
- goto ereqmemreg;
+ return -EBUSY;
}
- ret = -ENOMEM;
- info->pram = ioremap(res->start, PRAM_SIZE);
+ info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
if (!info->pram)
- goto emappram;
- info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+ return -ENOMEM;
+ info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+ XRAM_SIZE);
if (!info->xram)
- goto emapxram;
- info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+ return -ENOMEM;
+ info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+ YRAM_SIZE);
if (!info->yram)
- goto emapyram;
- info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
- REG_OFFSET);
+ return -ENOMEM;
+ info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+ resource_size(res) - REG_OFFSET);
if (!info->reg)
- goto emapreg;
+ return -ENOMEM;
dev_set_drvdata(&pdev->dev, info);
/* register using ARRAY version so we can keep dai name */
- ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
- &siu_i2s_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+ &siu_i2s_dai, 1);
if (ret < 0)
- goto edaiinit;
+ return ret;
- ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+ ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
if (ret < 0)
- goto esocregp;
+ return ret;
pm_runtime_enable(&pdev->dev);
- return ret;
-
-esocregp:
- snd_soc_unregister_component(&pdev->dev);
-edaiinit:
- iounmap(info->reg);
-emapreg:
- iounmap(info->yram);
-emapyram:
- iounmap(info->xram);
-emapxram:
- iounmap(info->pram);
-emappram:
- release_mem_region(res->start, resource_size(res));
-ereqmemreg:
-egetres:
-ereqfw:
- kfree(info);
-
- return ret;
+ return 0;
}
static int siu_remove(struct platform_device *pdev)
{
- struct siu_info *info = dev_get_drvdata(&pdev->dev);
- struct resource *res;
-
pm_runtime_disable(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
-
- iounmap(info->reg);
- iounmap(info->yram);
- iounmap(info->xram);
- iounmap(info->pram);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res)
- release_mem_region(res->start, resource_size(res));
- kfree(info);
-
return 0;
}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 025c38fbe3c0..12a9820feac1 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
.get_codec_caps = soc_compr_get_codec_caps
};
-/* create a new compress */
-int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+/**
+ * snd_soc_new_compress - create a new compress.
+ *
+ * @rtd: The runtime for which we will create compress
+ * @num: the device index number (zero based - shared with normal PCMs)
+ *
+ * Return: 0 for success, else error.
+ */
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
@@ -703,3 +710,4 @@ compr_err:
kfree(compr);
return ret;
}
+EXPORT_SYMBOL_GPL(snd_soc_new_compress);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 6173d15236c3..24b096066a07 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
soc_dpcm_debugfs_add(rtd);
#endif
- if (cpu_dai->driver->compress_dai) {
+ if (cpu_dai->driver->compress_new) {
/*create compress_device"*/
- ret = soc_new_compress(rtd, num);
+ ret = cpu_dai->driver->compress_new(rtd, num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
@@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
+static int snd_soc_of_get_slot_mask(struct device_node *np,
+ const char *prop_name,
+ unsigned int *mask)
+{
+ u32 val;
+ const __be32 *of_slot_mask = of_get_property(np, prop_name, &val);
+ int i;
+
+ if (!of_slot_mask)
+ return 0;
+ val /= sizeof(u32);
+ for (i = 0; i < val; i++)
+ if (be32_to_cpup(&of_slot_mask[i]))
+ *mask |= (1 << i);
+
+ return val;
+}
+
int snd_soc_of_parse_tdm_slot(struct device_node *np,
+ unsigned int *tx_mask,
+ unsigned int *rx_mask,
unsigned int *slots,
unsigned int *slot_width)
{
u32 val;
int ret;
+ if (tx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
+ if (rx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
if (of_property_read_bool(np, "dai-tdm-slot-num")) {
ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
if (ret)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f4bf21a5539b..016eba10b1ec 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -509,6 +509,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
}
/**
+ * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
+ struct snd_kcontrol *kcontrol)
+{
+ return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+
+/**
* snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
* kcontrol
* @kcontrol: The kcontrol
@@ -779,7 +791,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
* Determine if a kcontrol is shared. If it is, look it up. If it isn't,
* create it. Either way, add the widget into the control's widget list
*/
-static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
+static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
int kci)
{
struct snd_soc_dapm_context *dapm = w->dapm;
@@ -810,6 +822,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_pga:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
@@ -899,7 +912,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
continue;
if (!w->kcontrols[i]) {
- ret = dapm_create_or_share_mixmux_kcontrol(w, i);
+ ret = dapm_create_or_share_kcontrol(w, i);
if (ret < 0)
return ret;
}
@@ -952,7 +965,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
return -EINVAL;
}
- ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
+ ret = dapm_create_or_share_kcontrol(w, 0);
if (ret < 0)
return ret;
@@ -967,9 +980,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
/* create new dapm volume control */
static int dapm_new_pga(struct snd_soc_dapm_widget *w)
{
- if (w->num_kcontrols)
- dev_err(w->dapm->dev,
- "ASoC: PGA controls not supported: '%s'\n", w->name);
+ int i, ret;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ ret = dapm_create_or_share_kcontrol(w, i);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
@@ -3473,11 +3490,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ if (source->driver->ops && source->driver->ops->startup) {
+ ret = source->driver->ops->startup(&substream, source);
+ if (ret < 0) {
+ dev_err(source->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ source->active++;
+ }
ret = soc_dai_hw_params(&substream, params, source);
if (ret < 0)
goto out;
substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ if (sink->driver->ops && sink->driver->ops->startup) {
+ ret = sink->driver->ops->startup(&substream, sink);
+ if (ret < 0) {
+ dev_err(sink->dev,
+ "ASoC: startup() failed: %d\n", ret);
+ goto out;
+ }
+ sink->active++;
+ }
ret = soc_dai_hw_params(&substream, params, sink);
if (ret < 0)
goto out;
@@ -3497,11 +3532,23 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
ret = 0;
+
+ source->active--;
+ if (source->driver->ops && source->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ source->driver->ops->shutdown(&substream, source);
+ }
+
+ sink->active--;
+ if (sink->driver->ops && sink->driver->ops->shutdown) {
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ sink->driver->ops->shutdown(&substream, sink);
+ }
break;
default:
WARN(1, "Unknown event %d\n", event);
- return -EINVAL;
+ ret = -EINVAL;
}
out:
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 100d92b5b77e..ecd38e52285a 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -207,6 +207,34 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
/**
+ * snd_soc_info_volsw_sx - Mixer info callback for SX TLV controls
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers of the SX TLV type. SX TLV controls
+ * have a range that represents both positive and negative values either side
+ * of zero but without a sign bit.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ snd_soc_info_volsw(kcontrol, uinfo);
+ /* Max represents the number of levels in an SX control not the
+ * maximum value, so add the minimum value back on
+ */
+ uinfo->value.integer.max += mc->min;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx);
+
+/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
@@ -560,16 +588,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
- * @codec: where to look for the control
+ * @card: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
+int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max)
{
- struct snd_card *card = codec->component.card->snd_card;
+ struct snd_card *snd_card = card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
@@ -579,7 +607,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
if (unlikely(!name || max <= 0))
return -EINVAL;
- list_for_each_entry(kctl, &card->controls, list) {
+ list_for_each_entry(kctl, &snd_card->controls, list) {
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
found = 1;
break;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 70e4b9d8bdcd..317395824cd7 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -34,6 +34,24 @@
#define DPCM_MAX_BE_USERS 8
+/*
+ * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
+ *
+ * Returns true if the DAI supports the indicated stream type.
+ */
+static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_pcm_stream *codec_stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &dai->driver->playback;
+ else
+ codec_stream = &dai->driver->capture;
+
+ /* If the codec specifies any rate at all, it supports the stream. */
+ return codec_stream->rates;
+}
+
/**
* snd_soc_runtime_activate() - Increment active count for PCM runtime components
* @rtd: ASoC PCM runtime that is activated
@@ -371,6 +389,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
/* first calculate min/max only for CODECs in the DAI link */
for (i = 0; i < rtd->num_codecs; i++) {
+
+ /*
+ * Skip CODECs which don't support the current stream type.
+ * Otherwise, since the rate, channel, and format values will
+ * zero in that case, we would have no usable settings left,
+ * causing the resulting setup to fail.
+ * At least one CODEC should match, otherwise we should have
+ * bailed out on a higher level, since there would be no
+ * CODEC to support the transfer direction in that case.
+ */
+ if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
+ substream->stream))
+ continue;
+
codec_dai_drv = rtd->codec_dais[i]->driver;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
codec_stream = &codec_dai_drv->playback;
@@ -827,6 +859,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
struct snd_pcm_hw_params codec_params;
+ /*
+ * Skip CODECs which don't support the current stream type,
+ * the idea being that if a CODEC is not used for the currently
+ * set up transfer direction, it should not need to be
+ * configured, especially since the configuration used might
+ * not even be supported by that CODEC. There may be cases
+ * however where a CODEC needs to be set up although it is
+ * actually not being used for the transfer, e.g. if a
+ * capture-only CODEC is acting as an LRCLK and/or BCLK master
+ * for the DAI link including a playback-only CODEC.
+ * If this becomes necessary, we will have to augment the
+ * machine driver setup with information on how to act, so
+ * we can do the right thing here.
+ */
+ if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
+ continue;
+
/* copy params for each codec */
codec_params = *params;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 362c69ac1d6c..53dd085d3ee2 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -101,6 +101,15 @@ static struct snd_soc_codec_driver dummy_codec;
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+/*
+ * The dummy CODEC is only meant to be used in situations where there is no
+ * actual hardware.
+ *
+ * If there is actual hardware even if it does not have a control bus
+ * the hardware will still have constraints like supported samplerates, etc.
+ * which should be modelled. And the data flow graph also should be modelled
+ * using DAPM.
+ */
static struct snd_soc_dai_driver dummy_dai = {
.name = "snd-soc-dummy-dai",
.playback = {
diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig
index 0a53053495f3..4fb91412ebec 100644
--- a/sound/soc/spear/Kconfig
+++ b/sound/soc/spear/Kconfig
@@ -1,6 +1,6 @@
config SND_SPEAR_SOC
tristate
- select SND_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_SPEAR_SPDIF_OUT
tristate
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index f6eefe1b8f8f..843f037a317d 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -989,8 +989,8 @@ static int uni_player_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- of_property_read_u32(pnode, "version", &player->ver);
- if (player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
+ if (of_property_read_u32(pnode, "version", &player->ver) ||
+ player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
dev_err(dev, "Unknown uniperipheral version ");
return -EINVAL;
}
@@ -998,10 +998,16 @@ static int uni_player_parse_dt(struct platform_device *pdev,
if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
info->underflow_enabled = 1;
- of_property_read_u32(pnode, "uniperiph-id", &info->id);
+ if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) {
+ dev_err(dev, "uniperipheral id not defined");
+ return -EINVAL;
+ }
/* Read the device mode property */
- of_property_read_string(pnode, "mode", &mode);
+ if (of_property_read_string(pnode, "mode", &mode)) {
+ dev_err(dev, "uniperipheral mode not defined");
+ return -EINVAL;
+ }
if (strcasecmp(mode, "hdmi") == 0)
info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI;
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index c502626f339b..f791239a3087 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -316,7 +316,11 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- of_property_read_u32(node, "version", &reader->ver);
+ if (of_property_read_u32(node, "version", &reader->ver) ||
+ reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
+ dev_err(&pdev->dev, "Unknown uniperipheral version ");
+ return -EINVAL;
+ }
/* Save the info structure */
reader->info = info;
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
new file mode 100644
index 000000000000..84c72ec6ad73
--- /dev/null
+++ b/sound/soc/sunxi/Kconfig
@@ -0,0 +1,11 @@
+menu "Allwinner SoC Audio support"
+
+config SND_SUN4I_CODEC
+ tristate "Allwinner A10 Codec Support"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Select Y or M to add support for the Codec embedded in the Allwinner
+ A10 and affiliated SoCs.
+
+endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
new file mode 100644
index 000000000000..ea8a08c881d6
--- /dev/null
+++ b/sound/soc/sunxi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
new file mode 100644
index 000000000000..8d59d83b5aa4
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUN4I_CODEC_DAC_DPC (0x00)
+#define SUN4I_CODEC_DAC_DPC_EN_DA (31)
+#define SUN4I_CODEC_DAC_DPC_DVOL (12)
+#define SUN4I_CODEC_DAC_FIFOC (0x04)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29)
+#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28)
+#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26)
+#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24)
+#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21)
+#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6)
+#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4)
+#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_DAC_FIFOS (0x08)
+#define SUN4I_CODEC_DAC_TXDATA (0x0c)
+#define SUN4I_CODEC_DAC_ACTL (0x10)
+#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
+#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
+#define SUN4I_CODEC_DAC_ACTL_MIXEN (29)
+#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15)
+#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14)
+#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13)
+#define SUN4I_CODEC_DAC_ACTL_DACPAS (8)
+#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7)
+#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6)
+#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0)
+#define SUN4I_CODEC_DAC_TUNE (0x14)
+#define SUN4I_CODEC_DAC_DEBUG (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUN4I_CODEC_ADC_FIFOC (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
+#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
+#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
+#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7)
+#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4)
+#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
+#define SUN4I_CODEC_ADC_FIFOS (0x20)
+#define SUN4I_CODEC_ADC_RXDATA (0x24)
+#define SUN4I_CODEC_ADC_ACTL (0x28)
+#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
+#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
+#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29)
+#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28)
+#define SUN4I_CODEC_ADC_ACTL_VMICEN (27)
+#define SUN4I_CODEC_ADC_ACTL_VADCG (20)
+#define SUN4I_CODEC_ADC_ACTL_ADCIS (17)
+#define SUN4I_CODEC_ADC_ACTL_PA_EN (4)
+#define SUN4I_CODEC_ADC_ACTL_DDE (3)
+#define SUN4I_CODEC_ADC_DEBUG (0x2c)
+
+/* Other various ADC registers */
+#define SUN4I_CODEC_DAC_TXCNT (0x30)
+#define SUN4I_CODEC_ADC_RXCNT (0x34)
+#define SUN4I_CODEC_AC_SYS_VERI (0x38)
+#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
+
+struct sun4i_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *clk_apb;
+ struct clk *clk_module;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO high here on some boards
+ */
+
+ /* Flush TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Enable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
+{
+ /*
+ * FIXME: according to the BSP, we might need to drive a PA
+ * GPIO low here on some boards
+ */
+
+ /* Disable DAC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+ 0);
+}
+
+static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sun4i_codec_start_playback(scodec);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sun4i_codec_stop_playback(scodec);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ /* Flush the TX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+ /* Set TX FIFO Empty Trigger Level */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
+ 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
+
+ if (substream->runtime->rate > 32000)
+ /* Use 64 bits FIR filter */
+ val = 0;
+ else
+ /* Use 32 bits FIR filter */
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
+ val);
+
+ /* Send zeros when we have an underrun */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
+ 0);
+
+ return 0;
+}
+
+static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 176400:
+ case 88200:
+ case 44100:
+ case 33075:
+ case 22050:
+ case 14700:
+ case 11025:
+ case 7350:
+ return 22579200;
+
+ case 192000:
+ case 96000:
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 12000:
+ case 8000:
+ return 24576000;
+
+ default:
+ return 0;
+ }
+}
+
+static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+
+ switch (rate) {
+ case 192000:
+ case 176400:
+ return 6;
+
+ case 96000:
+ case 88200:
+ return 7;
+
+ case 48000:
+ case 44100:
+ return 0;
+
+ case 32000:
+ case 33075:
+ return 1;
+
+ case 24000:
+ case 22050:
+ return 2;
+
+ case 16000:
+ case 14700:
+ return 3;
+
+ case 12000:
+ case 11025:
+ return 4;
+
+ case 8000:
+ case 7350:
+ return 5;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long clk_freq;
+ int hwrate;
+ u32 val;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENOTSUPP;
+
+ clk_freq = sun4i_codec_get_mod_freq(params);
+ if (!clk_freq)
+ return -EINVAL;
+
+ if (clk_set_rate(scodec->clk_module, clk_freq))
+ return -EINVAL;
+
+ hwrate = sun4i_codec_get_hw_rate(params);
+ if (hwrate < 0)
+ return hwrate;
+
+ /* Set DAC sample rate */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
+ hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
+
+ /* Set the number of channels we want to use */
+ if (params_channels(params) == 1)
+ val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
+ else
+ val = 0;
+
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
+ val);
+
+ /* Set the number of sample bits to either 16 or 24 bits */
+ if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
+
+ /* Set TX FIFO mode to padding the LSBs with 0 */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ 0);
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ } else {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+ 0);
+
+ /* Set TX FIFO mode to repeat the MSB */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+ BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
+
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ }
+
+ return 0;
+}
+
+static int sun4i_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ /*
+ * Stop issuing DRQ when we have room for less than 16 samples
+ * in our TX FIFO
+ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
+ 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
+
+ return clk_prepare_enable(scodec->clk_module);
+}
+
+static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+ clk_disable_unprepare(scodec->clk_module);
+}
+
+static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
+ .startup = sun4i_codec_startup,
+ .shutdown = sun4i_codec_shutdown,
+ .trigger = sun4i_codec_trigger,
+ .hw_params = sun4i_codec_hw_params,
+ .prepare = sun4i_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sun4i_codec_dai = {
+ .name = "Codec",
+ .ops = &sun4i_codec_dai_ops,
+ .playback = {
+ .stream_name = "Codec Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+};
+
+/*** Codec ***/
+static const struct snd_kcontrol_new sun4i_codec_pa_mute =
+ SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+ SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
+ sun4i_codec_pa_volume_scale),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
+ SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+ NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_left_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ sun4i_codec_right_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
+
+ /* Global Mixer Enable */
+ SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
+ SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
+
+ /* Pre-Amplifier */
+ SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
+ sun4i_codec_pa_mixer_controls,
+ ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
+ SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+ &sun4i_codec_pa_mute),
+
+ SND_SOC_DAPM_OUTPUT("HP Right"),
+ SND_SOC_DAPM_OUTPUT("HP Left"),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
+ /* Left DAC Routes */
+ { "Left DAC", NULL, "DAC" },
+
+ /* Right DAC Routes */
+ { "Right DAC", NULL, "DAC" },
+
+ /* Right Mixer Routes */
+ { "Right Mixer", NULL, "Mixer Enable" },
+ { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+ { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+
+ /* Left Mixer Routes */
+ { "Left Mixer", NULL, "Mixer Enable" },
+ { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+
+ /* Pre-Amplifier Mixer Routes */
+ { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
+ { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
+ { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
+ { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+
+ /* PA -> HP path */
+ { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
+ { "HP Right", NULL, "Pre-Amplifier Mute" },
+ { "HP Left", NULL, "Pre-Amplifier Mute" },
+};
+
+static struct snd_soc_codec_driver sun4i_codec_codec = {
+ .controls = sun4i_codec_widgets,
+ .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
+ .dapm_widgets = sun4i_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets),
+ .dapm_routes = sun4i_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes),
+};
+
+static const struct snd_soc_component_driver sun4i_codec_component = {
+ .name = "sun4i-codec",
+};
+
+#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
+ NULL);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+ .name = "sun4i-codec-cpu-dai",
+ .probe = sun4i_codec_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUN4I_CODEC_RATES,
+ .formats = SUN4I_CODEC_FORMATS,
+ .sig_bits = 24,
+ },
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-codec" },
+ { .compatible = "allwinner,sun7i-a20-codec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
+static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
+ int *num_links)
+{
+ struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
+ GFP_KERNEL);
+ if (!link)
+ return NULL;
+
+ link->name = "cdc";
+ link->stream_name = "CDC PCM";
+ link->codec_dai_name = "Codec";
+ link->cpu_dai_name = dev_name(dev);
+ link->codec_name = dev_name(dev);
+ link->platform_name = dev_name(dev);
+ link->dai_fmt = SND_SOC_DAIFMT_I2S;
+
+ *num_links = 1;
+
+ return link;
+};
+
+static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
+{
+ struct snd_soc_card *card;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return NULL;
+
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+ if (!card->dai_link)
+ return NULL;
+
+ card->dev = dev;
+ card->name = "sun4i-codec";
+
+ ret = snd_soc_of_parse_audio_routing(card, "routing");
+ if (ret) {
+ dev_err(dev, "Failed to create our audio routing\n");
+ return NULL;
+ }
+
+ return card;
+};
+
+static int sun4i_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct sun4i_codec *scodec;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+ if (!scodec)
+ return -ENOMEM;
+
+ scodec->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "Failed to map the registers\n");
+ return PTR_ERR(base);
+ }
+
+ scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_codec_regmap_config);
+ if (IS_ERR(scodec->regmap)) {
+ dev_err(&pdev->dev, "Failed to create our regmap\n");
+ return PTR_ERR(scodec->regmap);
+ }
+
+ /* Get the clocks from the DT */
+ scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+ if (IS_ERR(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to get the APB clock\n");
+ return PTR_ERR(scodec->clk_apb);
+ }
+
+ scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
+ if (IS_ERR(scodec->clk_module)) {
+ dev_err(&pdev->dev, "Failed to get the module clock\n");
+ return PTR_ERR(scodec->clk_module);
+ }
+
+ /* Enable the bus clock */
+ if (clk_prepare_enable(scodec->clk_apb)) {
+ dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+ return -EINVAL;
+ }
+
+ /* DMA configuration for TX FIFO */
+ scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
+ scodec->playback_dma_data.maxburst = 4;
+ scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+ &sun4i_codec_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our codec\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_codec_component,
+ &dummy_cpu_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our DAI\n");
+ goto err_unregister_codec;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
+ goto err_unregister_codec;
+ }
+
+ card = sun4i_codec_create_card(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "Failed to create our card\n");
+ goto err_unregister_codec;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, scodec);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register our card\n");
+ goto err_unregister_codec;
+ }
+
+ return 0;
+
+err_unregister_codec:
+ snd_soc_unregister_codec(&pdev->dev);
+err_clk_disable:
+ clk_disable_unprepare(scodec->clk_apb);
+ return ret;
+}
+
+static int sun4i_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+ snd_soc_unregister_codec(&pdev->dev);
+ clk_disable_unprepare(scodec->clk_apb);
+
+ return 0;
+}
+
+static struct platform_driver sun4i_codec_driver = {
+ .driver = {
+ .name = "sun4i-codec",
+ .of_match_table = sun4i_codec_of_match,
+ },
+ .probe = sun4i_codec_probe,
+ .remove = sun4i_codec_remove,
+};
+module_platform_driver(sun4i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 codec driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 4e0c0e502ade..ba9fc099cf67 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = {
{ .compatible = "stericsson,snd-soc-mop500", },
{},
};
+MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
static struct platform_driver snd_soc_mop500_driver = {
.driver = {
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index f5df08ded770..6ba8ae9ecc7a 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = {
{ .compatible = "stericsson,ux500-msp-i2s", },
{},
};
+MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match);
static struct platform_driver msp_i2s_driver = {
.driver = {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index 82e350e9501c..ac75816ada7c 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -69,7 +69,8 @@ snd_emux_init_seq_oss(struct snd_emux *emu)
struct snd_seq_oss_reg *arg;
struct snd_seq_device *dev;
- if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS,
+ /* using device#1 here for avoiding conflicts with OPL3 */
+ if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS,
sizeof(struct snd_seq_oss_reg), &dev) < 0)
return;