]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge remote-tracking branches 'asoc/topic/ak4613', 'asoc/topic/ak4642', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Wed, 23 Sep 2015 18:01:16 +0000 (11:01 -0700)
committerMark Brown <broonie@kernel.org>
Wed, 23 Sep 2015 18:01:16 +0000 (11:01 -0700)
14 files changed:
Documentation/devicetree/bindings/sound/ak4613.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ak4642.txt
drivers/spi/spi-atmel.c
include/sound/soc.h
sound/soc/atmel/atmel_wm8904.c
sound/soc/au1x/db1000.c
sound/soc/au1x/db1200.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4613.c [new file with mode: 0644]
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/wm5110.c

diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt
new file mode 100644 (file)
index 0000000..15a9195
--- /dev/null
@@ -0,0 +1,17 @@
+AK4613 I2C transmitter
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "asahi-kasei,ak4613"
+- reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       ak4613: ak4613@0x10 {
+               compatible = "asahi-kasei,ak4613";
+               reg = <0x10>;
+       };
+};
index 623d4e70ae11d9ef5ee6231c09f0e8293fb32680..340784db6808809eecf4ae0e310023550713e2c6 100644 (file)
@@ -7,7 +7,14 @@ Required properties:
   - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
   - reg : The chip select number on the I2C bus
 
-Example:
+Optional properties:
+
+  - #clock-cells :             common clock binding; shall be set to 0
+  - clocks :                   common clock binding; MCKI clock
+  - clock-frequency :          common clock binding; frequency of MCKO
+  - clock-output-names :       common clock binding; MCKO clock name
+
+Example 1:
 
 &i2c {
        ak4648: ak4648@0x12 {
@@ -15,3 +22,16 @@ Example:
                reg = <0x12>;
        };
 };
+
+Example 2:
+
+&i2c {
+       ak4643: codec@12 {
+               compatible = "asahi-kasei,ak4643";
+               reg = <0x12>;
+               #clock-cells = <0>;
+               clocks = <&audio_clock>;
+               clock-frequency = <12288000>;
+               clock-output-names = "ak4643_mcko";
+       };
+};
index bf9ed380bb1c0754addbd27aedf7d874cee89ff2..1cc1f1e0d17fd60e88957b807a580d738dc227cc 100644 (file)
@@ -871,14 +871,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as,
         * Calculate the lowest divider that satisfies the
         * constraint, assuming div32/fdiv/mbz == 0.
         */
