]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/adsp', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:35 +0000 (00:23 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:35 +0000 (00:23 +0000)
20 files changed:
Documentation/devicetree/bindings/sound/ak4613.txt
Documentation/devicetree/bindings/sound/atmel-pdmic.txt [new file with mode: 0644]
include/sound/ac97_codec.h
include/uapi/sound/compress_params.h
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel-classd.c
sound/soc/atmel/atmel-pdmic.c [new file with mode: 0644]
sound/soc/atmel/atmel-pdmic.h [new file with mode: 0644]
sound/soc/atmel/atmel_wm8904.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4613.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs47l24.c [new file with mode: 0644]
sound/soc/codecs/cs47l24.h [new file with mode: 0644]
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/soc-ac97.c

index 15a919522b4286e5bf8171cf86ace340f9e27faf..1783f9ef093096a0f06c1daf8ba854bba91e397d 100644 (file)
@@ -7,6 +7,16 @@ Required properties:
 - compatible : "asahi-kasei,ak4613"
 - reg : The chip select number on the I2C bus
 
+Optional properties:
+- asahi-kasei,in1-single-end   : Boolean. Indicate input / output pins are single-ended.
+- asahi-kasei,in2-single-end     rather than differential.
+- asahi-kasei,out1-single-end
+- asahi-kasei,out2-single-end
+- asahi-kasei,out3-single-end
+- asahi-kasei,out4-single-end
+- asahi-kasei,out5-single-end
+- asahi-kasei,out6-single-end
+
 Example:
 
 &i2c {
diff --git a/Documentation/devicetree/bindings/sound/atmel-pdmic.txt b/Documentation/devicetree/bindings/sound/atmel-pdmic.txt
new file mode 100644 (file)
index 0000000..e0875f1
--- /dev/null
@@ -0,0 +1,55 @@
+* Atmel PDMIC driver under ALSA SoC architecture
+
+Required properties:
+- compatible
+       Should be "atmel,sama5d2-pdmic".
+- reg
+       Should contain PDMIC registers location and length.
+- interrupts
+       Should contain the IRQ line for the PDMIC.
+- dmas
+       One DMA specifiers as described in atmel-dma.txt and dma.txt files.
+- dma-names
+       Must be "rx".
+- clock-names
+       Required elements:
+       - "pclk"        peripheral clock
+       - "gclk"        generated clock
+- clocks
+       Must contain an entry for each required entry in clock-names.
+       Please refer to clock-bindings.txt.
+- atmel,mic-min-freq
+       The minimal frequency that the micphone supports.
+- atmel,mic-max-freq
+       The maximal frequency that the micphone supports.
+
+Optional properties:
+- pinctrl-names, pinctrl-0
+       Please refer to pinctrl-bindings.txt.
+- atmel,model
+       The user-visible name of this sound card.
+       The default value is "PDMIC".
+- atmel,mic-offset
+       The offset that should be added.
+       The range is from -32768 to 32767.
+       The default value is 0.
+
+Example:
+       pdmic@f8018000 {
+                               compatible = "atmel,sama5d2-pdmic";
+                               reg = <0xf8018000 0x124>;
+                               interrupts = <48 IRQ_TYPE_LEVEL_HIGH 7>;
+                               dmas = <&dma0
+                                       (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
+                                       | AT91_XDMAC_DT_PERID(50))>;
+                               dma-names = "rx";
+                               clocks = <&pdmic_clk>, <&pdmic_gclk>;
+                               clock-names = "pclk", "gclk";
+
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_pdmic_default>;
+                               atmel,model = "PDMIC @ sama5d2_xplained";
+                               atmel,mic-min-freq = <1000000>;
+                               atmel,mic-max-freq = <3246000>;
+                               atmel,mic-offset = <0x0>;
+       };
index 74bc85473b58c18e3bc92e2fcf7c70a232fe75d7..15aa5f07c955bed7f6b0d7f1dfc9471d7f229120 100644 (file)
 #define AC97_RATES_MIC_ADC     4
 #define AC97_RATES_SPDIF       5
 
+#define AC97_NUM_GPIOS         16
 /*
  *
  */
 
 struct snd_ac97;
+struct snd_ac97_gpio_priv;
 struct snd_pcm_chmap;
 
 struct snd_ac97_build_ops {
@@ -529,6 +531,7 @@ struct snd_ac97 {
        struct delayed_work power_work;
 #endif
        struct device dev;
+       struct snd_ac97_gpio_priv *gpio_priv;
 
        struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */
 };
index d9bd9ca0d5b020519da8c8aebb3b93cc5a295c71..9625484a4a2a2a12e5ad8d00a759e27ea48a96b3 100644 (file)
@@ -73,7 +73,8 @@
 #define SND_AUDIOCODEC_IEC61937              ((__u32) 0x0000000B)
 #define SND_AUDIOCODEC_G723_1                ((__u32) 0x0000000C)
 #define SND_AUDIOCODEC_G729                  ((__u32) 0x0000000D)
-#define SND_AUDIOCODEC_MAX                   SND_AUDIOCODEC_G729
+#define SND_AUDIOCODEC_BESPOKE               ((__u32) 0x0000000E)
+#define SND_AUDIOCODEC_MAX                   SND_AUDIOCODEC_BESPOKE
 
 /*
  * Profile and modes are listed with bit masks. This allows for a
@@ -312,7 +313,7 @@ struct snd_enc_flac {
 
 struct snd_enc_generic {
        __u32 bw;       /* encoder bandwidth */
-       __s32 reserved[15];
+       __s32 reserved[15];     /* Can be used for SND_AUDIOCODEC_BESPOKE */
 } __attribute__((packed, aligned(4)));
 
 union snd_codec_options {
index 2d30464b81cef315b2faf29f6dc21352b38c60d2..06e099e802df94039328fdb677480cda521021fb 100644 (file)
@@ -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
index f6f7db4282164a7b15ffcc52535ca64c9296e045..a2b127bd9c8717ac6c1501eff669014c306058a5 100644 (file)
@@ -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
index 8276675730ef1252483b4a5e1d2b649bf71dfe1e..f3ffb39bfe2753b7477d75b7e63c323d30574391 100644 (file)
@@ -636,8 +636,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 +647,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 (file)
index 0000000..aee4787
--- /dev/null
@@ -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 (file)
index 0000000..4527ac7
--- /dev/null
@@ -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
index 1933bcd46cca043d68db0ecc771124da37416908..fdd28ed3e0b9ec3d6c80e7081dd03b408472f45b 100644 (file)
@@ -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,
index 012bdcf6d175a571fab7d0251734b60729e0e888..efdd0f8e2e9ca7c5ed147e770c700d2edde133f8 100644 (file)
@@ -55,6 +55,7 @@ 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
@@ -197,10 +198,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
@@ -213,9 +216,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
@@ -424,6 +430,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
index 890ae5571a00948b6b4602206a6950664e6003a6..463d7d0880b74bb1611691491448558d65029ad0 100644 (file)
@@ -47,6 +47,7 @@ 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
@@ -245,6 +246,7 @@ 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
index 07a266460ec39cb3941dde8d4fe96eeb1bb8d414..647f69de6baac94c3d6aef8a9ff297894ec0c47b 100644 (file)
 #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);
index b4f1867ae9d6946022f664ef564d794ef95182cb..8b6adb5419bb896482139426167752a22aa4df57 100644 (file)
@@ -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
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
new file mode 100644 (file)
index 0000000..dc5ae7f
--- /dev/null
@@ -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 (file)
index 0000000..77ab2b7
--- /dev/null
@@ -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
index e93e5420943ecf003a7d9c65200cf759b3d7af1d..c3640960183589eac7856e0edcbc77647c5e78f7 100644 (file)
@@ -1810,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" },
 
@@ -2132,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);
@@ -2224,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);
@@ -2284,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)
index 0bb415a287239a939dfc11adfddbe85c1f2e7f8d..ac879d16c6a673b0250cbac7cfab57dfe77f7f09 100644 (file)
@@ -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");
index 2d117cf0e95365202295fdf5c74ef0519ec40557..43af093fafcf5b9a0d31caa31960992b2dc179ac 100644 (file)
@@ -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
index d40efc9fe0a9b4597511b2de2ad02712aa912043..733f5128eeff9f6b9049a901c9fa020a6a0f2c7e 100644 (file)
@@ -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);