summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile2
-rw-r--r--sound/soc/atmel/atmel-classd.c26
-rw-r--r--sound/soc/atmel/atmel-pdmic.c738
-rw-r--r--sound/soc/atmel/atmel-pdmic.h80
-rw-r--r--sound/soc/atmel/atmel_wm8904.c1
-rw-r--r--sound/soc/codecs/Kconfig30
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ak4613.c118
-rw-r--r--sound/soc/codecs/arizona.c159
-rw-r--r--sound/soc/codecs/arizona.h17
-rw-r--r--sound/soc/codecs/cs47l24.c1148
-rw-r--r--sound/soc/codecs/cs47l24.h23
-rw-r--r--sound/soc/codecs/da7218.c3314
-rw-r--r--sound/soc/codecs/da7218.h1414
-rw-r--r--sound/soc/codecs/da7219.c89
-rw-r--r--sound/soc/codecs/da7219.h9
-rw-r--r--sound/soc/codecs/es8328.c41
-rw-r--r--sound/soc/codecs/es8328.h1
-rw-r--r--sound/soc/codecs/nau8825.c31
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c66
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c65
-rw-r--r--sound/soc/codecs/pcm3168a.c767
-rw-r--r--sound/soc/codecs/pcm3168a.h100
-rw-r--r--sound/soc/codecs/rl6231.c6
-rw-r--r--sound/soc/codecs/rt5645.c305
-rw-r--r--sound/soc/codecs/rt5670.h12
-rw-r--r--sound/soc/codecs/rt5677.c100
-rw-r--r--sound/soc/codecs/sgtl5000.c1
-rw-r--r--sound/soc/codecs/wm5110.c206
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8962.c4
-rw-r--r--sound/soc/codecs/wm8974.c1
-rw-r--r--sound/soc/codecs/wm8998.c46
-rw-r--r--sound/soc/codecs/wm_adsp.c769
-rw-r--r--sound/soc/codecs/wm_adsp.h23
-rw-r--r--sound/soc/davinci/davinci-mcasp.c16
-rw-r--r--sound/soc/dwc/designware_i2s.c113
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c21
-rw-r--r--sound/soc/fsl/fsl_asrc.c53
-rw-r--r--sound/soc/fsl/fsl_asrc.h2
-rw-r--r--sound/soc/fsl/fsl_esai.c17
-rw-r--r--sound/soc/fsl/fsl_sai.c21
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c10
-rw-r--r--sound/soc/generic/simple-card.c12
-rw-r--r--sound/soc/intel/Kconfig2
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c12
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-pcm.c2
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c7
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c7
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c7
-rw-r--r--sound/soc/intel/skylake/skl-topology.c1
-rw-r--r--sound/soc/omap/omap-hdmi-audio.c2
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c6
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c8
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h8
-rw-r--r--sound/soc/samsung/bells.c40
-rw-r--r--sound/soc/samsung/littlemill.c32
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c9
-rw-r--r--sound/soc/samsung/snow.c9
-rw-r--r--sound/soc/samsung/speyside.c12
-rw-r--r--sound/soc/samsung/tobermory.c21
-rw-r--r--sound/soc/sh/rcar/core.c3
-rw-r--r--sound/soc/sh/rcar/gen.c2
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c6
-rw-r--r--sound/soc/sh/rcar/src.c7
-rw-r--r--sound/soc/soc-ac97.c125
-rw-r--r--sound/soc/soc-core.c608
-rw-r--r--sound/soc/soc-dapm.c21
-rw-r--r--sound/soc/soc-ops.c2
-rw-r--r--sound/soc/soc-pcm.c82
-rw-r--r--sound/soc/soc-topology.c3
-rw-r--r--sound/soc/sti/uniperif_player.c9
-rw-r--r--sound/soc/sti/uniperif_reader.c3
-rw-r--r--sound/soc/sunxi/sun4i-codec.c308
-rw-r--r--sound/soc/tegra/tegra_wm8903.c3
78 files changed, 10565 insertions, 804 deletions
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 2d30464b81ce..06e099e802df 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -68,4 +68,13 @@ config SND_ATMEL_SOC_CLASSD
help
Say Y if you want to add support for Atmel ASoC driver for boards using
CLASSD.
+
+config SND_ATMEL_SOC_PDMIC
+ tristate "Atmel ASoC driver for boards using PDMIC"
+ depends on OF && (ARCH_AT91 || COMPILE_TEST)
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add support for Atmel ASoC driver for boards using
+ PDMIC.
endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index f6f7db428216..a2b127bd9c87 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -12,8 +12,10 @@ 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
+snd-atmel-soc-pdmic-objs := atmel-pdmic.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
+obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 8276675730ef..6107de9c538b 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
.rates = ATMEL_CLASSD_RATES,
.rate_min = 8000,
.rate_max = 96000,
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 256,
@@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
.playback = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
@@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ if (params_channels(params) == 1)
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else
+ slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
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;
@@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
.name = ATMEL_CLASSD_CODEC_DAI_NAME,
.playback = {
.stream_name = "Playback",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -636,8 +640,10 @@ static int atmel_classd_probe(struct platform_device *pdev)
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
- if (!card)
- return -ENOMEM;
+ if (!card) {
+ ret = -ENOMEM;
+ goto unregister_codec;
+ }
snd_soc_card_set_drvdata(card, dd);
platform_set_drvdata(pdev, card);
@@ -645,16 +651,20 @@ static int atmel_classd_probe(struct platform_device *pdev)
ret = atmel_classd_asoc_card_init(dev, card);
if (ret) {
dev_err(dev, "failed to init sound card\n");
- return ret;
+ goto unregister_codec;
}
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_err(dev, "failed to register sound card: %d\n", ret);
- return ret;
+ goto unregister_codec;
}
return 0;
+
+unregister_codec:
+ snd_soc_unregister_codec(dev);
+ return ret;
}
static int atmel_classd_remove(struct platform_device *pdev)
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
new file mode 100644
index 000000000000..aee4787a0b89
--- /dev/null
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -0,0 +1,738 @@
+/* Atmel PDMIC 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-pdmic.h"
+
+struct atmel_pdmic_pdata {
+ u32 mic_min_freq;
+ u32 mic_max_freq;
+ s32 mic_offset;
+ const char *card_name;
+};
+
+struct atmel_pdmic {
+ dma_addr_t phy_base;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ int irq;
+ struct snd_pcm_substream *substream;
+ const struct atmel_pdmic_pdata *pdata;
+};
+
+static const struct of_device_id atmel_pdmic_of_match[] = {
+ {
+ .compatible = "atmel,sama5d2-pdmic",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match);
+
+#define PDMIC_OFFSET_MAX_VAL S16_MAX
+#define PDMIC_OFFSET_MIN_VAL S16_MIN
+
+static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct atmel_pdmic_pdata *pdata;
+
+ 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);
+
+ if (of_property_read_string(np, "atmel,model", &pdata->card_name))
+ pdata->card_name = "PDMIC";
+
+ if (of_property_read_u32(np, "atmel,mic-min-freq",
+ &pdata->mic_min_freq)) {
+ dev_err(dev, "failed to get mic-min-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "atmel,mic-max-freq",
+ &pdata->mic_max_freq)) {
+ dev_err(dev, "failed to get mic-max-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (pdata->mic_max_freq < pdata->mic_min_freq) {
+ dev_err(dev,
+ "mic-max-freq should not less than mic-min-freq\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset))
+ pdata->mic_offset = 0;
+
+ if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) {
+ dev_warn(dev,
+ "mic-offset value %d is larger than the max value %d, the max value is specified\n",
+ pdata->mic_offset, PDMIC_OFFSET_MAX_VAL);
+ pdata->mic_offset = PDMIC_OFFSET_MAX_VAL;
+ } else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) {
+ dev_warn(dev,
+ "mic-offset value %d is less than the min value %d, the min value is specified\n",
+ pdata->mic_offset, PDMIC_OFFSET_MIN_VAL);
+ pdata->mic_offset = PDMIC_OFFSET_MIN_VAL;
+ }
+
+ return pdata;
+}
+
+/* cpu dai component */
+static int atmel_pdmic_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_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = clk_prepare_enable(dd->gclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dd->pclk);
+ if (ret)
+ return ret;
+
+ /* Clear all bits in the Control Register(PDMIC_CR) */
+ regmap_write(dd->regmap, PDMIC_CR, 0);
+
+ dd->substream = substream;
+
+ /* Enable the overrun error interrupt */
+ regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE);
+
+ return 0;
+}
+
+static void atmel_pdmic_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_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+
+ /* Disable the overrun error interrupt */
+ regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->pclk);
+}
+
+static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ u32 val;
+
+ /* Clean the PDMIC Converted Data Register */
+ return regmap_read(dd->regmap, PDMIC_CDR, &val);
+}
+
+static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
+ .startup = atmel_pdmic_cpu_dai_startup,
+ .shutdown = atmel_pdmic_cpu_dai_shutdown,
+ .prepare = atmel_pdmic_cpu_dai_prepare,
+};
+
+#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ATMEL_PDMIC_FORMATS,},
+ .ops = &atmel_pdmic_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
+ .name = "atmel-pdmic",
+};
+
+/* platform */
+#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
+#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE
+
+static const struct snd_pcm_hardware atmel_pdmic_hw = {
+ .info = SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE,
+ .formats = ATMEL_PDMIC_FORMATS,
+ .buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE,
+ .period_bytes_min = 256,
+ .period_bytes_max = 32 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+};
+
+static int
+atmel_pdmic_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_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ slave_config);
+ if (ret) {
+ dev_err(rtd->platform->dev,
+ "hw params to dma slave configure failed\n");
+ return ret;
+ }
+
+ slave_config->src_addr = dd->phy_base + PDMIC_CDR;
+ slave_config->src_maxburst = 1;
+ slave_config->dst_maxburst = 1;
+
+ return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_pdmic_dmaengine_pcm_config = {
+ .prepare_slave_config = atmel_pdmic_platform_configure_dma,
+ .pcm_hardware = &atmel_pdmic_hw,
+ .prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+/* Mic Gain = dgain * 2^(-scale) */
+struct mic_gain {
+ unsigned int dgain;
+ unsigned int scale;
+};
+
+/* range from -90 dB to 90 dB */
+static const struct mic_gain mic_gain_table[] = {
+{ 1, 15}, { 1, 14}, /* -90, -84 dB */
+{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */
+{ 5, 14}, { 13, 15}, /* -70, -68 dB */
+{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */
+{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */
+{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */
+{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */
+{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */
+{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */
+{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */
+{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */
+{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */
+{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */
+{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */
+{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */
+{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */
+{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */
+{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */
+{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */
+{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */
+{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */
+{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */
+{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */
+{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */
+{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */
+{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */
+{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */
+{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */
+{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */
+{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */
+{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */
+{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */
+{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */
+{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */
+{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */
+{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */
+{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */
+{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */
+{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */
+{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */
+{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */
+{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */
+};
+
+static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0),
+ 2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0),
+ 8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
+);
+
+int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ unsigned int dgain_val, scale_val;
+ int i;
+
+ dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
+ >> PDMIC_DSPR1_DGAIN_SHIFT;
+
+ scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
+ >> PDMIC_DSPR0_SCALE_SHIFT;
+
+ for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
+ if ((mic_gain_table[i].dgain == dgain_val) &&
+ (mic_gain_table[i].scale == scale_val))
+ ucontrol->value.integer.value[0] = i;
+ }
+
+ return 0;
+}
+
+static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ int max = mc->max;
+ unsigned int val;
+ int ret;
+
+ val = ucontrol->value.integer.value[0];
+
+ if (val > max)
+ return -EINVAL;
+
+ ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
+ mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
+ mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = {
+SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT,
+ ARRAY_SIZE(mic_gain_table)-1, 0,
+ pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv),
+
+SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
+ PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1),
+
+SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
+};
+
+static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+ struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
+
+ snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
+ (u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
+ .probe = atmel_pdmic_codec_probe,
+ .controls = atmel_pdmic_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
+};
+
+/* codec dai component */
+#define PDMIC_MR_PRESCAL_MAX_VAL 127
+
+static int
+atmel_pdmic_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_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int rate_min = substream->runtime->hw.rate_min;
+ unsigned int rate_max = substream->runtime->hw.rate_max;
+ int fs = params_rate(params);
+ int bits = params_width(params);
+ unsigned long pclk_rate, gclk_rate;
+ unsigned int f_pdmic;
+ u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
+
+ if (params_channels(params) != 1) {
+ dev_err(codec->dev,
+ "only supports one channel\n");
+ return -EINVAL;
+ }
+
+ if ((fs < rate_min) || (fs > rate_max)) {
+ dev_err(codec->dev,
+ "sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
+ fs, rate_min, rate_max);
+
+ return -EINVAL;
+ }
+
+ switch (bits) {
+ case 16:
+ dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS
+ << PDMIC_DSPR0_SIZE_SHIFT);
+ break;
+ case 32:
+ dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS
+ << PDMIC_DSPR0_SIZE_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((fs << 7) > (rate_max << 6)) {
+ f_pdmic = fs << 6;
+ dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT;
+ } else {
+ f_pdmic = fs << 7;
+ dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT;
+ }
+
+ pclk_rate = clk_get_rate(dd->pclk);
+ gclk_rate = clk_get_rate(dd->gclk);
+
+ /* PRESCAL = SELCK/(2*f_pdmic) - 1*/
+ pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1;
+ gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1;
+
+ if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) ||
+ (gclk_rate/((gclk_prescal + 1) << 1) <
+ pclk_rate/((pclk_prescal + 1) << 1))) {
+ mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
+ mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT;
+ } else {
+ mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
+ mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
+ }
+
+ snd_soc_update_bits(codec, PDMIC_MR,
+ PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
+
+ snd_soc_update_bits(codec, PDMIC_DSPR0,
+ PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
+
+ return 0;
+}
+
+static int atmel_pdmic_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, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
+ PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
+
+ return 0;
+}
+
+static int atmel_pdmic_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 val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
+ .hw_params = atmel_pdmic_codec_dai_hw_params,
+ .prepare = atmel_pdmic_codec_dai_prepare,
+ .trigger = atmel_pdmic_codec_dai_trigger,
+};
+
+#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
+
+static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
+ .name = ATMEL_PDMIC_CODEC_DAI_NAME,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = ATMEL_PDMIC_FORMATS,
+ },
+ .ops = &atmel_pdmic_codec_dai_ops,
+};
+
+/* ASoC sound card */
+static int atmel_pdmic_asoc_card_init(struct device *dev,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct atmel_pdmic *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 = "PDMIC";
+ dai_link->stream_name = "PDMIC PCM";
+ dai_link->codec_dai_name = ATMEL_PDMIC_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;
+}
+
+static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd,
+ unsigned int *rate_min, unsigned int *rate_max)
+{
+ u32 mic_min_freq = dd->pdata->mic_min_freq;
+ u32 mic_max_freq = dd->pdata->mic_max_freq;
+ u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1);
+ u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8);
+
+ if (mic_max_freq > clk_max_rate)
+ mic_max_freq = clk_max_rate;
+
+ if (mic_min_freq < clk_min_rate)
+ mic_min_freq = clk_min_rate;
+
+ *rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128);
+ *rate_max = mic_max_freq >> 6;
+}
+
+/* PDMIC interrupt handler */
+static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id)
+{
+ struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id;
+ u32 pdmic_isr;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr);
+
+ if (pdmic_isr & PDMIC_ISR_OVRE) {
+ regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
+ PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
+
+ snd_pcm_stop_xrun(dd->substream);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+/* regmap configuration */
+#define ATMEL_PDMIC_REG_MAX 0x124
+static const struct regmap_config atmel_pdmic_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = ATMEL_PDMIC_REG_MAX,
+};
+
+static int atmel_pdmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_pdmic *dd;
+ struct resource *res;
+ void __iomem *io_base;
+ const struct atmel_pdmic_pdata *pdata;
+ struct snd_soc_card *card;
+ unsigned int rate_min, rate_max;
+ int ret;
+
+ pdata = atmel_pdmic_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: %d\n", ret);
+ return ret;
+ }
+
+ /* The gclk clock frequency must always be tree times
+ * lower than the pclk clock frequency
+ */
+ ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
+ if (ret) {
+ dev_err(dev, "failed to set GCK clock rate: %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_pdmic_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_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0,
+ "PDMIC", (void *)dd);
+ if (ret < 0) {
+ dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
+ dd->irq, ret);
+ return ret;
+ }
+
+ /* Get the minimal and maximal sample rate that micphone supports */
+ atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
+
+ /* register cpu dai */
+ atmel_pdmic_cpu_dai.capture.rate_min = rate_min;
+ atmel_pdmic_cpu_dai.capture.rate_max = rate_max;
+ ret = devm_snd_soc_register_component(dev,
+ &atmel_pdmic_cpu_dai_component,
+ &atmel_pdmic_cpu_dai, 1);
+ if (ret) {
+ dev_err(dev, "could not register CPU DAI: %d\n", ret);
+ return ret;
+ }
+
+ /* register platform */
+ ret = devm_snd_dmaengine_pcm_register(dev,
+ &atmel_pdmic_dmaengine_pcm_config,
+ 0);
+ if (ret) {
+ dev_err(dev, "could not register platform: %d\n", ret);
+ return ret;
+ }
+
+ /* register codec and codec dai */
+ atmel_pdmic_codec_dai.capture.rate_min = rate_min;
+ atmel_pdmic_codec_dai.capture.rate_max = rate_max;
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
+ &atmel_pdmic_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) {
+ ret = -ENOMEM;
+ goto unregister_codec;
+ }
+
+ snd_soc_card_set_drvdata(card, dd);
+ platform_set_drvdata(pdev, card);
+
+ ret = atmel_pdmic_asoc_card_init(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to init sound card: %d\n", ret);
+ goto unregister_codec;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret) {
+ dev_err(dev, "failed to register sound card: %d\n", ret);
+ goto unregister_codec;
+ }
+
+ return 0;
+
+unregister_codec:
+ snd_soc_unregister_codec(dev);
+ return ret;
+}
+
+static int atmel_pdmic_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver atmel_pdmic_driver = {
+ .driver = {
+ .name = "atmel-pdmic",
+ .of_match_table = of_match_ptr(atmel_pdmic_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = atmel_pdmic_probe,
+ .remove = atmel_pdmic_remove,
+};
+module_platform_driver(atmel_pdmic_driver);
+
+MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/atmel/atmel-pdmic.h b/sound/soc/atmel/atmel-pdmic.h
new file mode 100644
index 000000000000..4527ac741919
--- /dev/null
+++ b/sound/soc/atmel/atmel-pdmic.h
@@ -0,0 +1,80 @@
+#ifndef __ATMEL_PDMIC_H_
+#define __ATMEL_PDMIC_H_
+
+#include <linux/bitops.h>
+
+#define PDMIC_CR 0x00000000
+
+#define PDMIC_CR_SWRST 0x1
+#define PDMIC_CR_SWRST_MASK BIT(0)
+#define PDMIC_CR_SWRST_SHIFT (0)
+
+#define PDMIC_CR_ENPDM_DIS 0x0
+#define PDMIC_CR_ENPDM_EN 0x1
+#define PDMIC_CR_ENPDM_MASK BIT(4)
+#define PDMIC_CR_ENPDM_SHIFT (4)
+
+#define PDMIC_MR 0x00000004
+
+#define PDMIC_MR_CLKS_PCK 0x0
+#define PDMIC_MR_CLKS_GCK 0x1
+#define PDMIC_MR_CLKS_MASK BIT(4)
+#define PDMIC_MR_CLKS_SHIFT (4)
+
+#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8)
+#define PDMIC_MR_PRESCAL_SHIFT (8)
+
+#define PDMIC_CDR 0x00000014
+
+#define PDMIC_IER 0x00000018
+#define PDMIC_IER_OVRE BIT(25)
+
+#define PDMIC_IDR 0x0000001c
+#define PDMIC_IDR_OVRE BIT(25)
+
+#define PDMIC_IMR 0x00000020
+
+#define PDMIC_ISR 0x00000024
+#define PDMIC_ISR_OVRE BIT(25)
+
+#define PDMIC_DSPR0 0x00000058
+
+#define PDMIC_DSPR0_HPFBYP_DIS 0x1
+#define PDMIC_DSPR0_HPFBYP_EN 0x0
+#define PDMIC_DSPR0_HPFBYP_MASK BIT(1)
+#define PDMIC_DSPR0_HPFBYP_SHIFT (1)
+
+#define PDMIC_DSPR0_SINBYP_DIS 0x1
+#define PDMIC_DSPR0_SINBYP_EN 0x0
+#define PDMIC_DSPR0_SINBYP_MASK BIT(2)
+#define PDMIC_DSPR0_SINBYP_SHIFT (2)
+
+#define PDMIC_DSPR0_SIZE_16_BITS 0x0
+#define PDMIC_DSPR0_SIZE_32_BITS 0x1
+#define PDMIC_DSPR0_SIZE_MASK BIT(3)
+#define PDMIC_DSPR0_SIZE_SHIFT (3)
+
+#define PDMIC_DSPR0_OSR_128 0x0
+#define PDMIC_DSPR0_OSR_64 0x1
+#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4)
+#define PDMIC_DSPR0_OSR_SHIFT (4)
+
+#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8)
+#define PDMIC_DSPR0_SCALE_SHIFT (8)
+
+#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12)
+#define PDMIC_DSPR0_SHIFT_SHIFT (12)
+
+#define PDMIC_DSPR1 0x0000005c
+
+#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0)
+#define PDMIC_DSPR1_DGAIN_SHIFT (0)
+
+#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16)
+#define PDMIC_DSPR1_OFFSET_SHIFT (16)
+
+#define PDMIC_WPMR 0x000000e4
+
+#define PDMIC_WPSR 0x000000e8
+
+#endif
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index 1933bcd46cca..fdd28ed3e0b9 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -183,6 +183,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = {
.driver = {
.name = "atmel-wm8904-audio",
.of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
+ .pm = &snd_soc_pm_ops,
},
.probe = atmel_asoc_wm8904_probe,
.remove = atmel_asoc_wm8904_remove,
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index cfdafc4c11ea..05fb938c7704 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -55,9 +55,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CS4349 if I2C
+ select SND_SOC_CS47L24 if MFD_CS47L24
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_DA7218 if I2C
select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
@@ -85,6 +87,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
+ select SND_SOC_PCM3168A_I2C if I2C
+ select SND_SOC_PCM3168A_SPI if SPI_MASTER
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
@@ -195,10 +199,12 @@ config SND_SOC_88PM860X
config SND_SOC_ARIZONA
tristate
+ default y if SND_SOC_CS47L24=y
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_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
@@ -211,9 +217,12 @@ config SND_SOC_WM_HUBS
config SND_SOC_WM_ADSP
tristate
+ select SND_SOC_COMPRESS
+ default y if SND_SOC_CS47L24=y
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM2200=y
+ default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM2200=m
@@ -422,6 +431,9 @@ config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
depends on I2C
+config SND_SOC_CS47L24
+ tristate
+
config SND_SOC_CX20442
tristate
depends on TTY
@@ -439,6 +451,9 @@ config SND_SOC_DA7210
config SND_SOC_DA7213
tristate
+config SND_SOC_DA7218
+ tristate
+
config SND_SOC_DA7219
tristate
@@ -506,6 +521,21 @@ config SND_SOC_PCM1792A
config SND_SOC_PCM3008
tristate
+config SND_SOC_PCM3168A
+ tristate
+
+config SND_SOC_PCM3168A_I2C
+ tristate "Texas Instruments PCM3168A CODEC - I2C"
+ depends on I2C
+ select SND_SOC_PCM3168A
+ select REGMAP_I2C
+
+config SND_SOC_PCM3168A_SPI
+ tristate "Texas Instruments PCM3168A CODEC - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM3168A
+ select REGMAP_SPI
+
config SND_SOC_PCM512x
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f632fc42f59f..266c6be0c7f5 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -47,9 +47,11 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cs4349-objs := cs4349.o
+snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
+snd-soc-da7218-objs := da7218.o
snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
@@ -78,6 +80,9 @@ 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
+snd-soc-pcm3168a-objs := pcm3168a.o
+snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
@@ -242,9 +247,11 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
+obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.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_DA7218) += snd-soc-da7218.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
@@ -273,6 +280,9 @@ 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
+obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
+obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 07a266460ec3..647f69de6baa 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -70,18 +70,11 @@
#define FMT_MASK (0xf8)
/* CTRL2 */
+#define DFS_MASK (3 << 2)
#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;
@@ -92,6 +85,16 @@ struct ak4613_interface {
struct ak4613_formats playback;
};
+struct ak4613_priv {
+ struct mutex lock;
+ const struct ak4613_interface *iface;
+
+ unsigned int fmt;
+ u8 oc;
+ u8 ic;
+ int cnt;
+};
+
/*
* Playback Volume
*
@@ -126,7 +129,7 @@ static const struct reg_default ak4613_reg[] = {
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
};
-#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
static const struct ak4613_interface ak4613_iface[] = {
/* capture */ /* playback */
@@ -240,7 +243,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
priv->cnt = 0;
}
if (!priv->cnt)
- priv->fmt_ctrl = NO_FMT;
+ priv->iface = NULL;
mutex_unlock(&priv->lock);
}
@@ -265,13 +268,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
+ int is_play,
+ unsigned int fmt, unsigned int width)
+{
+ const struct ak4613_formats *fmts;
+
+ fmts = (is_play) ? &iface->playback : &iface->capture;
+
+ if (fmts->fmt != fmt)
+ return false;
+
+ if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+ if (fmts->width != width)
+ return false;
+ } else {
+ if (fmts->width < width)
+ return false;
+ }
+
+ return true;
+}
+
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;
+ const struct ak4613_interface *iface;
struct device *dev = codec->dev;
unsigned int width = params_width(params);
unsigned int fmt = priv->fmt;
@@ -305,33 +330,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
* 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;
+ ret = -EINVAL;
+ iface = NULL;
- if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
- if (fmts->width != width)
- continue;
- } else {
- if (fmts->width < width)
+ mutex_lock(&priv->lock);
+ if (priv->iface) {
+ if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
+ iface = priv->iface;
+ } else {
+ for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) {
+ if (!ak4613_dai_fmt_matching(ak4613_iface + i,
+ is_play,
+ fmt, width))
continue;
+ iface = ak4613_iface + i;
+ break;
}
-
- 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;
+ if ((priv->iface == NULL) ||
+ (priv->iface == iface)) {
+ priv->iface = iface;
priv->cnt++;
ret = 0;
}
@@ -340,8 +359,13 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
goto hw_params_end;
+ fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
+
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
- snd_soc_write(codec, CTRL2, ctrl2);
+ snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
+
+ snd_soc_write(codec, ICTRL, priv->ic);
+ snd_soc_write(codec, OCTRL, priv->oc);
hw_params_end:
if (ret < 0)
@@ -431,6 +455,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
.num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
};
+static void ak4613_parse_of(struct ak4613_priv *priv,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ char prop[32];
+ int i;
+
+ /* Input 1 - 2 */
+ for (i = 0; i < 2; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
+ if (!of_get_property(np, prop, NULL))
+ priv->ic |= 1 << i;
+ }
+
+ /* Output 1 - 6 */
+ for (i = 0; i < 6; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
+ if (!of_get_property(np, prop, NULL))
+ priv->oc |= 1 << i;
+ }
+}
+
static int ak4613_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -458,7 +504,9 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
- priv->fmt_ctrl = NO_FMT;
+ ak4613_parse_of(priv, dev);
+
+ priv->iface = NULL;
priv->cnt = 0;
mutex_init(&priv->lock);
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 9929efc6b9aa..38a73e3da508 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -310,7 +310,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
-const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
+const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
"Tone Generator 2",
@@ -418,7 +418,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
};
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
-int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
+unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x00, /* None */
0x04, /* Tone */
0x05,
@@ -555,12 +555,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
}
EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
-const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
+const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
};
EXPORT_SYMBOL_GPL(arizona_rate_text);
-const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
+const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
0, 1, 2, 8,
};
EXPORT_SYMBOL_GPL(arizona_rate_val);
@@ -702,6 +702,100 @@ const struct soc_enum arizona_in_dmic_osr[] = {
};
EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+static const char * const arizona_anc_input_src_text[] = {
+ "None", "IN1", "IN2", "IN3", "IN4",
+};
+
+static const char * const arizona_anc_channel_src_text[] = {
+ "None", "Left", "Right", "Combine",
+};
+
+const struct soc_enum arizona_anc_input_src[] = {
+ SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
+ ARIZONA_IN_RXANCL_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_input_src_text),
+ arizona_anc_input_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
+ ARIZONA_FCL_MIC_MODE_SEL,
+ ARRAY_SIZE(arizona_anc_channel_src_text),
+ arizona_anc_channel_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
+ ARIZONA_IN_RXANCR_SEL_SHIFT,
+ ARRAY_SIZE(arizona_anc_input_src_text),
+ arizona_anc_input_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
+ ARIZONA_FCR_MIC_MODE_SEL,
+ ARRAY_SIZE(arizona_anc_channel_src_text),
+ arizona_anc_channel_src_text),
+};
+EXPORT_SYMBOL_GPL(arizona_anc_input_src);
+
+static const char * const arizona_anc_ng_texts[] = {
+ "None",
+ "Internal",
+ "External",
+};
+
+SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0,
+ arizona_anc_ng_texts);
+EXPORT_SYMBOL_GPL(arizona_anc_ng_enum);
+
+static const char * const arizona_output_anc_src_text[] = {
+ "None", "RXANCL", "RXANCR",
+};
+
+const struct soc_enum arizona_output_anc_src[] = {
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R,
+ ARIZONA_OUT1R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
+ ARIZONA_OUT2L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R,
+ ARIZONA_OUT2R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R,
+ ARIZONA_OUT3R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R,
+ ARIZONA_OUT4R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R,
+ ARIZONA_OUT5R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L,
+ ARIZONA_OUT6L_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+ SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R,
+ ARIZONA_OUT6R_ANC_SRC_SHIFT,
+ ARRAY_SIZE(arizona_output_anc_src_text),
+ arizona_output_anc_src_text),
+};
+EXPORT_SYMBOL_GPL(arizona_output_anc_src);
+
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -1023,24 +1117,43 @@ void arizona_init_dvfs(struct arizona_priv *priv)
}
EXPORT_SYMBOL_GPL(arizona_init_dvfs);
-static unsigned int arizona_sysclk_48k_rates[] = {
+int arizona_anc_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 mask = 0x3 << w->shift;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1 << w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 1 << (w->shift + 1);
+ break;
+ default:
+ return 0;
+ }
+
+ snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_anc_ev);
+
+static unsigned int arizona_opclk_ref_48k_rates[] = {
6144000,
12288000,
24576000,
49152000,
- 73728000,
- 98304000,
- 147456000,
};
-static unsigned int arizona_sysclk_44k1_rates[] = {
+static unsigned int arizona_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
- 67737600,
- 90316800,
- 135475200,
};
static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
@@ -1065,11 +1178,11 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
if (refclk % 8000)
- rates = arizona_sysclk_44k1_rates;
+ rates = arizona_opclk_ref_44k1_rates;
else
- rates = arizona_sysclk_48k_rates;
+ rates = arizona_opclk_ref_48k_rates;
- for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
+ for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
rates[ref] <= refclk; ref++) {
div = 1;
while (rates[ref] / div >= freq && div < 32) {
@@ -1101,7 +1214,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
unsigned int reg;
unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
- unsigned int *clk;
+ int *clk;
switch (clk_id) {
case ARIZONA_CLK_SYSCLK:
@@ -1907,18 +2020,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
}
switch (fll->arizona->type) {
+ case WM5102:
+ case WM8997:
+ return init_ratio;
case WM5110:
case WM8280:
if (fll->arizona->rev < 3 || sync)
return init_ratio;
break;
- case WM8998:
- case WM1814:
+ default:
if (sync)
return init_ratio;
break;
- default:
- return init_ratio;
}
cfg->fratio = init_ratio - 1;
@@ -2099,9 +2212,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
/* Facilitate smooth refclk across the transition */
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
ARIZONA_FLL1_GAIN_MASK, 0);
- regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_FREERUN,
- ARIZONA_FLL1_FREERUN);
+ regmap_update_bits(fll->arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
+ udelay(32);
}
/*
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index fea8b8ae8e1a..8b6adb5419bb 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 6
+#define ARIZONA_MAX_DAI 8
#define ARIZONA_MAX_ADSP 4
#define ARIZONA_DVFS_SR1_RQ 0x001
@@ -96,8 +96,8 @@ struct arizona_priv {
#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];
+extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
+extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_GAINMUX_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
@@ -216,8 +216,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#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_rate_text[ARIZONA_RATE_ENUM_SIZE];
+extern const unsigned 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];
@@ -242,6 +242,10 @@ extern const struct soc_enum arizona_in_dmic_osr[];
extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
+extern const struct soc_enum arizona_anc_input_src[];
+extern const struct soc_enum arizona_anc_ng_enum;
+extern const struct soc_enum arizona_output_anc_src[];
+
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@@ -251,6 +255,9 @@ extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
+extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event);
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
new file mode 100644
index 000000000000..dc5ae7f7a1bd
--- /dev/null
+++ b/sound/soc/codecs/cs47l24.c
@@ -0,0 +1,1148 @@
+/*
+ * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
+ *
+ * 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 "wm_adsp.h"
+#include "cs47l24.h"
+
+struct cs47l24_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static const struct wm_adsp_region cs47l24_dsp2_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x200000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x280000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x290000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x2a8000 },
+};
+
+static const struct wm_adsp_region cs47l24_dsp3_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x300000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x380000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x390000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3a8000 },
+};
+
+static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
+ cs47l24_dsp2_regions,
+ cs47l24_dsp3_regions,
+};
+
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define CS47L24_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0)
+
+static const struct snd_kcontrol_new cs47l24_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+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("IN2L HPF Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_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("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_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_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+
+ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2),
+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),
+
+ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2),
+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),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
+ ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_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),
+
+ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2),
+ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
+ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
+ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
+
+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("ISRC3 FSL", arizona_isrc_fsl[2]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
+ ARIZONA_HP1_SC_ENA_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+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),
+
+CS47L24_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+CS47L24_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+CS47L24_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+
+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("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_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_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_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(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_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(SPKOUT, ARIZONA_OUT4LMIX_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(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_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(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(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE);
+
+static const char * const cs47l24_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "SPKOUT",
+};
+
+static const unsigned int cs47l24_aec_loopback_values[] = {
+ 0, 1, 6,
+};
+
+static const struct soc_enum cs47l24_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(cs47l24_aec_loopback_texts),
+ cs47l24_aec_loopback_texts,
+ cs47l24_aec_loopback_values);
+
+static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", cs47l24_aec_loopback);
+
+static const struct snd_soc_dapm_widget cs47l24_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("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+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,
+ 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("IN2L 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_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_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_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_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("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("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_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("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+ NULL, 0),
+
+WM_ADSP2("DSP2", 1),
+WM_ADSP2("DSP3", 2),
+
+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("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT3_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_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3,
+ ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &cs47l24_aec_loopback_mux),
+
+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_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_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_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_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_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_PMD |
+ SND_SOC_DAPM_PRE_PMU | 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_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+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, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+
+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(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+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(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_DSP_WIDGETS(DSP2, "DSP2"),
+ARIZONA_DSP_WIDGETS(DSP3, "DSP3"),
+
+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(ISRC2DEC3, "ISRC2DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"),
+ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"),
+ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { 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, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { 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, "ISRC2DEC3", "ISRC2DEC3" }, \
+ { name, "ISRC2DEC4", "ISRC2DEC4" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC2INT3", "ISRC2INT3" }, \
+ { name, "ISRC2INT4", "ISRC2INT4" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3DEC3", "ISRC3DEC3" }, \
+ { name, "ISRC3DEC4", "ISRC3DEC4" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "ISRC3INT3", "ISRC3INT3" }, \
+ { name, "ISRC3INT4", "ISRC3INT4" }, \
+ { name, "DSP2.1", "DSP2" }, \
+ { name, "DSP2.2", "DSP2" }, \
+ { name, "DSP2.3", "DSP2" }, \
+ { name, "DSP2.4", "DSP2" }, \
+ { name, "DSP2.5", "DSP2" }, \
+ { name, "DSP2.6", "DSP2" }, \
+ { name, "DSP3.1", "DSP3" }, \
+ { name, "DSP3.2", "DSP3" }, \
+ { name, "DSP3.3", "DSP3" }, \
+ { name, "DSP3.4", "DSP3" }, \
+ { name, "DSP3.5", "DSP3" }, \
+ { name, "DSP3.6", "DSP3" }
+
+static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "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" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", 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" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "AIF3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "AIF3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+
+ 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("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ 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_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+ ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
+ ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ 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_DSP_ROUTES("DSP2"),
+ ARIZONA_DSP_ROUTES("DSP3"),
+
+ 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("ISRC2INT3", "ISRC2INT3"),
+ ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+ ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"),
+ ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"),
+
+ ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"),
+ ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+ { "DRC2 Signal Activity", NULL, "DRC2L" },
+ { "DRC2 Signal Activity", NULL, "DRC2R" },
+};
+
+static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct cs47l24_priv *cs47l24 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case CS47L24_FLL1:
+ return arizona_set_fll(&cs47l24->fll[0], source, Fref, Fout);
+ case CS47L24_FLL2:
+ return arizona_set_fll(&cs47l24->fll[1], source, Fref, Fout);
+ case CS47L24_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&cs47l24->fll[0], source, Fref,
+ Fout);
+ case CS47L24_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&cs47l24->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000
+
+#define CS47L24_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 cs47l24_dai[] = {
+ {
+ .name = "cs47l24-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "cs47l24-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+ {
+ .name = "cs47l24-aif3",
+ .id = 3,
+ .base = ARIZONA_AIF3_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ },
+};
+
+static int cs47l24_codec_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ priv->core.arizona->dapm = dapm;
+
+ arizona_init_spk(codec);
+ arizona_init_gpio(codec);
+ arizona_init_mono(codec);
+
+ ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ ret = wm_adsp2_codec_probe(&priv->core.adsp[2], codec);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ ret = snd_soc_add_codec_controls(codec,
+ &arizona_adsp2_rate_controls[1], 2);
+ if (ret)
+ goto err_adsp2_codec_probe;
+
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+ return 0;
+
+err_adsp2_codec_probe:
+ wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
+ wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+
+ return ret;
+}
+
+static int cs47l24_codec_remove(struct snd_soc_codec *codec)
+{
+ struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+
+ wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
+ wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define CS47L24_DIG_VU 0x0200
+
+static unsigned int cs47l24_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+};
+
+static struct regmap *cs47l24_get_regmap(struct device *dev)
+{
+ struct cs47l24_priv *priv = dev_get_drvdata(dev);
+
+ return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
+ .probe = cs47l24_codec_probe,
+ .remove = cs47l24_codec_remove,
+ .get_regmap = cs47l24_get_regmap,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = cs47l24_set_fll,
+
+ .controls = cs47l24_snd_controls,
+ .num_controls = ARRAY_SIZE(cs47l24_snd_controls),
+ .dapm_widgets = cs47l24_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets),
+ .dapm_routes = cs47l24_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
+};
+
+static int cs47l24_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct cs47l24_priv *cs47l24;
+ int i, ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI);
+
+ cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv),
+ GFP_KERNEL);
+ if (!cs47l24)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cs47l24);
+
+ cs47l24->core.arizona = arizona;
+ cs47l24->core.num_inputs = 4;
+
+ for (i = 1; i <= 2; i++) {
+ cs47l24->core.adsp[i].part = "cs47l24";
+ cs47l24->core.adsp[i].num = i + 1;
+ cs47l24->core.adsp[i].type = WMFW_ADSP2;
+ cs47l24->core.adsp[i].dev = arizona->dev;
+ cs47l24->core.adsp[i].regmap = arizona->regmap;
+
+ cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 +
+ (0x100 * i);
+ cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1];
+ cs47l24->core.adsp[i].num_mems =
+ ARRAY_SIZE(cs47l24_dsp2_regions);
+
+ ret = wm_adsp2_init(&cs47l24->core.adsp[i]);
+ if (ret != 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs47l24->fll); i++)
+ cs47l24->fll[i].vco_mult = 3;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &cs47l24->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &cs47l24->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(cs47l24_dai); i++)
+ arizona_init_dai(&cs47l24->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(cs47l24_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, cs47l24_digital_vu[i],
+ CS47L24_DIG_VU, CS47L24_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
+ cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
+}
+
+static int cs47l24_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver cs47l24_codec_driver = {
+ .driver = {
+ .name = "cs47l24-codec",
+ },
+ .probe = cs47l24_probe,
+ .remove = cs47l24_remove,
+};
+
+module_platform_driver(cs47l24_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS47L24 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l24-codec");
diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h
new file mode 100644
index 000000000000..77ab2b77b2e6
--- /dev/null
+++ b/sound/soc/codecs/cs47l24.h
@@ -0,0 +1,23 @@
+/*
+ * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
+ *
+ * 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 _CS47L24_H
+#define _CS47L24_H
+
+#include "arizona.h"
+
+#define CS47L24_FLL1 1
+#define CS47L24_FLL2 2
+#define CS47L24_FLL1_REFCLK 3
+#define CS47L24_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
new file mode 100644
index 000000000000..72686517ff54
--- /dev/null
+++ b/sound/soc/codecs/da7218.c
@@ -0,0 +1,3314 @@
+/*
+ * da7218.c - DA7218 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/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7218.h>
+#include "da7218.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_in_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_ags_trigger_tlv, -9000, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_ags_att_max_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_alc_ana_gain_tlv, 0, 600, 0);
+
+/* Input/Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_dmix_gain_tlv, -4200, 150, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_trigger_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_anticlip_tlv, -4200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dgs_signal_tlv, -9000, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_out_eq_band_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_out_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_mixout_gain_tlv, -100, 50, 0);
+static const DECLARE_TLV_DB_SCALE(da7218_hp_gain_tlv, -5700, 150, 0);
+
+/* Input Enums */
+static const char * const da7218_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 da7218_alc_attack_rate =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_ATTACK_SHIFT,
+ DA7218_ALC_ATTACK_MAX, da7218_alc_attack_rate_txt);
+
+static const char * const da7218_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 da7218_alc_release_rate =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_RELEASE_SHIFT,
+ DA7218_ALC_RELEASE_MAX, da7218_alc_release_rate_txt);
+
+static const char * const da7218_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 da7218_alc_hold_time =
+ SOC_ENUM_SINGLE(DA7218_ALC_CTRL3, DA7218_ALC_HOLD_SHIFT,
+ DA7218_ALC_HOLD_MAX, da7218_alc_hold_time_txt);
+
+static const char * const da7218_alc_anticlip_step_txt[] = {
+ "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs",
+};
+
+static const struct soc_enum da7218_alc_anticlip_step =
+ SOC_ENUM_SINGLE(DA7218_ALC_ANTICLIP_CTRL,
+ DA7218_ALC_ANTICLIP_STEP_SHIFT,
+ DA7218_ALC_ANTICLIP_STEP_MAX,
+ da7218_alc_anticlip_step_txt);
+
+static const char * const da7218_integ_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7218_integ_attack_rate =
+ SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_ATTACK_SHIFT,
+ DA7218_INTEG_MAX, da7218_integ_rate_txt);
+
+static const struct soc_enum da7218_integ_release_rate =
+ SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_RELEASE_SHIFT,
+ DA7218_INTEG_MAX, da7218_integ_rate_txt);
+
+/* Input/Output Enums */
+static const char * const da7218_gain_ramp_rate_txt[] = {
+ "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+ "Nominal Rate / 16",
+};
+
+static const struct soc_enum da7218_gain_ramp_rate =
+ SOC_ENUM_SINGLE(DA7218_GAIN_RAMP_CTRL, DA7218_GAIN_RAMP_RATE_SHIFT,
+ DA7218_GAIN_RAMP_RATE_MAX, da7218_gain_ramp_rate_txt);
+
+static const char * const da7218_hpf_mode_txt[] = {
+ "Disabled", "Audio", "Voice",
+};
+
+static const unsigned int da7218_hpf_mode_val[] = {
+ DA7218_HPF_DISABLED, DA7218_HPF_AUDIO_EN, DA7218_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7218_in1_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const struct soc_enum da7218_in2_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const struct soc_enum da7218_out1_hpf_mode =
+ SOC_VALUE_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK,
+ DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt,
+ da7218_hpf_mode_val);
+
+static const char * const da7218_audio_hpf_corner_txt[] = {
+ "2Hz", "4Hz", "8Hz", "16Hz",
+};
+
+static const struct soc_enum da7218_in1_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const struct soc_enum da7218_in2_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const struct soc_enum da7218_out1_audio_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT,
+ DA7218_AUDIO_HPF_CORNER_MAX,
+ da7218_audio_hpf_corner_txt);
+
+static const char * const da7218_voice_hpf_corner_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz",
+};
+
+static const struct soc_enum da7218_in1_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const struct soc_enum da7218_in2_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const struct soc_enum da7218_out1_voice_hpf_corner =
+ SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL,
+ DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT,
+ DA7218_VOICE_HPF_CORNER_MAX,
+ da7218_voice_hpf_corner_txt);
+
+static const char * const da7218_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7218_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG1, DA7218_DTMF_REG_SHIFT,
+ DA7218_DTMF_REG_MAX, da7218_tonegen_dtmf_key_txt);
+
+static const char * const da7218_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7218_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG2, DA7218_SWG_SEL_SHIFT,
+ DA7218_SWG_SEL_MAX, da7218_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7218_dgs_rise_coeff_txt[] = {
+ "1/1", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384",
+};
+
+static const struct soc_enum da7218_dgs_rise_coeff =
+ SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_RISE_COEFF_SHIFT,
+ DA7218_DGS_RISE_COEFF_MAX, da7218_dgs_rise_coeff_txt);
+
+static const char * const da7218_dgs_fall_coeff_txt[] = {
+ "1/4", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", "1/65536",
+};
+
+static const struct soc_enum da7218_dgs_fall_coeff =
+ SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_FALL_COEFF_SHIFT,
+ DA7218_DGS_FALL_COEFF_MAX, da7218_dgs_fall_coeff_txt);
+
+static const char * const da7218_dac_ng_setup_time_txt[] = {
+ "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7218_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_SETUP_TIME_SHIFT,
+ DA7218_DAC_NG_SETUP_TIME_MAX,
+ da7218_dac_ng_setup_time_txt);
+
+static const char * const da7218_dac_ng_rampup_txt[] = {
+ "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7218_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_RAMPUP_RATE_SHIFT,
+ DA7218_DAC_NG_RAMPUP_RATE_MAX,
+ da7218_dac_ng_rampup_txt);
+
+static const char * const da7218_dac_ng_rampdown_txt[] = {
+ "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7218_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME,
+ DA7218_DAC_NG_RAMPDN_RATE_SHIFT,
+ DA7218_DAC_NG_RAMPDN_RATE_MAX,
+ da7218_dac_ng_rampdown_txt);
+
+static const char * const da7218_cp_mchange_txt[] = {
+ "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7218_cp_mchange_val[] = {
+ DA7218_CP_MCHANGE_LARGEST_VOL, DA7218_CP_MCHANGE_DAC_VOL,
+ DA7218_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7218_cp_mchange =
+ SOC_VALUE_ENUM_SINGLE(DA7218_CP_CTRL, DA7218_CP_MCHANGE_SHIFT,
+ DA7218_CP_MCHANGE_REL_MASK, DA7218_CP_MCHANGE_MAX,
+ da7218_cp_mchange_txt, da7218_cp_mchange_val);
+
+static const char * const da7218_cp_fcontrol_txt[] = {
+ "1MHz", "500KHz", "250KHz", "125KHz", "63KHz", "0KHz"
+};
+
+static const struct soc_enum da7218_cp_fcontrol =
+ SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_FCONTROL_SHIFT,
+ DA7218_CP_FCONTROL_MAX, da7218_cp_fcontrol_txt);
+
+static const char * const da7218_cp_tau_delay_txt[] = {
+ "0ms", "2ms", "4ms", "16ms", "64ms", "128ms", "256ms", "512ms"
+};
+
+static const struct soc_enum da7218_cp_tau_delay =
+ SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_TAU_DELAY_SHIFT,
+ DA7218_CP_TAU_DELAY_MAX, da7218_cp_tau_delay_txt);
+
+/*
+ * Control Functions
+ */
+
+/* ALC */
+static void da7218_alc_calib(struct snd_soc_codec *codec)
+{
+ u8 mic_1_ctrl, mic_2_ctrl;
+ u8 mixin_1_ctrl, mixin_2_ctrl;
+ u8 in_1l_filt_ctrl, in_1r_filt_ctrl, in_2l_filt_ctrl, in_2r_filt_ctrl;
+ u8 in_1_hpf_ctrl, in_2_hpf_ctrl;
+ u8 calib_ctrl;
+ int i = 0;
+ bool calibrated = false;
+
+ /* Save current state of MIC control registers */
+ mic_1_ctrl = snd_soc_read(codec, DA7218_MIC_1_CTRL);
+ mic_2_ctrl = snd_soc_read(codec, DA7218_MIC_2_CTRL);
+
+ /* Save current state of input mixer control registers */
+ mixin_1_ctrl = snd_soc_read(codec, DA7218_MIXIN_1_CTRL);
+ mixin_2_ctrl = snd_soc_read(codec, DA7218_MIXIN_2_CTRL);
+
+ /* Save current state of input filter control registers */
+ in_1l_filt_ctrl = snd_soc_read(codec, DA7218_IN_1L_FILTER_CTRL);
+ in_1r_filt_ctrl = snd_soc_read(codec, DA7218_IN_1R_FILTER_CTRL);
+ in_2l_filt_ctrl = snd_soc_read(codec, DA7218_IN_2L_FILTER_CTRL);
+ in_2r_filt_ctrl = snd_soc_read(codec, DA7218_IN_2R_FILTER_CTRL);
+
+ /* Save current state of input HPF control registers */
+ in_1_hpf_ctrl = snd_soc_read(codec, DA7218_IN_1_HPF_FILTER_CTRL);
+ in_2_hpf_ctrl = snd_soc_read(codec, DA7218_IN_2_HPF_FILTER_CTRL);
+
+ /* Enable then Mute MIC PGAs */
+ snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK,
+ DA7218_MIC_1_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK,
+ DA7218_MIC_2_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_MUTE_EN_MASK,
+ DA7218_MIC_1_AMP_MUTE_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_MUTE_EN_MASK,
+ DA7218_MIC_2_AMP_MUTE_EN_MASK);
+
+ /* Enable input mixers unmuted */
+ snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_EN_MASK |
+ DA7218_MIXIN_1_AMP_MUTE_EN_MASK,
+ DA7218_MIXIN_1_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_EN_MASK |
+ DA7218_MIXIN_2_AMP_MUTE_EN_MASK,
+ DA7218_MIXIN_2_AMP_EN_MASK);
+
+ /* Enable input filters unmuted */
+ snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_FILTER_EN_MASK |
+ DA7218_IN_1L_MUTE_EN_MASK,
+ DA7218_IN_1L_FILTER_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_MASK |
+ DA7218_IN_1R_MUTE_EN_MASK,
+ DA7218_IN_1R_FILTER_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_FILTER_EN_MASK |
+ DA7218_IN_2L_MUTE_EN_MASK,
+ DA7218_IN_2L_FILTER_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_FILTER_EN_MASK |
+ DA7218_IN_2R_MUTE_EN_MASK,
+ DA7218_IN_2R_FILTER_EN_MASK);
+
+ /*
+ * Make sure input HPFs voice mode is disabled, otherwise for sampling
+ * rates above 32KHz the ADC signals will be stopped and will cause
+ * calibration to lock up.
+ */
+ snd_soc_update_bits(codec, DA7218_IN_1_HPF_FILTER_CTRL,
+ DA7218_IN_1_VOICE_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7218_IN_2_HPF_FILTER_CTRL,
+ DA7218_IN_2_VOICE_EN_MASK, 0);
+
+ /* Perform auto calibration */
+ snd_soc_update_bits(codec, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK,
+ DA7218_CALIB_AUTO_EN_MASK);
+ do {
+ calib_ctrl = snd_soc_read(codec, DA7218_CALIB_CTRL);
+ if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) {
+ ++i;
+ usleep_range(DA7218_ALC_CALIB_DELAY_MIN,
+ DA7218_ALC_CALIB_DELAY_MAX);
+ } else {
+ calibrated = true;
+ }
+
+ } while ((i < DA7218_ALC_CALIB_MAX_TRIES) && (!calibrated));
+
+ /* If auto calibration fails, disable DC offset, hybrid ALC */
+ if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) {
+ dev_warn(codec->dev,
+ "ALC auto calibration failed - %s\n",
+ (calibrated) ? "overflow" : "timeout");
+ snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ DA7218_CALIB_OFFSET_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7218_ALC_CTRL1,
+ DA7218_ALC_SYNC_MODE_MASK, 0);
+
+ } else {
+ /* Enable DC offset cancellation */
+ snd_soc_update_bits(codec, DA7218_CALIB_CTRL,
+ DA7218_CALIB_OFFSET_EN_MASK,
+ DA7218_CALIB_OFFSET_EN_MASK);
+
+ /* Enable ALC hybrid mode */
+ snd_soc_update_bits(codec, DA7218_ALC_CTRL1,
+ DA7218_ALC_SYNC_MODE_MASK,
+ DA7218_ALC_SYNC_MODE_CH1 |
+ DA7218_ALC_SYNC_MODE_CH2);
+ }
+
+ /* Restore input HPF control registers to original states */
+ snd_soc_write(codec, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl);
+ snd_soc_write(codec, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl);
+
+ /* Restore input filter control registers to original states */
+ snd_soc_write(codec, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl);
+ snd_soc_write(codec, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl);
+ snd_soc_write(codec, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl);
+ snd_soc_write(codec, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl);
+
+ /* Restore input mixer control registers to original state */
+ snd_soc_write(codec, DA7218_MIXIN_1_CTRL, mixin_1_ctrl);
+ snd_soc_write(codec, DA7218_MIXIN_2_CTRL, mixin_2_ctrl);
+
+ /* Restore MIC control registers to original states */
+ snd_soc_write(codec, DA7218_MIC_1_CTRL, mic_1_ctrl);
+ snd_soc_write(codec, DA7218_MIC_2_CTRL, mic_2_ctrl);
+}
+
+static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = 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) && (da7218->alc_en))
+ da7218_alc_calib(codec);
+
+ return ret;
+}
+
+static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ unsigned int lvalue = ucontrol->value.integer.value[0];
+ unsigned int rvalue = ucontrol->value.integer.value[1];
+ unsigned int lshift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ unsigned int mask = (mc->max << lshift) | (mc->max << rshift);
+
+ /* Force ALC offset calibration if enabling ALC */
+ if ((lvalue || rvalue) && (!da7218->alc_en))
+ da7218_alc_calib(codec);
+
+ /* Update bits to detail which channels are enabled/disabled */
+ da7218->alc_en &= ~mask;
+ da7218->alc_en |= (lvalue << lshift) | (rvalue << rshift);
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = 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 host endianness here.
+ */
+ ret = regmap_raw_read(da7218->regmap, reg, &val, 2);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+ return 0;
+}
+
+static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = 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;
+
+ /*
+ * 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]);
+
+ return regmap_raw_write(da7218->regmap, reg, &val, 2);
+}
+
+static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int lvalue = ucontrol->value.integer.value[0];
+ unsigned int rvalue = ucontrol->value.integer.value[1];
+ unsigned int lshift = mixer_ctrl->shift;
+ unsigned int rshift = mixer_ctrl->rshift;
+ unsigned int mask = (mixer_ctrl->max << lshift) |
+ (mixer_ctrl->max << rshift);
+ da7218->mic_lvl_det_en &= ~mask;
+ da7218->mic_lvl_det_en |= (lvalue << lshift) | (rvalue << rshift);
+
+ /*
+ * Here we only enable the feature on paths which are already
+ * powered. If a channel is enabled here for level detect, but that path
+ * isn't powered, then the channel will actually be enabled when we do
+ * power the path (IN_FILTER widget events). This handling avoids
+ * unwanted level detect events.
+ */
+ return snd_soc_write(codec, mixer_ctrl->reg,
+ (da7218->in_filt_en & da7218->mic_lvl_det_en));
+}
+
+static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mixer_ctrl =
+ (struct soc_mixer_control *) kcontrol->private_value;
+ unsigned int lshift = mixer_ctrl->shift;
+ unsigned int rshift = mixer_ctrl->rshift;
+ unsigned int lmask = (mixer_ctrl->max << lshift);
+ unsigned int rmask = (mixer_ctrl->max << rshift);
+
+ ucontrol->value.integer.value[0] =
+ (da7218->mic_lvl_det_en & lmask) >> lshift;
+ ucontrol->value.integer.value[1] =
+ (da7218->mic_lvl_det_en & rmask) >> rshift;
+
+ return 0;
+}
+
+static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+
+ /* Determine which BiQuads we're setting based on size of config data */
+ switch (bytes_ext->max) {
+ case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE:
+ memcpy(ucontrol->value.bytes.data, da7218->biq_5stage_coeff,
+ bytes_ext->max);
+ break;
+ case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE:
+ memcpy(ucontrol->value.bytes.data, da7218->stbiq_3stage_coeff,
+ bytes_ext->max);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ u8 reg, out_filt1l;
+ u8 cfg[DA7218_BIQ_CFG_SIZE];
+ int i;
+
+ /*
+ * Determine which BiQuads we're setting based on size of config data,
+ * and stored the data for use by get function.
+ */
+ switch (bytes_ext->max) {
+ case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE:
+ reg = DA7218_OUT_1_BIQ_5STAGE_DATA;
+ memcpy(da7218->biq_5stage_coeff, ucontrol->value.bytes.data,
+ bytes_ext->max);
+ break;
+ case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE:
+ reg = DA7218_SIDETONE_BIQ_3STAGE_DATA;
+ memcpy(da7218->stbiq_3stage_coeff, ucontrol->value.bytes.data,
+ bytes_ext->max);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Make sure at least out filter1 enabled to allow programming */
+ out_filt1l = snd_soc_read(codec, DA7218_OUT_1L_FILTER_CTRL);
+ snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL,
+ out_filt1l | DA7218_OUT_1L_FILTER_EN_MASK);
+
+ for (i = 0; i < bytes_ext->max; ++i) {
+ cfg[DA7218_BIQ_CFG_DATA] = ucontrol->value.bytes.data[i];
+ cfg[DA7218_BIQ_CFG_ADDR] = i;
+ regmap_raw_write(da7218->regmap, reg, cfg, DA7218_BIQ_CFG_SIZE);
+ }
+
+ /* Restore filter to previous setting */
+ snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL, out_filt1l);
+
+ return 0;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7218_snd_controls[] = {
+ /* Mics */
+ SOC_SINGLE_TLV("Mic1 Volume", DA7218_MIC_1_GAIN,
+ DA7218_MIC_1_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_mic_gain_tlv),
+ SOC_SINGLE("Mic1 Switch", DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE_TLV("Mic2 Volume", DA7218_MIC_2_GAIN,
+ DA7218_MIC_2_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_mic_gain_tlv),
+ SOC_SINGLE("Mic2 Switch", DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Mixer Input */
+ SOC_SINGLE_EXT_TLV("Mixin1 Volume", DA7218_MIXIN_1_GAIN,
+ DA7218_MIXIN_1_AMP_GAIN_SHIFT,
+ DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_mixin_gain_put,
+ da7218_mixin_gain_tlv),
+ SOC_SINGLE("Mixin1 Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("Mixin1 Gain Ramp Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("Mixin1 ZC Gain Switch", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_EXT_TLV("Mixin2 Volume", DA7218_MIXIN_2_GAIN,
+ DA7218_MIXIN_2_AMP_GAIN_SHIFT,
+ DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_mixin_gain_put,
+ da7218_mixin_gain_tlv),
+ SOC_SINGLE("Mixin2 Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("Mixin2 Gain Ramp Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("Mixin2 ZC Gain Switch", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* ADCs */
+ SOC_SINGLE("ADC1 AAF Switch", DA7218_ADC_1_CTRL,
+ DA7218_ADC_1_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ADC2 AAF Switch", DA7218_ADC_2_CTRL,
+ DA7218_ADC_2_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ADC LP Mode Switch", DA7218_ADC_MODE,
+ DA7218_ADC_LP_MODE_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* Input Filters */
+ SOC_SINGLE_TLV("In Filter1L Volume", DA7218_IN_1L_GAIN,
+ DA7218_IN_1L_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter1L Switch", DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter1L Gain Ramp Switch", DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter1R Volume", DA7218_IN_1R_GAIN,
+ DA7218_IN_1R_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter1R Switch", DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter1R Gain Ramp Switch",
+ DA7218_IN_1R_FILTER_CTRL, DA7218_IN_1R_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter2L Volume", DA7218_IN_2L_GAIN,
+ DA7218_IN_2L_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter2L Switch", DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter2L Gain Ramp Switch", DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("In Filter2R Volume", DA7218_IN_2R_GAIN,
+ DA7218_IN_2R_DIGITAL_GAIN_SHIFT,
+ DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_in_dig_gain_tlv),
+ SOC_SINGLE("In Filter2R Switch", DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_SINGLE("In Filter2R Gain Ramp Switch",
+ DA7218_IN_2R_FILTER_CTRL, DA7218_IN_2R_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* AGS */
+ SOC_SINGLE_TLV("AGS Trigger", DA7218_AGS_TRIGGER,
+ DA7218_AGS_TRIGGER_SHIFT, DA7218_AGS_TRIGGER_MAX,
+ DA7218_INVERT, da7218_ags_trigger_tlv),
+ SOC_SINGLE_TLV("AGS Max Attenuation", DA7218_AGS_ATT_MAX,
+ DA7218_AGS_ATT_MAX_SHIFT, DA7218_AGS_ATT_MAX_MAX,
+ DA7218_NO_INVERT, da7218_ags_att_max_tlv),
+ SOC_SINGLE("AGS Anticlip Switch", DA7218_AGS_ANTICLIP_CTRL,
+ DA7218_AGS_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("AGS Channel1 Switch", DA7218_AGS_ENABLE,
+ DA7218_AGS_ENABLE_CHAN1_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("AGS Channel2 Switch", DA7218_AGS_ENABLE,
+ DA7218_AGS_ENABLE_CHAN2_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* ALC */
+ SOC_ENUM("ALC Attack Rate", da7218_alc_attack_rate),
+ SOC_ENUM("ALC Release Rate", da7218_alc_release_rate),
+ SOC_ENUM("ALC Hold Time", da7218_alc_hold_time),
+ SOC_SINGLE_TLV("ALC Noise Threshold", DA7218_ALC_NOISE,
+ DA7218_ALC_NOISE_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Min Threshold", DA7218_ALC_TARGET_MIN,
+ DA7218_ALC_THRESHOLD_MIN_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold", DA7218_ALC_TARGET_MAX,
+ DA7218_ALC_THRESHOLD_MAX_SHIFT, DA7218_ALC_THRESHOLD_MAX,
+ DA7218_INVERT, da7218_alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation", DA7218_ALC_GAIN_LIMITS,
+ DA7218_ALC_ATTEN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain", DA7218_ALC_GAIN_LIMITS,
+ DA7218_ALC_GAIN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Min Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS,
+ DA7218_ALC_ANA_GAIN_MIN_SHIFT,
+ DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_ana_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("ALC Max Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS,
+ DA7218_ALC_ANA_GAIN_MAX_SHIFT,
+ DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_alc_ana_gain_tlv),
+ SOC_ENUM("ALC Anticlip Step", da7218_alc_anticlip_step),
+ SOC_SINGLE("ALC Anticlip Switch", DA7218_ALC_ANTICLIP_CTRL,
+ DA7218_ALC_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE_EXT("ALC Channel1 Switch", DA7218_ALC_CTRL1,
+ DA7218_ALC_CHAN1_L_EN_SHIFT, DA7218_ALC_CHAN1_R_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_alc_sw_put),
+ SOC_DOUBLE_EXT("ALC Channel2 Switch", DA7218_ALC_CTRL1,
+ DA7218_ALC_CHAN2_L_EN_SHIFT, DA7218_ALC_CHAN2_R_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT,
+ snd_soc_get_volsw, da7218_alc_sw_put),
+
+ /* Envelope Tracking */
+ SOC_ENUM("Envelope Tracking Attack Rate", da7218_integ_attack_rate),
+ SOC_ENUM("Envelope Tracking Release Rate", da7218_integ_release_rate),
+
+ /* Input High-Pass Filters */
+ SOC_ENUM("In Filter1 HPF Mode", da7218_in1_hpf_mode),
+ SOC_ENUM("In Filter1 HPF Corner Audio", da7218_in1_audio_hpf_corner),
+ SOC_ENUM("In Filter1 HPF Corner Voice", da7218_in1_voice_hpf_corner),
+ SOC_ENUM("In Filter2 HPF Mode", da7218_in2_hpf_mode),
+ SOC_ENUM("In Filter2 HPF Corner Audio", da7218_in2_audio_hpf_corner),
+ SOC_ENUM("In Filter2 HPF Corner Voice", da7218_in2_voice_hpf_corner),
+
+ /* Mic Level Detect */
+ SOC_DOUBLE_EXT("Mic Level Detect Channel1 Switch", DA7218_LVL_DET_CTRL,
+ DA7218_LVL_DET_EN_CHAN1L_SHIFT,
+ DA7218_LVL_DET_EN_CHAN1R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get,
+ da7218_mic_lvl_det_sw_put),
+ SOC_DOUBLE_EXT("Mic Level Detect Channel2 Switch", DA7218_LVL_DET_CTRL,
+ DA7218_LVL_DET_EN_CHAN2L_SHIFT,
+ DA7218_LVL_DET_EN_CHAN2R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get,
+ da7218_mic_lvl_det_sw_put),
+ SOC_SINGLE("Mic Level Detect Level", DA7218_LVL_DET_LEVEL,
+ DA7218_LVL_DET_LEVEL_SHIFT, DA7218_LVL_DET_LEVEL_MAX,
+ DA7218_NO_INVERT),
+
+ /* Digital Mixer (Input) */
+ SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN,
+ DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN,
+ DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN,
+ DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN,
+ DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN,
+ DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN,
+ DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN,
+ DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN,
+ DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN,
+ DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN,
+ DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN,
+ DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN,
+ DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN,
+ DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN,
+ DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN,
+ DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN,
+ DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix ToneGen Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN,
+ DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN,
+ DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN,
+ DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN,
+ DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIL Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN,
+ DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN,
+ DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN,
+ DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN,
+ DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIR Out1 DAIL Volume",
+ DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN,
+ DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out1 DAIR Volume",
+ DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN,
+ DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out2 DAIL Volume",
+ DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN,
+ DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out2 DAIR Volume",
+ DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN,
+ DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ /* Digital Mixer (Output) */
+ SOC_SINGLE_TLV("DMix In Filter1L Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN,
+ DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1L Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN,
+ DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter1R Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN,
+ DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter1R Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN,
+ DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2L Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN,
+ DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2L Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN,
+ DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In Filter2R Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN,
+ DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In Filter2R Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN,
+ DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix ToneGen Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN,
+ DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix ToneGen Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN,
+ DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIL Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN,
+ DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIL Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN,
+ DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ SOC_SINGLE_TLV("DMix In DAIR Out FilterL Volume",
+ DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN,
+ DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+ SOC_SINGLE_TLV("DMix In DAIR Out FilterR Volume",
+ DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN,
+ DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT,
+ DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_dmix_gain_tlv),
+
+ /* Sidetone Filter */
+ SND_SOC_BYTES_EXT("Sidetone BiQuad Coefficients",
+ DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE,
+ da7218_biquad_coeff_get, da7218_biquad_coeff_put),
+ SOC_SINGLE_TLV("Sidetone Volume", DA7218_SIDETONE_GAIN,
+ DA7218_SIDETONE_GAIN_SHIFT, DA7218_DMIX_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_dmix_gain_tlv),
+ SOC_SINGLE("Sidetone Switch", DA7218_SIDETONE_CTRL,
+ DA7218_SIDETONE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Tone Generator */
+ SOC_ENUM("ToneGen DTMF Key", da7218_tonegen_dtmf_key),
+ SOC_SINGLE("ToneGen DTMF Switch", DA7218_TONE_GEN_CFG1,
+ DA7218_DTMF_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_ENUM("ToneGen Sinewave Gen Type", da7218_tonegen_swg_sel),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7218_TONE_GEN_FREQ1_L,
+ DA7218_FREQ1_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT,
+ da7218_tonegen_freq_get, da7218_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7218_TONE_GEN_FREQ2_L,
+ DA7218_FREQ2_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT,
+ da7218_tonegen_freq_get, da7218_tonegen_freq_put),
+ SOC_SINGLE("ToneGen On Time", DA7218_TONE_GEN_ON_PER,
+ DA7218_BEEP_ON_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("ToneGen Off Time", DA7218_TONE_GEN_OFF_PER,
+ DA7218_BEEP_OFF_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX,
+ DA7218_NO_INVERT),
+
+ /* Gain ramping */
+ SOC_ENUM("Gain Ramp Rate", da7218_gain_ramp_rate),
+
+ /* DGS */
+ SOC_SINGLE_TLV("DGS Trigger", DA7218_DGS_TRIGGER,
+ DA7218_DGS_TRIGGER_LVL_SHIFT, DA7218_DGS_TRIGGER_MAX,
+ DA7218_INVERT, da7218_dgs_trigger_tlv),
+ SOC_ENUM("DGS Rise Coefficient", da7218_dgs_rise_coeff),
+ SOC_ENUM("DGS Fall Coefficient", da7218_dgs_fall_coeff),
+ SOC_SINGLE("DGS Sync Delay", DA7218_DGS_SYNC_DELAY,
+ DA7218_DGS_SYNC_DELAY_SHIFT, DA7218_DGS_SYNC_DELAY_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Fast SR Sync Delay", DA7218_DGS_SYNC_DELAY2,
+ DA7218_DGS_SYNC_DELAY2_SHIFT, DA7218_DGS_SYNC_DELAY_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Voice Filter Sync Delay", DA7218_DGS_SYNC_DELAY3,
+ DA7218_DGS_SYNC_DELAY3_SHIFT, DA7218_DGS_SYNC_DELAY3_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE_TLV("DGS Anticlip Level", DA7218_DGS_LEVELS,
+ DA7218_DGS_ANTICLIP_LVL_SHIFT,
+ DA7218_DGS_ANTICLIP_LVL_MAX, DA7218_INVERT,
+ da7218_dgs_anticlip_tlv),
+ SOC_SINGLE_TLV("DGS Signal Level", DA7218_DGS_LEVELS,
+ DA7218_DGS_SIGNAL_LVL_SHIFT, DA7218_DGS_SIGNAL_LVL_MAX,
+ DA7218_INVERT, da7218_dgs_signal_tlv),
+ SOC_SINGLE("DGS Gain Subrange Switch", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_SUBR_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Gain Ramp Switch", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_SINGLE("DGS Gain Steps", DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_STEPS_SHIFT, DA7218_DGS_STEPS_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE("DGS Switch", DA7218_DGS_ENABLE, DA7218_DGS_ENABLE_L_SHIFT,
+ DA7218_DGS_ENABLE_R_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* Output High-Pass Filter */
+ SOC_ENUM("Out Filter HPF Mode", da7218_out1_hpf_mode),
+ SOC_ENUM("Out Filter HPF Corner Audio", da7218_out1_audio_hpf_corner),
+ SOC_ENUM("Out Filter HPF Corner Voice", da7218_out1_voice_hpf_corner),
+
+ /* 5-Band Equaliser */
+ SOC_SINGLE_TLV("Out EQ Band1 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND1_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band2 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND2_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band3 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND3_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band4 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND4_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE_TLV("Out EQ Band5 Volume", DA7218_OUT_1_EQ_5_FILTER_CTRL,
+ DA7218_OUT_1_EQ_BAND5_SHIFT, DA7218_OUT_EQ_BAND_MAX,
+ DA7218_NO_INVERT, da7218_out_eq_band_tlv),
+ SOC_SINGLE("Out EQ Switch", DA7218_OUT_1_EQ_5_FILTER_CTRL,
+ DA7218_OUT_1_EQ_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+
+ /* BiQuad Filters */
+ SND_SOC_BYTES_EXT("BiQuad Coefficients",
+ DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE,
+ da7218_biquad_coeff_get, da7218_biquad_coeff_put),
+ SOC_SINGLE("BiQuad Filter Switch", DA7218_OUT_1_BIQ_5STAGE_CTRL,
+ DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+
+ /* Output Filters */
+ SOC_DOUBLE_R_RANGE_TLV("Out Filter Volume", DA7218_OUT_1L_GAIN,
+ DA7218_OUT_1R_GAIN,
+ DA7218_OUT_1L_DIGITAL_GAIN_SHIFT,
+ DA7218_OUT_DIGITAL_GAIN_MIN,
+ DA7218_OUT_DIGITAL_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_out_dig_gain_tlv),
+ SOC_DOUBLE_R("Out Filter Switch", DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_MUTE_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_INVERT),
+ SOC_DOUBLE_R("Out Filter Gain Subrange Switch",
+ DA7218_OUT_1L_FILTER_CTRL, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1L_SUBRANGE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_NO_INVERT),
+ SOC_DOUBLE_R("Out Filter Gain Ramp Switch", DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* Mixer Output */
+ SOC_DOUBLE_R_RANGE_TLV("Mixout Volume", DA7218_MIXOUT_L_GAIN,
+ DA7218_MIXOUT_R_GAIN,
+ DA7218_MIXOUT_L_AMP_GAIN_SHIFT,
+ DA7218_MIXOUT_AMP_GAIN_MIN,
+ DA7218_MIXOUT_AMP_GAIN_MAX, DA7218_NO_INVERT,
+ da7218_mixout_gain_tlv),
+
+ /* DAC Noise Gate */
+ SOC_ENUM("DAC NG Setup Time", da7218_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da7218_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da7218_dac_ng_rampdown_rate),
+ SOC_SINGLE_TLV("DAC NG Off Threshold", DA7218_DAC_NG_OFF_THRESH,
+ DA7218_DAC_NG_OFF_THRESHOLD_SHIFT,
+ DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT,
+ da7218_dac_ng_threshold_tlv),
+ SOC_SINGLE_TLV("DAC NG On Threshold", DA7218_DAC_NG_ON_THRESH,
+ DA7218_DAC_NG_ON_THRESHOLD_SHIFT,
+ DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT,
+ da7218_dac_ng_threshold_tlv),
+ SOC_SINGLE("DAC NG Switch", DA7218_DAC_NG_CTRL, DA7218_DAC_NG_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+
+ /* CP */
+ SOC_ENUM("Charge Pump Track Mode", da7218_cp_mchange),
+ SOC_ENUM("Charge Pump Frequency", da7218_cp_fcontrol),
+ SOC_ENUM("Charge Pump Decay Rate", da7218_cp_tau_delay),
+ SOC_SINGLE("Charge Pump Threshold", DA7218_CP_VOL_THRESHOLD1,
+ DA7218_CP_THRESH_VDD2_SHIFT, DA7218_CP_THRESH_VDD2_MAX,
+ DA7218_NO_INVERT),
+
+ /* Headphones */
+ SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", DA7218_HP_L_GAIN,
+ DA7218_HP_R_GAIN, DA7218_HP_L_AMP_GAIN_SHIFT,
+ DA7218_HP_AMP_GAIN_MIN, DA7218_HP_AMP_GAIN_MAX,
+ DA7218_NO_INVERT, da7218_hp_gain_tlv),
+ SOC_DOUBLE_R("Headphone Switch", DA7218_HP_L_CTRL, DA7218_HP_R_CTRL,
+ DA7218_HP_L_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX,
+ DA7218_INVERT),
+ SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7218_HP_L_CTRL,
+ DA7218_HP_R_CTRL, DA7218_HP_L_AMP_RAMP_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+ SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7218_HP_L_CTRL,
+ DA7218_HP_R_CTRL, DA7218_HP_L_AMP_ZC_EN_SHIFT,
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7218_mic_sel_text[] = { "Analog", "Digital" };
+
+static const struct soc_enum da7218_mic1_sel =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text),
+ da7218_mic_sel_text);
+
+static const struct snd_kcontrol_new da7218_mic1_sel_mux =
+ SOC_DAPM_ENUM("Mic1 Mux", da7218_mic1_sel);
+
+static const struct soc_enum da7218_mic2_sel =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text),
+ da7218_mic_sel_text);
+
+static const struct snd_kcontrol_new da7218_mic2_sel_mux =
+ SOC_DAPM_ENUM("Mic2 Mux", da7218_mic2_sel);
+
+static const char * const da7218_sidetone_in_sel_txt[] = {
+ "In Filter1L", "In Filter1R", "In Filter2L", "In Filter2R"
+};
+
+static const struct soc_enum da7218_sidetone_in_sel =
+ SOC_ENUM_SINGLE(DA7218_SIDETONE_IN_SELECT,
+ DA7218_SIDETONE_IN_SELECT_SHIFT,
+ DA7218_SIDETONE_IN_SELECT_MAX,
+ da7218_sidetone_in_sel_txt);
+
+static const struct snd_kcontrol_new da7218_sidetone_in_sel_mux =
+ SOC_DAPM_ENUM("Sidetone Mux", da7218_sidetone_in_sel);
+
+static const char * const da7218_out_filt_biq_sel_txt[] = {
+ "Bypass", "Enabled"
+};
+
+static const struct soc_enum da7218_out_filtl_biq_sel =
+ SOC_ENUM_SINGLE(DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT,
+ DA7218_OUT_BIQ_5STAGE_SEL_MAX,
+ da7218_out_filt_biq_sel_txt);
+
+static const struct snd_kcontrol_new da7218_out_filtl_biq_sel_mux =
+ SOC_DAPM_ENUM("Out FilterL BiQuad Mux", da7218_out_filtl_biq_sel);
+
+static const struct soc_enum da7218_out_filtr_biq_sel =
+ SOC_ENUM_SINGLE(DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT,
+ DA7218_OUT_BIQ_5STAGE_SEL_MAX,
+ da7218_out_filt_biq_sel_txt);
+
+static const struct snd_kcontrol_new da7218_out_filtr_biq_sel_mux =
+ SOC_DAPM_ENUM("Out FilterR BiQuad Mux", da7218_out_filtr_biq_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+#define DA7218_DMIX_CTRLS(reg) \
+ SOC_DAPM_SINGLE("In Filter1L Switch", reg, \
+ DA7218_DMIX_SRC_INFILT1L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter1R Switch", reg, \
+ DA7218_DMIX_SRC_INFILT1R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter2L Switch", reg, \
+ DA7218_DMIX_SRC_INFILT2L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("In Filter2R Switch", reg, \
+ DA7218_DMIX_SRC_INFILT2R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("ToneGen Switch", reg, \
+ DA7218_DMIX_SRC_TONEGEN, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("DAIL Switch", reg, DA7218_DMIX_SRC_DAIL, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("DAIR Switch", reg, DA7218_DMIX_SRC_DAIR, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT)
+
+static const struct snd_kcontrol_new da7218_out_dai1l_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1L),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai1r_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1R),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai2l_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2L),
+};
+
+static const struct snd_kcontrol_new da7218_out_dai2r_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2R),
+};
+
+static const struct snd_kcontrol_new da7218_out_filtl_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7218_out_filtr_mix_controls[] = {
+ DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1R),
+};
+
+#define DA7218_DMIX_ST_CTRLS(reg) \
+ SOC_DAPM_SINGLE("Out FilterL Switch", reg, \
+ DA7218_DMIX_ST_SRC_OUTFILT1L, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("Out FilterR Switch", reg, \
+ DA7218_DMIX_ST_SRC_OUTFILT1R, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \
+ SOC_DAPM_SINGLE("Sidetone Switch", reg, \
+ DA7218_DMIX_ST_SRC_SIDETONE, \
+ DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) \
+
+static const struct snd_kcontrol_new da7218_st_out_filtl_mix_controls[] = {
+ DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = {
+ DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+/*
+ * We keep track of which input filters are enabled. This is used in the logic
+ * for controlling the mic level detect feature.
+ */
+static int da7218_in_filter_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ u8 mask;
+
+ switch (w->reg) {
+ case DA7218_IN_1L_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN1L_SHIFT);
+ break;
+ case DA7218_IN_1R_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN1R_SHIFT);
+ break;
+ case DA7218_IN_2L_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN2L_SHIFT);
+ break;
+ case DA7218_IN_2R_FILTER_CTRL:
+ mask = (1 << DA7218_LVL_DET_EN_CHAN2R_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ da7218->in_filt_en |= mask;
+ /*
+ * If we're enabling path for mic level detect, wait for path
+ * to settle before enabling feature to avoid incorrect and
+ * unwanted detect events.
+ */
+ if (mask & da7218->mic_lvl_det_en)
+ msleep(DA7218_MIC_LVL_DET_DELAY);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ da7218->in_filt_en &= ~mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable configured level detection paths */
+ snd_soc_write(codec, DA7218_LVL_DET_CTRL,
+ (da7218->in_filt_en & da7218->mic_lvl_det_en));
+
+ return 0;
+}
+
+static int da7218_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ u8 pll_ctrl, pll_status, refosc_cal;
+ int i;
+ bool success;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (da7218->master)
+ /* Enable DAI clks for master mode */
+ snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_CLK_EN_MASK,
+ DA7218_DAI_CLK_EN_MASK);
+
+ /* Tune reference oscillator */
+ snd_soc_write(codec, DA7218_PLL_REFOSC_CAL,
+ DA7218_PLL_REFOSC_CAL_START_MASK);
+ snd_soc_write(codec, DA7218_PLL_REFOSC_CAL,
+ DA7218_PLL_REFOSC_CAL_START_MASK |
+ DA7218_PLL_REFOSC_CAL_EN_MASK);
+
+ /* Check tuning complete */
+ i = 0;
+ success = false;
+ do {
+ refosc_cal = snd_soc_read(codec, DA7218_PLL_REFOSC_CAL);
+ if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) {
+ success = true;
+ } else {
+ ++i;
+ usleep_range(DA7218_REF_OSC_CHECK_DELAY_MIN,
+ DA7218_REF_OSC_CHECK_DELAY_MAX);
+ }
+ } while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success));
+
+ if (!success)
+ dev_warn(codec->dev,
+ "Reference oscillator failed calibration\n");
+
+ /* PC synchronised to DAI */
+ snd_soc_write(codec, DA7218_PC_COUNT,
+ DA7218_PC_RESYNC_AUTO_MASK);
+
+ /* If SRM not enabled, we don't need to check status */
+ pll_ctrl = snd_soc_read(codec, DA7218_PLL_CTRL);
+ if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM)
+ return 0;
+
+ /* Check SRM has locked */
+ i = 0;
+ success = false;
+ do {
+ pll_status = snd_soc_read(codec, DA7218_PLL_STATUS);
+ if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) {
+ success = true;
+ } else {
+ ++i;
+ msleep(DA7218_SRM_CHECK_DELAY);
+ }
+ } while ((i < DA7218_SRM_CHECK_TRIES) & (!success));
+
+ if (!success)
+ dev_warn(codec->dev, "SRM failed to lock\n");
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMD:
+ /* PC free-running */
+ snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+
+ if (da7218->master)
+ /* Disable DAI clks for master mode */
+ snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_CLK_EN_MASK, 0);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da7218_cp_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * If this is DA7217 and we're using single supply for differential
+ * output, we really don't want to touch the charge pump.
+ */
+ if (da7218->hp_single_supply)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ DA7218_CP_EN_MASK);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK,
+ 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da7218_hp_pga_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable headphone output */
+ snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK,
+ DA7218_HP_AMP_OE_MASK);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Headphone output high impedance */
+ snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK, 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {
+ /* Input Supplies */
+ SND_SOC_DAPM_SUPPLY("Mic Bias1", DA7218_MICBIAS_EN,
+ DA7218_MICBIAS_1_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias2", DA7218_MICBIAS_EN,
+ DA7218_MICBIAS_2_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic1 Left", DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1L_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic1 Right", DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1R_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic2 Left", DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2L_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMic2 Right", DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2R_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1L"),
+ SND_SOC_DAPM_INPUT("DMIC1R"),
+ SND_SOC_DAPM_INPUT("DMIC2L"),
+ SND_SOC_DAPM_INPUT("DMIC2R"),
+
+ /* Input Mixer Supplies */
+ SND_SOC_DAPM_SUPPLY("Mixin1 Supply", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_MIX_SEL_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mixin2 Supply", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_MIX_SEL_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic1 PGA", DA7218_MIC_1_CTRL,
+ DA7218_MIC_1_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mic2 PGA", DA7218_MIC_2_CTRL,
+ DA7218_MIC_2_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin1 PGA", DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixin2 PGA", DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+
+ /* Mic/DMic Muxes */
+ SND_SOC_DAPM_MUX("Mic1 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic1_sel_mux),
+ SND_SOC_DAPM_MUX("Mic2 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic2_sel_mux),
+
+ /* Input Filters */
+ SND_SOC_DAPM_ADC_E("In Filter1L", NULL, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter1R", NULL, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter2L", NULL, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("In Filter2R", NULL, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_FILTER_EN_SHIFT, DA7218_NO_INVERT,
+ da7218_in_filter_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Tone Generator */
+ SND_SOC_DAPM_SIGGEN("TONE"),
+ SND_SOC_DAPM_PGA("Tone Generator", DA7218_TONE_GEN_CFG1,
+ DA7218_START_STOPN_SHIFT, DA7218_NO_INVERT, NULL, 0),
+
+ /* Sidetone Input */
+ SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_sidetone_in_sel_mux),
+ SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7218_SIDETONE_CTRL,
+ DA7218_SIDETONE_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("Mixer DAI1L", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai1l_mix_controls,
+ ARRAY_SIZE(da7218_out_dai1l_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI1R", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai1r_mix_controls,
+ ARRAY_SIZE(da7218_out_dai1r_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI2L", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai2l_mix_controls,
+ ARRAY_SIZE(da7218_out_dai2l_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer DAI2R", SND_SOC_NOPM, 0, 0,
+ da7218_out_dai2r_mix_controls,
+ ARRAY_SIZE(da7218_out_dai2r_mix_controls)),
+
+ /* DAI Supply */
+ SND_SOC_DAPM_SUPPLY("DAI", DA7218_DAI_CTRL, DA7218_DAI_EN_SHIFT,
+ DA7218_NO_INVERT, da7218_dai_event,
+ SND_SOC_DAPM_POST_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 Mixers */
+ SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7218_out_filtl_mix_controls,
+ ARRAY_SIZE(da7218_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7218_out_filtr_mix_controls,
+ ARRAY_SIZE(da7218_out_filtr_mix_controls)),
+
+ /* BiQuad Filters */
+ SND_SOC_DAPM_MUX("Out FilterL BiQuad Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_out_filtl_biq_sel_mux),
+ SND_SOC_DAPM_MUX("Out FilterR BiQuad Mux", SND_SOC_NOPM, 0, 0,
+ &da7218_out_filtr_biq_sel_mux),
+ SND_SOC_DAPM_DAC("BiQuad Filter", NULL, DA7218_OUT_1_BIQ_5STAGE_CTRL,
+ DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT,
+ DA7218_NO_INVERT),
+
+ /* Sidetone Mixers */
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+ da7218_st_out_filtl_mix_controls,
+ ARRAY_SIZE(da7218_st_out_filtl_mix_controls)),
+ SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+ da7218_st_out_filtr_mix_controls,
+ ARRAY_SIZE(da7218_st_out_filtr_mix_controls)),
+
+ /* Output Filters */
+ SND_SOC_DAPM_DAC("Out FilterL", NULL, DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+ SND_SOC_DAPM_DAC("Out FilterR", NULL, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("Mixout Left PGA", DA7218_MIXOUT_L_CTRL,
+ DA7218_MIXOUT_L_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA("Mixout Right PGA", DA7218_MIXOUT_R_CTRL,
+ DA7218_MIXOUT_R_AMP_EN_SHIFT, DA7218_NO_INVERT,
+ NULL, 0),
+ SND_SOC_DAPM_PGA_E("Headphone Left PGA", DA7218_HP_L_CTRL,
+ DA7218_HP_L_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0,
+ da7218_hp_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Headphone Right PGA", DA7218_HP_R_CTRL,
+ DA7218_HP_R_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0,
+ da7218_hp_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output Supplies */
+ SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, da7218_cp_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7218_DMIX_ROUTES(name) \
+ {name, "In Filter1L Switch", "In Filter1L"}, \
+ {name, "In Filter1R Switch", "In Filter1R"}, \
+ {name, "In Filter2L Switch", "In Filter2L"}, \
+ {name, "In Filter2R Switch", "In Filter2R"}, \
+ {name, "ToneGen Switch", "Tone Generator"}, \
+ {name, "DAIL Switch", "DAIIN"}, \
+ {name, "DAIR Switch", "DAIIN"}
+
+#define DA7218_DMIX_ST_ROUTES(name) \
+ {name, "Out FilterL Switch", "Out FilterL BiQuad Mux"}, \
+ {name, "Out FilterR Switch", "Out FilterR BiQuad Mux"}, \
+ {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7218_audio_map[] = {
+ /* Input paths */
+ {"MIC1", NULL, "Mic Bias1"},
+ {"MIC2", NULL, "Mic Bias2"},
+ {"DMIC1L", NULL, "Mic Bias1"},
+ {"DMIC1L", NULL, "DMic1 Left"},
+ {"DMIC1R", NULL, "Mic Bias1"},
+ {"DMIC1R", NULL, "DMic1 Right"},
+ {"DMIC2L", NULL, "Mic Bias2"},
+ {"DMIC2L", NULL, "DMic2 Left"},
+ {"DMIC2R", NULL, "Mic Bias2"},
+ {"DMIC2R", NULL, "DMic2 Right"},
+
+ {"Mic1 PGA", NULL, "MIC1"},
+ {"Mic2 PGA", NULL, "MIC2"},
+
+ {"Mixin1 PGA", NULL, "Mixin1 Supply"},
+ {"Mixin2 PGA", NULL, "Mixin2 Supply"},
+
+ {"Mixin1 PGA", NULL, "Mic1 PGA"},
+ {"Mixin2 PGA", NULL, "Mic2 PGA"},
+
+ {"Mic1 Mux", "Analog", "Mixin1 PGA"},
+ {"Mic1 Mux", "Digital", "DMIC1L"},
+ {"Mic1 Mux", "Digital", "DMIC1R"},
+ {"Mic2 Mux", "Analog", "Mixin2 PGA"},
+ {"Mic2 Mux", "Digital", "DMIC2L"},
+ {"Mic2 Mux", "Digital", "DMIC2R"},
+
+ {"In Filter1L", NULL, "Mic1 Mux"},
+ {"In Filter1R", NULL, "Mic1 Mux"},
+ {"In Filter2L", NULL, "Mic2 Mux"},
+ {"In Filter2R", NULL, "Mic2 Mux"},
+
+ {"Tone Generator", NULL, "TONE"},
+
+ {"Sidetone Mux", "In Filter1L", "In Filter1L"},
+ {"Sidetone Mux", "In Filter1R", "In Filter1R"},
+ {"Sidetone Mux", "In Filter2L", "In Filter2L"},
+ {"Sidetone Mux", "In Filter2R", "In Filter2R"},
+ {"Sidetone Filter", NULL, "Sidetone Mux"},
+
+ DA7218_DMIX_ROUTES("Mixer DAI1L"),
+ DA7218_DMIX_ROUTES("Mixer DAI1R"),
+ DA7218_DMIX_ROUTES("Mixer DAI2L"),
+ DA7218_DMIX_ROUTES("Mixer DAI2R"),
+
+ {"DAIOUT", NULL, "Mixer DAI1L"},
+ {"DAIOUT", NULL, "Mixer DAI1R"},
+ {"DAIOUT", NULL, "Mixer DAI2L"},
+ {"DAIOUT", NULL, "Mixer DAI2R"},
+
+ {"DAIOUT", NULL, "DAI"},
+
+ /* Output paths */
+ {"DAIIN", NULL, "DAI"},
+
+ DA7218_DMIX_ROUTES("Mixer Out FilterL"),
+ DA7218_DMIX_ROUTES("Mixer Out FilterR"),
+
+ {"BiQuad Filter", NULL, "Mixer Out FilterL"},
+ {"BiQuad Filter", NULL, "Mixer Out FilterR"},
+
+ {"Out FilterL BiQuad Mux", "Bypass", "Mixer Out FilterL"},
+ {"Out FilterL BiQuad Mux", "Enabled", "BiQuad Filter"},
+ {"Out FilterR BiQuad Mux", "Bypass", "Mixer Out FilterR"},
+ {"Out FilterR BiQuad Mux", "Enabled", "BiQuad Filter"},
+
+ DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+ DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+ {"Out FilterL", NULL, "ST Mixer Out FilterL"},
+ {"Out FilterR", NULL, "ST Mixer Out FilterR"},
+
+ {"Mixout Left PGA", NULL, "Out FilterL"},
+ {"Mixout Right PGA", NULL, "Out FilterR"},
+
+ {"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 da7218_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ if (da7218->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 DA7218_CLKSRC_MCLK_SQR:
+ snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ DA7218_PLL_MCLK_SQR_EN_MASK,
+ DA7218_PLL_MCLK_SQR_EN_MASK);
+ break;
+ case DA7218_CLKSRC_MCLK:
+ snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ DA7218_PLL_MCLK_SQR_EN_MASK, 0);
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ if (da7218->mclk) {
+ freq = clk_round_rate(da7218->mclk, freq);
+ ret = clk_set_rate(da7218->mclk, freq);
+ if (ret) {
+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+ freq);
+ return ret;
+ }
+ }
+
+ da7218->mclk_rate = freq;
+
+ return 0;
+}
+
+static int da7218_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 da7218_priv *da7218 = 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 (da7218->mclk_rate == 32768) {
+ indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
+ indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
+ } else if (da7218->mclk_rate < 2000000) {
+ dev_err(codec->dev, "PLL input clock %d below valid range\n",
+ da7218->mclk_rate);
+ return -EINVAL;
+ } else if (da7218->mclk_rate <= 5000000) {
+ indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
+ indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 10000000) {
+ indiv_bits = DA7218_PLL_INDIV_5_10_MHZ;
+ indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 20000000) {
+ indiv_bits = DA7218_PLL_INDIV_10_20_MHZ;
+ indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 40000000) {
+ indiv_bits = DA7218_PLL_INDIV_20_40_MHZ;
+ indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL;
+ } else if (da7218->mclk_rate <= 54000000) {
+ indiv_bits = DA7218_PLL_INDIV_40_54_MHZ;
+ indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL;
+ } else {
+ dev_err(codec->dev, "PLL input clock %d above valid range\n",
+ da7218->mclk_rate);
+ return -EINVAL;
+ }
+ freq_ref = (da7218->mclk_rate / indiv);
+ pll_ctrl = indiv_bits;
+
+ /* Configure PLL */
+ switch (source) {
+ case DA7218_SYSCLK_MCLK:
+ pll_ctrl |= DA7218_PLL_MODE_BYPASS;
+ snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ DA7218_PLL_INDIV_MASK |
+ DA7218_PLL_MODE_MASK, pll_ctrl);
+ return 0;
+ case DA7218_SYSCLK_PLL:
+ pll_ctrl |= DA7218_PLL_MODE_NORMAL;
+ break;
+ case DA7218_SYSCLK_PLL_SRM:
+ pll_ctrl |= DA7218_PLL_MODE_SRM;
+ break;
+ case DA7218_SYSCLK_PLL_32KHZ:
+ pll_ctrl |= DA7218_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 >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK;
+ pll_frac_bot = (frac_div) & DA7218_BYTE_MASK;
+
+ /* Write PLL config & dividers */
+ snd_soc_write(codec, DA7218_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_write(codec, DA7218_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_write(codec, DA7218_PLL_INTEGER, pll_integer);
+ snd_soc_update_bits(codec, DA7218_PLL_CTRL,
+ DA7218_PLL_MODE_MASK | DA7218_PLL_INDIV_MASK,
+ pll_ctrl);
+
+ return 0;
+}
+
+static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da7218_priv *da7218 = 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:
+ da7218->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ da7218->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 |= DA7218_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV | DA7218_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_ctrl |= DA7218_DAI_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_ctrl |= DA7218_DAI_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dai_ctrl |= DA7218_DAI_FORMAT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dai_ctrl |= DA7218_DAI_FORMAT_DSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default 64 BCLKs per WCLK is supported */
+ dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64;
+
+ snd_soc_write(codec, DA7218_DAI_CLK_MODE, dai_clk_mode);
+ snd_soc_update_bits(codec, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK,
+ dai_ctrl);
+
+ return 0;
+}
+
+static int da7218_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;
+ u8 dai_bclks_per_wclk;
+ u32 frame_size;
+
+ /* No channels enabled so disable TDM, revert to 64-bit frames */
+ if (!tx_mask) {
+ snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_TDM_CH_EN_MASK |
+ DA7218_DAI_TDM_MODE_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_BCLKS_PER_WCLK_MASK,
+ DA7218_DAI_BCLKS_PER_WCLK_64);
+ return 0;
+ }
+
+ /* Check we have valid slots */
+ if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) {
+ dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+ DA7218_DAI_TDM_MAX_SLOTS);
+ return -EINVAL;
+ }
+
+ /* Check we have a valid offset given (first 2 bytes of rx_mask) */
+ if (rx_mask >> DA7218_2BYTE_SHIFT) {
+ dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+ DA7218_2BYTE_MASK);
+ 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 = DA7218_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid frame size\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE,
+ DA7218_DAI_BCLKS_PER_WCLK_MASK,
+ dai_bclks_per_wclk);
+ snd_soc_write(codec, DA7218_DAI_OFFSET_LOWER,
+ (rx_mask & DA7218_BYTE_MASK));
+ snd_soc_write(codec, DA7218_DAI_OFFSET_UPPER,
+ ((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK));
+ snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL,
+ DA7218_DAI_TDM_CH_EN_MASK |
+ DA7218_DAI_TDM_MODE_EN_MASK,
+ (tx_mask << DA7218_DAI_TDM_CH_EN_SHIFT) |
+ DA7218_DAI_TDM_MODE_EN_MASK);
+
+ return 0;
+}
+
+static int da7218_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 |= DA7218_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7218_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) {
+ dev_err(codec->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7218_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7218_DAI_CH_NUM_SHIFT;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7218_SR_8000;
+ break;
+ case 11025:
+ fs = DA7218_SR_11025;
+ break;
+ case 12000:
+ fs = DA7218_SR_12000;
+ break;
+ case 16000:
+ fs = DA7218_SR_16000;
+ break;
+ case 22050:
+ fs = DA7218_SR_22050;
+ break;
+ case 24000:
+ fs = DA7218_SR_24000;
+ break;
+ case 32000:
+ fs = DA7218_SR_32000;
+ break;
+ case 44100:
+ fs = DA7218_SR_44100;
+ break;
+ case 48000:
+ fs = DA7218_SR_48000;
+ break;
+ case 88200:
+ fs = DA7218_SR_88200;
+ break;
+ case 96000:
+ fs = DA7218_SR_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, DA7218_DAI_CTRL,
+ DA7218_DAI_WORD_LENGTH_MASK | DA7218_DAI_CH_NUM_MASK,
+ dai_ctrl);
+ /* SRs tied for ADCs and DACs. */
+ snd_soc_write(codec, DA7218_SR,
+ (fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT));
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops da7218_dai_ops = {
+ .hw_params = da7218_hw_params,
+ .set_sysclk = da7218_set_dai_sysclk,
+ .set_pll = da7218_set_dai_pll,
+ .set_fmt = da7218_set_dai_fmt,
+ .set_tdm_slot = da7218_set_dai_tdm_slot,
+};
+
+#define DA7218_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 da7218_dai = {
+ .name = "da7218-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4, /* Only 2 channels of data */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7218_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA7218_FORMATS,
+ },
+ .ops = &da7218_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_channels = 1,
+ .symmetric_samplebits = 1,
+};
+
+
+/*
+ * HP Detect
+ */
+
+int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+
+ if (da7218->dev_id == DA7217_DEV_ID)
+ return -EINVAL;
+
+ da7218->jack = jack;
+ snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ DA7218_HPLDET_JACK_EN_MASK,
+ jack ? DA7218_HPLDET_JACK_EN_MASK : 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(da7218_hpldet);
+
+static void da7218_micldet_irq(struct snd_soc_codec *codec)
+{
+ char *envp[] = {
+ "EVENT=MIC_LEVEL_DETECT",
+ NULL,
+ };
+
+ kobject_uevent_env(&codec->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+static void da7218_hpldet_irq(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ u8 jack_status;
+ int report;
+
+ jack_status = snd_soc_read(codec, DA7218_EVENT_STATUS);
+
+ if (jack_status & DA7218_HPLDET_JACK_STS_MASK)
+ report = SND_JACK_HEADPHONE;
+ else
+ report = 0;
+
+ snd_soc_jack_report(da7218->jack, report, SND_JACK_HEADPHONE);
+}
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7218_irq_thread(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ u8 status;
+
+ /* Read IRQ status reg */
+ status = snd_soc_read(codec, DA7218_EVENT);
+ if (!status)
+ return IRQ_NONE;
+
+ /* Mic level detect */
+ if (status & DA7218_LVL_DET_EVENT_MASK)
+ da7218_micldet_irq(codec);
+
+ /* HP detect */
+ if (status & DA7218_HPLDET_JACK_EVENT_MASK)
+ da7218_hpldet_irq(codec);
+
+ /* Clear interrupts */
+ snd_soc_write(codec, DA7218_EVENT, status);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7218_of_match[] = {
+ { .compatible = "dlg,da7217", .data = (void *) DA7217_DEV_ID },
+ { .compatible = "dlg,da7218", .data = (void *) DA7218_DEV_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7218_of_match);
+
+static inline int da7218_of_get_id(struct device *dev)
+{
+ const struct of_device_id *id = of_match_device(da7218_of_match, dev);
+
+ if (id)
+ return (uintptr_t)id->data;
+ else
+ return -EINVAL;
+}
+
+static enum da7218_micbias_voltage
+ da7218_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1200:
+ return DA7218_MICBIAS_1_2V;
+ case 1600:
+ return DA7218_MICBIAS_1_6V;
+ case 1800:
+ return DA7218_MICBIAS_1_8V;
+ case 2000:
+ return DA7218_MICBIAS_2_0V;
+ case 2200:
+ return DA7218_MICBIAS_2_2V;
+ case 2400:
+ return DA7218_MICBIAS_2_4V;
+ case 2600:
+ return DA7218_MICBIAS_2_6V;
+ case 2800:
+ return DA7218_MICBIAS_2_8V;
+ case 3000:
+ return DA7218_MICBIAS_3_0V;
+ default:
+ dev_warn(codec->dev, "Invalid micbias level");
+ return DA7218_MICBIAS_1_6V;
+ }
+}
+
+static enum da7218_mic_amp_in_sel
+ da7218_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "diff")) {
+ return DA7218_MIC_AMP_IN_SEL_DIFF;
+ } else if (!strcmp(str, "se_p")) {
+ return DA7218_MIC_AMP_IN_SEL_SE_P;
+ } else if (!strcmp(str, "se_n")) {
+ return DA7218_MIC_AMP_IN_SEL_SE_N;
+ } else {
+ dev_warn(codec->dev, "Invalid mic input type selection");
+ return DA7218_MIC_AMP_IN_SEL_DIFF;
+ }
+}
+
+static enum da7218_dmic_data_sel
+ da7218_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "lrise_rfall")) {
+ return DA7218_DMIC_DATA_LRISE_RFALL;
+ } else if (!strcmp(str, "lfall_rrise")) {
+ return DA7218_DMIC_DATA_LFALL_RRISE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC data type selection");
+ return DA7218_DMIC_DATA_LRISE_RFALL;
+ }
+}
+
+static enum da7218_dmic_samplephase
+ da7218_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+{
+ if (!strcmp(str, "on_clkedge")) {
+ return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+ } else if (!strcmp(str, "between_clkedge")) {
+ return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+ } else {
+ dev_warn(codec->dev, "Invalid DMIC sample phase");
+ return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+ }
+}
+
+static enum da7218_dmic_clk_rate
+ da7218_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 1500000:
+ return DA7218_DMIC_CLK_1_5MHZ;
+ case 3000000:
+ return DA7218_DMIC_CLK_3_0MHZ;
+ default:
+ dev_warn(codec->dev, "Invalid DMIC clock rate");
+ return DA7218_DMIC_CLK_3_0MHZ;
+ }
+}
+
+static enum da7218_hpldet_jack_rate
+ da7218_of_jack_rate(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 5:
+ return DA7218_HPLDET_JACK_RATE_5US;
+ case 10:
+ return DA7218_HPLDET_JACK_RATE_10US;
+ case 20:
+ return DA7218_HPLDET_JACK_RATE_20US;
+ case 40:
+ return DA7218_HPLDET_JACK_RATE_40US;
+ case 80:
+ return DA7218_HPLDET_JACK_RATE_80US;
+ case 160:
+ return DA7218_HPLDET_JACK_RATE_160US;
+ case 320:
+ return DA7218_HPLDET_JACK_RATE_320US;
+ case 640:
+ return DA7218_HPLDET_JACK_RATE_640US;
+ default:
+ dev_warn(codec->dev, "Invalid jack detect rate");
+ return DA7218_HPLDET_JACK_RATE_40US;
+ }
+}
+
+static enum da7218_hpldet_jack_debounce
+ da7218_of_jack_debounce(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 0:
+ return DA7218_HPLDET_JACK_DEBOUNCE_OFF;
+ case 2:
+ return DA7218_HPLDET_JACK_DEBOUNCE_2;
+ case 3:
+ return DA7218_HPLDET_JACK_DEBOUNCE_3;
+ case 4:
+ return DA7218_HPLDET_JACK_DEBOUNCE_4;
+ default:
+ dev_warn(codec->dev, "Invalid jack debounce");
+ return DA7218_HPLDET_JACK_DEBOUNCE_2;
+ }
+}
+
+static enum da7218_hpldet_jack_thr
+ da7218_of_jack_thr(struct snd_soc_codec *codec, u32 val)
+{
+ switch (val) {
+ case 84:
+ return DA7218_HPLDET_JACK_THR_84PCT;
+ case 88:
+ return DA7218_HPLDET_JACK_THR_88PCT;
+ case 92:
+ return DA7218_HPLDET_JACK_THR_92PCT;
+ case 96:
+ return DA7218_HPLDET_JACK_THR_96PCT;
+ default:
+ dev_warn(codec->dev, "Invalid jack threshold level");
+ return DA7218_HPLDET_JACK_THR_84PCT;
+ }
+}
+
+static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct device_node *np = codec->dev->of_node;
+ struct device_node *hpldet_np;
+ struct da7218_pdata *pdata;
+ struct da7218_hpldet_pdata *hpldet_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-millivolt", &of_val32) >= 0)
+ pdata->micbias1_lvl = da7218_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias1_lvl = DA7218_MICBIAS_1_6V;
+
+ if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0)
+ pdata->micbias2_lvl = da7218_of_micbias_lvl(codec, of_val32);
+ else
+ pdata->micbias2_lvl = DA7218_MICBIAS_1_6V;
+
+ if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str))
+ pdata->mic1_amp_in_sel =
+ da7218_of_mic_amp_in_sel(codec, of_str);
+ else
+ pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
+
+ if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str))
+ pdata->mic2_amp_in_sel =
+ da7218_of_mic_amp_in_sel(codec, of_str);
+ else
+ pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
+
+ if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str))
+ pdata->dmic1_data_sel = da7218_of_dmic_data_sel(codec, of_str);
+ else
+ pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str))
+ pdata->dmic1_samplephase =
+ da7218_of_dmic_samplephase(codec, of_str);
+ else
+ pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0)
+ pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(codec, of_val32);
+ else
+ pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
+
+ if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str))
+ pdata->dmic2_data_sel = da7218_of_dmic_data_sel(codec, of_str);
+ else
+ pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
+
+ if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str))
+ pdata->dmic2_samplephase =
+ da7218_of_dmic_samplephase(codec, of_str);
+ else
+ pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
+
+ if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0)
+ pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(codec, of_val32);
+ else
+ pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
+
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ if (of_property_read_bool(np, "dlg,hp-diff-single-supply"))
+ pdata->hp_diff_single_supply = true;
+ }
+
+ if (da7218->dev_id == DA7218_DEV_ID) {
+ hpldet_np = of_find_node_by_name(np, "da7218_hpldet");
+ if (!hpldet_np)
+ return pdata;
+
+ hpldet_pdata = devm_kzalloc(codec->dev, sizeof(*hpldet_pdata),
+ GFP_KERNEL);
+ if (!hpldet_pdata) {
+ dev_warn(codec->dev,
+ "Failed to allocate memory for hpldet pdata\n");
+ of_node_put(hpldet_np);
+ return pdata;
+ }
+ pdata->hpldet_pdata = hpldet_pdata;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_rate =
+ da7218_of_jack_rate(codec, of_val32);
+ else
+ hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-debounce",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_debounce =
+ da7218_of_jack_debounce(codec, of_val32);
+ else
+ hpldet_pdata->jack_debounce =
+ DA7218_HPLDET_JACK_DEBOUNCE_2;
+
+ if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct",
+ &of_val32) >= 0)
+ hpldet_pdata->jack_thr =
+ da7218_of_jack_thr(codec, of_val32);
+ else
+ hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT;
+
+ if (of_property_read_bool(hpldet_np, "dlg,comp-inv"))
+ hpldet_pdata->comp_inv = true;
+
+ if (of_property_read_bool(hpldet_np, "dlg,hyst"))
+ hpldet_pdata->hyst = true;
+
+ if (of_property_read_bool(hpldet_np, "dlg,discharge"))
+ hpldet_pdata->discharge = true;
+
+ of_node_put(hpldet_np);
+ }
+
+ return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7218_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct da7218_priv *da7218 = 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 (da7218->mclk) {
+ ret = clk_prepare_enable(da7218->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to enable mclk\n");
+ return ret;
+ }
+ }
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7218_REFERENCES,
+ DA7218_BIAS_EN_MASK,
+ DA7218_BIAS_EN_MASK);
+
+ /* Internal LDO */
+ snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ DA7218_LDO_EN_MASK,
+ DA7218_LDO_EN_MASK);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Only disable if jack detection disabled */
+ if (!da7218->jack) {
+ /* Internal LDO */
+ snd_soc_update_bits(codec, DA7218_LDO_CTRL,
+ DA7218_LDO_EN_MASK, 0);
+
+ /* Master bias */
+ snd_soc_update_bits(codec, DA7218_REFERENCES,
+ DA7218_BIAS_EN_MASK, 0);
+ }
+
+ /* MCLK */
+ if (da7218->mclk)
+ clk_disable_unprepare(da7218->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = {
+ [DA7218_SUPPLY_VDD] = "VDD",
+ [DA7218_SUPPLY_VDDMIC] = "VDDMIC",
+ [DA7218_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7218_handle_supplies(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddio;
+ u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+ int i, ret;
+
+ /* Get required supplies */
+ for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
+ da7218->supplies[i].supply = da7218_supply_names[i];
+
+ ret = devm_regulator_bulk_get(codec->dev, DA7218_NUM_SUPPLIES,
+ da7218->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to get supplies\n");
+ return ret;
+ }
+
+ /* Determine VDDIO voltage provided */
+ vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer;
+ ret = regulator_get_voltage(vddio);
+ if (ret < 1500000)
+ dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+ else if (ret < 2500000)
+ io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V;
+
+ /* Enable main supplies */
+ ret = regulator_bulk_enable(DA7218_NUM_SUPPLIES, da7218->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies\n");
+ return ret;
+ }
+
+ /* Ensure device in active mode */
+ snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK);
+
+ /* Update IO voltage level range */
+ snd_soc_write(codec, DA7218_IO_CTRL, io_voltage_lvl);
+
+ return 0;
+}
+
+static void da7218_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ struct da7218_pdata *pdata = da7218->pdata;
+
+ if (pdata) {
+ u8 micbias_lvl = 0, dmic_cfg = 0;
+
+ /* Mic Bias voltages */
+ switch (pdata->micbias1_lvl) {
+ case DA7218_MICBIAS_1_2V:
+ micbias_lvl |= DA7218_MICBIAS_1_LP_MODE_MASK;
+ break;
+ case DA7218_MICBIAS_1_6V:
+ case DA7218_MICBIAS_1_8V:
+ case DA7218_MICBIAS_2_0V:
+ case DA7218_MICBIAS_2_2V:
+ case DA7218_MICBIAS_2_4V:
+ case DA7218_MICBIAS_2_6V:
+ case DA7218_MICBIAS_2_8V:
+ case DA7218_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias1_lvl <<
+ DA7218_MICBIAS_1_LEVEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->micbias2_lvl) {
+ case DA7218_MICBIAS_1_2V:
+ micbias_lvl |= DA7218_MICBIAS_2_LP_MODE_MASK;
+ break;
+ case DA7218_MICBIAS_1_6V:
+ case DA7218_MICBIAS_1_8V:
+ case DA7218_MICBIAS_2_0V:
+ case DA7218_MICBIAS_2_2V:
+ case DA7218_MICBIAS_2_4V:
+ case DA7218_MICBIAS_2_6V:
+ case DA7218_MICBIAS_2_8V:
+ case DA7218_MICBIAS_3_0V:
+ micbias_lvl |= (pdata->micbias2_lvl <<
+ DA7218_MICBIAS_2_LEVEL_SHIFT);
+ break;
+ }
+
+ snd_soc_write(codec, DA7218_MICBIAS_CTRL, micbias_lvl);
+
+ /* Mic */
+ switch (pdata->mic1_amp_in_sel) {
+ case DA7218_MIC_AMP_IN_SEL_DIFF:
+ case DA7218_MIC_AMP_IN_SEL_SE_P:
+ case DA7218_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_write(codec, DA7218_MIC_1_SELECT,
+ pdata->mic1_amp_in_sel);
+ break;
+ }
+
+ switch (pdata->mic2_amp_in_sel) {
+ case DA7218_MIC_AMP_IN_SEL_DIFF:
+ case DA7218_MIC_AMP_IN_SEL_SE_P:
+ case DA7218_MIC_AMP_IN_SEL_SE_N:
+ snd_soc_write(codec, DA7218_MIC_2_SELECT,
+ pdata->mic2_amp_in_sel);
+ break;
+ }
+
+ /* DMic */
+ switch (pdata->dmic1_data_sel) {
+ case DA7218_DMIC_DATA_LFALL_RRISE:
+ case DA7218_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic1_data_sel <<
+ DA7218_DMIC_1_DATA_SEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic1_samplephase) {
+ case DA7218_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic1_samplephase <<
+ DA7218_DMIC_1_SAMPLEPHASE_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic1_clk_rate) {
+ case DA7218_DMIC_CLK_3_0MHZ:
+ case DA7218_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic1_clk_rate <<
+ DA7218_DMIC_1_CLK_RATE_SHIFT);
+ break;
+ }
+
+ snd_soc_update_bits(codec, DA7218_DMIC_1_CTRL,
+ DA7218_DMIC_1_DATA_SEL_MASK |
+ DA7218_DMIC_1_SAMPLEPHASE_MASK |
+ DA7218_DMIC_1_CLK_RATE_MASK, dmic_cfg);
+
+ dmic_cfg = 0;
+ switch (pdata->dmic2_data_sel) {
+ case DA7218_DMIC_DATA_LFALL_RRISE:
+ case DA7218_DMIC_DATA_LRISE_RFALL:
+ dmic_cfg |= (pdata->dmic2_data_sel <<
+ DA7218_DMIC_2_DATA_SEL_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic2_samplephase) {
+ case DA7218_DMIC_SAMPLE_ON_CLKEDGE:
+ case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE:
+ dmic_cfg |= (pdata->dmic2_samplephase <<
+ DA7218_DMIC_2_SAMPLEPHASE_SHIFT);
+ break;
+ }
+
+ switch (pdata->dmic2_clk_rate) {
+ case DA7218_DMIC_CLK_3_0MHZ:
+ case DA7218_DMIC_CLK_1_5MHZ:
+ dmic_cfg |= (pdata->dmic2_clk_rate <<
+ DA7218_DMIC_2_CLK_RATE_SHIFT);
+ break;
+ }
+
+ snd_soc_update_bits(codec, DA7218_DMIC_2_CTRL,
+ DA7218_DMIC_2_DATA_SEL_MASK |
+ DA7218_DMIC_2_SAMPLEPHASE_MASK |
+ DA7218_DMIC_2_CLK_RATE_MASK, dmic_cfg);
+
+ /* DA7217 Specific */
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ da7218->hp_single_supply =
+ pdata->hp_diff_single_supply;
+
+ if (da7218->hp_single_supply) {
+ snd_soc_write(codec, DA7218_HP_DIFF_UNLOCK,
+ DA7218_HP_DIFF_UNLOCK_VAL);
+ snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK,
+ DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK);
+ }
+ }
+
+ /* DA7218 Specific */
+ if ((da7218->dev_id == DA7218_DEV_ID) &&
+ (pdata->hpldet_pdata)) {
+ struct da7218_hpldet_pdata *hpldet_pdata =
+ pdata->hpldet_pdata;
+ u8 hpldet_cfg = 0;
+
+ switch (hpldet_pdata->jack_rate) {
+ case DA7218_HPLDET_JACK_RATE_5US:
+ case DA7218_HPLDET_JACK_RATE_10US:
+ case DA7218_HPLDET_JACK_RATE_20US:
+ case DA7218_HPLDET_JACK_RATE_40US:
+ case DA7218_HPLDET_JACK_RATE_80US:
+ case DA7218_HPLDET_JACK_RATE_160US:
+ case DA7218_HPLDET_JACK_RATE_320US:
+ case DA7218_HPLDET_JACK_RATE_640US:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_rate <<
+ DA7218_HPLDET_JACK_RATE_SHIFT);
+ break;
+ }
+
+ switch (hpldet_pdata->jack_debounce) {
+ case DA7218_HPLDET_JACK_DEBOUNCE_OFF:
+ case DA7218_HPLDET_JACK_DEBOUNCE_2:
+ case DA7218_HPLDET_JACK_DEBOUNCE_3:
+ case DA7218_HPLDET_JACK_DEBOUNCE_4:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_debounce <<
+ DA7218_HPLDET_JACK_DEBOUNCE_SHIFT);
+ break;
+ }
+
+ switch (hpldet_pdata->jack_thr) {
+ case DA7218_HPLDET_JACK_THR_84PCT:
+ case DA7218_HPLDET_JACK_THR_88PCT:
+ case DA7218_HPLDET_JACK_THR_92PCT:
+ case DA7218_HPLDET_JACK_THR_96PCT:
+ hpldet_cfg |=
+ (hpldet_pdata->jack_thr <<
+ DA7218_HPLDET_JACK_THR_SHIFT);
+ break;
+ }
+ snd_soc_update_bits(codec, DA7218_HPLDET_JACK,
+ DA7218_HPLDET_JACK_RATE_MASK |
+ DA7218_HPLDET_JACK_DEBOUNCE_MASK |
+ DA7218_HPLDET_JACK_THR_MASK,
+ hpldet_cfg);
+
+ hpldet_cfg = 0;
+ if (hpldet_pdata->comp_inv)
+ hpldet_cfg |= DA7218_HPLDET_COMP_INV_MASK;
+
+ if (hpldet_pdata->hyst)
+ hpldet_cfg |= DA7218_HPLDET_HYST_EN_MASK;
+
+ if (hpldet_pdata->discharge)
+ hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK;
+
+ snd_soc_write(codec, DA7218_HPLDET_CTRL, hpldet_cfg);
+ }
+ }
+}
+
+static int da7218_probe(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ /* Regulator configuration */
+ ret = da7218_handle_supplies(codec);
+ if (ret)
+ return ret;
+
+ /* Handle DT/Platform data */
+ if (codec->dev->of_node)
+ da7218->pdata = da7218_of_to_pdata(codec);
+ else
+ da7218->pdata = dev_get_platdata(codec->dev);
+
+ da7218_handle_pdata(codec);
+
+ /* Check if MCLK provided, if not the clock is NULL */
+ da7218->mclk = devm_clk_get(codec->dev, "mclk");
+ if (IS_ERR(da7218->mclk)) {
+ if (PTR_ERR(da7218->mclk) != -ENOENT) {
+ ret = PTR_ERR(da7218->mclk);
+ goto err_disable_reg;
+ } else {
+ da7218->mclk = NULL;
+ }
+ }
+
+ /* Default PC to free-running */
+ snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
+
+ /*
+ * Default Output Filter mixers to off otherwise DAPM will power
+ * Mic to HP passthrough paths by default at startup.
+ */
+ snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1L, 0);
+ snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1R, 0);
+
+ /* Default CP to normal load, power mode */
+ snd_soc_update_bits(codec, DA7218_CP_CTRL,
+ DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0);
+
+ /* Default gain ramping */
+ snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL,
+ DA7218_MIXIN_1_AMP_RAMP_EN_MASK,
+ DA7218_MIXIN_1_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL,
+ DA7218_MIXIN_2_AMP_RAMP_EN_MASK,
+ DA7218_MIXIN_2_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL,
+ DA7218_IN_1L_RAMP_EN_MASK,
+ DA7218_IN_1L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL,
+ DA7218_IN_1R_RAMP_EN_MASK,
+ DA7218_IN_1R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL,
+ DA7218_IN_2L_RAMP_EN_MASK,
+ DA7218_IN_2L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL,
+ DA7218_IN_2R_RAMP_EN_MASK,
+ DA7218_IN_2R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_DGS_GAIN_CTRL,
+ DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_OUT_1L_FILTER_CTRL,
+ DA7218_OUT_1L_RAMP_EN_MASK,
+ DA7218_OUT_1L_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_OUT_1R_FILTER_CTRL,
+ DA7218_OUT_1R_RAMP_EN_MASK,
+ DA7218_OUT_1R_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_HP_L_CTRL,
+ DA7218_HP_L_AMP_RAMP_EN_MASK,
+ DA7218_HP_L_AMP_RAMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7218_HP_R_CTRL,
+ DA7218_HP_R_AMP_RAMP_EN_MASK,
+ DA7218_HP_R_AMP_RAMP_EN_MASK);
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_write(codec, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK);
+
+ /* DA7217 specific config */
+ if (da7218->dev_id == DA7217_DEV_ID) {
+ snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL,
+ DA7218_HP_AMP_DIFF_MODE_EN_MASK,
+ DA7218_HP_AMP_DIFF_MODE_EN_MASK);
+
+ /* Only DA7218 supports HP detect, mask off for DA7217 */
+ snd_soc_write(codec, DA7218_EVENT_MASK,
+ DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK);
+ }
+
+ if (da7218->irq) {
+ ret = devm_request_threaded_irq(codec->dev, da7218->irq, NULL,
+ da7218_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7218", codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
+ da7218->irq, ret);
+ goto err_disable_reg;
+ }
+
+ }
+
+ return 0;
+
+err_disable_reg:
+ regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
+
+ return ret;
+}
+
+static int da7218_remove(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+
+ regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int da7218_suspend(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+
+ da7218_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* Put device into standby mode if jack detection disabled */
+ if (!da7218->jack)
+ snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, 0);
+
+ return 0;
+}
+
+static int da7218_resume(struct snd_soc_codec *codec)
+{
+ struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec);
+
+ /* Put device into active mode if previously moved to standby */
+ if (!da7218->jack)
+ snd_soc_write(codec, DA7218_SYSTEM_ACTIVE,
+ DA7218_SYSTEM_ACTIVE_MASK);
+
+ da7218_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define da7218_suspend NULL
+#define da7218_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_da7218 = {
+ .probe = da7218_probe,
+ .remove = da7218_remove,
+ .suspend = da7218_suspend,
+ .resume = da7218_resume,
+ .set_bias_level = da7218_set_bias_level,
+
+ .controls = da7218_snd_controls,
+ .num_controls = ARRAY_SIZE(da7218_snd_controls),
+
+ .dapm_widgets = da7218_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets),
+ .dapm_routes = da7218_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7218_audio_map),
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7218_reg_defaults[] = {
+ { DA7218_SYSTEM_ACTIVE, 0x00 },
+ { DA7218_CIF_CTRL, 0x00 },
+ { DA7218_SPARE1, 0x00 },
+ { DA7218_SR, 0xAA },
+ { DA7218_PC_COUNT, 0x02 },
+ { DA7218_GAIN_RAMP_CTRL, 0x00 },
+ { DA7218_CIF_TIMEOUT_CTRL, 0x01 },
+ { DA7218_SYSTEM_MODES_INPUT, 0x00 },
+ { DA7218_SYSTEM_MODES_OUTPUT, 0x00 },
+ { DA7218_IN_1L_FILTER_CTRL, 0x00 },
+ { DA7218_IN_1R_FILTER_CTRL, 0x00 },
+ { DA7218_IN_2L_FILTER_CTRL, 0x00 },
+ { DA7218_IN_2R_FILTER_CTRL, 0x00 },
+ { DA7218_OUT_1L_FILTER_CTRL, 0x40 },
+ { DA7218_OUT_1R_FILTER_CTRL, 0x40 },
+ { DA7218_OUT_1_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_OUT_1_EQ_12_FILTER_CTRL, 0x77 },
+ { DA7218_OUT_1_EQ_34_FILTER_CTRL, 0x77 },
+ { DA7218_OUT_1_EQ_5_FILTER_CTRL, 0x07 },
+ { DA7218_OUT_1_BIQ_5STAGE_CTRL, 0x40 },
+ { DA7218_OUT_1_BIQ_5STAGE_DATA, 0x00 },
+ { DA7218_OUT_1_BIQ_5STAGE_ADDR, 0x00 },
+ { DA7218_MIXIN_1_CTRL, 0x48 },
+ { DA7218_MIXIN_1_GAIN, 0x03 },
+ { DA7218_MIXIN_2_CTRL, 0x48 },
+ { DA7218_MIXIN_2_GAIN, 0x03 },
+ { DA7218_ALC_CTRL1, 0x00 },
+ { DA7218_ALC_CTRL2, 0x00 },
+ { DA7218_ALC_CTRL3, 0x00 },
+ { DA7218_ALC_NOISE, 0x3F },
+ { DA7218_ALC_TARGET_MIN, 0x3F },
+ { DA7218_ALC_TARGET_MAX, 0x00 },
+ { DA7218_ALC_GAIN_LIMITS, 0xFF },
+ { DA7218_ALC_ANA_GAIN_LIMITS, 0x71 },
+ { DA7218_ALC_ANTICLIP_CTRL, 0x00 },
+ { DA7218_AGS_ENABLE, 0x00 },
+ { DA7218_AGS_TRIGGER, 0x09 },
+ { DA7218_AGS_ATT_MAX, 0x00 },
+ { DA7218_AGS_TIMEOUT, 0x00 },
+ { DA7218_AGS_ANTICLIP_CTRL, 0x00 },
+ { DA7218_ENV_TRACK_CTRL, 0x00 },
+ { DA7218_LVL_DET_CTRL, 0x00 },
+ { DA7218_LVL_DET_LEVEL, 0x7F },
+ { DA7218_DGS_TRIGGER, 0x24 },
+ { DA7218_DGS_ENABLE, 0x00 },
+ { DA7218_DGS_RISE_FALL, 0x50 },
+ { DA7218_DGS_SYNC_DELAY, 0xA3 },
+ { DA7218_DGS_SYNC_DELAY2, 0x31 },
+ { DA7218_DGS_SYNC_DELAY3, 0x11 },
+ { DA7218_DGS_LEVELS, 0x01 },
+ { DA7218_DGS_GAIN_CTRL, 0x74 },
+ { DA7218_DROUTING_OUTDAI_1L, 0x01 },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_1R, 0x04 },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTFILT_1L, 0x01 },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTFILT_1R, 0x04 },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_2L, 0x04 },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DROUTING_OUTDAI_2R, 0x08 },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, 0x1C },
+ { DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, 0x1C },
+ { DA7218_DAI_CTRL, 0x28 },
+ { DA7218_DAI_TDM_CTRL, 0x40 },
+ { DA7218_DAI_OFFSET_LOWER, 0x00 },
+ { DA7218_DAI_OFFSET_UPPER, 0x00 },
+ { DA7218_DAI_CLK_MODE, 0x01 },
+ { DA7218_PLL_CTRL, 0x04 },
+ { DA7218_PLL_FRAC_TOP, 0x00 },
+ { DA7218_PLL_FRAC_BOT, 0x00 },
+ { DA7218_PLL_INTEGER, 0x20 },
+ { DA7218_DAC_NG_CTRL, 0x00 },
+ { DA7218_DAC_NG_SETUP_TIME, 0x00 },
+ { DA7218_DAC_NG_OFF_THRESH, 0x00 },
+ { DA7218_DAC_NG_ON_THRESH, 0x00 },
+ { DA7218_TONE_GEN_CFG2, 0x00 },
+ { DA7218_TONE_GEN_FREQ1_L, 0x55 },
+ { DA7218_TONE_GEN_FREQ1_U, 0x15 },
+ { DA7218_TONE_GEN_FREQ2_L, 0x00 },
+ { DA7218_TONE_GEN_FREQ2_U, 0x40 },
+ { DA7218_TONE_GEN_CYCLES, 0x00 },
+ { DA7218_TONE_GEN_ON_PER, 0x02 },
+ { DA7218_TONE_GEN_OFF_PER, 0x01 },
+ { DA7218_CP_CTRL, 0x60 },
+ { DA7218_CP_DELAY, 0x11 },
+ { DA7218_CP_VOL_THRESHOLD1, 0x0E },
+ { DA7218_MIC_1_CTRL, 0x40 },
+ { DA7218_MIC_1_GAIN, 0x01 },
+ { DA7218_MIC_1_SELECT, 0x00 },
+ { DA7218_MIC_2_CTRL, 0x40 },
+ { DA7218_MIC_2_GAIN, 0x01 },
+ { DA7218_MIC_2_SELECT, 0x00 },
+ { DA7218_IN_1_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_IN_2_HPF_FILTER_CTRL, 0x80 },
+ { DA7218_ADC_1_CTRL, 0x07 },
+ { DA7218_ADC_2_CTRL, 0x07 },
+ { DA7218_MIXOUT_L_CTRL, 0x00 },
+ { DA7218_MIXOUT_L_GAIN, 0x03 },
+ { DA7218_MIXOUT_R_CTRL, 0x00 },
+ { DA7218_MIXOUT_R_GAIN, 0x03 },
+ { DA7218_HP_L_CTRL, 0x40 },
+ { DA7218_HP_L_GAIN, 0x3B },
+ { DA7218_HP_R_CTRL, 0x40 },
+ { DA7218_HP_R_GAIN, 0x3B },
+ { DA7218_HP_DIFF_CTRL, 0x00 },
+ { DA7218_HP_DIFF_UNLOCK, 0xC3 },
+ { DA7218_HPLDET_JACK, 0x0B },
+ { DA7218_HPLDET_CTRL, 0x00 },
+ { DA7218_REFERENCES, 0x08 },
+ { DA7218_IO_CTRL, 0x00 },
+ { DA7218_LDO_CTRL, 0x00 },
+ { DA7218_SIDETONE_CTRL, 0x40 },
+ { DA7218_SIDETONE_IN_SELECT, 0x00 },
+ { DA7218_SIDETONE_GAIN, 0x1C },
+ { DA7218_DROUTING_ST_OUTFILT_1L, 0x01 },
+ { DA7218_DROUTING_ST_OUTFILT_1R, 0x02 },
+ { DA7218_SIDETONE_BIQ_3STAGE_DATA, 0x00 },
+ { DA7218_SIDETONE_BIQ_3STAGE_ADDR, 0x00 },
+ { DA7218_EVENT_MASK, 0x00 },
+ { DA7218_DMIC_1_CTRL, 0x00 },
+ { DA7218_DMIC_2_CTRL, 0x00 },
+ { DA7218_IN_1L_GAIN, 0x6F },
+ { DA7218_IN_1R_GAIN, 0x6F },
+ { DA7218_IN_2L_GAIN, 0x6F },
+ { DA7218_IN_2R_GAIN, 0x6F },
+ { DA7218_OUT_1L_GAIN, 0x6F },
+ { DA7218_OUT_1R_GAIN, 0x6F },
+ { DA7218_MICBIAS_CTRL, 0x00 },
+ { DA7218_MICBIAS_EN, 0x00 },
+};
+
+static bool da7218_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA7218_STATUS1:
+ case DA7218_SOFT_RESET:
+ case DA7218_SYSTEM_STATUS:
+ case DA7218_CALIB_CTRL:
+ case DA7218_CALIB_OFFSET_AUTO_M_1:
+ case DA7218_CALIB_OFFSET_AUTO_U_1:
+ case DA7218_CALIB_OFFSET_AUTO_M_2:
+ case DA7218_CALIB_OFFSET_AUTO_U_2:
+ case DA7218_PLL_STATUS:
+ case DA7218_PLL_REFOSC_CAL:
+ case DA7218_TONE_GEN_CFG1:
+ case DA7218_ADC_MODE:
+ case DA7218_HP_SNGL_CTRL:
+ case DA7218_HPLDET_TEST:
+ case DA7218_EVENT_STATUS:
+ case DA7218_EVENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config da7218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DA7218_MICBIAS_EN,
+ .reg_defaults = da7218_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da7218_reg_defaults),
+ .volatile_reg = da7218_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7218_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da7218_priv *da7218;
+ int ret;
+
+ da7218 = devm_kzalloc(&i2c->dev, sizeof(struct da7218_priv),
+ GFP_KERNEL);
+ if (!da7218)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, da7218);
+
+ if (i2c->dev.of_node)
+ da7218->dev_id = da7218_of_get_id(&i2c->dev);
+ else
+ da7218->dev_id = id->driver_data;
+
+ if ((da7218->dev_id != DA7217_DEV_ID) &&
+ (da7218->dev_id != DA7218_DEV_ID)) {
+ dev_err(&i2c->dev, "Invalid device Id\n");
+ return -EINVAL;
+ }
+
+ da7218->irq = i2c->irq;
+
+ da7218->regmap = devm_regmap_init_i2c(i2c, &da7218_regmap_config);
+ if (IS_ERR(da7218->regmap)) {
+ ret = PTR_ERR(da7218->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_da7218, &da7218_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da7218 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int da7218_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da7218_i2c_id[] = {
+ { "da7217", DA7217_DEV_ID },
+ { "da7218", DA7218_DEV_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da7218_i2c_id);
+
+static struct i2c_driver da7218_i2c_driver = {
+ .driver = {
+ .name = "da7218",
+ .of_match_table = of_match_ptr(da7218_of_match),
+ },
+ .probe = da7218_i2c_probe,
+ .remove = da7218_i2c_remove,
+ .id_table = da7218_i2c_id,
+};
+
+module_i2c_driver(da7218_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7218 Codec driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
new file mode 100644
index 000000000000..c2c59049a2ad
--- /dev/null
+++ b/sound/soc/codecs/da7218.h
@@ -0,0 +1,1414 @@
+/*
+ * da7218.h - DA7218 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 _DA7218_H
+#define _DA7218_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7218.h>
+
+
+/*
+ * Registers
+ */
+#define DA7218_SYSTEM_ACTIVE 0x0
+#define DA7218_CIF_CTRL 0x1
+#define DA7218_CHIP_ID1 0x4
+#define DA7218_CHIP_ID2 0x5
+#define DA7218_CHIP_REVISION 0x6
+#define DA7218_SPARE1 0x7
+#define DA7218_STATUS1 0x8
+#define DA7218_SOFT_RESET 0x9
+#define DA7218_SR 0xB
+#define DA7218_PC_COUNT 0xC
+#define DA7218_GAIN_RAMP_CTRL 0xD
+#define DA7218_CIF_TIMEOUT_CTRL 0x10
+#define DA7218_SYSTEM_MODES_INPUT 0x14
+#define DA7218_SYSTEM_MODES_OUTPUT 0x15
+#define DA7218_SYSTEM_STATUS 0x16
+#define DA7218_IN_1L_FILTER_CTRL 0x18
+#define DA7218_IN_1R_FILTER_CTRL 0x19
+#define DA7218_IN_2L_FILTER_CTRL 0x1A
+#define DA7218_IN_2R_FILTER_CTRL 0x1B
+#define DA7218_OUT_1L_FILTER_CTRL 0x20
+#define DA7218_OUT_1R_FILTER_CTRL 0x21
+#define DA7218_OUT_1_HPF_FILTER_CTRL 0x24
+#define DA7218_OUT_1_EQ_12_FILTER_CTRL 0x25
+#define DA7218_OUT_1_EQ_34_FILTER_CTRL 0x26
+#define DA7218_OUT_1_EQ_5_FILTER_CTRL 0x27
+#define DA7218_OUT_1_BIQ_5STAGE_CTRL 0x28
+#define DA7218_OUT_1_BIQ_5STAGE_DATA 0x29
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR 0x2A
+#define DA7218_MIXIN_1_CTRL 0x2C
+#define DA7218_MIXIN_1_GAIN 0x2D
+#define DA7218_MIXIN_2_CTRL 0x2E
+#define DA7218_MIXIN_2_GAIN 0x2F
+#define DA7218_ALC_CTRL1 0x30
+#define DA7218_ALC_CTRL2 0x31
+#define DA7218_ALC_CTRL3 0x32
+#define DA7218_ALC_NOISE 0x33
+#define DA7218_ALC_TARGET_MIN 0x34
+#define DA7218_ALC_TARGET_MAX 0x35
+#define DA7218_ALC_GAIN_LIMITS 0x36
+#define DA7218_ALC_ANA_GAIN_LIMITS 0x37
+#define DA7218_ALC_ANTICLIP_CTRL 0x38
+#define DA7218_AGS_ENABLE 0x3C
+#define DA7218_AGS_TRIGGER 0x3D
+#define DA7218_AGS_ATT_MAX 0x3E
+#define DA7218_AGS_TIMEOUT 0x3F
+#define DA7218_AGS_ANTICLIP_CTRL 0x40
+#define DA7218_CALIB_CTRL 0x44
+#define DA7218_CALIB_OFFSET_AUTO_M_1 0x45
+#define DA7218_CALIB_OFFSET_AUTO_U_1 0x46
+#define DA7218_CALIB_OFFSET_AUTO_M_2 0x47
+#define DA7218_CALIB_OFFSET_AUTO_U_2 0x48
+#define DA7218_ENV_TRACK_CTRL 0x4C
+#define DA7218_LVL_DET_CTRL 0x50
+#define DA7218_LVL_DET_LEVEL 0x51
+#define DA7218_DGS_TRIGGER 0x54
+#define DA7218_DGS_ENABLE 0x55
+#define DA7218_DGS_RISE_FALL 0x56
+#define DA7218_DGS_SYNC_DELAY 0x57
+#define DA7218_DGS_SYNC_DELAY2 0x58
+#define DA7218_DGS_SYNC_DELAY3 0x59
+#define DA7218_DGS_LEVELS 0x5A
+#define DA7218_DGS_GAIN_CTRL 0x5B
+#define DA7218_DROUTING_OUTDAI_1L 0x5C
+#define DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN 0x5D
+#define DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN 0x5E
+#define DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN 0x5F
+#define DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN 0x60
+#define DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN 0x61
+#define DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN 0x62
+#define DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN 0x63
+#define DA7218_DROUTING_OUTDAI_1R 0x64
+#define DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN 0x65
+#define DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN 0x66
+#define DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN 0x67
+#define DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN 0x68
+#define DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN 0x69
+#define DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN 0x6A
+#define DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN 0x6B
+#define DA7218_DROUTING_OUTFILT_1L 0x6C
+#define DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN 0x6D
+#define DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN 0x6E
+#define DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN 0x6F
+#define DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN 0x70
+#define DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN 0x71
+#define DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN 0x72
+#define DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN 0x73
+#define DA7218_DROUTING_OUTFILT_1R 0x74
+#define DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN 0x75
+#define DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN 0x76
+#define DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN 0x77
+#define DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN 0x78
+#define DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN 0x79
+#define DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN 0x7A
+#define DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN 0x7B
+#define DA7218_DROUTING_OUTDAI_2L 0x7C
+#define DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN 0x7D
+#define DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN 0x7E
+#define DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN 0x7F
+#define DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN 0x80
+#define DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN 0x81
+#define DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN 0x82
+#define DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN 0x83
+#define DA7218_DROUTING_OUTDAI_2R 0x84
+#define DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN 0x85
+#define DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN 0x86
+#define DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN 0x87
+#define DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN 0x88
+#define DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN 0x89
+#define DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN 0x8A
+#define DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN 0x8B
+#define DA7218_DAI_CTRL 0x8C
+#define DA7218_DAI_TDM_CTRL 0x8D
+#define DA7218_DAI_OFFSET_LOWER 0x8E
+#define DA7218_DAI_OFFSET_UPPER 0x8F
+#define DA7218_DAI_CLK_MODE 0x90
+#define DA7218_PLL_CTRL 0x91
+#define DA7218_PLL_FRAC_TOP 0x92
+#define DA7218_PLL_FRAC_BOT 0x93
+#define DA7218_PLL_INTEGER 0x94
+#define DA7218_PLL_STATUS 0x95
+#define DA7218_PLL_REFOSC_CAL 0x98
+#define DA7218_DAC_NG_CTRL 0x9C
+#define DA7218_DAC_NG_SETUP_TIME 0x9D
+#define DA7218_DAC_NG_OFF_THRESH 0x9E
+#define DA7218_DAC_NG_ON_THRESH 0x9F
+#define DA7218_TONE_GEN_CFG1 0xA0
+#define DA7218_TONE_GEN_CFG2 0xA1
+#define DA7218_TONE_GEN_FREQ1_L 0xA2
+#define DA7218_TONE_GEN_FREQ1_U 0xA3
+#define DA7218_TONE_GEN_FREQ2_L 0xA4
+#define DA7218_TONE_GEN_FREQ2_U 0xA5
+#define DA7218_TONE_GEN_CYCLES 0xA6
+#define DA7218_TONE_GEN_ON_PER 0xA7
+#define DA7218_TONE_GEN_OFF_PER 0xA8
+#define DA7218_CP_CTRL 0xAC
+#define DA7218_CP_DELAY 0xAD
+#define DA7218_CP_VOL_THRESHOLD1 0xAE
+#define DA7218_MIC_1_CTRL 0xB4
+#define DA7218_MIC_1_GAIN 0xB5
+#define DA7218_MIC_1_SELECT 0xB7
+#define DA7218_MIC_2_CTRL 0xB8
+#define DA7218_MIC_2_GAIN 0xB9
+#define DA7218_MIC_2_SELECT 0xBB
+#define DA7218_IN_1_HPF_FILTER_CTRL 0xBC
+#define DA7218_IN_2_HPF_FILTER_CTRL 0xBD
+#define DA7218_ADC_1_CTRL 0xC0
+#define DA7218_ADC_2_CTRL 0xC1
+#define DA7218_ADC_MODE 0xC2
+#define DA7218_MIXOUT_L_CTRL 0xCC
+#define DA7218_MIXOUT_L_GAIN 0xCD
+#define DA7218_MIXOUT_R_CTRL 0xCE
+#define DA7218_MIXOUT_R_GAIN 0xCF
+#define DA7218_HP_L_CTRL 0xD0
+#define DA7218_HP_L_GAIN 0xD1
+#define DA7218_HP_R_CTRL 0xD2
+#define DA7218_HP_R_GAIN 0xD3
+#define DA7218_HP_SNGL_CTRL 0xD4
+#define DA7218_HP_DIFF_CTRL 0xD5
+#define DA7218_HP_DIFF_UNLOCK 0xD7
+#define DA7218_HPLDET_JACK 0xD8
+#define DA7218_HPLDET_CTRL 0xD9
+#define DA7218_HPLDET_TEST 0xDA
+#define DA7218_REFERENCES 0xDC
+#define DA7218_IO_CTRL 0xE0
+#define DA7218_LDO_CTRL 0xE1
+#define DA7218_SIDETONE_CTRL 0xE4
+#define DA7218_SIDETONE_IN_SELECT 0xE5
+#define DA7218_SIDETONE_GAIN 0xE6
+#define DA7218_DROUTING_ST_OUTFILT_1L 0xE8
+#define DA7218_DROUTING_ST_OUTFILT_1R 0xE9
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA 0xEA
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR 0xEB
+#define DA7218_EVENT_STATUS 0xEC
+#define DA7218_EVENT 0xED
+#define DA7218_EVENT_MASK 0xEE
+#define DA7218_DMIC_1_CTRL 0xF0
+#define DA7218_DMIC_2_CTRL 0xF1
+#define DA7218_IN_1L_GAIN 0xF4
+#define DA7218_IN_1R_GAIN 0xF5
+#define DA7218_IN_2L_GAIN 0xF6
+#define DA7218_IN_2R_GAIN 0xF7
+#define DA7218_OUT_1L_GAIN 0xF8
+#define DA7218_OUT_1R_GAIN 0xF9
+#define DA7218_MICBIAS_CTRL 0xFC
+#define DA7218_MICBIAS_EN 0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7218_SWITCH_EN_MAX 0x1
+
+/* DA7218_SYSTEM_ACTIVE = 0x0 */
+#define DA7218_SYSTEM_ACTIVE_SHIFT 0
+#define DA7218_SYSTEM_ACTIVE_MASK (0x1 << 0)
+
+/* DA7218_CIF_CTRL = 0x1 */
+#define DA7218_CIF_I2C_WRITE_MODE_SHIFT 0
+#define DA7218_CIF_I2C_WRITE_MODE_MASK (0x1 << 0)
+
+/* DA7218_CHIP_ID1 = 0x4 */
+#define DA7218_CHIP_ID1_SHIFT 0
+#define DA7218_CHIP_ID1_MASK (0xFF << 0)
+
+/* DA7218_CHIP_ID2 = 0x5 */
+#define DA7218_CHIP_ID2_SHIFT 0
+#define DA7218_CHIP_ID2_MASK (0xFF << 0)
+
+/* DA7218_CHIP_REVISION = 0x6 */
+#define DA7218_CHIP_MINOR_SHIFT 0
+#define DA7218_CHIP_MINOR_MASK (0xF << 0)
+#define DA7218_CHIP_MAJOR_SHIFT 4
+#define DA7218_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7218_SPARE1 = 0x7 */
+#define DA7218_SPARE1_SHIFT 0
+#define DA7218_SPARE1_MASK (0xFF << 0)
+
+/* DA7218_STATUS1 = 0x8 */
+#define DA7218_STATUS_SPARE1_SHIFT 0
+#define DA7218_STATUS_SPARE1_MASK (0xFF << 0)
+
+/* DA7218_SOFT_RESET = 0x9 */
+#define DA7218_CIF_REG_SOFT_RESET_SHIFT 7
+#define DA7218_CIF_REG_SOFT_RESET_MASK (0x1 << 7)
+
+/* DA7218_SR = 0xB */
+#define DA7218_SR_ADC_SHIFT 0
+#define DA7218_SR_ADC_MASK (0xF << 0)
+#define DA7218_SR_DAC_SHIFT 4
+#define DA7218_SR_DAC_MASK (0xF << 4)
+#define DA7218_SR_8000 0x01
+#define DA7218_SR_11025 0x02
+#define DA7218_SR_12000 0x03
+#define DA7218_SR_16000 0x05
+#define DA7218_SR_22050 0x06
+#define DA7218_SR_24000 0x07
+#define DA7218_SR_32000 0x09
+#define DA7218_SR_44100 0x0A
+#define DA7218_SR_48000 0x0B
+#define DA7218_SR_88200 0x0E
+#define DA7218_SR_96000 0x0F
+
+/* DA7218_PC_COUNT = 0xC */
+#define DA7218_PC_FREERUN_SHIFT 0
+#define DA7218_PC_FREERUN_MASK (0x1 << 0)
+#define DA7218_PC_RESYNC_AUTO_SHIFT 1
+#define DA7218_PC_RESYNC_AUTO_MASK (0x1 << 1)
+
+/* DA7218_GAIN_RAMP_CTRL = 0xD */
+#define DA7218_GAIN_RAMP_RATE_SHIFT 0
+#define DA7218_GAIN_RAMP_RATE_MASK (0x3 << 0)
+#define DA7218_GAIN_RAMP_RATE_MAX 4
+
+/* DA7218_CIF_TIMEOUT_CTRL = 0x10 */
+#define DA7218_I2C_TIMEOUT_EN_SHIFT 0
+#define DA7218_I2C_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7218_SYSTEM_MODES_INPUT = 0x14 */
+#define DA7218_MODE_SUBMIT_SHIFT 0
+#define DA7218_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7218_ADC_MODE_SHIFT 1
+#define DA7218_ADC_MODE_MASK (0x7F << 1)
+
+/* DA7218_SYSTEM_MODES_OUTPUT = 0x15 */
+#define DA7218_MODE_SUBMIT_SHIFT 0
+#define DA7218_MODE_SUBMIT_MASK (0x1 << 0)
+#define DA7218_DAC_MODE_SHIFT 1
+#define DA7218_DAC_MODE_MASK (0x7F << 1)
+
+/* DA7218_SYSTEM_STATUS = 0x16 */
+#define DA7218_SC1_BUSY_SHIFT 0
+#define DA7218_SC1_BUSY_MASK (0x1 << 0)
+#define DA7218_SC2_BUSY_SHIFT 1
+#define DA7218_SC2_BUSY_MASK (0x1 << 1)
+
+/* DA7218_IN_1L_FILTER_CTRL = 0x18 */
+#define DA7218_IN_1L_RAMP_EN_SHIFT 5
+#define DA7218_IN_1L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_1L_MUTE_EN_SHIFT 6
+#define DA7218_IN_1L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_1L_FILTER_EN_SHIFT 7
+#define DA7218_IN_1L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_1R_FILTER_CTRL = 0x19 */
+#define DA7218_IN_1R_RAMP_EN_SHIFT 5
+#define DA7218_IN_1R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_1R_MUTE_EN_SHIFT 6
+#define DA7218_IN_1R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_1R_FILTER_EN_SHIFT 7
+#define DA7218_IN_1R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2L_FILTER_CTRL = 0x1A */
+#define DA7218_IN_2L_RAMP_EN_SHIFT 5
+#define DA7218_IN_2L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_2L_MUTE_EN_SHIFT 6
+#define DA7218_IN_2L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_2L_FILTER_EN_SHIFT 7
+#define DA7218_IN_2L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2R_FILTER_CTRL = 0x1B */
+#define DA7218_IN_2R_RAMP_EN_SHIFT 5
+#define DA7218_IN_2R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_IN_2R_MUTE_EN_SHIFT 6
+#define DA7218_IN_2R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_IN_2R_FILTER_EN_SHIFT 7
+#define DA7218_IN_2R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1L_FILTER_CTRL = 0x20 */
+#define DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT 3
+#define DA7218_OUT_1L_BIQ_5STAGE_SEL_MASK (0x1 << 3)
+#define DA7218_OUT_BIQ_5STAGE_SEL_MAX 2
+#define DA7218_OUT_1L_SUBRANGE_EN_SHIFT 4
+#define DA7218_OUT_1L_SUBRANGE_EN_MASK (0x1 << 4)
+#define DA7218_OUT_1L_RAMP_EN_SHIFT 5
+#define DA7218_OUT_1L_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_OUT_1L_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1L_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1L_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1L_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1R_FILTER_CTRL = 0x21 */
+#define DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT 3
+#define DA7218_OUT_1R_BIQ_5STAGE_SEL_MASK (0x1 << 3)
+#define DA7218_OUT_1R_SUBRANGE_EN_SHIFT 4
+#define DA7218_OUT_1R_SUBRANGE_EN_MASK (0x1 << 4)
+#define DA7218_OUT_1R_RAMP_EN_SHIFT 5
+#define DA7218_OUT_1R_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_OUT_1R_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1R_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1R_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1R_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_HPF_FILTER_CTRL = 0x24 */
+#define DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_OUT_1_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_VOICE_HPF_CORNER_MAX 8
+#define DA7218_OUT_1_VOICE_EN_SHIFT 3
+#define DA7218_OUT_1_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_OUT_1_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_AUDIO_HPF_CORNER_MAX 4
+#define DA7218_OUT_1_HPF_EN_SHIFT 7
+#define DA7218_OUT_1_HPF_EN_MASK (0x1 << 7)
+#define DA7218_HPF_MODE_SHIFT 0
+#define DA7218_HPF_DISABLED ((0x0 << 3) | (0x0 << 7))
+#define DA7218_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7))
+#define DA7218_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7))
+#define DA7218_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7))
+#define DA7218_HPF_MODE_MAX 3
+
+/* DA7218_OUT_1_EQ_12_FILTER_CTRL = 0x25 */
+#define DA7218_OUT_1_EQ_BAND1_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND1_MASK (0xF << 0)
+#define DA7218_OUT_EQ_BAND_MAX 0xF
+#define DA7218_OUT_1_EQ_BAND2_SHIFT 4
+#define DA7218_OUT_1_EQ_BAND2_MASK (0xF << 4)
+
+/* DA7218_OUT_1_EQ_34_FILTER_CTRL = 0x26 */
+#define DA7218_OUT_1_EQ_BAND3_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND3_MASK (0xF << 0)
+#define DA7218_OUT_1_EQ_BAND4_SHIFT 4
+#define DA7218_OUT_1_EQ_BAND4_MASK (0xF << 4)
+
+/* DA7218_OUT_1_EQ_5_FILTER_CTRL = 0x27 */
+#define DA7218_OUT_1_EQ_BAND5_SHIFT 0
+#define DA7218_OUT_1_EQ_BAND5_MASK (0xF << 0)
+#define DA7218_OUT_1_EQ_EN_SHIFT 7
+#define DA7218_OUT_1_EQ_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_BIQ_5STAGE_CTRL = 0x28 */
+#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT 6
+#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT 7
+#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_OUT_1_BIQ_5STAGE_DATA = 0x29 */
+#define DA7218_OUT_1_BIQ_5STAGE_DATA_SHIFT 0
+#define DA7218_OUT_1_BIQ_5STAGE_DATA_MASK (0xFF << 0)
+
+/* DA7218_OUT_1_BIQ_5STAGE_ADDR = 0x2A */
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR_SHIFT 0
+#define DA7218_OUT_1_BIQ_5STAGE_ADDR_MASK (0x3F << 0)
+#define DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE 50
+
+/* DA7218_MIXIN_1_CTRL = 0x2C */
+#define DA7218_MIXIN_1_MIX_SEL_SHIFT 3
+#define DA7218_MIXIN_1_MIX_SEL_MASK (0x1 << 3)
+#define DA7218_MIXIN_1_AMP_ZC_EN_SHIFT 4
+#define DA7218_MIXIN_1_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT 5
+#define DA7218_MIXIN_1_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIXIN_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIXIN_1_AMP_EN_SHIFT 7
+#define DA7218_MIXIN_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXIN_1_GAIN = 0x2D */
+#define DA7218_MIXIN_1_AMP_GAIN_SHIFT 0
+#define DA7218_MIXIN_1_AMP_GAIN_MASK (0xF << 0)
+#define DA7218_MIXIN_AMP_GAIN_MAX 0xF
+
+/* DA7218_MIXIN_2_CTRL = 0x2E */
+#define DA7218_MIXIN_2_MIX_SEL_SHIFT 3
+#define DA7218_MIXIN_2_MIX_SEL_MASK (0x1 << 3)
+#define DA7218_MIXIN_2_AMP_ZC_EN_SHIFT 4
+#define DA7218_MIXIN_2_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT 5
+#define DA7218_MIXIN_2_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIXIN_2_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIXIN_2_AMP_EN_SHIFT 7
+#define DA7218_MIXIN_2_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXIN_2_GAIN = 0x2F */
+#define DA7218_MIXIN_2_AMP_GAIN_SHIFT 0
+#define DA7218_MIXIN_2_AMP_GAIN_MASK (0xF << 0)
+
+/* DA7218_ALC_CTRL1 = 0x30 */
+#define DA7218_ALC_EN_SHIFT 0
+#define DA7218_ALC_EN_MASK (0xF << 0)
+#define DA7218_ALC_CHAN1_L_EN_SHIFT 0
+#define DA7218_ALC_CHAN1_R_EN_SHIFT 1
+#define DA7218_ALC_CHAN2_L_EN_SHIFT 2
+#define DA7218_ALC_CHAN2_R_EN_SHIFT 3
+#define DA7218_ALC_SYNC_MODE_SHIFT 4
+#define DA7218_ALC_SYNC_MODE_MASK (0xF << 4)
+#define DA7218_ALC_SYNC_MODE_CH1 (0x1 << 4)
+#define DA7218_ALC_SYNC_MODE_CH2 (0x4 << 4)
+
+/* DA7218_ALC_CTRL2 = 0x31 */
+#define DA7218_ALC_ATTACK_SHIFT 0
+#define DA7218_ALC_ATTACK_MASK (0xF << 0)
+#define DA7218_ALC_ATTACK_MAX 13
+#define DA7218_ALC_RELEASE_SHIFT 4
+#define DA7218_ALC_RELEASE_MASK (0xF << 4)
+#define DA7218_ALC_RELEASE_MAX 11
+
+/* DA7218_ALC_CTRL3 = 0x32 */
+#define DA7218_ALC_HOLD_SHIFT 0
+#define DA7218_ALC_HOLD_MASK (0xF << 0)
+#define DA7218_ALC_HOLD_MAX 16
+
+/* DA7218_ALC_NOISE = 0x33 */
+#define DA7218_ALC_NOISE_SHIFT 0
+#define DA7218_ALC_NOISE_MASK (0x3F << 0)
+#define DA7218_ALC_THRESHOLD_MAX 0x3F
+
+/* DA7218_ALC_TARGET_MIN = 0x34 */
+#define DA7218_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7218_ALC_THRESHOLD_MIN_MASK (0x3F << 0)
+
+/* DA7218_ALC_TARGET_MAX = 0x35 */
+#define DA7218_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7218_ALC_THRESHOLD_MAX_MASK (0x3F << 0)
+
+/* DA7218_ALC_GAIN_LIMITS = 0x36 */
+#define DA7218_ALC_ATTEN_MAX_SHIFT 0
+#define DA7218_ALC_ATTEN_MAX_MASK (0xF << 0)
+#define DA7218_ALC_ATTEN_GAIN_MAX 0xF
+#define DA7218_ALC_GAIN_MAX_SHIFT 4
+#define DA7218_ALC_GAIN_MAX_MASK (0xF << 4)
+
+/* DA7218_ALC_ANA_GAIN_LIMITS = 0x37 */
+#define DA7218_ALC_ANA_GAIN_MIN_SHIFT 0
+#define DA7218_ALC_ANA_GAIN_MIN_MASK (0x7 << 0)
+#define DA7218_ALC_ANA_GAIN_MIN 0x1
+#define DA7218_ALC_ANA_GAIN_MAX 0x7
+#define DA7218_ALC_ANA_GAIN_MAX_SHIFT 4
+#define DA7218_ALC_ANA_GAIN_MAX_MASK (0x7 << 4)
+
+/* DA7218_ALC_ANTICLIP_CTRL = 0x38 */
+#define DA7218_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7218_ALC_ANTICLIP_STEP_MASK (0x3 << 0)
+#define DA7218_ALC_ANTICLIP_STEP_MAX 4
+#define DA7218_ALC_ANTICLIP_EN_SHIFT 7
+#define DA7218_ALC_ANTICLIP_EN_MASK (0x1 << 7)
+
+/* DA7218_AGS_ENABLE = 0x3C */
+#define DA7218_AGS_ENABLE_SHIFT 0
+#define DA7218_AGS_ENABLE_MASK (0x3 << 0)
+#define DA7218_AGS_ENABLE_CHAN1_SHIFT 0
+#define DA7218_AGS_ENABLE_CHAN2_SHIFT 1
+
+/* DA7218_AGS_TRIGGER = 0x3D */
+#define DA7218_AGS_TRIGGER_SHIFT 0
+#define DA7218_AGS_TRIGGER_MASK (0xF << 0)
+#define DA7218_AGS_TRIGGER_MAX 0xF
+
+/* DA7218_AGS_ATT_MAX = 0x3E */
+#define DA7218_AGS_ATT_MAX_SHIFT 0
+#define DA7218_AGS_ATT_MAX_MASK (0x7 << 0)
+#define DA7218_AGS_ATT_MAX_MAX 0x7
+
+/* DA7218_AGS_TIMEOUT = 0x3F */
+#define DA7218_AGS_TIMEOUT_EN_SHIFT 0
+#define DA7218_AGS_TIMEOUT_EN_MASK (0x1 << 0)
+
+/* DA7218_AGS_ANTICLIP_CTRL = 0x40 */
+#define DA7218_AGS_ANTICLIP_EN_SHIFT 7
+#define DA7218_AGS_ANTICLIP_EN_MASK (0x1 << 7)
+
+/* DA7218_CALIB_CTRL = 0x44 */
+#define DA7218_CALIB_OFFSET_EN_SHIFT 0
+#define DA7218_CALIB_OFFSET_EN_MASK (0x1 << 0)
+#define DA7218_CALIB_AUTO_EN_SHIFT 2
+#define DA7218_CALIB_AUTO_EN_MASK (0x1 << 2)
+#define DA7218_CALIB_OVERFLOW_SHIFT 3
+#define DA7218_CALIB_OVERFLOW_MASK (0x1 << 3)
+
+/* DA7218_CALIB_OFFSET_AUTO_M_1 = 0x45 */
+#define DA7218_CALIB_OFFSET_AUTO_M_1_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_M_1_MASK (0xFF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_U_1 = 0x46 */
+#define DA7218_CALIB_OFFSET_AUTO_U_1_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_U_1_MASK (0xF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_M_2 = 0x47 */
+#define DA7218_CALIB_OFFSET_AUTO_M_2_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_M_2_MASK (0xFF << 0)
+
+/* DA7218_CALIB_OFFSET_AUTO_U_2 = 0x48 */
+#define DA7218_CALIB_OFFSET_AUTO_U_2_SHIFT 0
+#define DA7218_CALIB_OFFSET_AUTO_U_2_MASK (0xF << 0)
+
+/* DA7218_ENV_TRACK_CTRL = 0x4C */
+#define DA7218_INTEG_ATTACK_SHIFT 0
+#define DA7218_INTEG_ATTACK_MASK (0x3 << 0)
+#define DA7218_INTEG_RELEASE_SHIFT 4
+#define DA7218_INTEG_RELEASE_MASK (0x3 << 4)
+#define DA7218_INTEG_MAX 4
+
+/* DA7218_LVL_DET_CTRL = 0x50 */
+#define DA7218_LVL_DET_EN_SHIFT 0
+#define DA7218_LVL_DET_EN_MASK (0xF << 0)
+#define DA7218_LVL_DET_EN_CHAN1L_SHIFT 0
+#define DA7218_LVL_DET_EN_CHAN1R_SHIFT 1
+#define DA7218_LVL_DET_EN_CHAN2L_SHIFT 2
+#define DA7218_LVL_DET_EN_CHAN2R_SHIFT 3
+
+/* DA7218_LVL_DET_LEVEL = 0x51 */
+#define DA7218_LVL_DET_LEVEL_SHIFT 0
+#define DA7218_LVL_DET_LEVEL_MASK (0x7F << 0)
+#define DA7218_LVL_DET_LEVEL_MAX 0x7F
+
+/* DA7218_DGS_TRIGGER = 0x54 */
+#define DA7218_DGS_TRIGGER_LVL_SHIFT 0
+#define DA7218_DGS_TRIGGER_LVL_MASK (0x3F << 0)
+#define DA7218_DGS_TRIGGER_MAX 0x3F
+
+/* DA7218_DGS_ENABLE = 0x55 */
+#define DA7218_DGS_ENABLE_SHIFT 0
+#define DA7218_DGS_ENABLE_MASK (0x3 << 0)
+#define DA7218_DGS_ENABLE_L_SHIFT 0
+#define DA7218_DGS_ENABLE_R_SHIFT 1
+
+/* DA7218_DGS_RISE_FALL = 0x56 */
+#define DA7218_DGS_RISE_COEFF_SHIFT 0
+#define DA7218_DGS_RISE_COEFF_MASK (0x7 << 0)
+#define DA7218_DGS_RISE_COEFF_MAX 7
+#define DA7218_DGS_FALL_COEFF_SHIFT 4
+#define DA7218_DGS_FALL_COEFF_MASK (0x7 << 4)
+#define DA7218_DGS_FALL_COEFF_MAX 8
+
+/* DA7218_DGS_SYNC_DELAY = 0x57 */
+#define DA7218_DGS_SYNC_DELAY_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY_MASK (0xFF << 0)
+#define DA7218_DGS_SYNC_DELAY_MAX 0xFF
+
+/* DA7218_DGS_SYNC_DELAY2 = 0x58 */
+#define DA7218_DGS_SYNC_DELAY2_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY2_MASK (0xFF << 0)
+
+/* DA7218_DGS_SYNC_DELAY3 = 0x59 */
+#define DA7218_DGS_SYNC_DELAY3_SHIFT 0
+#define DA7218_DGS_SYNC_DELAY3_MASK (0x7F << 0)
+#define DA7218_DGS_SYNC_DELAY3_MAX 0x7F
+
+/* DA7218_DGS_LEVELS = 0x5A */
+#define DA7218_DGS_ANTICLIP_LVL_SHIFT 0
+#define DA7218_DGS_ANTICLIP_LVL_MASK (0x7 << 0)
+#define DA7218_DGS_ANTICLIP_LVL_MAX 0x7
+#define DA7218_DGS_SIGNAL_LVL_SHIFT 4
+#define DA7218_DGS_SIGNAL_LVL_MASK (0xF << 4)
+#define DA7218_DGS_SIGNAL_LVL_MAX 0xF
+
+/* DA7218_DGS_GAIN_CTRL = 0x5B */
+#define DA7218_DGS_STEPS_SHIFT 0
+#define DA7218_DGS_STEPS_MASK (0x1F << 0)
+#define DA7218_DGS_STEPS_MAX 0x1F
+#define DA7218_DGS_RAMP_EN_SHIFT 5
+#define DA7218_DGS_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_DGS_SUBR_EN_SHIFT 6
+#define DA7218_DGS_SUBR_EN_MASK (0x1 << 6)
+
+/* DA7218_DROUTING_OUTDAI_1L = 0x5C */
+#define DA7218_OUTDAI_1L_SRC_SHIFT 0
+#define DA7218_OUTDAI_1L_SRC_MASK (0x7F << 0)
+#define DA7218_DMIX_SRC_INFILT1L 0
+#define DA7218_DMIX_SRC_INFILT1R 1
+#define DA7218_DMIX_SRC_INFILT2L 2
+#define DA7218_DMIX_SRC_INFILT2R 3
+#define DA7218_DMIX_SRC_TONEGEN 4
+#define DA7218_DMIX_SRC_DAIL 5
+#define DA7218_DMIX_SRC_DAIR 6
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN = 0x5D */
+#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_MASK (0x1F << 0)
+#define DA7218_DMIX_GAIN_MAX 0x1F
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN = 0x5E */
+#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN = 0x5F */
+#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN = 0x60 */
+#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN = 0x61 */
+#define DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN = 0x62 */
+#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN = 0x63 */
+#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_1R = 0x64 */
+#define DA7218_OUTDAI_1R_SRC_SHIFT 0
+#define DA7218_OUTDAI_1R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN = 0x65 */
+#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN = 0x66 */
+#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN = 0x67 */
+#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN = 0x68 */
+#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN = 0x69 */
+#define DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN = 0x6A */
+#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN = 0x6B */
+#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTFILT_1L = 0x6C */
+#define DA7218_OUTFILT_1L_SRC_SHIFT 0
+#define DA7218_OUTFILT_1L_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN = 0x6D */
+#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN = 0x6E */
+#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN = 0x6F */
+#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN = 0x70 */
+#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN = 0x71 */
+#define DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN = 0x72 */
+#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN = 0x73 */
+#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTFILT_1R = 0x74 */
+#define DA7218_OUTFILT_1R_SRC_SHIFT 0
+#define DA7218_OUTFILT_1R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN = 0x75 */
+#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN = 0x76 */
+#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN = 0x77 */
+#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN = 0x78 */
+#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN = 0x79 */
+#define DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN = 0x7A */
+#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN = 0x7B */
+#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_2L = 0x7C */
+#define DA7218_OUTDAI_2L_SRC_SHIFT 0
+#define DA7218_OUTDAI_2L_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN = 0x7D */
+#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN = 0x7E */
+#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN = 0x7F */
+#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN = 0x80 */
+#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN = 0x81 */
+#define DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN = 0x82 */
+#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN = 0x83 */
+#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_OUTDAI_2R = 0x84 */
+#define DA7218_OUTDAI_2R_SRC_SHIFT 0
+#define DA7218_OUTDAI_2R_SRC_MASK (0x7F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN = 0x85 */
+#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN = 0x86 */
+#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN = 0x87 */
+#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN = 0x88 */
+#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN = 0x89 */
+#define DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_TONEGEN_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN = 0x8A */
+#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN = 0x8B */
+#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT 0
+#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DAI_CTRL = 0x8C */
+#define DA7218_DAI_FORMAT_SHIFT 0
+#define DA7218_DAI_FORMAT_MASK (0x3 << 0)
+#define DA7218_DAI_FORMAT_I2S (0x0 << 0)
+#define DA7218_DAI_FORMAT_LEFT_J (0x1 << 0)
+#define DA7218_DAI_FORMAT_RIGHT_J (0x2 << 0)
+#define DA7218_DAI_FORMAT_DSP (0x3 << 0)
+#define DA7218_DAI_WORD_LENGTH_SHIFT 2
+#define DA7218_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7218_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
+#define DA7218_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
+#define DA7218_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
+#define DA7218_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
+#define DA7218_DAI_CH_NUM_SHIFT 4
+#define DA7218_DAI_CH_NUM_MASK (0x7 << 4)
+#define DA7218_DAI_CH_NUM_MAX 4
+#define DA7218_DAI_EN_SHIFT 7
+#define DA7218_DAI_EN_MASK (0x1 << 7)
+
+/* DA7218_DAI_TDM_CTRL = 0x8D */
+#define DA7218_DAI_TDM_CH_EN_SHIFT 0
+#define DA7218_DAI_TDM_CH_EN_MASK (0xF << 0)
+#define DA7218_DAI_TDM_MAX_SLOTS 4
+#define DA7218_DAI_OE_SHIFT 6
+#define DA7218_DAI_OE_MASK (0x1 << 6)
+#define DA7218_DAI_TDM_MODE_EN_SHIFT 7
+#define DA7218_DAI_TDM_MODE_EN_MASK (0x1 << 7)
+
+/* DA7218_DAI_OFFSET_LOWER = 0x8E */
+#define DA7218_DAI_OFFSET_LOWER_SHIFT 0
+#define DA7218_DAI_OFFSET_LOWER_MASK (0xFF << 0)
+
+/* DA7218_DAI_OFFSET_UPPER = 0x8F */
+#define DA7218_DAI_OFFSET_UPPER_SHIFT 0
+#define DA7218_DAI_OFFSET_UPPER_MASK (0x7 << 0)
+
+/* DA7218_DAI_CLK_MODE = 0x90 */
+#define DA7218_DAI_BCLKS_PER_WCLK_SHIFT 0
+#define DA7218_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
+#define DA7218_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
+#define DA7218_DAI_CLK_POL_SHIFT 2
+#define DA7218_DAI_CLK_POL_MASK (0x1 << 2)
+#define DA7218_DAI_CLK_POL_INV (0x1 << 2)
+#define DA7218_DAI_WCLK_POL_SHIFT 3
+#define DA7218_DAI_WCLK_POL_MASK (0x1 << 3)
+#define DA7218_DAI_WCLK_POL_INV (0x1 << 3)
+#define DA7218_DAI_WCLK_TRI_STATE_SHIFT 4
+#define DA7218_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7218_DAI_CLK_EN_SHIFT 7
+#define DA7218_DAI_CLK_EN_MASK (0x1 << 7)
+
+/* DA7218_PLL_CTRL = 0x91 */
+#define DA7218_PLL_INDIV_SHIFT 0
+#define DA7218_PLL_INDIV_MASK (0x7 << 0)
+#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0)
+#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0)
+#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0)
+#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0)
+#define DA7218_PLL_INDIV_40_54_MHZ (0x4 << 0)
+#define DA7218_PLL_INDIV_2_10_MHZ_VAL 2
+#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4
+#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8
+#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4
+#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4)
+#define DA7218_PLL_MODE_SHIFT 6
+#define DA7218_PLL_MODE_MASK (0x3 << 6)
+#define DA7218_PLL_MODE_BYPASS (0x0 << 6)
+#define DA7218_PLL_MODE_NORMAL (0x1 << 6)
+#define DA7218_PLL_MODE_SRM (0x2 << 6)
+#define DA7218_PLL_MODE_32KHZ (0x3 << 6)
+
+/* DA7218_PLL_FRAC_TOP = 0x92 */
+#define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0
+#define DA7218_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7218_PLL_FRAC_BOT = 0x93 */
+#define DA7218_PLL_FBDIV_FRAC_BOT_SHIFT 0
+#define DA7218_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7218_PLL_INTEGER = 0x94 */
+#define DA7218_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7218_PLL_FBDIV_INTEGER_MASK (0x7F << 0)
+
+/* DA7218_PLL_STATUS = 0x95 */
+#define DA7218_PLL_SRM_STATUS_SHIFT 0
+#define DA7218_PLL_SRM_STATUS_MASK (0xFF << 0)
+#define DA7218_PLL_SRM_STATUS_SRM_LOCK (0x1 << 7)
+
+/* DA7218_PLL_REFOSC_CAL = 0x98 */
+#define DA7218_PLL_REFOSC_CAL_CTRL_SHIFT 0
+#define DA7218_PLL_REFOSC_CAL_CTRL_MASK (0x1F << 0)
+#define DA7218_PLL_REFOSC_CAL_START_SHIFT 6
+#define DA7218_PLL_REFOSC_CAL_START_MASK (0x1 << 6)
+#define DA7218_PLL_REFOSC_CAL_EN_SHIFT 7
+#define DA7218_PLL_REFOSC_CAL_EN_MASK (0x1 << 7)
+
+/* DA7218_DAC_NG_CTRL = 0x9C */
+#define DA7218_DAC_NG_EN_SHIFT 7
+#define DA7218_DAC_NG_EN_MASK (0x1 << 7)
+
+/* DA7218_DAC_NG_SETUP_TIME = 0x9D */
+#define DA7218_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7218_DAC_NG_SETUP_TIME_MASK (0x3 << 0)
+#define DA7218_DAC_NG_SETUP_TIME_MAX 4
+#define DA7218_DAC_NG_RAMPUP_RATE_SHIFT 2
+#define DA7218_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7218_DAC_NG_RAMPUP_RATE_MAX 2
+#define DA7218_DAC_NG_RAMPDN_RATE_SHIFT 3
+#define DA7218_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7218_DAC_NG_RAMPDN_RATE_MAX 2
+
+/* DA7218_DAC_NG_OFF_THRESH = 0x9E */
+#define DA7218_DAC_NG_OFF_THRESHOLD_SHIFT 0
+#define DA7218_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0)
+#define DA7218_DAC_NG_THRESHOLD_MAX 0x7
+
+/* DA7218_DAC_NG_ON_THRESH = 0x9F */
+#define DA7218_DAC_NG_ON_THRESHOLD_SHIFT 0
+#define DA7218_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0)
+
+/* DA7218_TONE_GEN_CFG1 = 0xA0 */
+#define DA7218_DTMF_REG_SHIFT 0
+#define DA7218_DTMF_REG_MASK (0xF << 0)
+#define DA7218_DTMF_REG_MAX 16
+#define DA7218_DTMF_EN_SHIFT 4
+#define DA7218_DTMF_EN_MASK (0x1 << 4)
+#define DA7218_START_STOPN_SHIFT 7
+#define DA7218_START_STOPN_MASK (0x1 << 7)
+
+/* DA7218_TONE_GEN_CFG2 = 0xA1 */
+#define DA7218_SWG_SEL_SHIFT 0
+#define DA7218_SWG_SEL_MASK (0x3 << 0)
+#define DA7218_SWG_SEL_MAX 4
+
+/* DA7218_TONE_GEN_FREQ1_L = 0xA2 */
+#define DA7218_FREQ1_L_SHIFT 0
+#define DA7218_FREQ1_L_MASK (0xFF << 0)
+#define DA7218_FREQ_MAX 0xFFFF
+
+/* DA7218_TONE_GEN_FREQ1_U = 0xA3 */
+#define DA7218_FREQ1_U_SHIFT 0
+#define DA7218_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_FREQ2_L = 0xA4 */
+#define DA7218_FREQ2_L_SHIFT 0
+#define DA7218_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_FREQ2_U = 0xA5 */
+#define DA7218_FREQ2_U_SHIFT 0
+#define DA7218_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7218_TONE_GEN_CYCLES = 0xA6 */
+#define DA7218_BEEP_CYCLES_SHIFT 0
+#define DA7218_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7218_TONE_GEN_ON_PER = 0xA7 */
+#define DA7218_BEEP_ON_PER_SHIFT 0
+#define DA7218_BEEP_ON_PER_MASK (0x3F << 0)
+
+/* DA7218_TONE_GEN_OFF_PER = 0xA8 */
+#define DA7218_BEEP_OFF_PER_SHIFT 0
+#define DA7218_BEEP_OFF_PER_MASK (0x3F << 0)
+#define DA7218_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7218_CP_CTRL = 0xAC */
+#define DA7218_CP_MOD_SHIFT 2
+#define DA7218_CP_MOD_MASK (0x3 << 2)
+#define DA7218_CP_MCHANGE_SHIFT 4
+#define DA7218_CP_MCHANGE_MASK (0x3 << 4)
+#define DA7218_CP_MCHANGE_REL_MASK 0x3
+#define DA7218_CP_MCHANGE_MAX 3
+#define DA7218_CP_MCHANGE_LARGEST_VOL 0x1
+#define DA7218_CP_MCHANGE_DAC_VOL 0x2
+#define DA7218_CP_MCHANGE_SIG_MAG 0x3
+#define DA7218_CP_SMALL_SWITCH_FREQ_EN_SHIFT 6
+#define DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK (0x1 << 6)
+#define DA7218_CP_EN_SHIFT 7
+#define DA7218_CP_EN_MASK (0x1 << 7)
+
+/* DA7218_CP_DELAY = 0xAD */
+#define DA7218_CP_FCONTROL_SHIFT 0
+#define DA7218_CP_FCONTROL_MASK (0x7 << 0)
+#define DA7218_CP_FCONTROL_MAX 6
+#define DA7218_CP_TAU_DELAY_SHIFT 3
+#define DA7218_CP_TAU_DELAY_MASK (0x7 << 3)
+#define DA7218_CP_TAU_DELAY_MAX 8
+
+/* DA7218_CP_VOL_THRESHOLD1 = 0xAE */
+#define DA7218_CP_THRESH_VDD2_SHIFT 0
+#define DA7218_CP_THRESH_VDD2_MASK (0x3F << 0)
+#define DA7218_CP_THRESH_VDD2_MAX 0x3F
+
+/* DA7218_MIC_1_CTRL = 0xB4 */
+#define DA7218_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIC_1_AMP_EN_SHIFT 7
+#define DA7218_MIC_1_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIC_1_GAIN = 0xB5 */
+#define DA7218_MIC_1_AMP_GAIN_SHIFT 0
+#define DA7218_MIC_1_AMP_GAIN_MASK (0x7 << 0)
+#define DA7218_MIC_AMP_GAIN_MAX 0x7
+
+/* DA7218_MIC_1_SELECT = 0xB7 */
+#define DA7218_MIC_1_AMP_IN_SEL_SHIFT 0
+#define DA7218_MIC_1_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7218_MIC_2_CTRL = 0xB8 */
+#define DA7218_MIC_2_AMP_MUTE_EN_SHIFT 6
+#define DA7218_MIC_2_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_MIC_2_AMP_EN_SHIFT 7
+#define DA7218_MIC_2_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIC_2_GAIN = 0xB9 */
+#define DA7218_MIC_2_AMP_GAIN_SHIFT 0
+#define DA7218_MIC_2_AMP_GAIN_MASK (0x7 << 0)
+
+/* DA7218_MIC_2_SELECT = 0xBB */
+#define DA7218_MIC_2_AMP_IN_SEL_SHIFT 0
+#define DA7218_MIC_2_AMP_IN_SEL_MASK (0x3 << 0)
+
+/* DA7218_IN_1_HPF_FILTER_CTRL = 0xBC */
+#define DA7218_IN_1_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_IN_1_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_IN_VOICE_HPF_CORNER_MAX 8
+#define DA7218_IN_1_VOICE_EN_SHIFT 3
+#define DA7218_IN_1_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_IN_1_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_IN_1_HPF_EN_SHIFT 7
+#define DA7218_IN_1_HPF_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_2_HPF_FILTER_CTRL = 0xBD */
+#define DA7218_IN_2_VOICE_HPF_CORNER_SHIFT 0
+#define DA7218_IN_2_VOICE_HPF_CORNER_MASK (0x7 << 0)
+#define DA7218_IN_2_VOICE_EN_SHIFT 3
+#define DA7218_IN_2_VOICE_EN_MASK (0x1 << 3)
+#define DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT 4
+#define DA7218_IN_2_AUDIO_HPF_CORNER_MASK (0x3 << 4)
+#define DA7218_IN_2_HPF_EN_SHIFT 7
+#define DA7218_IN_2_HPF_EN_MASK (0x1 << 7)
+
+/* DA7218_ADC_1_CTRL = 0xC0 */
+#define DA7218_ADC_1_AAF_EN_SHIFT 2
+#define DA7218_ADC_1_AAF_EN_MASK (0x1 << 2)
+
+/* DA7218_ADC_2_CTRL = 0xC1 */
+#define DA7218_ADC_2_AAF_EN_SHIFT 2
+#define DA7218_ADC_2_AAF_EN_MASK (0x1 << 2)
+
+/* DA7218_ADC_MODE = 0xC2 */
+#define DA7218_ADC_LP_MODE_SHIFT 0
+#define DA7218_ADC_LP_MODE_MASK (0x1 << 0)
+#define DA7218_ADC_LVLDET_MODE_SHIFT 1
+#define DA7218_ADC_LVLDET_MODE_MASK (0x1 << 1)
+#define DA7218_ADC_LVLDET_AUTO_EXIT_SHIFT 2
+#define DA7218_ADC_LVLDET_AUTO_EXIT_MASK (0x1 << 2)
+
+/* DA7218_MIXOUT_L_CTRL = 0xCC */
+#define DA7218_MIXOUT_L_AMP_EN_SHIFT 7
+#define DA7218_MIXOUT_L_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXOUT_L_GAIN = 0xCD */
+#define DA7218_MIXOUT_L_AMP_GAIN_SHIFT 0
+#define DA7218_MIXOUT_L_AMP_GAIN_MASK (0x3 << 0)
+#define DA7218_MIXOUT_AMP_GAIN_MIN 0x1
+#define DA7218_MIXOUT_AMP_GAIN_MAX 0x3
+
+/* DA7218_MIXOUT_R_CTRL = 0xCE */
+#define DA7218_MIXOUT_R_AMP_EN_SHIFT 7
+#define DA7218_MIXOUT_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_MIXOUT_R_GAIN = 0xCF */
+#define DA7218_MIXOUT_R_AMP_GAIN_SHIFT 0
+#define DA7218_MIXOUT_R_AMP_GAIN_MASK (0x3 << 0)
+
+/* DA7218_HP_L_CTRL = 0xD0 */
+#define DA7218_HP_L_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7218_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7218_HP_L_AMP_OE_SHIFT 3
+#define DA7218_HP_L_AMP_OE_MASK (0x1 << 3)
+#define DA7218_HP_L_AMP_ZC_EN_SHIFT 4
+#define DA7218_HP_L_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_HP_L_AMP_RAMP_EN_SHIFT 5
+#define DA7218_HP_L_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_HP_L_AMP_MUTE_EN_SHIFT 6
+#define DA7218_HP_L_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_HP_L_AMP_EN_SHIFT 7
+#define DA7218_HP_L_AMP_EN_MASK (0x1 << 7)
+#define DA7218_HP_AMP_OE_MASK (0x1 << 3)
+
+/* DA7218_HP_L_GAIN = 0xD1 */
+#define DA7218_HP_L_AMP_GAIN_SHIFT 0
+#define DA7218_HP_L_AMP_GAIN_MASK (0x3F << 0)
+#define DA7218_HP_AMP_GAIN_MIN 0x15
+#define DA7218_HP_AMP_GAIN_MAX 0x3F
+
+/* DA7218_HP_R_CTRL = 0xD2 */
+#define DA7218_HP_R_AMP_MIN_GAIN_EN_SHIFT 2
+#define DA7218_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2)
+#define DA7218_HP_R_AMP_OE_SHIFT 3
+#define DA7218_HP_R_AMP_OE_MASK (0x1 << 3)
+#define DA7218_HP_R_AMP_ZC_EN_SHIFT 4
+#define DA7218_HP_R_AMP_ZC_EN_MASK (0x1 << 4)
+#define DA7218_HP_R_AMP_RAMP_EN_SHIFT 5
+#define DA7218_HP_R_AMP_RAMP_EN_MASK (0x1 << 5)
+#define DA7218_HP_R_AMP_MUTE_EN_SHIFT 6
+#define DA7218_HP_R_AMP_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_HP_R_AMP_EN_SHIFT 7
+#define DA7218_HP_R_AMP_EN_MASK (0x1 << 7)
+
+/* DA7218_HP_R_GAIN = 0xD3 */
+#define DA7218_HP_R_AMP_GAIN_SHIFT 0
+#define DA7218_HP_R_AMP_GAIN_MASK (0x3F << 0)
+
+/* DA7218_HP_SNGL_CTRL = 0xD4 */
+#define DA7218_HP_AMP_STEREO_DETECT_STATUS_SHIFT 0
+#define DA7218_HP_AMP_STEREO_DETECT_STATUS_MASK (0x1 << 0)
+#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_SHIFT 1
+#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 1)
+#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_SHIFT 2
+#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 2)
+#define DA7218_HP_AMP_LOAD_DETECT_EN_SHIFT 6
+#define DA7218_HP_AMP_LOAD_DETECT_EN_MASK (0x1 << 6)
+#define DA7218_HP_AMP_STEREO_DETECT_EN_SHIFT 7
+#define DA7218_HP_AMP_STEREO_DETECT_EN_MASK (0x1 << 7)
+
+/* DA7218_HP_DIFF_CTRL = 0xD5 */
+#define DA7218_HP_AMP_DIFF_MODE_EN_SHIFT 0
+#define DA7218_HP_AMP_DIFF_MODE_EN_MASK (0x1 << 0)
+#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_SHIFT 4
+#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK (0x1 << 4)
+
+/* DA7218_HP_DIFF_UNLOCK = 0xD7 */
+#define DA7218_HP_DIFF_UNLOCK_SHIFT 0
+#define DA7218_HP_DIFF_UNLOCK_MASK (0x1 << 0)
+#define DA7218_HP_DIFF_UNLOCK_VAL 0xC3
+
+/* DA7218_HPLDET_JACK = 0xD8 */
+#define DA7218_HPLDET_JACK_RATE_SHIFT 0
+#define DA7218_HPLDET_JACK_RATE_MASK (0x7 << 0)
+#define DA7218_HPLDET_JACK_DEBOUNCE_SHIFT 3
+#define DA7218_HPLDET_JACK_DEBOUNCE_MASK (0x3 << 3)
+#define DA7218_HPLDET_JACK_THR_SHIFT 5
+#define DA7218_HPLDET_JACK_THR_MASK (0x3 << 5)
+#define DA7218_HPLDET_JACK_EN_SHIFT 7
+#define DA7218_HPLDET_JACK_EN_MASK (0x1 << 7)
+
+/* DA7218_HPLDET_CTRL = 0xD9 */
+#define DA7218_HPLDET_COMP_INV_SHIFT 0
+#define DA7218_HPLDET_COMP_INV_MASK (0x1 << 0)
+#define DA7218_HPLDET_HYST_EN_SHIFT 1
+#define DA7218_HPLDET_HYST_EN_MASK (0x1 << 1)
+#define DA7218_HPLDET_DISCHARGE_EN_SHIFT 7
+#define DA7218_HPLDET_DISCHARGE_EN_MASK (0x1 << 7)
+
+/* DA7218_HPLDET_TEST = 0xDA */
+#define DA7218_HPLDET_COMP_STS_SHIFT 4
+#define DA7218_HPLDET_COMP_STS_MASK (0x1 << 4)
+
+/* DA7218_REFERENCES = 0xDC */
+#define DA7218_BIAS_EN_SHIFT 3
+#define DA7218_BIAS_EN_MASK (0x1 << 3)
+
+/* DA7218_IO_CTRL = 0xE0 */
+#define DA7218_IO_VOLTAGE_LEVEL_SHIFT 0
+#define DA7218_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
+#define DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V 0
+#define DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V 1
+
+/* DA7218_LDO_CTRL = 0xE1 */
+#define DA7218_LDO_LEVEL_SELECT_SHIFT 4
+#define DA7218_LDO_LEVEL_SELECT_MASK (0x3 << 4)
+#define DA7218_LDO_EN_SHIFT 7
+#define DA7218_LDO_EN_MASK (0x1 << 7)
+
+/* DA7218_SIDETONE_CTRL = 0xE4 */
+#define DA7218_SIDETONE_MUTE_EN_SHIFT 6
+#define DA7218_SIDETONE_MUTE_EN_MASK (0x1 << 6)
+#define DA7218_SIDETONE_FILTER_EN_SHIFT 7
+#define DA7218_SIDETONE_FILTER_EN_MASK (0x1 << 7)
+
+/* DA7218_SIDETONE_IN_SELECT = 0xE5 */
+#define DA7218_SIDETONE_IN_SELECT_SHIFT 0
+#define DA7218_SIDETONE_IN_SELECT_MASK (0x3 << 0)
+#define DA7218_SIDETONE_IN_SELECT_MAX 4
+
+/* DA7218_SIDETONE_GAIN = 0xE6 */
+#define DA7218_SIDETONE_GAIN_SHIFT 0
+#define DA7218_SIDETONE_GAIN_MASK (0x1F << 0)
+
+/* DA7218_DROUTING_ST_OUTFILT_1L = 0xE8 */
+#define DA7218_OUTFILT_ST_1L_SRC_SHIFT 0
+#define DA7218_OUTFILT_ST_1L_SRC_MASK (0x7 << 0)
+#define DA7218_DMIX_ST_SRC_OUTFILT1L 0
+#define DA7218_DMIX_ST_SRC_OUTFILT1R 1
+#define DA7218_DMIX_ST_SRC_SIDETONE 2
+
+/* DA7218_DROUTING_ST_OUTFILT_1R = 0xE9 */
+#define DA7218_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7218_OUTFILT_ST_1R_SRC_MASK (0x7 << 0)
+
+/* DA7218_SIDETONE_BIQ_3STAGE_DATA = 0xEA */
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA_SHIFT 0
+#define DA7218_SIDETONE_BIQ_3STAGE_DATA_MASK (0xFF << 0)
+
+/* DA7218_SIDETONE_BIQ_3STAGE_ADDR = 0xEB */
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_SHIFT 0
+#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_MASK (0x1F << 0)
+#define DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE 30
+
+/* DA7218_EVENT_STATUS = 0xEC */
+#define DA7218_HPLDET_JACK_STS_SHIFT 7
+#define DA7218_HPLDET_JACK_STS_MASK (0x1 << 7)
+
+/* DA7218_EVENT = 0xED */
+#define DA7218_LVL_DET_EVENT_SHIFT 0
+#define DA7218_LVL_DET_EVENT_MASK (0x1 << 0)
+#define DA7218_HPLDET_JACK_EVENT_SHIFT 7
+#define DA7218_HPLDET_JACK_EVENT_MASK (0x1 << 7)
+
+/* DA7218_EVENT_MASK = 0xEE */
+#define DA7218_LVL_DET_EVENT_MSK_SHIFT 0
+#define DA7218_LVL_DET_EVENT_MSK_MASK (0x1 << 0)
+#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_SHIFT 7
+#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK (0x1 << 7)
+
+/* DA7218_DMIC_1_CTRL = 0xF0 */
+#define DA7218_DMIC_1_DATA_SEL_SHIFT 0
+#define DA7218_DMIC_1_DATA_SEL_MASK (0x1 << 0)
+#define DA7218_DMIC_1_SAMPLEPHASE_SHIFT 1
+#define DA7218_DMIC_1_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7218_DMIC_1_CLK_RATE_SHIFT 2
+#define DA7218_DMIC_1_CLK_RATE_MASK (0x1 << 2)
+#define DA7218_DMIC_1L_EN_SHIFT 6
+#define DA7218_DMIC_1L_EN_MASK (0x1 << 6)
+#define DA7218_DMIC_1R_EN_SHIFT 7
+#define DA7218_DMIC_1R_EN_MASK (0x1 << 7)
+
+/* DA7218_DMIC_2_CTRL = 0xF1 */
+#define DA7218_DMIC_2_DATA_SEL_SHIFT 0
+#define DA7218_DMIC_2_DATA_SEL_MASK (0x1 << 0)
+#define DA7218_DMIC_2_SAMPLEPHASE_SHIFT 1
+#define DA7218_DMIC_2_SAMPLEPHASE_MASK (0x1 << 1)
+#define DA7218_DMIC_2_CLK_RATE_SHIFT 2
+#define DA7218_DMIC_2_CLK_RATE_MASK (0x1 << 2)
+#define DA7218_DMIC_2L_EN_SHIFT 6
+#define DA7218_DMIC_2L_EN_MASK (0x1 << 6)
+#define DA7218_DMIC_2R_EN_SHIFT 7
+#define DA7218_DMIC_2R_EN_MASK (0x1 << 7)
+
+/* DA7218_IN_1L_GAIN = 0xF4 */
+#define DA7218_IN_1L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_1L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7218_IN_DIGITAL_GAIN_MAX 0x7F
+
+/* DA7218_IN_1R_GAIN = 0xF5 */
+#define DA7218_IN_1R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_1R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_IN_2L_GAIN = 0xF6 */
+#define DA7218_IN_2L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_2L_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_IN_2R_GAIN = 0xF7 */
+#define DA7218_IN_2R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_IN_2R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7218_OUT_1L_GAIN = 0xF8 */
+#define DA7218_OUT_1L_DIGITAL_GAIN_SHIFT 0
+#define DA7218_OUT_1L_DIGITAL_GAIN_MASK (0xFF << 0)
+#define DA7218_OUT_DIGITAL_GAIN_MIN 0x0
+#define DA7218_OUT_DIGITAL_GAIN_MAX 0x97
+
+/* DA7218_OUT_1R_GAIN = 0xF9 */
+#define DA7218_OUT_1R_DIGITAL_GAIN_SHIFT 0
+#define DA7218_OUT_1R_DIGITAL_GAIN_MASK (0xFF << 0)
+
+/* DA7218_MICBIAS_CTRL = 0xFC */
+#define DA7218_MICBIAS_1_LEVEL_SHIFT 0
+#define DA7218_MICBIAS_1_LEVEL_MASK (0x7 << 0)
+#define DA7218_MICBIAS_1_LP_MODE_SHIFT 3
+#define DA7218_MICBIAS_1_LP_MODE_MASK (0x1 << 3)
+#define DA7218_MICBIAS_2_LEVEL_SHIFT 4
+#define DA7218_MICBIAS_2_LEVEL_MASK (0x7 << 4)
+#define DA7218_MICBIAS_2_LP_MODE_SHIFT 7
+#define DA7218_MICBIAS_2_LP_MODE_MASK (0x1 << 7)
+
+/* DA7218_MICBIAS_EN = 0xFD */
+#define DA7218_MICBIAS_1_EN_SHIFT 0
+#define DA7218_MICBIAS_1_EN_MASK (0x1 << 0)
+#define DA7218_MICBIAS_2_EN_SHIFT 4
+#define DA7218_MICBIAS_2_EN_MASK (0x1 << 4)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7218_NO_INVERT 0
+#define DA7218_INVERT 1
+
+/* Byte related defines */
+#define DA7218_BYTE_SHIFT 8
+#define DA7218_BYTE_MASK 0xFF
+#define DA7218_2BYTE_SHIFT 16
+#define DA7218_2BYTE_MASK 0xFFFF
+
+/* PLL Output Frequencies */
+#define DA7218_PLL_FREQ_OUT_90316 90316800
+#define DA7218_PLL_FREQ_OUT_98304 98304000
+
+/* ALC Calibration */
+#define DA7218_ALC_CALIB_DELAY_MIN 2500
+#define DA7218_ALC_CALIB_DELAY_MAX 5000
+#define DA7218_ALC_CALIB_MAX_TRIES 5
+
+/* Ref Oscillator */
+#define DA7218_REF_OSC_CHECK_DELAY_MIN 5000
+#define DA7218_REF_OSC_CHECK_DELAY_MAX 10000
+#define DA7218_REF_OSC_CHECK_TRIES 4
+
+/* SRM */
+#define DA7218_SRM_CHECK_DELAY 50
+#define DA7218_SRM_CHECK_TRIES 8
+
+/* Mic Level Detect */
+#define DA7218_MIC_LVL_DET_DELAY 50
+
+enum da7218_biq_cfg {
+ DA7218_BIQ_CFG_DATA = 0,
+ DA7218_BIQ_CFG_ADDR,
+ DA7218_BIQ_CFG_SIZE,
+};
+
+enum da7218_clk_src {
+ DA7218_CLKSRC_MCLK = 0,
+ DA7218_CLKSRC_MCLK_SQR,
+};
+
+enum da7218_sys_clk {
+ DA7218_SYSCLK_MCLK = 0,
+ DA7218_SYSCLK_PLL,
+ DA7218_SYSCLK_PLL_SRM,
+ DA7218_SYSCLK_PLL_32KHZ
+};
+
+enum da7218_dev_id {
+ DA7217_DEV_ID = 0,
+ DA7218_DEV_ID,
+};
+
+/* Regulators */
+enum da7218_supplies {
+ DA7218_SUPPLY_VDD = 0,
+ DA7218_SUPPLY_VDDMIC,
+ DA7218_SUPPLY_VDDIO,
+ DA7218_NUM_SUPPLIES,
+};
+
+/* Private data */
+struct da7218_priv {
+ struct da7218_pdata *pdata;
+
+ struct regulator_bulk_data supplies[DA7218_NUM_SUPPLIES];
+ struct regmap *regmap;
+ int dev_id;
+
+ struct snd_soc_jack *jack;
+ int irq;
+
+ struct clk *mclk;
+ unsigned int mclk_rate;
+
+ bool hp_single_supply;
+ bool master;
+ u8 alc_en;
+ u8 in_filt_en;
+ u8 mic_lvl_det_en;
+
+ u8 biq_5stage_coeff[DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE];
+ u8 stbiq_3stage_coeff[DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE];
+};
+
+/* HP detect control */
+int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif /* _DA7218_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index f238c1e8a69c..c6d3b32bb4ae 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -968,10 +968,11 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixin PGA", NULL, "Mic PGA"},
{"ADC", NULL, "Mixin PGA"},
- {"Sidetone Filter", NULL, "ADC"},
{"Mixer In", NULL, "Mixer In Supply"},
{"Mixer In", "Mic Switch", "ADC"},
+ {"Sidetone Filter", NULL, "Mixer In"},
+
{"Tone Generator", NULL, "TONE"},
DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
@@ -1073,11 +1074,8 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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) {
+ /* Verify 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7219->mclk_rate < 2000000) {
dev_err(codec->dev, "PLL input clock %d below valid range\n",
da7219->mclk_rate);
return -EINVAL;
@@ -1118,9 +1116,6 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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;
@@ -1306,7 +1301,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
}
channels = params_channels(params);
- if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
+ 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);
@@ -1405,28 +1400,12 @@ static const struct of_device_id da7219_of_match[] = {
};
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 1600:
+ return DA7219_MICBIAS_1_6V;
case 1800:
return DA7219_MICBIAS_1_8V;
case 2000:
@@ -1469,9 +1448,6 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
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
@@ -1516,24 +1492,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
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 */
+ /* Only disable master bias if jack detection not active */
+ if (!da7219->aad->jack)
snd_soc_update_bits(codec, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK, 0);
- }
/* MCLK */
if (da7219->mclk)
@@ -1600,21 +1565,9 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
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_6V:
case DA7219_MICBIAS_1_8V:
case DA7219_MICBIAS_2_0V:
case DA7219_MICBIAS_2_2V:
@@ -1662,10 +1615,12 @@ static int da7219_probe(struct snd_soc_codec *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
+ if (PTR_ERR(da7219->mclk) != -ENOENT) {
+ ret = PTR_ERR(da7219->mclk);
+ goto err_disable_reg;
+ } else {
da7219->mclk = NULL;
+ }
}
/* Default PC counter to free-running */
@@ -1693,7 +1648,16 @@ static int da7219_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
/* Initialise AAD block */
- return da7219_aad_init(codec);
+ ret = da7219_aad_init(codec);
+ if (ret)
+ goto err_disable_reg;
+
+ return 0;
+
+err_disable_reg:
+ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+
+ return ret;
}
static int da7219_remove(struct snd_soc_codec *codec)
@@ -1776,7 +1740,7 @@ static struct reg_default da7219_reg_defaults[] = {
{ DA7219_DIG_ROUTING_DAC, 0x32 },
{ DA7219_DAI_OFFSET_LOWER, 0x00 },
{ DA7219_DAI_OFFSET_UPPER, 0x00 },
- { DA7219_REFERENCES, 0x00 },
+ { DA7219_REFERENCES, 0x08 },
{ DA7219_MIXIN_L_SELECT, 0x00 },
{ DA7219_MIXIN_L_GAIN, 0x03 },
{ DA7219_ADC_L_GAIN, 0x6F },
@@ -1811,7 +1775,6 @@ static struct reg_default da7219_reg_defaults[] = {
{ 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 },
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index b514268c6c56..5a787e738084 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -85,7 +85,6 @@
#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
@@ -207,7 +206,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
@@ -569,12 +567,6 @@
#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)
@@ -787,7 +779,6 @@ enum da7219_sys_clk {
DA7219_SYSCLK_MCLK = 0,
DA7219_SYSCLK_PLL,
DA7219_SYSCLK_PLL_SRM,
- DA7219_SYSCLK_PLL_32KHZ
};
/* Regulators */
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 969e337dc17c..afa6c5db9dcc 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -85,7 +85,15 @@ static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
-static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+static const struct {
+ int rate;
+ unsigned int val;
+} deemph_settings[] = {
+ { 0, ES8328_DACCONTROL6_DEEMPH_OFF },
+ { 32000, ES8328_DACCONTROL6_DEEMPH_32k },
+ { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k },
+ { 48000, ES8328_DACCONTROL6_DEEMPH_48k },
+};
static int es8328_set_deemph(struct snd_soc_codec *codec)
{
@@ -97,21 +105,22 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
* rate.
*/
if (es8328->deemph) {
- best = 1;
- for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
- if (abs(deemph_settings[i] - es8328->playback_fs) <
- abs(deemph_settings[best] - es8328->playback_fs))
+ best = 0;
+ for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i].rate - es8328->playback_fs) <
+ abs(deemph_settings[best].rate - es8328->playback_fs))
best = i;
}
- val = best << 1;
+ val = deemph_settings[best].val;
} else {
- val = 0;
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
+ return snd_soc_update_bits(codec, ES8328_DACCONTROL6,
+ ES8328_DACCONTROL6_DEEMPH_MASK, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
@@ -205,18 +214,18 @@ static const struct snd_kcontrol_new es8328_right_line_controls =
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
- SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
static const char * const es8328_pga_sel[] = {
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
index cb36afe10c0e..156c748c89c7 100644
--- a/sound/soc/codecs/es8328.h
+++ b/sound/soc/codecs/es8328.h
@@ -153,6 +153,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6)
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 7fc7b4e3f444..c1b87c5800b1 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1271,6 +1271,36 @@ static int nau8825_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int nau8825_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ disable_irq(client->irq);
+ regcache_cache_only(nau8825->regmap, true);
+ regcache_mark_dirty(nau8825->regmap);
+
+ return 0;
+}
+
+static int nau8825_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ regcache_cache_only(nau8825->regmap, false);
+ regcache_sync(nau8825->regmap);
+ enable_irq(client->irq);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops nau8825_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume)
+};
+
static const struct i2c_device_id nau8825_i2c_ids[] = {
{ "nau8825", 0 },
{ }
@@ -1297,6 +1327,7 @@ static struct i2c_driver nau8825_driver = {
.name = "nau8825",
.of_match_table = of_match_ptr(nau8825_of_ids),
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ .pm = &nau8825_pm,
},
.probe = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
new file mode 100644
index 000000000000..6feb0901dfeb
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -0,0 +1,66 @@
+/*
+ * PCM3168A codec i2c driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm3168a_probe(&i2c->dev, regmap);
+}
+
+static int pcm3168a_i2c_remove(struct i2c_client *i2c)
+{
+ pcm3168a_remove(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id pcm3168a_i2c_id[] = {
+ { "pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+ { .compatible = "ti,pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct i2c_driver pcm3168a_i2c_driver = {
+ .probe = pcm3168a_i2c_probe,
+ .remove = pcm3168a_i2c_remove,
+ .id_table = pcm3168a_i2c_id,
+ .driver = {
+ .name = "pcm3168a",
+ .of_match_table = pcm3168a_of_match,
+ .pm = &pcm3168a_pm_ops,
+ },
+};
+module_i2c_driver(pcm3168a_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3168A I2C codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
new file mode 100644
index 000000000000..03945a27ae40
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a-spi.c
@@ -0,0 +1,65 @@
+/*
+ * PCM3168A codec spi driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm3168a_probe(&spi->dev, regmap);
+}
+
+static int pcm3168a_spi_remove(struct spi_device *spi)
+{
+ pcm3168a_remove(&spi->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id pcm3168a_spi_id[] = {
+ { "pcm3168a", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+ { .compatible = "ti,pcm3168a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct spi_driver pcm3168a_spi_driver = {
+ .probe = pcm3168a_spi_probe,
+ .remove = pcm3168a_spi_remove,
+ .id_table = pcm3168a_spi_id,
+ .driver = {
+ .name = "pcm3168a",
+ .of_match_table = pcm3168a_of_match,
+ .pm = &pcm3168a_pm_ops,
+ },
+};
+module_spi_driver(pcm3168a_spi_driver);
+
+MODULE_DESCRIPTION("PCM3168A SPI codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
new file mode 100644
index 000000000000..44b268aa4dd8
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a.c
@@ -0,0 +1,767 @@
+/*
+ * PCM3168A codec driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3168a.h"
+
+#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM3168A_FMT_I2S 0x0
+#define PCM3168A_FMT_LEFT_J 0x1
+#define PCM3168A_FMT_RIGHT_J 0x2
+#define PCM3168A_FMT_RIGHT_J_16 0x3
+#define PCM3168A_FMT_DSP_A 0x4
+#define PCM3168A_FMT_DSP_B 0x5
+#define PCM3168A_FMT_DSP_MASK 0x4
+
+#define PCM3168A_NUM_SUPPLIES 6
+static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+ "VDD1",
+ "VDD2",
+ "VCCAD1",
+ "VCCAD2",
+ "VCCDA1",
+ "VCCDA2"
+};
+
+struct pcm3168a_priv {
+ struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct clk *scki;
+ bool adc_master_mode;
+ bool dac_master_mode;
+ unsigned long sysclk;
+ unsigned int adc_fmt;
+ unsigned int dac_fmt;
+};
+
+static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
+
+static const char *const pcm3168a_volume_type[] = {
+ "Individual", "Master + Individual" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
+
+static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
+
+static const char *const pcm3168a_demp[] = {
+ "Disabled", "48khz", "44.1khz", "32khz" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
+
+static const char *const pcm3168a_zf_func[] = {
+ "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
+ "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
+
+static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
+ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
+
+static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
+
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
+ 0, 1, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
+ 2, 3, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
+ 4, 5, pcm3168a_con);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
+ PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
+
+/* -100db to 0db, register values 0-54 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
+
+/* -100db to 20db, register values 0-14 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
+ SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
+ PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
+ SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
+ SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
+ SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
+ SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
+ SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
+ SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
+ SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
+ SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
+ SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
+ SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
+ SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
+ SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
+ SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
+ SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
+ SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
+ SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
+ SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
+ SOC_SINGLE_RANGE_TLV("Master Playback Volume",
+ PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
+ pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START,
+ PCM3168A_DAC_VOL_CHAN_START + 1,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 2,
+ PCM3168A_DAC_VOL_CHAN_START + 3,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 4,
+ PCM3168A_DAC_VOL_CHAN_START + 5,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
+ PCM3168A_DAC_VOL_CHAN_START + 6,
+ PCM3168A_DAC_VOL_CHAN_START + 7,
+ 0, 54, 255, 0, pcm3168a_dac_tlv),
+ SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
+ SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
+ SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
+ SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
+ SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
+ SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
+ SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
+ SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
+ SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
+ SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
+ SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
+ SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
+ SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
+ SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
+ SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
+ SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
+ SOC_SINGLE_RANGE_TLV("Master Capture Volume",
+ PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
+ pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START,
+ PCM3168A_ADC_VOL_CHAN_START + 1,
+ 0, 14, 255, 0, pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START + 2,
+ PCM3168A_ADC_VOL_CHAN_START + 3,
+ 0, 14, 255, 0, pcm3168a_adc_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
+ PCM3168A_ADC_VOL_CHAN_START + 4,
+ PCM3168A_ADC_VOL_CHAN_START + 5,
+ 0, 14, 255, 0, pcm3168a_adc_tlv)
+};
+
+static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT, 1),
+ SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
+ SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
+ SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
+ PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+ SND_SOC_DAPM_OUTPUT("AOUT3L"),
+ SND_SOC_DAPM_OUTPUT("AOUT3R"),
+ SND_SOC_DAPM_OUTPUT("AOUT4L"),
+ SND_SOC_DAPM_OUTPUT("AOUT4R"),
+
+ SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
+ SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
+ PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
+
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN3L"),
+ SND_SOC_DAPM_INPUT("AIN3R")
+};
+
+static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
+ /* Playback */
+ { "AOUT1L", NULL, "DAC1" },
+ { "AOUT1R", NULL, "DAC1" },
+
+ { "AOUT2L", NULL, "DAC2" },
+ { "AOUT2R", NULL, "DAC2" },
+
+ { "AOUT3L", NULL, "DAC3" },
+ { "AOUT3R", NULL, "DAC3" },
+
+ { "AOUT4L", NULL, "DAC4" },
+ { "AOUT4R", NULL, "DAC4" },
+
+ /* Capture */
+ { "ADC1", NULL, "AIN1L" },
+ { "ADC1", NULL, "AIN1R" },
+
+ { "ADC2", NULL, "AIN2L" },
+ { "ADC2", NULL, "AIN2R" },
+
+ { "ADC3", NULL, "AIN3L" },
+ { "ADC3", NULL, "AIN3R" }
+};
+
+static unsigned int pcm3168a_scki_ratios[] = {
+ 768,
+ 512,
+ 384,
+ 256,
+ 192,
+ 128
+};
+
+#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
+#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
+
+#define PCM1368A_MAX_SYSCLK 36864000
+
+static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
+{
+ int ret;
+
+ ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
+ if (ret)
+ return ret;
+
+ /* Internal reset is de-asserted after 3846 SCKI cycles */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+
+ return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
+ PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
+}
+
+static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+
+ regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
+
+ return 0;
+}
+
+static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (freq > PCM1368A_MAX_SYSCLK)
+ return -EINVAL;
+
+ pcm3168a->sysclk = freq;
+
+ return 0;
+}
+
+static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format, bool dac)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+ u32 fmt, reg, mask, shift;
+ bool master_mode;
+
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = PCM3168A_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ fmt = PCM3168A_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ fmt = PCM3168A_FMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ fmt = PCM3168A_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ fmt = PCM3168A_FMT_DSP_B;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported dai format\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master_mode = false;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master_mode = true;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (dac) {
+ reg = PCM3168A_DAC_PWR_MST_FMT;
+ mask = PCM3168A_DAC_FMT_MASK;
+ shift = PCM3168A_DAC_FMT_SHIFT;
+ pcm3168a->dac_master_mode = master_mode;
+ pcm3168a->dac_fmt = fmt;
+ } else {
+ reg = PCM3168A_ADC_MST_FMT;
+ mask = PCM3168A_ADC_FMTAD_MASK;
+ shift = PCM3168A_ADC_FMTAD_SHIFT;
+ pcm3168a->adc_master_mode = master_mode;
+ pcm3168a->adc_fmt = fmt;
+ }
+
+ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+ return 0;
+}
+
+static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
+ unsigned int format)
+{
+ return pcm3168a_set_dai_fmt(dai, format, true);
+}
+
+static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
+ unsigned int format)
+{
+ return pcm3168a_set_dai_fmt(dai, format, false);
+}
+
+static int pcm3168a_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 pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+ bool tx, master_mode;
+ u32 val, mask, shift, reg;
+ unsigned int rate, channels, fmt, ratio, max_ratio;
+ int i, min_frame_size;
+ snd_pcm_format_t format;
+
+ rate = params_rate(params);
+ format = params_format(params);
+ channels = params_channels(params);
+
+ ratio = pcm3168a->sysclk / rate;
+
+ tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ if (tx) {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+ reg = PCM3168A_DAC_PWR_MST_FMT;
+ mask = PCM3168A_DAC_MSDA_MASK;
+ shift = PCM3168A_DAC_MSDA_SHIFT;
+ master_mode = pcm3168a->dac_master_mode;
+ fmt = pcm3168a->dac_fmt;
+ } else {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+ reg = PCM3168A_ADC_MST_FMT;
+ mask = PCM3168A_ADC_MSAD_MASK;
+ shift = PCM3168A_ADC_MSAD_SHIFT;
+ master_mode = pcm3168a->adc_master_mode;
+ fmt = pcm3168a->adc_fmt;
+ }
+
+ for (i = 0; i < max_ratio; i++) {
+ if (pcm3168a_scki_ratios[i] == ratio)
+ break;
+ }
+
+ if (i == max_ratio) {
+ dev_err(codec->dev, "unsupported sysclk ratio\n");
+ return -EINVAL;
+ }
+
+ min_frame_size = params_width(params) * 2;
+ switch (min_frame_size) {
+ case 32:
+ if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
+ dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
+ return -EINVAL;
+ }
+ fmt = PCM3168A_FMT_RIGHT_J_16;
+ break;
+ case 48:
+ if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
+ dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+ return -EINVAL;
+ }
+ break;
+ case 64:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+ return -EINVAL;
+ }
+
+ if (master_mode)
+ val = ((i + 1) << shift);
+ else
+ val = 0;
+
+ regmap_update_bits(pcm3168a->regmap, reg, mask, val);
+
+ if (tx) {
+ mask = PCM3168A_DAC_FMT_MASK;
+ shift = PCM3168A_DAC_FMT_SHIFT;
+ } else {
+ mask = PCM3168A_ADC_FMTAD_MASK;
+ shift = PCM3168A_ADC_FMTAD_SHIFT;
+ }
+
+ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt_dac,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
+ .hw_params = pcm3168a_hw_params,
+ .digital_mute = pcm3168a_digital_mute
+};
+
+static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt_adc,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
+ .hw_params = pcm3168a_hw_params
+};
+
+static struct snd_soc_dai_driver pcm3168a_dais[] = {
+ {
+ .name = "pcm3168a-dac",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = PCM3168A_FORMATS
+ },
+ .ops = &pcm3168a_dac_dai_ops
+ },
+ {
+ .name = "pcm3168a-adc",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = PCM3168A_FORMATS
+ },
+ .ops = &pcm3168a_adc_dai_ops
+ },
+};
+
+static const struct reg_default pcm3168a_reg_default[] = {
+ { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
+ { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
+ { PCM3168A_DAC_OP_FLT, 0x00 },
+ { PCM3168A_DAC_INV, 0x00 },
+ { PCM3168A_DAC_MUTE, 0x00 },
+ { PCM3168A_DAC_ZERO, 0x00 },
+ { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
+ { PCM3168A_DAC_VOL_MASTER, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
+ { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
+ { PCM3168A_ADC_SMODE, 0x00 },
+ { PCM3168A_ADC_MST_FMT, 0x00 },
+ { PCM3168A_ADC_PWR_HPFB, 0x00 },
+ { PCM3168A_ADC_SEAD, 0x00 },
+ { PCM3168A_ADC_INV, 0x00 },
+ { PCM3168A_ADC_MUTE, 0x00 },
+ { PCM3168A_ADC_OV, 0x00 },
+ { PCM3168A_ADC_ATT_OVF, 0x00 },
+ { PCM3168A_ADC_VOL_MASTER, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
+ { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
+};
+
+static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg >= PCM3168A_RST_SMODE)
+ return true;
+ else
+ return false;
+}
+
+static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM3168A_DAC_ZERO:
+ case PCM3168A_ADC_OV:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg < PCM3168A_RST_SMODE)
+ return false;
+
+ switch (reg) {
+ case PCM3168A_DAC_ZERO:
+ case PCM3168A_ADC_OV:
+ return false;
+ default:
+ return true;
+ }
+}
+
+const struct regmap_config pcm3168a_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
+ .reg_defaults = pcm3168a_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
+ .readable_reg = pcm3168a_readable_register,
+ .volatile_reg = pcm3168a_volatile_register,
+ .writeable_reg = pcm3168a_writeable_register,
+ .cache_type = REGCACHE_FLAT
+};
+EXPORT_SYMBOL_GPL(pcm3168a_regmap);
+
+static const struct snd_soc_codec_driver pcm3168a_driver = {
+ .idle_bias_off = true,
+ .controls = pcm3168a_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
+ .dapm_widgets = pcm3168a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
+ .dapm_routes = pcm3168a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
+};
+
+int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+{
+ struct pcm3168a_priv *pcm3168a;
+ int ret, i;
+
+ pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
+ if (pcm3168a == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pcm3168a);
+
+ pcm3168a->scki = devm_clk_get(dev, "scki");
+ if (IS_ERR(pcm3168a->scki)) {
+ ret = PTR_ERR(pcm3168a->scki);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(pcm3168a->scki);
+ if (ret) {
+ dev_err(dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+
+ for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
+ pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ pcm3168a->regmap = regmap;
+ if (IS_ERR(pcm3168a->regmap)) {
+ ret = PTR_ERR(pcm3168a->regmap);
+ dev_err(dev, "failed to allocate regmap: %d\n", ret);
+ goto err_regulator;
+ }
+
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
+ ARRAY_SIZE(pcm3168a_dais));
+ if (ret) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+err_clk:
+ clk_disable_unprepare(pcm3168a->scki);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pcm3168a_probe);
+
+void pcm3168a_remove(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ pm_runtime_disable(dev);
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ clk_disable_unprepare(pcm3168a->scki);
+}
+EXPORT_SYMBOL_GPL(pcm3168a_remove);
+
+#ifdef CONFIG_PM
+static int pcm3168a_rt_resume(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(pcm3168a->scki);
+ if (ret) {
+ dev_err(dev, "Failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
+
+ regcache_cache_only(pcm3168a->regmap, false);
+
+ regcache_mark_dirty(pcm3168a->regmap);
+
+ ret = regcache_sync(pcm3168a->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync regmap: %d\n", ret);
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+err_clk:
+ clk_disable_unprepare(pcm3168a->scki);
+
+ return ret;
+}
+
+static int pcm3168a_rt_suspend(struct device *dev)
+{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ regcache_cache_only(pcm3168a->regmap, true);
+
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+ pcm3168a->supplies);
+
+ clk_disable_unprepare(pcm3168a->scki);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops pcm3168a_pm_ops = {
+ SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
+
+MODULE_DESCRIPTION("PCM3168A codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
new file mode 100644
index 000000000000..56c8332d82fb
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a.h
@@ -0,0 +1,100 @@
+/*
+ * PCM3168A codec driver header
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __PCM3168A_H__
+#define __PCM3168A_H__
+
+extern const struct dev_pm_ops pcm3168a_pm_ops;
+extern const struct regmap_config pcm3168a_regmap;
+
+extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
+extern void pcm3168a_remove(struct device *dev);
+
+#define PCM3168A_RST_SMODE 0x40
+#define PCM3168A_MRST_MASK 0x80
+#define PCM3168A_SRST_MASK 0x40
+#define PCM3168A_DAC_SRDA_SHIFT 0
+#define PCM3168A_DAC_SRDA_MASK 0x3
+
+#define PCM3168A_DAC_PWR_MST_FMT 0x41
+#define PCM3168A_DAC_PSMDA_SHIFT 7
+#define PCM3168A_DAC_PSMDA_MASK 0x80
+#define PCM3168A_DAC_MSDA_SHIFT 4
+#define PCM3168A_DAC_MSDA_MASK 0x70
+#define PCM3168A_DAC_FMT_SHIFT 0
+#define PCM3168A_DAC_FMT_MASK 0xf
+
+#define PCM3168A_DAC_OP_FLT 0x42
+#define PCM3168A_DAC_OPEDA_SHIFT 4
+#define PCM3168A_DAC_OPEDA_MASK 0xf0
+#define PCM3168A_DAC_FLT_SHIFT 0
+#define PCM3168A_DAC_FLT_MASK 0xf
+
+#define PCM3168A_DAC_INV 0x43
+
+#define PCM3168A_DAC_MUTE 0x44
+
+#define PCM3168A_DAC_ZERO 0x45
+
+#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
+#define PCM3168A_DAC_ATMDDA_MASK 0x80
+#define PCM3168A_DAC_ATMDDA_SHIFT 7
+#define PCM3168A_DAC_ATSPDA_MASK 0x40
+#define PCM3168A_DAC_ATSPDA_SHIFT 6
+#define PCM3168A_DAC_DEMP_SHIFT 4
+#define PCM3168A_DAC_DEMP_MASK 0x30
+#define PCM3168A_DAC_AZRO_SHIFT 1
+#define PCM3168A_DAC_AZRO_MASK 0xe
+#define PCM3168A_DAC_ZREV_MASK 0x1
+#define PCM3168A_DAC_ZREV_SHIFT 0
+
+#define PCM3168A_DAC_VOL_MASTER 0x47
+
+#define PCM3168A_DAC_VOL_CHAN_START 0x48
+
+#define PCM3168A_ADC_SMODE 0x50
+#define PCM3168A_ADC_SRAD_SHIFT 0
+#define PCM3168A_ADC_SRAD_MASK 0x3
+
+#define PCM3168A_ADC_MST_FMT 0x51
+#define PCM3168A_ADC_MSAD_SHIFT 4
+#define PCM3168A_ADC_MSAD_MASK 0x70
+#define PCM3168A_ADC_FMTAD_SHIFT 0
+#define PCM3168A_ADC_FMTAD_MASK 0x7
+
+#define PCM3168A_ADC_PWR_HPFB 0x52
+#define PCM3168A_ADC_PSVAD_SHIFT 4
+#define PCM3168A_ADC_PSVAD_MASK 0x70
+#define PCM3168A_ADC_BYP_SHIFT 0
+#define PCM3168A_ADC_BYP_MASK 0x7
+
+#define PCM3168A_ADC_SEAD 0x53
+
+#define PCM3168A_ADC_INV 0x54
+
+#define PCM3168A_ADC_MUTE 0x55
+
+#define PCM3168A_ADC_OV 0x56
+
+#define PCM3168A_ADC_ATT_OVF 0x57
+#define PCM3168A_ADC_ATMDAD_MASK 0x80
+#define PCM3168A_ADC_ATMDAD_SHIFT 7
+#define PCM3168A_ADC_ATSPAD_MASK 0x40
+#define PCM3168A_ADC_ATSPAD_SHIFT 6
+#define PCM3168A_ADC_OVFP_MASK 0x1
+#define PCM3168A_ADC_OVFP_SHIFT 0
+
+#define PCM3168A_ADC_VOL_MASTER 0x58
+
+#define PCM3168A_ADC_VOL_CHAN_START 0x59
+
+#endif
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
index aca479fa7670..1dc68ab08a17 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -80,8 +80,10 @@ int rl6231_calc_dmic_clk(int rate)
}
for (i = 0; i < ARRAY_SIZE(div); i++) {
- /* find divider that gives DMIC frequency below 3MHz */
- if (3000000 * div[i] >= rate)
+ if ((div[i] % 3) == 0)
+ continue;
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
return i;
}
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 28132375e427..3e8d66661b7e 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -226,6 +226,163 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
+static const struct reg_default rt5650_reg[] = {
+ { 0x00, 0x0000 },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x0a, 0x0002 },
+ { 0x0b, 0x2827 },
+ { 0x0c, 0xe000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x14, 0x3333 },
+ { 0x16, 0x4b00 },
+ { 0x18, 0x018b },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0001 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x20, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5656 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaaa0 },
+ { 0x2d, 0x0000 },
+ { 0x2f, 0x1002 },
+ { 0x31, 0x5000 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0000 },
+ { 0x35, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x3f, 0x0000 },
+ { 0x40, 0x001f },
+ { 0x41, 0x0000 },
+ { 0x42, 0x001f },
+ { 0x45, 0x6000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf807 },
+ { 0x4a, 0x0004 },
+ { 0x4d, 0x0000 },
+ { 0x4e, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x56, 0x0111 },
+ { 0x57, 0x0064 },
+ { 0x58, 0xef0e },
+ { 0x59, 0xf0f0 },
+ { 0x5a, 0xef0e },
+ { 0x5b, 0xf0f0 },
+ { 0x5c, 0xef0e },
+ { 0x5d, 0xf0f0 },
+ { 0x5e, 0xf000 },
+ { 0x5f, 0x0000 },
+ { 0x61, 0x0300 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c2 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0aaa },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x7770 },
+ { 0x74, 0x3e00 },
+ { 0x75, 0x2409 },
+ { 0x76, 0x000a },
+ { 0x77, 0x0c00 },
+ { 0x78, 0x0000 },
+ { 0x79, 0x0123 },
+ { 0x7a, 0x0123 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c06 },
+ { 0x93, 0x0000 },
+ { 0x94, 0x0200 },
+ { 0x95, 0x0000 },
+ { 0x9a, 0x2184 },
+ { 0x9b, 0x010a },
+ { 0x9c, 0x0aea },
+ { 0x9d, 0x000c },
+ { 0x9e, 0x0400 },
+ { 0xa0, 0xa0a8 },
+ { 0xa1, 0x0059 },
+ { 0xa2, 0x0001 },
+ { 0xae, 0x6000 },
+ { 0xaf, 0x0000 },
+ { 0xb0, 0x6000 },
+ { 0xb1, 0x0000 },
+ { 0xb2, 0x0000 },
+ { 0xb3, 0x001f },
+ { 0xb4, 0x020c },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x3100 },
+ { 0xc0, 0x0000 },
+ { 0xc1, 0x0000 },
+ { 0xc2, 0x0000 },
+ { 0xc3, 0x2000 },
+ { 0xcd, 0x0000 },
+ { 0xce, 0x0000 },
+ { 0xcf, 0x1813 },
+ { 0xd0, 0x0690 },
+ { 0xd1, 0x1c17 },
+ { 0xd3, 0xb320 },
+ { 0xd4, 0x0000 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xda, 0x0000 },
+ { 0xdb, 0x0003 },
+ { 0xdc, 0x0049 },
+ { 0xdd, 0x001b },
+ { 0xdf, 0x0008 },
+ { 0xe0, 0x4000 },
+ { 0xe6, 0x8000 },
+ { 0xe7, 0x0200 },
+ { 0xec, 0xb300 },
+ { 0xed, 0x0000 },
+ { 0xf0, 0x001f },
+ { 0xf1, 0x020c },
+ { 0xf2, 0x1f00 },
+ { 0xf3, 0x0000 },
+ { 0xf4, 0x4000 },
+ { 0xf8, 0x0000 },
+ { 0xf9, 0x0000 },
+ { 0xfa, 0x2060 },
+ { 0xfb, 0x4040 },
+ { 0xfc, 0x0000 },
+ { 0xfd, 0x0002 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6308 },
+};
+
struct rt5645_eq_param_s {
unsigned short reg;
unsigned short val;
@@ -245,7 +402,7 @@ struct rt5645_priv {
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;
- struct delayed_work jack_detect_work;
+ struct delayed_work jack_detect_work, rcclock_work;
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
struct rt5645_eq_param_s *eq_param;
@@ -565,12 +722,31 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
.put = rt5645_hweq_put \
}
+static int rt5645_spk_put_volsw(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);
+ int ret;
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
+ msecs_to_jiffies(200));
+
+ return ret;
+}
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
- SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw,
+ rt5645_spk_put_volsw, out_vol_tlv),
/* ClassD modulator Speaker Gain Ratio */
SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
@@ -1498,7 +1674,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);
- msleep(40);
+ msleep(70);
rt5645->hp_on = true;
} else {
/* depop parameters */
@@ -3122,6 +3298,15 @@ static void rt5645_jack_detect_work(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
+static void rt5645_rcclock_work(struct work_struct *work)
+{
+ struct rt5645_priv *rt5645 =
+ container_of(work, struct rt5645_priv, rcclock_work.work);
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD);
+}
+
static irqreturn_t rt5645_irq(int irq, void *data)
{
struct rt5645_priv *rt5645 = data;
@@ -3288,6 +3473,31 @@ static const struct regmap_config rt5645_regmap = {
.num_ranges = ARRAY_SIZE(rt5645_ranges),
};
+static const struct regmap_config rt5650_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = true,
+ .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
+ RT5645_PR_SPACING),
+ .volatile_reg = rt5645_volatile_register,
+ .readable_reg = rt5645_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5650_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5650_reg),
+ .ranges = rt5645_ranges,
+ .num_ranges = ARRAY_SIZE(rt5645_ranges),
+};
+
+static const struct regmap_config temp_regmap = {
+ .name="nocache",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = true,
+ .max_register = RT5645_VENDOR_ID2 + 1,
+ .cache_type = REGCACHE_NONE,
+};
+
static const struct i2c_device_id rt5645_i2c_id[] = {
{ "rt5645", 0 },
{ "rt5650", 0 },
@@ -3304,48 +3514,23 @@ static struct acpi_device_id rt5645_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
#endif
-static struct rt5645_platform_data *rt5645_pdata;
-
-static struct rt5645_platform_data strago_platform_data = {
+static struct rt5645_platform_data general_platform_data = {
.dmic1_data_pin = RT5645_DMIC1_DISABLE,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
.jd_mode = 3,
};
-static int strago_quirk_cb(const struct dmi_system_id *id)
-{
- rt5645_pdata = &strago_platform_data;
-
- return 1;
-}
-
static const struct dmi_system_id dmi_platform_intel_braswell[] = {
{
.ident = "Intel Strago",
- .callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
},
},
{
- .ident = "Google Celes",
- .callback = strago_quirk_cb,
- .matches = {
- 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,
+ .ident = "Google Chrome",
.matches = {
- DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
},
},
{ }
@@ -3358,17 +3543,9 @@ static struct rt5645_platform_data buddy_platform_data = {
.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"),
},
@@ -3376,6 +3553,16 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
{ }
};
+static bool rt5645_check_dp(struct device *dev)
+{
+ if (device_property_present(dev, "realtek,in2-differential") ||
+ device_property_present(dev, "realtek,dmic1-data-pin") ||
+ device_property_present(dev, "realtek,dmic2-data-pin") ||
+ device_property_present(dev, "realtek,jd-mode"))
+ return true;
+
+ return false;
+}
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
{
@@ -3398,6 +3585,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
struct rt5645_priv *rt5645;
int ret, i;
unsigned int val;
+ struct regmap *regmap;
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
GFP_KERNEL);
@@ -3409,11 +3597,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
- else if (dmi_check_system(dmi_platform_intel_braswell) ||
- dmi_check_system(dmi_platform_intel_broadwell))
- rt5645->pdata = *rt5645_pdata;
- else
+ else if (dmi_check_system(dmi_platform_intel_broadwell))
+ rt5645->pdata = buddy_platform_data;
+ else if (rt5645_check_dp(&i2c->dev))
rt5645_parse_dt(rt5645, &i2c->dev);
+ else if (dmi_check_system(dmi_platform_intel_braswell))
+ rt5645->pdata = general_platform_data;
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
GPIOD_IN);
@@ -3423,14 +3612,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return PTR_ERR(rt5645->gpiod_hp_det);
}
- rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
- if (IS_ERR(rt5645->regmap)) {
- ret = PTR_ERR(rt5645->regmap);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
- return ret;
- }
-
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
rt5645->supplies[i].supply = rt5645_supply_names[i];
@@ -3449,13 +3630,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
+ regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
+ ret);
+ return ret;
+ }
+ regmap_read(regmap, RT5645_VENDOR_ID2, &val);
switch (val) {
case RT5645_DEVICE_ID:
+ rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
rt5645->codec_type = CODEC_TYPE_RT5645;
break;
case RT5650_DEVICE_ID:
+ rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap);
rt5645->codec_type = CODEC_TYPE_RT5650;
break;
default:
@@ -3466,6 +3656,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
+ if (IS_ERR(rt5645->regmap)) {
+ ret = PTR_ERR(rt5645->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
regmap_write(rt5645->regmap, RT5645_RESET, 0);
ret = regmap_register_patch(rt5645->regmap, init_list,
@@ -3587,6 +3784,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+ INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
if (rt5645->i2c->irq) {
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
@@ -3621,6 +3819,7 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
free_irq(i2c->irq, rt5645);
cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
snd_soc_unregister_codec(&i2c->dev);
regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index dc2b46236c5c..3f1b0f1df809 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -973,12 +973,12 @@
#define RT5670_SCLK_SRC_MCLK (0x0 << 14)
#define RT5670_SCLK_SRC_PLL1 (0x1 << 14)
#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */
-#define RT5670_PLL1_SRC_MASK (0x3 << 12)
-#define RT5670_PLL1_SRC_SFT 12
-#define RT5670_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5670_PLL1_SRC_MASK (0x7 << 11)
+#define RT5670_PLL1_SRC_SFT 11
+#define RT5670_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5670_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5670_PLL1_SRC_BCLK2 (0x2 << 11)
+#define RT5670_PLL1_SRC_BCLK3 (0x3 << 11)
#define RT5670_PLL1_PD_MASK (0x1 << 3)
#define RT5670_PLL1_PD_SFT 3
#define RT5670_PLL1_PD_1 (0x0 << 3)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index b4cd7e3bf5f8..69d987a9935c 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1386,90 +1386,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
};
static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_STO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_STO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_STO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_STO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
};
@@ -2596,6 +2596,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(50);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU |
@@ -3072,19 +3087,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
/* DAC Mixer */
SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)),
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index f540f82b1f27..08b40460663c 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -189,6 +189,7 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+ msleep(400);
break;
case SND_SOC_DAPM_PRE_PMD:
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index c04c0bc6f58a..c36409601835 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -575,6 +575,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
+#define WM5110_RXANC_INPUT_ROUTES(widget, name) \
+ { widget, NULL, name " NG Mux" }, \
+ { name " NG Internal", NULL, "RXANC NG Clock" }, \
+ { name " NG Internal", NULL, name " Channel" }, \
+ { name " NG External", NULL, "RXANC NG External Clock" }, \
+ { name " NG External", NULL, name " Channel" }, \
+ { name " NG Mux", "None", name " Channel" }, \
+ { name " NG Mux", "Internal", name " NG Internal" }, \
+ { name " NG Mux", "External", name " NG External" }, \
+ { name " Channel", "Left", name " Left Input" }, \
+ { name " Channel", "Combine", name " Left Input" }, \
+ { name " Channel", "Right", name " Right Input" }, \
+ { name " Channel", "Combine", name " Right Input" }, \
+ { name " Left Input", "IN1", "IN1L PGA" }, \
+ { name " Right Input", "IN1", "IN1R PGA" }, \
+ { name " Left Input", "IN2", "IN2L PGA" }, \
+ { name " Right Input", "IN2", "IN2R PGA" }, \
+ { name " Left Input", "IN3", "IN3L PGA" }, \
+ { name " Right Input", "IN3", "IN3R PGA" }, \
+ { name " Left Input", "IN4", "IN4L PGA" }, \
+ { name " Right Input", "IN4", "IN4R PGA" }
+
+#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \
+ { widget, NULL, name " ANC Source" }, \
+ { name " ANC Source", "RXANCL", "RXANCL" }, \
+ { name " ANC Source", "RXANCR", "RXANCR" }
+
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
@@ -639,6 +666,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START,
+ ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1),
+SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START,
+ ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1),
+SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1),
+SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START,
+ ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1),
+
ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
@@ -995,6 +1031,31 @@ static const struct soc_enum wm5110_aec_loopback =
static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
+static const struct snd_kcontrol_new wm5110_anc_input_mux[] = {
+ SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]),
+ SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]),
+ SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]),
+ SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]),
+};
+
+static const struct snd_kcontrol_new wm5110_anc_ng_mux =
+ SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum);
+
+static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
+ SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]),
+ SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]),
+ SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]),
+ SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]),
+ SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]),
+ SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]),
+ SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]),
+ SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]),
+ SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]),
+ SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]),
+ SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]),
+ SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]),
+};
+
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
@@ -1185,6 +1246,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
&wm5110_aec_loopback_mux),
+SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
+ ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
+ ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[0]),
+SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[1]),
+SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
+SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[2]),
+SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
+ &wm5110_anc_input_mux[3]),
+SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
+
+SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT,
+ 0, NULL, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT,
+ 0, NULL, 0, arizona_anc_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[0]),
+SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[1]),
+SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[2]),
+SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[3]),
+SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[4]),
+SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[5]),
+SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[6]),
+SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[7]),
+SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[8]),
+SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[9]),
+SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[10]),
+SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
+ &wm5110_output_anc_src[11]),
+
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,
@@ -1690,6 +1810,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
+ { "Voice Control DSP", NULL, "DSP3" },
+ { "Voice Control DSP", NULL, "SYSCLK" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1838,6 +1961,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "SPKDAT2L", NULL, "OUT6L" },
{ "SPKDAT2R", NULL, "OUT6R" },
+ WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
+ WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
+
+ WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
+ WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
+
{ "MICSUPP", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
@@ -1996,8 +2135,48 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
},
.ops = &arizona_simple_dai_ops,
},
+ {
+ .name = "wm5110-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "wm5110-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ },
};
+static int wm5110_open(struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct arizona *arizona = priv->core.arizona;
+ int n_adsp;
+
+ if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else {
+ dev_err(arizona->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ rtd->codec_dai->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@@ -2088,6 +2267,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
};
+static struct snd_compr_ops wm5110_compr_ops = {
+ .open = wm5110_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+};
+
+static struct snd_soc_platform_driver wm5110_compr_platform = {
+ .compr_ops = &wm5110_compr_ops,
+};
+
static int wm5110_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -2148,8 +2339,21 @@ static int wm5110_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
+ ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+ goto error;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
wm5110_dai, ARRAY_SIZE(wm5110_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ snd_soc_unregister_platform(&pdev->dev);
+ }
+
+error:
+ return ret;
}
static int wm5110_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 056375339ea3..5380798883b5 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -229,7 +229,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
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),
+ 7, 1, 1),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 39ebd7bf4f53..a7e79784fc16 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -365,8 +365,8 @@ static const struct reg_default wm8962_reg[] = {
{ 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */
{ 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */
- { 17048, 0x0083 }, /* R17408 - HPF_C_1 */
- { 17049, 0x98AD }, /* R17409 - HPF_C_0 */
+ { 17408, 0x0083 }, /* R17408 - HPF_C_1 */
+ { 17409, 0x98AD }, /* R17409 - HPF_C_0 */
{ 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */
{ 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 0a60677397b3..4c29bd2ae75c 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -574,6 +574,7 @@ static const struct regmap_config wm8974_regmap = {
.max_register = WM8974_MONOMIX,
.reg_defaults = wm8974_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
};
static int wm8974_probe(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 8782dfb628ab..7719bc509e50 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -199,20 +199,20 @@ static const char * const wm8998_inmux_texts[] = {
"B",
};
-static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
- ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_IN1L_SRC_SHIFT,
- wm8998_inmux_texts);
+static 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 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 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,
@@ -522,17 +522,17 @@ 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 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 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),
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 0bb415a28723..ac879d16c6a6 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -201,27 +201,186 @@ static void wm_adsp_buf_free(struct list_head *list)
}
}
-#define WM_ADSP_NUM_FW 4
-
-#define WM_ADSP_FW_MBC_VSS 0
-#define WM_ADSP_FW_TX 1
-#define WM_ADSP_FW_TX_SPK 2
-#define WM_ADSP_FW_RX_ANC 3
+#define WM_ADSP_FW_MBC_VSS 0
+#define WM_ADSP_FW_HIFI 1
+#define WM_ADSP_FW_TX 2
+#define WM_ADSP_FW_TX_SPK 3
+#define WM_ADSP_FW_RX 4
+#define WM_ADSP_FW_RX_ANC 5
+#define WM_ADSP_FW_CTRL 6
+#define WM_ADSP_FW_ASR 7
+#define WM_ADSP_FW_TRACE 8
+#define WM_ADSP_FW_SPK_PROT 9
+#define WM_ADSP_FW_MISC 10
+
+#define WM_ADSP_NUM_FW 11
static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
- [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
- [WM_ADSP_FW_TX] = "Tx",
- [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
- [WM_ADSP_FW_RX_ANC] = "Rx ANC",
+ [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
+ [WM_ADSP_FW_HIFI] = "MasterHiFi",
+ [WM_ADSP_FW_TX] = "Tx",
+ [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
+ [WM_ADSP_FW_RX] = "Rx",
+ [WM_ADSP_FW_RX_ANC] = "Rx ANC",
+ [WM_ADSP_FW_CTRL] = "Voice Ctrl",
+ [WM_ADSP_FW_ASR] = "ASR Assist",
+ [WM_ADSP_FW_TRACE] = "Dbg Trace",
+ [WM_ADSP_FW_SPK_PROT] = "Protection",
+ [WM_ADSP_FW_MISC] = "Misc",
+};
+
+struct wm_adsp_system_config_xm_hdr {
+ __be32 sys_enable;
+ __be32 fw_id;
+ __be32 fw_rev;
+ __be32 boot_status;
+ __be32 watchdog;
+ __be32 dma_buffer_size;
+ __be32 rdma[6];
+ __be32 wdma[8];
+ __be32 build_job_name[3];
+ __be32 build_job_number;
+};
+
+struct wm_adsp_alg_xm_struct {
+ __be32 magic;
+ __be32 smoothing;
+ __be32 threshold;
+ __be32 host_buf_ptr;
+ __be32 start_seq;
+ __be32 high_water_mark;
+ __be32 low_water_mark;
+ __be64 smoothed_power;
+};
+
+struct wm_adsp_buffer {
+ __be32 X_buf_base; /* XM base addr of first X area */
+ __be32 X_buf_size; /* Size of 1st X area in words */
+ __be32 X_buf_base2; /* XM base addr of 2nd X area */
+ __be32 X_buf_brk; /* Total X size in words */
+ __be32 Y_buf_base; /* YM base addr of Y area */
+ __be32 wrap; /* Total size X and Y in words */
+ __be32 high_water_mark; /* Point at which IRQ is asserted */
+ __be32 irq_count; /* bits 1-31 count IRQ assertions */
+ __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */
+ __be32 next_write_index; /* word index of next write */
+ __be32 next_read_index; /* word index of next read */
+ __be32 error; /* error if any */
+ __be32 oldest_block_index; /* word index of oldest surviving */
+ __be32 requested_rewind; /* how many blocks rewind was done */
+ __be32 reserved_space; /* internal */
+ __be32 min_free; /* min free space since stream start */
+ __be32 blocks_written[2]; /* total blocks written (64 bit) */
+ __be32 words_written[2]; /* total words written (64 bit) */
+};
+
+struct wm_adsp_compr_buf {
+ struct wm_adsp *dsp;
+
+ struct wm_adsp_buffer_region *regions;
+ u32 host_buf_ptr;
+};
+
+struct wm_adsp_compr {
+ struct wm_adsp *dsp;
+ struct wm_adsp_compr_buf *buf;
+
+ struct snd_compr_stream *stream;
+ struct snd_compressed_buffer size;
+};
+
+#define WM_ADSP_DATA_WORD_SIZE 3
+
+#define WM_ADSP_MIN_FRAGMENTS 1
+#define WM_ADSP_MAX_FRAGMENTS 256
+#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE)
+#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE)
+
+#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7
+
+#define HOST_BUFFER_FIELD(field) \
+ (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
+
+#define ALG_XM_FIELD(field) \
+ (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
+
+static int wm_adsp_buffer_init(struct wm_adsp *dsp);
+static int wm_adsp_buffer_free(struct wm_adsp *dsp);
+
+struct wm_adsp_buffer_region {
+ unsigned int offset;
+ unsigned int cumulative_size;
+ unsigned int mem_type;
+ unsigned int base_addr;
+};
+
+struct wm_adsp_buffer_region_def {
+ unsigned int mem_type;
+ unsigned int base_offset;
+ unsigned int size_offset;
+};
+
+static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+ {
+ .mem_type = WMFW_ADSP2_XM,
+ .base_offset = HOST_BUFFER_FIELD(X_buf_base),
+ .size_offset = HOST_BUFFER_FIELD(X_buf_size),
+ },
+ {
+ .mem_type = WMFW_ADSP2_XM,
+ .base_offset = HOST_BUFFER_FIELD(X_buf_base2),
+ .size_offset = HOST_BUFFER_FIELD(X_buf_brk),
+ },
+ {
+ .mem_type = WMFW_ADSP2_YM,
+ .base_offset = HOST_BUFFER_FIELD(Y_buf_base),
+ .size_offset = HOST_BUFFER_FIELD(wrap),
+ },
+};
+
+struct wm_adsp_fw_caps {
+ u32 id;
+ struct snd_codec_desc desc;
+ int num_regions;
+ struct wm_adsp_buffer_region_def *region_defs;
+};
+
+static const struct wm_adsp_fw_caps ez2control_caps[] = {
+ {
+ .id = SND_AUDIOCODEC_BESPOKE,
+ .desc = {
+ .max_ch = 1,
+ .sample_rates = { 16000 },
+ .num_sample_rates = 1,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .num_regions = ARRAY_SIZE(ez2control_regions),
+ .region_defs = ez2control_regions,
+ },
};
-static struct {
+static const struct {
const char *file;
+ int compr_direction;
+ int num_caps;
+ const struct wm_adsp_fw_caps *caps;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
- [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
- [WM_ADSP_FW_TX] = { .file = "tx" },
- [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
- [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
+ [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
+ [WM_ADSP_FW_HIFI] = { .file = "hifi" },
+ [WM_ADSP_FW_TX] = { .file = "tx" },
+ [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
+ [WM_ADSP_FW_RX] = { .file = "rx" },
+ [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
+ [WM_ADSP_FW_CTRL] = {
+ .file = "ctrl",
+ .compr_direction = SND_COMPRESS_CAPTURE,
+ .num_caps = ARRAY_SIZE(ez2control_caps),
+ .caps = ez2control_caps,
+ },
+ [WM_ADSP_FW_ASR] = { .file = "asr" },
+ [WM_ADSP_FW_TRACE] = { .file = "trace" },
+ [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
+ [WM_ADSP_FW_MISC] = { .file = "misc" },
};
struct wm_coeff_ctl_ops {
@@ -254,30 +413,24 @@ static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
- mutex_lock(&dsp->debugfs_lock);
kfree(dsp->wmfw_file_name);
dsp->wmfw_file_name = tmp;
- mutex_unlock(&dsp->debugfs_lock);
}
static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
- mutex_lock(&dsp->debugfs_lock);
kfree(dsp->bin_file_name);
dsp->bin_file_name = tmp;
- mutex_unlock(&dsp->debugfs_lock);
}
static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
{
- mutex_lock(&dsp->debugfs_lock);
kfree(dsp->wmfw_file_name);
kfree(dsp->bin_file_name);
dsp->wmfw_file_name = NULL;
dsp->bin_file_name = NULL;
- mutex_unlock(&dsp->debugfs_lock);
}
static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
@@ -287,7 +440,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
struct wm_adsp *dsp = file->private_data;
ssize_t ret;
- mutex_lock(&dsp->debugfs_lock);
+ mutex_lock(&dsp->pwr_lock);
if (!dsp->wmfw_file_name || !dsp->running)
ret = 0;
@@ -296,7 +449,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
dsp->wmfw_file_name,
strlen(dsp->wmfw_file_name));
- mutex_unlock(&dsp->debugfs_lock);
+ mutex_unlock(&dsp->pwr_lock);
return ret;
}
@@ -307,7 +460,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
struct wm_adsp *dsp = file->private_data;
ssize_t ret;
- mutex_lock(&dsp->debugfs_lock);
+ mutex_lock(&dsp->pwr_lock);
if (!dsp->bin_file_name || !dsp->running)
ret = 0;
@@ -316,7 +469,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
dsp->bin_file_name,
strlen(dsp->bin_file_name));
- mutex_unlock(&dsp->debugfs_lock);
+ mutex_unlock(&dsp->pwr_lock);
return ret;
}
@@ -436,6 +589,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
return 0;
@@ -443,12 +597,16 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
- if (dsp[e->shift_l].running)
- return -EBUSY;
+ mutex_lock(&dsp[e->shift_l].pwr_lock);
- dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+ if (dsp[e->shift_l].running || dsp[e->shift_l].compr)
+ ret = -EBUSY;
+ else
+ dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
- return 0;
+ mutex_unlock(&dsp[e->shift_l].pwr_lock);
+
+ return ret;
}
static const struct soc_enum wm_adsp_fw_enum[] = {
@@ -523,10 +681,10 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
be16_to_cpu(scratch[3]));
}
-static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = ctl->len;
@@ -572,19 +730,24 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
return 0;
}
-static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+static int wm_coeff_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
char *p = ucontrol->value.bytes.data;
+ int ret = 0;
+
+ mutex_lock(&ctl->dsp->pwr_lock);
memcpy(ctl->cache, p, ctl->len);
ctl->set = 1;
- if (!ctl->enabled)
- return 0;
+ if (ctl->enabled)
+ ret = wm_coeff_write_control(ctl, p, ctl->len);
+
+ mutex_unlock(&ctl->dsp->pwr_lock);
- return wm_coeff_write_control(ctl, p, ctl->len);
+ return ret;
}
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
@@ -626,22 +789,30 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
return 0;
}
-static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+static int wm_coeff_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
char *p = ucontrol->value.bytes.data;
+ int ret = 0;
+
+ mutex_lock(&ctl->dsp->pwr_lock);
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
if (ctl->enabled)
- return wm_coeff_read_control(ctl, p, ctl->len);
+ ret = wm_coeff_read_control(ctl, p, ctl->len);
else
- return -EPERM;
+ ret = -EPERM;
+ } else {
+ if (!ctl->flags && ctl->enabled)
+ ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
+
+ memcpy(p, ctl->cache, ctl->len);
}
- memcpy(p, ctl->cache, ctl->len);
+ mutex_unlock(&ctl->dsp->pwr_lock);
- return 0;
+ return ret;
}
struct wmfw_ctl_work {
@@ -808,8 +979,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
break;
}
- list_for_each_entry(ctl, &dsp->ctl_list,
- list) {
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
if (!strcmp(ctl->name, name)) {
if (!ctl->enabled)
ctl->enabled = 1;
@@ -1088,7 +1258,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
goto out_fw;
}
- header = (void*)&firmware->data[0];
+ header = (void *)&firmware->data[0];
if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
adsp_err(dsp, "%s: invalid magic\n", file);
@@ -1168,7 +1338,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
offset = le32_to_cpu(region->offset) & 0xffffff;
type = be32_to_cpu(region->type) & 0xff;
mem = wm_adsp_find_region(dsp, type);
-
+
switch (type) {
case WMFW_NAME_TEXT:
region_name = "Firmware name";
@@ -1333,6 +1503,19 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
return alg;
}
+static struct wm_adsp_alg_region *
+ wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id)
+{
+ struct wm_adsp_alg_region *alg_region;
+
+ list_for_each_entry(alg_region, &dsp->alg_regions, list) {
+ if (id == alg_region->alg && type == alg_region->type)
+ return alg_region;
+ }
+
+ return NULL;
+}
+
static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
int type, __be32 id,
__be32 base)
@@ -1625,7 +1808,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
goto out_fw;
}
- hdr = (void*)&firmware->data[0];
+ hdr = (void *)&firmware->data[0];
if (memcmp(hdr->magic, "WMDR", 4) != 0) {
adsp_err(dsp, "%s: invalid magic\n", file);
goto out_fw;
@@ -1651,7 +1834,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
blocks = 0;
while (pos < firmware->size &&
pos - firmware->size > sizeof(*blk)) {
- blk = (void*)(&firmware->data[pos]);
+ blk = (void *)(&firmware->data[pos]);
type = le16_to_cpu(blk->type);
offset = le16_to_cpu(blk->offset);
@@ -1705,22 +1888,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
break;
}
- reg = 0;
- list_for_each_entry(alg_region,
- &dsp->alg_regions, list) {
- if (le32_to_cpu(blk->id) == alg_region->alg &&
- type == alg_region->type) {
- reg = alg_region->base;
- reg = wm_adsp_region_to_reg(mem,
- reg);
- reg += offset;
- break;
- }
- }
-
- if (reg == 0)
+ alg_region = wm_adsp_find_alg_region(dsp, type,
+ le32_to_cpu(blk->id));
+ if (alg_region) {
+ reg = alg_region->base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+ reg += offset;
+ } else {
adsp_err(dsp, "No %x for algorithm %x\n",
type, le32_to_cpu(blk->id));
+ }
break;
default:
@@ -1778,9 +1955,8 @@ int wm_adsp1_init(struct wm_adsp *dsp)
{
INIT_LIST_HEAD(&dsp->alg_regions);
-#ifdef CONFIG_DEBUG_FS
- mutex_init(&dsp->debugfs_lock);
-#endif
+ mutex_init(&dsp->pwr_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp1_init);
@@ -1795,10 +1971,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl;
int ret;
- int val;
+ unsigned int val;
dsp->card = codec->component.card;
+ mutex_lock(&dsp->pwr_lock);
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -1808,12 +1986,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
* For simplicity set the DSP clock rate to be the
* SYSCLK rate rather than making it configurable.
*/
- if(dsp->sysclk_reg) {
+ if (dsp->sysclk_reg) {
ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
if (ret != 0) {
adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
ret);
- return ret;
+ goto err_mutex;
}
val = (val & dsp->sysclk_mask)
@@ -1825,31 +2003,31 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0) {
adsp_err(dsp, "Failed to set clock rate: %d\n",
ret);
- return ret;
+ goto err_mutex;
}
}
ret = wm_adsp_load(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
ret = wm_adsp1_setup_algs(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
ret = wm_adsp_load_coeff(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
/* Initialize caches for enabled and unset controls */
ret = wm_coeff_init_control_caches(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
/* Sync set controls */
ret = wm_coeff_sync_controls(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -1884,11 +2062,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
break;
}
+ mutex_unlock(&dsp->pwr_lock);
+
return 0;
-err:
+err_ena:
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
+err_mutex:
+ mutex_unlock(&dsp->pwr_lock);
+
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp1_event);
@@ -1934,6 +2117,8 @@ static void wm_adsp2_boot_work(struct work_struct *work)
int ret;
unsigned int val;
+ mutex_lock(&dsp->pwr_lock);
+
/*
* For simplicity set the DSP clock rate to be the
* SYSCLK rate rather than making it configurable.
@@ -1941,7 +2126,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)
ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
if (ret != 0) {
adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
- return;
+ goto err_mutex;
}
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
>> ARIZONA_SYSCLK_FREQ_SHIFT;
@@ -1951,42 +2136,46 @@ static void wm_adsp2_boot_work(struct work_struct *work)
ADSP2_CLK_SEL_MASK, val);
if (ret != 0) {
adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
- return;
+ goto err_mutex;
}
ret = wm_adsp2_ena(dsp);
if (ret != 0)
- return;
+ goto err_mutex;
ret = wm_adsp_load(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
ret = wm_adsp2_setup_algs(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
ret = wm_adsp_load_coeff(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
/* Initialize caches for enabled and unset controls */
ret = wm_coeff_init_control_caches(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
/* Sync set controls */
ret = wm_coeff_sync_controls(dsp);
if (ret != 0)
- goto err;
+ goto err_ena;
dsp->running = true;
+ mutex_unlock(&dsp->pwr_lock);
+
return;
-err:
+err_ena:
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+err_mutex:
+ mutex_unlock(&dsp->pwr_lock);
}
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
@@ -2033,12 +2222,18 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ADSP2_CORE_ENA | ADSP2_START);
if (ret != 0)
goto err;
+
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ ret = wm_adsp_buffer_init(dsp);
+
break;
case SND_SOC_DAPM_PRE_PMD:
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status(dsp);
+ mutex_lock(&dsp->pwr_lock);
+
wm_adsp_debugfs_clear(dsp);
dsp->fw_id = 0;
@@ -2065,6 +2260,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
kfree(alg_region);
}
+ if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ wm_adsp_buffer_free(dsp);
+
+ mutex_unlock(&dsp->pwr_lock);
+
adsp_dbg(dsp, "Shutdown complete\n");
break;
@@ -2117,11 +2317,406 @@ int wm_adsp2_init(struct wm_adsp *dsp)
INIT_LIST_HEAD(&dsp->ctl_list);
INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
-#ifdef CONFIG_DEBUG_FS
- mutex_init(&dsp->debugfs_lock);
-#endif
+ mutex_init(&dsp->pwr_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
+int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
+{
+ struct wm_adsp_compr *compr;
+ int ret = 0;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (wm_adsp_fw[dsp->fw].num_caps == 0) {
+ adsp_err(dsp, "Firmware does not support compressed API\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {
+ adsp_err(dsp, "Firmware does not support stream direction\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (dsp->compr) {
+ /* It is expect this limitation will be removed in future */
+ adsp_err(dsp, "Only a single stream supported per DSP\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ compr = kzalloc(sizeof(*compr), GFP_KERNEL);
+ if (!compr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ compr->dsp = dsp;
+ compr->stream = stream;
+
+ dsp->compr = compr;
+
+ stream->runtime->private_data = compr;
+
+out:
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_open);
+
+int wm_adsp_compr_free(struct snd_compr_stream *stream)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp *dsp = compr->dsp;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ dsp->compr = NULL;
+
+ kfree(compr);
+
+ mutex_unlock(&dsp->pwr_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_free);
+
+static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp *dsp = compr->dsp;
+ const struct wm_adsp_fw_caps *caps;
+ const struct snd_codec_desc *desc;
+ int i, j;
+
+ if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||
+ params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||
+ params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||
+ params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||
+ params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) {
+ adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n",
+ params->buffer.fragment_size,
+ params->buffer.fragments);
+
+ return -EINVAL;
+ }
+
+ for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {
+ caps = &wm_adsp_fw[dsp->fw].caps[i];
+ desc = &caps->desc;
+
+ if (caps->id != params->codec.id)
+ continue;
+
+ if (stream->direction == SND_COMPRESS_PLAYBACK) {
+ if (desc->max_ch < params->codec.ch_out)
+ continue;
+ } else {
+ if (desc->max_ch < params->codec.ch_in)
+ continue;
+ }
+
+ if (!(desc->formats & (1 << params->codec.format)))
+ continue;
+
+ for (j = 0; j < desc->num_sample_rates; ++j)
+ if (desc->sample_rates[j] == params->codec.sample_rate)
+ return 0;
+ }
+
+ adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",
+ params->codec.id, params->codec.ch_in, params->codec.ch_out,
+ params->codec.sample_rate, params->codec.format);
+ return -EINVAL;
+}
+
+int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ int ret;
+
+ ret = wm_adsp_compr_check_params(stream, params);
+ if (ret)
+ return ret;
+
+ compr->size = params->buffer;
+
+ adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
+ compr->size.fragment_size, compr->size.fragments);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
+
+int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ int fw = compr->dsp->fw;
+ int i;
+
+ if (wm_adsp_fw[fw].caps) {
+ for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)
+ caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;
+
+ caps->num_codecs = i;
+ caps->direction = wm_adsp_fw[fw].compr_direction;
+
+ caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;
+ caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
+
+static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
+ unsigned int mem_addr,
+ unsigned int num_words, u32 *data)
+{
+ struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
+ unsigned int i, reg;
+ int ret;
+
+ if (!mem)
+ return -EINVAL;
+
+ reg = wm_adsp_region_to_reg(mem, mem_addr);
+
+ ret = regmap_raw_read(dsp->regmap, reg, data,
+ sizeof(*data) * num_words);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < num_words; ++i)
+ data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
+
+ return 0;
+}
+
+static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
+ unsigned int mem_addr, u32 *data)
+{
+ return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
+}
+
+static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
+ unsigned int mem_addr, u32 data)
+{
+ struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
+ unsigned int reg;
+
+ if (!mem)
+ return -EINVAL;
+
+ reg = wm_adsp_region_to_reg(mem, mem_addr);
+
+ data = cpu_to_be32(data & 0x00ffffffu);
+
+ return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
+}
+
+static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
+ unsigned int field_offset, u32 *data)
+{
+ return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM,
+ buf->host_buf_ptr + field_offset, data);
+}
+
+static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
+ unsigned int field_offset, u32 data)
+{
+ return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM,
+ buf->host_buf_ptr + field_offset, data);
+}
+
+static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
+{
+ struct wm_adsp_alg_region *alg_region;
+ struct wm_adsp *dsp = buf->dsp;
+ u32 xmalg, addr, magic;
+ int i, ret;
+
+ alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
+ xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
+
+ addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
+ ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
+ if (ret < 0)
+ return ret;
+
+ if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
+ return -EINVAL;
+
+ addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
+ for (i = 0; i < 5; ++i) {
+ ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
+ &buf->host_buf_ptr);
+ if (ret < 0)
+ return ret;
+
+ if (buf->host_buf_ptr)
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (!buf->host_buf_ptr)
+ return -EIO;
+
+ adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
+
+ return 0;
+}
+
+static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
+{
+ const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
+ struct wm_adsp_buffer_region *region;
+ u32 offset = 0;
+ int i, ret;
+
+ for (i = 0; i < caps->num_regions; ++i) {
+ region = &buf->regions[i];
+
+ region->offset = offset;
+ region->mem_type = caps->region_defs[i].mem_type;
+
+ ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
+ &region->base_addr);
+ if (ret < 0)
+ return ret;
+
+ ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
+ &offset);
+ if (ret < 0)
+ return ret;
+
+ region->cumulative_size = offset;
+
+ adsp_dbg(buf->dsp,
+ "region=%d type=%d base=%04x off=%04x size=%04x\n",
+ i, region->mem_type, region->base_addr,
+ region->offset, region->cumulative_size);
+ }
+
+ return 0;
+}
+
+static int wm_adsp_buffer_init(struct wm_adsp *dsp)
+{
+ struct wm_adsp_compr_buf *buf;
+ int ret;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->dsp = dsp;
+
+ ret = wm_adsp_buffer_locate(buf);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
+ goto err_buffer;
+ }
+
+ buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
+ sizeof(*buf->regions), GFP_KERNEL);
+ if (!buf->regions) {
+ ret = -ENOMEM;
+ goto err_buffer;
+ }
+
+ ret = wm_adsp_buffer_populate(buf);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
+ goto err_regions;
+ }
+
+ dsp->buffer = buf;
+
+ return 0;
+
+err_regions:
+ kfree(buf->regions);
+err_buffer:
+ kfree(buf);
+ return ret;
+}
+
+static int wm_adsp_buffer_free(struct wm_adsp *dsp)
+{
+ if (dsp->buffer) {
+ kfree(dsp->buffer->regions);
+ kfree(dsp->buffer);
+
+ dsp->buffer = NULL;
+ }
+
+ return 0;
+}
+
+static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
+{
+ return compr->buf != NULL;
+}
+
+static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
+{
+ /*
+ * Note this will be more complex once each DSP can support multiple
+ * streams
+ */
+ if (!compr->dsp->buffer)
+ return -EINVAL;
+
+ compr->buf = compr->dsp->buffer;
+
+ return 0;
+}
+
+int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp *dsp = compr->dsp;
+ int ret = 0;
+
+ adsp_dbg(dsp, "Trigger: %d\n", cmd);
+
+ mutex_lock(&dsp->pwr_lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (wm_adsp_compr_attached(compr))
+ break;
+
+ ret = wm_adsp_compr_attach(compr);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to link buffer and stream: %d\n",
+ ret);
+ break;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
+
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 2d117cf0e953..43af093fafcf 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -15,6 +15,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/compress_driver.h>
#include "wmfw.h"
@@ -30,6 +31,9 @@ struct wm_adsp_alg_region {
unsigned int base;
};
+struct wm_adsp_compr;
+struct wm_adsp_compr_buf;
+
struct wm_adsp {
const char *part;
int num;
@@ -45,8 +49,8 @@ struct wm_adsp {
struct list_head alg_regions;
- int fw_id;
- int fw_id_version;
+ unsigned int fw_id;
+ unsigned int fw_id_version;
const struct wm_adsp_region *mem;
int num_mems;
@@ -59,9 +63,13 @@ struct wm_adsp {
struct work_struct boot_work;
+ struct wm_adsp_compr *compr;
+ struct wm_adsp_compr_buf *buffer;
+
+ struct mutex pwr_lock;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
- struct mutex debugfs_lock;
char *wmfw_file_name;
char *bin_file_name;
#endif
@@ -96,4 +104,13 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
+extern int wm_adsp_compr_open(struct wm_adsp *dsp,
+ struct snd_compr_stream *stream);
+extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
+extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params);
+extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps);
+extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
+
#endif
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 4495a40a9468..2ccb8bccc9d4 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -223,8 +223,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp)
/* wait for XDATA to be cleared */
cnt = 0;
- while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
- ~XRDATA) && (cnt < 100000))
+ while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
+ (cnt < 100000))
cnt++;
/* Release TX state machine */
@@ -681,8 +681,8 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
}
mcasp->tdm_slots = slots;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
mcasp->slot_width = slot_width;
return davinci_mcasp_set_ch_constraints(mcasp);
@@ -908,6 +908,14 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
FSRMOD(total_slots), FSRMOD(0x1FF));
+ /*
+ * If McASP is set to be TX/RX synchronous and the playback is
+ * not running already we need to configure the TX slots in
+ * order to have correct FSX on the bus
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
}
return 0;
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 6e6a70c5c2bd..825a1f480aab 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <sound/designware_i2s.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -93,7 +94,12 @@ struct dw_i2s_dev {
struct clk *clk;
int active;
unsigned int capability;
+ unsigned int quirks;
+ unsigned int i2s_reg_comp1;
+ unsigned int i2s_reg_comp2;
struct device *dev;
+ u32 ccr;
+ u32 xfer_resolution;
/* data related to DMA transfers b/w i2s and DMAC */
union dw_i2s_snd_dma_data play_dma_data;
@@ -213,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
return 0;
}
+static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+{
+ u32 ch_reg, irq;
+ struct i2s_clk_config_data *config = &dev->config;
+
+
+ i2s_disable_channels(dev, stream);
+
+ for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+ dev->xfer_resolution);
+ i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
+ irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
+ i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
+ i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
+ } else {
+ i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+ dev->xfer_resolution);
+ i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
+ irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
+ i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
+ i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
+ }
+
+ }
+}
+
static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
struct i2s_clk_config_data *config = &dev->config;
- u32 ccr, xfer_resolution, ch_reg, irq;
int ret;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
config->data_width = 16;
- ccr = 0x00;
- xfer_resolution = 0x02;
+ dev->ccr = 0x00;
+ dev->xfer_resolution = 0x02;
break;
case SNDRV_PCM_FORMAT_S24_LE:
config->data_width = 24;
- ccr = 0x08;
- xfer_resolution = 0x04;
+ dev->ccr = 0x08;
+ dev->xfer_resolution = 0x04;
break;
case SNDRV_PCM_FORMAT_S32_LE:
config->data_width = 32;
- ccr = 0x10;
- xfer_resolution = 0x05;
+ dev->ccr = 0x10;
+ dev->xfer_resolution = 0x05;
break;
default:
@@ -258,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- i2s_disable_channels(dev, substream->stream);
+ dw_i2s_config(dev, substream->stream);
- for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- i2s_write_reg(dev->i2s_base, TCR(ch_reg),
- xfer_resolution);
- i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
- irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
- i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
- i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
- } else {
- i2s_write_reg(dev->i2s_base, RCR(ch_reg),
- xfer_resolution);
- i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
- irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
- i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
- i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
- }
- }
-
- i2s_write_reg(dev->i2s_base, CCR, ccr);
+ i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
config->sample_rate = params_rate(params);
@@ -394,6 +409,23 @@ static const struct snd_soc_component_driver dw_i2s_component = {
};
#ifdef CONFIG_PM
+static int dw_i2s_runtime_suspend(struct device *dev)
+{
+ struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+
+ if (dw_dev->capability & DW_I2S_MASTER)
+ clk_disable(dw_dev->clk);
+ return 0;
+}
+
+static int dw_i2s_runtime_resume(struct device *dev)
+{
+ struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+
+ if (dw_dev->capability & DW_I2S_MASTER)
+ clk_enable(dw_dev->clk);
+ return 0;
+}
static int dw_i2s_suspend(struct snd_soc_dai *dai)
{
@@ -410,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
if (dev->capability & DW_I2S_MASTER)
clk_enable(dev->clk);
+
+ if (dai->playback_active)
+ dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
+ if (dai->capture_active)
+ dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
return 0;
}
@@ -459,8 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
* Read component parameter registers to extract
* the I2S block's configuration.
*/
- u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
- u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+ u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
u32 idx;
if (COMP1_TX_ENABLED(comp1)) {
@@ -503,7 +540,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
struct resource *res,
const struct i2s_platform_data *pdata)
{
- u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
+ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
u32 idx = COMP1_APB_DATA_WIDTH(comp1);
int ret;
@@ -607,6 +644,14 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (pdata) {
dev->capability = pdata->cap;
clk_id = NULL;
+ dev->quirks = pdata->quirks;
+ if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
+ dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
+ dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
+ } else {
+ dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+ dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+ }
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
} else {
clk_id = "i2sclk";
@@ -649,7 +694,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
goto err_clk_disable;
}
}
-
+ pm_runtime_enable(&pdev->dev);
return 0;
err_clk_disable:
@@ -665,6 +710,7 @@ static int dw_i2s_remove(struct platform_device *pdev)
if (dev->capability & DW_I2S_MASTER)
clk_disable_unprepare(dev->clk);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -677,12 +723,17 @@ static const struct of_device_id dw_i2s_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
#endif
+static const struct dev_pm_ops dwc_pm_ops = {
+ SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
+};
+
static struct platform_driver dw_i2s_driver = {
.probe = dw_i2s_probe,
.remove = dw_i2s_remove,
.driver = {
.name = "designware-i2s",
.of_match_table = of_match_ptr(dw_i2s_of_match),
+ .pm = &dwc_pm_ops,
},
};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 19c302b0d763..14dfdee05fd5 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -283,6 +283,8 @@ config SND_SOC_IMX_MC13783
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
+ # enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
+ depends on SND_AC97_CODEC || SND_AC97_CODEC=n
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_ESAI
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 1b05d1c5d9fd..562b3bd22d9a 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -107,6 +107,13 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"CPU-Capture", NULL, "Capture"},
};
+static const struct snd_soc_dapm_route audio_map_ac97[] = {
+ {"AC97 Playback", NULL, "ASRC-Playback"},
+ {"Playback", NULL, "AC97 Playback"},
+ {"ASRC-Capture", NULL, "AC97 Capture"},
+ {"AC97 Capture", NULL, "Capture"},
+};
+
/* Add all possible widgets into here without being redundant */
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
@@ -222,12 +229,15 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
enum snd_soc_bias_level level)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
unsigned int pll_out;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
if (dapm->dev != codec_dai->dev)
return 0;
@@ -414,14 +424,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(
+ &card->rtd_list, struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
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_soc_codec *codec = rtd->codec;
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
/*
@@ -574,7 +586,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dev = &pdev->dev;
priv->card.name = priv->name;
priv->card.dai_link = priv->dai_link;
- priv->card.dapm_routes = audio_map;
+ priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
+ audio_map_ac97 : audio_map;
priv->card.late_probe = fsl_asoc_card_late_probe;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 6d0636605ed2..dd1263b95dc7 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -31,21 +31,21 @@
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
/* Sample rates are aligned with that defined in pcm.h file */
-static const u8 process_option[][8][2] = {
- /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
- {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
- {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
- {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
- {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
- {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
- {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
- {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
- {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
- {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
- {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
- {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
- {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
+static const u8 process_option[][12][2] = {
+ /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
+ {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
+ {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
+ {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
+ {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
+ {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
+ {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
};
/* Corresponding to process_option */
@@ -55,7 +55,7 @@ static int supported_input_rate[] = {
};
static int supported_asrc_rate[] = {
- 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
};
/**
@@ -286,6 +286,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
+ if ((outrate > 8000 && outrate < 30000) &&
+ (outrate/inrate > 24 || inrate/outrate > 8)) {
+ pair_err("exceed supported ratio range [1/24, 8] for \
+ inrate/outrate: %d/%d\n", inrate, outrate);
+ return -EINVAL;
+ }
+
/* Validate input and output clock sources */
clk_index[IN] = clk_map[IN][config->inclk];
clk_index[OUT] = clk_map[OUT][config->outclk];
@@ -859,6 +866,10 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return PTR_ERR(asrc_priv->ipg_clk);
}
+ asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
+ if (IS_ERR(asrc_priv->spba_clk))
+ dev_warn(&pdev->dev, "failed to get spba clock\n");
+
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
@@ -939,6 +950,11 @@ static int fsl_asrc_runtime_resume(struct device *dev)
ret = clk_prepare_enable(asrc_priv->ipg_clk);
if (ret)
goto disable_mem_clk;
+ if (!IS_ERR(asrc_priv->spba_clk)) {
+ ret = clk_prepare_enable(asrc_priv->spba_clk);
+ if (ret)
+ goto disable_ipg_clk;
+ }
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
if (ret)
@@ -950,6 +966,9 @@ static int fsl_asrc_runtime_resume(struct device *dev)
disable_asrck_clk:
for (i--; i >= 0; i--)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+ if (!IS_ERR(asrc_priv->spba_clk))
+ clk_disable_unprepare(asrc_priv->spba_clk);
+disable_ipg_clk:
clk_disable_unprepare(asrc_priv->ipg_clk);
disable_mem_clk:
clk_disable_unprepare(asrc_priv->mem_clk);
@@ -963,6 +982,8 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+ if (!IS_ERR(asrc_priv->spba_clk))
+ clk_disable_unprepare(asrc_priv->spba_clk);
clk_disable_unprepare(asrc_priv->ipg_clk);
clk_disable_unprepare(asrc_priv->mem_clk);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 4aed63c4b431..68802cdc3f28 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -426,6 +426,7 @@ struct fsl_asrc_pair {
* @paddr: physical address to the base address of registers
* @mem_clk: clock source to access register
* @ipg_clk: clock source to drive peripheral
+ * @spba_clk: SPBA clock (optional, depending on SoC design)
* @asrck_clk: clock sources to driver ASRC internal logic
* @lock: spin lock for resource protection
* @pair: pair pointers
@@ -442,6 +443,7 @@ struct fsl_asrc {
unsigned long paddr;
struct clk *mem_clk;
struct clk *ipg_clk;
+ struct clk *spba_clk;
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
spinlock_t lock;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 45d4319b2079..26a90e12ede4 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -35,6 +35,7 @@
* @coreclk: clock source to access register
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
+ * @spbaclk: SPBA clock (optional, depending on SoC design)
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
@@ -54,6 +55,7 @@ struct fsl_esai {
struct clk *coreclk;
struct clk *extalclk;
struct clk *fsysclk;
+ struct clk *spbaclk;
u32 fifo_depth;
u32 slot_width;
u32 slots;
@@ -469,6 +471,11 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
ret = clk_prepare_enable(esai_priv->coreclk);
if (ret)
return ret;
+ if (!IS_ERR(esai_priv->spbaclk)) {
+ ret = clk_prepare_enable(esai_priv->spbaclk);
+ if (ret)
+ goto err_spbaclk;
+ }
if (!IS_ERR(esai_priv->extalclk)) {
ret = clk_prepare_enable(esai_priv->extalclk);
if (ret)
@@ -499,6 +506,9 @@ err_fsysclk:
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
err_extalck:
+ if (!IS_ERR(esai_priv->spbaclk))
+ clk_disable_unprepare(esai_priv->spbaclk);
+err_spbaclk:
clk_disable_unprepare(esai_priv->coreclk);
return ret;
@@ -564,6 +574,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
clk_disable_unprepare(esai_priv->fsysclk);
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
+ if (!IS_ERR(esai_priv->spbaclk))
+ clk_disable_unprepare(esai_priv->spbaclk);
clk_disable_unprepare(esai_priv->coreclk);
}
@@ -819,6 +831,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
PTR_ERR(esai_priv->fsysclk));
+ esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
+ if (IS_ERR(esai_priv->spbaclk))
+ dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
+ PTR_ERR(esai_priv->spbaclk));
+
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 3da278313591..fef264d27fd3 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -513,7 +513,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
@@ -563,6 +564,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+
+ /*
+ * For sai master mode, after several open/close sai,
+ * there will be no frame clock, and can't recover
+ * anymore. Add software reset to fix this issue.
+ * This is a hardware bug, and will be fix in the
+ * next sai version.
+ */
+ if (!sai->is_slave_mode) {
+ /* Software Reset for both Tx and Rx */
+ regmap_write(sai->regmap,
+ FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap,
+ FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ }
}
break;
default:
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index 1fc01ed3279d..f3d3d1ffa84e 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -62,6 +62,8 @@ int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
config = devm_kzalloc(&pdev->dev,
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
*config = imx_dmaengine_pcm_config;
if (size)
config->prealloc_buffer_size = size;
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index b38b98cae855..201a70d1027a 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -69,13 +69,16 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
struct imx_priv *priv = &card_priv;
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev;
unsigned int pll_out;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
if (dapm->dev != codec_dai->dev)
return 0;
@@ -135,12 +138,15 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
static int imx_wm8962_late_probe(struct snd_soc_card *card)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
struct imx_priv *priv = &card_priv;
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
data->clk_frequency, SND_SOC_CLOCK_IN);
if (ret < 0)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 54c33204541f..1ded8811598e 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -45,7 +45,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct simple_dai_props *dai_props =
- &priv->dai_props[rtd - rtd->card->rtd];
+ &priv->dai_props[rtd->num];
int ret;
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
@@ -64,7 +64,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct simple_dai_props *dai_props =
- &priv->dai_props[rtd - rtd->card->rtd];
+ &priv->dai_props[rtd->num];
clk_disable_unprepare(dai_props->cpu_dai.clk);
@@ -78,8 +78,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- &priv->dai_props[rtd - rtd->card->rtd];
+ struct simple_dai_props *dai_props = &priv->dai_props[rtd->num];
unsigned int mclk, mclk_fs = 0;
int ret = 0;
@@ -174,10 +173,9 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *codec = rtd->codec_dai;
struct snd_soc_dai *cpu = rtd->cpu_dai;
struct simple_dai_props *dai_props;
- int num, ret;
+ int ret;
- num = rtd - rtd->card->rtd;
- dai_props = &priv->dai_props[num];
+ dai_props = &priv->dai_props[rtd->num];
ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 7b778ab85f8b..d430ef5a4f38 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -144,7 +144,7 @@ config SND_SOC_INTEL_SKYLAKE
config SND_SOC_INTEL_SKL_RT286_MACH
tristate "ASoC Audio driver for SKL with RT286 I2S mode"
- depends on X86 && ACPI
+ depends on X86 && ACPI && I2C
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_RT286
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 0487cfaac538..8e475e823205 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -760,15 +760,15 @@ static int sst_platform_remove(struct platform_device *pdev)
static int sst_soc_prepare(struct device *dev)
{
struct sst_data *drv = dev_get_drvdata(dev);
- int i;
+ struct snd_soc_pcm_runtime *rtd;
/* suspend all pcms first */
snd_soc_suspend(drv->soc_card->dev);
snd_soc_poweroff(drv->soc_card->dev);
/* set the SSPs to idle */
- for (i = 0; i < drv->soc_card->num_rtd; i++) {
- struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
+ struct snd_soc_dai *dai = rtd->cpu_dai;
if (dai->active) {
send_ssp_cmd(dai, dai->name, 0);
@@ -782,11 +782,11 @@ static int sst_soc_prepare(struct device *dev)
static void sst_soc_complete(struct device *dev)
{
struct sst_data *drv = dev_get_drvdata(dev);
- int i;
+ struct snd_soc_pcm_runtime *rtd;
/* restart SSPs */
- for (i = 0; i < drv->soc_card->num_rtd; i++) {
- struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
+ struct snd_soc_dai *dai = rtd->cpu_dai;
if (dai->active) {
sst_handle_vb_timer(dai, true);
diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c
index 79547bec558b..4765ad474544 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c
@@ -377,6 +377,8 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
GFP_KERNEL);
+ if (!priv_data)
+ return -ENOMEM;
priv_data->byt = plat_data->dsp;
snd_soc_platform_set_drvdata(platform, priv_data);
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 4e2fcf188dd1..e36dad302bed 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -41,12 +41,9 @@ struct cht_mc_private {
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
{
- int i;
+ struct snd_soc_pcm_runtime *rtd;
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd;
-
- rtd = card->rtd + i;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
strlen(CHT_CODEC_DAI)))
return rtd->codec_dai;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 38d65a3529c4..1d2525a53bff 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -47,12 +47,9 @@ struct cht_mc_private {
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
{
- int i;
-
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_pcm_runtime *rtd;
- rtd = card->rtd + i;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
strlen(CHT_CODEC_DAI)))
return rtd->codec_dai;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 5621ccd92992..77fb3c419ca4 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -46,12 +46,9 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
{
- int i;
+ struct snd_soc_pcm_runtime *rtd;
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd;
-
- rtd = card->rtd + i;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
strlen(CHT_CODEC_DAI)))
return rtd->codec_dai;
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index a7854c8fc523..ffea427aeca8 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -1240,6 +1240,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
*/
ret = snd_soc_tplg_component_load(&platform->component,
&skl_tplg_ops, fw, 0);
+ release_firmware(fw);
if (ret < 0) {
dev_err(bus->dev, "tplg component load failed%d\n", ret);
return -EINVAL;
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c
index 584b2372339e..f83cc2bc0fc4 100644
--- a/sound/soc/omap/omap-hdmi-audio.c
+++ b/sound/soc/omap/omap-hdmi-audio.c
@@ -368,6 +368,8 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
card->owner = THIS_MODULE;
card->dai_link =
devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
card->dai_link->name = card->name;
card->dai_link->stream_name = card->name;
card->dai_link->cpu_dai_name = dev_name(ad->dssdev);
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 29bc60e85e92..5c8f9db50a47 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -81,8 +81,12 @@ static int rear_amp_power(struct snd_soc_codec *codec, int power)
static int rear_amp_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kctl, int event)
{
- struct snd_soc_codec *codec = widget->dapm->card->rtd[0].codec;
+ struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec = rtd->codec;
return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event));
}
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index a38a3029062c..5a806da89f42 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -152,8 +152,10 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
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);
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL_MASK,
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL(16));
if (ret != 0)
return ret;
@@ -280,7 +282,7 @@ static int rk_spdif_probe(struct platform_device *pdev)
int ret;
match = of_match_node(rk_spdif_match, np);
- if ((int) match->data == RK_SPDIF_RK3288) {
+ if (match->data == (void *)RK_SPDIF_RK3288) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
index 07f86a21046a..3ef12770ae12 100644
--- a/sound/soc/rockchip/rockchip_spdif.h
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -28,9 +28,9 @@
#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)
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
/*
* DMACR
@@ -42,7 +42,7 @@
#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)
+#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT)
/*
* XFER
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index e5f05e62fa3c..3dd246fa0059 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -58,11 +58,16 @@ static int bells_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_codec *codec;
struct bells_drvdata *bells = card->drvdata;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
+ codec_dai = rtd->codec_dai;
+ codec = codec_dai->codec;
+
if (dapm->dev != codec_dai->dev)
return 0;
@@ -99,11 +104,16 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_codec *codec;
struct bells_drvdata *bells = card->drvdata;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
+ codec_dai = rtd->codec_dai;
+ codec = codec_dai->codec;
+
if (dapm->dev != codec_dai->dev)
return 0;
@@ -137,14 +147,22 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
static int bells_late_probe(struct snd_soc_card *card)
{
struct bells_drvdata *bells = card->drvdata;
- struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
- struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
- struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *wm0010;
+ struct snd_soc_codec *codec;
+ struct snd_soc_dai *aif1_dai;
struct snd_soc_dai *aif2_dai;
struct snd_soc_dai *aif3_dai;
struct snd_soc_dai *wm9081_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name);
+ wm0010 = rtd->codec;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
+ codec = rtd->codec;
+ aif1_dai = rtd->codec_dai;
+
ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
ARIZONA_CLK_SRC_FLL1,
bells->sysclk_rate,
@@ -181,7 +199,8 @@ static int bells_late_probe(struct snd_soc_card *card)
return ret;
}
- aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name);
+ aif2_dai = rtd->cpu_dai;
ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
if (ret != 0) {
@@ -192,8 +211,9 @@ static int bells_late_probe(struct snd_soc_card *card)
if (card->num_rtd == DAI_CODEC_SUB)
return 0;
- aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
- wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name);
+ aif3_dai = rtd->cpu_dai;
+ wm9081_dai = rtd->codec_dai;
ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
if (ret != 0) {
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index 31a820eb0ac3..7cb204e649ca 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -23,9 +23,13 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *aif1_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ aif1_dai = rtd->codec_dai;
+
if (dapm->dev != aif1_dai->dev)
return 0;
@@ -66,9 +70,13 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *aif1_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ aif1_dai = rtd->codec_dai;
+
if (dapm->dev != aif1_dai->dev)
return 0;
@@ -168,9 +176,13 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_card *card = w->dapm->card;
- struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *aif2_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
+ aif2_dai = rtd->cpu_dai;
+
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
@@ -245,11 +257,19 @@ static struct snd_soc_jack littlemill_headset;
static int littlemill_late_probe(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec = card->rtd[0].codec;
- struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
- struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+ struct snd_soc_dai *aif1_dai;
+ struct snd_soc_dai *aif2_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec = rtd->codec;
+ aif1_dai = rtd->codec_dai;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
+ aif2_dai = rtd->cpu_dai;
+
ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c
index 596f1180a369..04217279fe25 100644
--- a/sound/soc/samsung/odroidx2_max98090.c
+++ b/sound/soc/samsung/odroidx2_max98090.c
@@ -25,10 +25,15 @@ static struct snd_soc_dai_link odroidx2_dai[];
static int odroidx2_late_probe(struct snd_soc_card *card)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
- struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
+ cpu_dai = rtd->cpu_dai;
+
ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
SND_SOC_CLOCK_IN);
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
index 07ce2cfa4845..d8ac907bbb0d 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -35,10 +35,15 @@ static struct snd_soc_dai_link snow_dai[] = {
static int snow_late_probe(struct snd_soc_card *card)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
- struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
+ cpu_dai = rtd->cpu_dai;
+
/* Set the MCLK rate for the codec */
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
FIN_PLL_RATE, SND_SOC_CLOCK_IN);
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index d1ae21c5e253..083ef5e21b17 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -25,9 +25,13 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
+ codec_dai = rtd->codec_dai;
+
if (dapm->dev != codec_dai->dev)
return 0;
@@ -57,9 +61,13 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
+ codec_dai = rtd->codec_dai;
+
if (dapm->dev != codec_dai->dev)
return 0;
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index 85ccfb7188cb..3310eda7cf53 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -23,9 +23,13 @@ static int tobermory_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
+
if (dapm->dev != codec_dai->dev)
return 0;
@@ -62,9 +66,13 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec_dai = rtd->codec_dai;
+
if (dapm->dev != codec_dai->dev)
return 0;
@@ -170,10 +178,15 @@ static struct snd_soc_jack_pin tobermory_headset_pins[] = {
static int tobermory_late_probe(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec = card->rtd[0].codec;
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_codec *codec;
+ struct snd_soc_dai *codec_dai;
int ret;
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ codec = rtd->codec;
+ codec_dai = rtd->codec_dai;
+
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index deed48ef28b8..e1da5654fa25 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -1033,14 +1033,13 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod))
{
- struct snd_soc_card *soc_card = rtd->card;
struct snd_card *card = rtd->card->snd_card;
struct snd_kcontrol *kctrl;
struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = name,
.info = rsnd_kctrl_info,
- .index = rtd - soc_card->rtd,
+ .index = rtd->num,
.get = rsnd_kctrl_get,
.put = rsnd_kctrl_put,
.private_value = (unsigned long)cfg,
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 76da7620904c..edcf4cc2e84f 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -235,7 +235,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc),
RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0),
- RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4),
+ RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4),
RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
index d61db9c385ea..94d23d8f2869 100644
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -75,7 +75,7 @@ static int rsrc_card_startup(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct rsrc_card_dai *dai_props =
- rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
+ rsrc_priv_to_props(priv, rtd->num);
return clk_prepare_enable(dai_props->clk);
}
@@ -85,7 +85,7 @@ static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct rsrc_card_dai *dai_props =
- rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
+ rsrc_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->clk);
}
@@ -101,7 +101,7 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *dai;
struct snd_soc_dai_link *dai_link;
struct rsrc_card_dai *dai_props;
- int num = rtd - rtd->card->rtd;
+ int num = rtd->num;
int ret;
dai_link = rsrc_priv_to_link(priv, num);
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 261b50217c48..68b439ed22d7 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -923,6 +923,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -937,6 +938,12 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
return 0;
/*
+ * SRC In doesn't work if DVC was enabled
+ */
+ if (dvc && !rsnd_io_is_play(io))
+ return 0;
+
+ /*
* enable sync convert
*/
ret = rsnd_kctrl_new_s(mod, io, rtd,
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index d40efc9fe0a9..733f5128eeff 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
@@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg {
int gpio_reset;
};
+struct snd_ac97_gpio_priv {
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+ unsigned int gpios_set;
+ struct snd_soc_codec *codec;
+};
+
static struct snd_ac97_bus soc_ac97_bus = {
.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
};
@@ -47,6 +56,117 @@ static void soc_ac97_device_release(struct device *dev)
kfree(to_ac97_t(dev));
}
+#ifdef CONFIG_GPIOLIB
+static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
+{
+ struct snd_ac97_gpio_priv *gpio_priv =
+ container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
+
+ return gpio_priv->codec;
+}
+
+static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset >= AC97_NUM_GPIOS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct snd_soc_codec *codec = gpio_to_codec(chip);
+
+ dev_dbg(codec->dev, "set gpio %d to output\n", offset);
+ return snd_soc_update_bits(codec, AC97_GPIO_CFG,
+ 1 << offset, 1 << offset);
+}
+
+static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct snd_soc_codec *codec = gpio_to_codec(chip);
+ int ret;
+
+ ret = snd_soc_read(codec, AC97_GPIO_STATUS);
+ dev_dbg(codec->dev, "get gpio %d : %d\n", offset,
+ ret < 0 ? ret : ret & (1 << offset));
+
+ return ret < 0 ? ret : !!(ret & (1 << offset));
+}
+
+static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct snd_ac97_gpio_priv *gpio_priv =
+ container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
+ struct snd_soc_codec *codec = gpio_to_codec(chip);
+
+ gpio_priv->gpios_set &= ~(1 << offset);
+ gpio_priv->gpios_set |= (!!value) << offset;
+ snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set);
+ dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value);
+}
+
+static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct snd_soc_codec *codec = gpio_to_codec(chip);
+
+ dev_dbg(codec->dev, "set gpio %d to output\n", offset);
+ snd_soc_ac97_gpio_set(chip, offset, value);
+ return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0);
+}
+
+static struct gpio_chip snd_soc_ac97_gpio_chip = {
+ .label = "snd_soc_ac97",
+ .owner = THIS_MODULE,
+ .request = snd_soc_ac97_gpio_request,
+ .direction_input = snd_soc_ac97_gpio_direction_in,
+ .get = snd_soc_ac97_gpio_get,
+ .direction_output = snd_soc_ac97_gpio_direction_out,
+ .set = snd_soc_ac97_gpio_set,
+ .can_sleep = 1,
+};
+
+static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
+ struct snd_soc_codec *codec)
+{
+ struct snd_ac97_gpio_priv *gpio_priv;
+ int ret;
+
+ gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL);
+ if (!gpio_priv)
+ return -ENOMEM;
+ ac97->gpio_priv = gpio_priv;
+ gpio_priv->codec = codec;
+ gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip;
+ gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS;
+ gpio_priv->gpio_chip.dev = codec->dev;
+ gpio_priv->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&gpio_priv->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+ return ret;
+}
+
+static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
+{
+ gpiochip_remove(&ac97->gpio_priv->gpio_chip);
+}
+#else
+static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
+ struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
+{
+}
+#endif
+
/**
* snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
* @codec: The CODEC for which to create the AC'97 device
@@ -119,6 +239,10 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
if (ret)
goto err_put_device;
+ ret = snd_soc_ac97_init_gpio(ac97, codec);
+ if (ret)
+ goto err_put_device;
+
return ac97;
err_put_device:
@@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
*/
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
{
+ snd_soc_ac97_free_gpio(ac97);
device_del(&ac97->dev);
ac97->bus = NULL;
put_device(&ac97->dev);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 24b096066a07..19f7486bf335 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -537,26 +537,75 @@ static inline void snd_soc_debugfs_exit(void)
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
const char *dai_link, int stream)
{
- int i;
+ struct snd_soc_pcm_runtime *rtd;
- for (i = 0; i < card->num_links; i++) {
- if (card->rtd[i].dai_link->no_pcm &&
- !strcmp(card->rtd[i].dai_link->name, dai_link))
- return card->rtd[i].pcm->streams[stream].substream;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (rtd->dai_link->no_pcm &&
+ !strcmp(rtd->dai_link->name, dai_link))
+ return rtd->pcm->streams[stream].substream;
}
dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
return NULL;
}
EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
+static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
+ struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
+ if (!rtd)
+ return NULL;
+
+ rtd->card = card;
+ rtd->dai_link = dai_link;
+ rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) *
+ dai_link->num_codecs,
+ GFP_KERNEL);
+ if (!rtd->codec_dais) {
+ kfree(rtd);
+ return NULL;
+ }
+
+ return rtd;
+}
+
+static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
+{
+ if (rtd && rtd->codec_dais)
+ kfree(rtd->codec_dais);
+ kfree(rtd);
+}
+
+static void soc_add_pcm_runtime(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ list_add_tail(&rtd->list, &card->rtd_list);
+ rtd->num = card->num_rtd;
+ card->num_rtd++;
+}
+
+static void soc_remove_pcm_runtimes(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd, *_rtd;
+
+ list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) {
+ list_del(&rtd->list);
+ soc_free_pcm_runtime(rtd);
+ }
+
+ card->num_rtd = 0;
+}
+
struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
const char *dai_link)
{
- int i;
+ struct snd_soc_pcm_runtime *rtd;
- for (i = 0; i < card->num_links; i++) {
- if (!strcmp(card->rtd[i].dai_link->name, dai_link))
- return &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (!strcmp(rtd->dai_link->name, dai_link))
+ return rtd;
}
dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
return NULL;
@@ -578,7 +627,8 @@ int snd_soc_suspend(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
struct snd_soc_codec *codec;
- int i, j;
+ struct snd_soc_pcm_runtime *rtd;
+ int i;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
@@ -595,13 +645,13 @@ int snd_soc_suspend(struct device *dev)
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
/* mute any active DACs */
- for (i = 0; i < card->num_rtd; i++) {
+ list_for_each_entry(rtd, &card->rtd_list, list) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
- for (j = 0; j < card->rtd[i].num_codecs; j++) {
- struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *dai = rtd->codec_dais[i];
struct snd_soc_dai_driver *drv = dai->driver;
if (drv->ops->digital_mute && dai->playback_active)
@@ -610,20 +660,20 @@ int snd_soc_suspend(struct device *dev)
}
/* suspend all pcms */
- for (i = 0; i < card->num_rtd; i++) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (rtd->dai_link->ignore_suspend)
continue;
- snd_pcm_suspend_all(card->rtd[i].pcm);
+ snd_pcm_suspend_all(rtd->pcm);
}
if (card->suspend_pre)
card->suspend_pre(card);
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
@@ -631,19 +681,19 @@ int snd_soc_suspend(struct device *dev)
}
/* close any waiting streams */
- for (i = 0; i < card->num_rtd; i++)
- flush_delayed_work(&card->rtd[i].delayed_work);
+ list_for_each_entry(rtd, &card->rtd_list, list)
+ flush_delayed_work(&rtd->delayed_work);
- for (i = 0; i < card->num_rtd; i++) {
+ list_for_each_entry(rtd, &card->rtd_list, list) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
- snd_soc_dapm_stream_event(&card->rtd[i],
+ snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_SUSPEND);
- snd_soc_dapm_stream_event(&card->rtd[i],
+ snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_CAPTURE,
SND_SOC_DAPM_STREAM_SUSPEND);
}
@@ -690,10 +740,10 @@ int snd_soc_suspend(struct device *dev)
}
}
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
@@ -717,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work)
{
struct snd_soc_card *card =
container_of(work, struct snd_soc_card, deferred_resume_work);
+ struct snd_soc_pcm_runtime *rtd;
struct snd_soc_codec *codec;
- int i, j;
+ int i;
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
* so userspace apps are blocked from touching us
@@ -733,10 +784,10 @@ static void soc_resume_deferred(struct work_struct *work)
card->resume_pre(card);
/* resume control bus DAIs */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
@@ -751,28 +802,28 @@ static void soc_resume_deferred(struct work_struct *work)
}
}
- for (i = 0; i < card->num_rtd; i++) {
+ list_for_each_entry(rtd, &card->rtd_list, list) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
- snd_soc_dapm_stream_event(&card->rtd[i],
+ snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_RESUME);
- snd_soc_dapm_stream_event(&card->rtd[i],
+ snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_CAPTURE,
SND_SOC_DAPM_STREAM_RESUME);
}
/* unmute any active DACs */
- for (i = 0; i < card->num_rtd; i++) {
+ list_for_each_entry(rtd, &card->rtd_list, list) {
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
- for (j = 0; j < card->rtd[i].num_codecs; j++) {
- struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *dai = rtd->codec_dais[i];
struct snd_soc_dai_driver *drv = dai->driver;
if (drv->ops->digital_mute && dai->playback_active)
@@ -780,10 +831,10 @@ static void soc_resume_deferred(struct work_struct *work)
}
}
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- if (card->rtd[i].dai_link->ignore_suspend)
+ if (rtd->dai_link->ignore_suspend)
continue;
if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
@@ -795,12 +846,12 @@ static void soc_resume_deferred(struct work_struct *work)
dev_dbg(card->dev, "ASoC: resume work completed\n");
- /* userspace can access us now we are back as we were before */
- snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
-
/* Recheck all endpoints too, their state is affected by suspend */
dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
+
+ /* userspace can access us now we are back as we were before */
+ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
}
/* powers up audio subsystem after a suspend */
@@ -808,15 +859,14 @@ int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
bool bus_control = false;
- int i;
+ struct snd_soc_pcm_runtime *rtd;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
return 0;
/* activate pins from sleep state */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai **codec_dais = rtd->codec_dais;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int j;
@@ -837,8 +887,8 @@ int snd_soc_resume(struct device *dev)
* have that problem and may take a substantial amount of time to resume
* due to I/O costs and anti-pop so handle them out of line.
*/
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
bus_control |= cpu_dai->driver->bus_control;
}
if (bus_control) {
@@ -910,18 +960,41 @@ static struct snd_soc_dai *snd_soc_find_dai(
return NULL;
}
-static int soc_bind_dai_link(struct snd_soc_card *card, int num)
+static bool soc_is_dai_link_bound(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
{
- struct snd_soc_dai_link *dai_link = &card->dai_link[num];
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+ struct snd_soc_pcm_runtime *rtd;
+
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (rtd->dai_link == dai_link)
+ return true;
+ }
+
+ return false;
+}
+
+static int soc_bind_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
struct snd_soc_dai_link_component cpu_dai_component;
- struct snd_soc_dai **codec_dais = rtd->codec_dais;
+ struct snd_soc_dai **codec_dais;
struct snd_soc_platform *platform;
const char *platform_name;
int i;
- dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
+ dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
+
+ rtd = soc_new_pcm_runtime(card, dai_link);
+ if (!rtd)
+ return -ENOMEM;
+
+ if (soc_is_dai_link_bound(card, dai_link)) {
+ dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
+ dai_link->name);
+ return 0;
+ }
cpu_dai_component.name = dai_link->cpu_name;
cpu_dai_component.of_node = dai_link->cpu_of_node;
@@ -930,18 +1003,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
if (!rtd->cpu_dai) {
dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
- return -EPROBE_DEFER;
+ goto _err_defer;
}
rtd->num_codecs = dai_link->num_codecs;
/* Find CODEC from registered CODECs */
+ codec_dais = rtd->codec_dais;
for (i = 0; i < rtd->num_codecs; i++) {
codec_dais[i] = snd_soc_find_dai(&codecs[i]);
if (!codec_dais[i]) {
dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
codecs[i].dai_name);
- return -EPROBE_DEFER;
+ goto _err_defer;
}
}
@@ -973,9 +1047,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
return -EPROBE_DEFER;
}
- card->num_rtd++;
-
+ soc_add_pcm_runtime(card, rtd);
return 0;
+
+_err_defer:
+ soc_free_pcm_runtime(rtd);
+ return -EPROBE_DEFER;
}
static void soc_remove_component(struct snd_soc_component *component)
@@ -1014,9 +1091,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order)
}
}
-static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
+static void soc_remove_link_dais(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd, int order)
{
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
int i;
/* unregister the rtd device */
@@ -1032,10 +1109,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
soc_remove_dai(rtd->cpu_dai, order);
}
-static void soc_remove_link_components(struct snd_soc_card *card, int num,
- int order)
+static void soc_remove_link_components(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd, int order)
{
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_component *component;
@@ -1061,22 +1137,199 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
static void soc_remove_dai_links(struct snd_soc_card *card)
{
- int dai, order;
+ int order;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai_link *link, *_link;
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- for (dai = 0; dai < card->num_rtd; dai++)
- soc_remove_link_dais(card, dai, order);
+ list_for_each_entry(rtd, &card->rtd_list, list)
+ soc_remove_link_dais(card, rtd, order);
}
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- for (dai = 0; dai < card->num_rtd; dai++)
- soc_remove_link_components(card, dai, order);
+ list_for_each_entry(rtd, &card->rtd_list, list)
+ soc_remove_link_components(card, rtd, order);
}
- card->num_rtd = 0;
+ list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
+ if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
+ dev_warn(card->dev, "Topology forgot to remove link %s?\n",
+ link->name);
+
+ list_del(&link->list);
+ card->num_dai_links--;
+ }
+}
+
+static int snd_soc_init_multicodec(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ /* Legacy codec/codec_dai link is a single entry in multicodec */
+ if (dai_link->codec_name || dai_link->codec_of_node ||
+ dai_link->codec_dai_name) {
+ dai_link->num_codecs = 1;
+
+ dai_link->codecs = devm_kzalloc(card->dev,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!dai_link->codecs)
+ return -ENOMEM;
+
+ dai_link->codecs[0].name = dai_link->codec_name;
+ dai_link->codecs[0].of_node = dai_link->codec_of_node;
+ dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
+ }
+
+ if (!dai_link->codecs) {
+ dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int soc_init_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
+{
+ int i, ret;
+
+ ret = snd_soc_init_multicodec(card, link);
+ if (ret) {
+ dev_err(card->dev, "ASoC: failed to init multicodec\n");
+ return ret;
+ }
+
+ for (i = 0; i < link->num_codecs; i++) {
+ /*
+ * Codec must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (!!link->codecs[i].name ==
+ !!link->codecs[i].of_node) {
+ dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ /* Codec DAI name must be specified */
+ if (!link->codecs[i].dai_name) {
+ dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Platform may be specified by either name or OF node, but
+ * can be left unspecified, and a dummy platform will be used.
+ */
+ if (link->platform_name && link->platform_of_node) {
+ dev_err(card->dev,
+ "ASoC: Both platform name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * CPU device may be specified by either name or OF node, but
+ * can be left unspecified, and will be matched based on DAI
+ * name alone..
+ */
+ if (link->cpu_name && link->cpu_of_node) {
+ dev_err(card->dev,
+ "ASoC: Neither/both cpu name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ /*
+ * At least one of CPU DAI name or CPU device name/node must be
+ * specified
+ */
+ if (!link->cpu_dai_name &&
+ !(link->cpu_name || link->cpu_of_node)) {
+ dev_err(card->dev,
+ "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * snd_soc_add_dai_link - Add a DAI link dynamically
+ * @card: The ASoC card to which the DAI link is added
+ * @dai_link: The new DAI link to add
+ *
+ * This function adds a DAI link to the ASoC card's link list.
+ *
+ * Note: Topology can use this API to add DAI links when probing the
+ * topology component. And machine drivers can still define static
+ * DAI links in dai_link array.
+ */
+int snd_soc_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ if (dai_link->dobj.type
+ && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
+ dev_err(card->dev, "Invalid dai link type %d\n",
+ dai_link->dobj.type);
+ return -EINVAL;
+ }
+
+ lockdep_assert_held(&client_mutex);
+ /* Notify the machine driver for extra initialization
+ * on the link created by topology.
+ */
+ if (dai_link->dobj.type && card->add_dai_link)
+ card->add_dai_link(card, dai_link);
+
+ list_add_tail(&dai_link->list, &card->dai_link_list);
+ card->num_dai_links++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
+
+/**
+ * snd_soc_remove_dai_link - Remove a DAI link from the list
+ * @card: The ASoC card that owns the link
+ * @dai_link: The DAI link to remove
+ *
+ * This function removes a DAI link from the ASoC card's link list.
+ *
+ * For DAI links previously added by topology, topology should
+ * remove them by using the dobj embedded in the link.
+ */
+void snd_soc_remove_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_dai_link *link, *_link;
+
+ if (dai_link->dobj.type
+ && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
+ dev_err(card->dev, "Invalid dai link type %d\n",
+ dai_link->dobj.type);
+ return;
+ }
+
+ lockdep_assert_held(&client_mutex);
+ /* Notify the machine driver for extra destruction
+ * on the link created by topology.
+ */
+ if (dai_link->dobj.type && card->remove_dai_link)
+ card->remove_dai_link(card, dai_link);
+
+ list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
+ if (link == dai_link) {
+ list_del(&link->list);
+ card->num_dai_links--;
+ return;
+ }
+ }
}
+EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
static void soc_set_name_prefix(struct snd_soc_card *card,
struct snd_soc_component *component)
@@ -1220,10 +1473,10 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static int soc_probe_link_components(struct snd_soc_card *card, int num,
+static int soc_probe_link_components(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd,
int order)
{
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_component *component;
int i, ret;
@@ -1283,35 +1536,35 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dapm_widget *play_w, *capture_w;
+ struct snd_soc_dapm_widget *sink, *source;
int ret;
if (rtd->num_codecs > 1)
dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
/* link the DAI widgets */
- play_w = codec_dai->playback_widget;
- capture_w = cpu_dai->capture_widget;
- if (play_w && capture_w) {
+ sink = codec_dai->playback_widget;
+ source = cpu_dai->capture_widget;
+ if (sink && source) {
ret = snd_soc_dapm_new_pcm(card, dai_link->params,
- dai_link->num_params, capture_w,
- play_w);
+ dai_link->num_params,
+ source, sink);
if (ret != 0) {
dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
- play_w->name, capture_w->name, ret);
+ sink->name, source->name, ret);
return ret;
}
}
- play_w = cpu_dai->playback_widget;
- capture_w = codec_dai->capture_widget;
- if (play_w && capture_w) {
+ sink = cpu_dai->playback_widget;
+ source = codec_dai->capture_widget;
+ if (sink && source) {
ret = snd_soc_dapm_new_pcm(card, dai_link->params,
- dai_link->num_params, capture_w,
- play_w);
+ dai_link->num_params,
+ source, sink);
if (ret != 0) {
dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
- play_w->name, capture_w->name, ret);
+ sink->name, source->name, ret);
return ret;
}
}
@@ -1319,15 +1572,15 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
return 0;
}
-static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
+static int soc_probe_link_dais(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd, int order)
{
- struct snd_soc_dai_link *dai_link = &card->dai_link[num];
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
- card->name, num, order);
+ card->name, rtd->num, order);
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
@@ -1372,7 +1625,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
if (cpu_dai->driver->compress_new) {
/*create compress_device"*/
- ret = cpu_dai->driver->compress_new(rtd, num);
+ ret = cpu_dai->driver->compress_new(rtd, rtd->num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
@@ -1382,7 +1635,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
if (!dai_link->params) {
/* create the pcm */
- ret = soc_new_pcm(rtd, num);
+ ret = soc_new_pcm(rtd, rtd->num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
dai_link->stream_name, ret);
@@ -1552,6 +1805,8 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai_link *dai_link;
int ret, i, order;
mutex_lock(&client_mutex);
@@ -1559,7 +1814,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
/* bind DAIs */
for (i = 0; i < card->num_links; i++) {
- ret = soc_bind_dai_link(card, i);
+ ret = soc_bind_dai_link(card, &card->dai_link[i]);
if (ret != 0)
goto base_error;
}
@@ -1571,6 +1826,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
goto base_error;
}
+ /* add predefined DAI links to the list */
+ for (i = 0; i < card->num_links; i++)
+ snd_soc_add_dai_link(card, card->dai_link+i);
+
/* initialize the register cache for each available codec */
list_for_each_entry(codec, &codec_list, list) {
if (codec->cache_init)
@@ -1624,8 +1883,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
/* probe all components used by DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- for (i = 0; i < card->num_links; i++) {
- ret = soc_probe_link_components(card, i, order);
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ ret = soc_probe_link_components(card, rtd, order);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
@@ -1635,11 +1894,26 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
+ /* Find new DAI links added during probing components and bind them.
+ * Components with topology may bring new DAIs and DAI links.
+ */
+ list_for_each_entry(dai_link, &card->dai_link_list, list) {
+ if (soc_is_dai_link_bound(card, dai_link))
+ continue;
+
+ ret = soc_init_dai_link(card, dai_link);
+ if (ret)
+ goto probe_dai_err;
+ ret = soc_bind_dai_link(card, dai_link);
+ if (ret)
+ goto probe_dai_err;
+ }
+
/* probe all DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- for (i = 0; i < card->num_links; i++) {
- ret = soc_probe_link_dais(card, i, order);
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ ret = soc_probe_link_dais(card, rtd, order);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
@@ -1733,6 +2007,7 @@ card_probe_error:
snd_card_free(card->snd_card);
base_error:
+ soc_remove_pcm_runtimes(card);
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
@@ -1763,13 +2038,12 @@ static int soc_probe(struct platform_device *pdev)
static int soc_cleanup_card_resources(struct snd_soc_card *card)
{
+ struct snd_soc_pcm_runtime *rtd;
int i;
/* make sure any delayed work runs */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list)
flush_delayed_work(&rtd->delayed_work);
- }
/* remove auxiliary devices */
for (i = 0; i < card->num_aux_devs; i++)
@@ -1777,6 +2051,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
/* remove and free each DAI */
soc_remove_dai_links(card);
+ soc_remove_pcm_runtimes(card);
soc_cleanup_card_debugfs(card);
@@ -1803,29 +2078,26 @@ static int soc_remove(struct platform_device *pdev)
int snd_soc_poweroff(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- int i;
+ struct snd_soc_pcm_runtime *rtd;
if (!card->instantiated)
return 0;
/* Flush out pmdown_time work - we actually do want to run it
* now, we're shutting down so no imminent restart. */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list)
flush_delayed_work(&rtd->delayed_work);
- }
snd_soc_dapm_shutdown(card);
/* deactivate pins to sleep state */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int j;
+ int i;
pinctrl_pm_select_sleep_state(cpu_dai->dev);
- for (j = 0; j < rtd->num_codecs; j++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
pinctrl_pm_select_sleep_state(codec_dai->dev);
}
}
@@ -2301,33 +2573,6 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
-static int snd_soc_init_multicodec(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- /* Legacy codec/codec_dai link is a single entry in multicodec */
- if (dai_link->codec_name || dai_link->codec_of_node ||
- dai_link->codec_dai_name) {
- dai_link->num_codecs = 1;
-
- dai_link->codecs = devm_kzalloc(card->dev,
- sizeof(struct snd_soc_dai_link_component),
- GFP_KERNEL);
- if (!dai_link->codecs)
- return -ENOMEM;
-
- dai_link->codecs[0].name = dai_link->codec_name;
- dai_link->codecs[0].of_node = dai_link->codec_of_node;
- dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
- }
-
- if (!dai_link->codecs) {
- dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
/**
* snd_soc_register_card - Register a card with the ASoC core
*
@@ -2336,7 +2581,8 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
- int i, j, ret;
+ int i, ret;
+ struct snd_soc_pcm_runtime *rtd;
if (!card->name || !card->dev)
return -EINVAL;
@@ -2344,63 +2590,11 @@ int snd_soc_register_card(struct snd_soc_card *card)
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
- ret = snd_soc_init_multicodec(card, link);
+ ret = soc_init_dai_link(card, link);
if (ret) {
- dev_err(card->dev, "ASoC: failed to init multicodec\n");
- return ret;
- }
-
- for (j = 0; j < link->num_codecs; j++) {
- /*
- * Codec must be specified by 1 of name or OF node,
- * not both or neither.
- */
- if (!!link->codecs[j].name ==
- !!link->codecs[j].of_node) {
- dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
- /* Codec DAI name must be specified */
- if (!link->codecs[j].dai_name) {
- dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
- link->name);
- return -EINVAL;
- }
- }
-
- /*
- * Platform may be specified by either name or OF node, but
- * can be left unspecified, and a dummy platform will be used.
- */
- if (link->platform_name && link->platform_of_node) {
- dev_err(card->dev,
- "ASoC: Both platform name/of_node are set for %s\n",
+ dev_err(card->dev, "ASoC: failed to init link %s\n",
link->name);
- return -EINVAL;
- }
-
- /*
- * CPU device may be specified by either name or OF node, but
- * can be left unspecified, and will be matched based on DAI
- * name alone..
- */
- if (link->cpu_name && link->cpu_of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both cpu name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
- /*
- * At least one of CPU DAI name or CPU device name/node must be
- * specified
- */
- if (!link->cpu_dai_name &&
- !(link->cpu_name || link->cpu_of_node)) {
- dev_err(card->dev,
- "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
+ return ret;
}
}
@@ -2408,25 +2602,18 @@ int snd_soc_register_card(struct snd_soc_card *card)
snd_soc_initialize_card_lists(card);
- card->rtd = devm_kzalloc(card->dev,
+ INIT_LIST_HEAD(&card->dai_link_list);
+ card->num_dai_links = 0;
+
+ INIT_LIST_HEAD(&card->rtd_list);
+ card->num_rtd = 0;
+
+ card->rtd_aux = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
- (card->num_links + card->num_aux_devs),
+ card->num_aux_devs,
GFP_KERNEL);
- if (card->rtd == NULL)
+ if (card->rtd_aux == NULL)
return -ENOMEM;
- card->num_rtd = 0;
- card->rtd_aux = &card->rtd[card->num_links];
-
- for (i = 0; i < card->num_links; i++) {
- card->rtd[i].card = card;
- card->rtd[i].dai_link = &card->dai_link[i];
- card->rtd[i].codec_dais = devm_kzalloc(card->dev,
- sizeof(struct snd_soc_dai *) *
- (card->rtd[i].dai_link->num_codecs),
- GFP_KERNEL);
- if (card->rtd[i].codec_dais == NULL)
- return -ENOMEM;
- }
for (i = 0; i < card->num_aux_devs; i++)
card->rtd_aux[i].card = card;
@@ -2442,8 +2629,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
return ret;
/* deactivate pins to sleep state */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int j;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 016eba10b1ec..5a2812fa8946 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1300,7 +1300,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
static int dapm_always_on_check_power(struct snd_soc_dapm_widget *w)
{
- return 1;
+ return w->connected;
}
static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
@@ -2293,6 +2293,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
kfree(w);
}
+void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm)
+{
+ dapm->path_sink_cache.widget = NULL;
+ dapm->path_source_cache.widget = NULL;
+}
+
/* free all dapm widgets and resources */
static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
{
@@ -2303,6 +2309,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
continue;
snd_soc_dapm_free_widget(w);
}
+ snd_soc_dapm_reset_cache(dapm);
}
static struct snd_soc_dapm_widget *dapm_find_widget(
@@ -3351,6 +3358,11 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_always_on_check_power;
break;
+ case snd_soc_dapm_sink:
+ w->is_ep = SND_SOC_DAPM_EP_SINK;
+ w->power_check = dapm_always_on_check_power;
+ break;
+
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
case snd_soc_dapm_switch:
@@ -3893,13 +3905,10 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
{
- struct snd_soc_pcm_runtime *rtd = card->rtd;
- int i;
+ struct snd_soc_pcm_runtime *rtd;
/* for each BE DAI link... */
- for (i = 0; i < card->num_rtd; i++) {
- rtd = &card->rtd[i];
-
+ list_for_each_entry(rtd, &card->rtd_list, list) {
/*
* dynamic FE links have no fixed DAI mapping.
* CODEC<->CODEC links have no direct connection.
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index ecd38e52285a..2f67ba6d7a8f 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -404,7 +404,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index c86dc96e8986..2a2ca2209656 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1213,11 +1213,10 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
struct snd_soc_dapm_widget *widget, int stream)
{
struct snd_soc_pcm_runtime *be;
- int i, j;
+ int i;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- for (i = 0; i < card->num_links; i++) {
- be = &card->rtd[i];
+ list_for_each_entry(be, &card->rtd_list, list) {
if (!be->dai_link->no_pcm)
continue;
@@ -1225,16 +1224,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (be->cpu_dai->playback_widget == widget)
return be;
- for (j = 0; j < be->num_codecs; j++) {
- struct snd_soc_dai *dai = be->codec_dais[j];
+ for (i = 0; i < be->num_codecs; i++) {
+ struct snd_soc_dai *dai = be->codec_dais[i];
if (dai->playback_widget == widget)
return be;
}
}
} else {
- for (i = 0; i < card->num_links; i++) {
- be = &card->rtd[i];
+ list_for_each_entry(be, &card->rtd_list, list) {
if (!be->dai_link->no_pcm)
continue;
@@ -1242,8 +1240,8 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (be->cpu_dai->capture_widget == widget)
return be;
- for (j = 0; j < be->num_codecs; j++) {
- struct snd_soc_dai *dai = be->codec_dais[j];
+ for (i = 0; i < be->num_codecs; i++) {
+ struct snd_soc_dai *dai = be->codec_dais[i];
if (dai->capture_widget == widget)
return be;
}
@@ -1616,6 +1614,56 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
snd_pcm_stream_unlock_irq(substream);
}
+static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
+ int stream)
+{
+ struct snd_soc_dpcm *dpcm;
+ struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+ struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
+ int err;
+
+ /* apply symmetry for FE */
+ if (soc_pcm_has_symmetry(fe_substream))
+ fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ /* Symmetry only applies if we've got an active stream. */
+ if (fe_cpu_dai->active) {
+ err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+ if (err < 0)
+ return err;
+ }
+
+ /* apply symmetry for BE */
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *be_substream =
+ snd_soc_dpcm_get_substream(be, stream);
+ struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
+ int i;
+
+ if (soc_pcm_has_symmetry(be_substream))
+ be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+ /* Symmetry only applies if we've got an active stream. */
+ if (rtd->cpu_dai->active) {
+ err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (rtd->codec_dais[i]->active) {
+ err = soc_pcm_apply_symmetry(be_substream,
+ rtd->codec_dais[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
@@ -1644,6 +1692,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
+ ret = dpcm_apply_symmetry(fe_substream, stream);
+ if (ret < 0) {
+ dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
+ ret);
+ goto unwind;
+ }
+
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
@@ -2115,7 +2170,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
continue;
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
continue;
dev_dbg(be->dev, "ASoC: prepare BE %s\n",
@@ -2343,12 +2399,12 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
*/
int soc_dpcm_runtime_update(struct snd_soc_card *card)
{
- int i, old, new, paths;
+ struct snd_soc_pcm_runtime *fe;
+ int old, new, paths;
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- for (i = 0; i < card->num_rtd; i++) {
+ list_for_each_entry(fe, &card->rtd_list, list) {
struct snd_soc_dapm_widget_list *list;
- struct snd_soc_pcm_runtime *fe = &card->rtd[i];
/* make sure link is FE */
if (!fe->dai_link->dynamic)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 8d7ec80af51b..6963ba20991c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -531,7 +531,7 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
/* TLV bytes controls need standard kcontrol info handler,
* TLV callback and extended put/get handlers.
*/
- k->info = snd_soc_bytes_info;
+ k->info = snd_soc_bytes_info_ext;
k->tlv.c = snd_soc_bytes_tlv_callback;
ext_ops = tplg->bytes_ext_ops;
@@ -1805,6 +1805,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
snd_soc_tplg_widget_remove(w);
snd_soc_dapm_free_widget(w);
}
+ snd_soc_dapm_reset_cache(dapm);
}
EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all);
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 843f037a317d..5c2bc53f0a9b 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -669,6 +669,7 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
+ player->substream = substream;
player->clk_adj = 0;
@@ -950,6 +951,8 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
if (player->state != UNIPERIF_STATE_STOPPED)
/* Stop the player */
uni_player_stop(player);
+
+ player->substream = NULL;
}
static int uni_player_parse_dt_clk_glue(struct platform_device *pdev,
@@ -989,7 +992,7 @@ static int uni_player_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- if (of_property_read_u32(pnode, "version", &player->ver) ||
+ if (of_property_read_u32(pnode, "st,version", &player->ver) ||
player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
dev_err(dev, "Unknown uniperipheral version ");
return -EINVAL;
@@ -998,13 +1001,13 @@ 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;
- if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) {
+ if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) {
dev_err(dev, "uniperipheral id not defined");
return -EINVAL;
}
/* Read the device mode property */
- if (of_property_read_string(pnode, "mode", &mode)) {
+ if (of_property_read_string(pnode, "st,mode", &mode)) {
dev_err(dev, "uniperipheral mode not defined");
return -EINVAL;
}
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index f791239a3087..8a0eb2050169 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -316,7 +316,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- if (of_property_read_u32(node, "version", &reader->ver) ||
+ if (of_property_read_u32(node, "st,version", &reader->ver) ||
reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
dev_err(&pdev->dev, "Unknown uniperipheral version ");
return -EINVAL;
@@ -346,7 +346,6 @@ int uni_reader_init(struct platform_device *pdev,
reader->hw = &uni_reader_pcm_hw;
reader->dai_ops = &uni_reader_dai_ops;
- dev_err(reader->dev, "%s: enter\n", __func__);
ret = uni_reader_parse_dt(pdev, reader);
if (ret < 0) {
dev_err(reader->dev, "Failed to parse DeviceTree");
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index bcbf4da168b6..44f170c73b06 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -2,6 +2,7 @@
* 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>
+ * Copyright 2015 Adam Sampson <ats@offog.org>
*
* Based on the Allwinner SDK driver, released under the GPL.
*
@@ -27,6 +28,7 @@
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -69,6 +71,7 @@
/* Codec ADC register offsets and bit fields */
#define SUN4I_CODEC_ADC_FIFOC (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
#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)
@@ -101,17 +104,14 @@ struct sun4i_codec {
struct regmap *regmap;
struct clk *clk_apb;
struct clk *clk_module;
+ struct gpio_desc *gpio_pa;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
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),
@@ -125,37 +125,50 @@ static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
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 void sun4i_codec_start_capture(struct sun4i_codec *scodec)
+{
+ /* Enable ADC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
+ BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
+{
+ /* Disable ADC DRQ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_ADC_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);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sun4i_codec_start_playback(scodec);
+ else
+ sun4i_codec_start_capture(scodec);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sun4i_codec_stop_playback(scodec);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sun4i_codec_stop_playback(scodec);
+ else
+ sun4i_codec_stop_capture(scodec);
break;
default:
@@ -165,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int sun4i_codec_prepare_capture(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 RX FIFO */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
+ BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
+
+
+ /* Set RX FIFO trigger level */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
+ 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
+
+ /*
+ * FIXME: Undocumented in the datasheet, but
+ * Allwinner's code mentions that it is related
+ * related to microphone gain
+ */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
+ 0x3 << 25,
+ 0x1 << 25);
+
+ if (of_device_is_compatible(scodec->dev->of_node,
+ "allwinner,sun7i-a20-codec"))
+ /* FIXME: Undocumented bits */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
+ 0x3 << 8,
+ 0x1 << 8);
+
+ /* Fill most significant bits with valid data MSB */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
+ BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
+
+ return 0;
+}
+
+static int sun4i_codec_prepare_playback(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;
/* Flush the TX FIFO */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -202,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
0);
return 0;
+};
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return sun4i_codec_prepare_playback(substream, dai);
+
+ return sun4i_codec_prepare_capture(substream, dai);
}
static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
@@ -276,30 +337,32 @@ static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
}
}
-static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
+ struct snd_pcm_hw_params *params,
+ unsigned int hwrate)
{
- 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 ret, hwrate;
- u32 val;
-
- if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
- return -ENOTSUPP;
+ /* Set ADC sample rate */
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
+ hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
- clk_freq = sun4i_codec_get_mod_freq(params);
- if (!clk_freq)
- return -EINVAL;
+ /* Set the number of channels we want to use */
+ if (params_channels(params) == 1)
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
+ else
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
- ret = clk_set_rate(scodec->clk_module, clk_freq);
- if (ret)
- return ret;
+ return 0;
+}
- hwrate = sun4i_codec_get_hw_rate(params);
- if (hwrate < 0)
- return hwrate;
+static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
+ struct snd_pcm_hw_params *params,
+ unsigned int hwrate)
+{
+ u32 val;
/* Set DAC sample rate */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
@@ -344,6 +407,35 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+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 ret, hwrate;
+
+ clk_freq = sun4i_codec_get_mod_freq(params);
+ if (!clk_freq)
+ return -EINVAL;
+
+ ret = clk_set_rate(scodec->clk_module, clk_freq);
+ if (ret)
+ return ret;
+
+ hwrate = sun4i_codec_get_hw_rate(params);
+ if (hwrate < 0)
+ return hwrate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return sun4i_codec_hw_params_playback(scodec, params,
+ hwrate);
+
+ return sun4i_codec_hw_params_capture(scodec, params,
+ hwrate);
+}
+
static int sun4i_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -394,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
SNDRV_PCM_FMTBIT_S32_LE,
.sig_bits = 24,
},
+ .capture = {
+ .stream_name = "Codec Capture",
+ .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 ***/
@@ -404,7 +510,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute =
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,
+ SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
sun4i_codec_pa_volume_scale),
};
@@ -428,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
};
-static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = {
+ /* Digital parts of the ADCs */
+ SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
+ SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
+ NULL, 0),
+
/* 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 ADCs */
+ SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
+
/* Analog parts of the DACs */
SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
@@ -452,23 +569,35 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
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,
+ /* VMIC */
+ SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
+
+ /* Mic Pre-Amplifiers */
+ SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+ SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
+
+ /* Power Amplifier */
+ SND_SOC_DAPM_MIXER("Power 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,
+ SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
&sun4i_codec_pa_mute),
+ SND_SOC_DAPM_INPUT("Mic1"),
+
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 */
+static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
+ /* Left ADC / DAC Routes */
+ { "Left ADC", NULL, "ADC" },
{ "Left DAC", NULL, "DAC" },
- /* Right DAC Routes */
+ /* Right ADC / DAC Routes */
+ { "Right ADC", NULL, "ADC" },
{ "Right DAC", NULL, "DAC" },
/* Right Mixer Routes */
@@ -480,25 +609,31 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_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" },
+ /* Power Amplifier Routes */
+ { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
+ { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
+ { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
+ { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
+
+ /* Headphone Output Routes */
+ { "Power Amplifier Mute", "Switch", "Power Amplifier" },
+ { "HP Right", NULL, "Power Amplifier Mute" },
+ { "HP Left", NULL, "Power Amplifier Mute" },
+
+ /* Mic1 Routes */
+ { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
+ { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
+ { "MIC1 Pre-Amplifier", NULL, "Mic1"},
+ { "Mic1", NULL, "VMIC" },
};
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),
+ .dapm_widgets = sun4i_codec_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
+ .dapm_routes = sun4i_codec_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
};
static const struct snd_soc_component_driver sun4i_codec_component = {
@@ -515,7 +650,7 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
- NULL);
+ &scodec->capture_dma_data);
return 0;
}
@@ -531,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
.formats = SUN4I_CODEC_FORMATS,
.sig_bits = 24,
},
+ .capture = {
+ .stream_name = "Capture",
+ .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 = {
@@ -568,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
return link;
};
+static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
+
+ if (scodec->gpio_pa)
+ gpiod_set_value_cansleep(scodec->gpio_pa,
+ !!SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = {
+ { "Speaker", NULL, "HP Right" },
+ { "Speaker", NULL, "HP Left" },
+};
+
static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
{
struct snd_soc_card *card;
@@ -582,6 +746,10 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
card->dev = dev;
card->name = "sun4i-codec";
+ card->dapm_widgets = sun4i_codec_card_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets);
+ card->dapm_routes = sun4i_codec_card_dapm_routes;
+ card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes);
return card;
};
@@ -633,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return -EINVAL;
}
+ scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(scodec->gpio_pa)) {
+ ret = PTR_ERR(scodec->gpio_pa);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
+ return ret;
+ }
+
/* 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;
+ /* DMA configuration for RX FIFO */
+ scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
+ scodec->capture_dma_data.maxburst = 4;
+ scodec->capture_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) {
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 21604009bc1a..e485278e027a 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -199,7 +199,8 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
static int tegra_wm8903_remove(struct snd_soc_card *card)
{
- struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]);
+ struct snd_soc_pcm_runtime *rtd =
+ snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
OpenPOWER on IntegriCloud