-       if (xfer->speed_hz)
-               scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
-       else
-               /*
-                * This can happend if max_speed is null.
-                * In this case, we set the lowest possible speed
-                */
-               scbr = 0xff;
+       scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
 
        /*
         * If the resulting divider doesn't fit into the
@@ -1300,14 +1293,12 @@ static int atmel_spi_one_transfer(struct spi_master *master,
                return -EINVAL;
        }
 
-       if (xfer->bits_per_word) {
-               asd = spi->controller_state;
-               bits = (asd->csr >> 4) & 0xf;
-               if (bits != xfer->bits_per_word - 8) {
-                       dev_dbg(&spi->dev,
+       asd = spi->controller_state;
+       bits = (asd->csr >> 4) & 0xf;
+       if (bits != xfer->bits_per_word - 8) {
+               dev_dbg(&spi->dev,
                        "you can't yet change bits_per_word in transfers\n");
-                       return -ENOPROTOOPT;
-               }
+               return -ENOPROTOOPT;
        }
 
        /*
index 884e728b09d9a57e8a56ac8ea224b3f873cb81c3..9ffa28514fa76556941acb8d4c05e45eea2b673b 100644 (file)
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) }
+#define SOC_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+                                xhandler_get, xhandler_put, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_range, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .rreg = xreg, .shift = xshift, \
+                .rshift = xshift, .min = xmin, .max = xmax, \
+                .platform_max = xmax, .invert = xinvert} }
 #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
index aa354e1c6ff7ce3df7ffd898682c3040817a6a8c..1933bcd46cca043d68db0ecc771124da37416908 100644 (file)
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
        { .compatible = "atmel,asoc-wm8904", },
        { }
 };
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
 #endif
 
 static struct platform_driver atmel_asoc_wm8904_driver = {
index 452f404abfd283abe56caaae06de856a47d2b1d9..e97c32798e98a066dba2eaf47883584f7eb04dad 100644 (file)
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &db1000_ac97;
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = db1000_audio_probe,
-       .remove         = db1000_audio_remove,
 };
 
 module_platform_driver(db1000_audio_driver);
index 58c3164802b8ceda545e1469f557a814702694be..638ca0ba7e6efcb4f891564af24f4d8a570b927f 100644 (file)
@@ -174,14 +174,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
 
        card = db1200_cards[pid->driver_data];
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1200_audio_driver = {
@@ -191,7 +184,6 @@ static struct platform_driver db1200_audio_driver = {
        },
        .id_table       = db1200_pids,
        .probe          = db1200_audio_probe,
-       .remove         = db1200_audio_remove,
 };
 
 module_platform_driver(db1200_audio_driver);
index 0c9733ecd17f29a040ec3f2a4c7dfab8c59007e7..a92e4d4b2eeea6a50b5871fa934f427ec0216b97 100644 (file)
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
        select SND_SOC_AK4554
+       select SND_SOC_AK4613 if I2C
        select SND_SOC_AK4641 if I2C
        select SND_SOC_AK4642 if I2C
        select SND_SOC_AK4671 if I2C
@@ -319,6 +320,10 @@ config SND_SOC_AK4535
 config SND_SOC_AK4554
        tristate "AKM AK4554 CODEC"
 
+config SND_SOC_AK4613
+       tristate "AKM AK4613 CODEC"
+       depends on I2C
+
 config SND_SOC_AK4641
        tristate
 
index 4a32077954aee6aee2966ced2573991d1673b02a..5b6c8af38a392c2ca0c6cb65da3e3c66ad192290 100644 (file)
@@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
 snd-soc-ak4641-objs := ak4641.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
@@ -216,6 +217,7 @@ obj-$(CONFIG_SND_SOC_ADS117X)       += snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4554)   += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613)   += snd-soc-ak4613.o
 obj-$(CONFIG_SND_SOC_AK4641)   += snd-soc-ak4641.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644 (file)
index 0000000..07a2664
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c  --  Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1       0x00 /* Power Management 1 */
+#define PW_MGMT2       0x01 /* Power Management 2 */
+#define PW_MGMT3       0x02 /* Power Management 3 */
+#define CTRL1          0x03 /* Control 1 */
+#define CTRL2          0x04 /* Control 2 */
+#define DEMP1          0x05 /* De-emphasis1 */
+#define DEMP2          0x06 /* De-emphasis2 */
+#define OFD            0x07 /* Overflow Detect */
+#define ZRD            0x08 /* Zero Detect */
+#define ICTRL          0x09 /* Input Control */
+#define OCTRL          0x0a /* Output Control */
+#define LOUT1          0x0b /* LOUT1 Volume Control */
+#define ROUT1          0x0c /* ROUT1 Volume Control */
+#define LOUT2          0x0d /* LOUT2 Volume Control */
+#define ROUT2          0x0e /* ROUT2 Volume Control */
+#define LOUT3          0x0f /* LOUT3 Volume Control */
+#define ROUT3          0x10 /* ROUT3 Volume Control */
+#define LOUT4          0x11 /* LOUT4 Volume Control */
+#define ROUT4          0x12 /* ROUT4 Volume Control */
+#define LOUT5          0x13 /* LOUT5 Volume Control */
+#define ROUT5          0x14 /* ROUT5 Volume Control */
+#define LOUT6          0x15 /* LOUT6 Volume Control */
+#define ROUT6          0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN           BIT(0)
+#define PMDAC          BIT(1)
+#define PMADC          BIT(2)
+#define PMVR           BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL       0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL       0x3f
+
+/* CTRL1 */
+#define DIF0           BIT(3)
+#define DIF1           BIT(4)
+#define DIF2           BIT(5)
+#define TDM0           BIT(6)
+#define TDM1           BIT(7)
+#define NO_FMT         (0xff)
+#define FMT_MASK       (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED       (0 << 2)
+#define DFS_DOUBLE_SPEED       (1 << 2)
+#define DFS_QUAD_SPEED         (2 << 2)
+
+struct ak4613_priv {
+       struct mutex lock;
+
+       unsigned int fmt;
+       u8 fmt_ctrl;
+       int cnt;
+};
+
+struct ak4613_formats {
+       unsigned int width;
+       unsigned int fmt;
+};
+
+struct ak4613_interface {
+       struct ak4613_formats capture;
+       struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ *       ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+                        0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+       { 0x0,  0x0f }, { 0x1,  0x07 }, { 0x2,  0x3f }, { 0x3,  0x20 },
+       { 0x4,  0x20 }, { 0x5,  0x55 }, { 0x6,  0x05 }, { 0x7,  0x07 },
+       { 0x8,  0x0f }, { 0x9,  0x07 }, { 0xa,  0x3f }, { 0xb,  0x00 },
+       { 0xc,  0x00 }, { 0xd,  0x00 }, { 0xe,  0x00 }, { 0xf,  0x00 },
+       { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+       { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+       /* capture */                           /* playback */
+       [0] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(16, RIGHT_J) },
+       [1] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(20, RIGHT_J) },
+       [2] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, RIGHT_J) },
+       [3] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, LEFT_J) },
+       [4] = { AUDIO_IFACE(24, I2S),           AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .max_register           = 0x16,
+       .reg_defaults           = ak4613_reg,
+       .num_reg_defaults       = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+       { .compatible = "asahi-kasei,ak4613",   .data = &ak4613_regmap_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+       { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("LOUT4"),
+       SND_SOC_DAPM_OUTPUT("LOUT5"),
+       SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT3"),
+       SND_SOC_DAPM_OUTPUT("ROUT4"),
+       SND_SOC_DAPM_OUTPUT("ROUT5"),
+       SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+
+       /* DAC */
+       SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+       SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+       SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+       SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+       SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+       SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+       /* ADC */
+       SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+       SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+       {"LOUT1", NULL, "DAC1"},
+       {"LOUT2", NULL, "DAC2"},
+       {"LOUT3", NULL, "DAC3"},
+       {"LOUT4", NULL, "DAC4"},
+       {"LOUT5", NULL, "DAC5"},
+       {"LOUT6", NULL, "DAC6"},
+
+       {"ROUT1", NULL, "DAC1"},
+       {"ROUT2", NULL, "DAC2"},
+       {"ROUT3", NULL, "DAC3"},
+       {"ROUT4", NULL, "DAC4"},
+       {"ROUT5", NULL, "DAC5"},
+       {"ROUT6", NULL, "DAC6"},
+
+       {"DAC1", NULL, "Playback"},
+       {"DAC2", NULL, "Playback"},
+       {"DAC3", NULL, "Playback"},
+       {"DAC4", NULL, "Playback"},
+       {"DAC5", NULL, "Playback"},
+       {"DAC6", NULL, "Playback"},
+
+       {"Capture", NULL, "ADC1"},
+       {"Capture", NULL, "ADC2"},
+
+       {"ADC1", NULL, "LIN1"},
+       {"ADC2", NULL, "LIN2"},
+
+       {"ADC1", NULL, "RIN1"},
+       {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct device *dev = codec->dev;
+
+       mutex_lock(&priv->lock);
+       priv->cnt--;
+       if (priv->cnt < 0) {
+               dev_err(dev, "unexpected counter error\n");
+               priv->cnt = 0;
+       }
+       if (!priv->cnt)
+               priv->fmt_ctrl = NO_FMT;
+       mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+       switch (fmt) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_I2S:
+               priv->fmt = fmt;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       const struct ak4613_formats *fmts;
+       struct device *dev = codec->dev;
+       unsigned int width = params_width(params);
+       unsigned int fmt = priv->fmt;
+       unsigned int rate;
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int i, ret;
+       u8 fmt_ctrl, ctrl2;
+
+       rate = params_rate(params);
+       switch (rate) {
+       case 32000:
+       case 44100:
+       case 48000:
+               ctrl2 = DFS_NORMAL_SPEED;
+               break;
+       case 88200:
+       case 96000:
+               ctrl2 = DFS_DOUBLE_SPEED;
+               break;
+       case 176400:
+       case 192000:
+               ctrl2 = DFS_QUAD_SPEED;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * FIXME
+        *
+        * It doesn't support TDM at this point
+        */
+       fmt_ctrl = NO_FMT;
+       for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+               fmts = (is_play) ?      &ak4613_iface[i].playback :
+                                       &ak4613_iface[i].capture;
+
+               if (fmts->fmt != fmt)
+                       continue;
+
+               if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+                       if (fmts->width != width)
+                               continue;
+               } else {
+                       if (fmts->width < width)
+                               continue;
+               }
+
+               fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+               break;
+       }
+
+       ret = -EINVAL;
+       if (fmt_ctrl == NO_FMT)
+               goto hw_params_end;
+
+       mutex_lock(&priv->lock);
+       if ((priv->fmt_ctrl == NO_FMT) ||
+           (priv->fmt_ctrl == fmt_ctrl)) {
+               priv->fmt_ctrl = fmt_ctrl;
+               priv->cnt++;
+               ret = 0;
+       }
+       mutex_unlock(&priv->lock);
+
+       if (ret < 0)
+               goto hw_params_end;
+
+       snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+       snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+       if (ret < 0)
+               dev_warn(dev, "unsupported data width/format combination\n");
+
+       return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u8 mgmt1 = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               mgmt1 |= RSTN;
+               /* fall through */
+       case SND_SOC_BIAS_PREPARE:
+               mgmt1 |= PMADC | PMDAC;
+               /* fall through */
+       case SND_SOC_BIAS_STANDBY:
+               mgmt1 |= PMVR;
+               /* fall through */
+       case SND_SOC_BIAS_OFF:
+       default:
+               break;
+       }
+
+       snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+       .shutdown       = ak4613_dai_shutdown,
+       .set_fmt        = ak4613_dai_set_fmt,
+       .hw_params      = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE                (SNDRV_PCM_RATE_32000  |\
+                                SNDRV_PCM_RATE_44100  |\
+                                SNDRV_PCM_RATE_48000  |\
+                                SNDRV_PCM_RATE_64000  |\
+                                SNDRV_PCM_RATE_88200  |\
+                                SNDRV_PCM_RATE_96000  |\
+                                SNDRV_PCM_RATE_176400 |\
+                                SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT      (SNDRV_PCM_FMTBIT_S16_LE |\
+                                SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+       .name = "ak4613-hifi",
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .ops = &ak4613_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+       regcache_mark_dirty(regmap);
+       return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+       .resume                 = ak4613_resume,
+       .set_bias_level         = ak4613_set_bias_level,
+       .controls               = ak4613_snd_controls,
+       .num_controls           = ARRAY_SIZE(ak4613_snd_controls),
+       .dapm_widgets           = ak4613_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(ak4613_dapm_widgets),
+       .dapm_routes            = ak4613_intercon,
+       .num_dapm_routes        = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
+       const struct regmap_config *regmap_cfg;
+       struct regmap *regmap;
+       struct ak4613_priv *priv;
+
+       regmap_cfg = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(ak4613_of_match, dev);
+               if (of_id)
+                       regmap_cfg = of_id->data;
+       } else {
+               regmap_cfg = (const struct regmap_config *)id->driver_data;
+       }
+
+       if (!regmap_cfg)
+               return -EINVAL;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->fmt_ctrl          = NO_FMT;
+       priv->cnt               = 0;
+
+       mutex_init(&priv->lock);
+
+       i2c_set_clientdata(i2c, priv);
+
+       regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+                                     &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+       .driver = {
+               .name = "ak4613-codec",
+               .owner = THIS_MODULE,
+               .of_match_table = ak4613_of_match,
+       },
+       .probe          = ak4613_i2c_probe,
+       .remove         = ak4613_i2c_remove,
+       .id_table       = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
index 4a90143d0e907b5034d0f4caa506c97000500f71..cda27c22812a71442dd3d927088c2118aa16dc17 100644 (file)
@@ -23,6 +23,8 @@
  * AK4648 is tested.
  */
 
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #define I2S            (3 << 0)
 
 /* MD_CTL2 */
-#define FS0            (1 << 0)
-#define FS1            (1 << 1)
-#define FS2            (1 << 2)
-#define FS3            (1 << 5)
-#define FS_MASK                (FS0 | FS1 | FS2 | FS3)
+#define FSs(val)       (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val)       ((val & 0x3) << 6)
 
 /* MD_CTL3 */
 #define BST1           (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
 
 struct ak4642_priv {
        const struct ak4642_drvdata *drvdata;
+       struct clk *mcko;
 };
 
 /*
@@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+                          u32 frequency)
+{
+       u32 fs_list[] = {
+               [0] = 8000,
+               [1] = 12000,
+               [2] = 16000,
+               [3] = 24000,
+               [4] = 7350,
+               [5] = 11025,
+               [6] = 14700,
+               [7] = 22050,
+               [10] = 32000,
+               [11] = 48000,
+               [14] = 29400,
+               [15] = 44100,
+       };
+       u32 ps_list[] = {
+               [0] = 256,
+               [1] = 128,
+               [2] = 64,
+               [3] = 32
+       };
+       int ps, fs;
+
+       for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+               for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+                       if (frequency == ps_list[ps] * fs_list[fs]) {
+                               snd_soc_write(codec, MD_CTL2,
+                                             PSs(ps) | FSs(fs));
+                               return 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
-       u8 rate;
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+       u32 rate = clk_get_rate(priv->mcko);
 
-       switch (params_rate(params)) {
-       case 7350:
-               rate = FS2;
-               break;
-       case 8000:
-               rate = 0;
-               break;
-       case 11025:
-               rate = FS2 | FS0;
-               break;
-       case 12000:
-               rate = FS0;
-               break;
-       case 14700:
-               rate = FS2 | FS1;
-               break;
-       case 16000:
-               rate = FS1;
-               break;
-       case 22050:
-               rate = FS2 | FS1 | FS0;
-               break;
-       case 24000:
-               rate = FS1 | FS0;
-               break;
-       case 29400:
-               rate = FS3 | FS2 | FS1;
-               break;
-       case 32000:
-               rate = FS3 | FS1;
-               break;
-       case 44100:
-               rate = FS3 | FS2 | FS1 | FS0;
-               break;
-       case 48000:
-               rate = FS3 | FS1 | FS0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+       if (!rate)
+               rate = params_rate(params) * 256;
 
-       return 0;
+       return ak4642_set_mcko(codec, rate);
 }
 
 static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (priv->mcko)
+               ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+       return 0;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+       .probe                  = ak4642_probe,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
@@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
        .extended_frequencies = 1,
 };
 
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk *clk;
+       const char *clk_name = np->name;
+       const char *parent_clk_name = NULL;
+       u32 rate;
+
+       if (of_property_read_u32(np, "clock-frequency", &rate))
+               return NULL;
+
+       if (of_property_read_bool(np, "clocks"))
+               parent_clk_name = of_clk_get_parent_name(np, 0);
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+                                     (parent_clk_name) ? 0 : CLK_IS_ROOT,
+                                     rate);
+       if (!IS_ERR(clk))
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+       return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
 static const struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
-       struct device_node *np = i2c->dev.of_node;
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
        const struct ak4642_drvdata *drvdata = NULL;
        struct regmap *regmap;
        struct ak4642_priv *priv;
+       struct clk *mcko = NULL;
 
        if (np) {
                const struct of_device_id *of_id;
 
-               of_id = of_match_device(ak4642_of_match, &i2c->dev);
+               mcko = ak4642_of_parse_mcko(dev);
+               if (IS_ERR(mcko))
+                       mcko = NULL;
+
+               of_id = of_match_device(ak4642_of_match, dev);
                if (of_id)
                        drvdata = of_id->data;
        } else {
@@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        }
 
        if (!drvdata) {
-               dev_err(&i2c->dev, "Unknown device type\n");
+               dev_err(dev, "Unknown device type\n");
                return -EINVAL;
        }
 
-       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        priv->drvdata = drvdata;
+       priv->mcko = mcko;
 
        i2c_set_clientdata(i2c, priv);
 
@@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       return snd_soc_register_codec(&i2c->dev,
+       return snd_soc_register_codec(dev,
                                      &soc_codec_dev_ak4642, &ak4642_dai, 1);
 }
 
index 8a2221ab3d10b97f5f7a97c21251264aa93eaa5b..ac21b85ff75ffb7b3305d8200c8c6a38ed3896bf 100644 (file)
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
                                                   0x4f5, 0x0da);
                }
                break;
+       default:
+               break;
        }
 
        return 0;
@@ -689,6 +691,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
                                    ARIZONA_IN_VU, val);
 }
 
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+       unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+       unsigned int val = snd_soc_read(codec, reg);
+
+       return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
 int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                  int event)
 {
@@ -725,6 +736,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
                if (reg == 0)
                        arizona_in_set_vu(codec, 0);
+               break;
+       default:
+               break;
        }
 
        return 0;
@@ -806,6 +820,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
                        break;
                }
                break;
+       default:
+               break;
        }
 
        return 0;
index ada0a418ff4b648034841e9b36f4df598d03fac2..7b68d05a0939049efec30f58aafb5aa673df20f3 100644 (file)
@@ -294,4 +294,6 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
                            bool diff);
 
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
 #endif
index 9756578fc7526a3182039bad491d3388e297de57..c04c0bc6f58ad434de2bbe5825be1b41182b1c24 100644 (file)
 struct wm5110_priv {
        struct arizona_priv core;
        struct arizona_fll fll[2];
+
+       unsigned int in_value;
+       int in_pre_pending;
+       int in_post_pending;
+
+       unsigned int in_pga_cache[6];
 };
 
 static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -428,6 +434,127 @@ err:
        return ret;
 }
 
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       unsigned int reg, mask;
+       struct reg_sequence analog_seq[] = {
+               { 0x80, 0x3 },
+               { 0x35d, 0 },
+               { 0x80, 0x0 },
+       };
+
+       reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+       mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+       switch (event) {
+       case SND_SOC_DAPM_WILL_PMU:
+               wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+               wm5110->in_pre_pending++;
+               wm5110->in_post_pending++;
+               return 0;
+       case SND_SOC_DAPM_PRE_PMU:
+               wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+               snd_soc_update_bits(codec, reg, mask,
+                                   0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+               wm5110->in_pre_pending--;
+               if (wm5110->in_pre_pending == 0) {
+                       analog_seq[1].def = wm5110->in_value;
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+
+                       msleep(55);
+
+                       wm5110->in_value = 0;
+               }
+
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, reg, mask,
+                                   wm5110->in_pga_cache[w->shift]);
+
+               wm5110->in_post_pending--;
+               if (wm5110->in_post_pending == 0)
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       switch (arizona->rev) {
+       case 0 ... 4:
+               if (arizona_input_analog(codec, w->shift))
+                       wm5110_in_analog_ev(w, kcontrol, event);
+
+               break;
+       default:
+               break;
+       }
+
+       return arizona_in_ev(w, kcontrol, event);
+}
+
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
 SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
 SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
 
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
-                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
-                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
-                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
-                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
-                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
-                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                        ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                        ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                        ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                        ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                        ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                        ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
 
 SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
 
@@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
                   0, NULL, 0, arizona_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |