Merge branch 'for-2.6.31' into for-2.6.32
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 23 Jul 2009 07:22:58 +0000 (08:22 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 23 Jul 2009 07:22:58 +0000 (08:22 +0100)
60 files changed:
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/uda1380.h [new file with mode: 0644]
include/sound/wm8993.h [new file with mode: 0644]
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ad73311.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/blackfin/bf5xx-ssm2602.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad1938.c [new file with mode: 0644]
sound/soc/codecs/ad1938.h [new file with mode: 0644]
sound/soc/codecs/ak4535.c
sound/soc/codecs/max9877.c [new file with mode: 0644]
sound/soc/codecs/max9877.h [new file with mode: 0644]
sound/soc/codecs/spdif_transciever.c
sound/soc/codecs/stac9766.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl4030.h
sound/soc/codecs/uda1380.c
sound/soc/codecs/uda1380.h
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8523.c [new file with mode: 0644]
sound/soc/codecs/wm8523.h [new file with mode: 0644]
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8961.c [new file with mode: 0644]
sound/soc/codecs/wm8961.h [new file with mode: 0644]
sound/soc/codecs/wm8988.c
sound/soc/codecs/wm8993.c [new file with mode: 0644]
sound/soc/codecs/wm8993.h [new file with mode: 0644]
sound/soc/codecs/wm9081.c
sound/soc/davinci/Kconfig
sound/soc/davinci/Makefile
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c [new file with mode: 0644]
sound/soc/davinci/davinci-mcasp.h [new file with mode: 0644]
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/omap-pcm.c
sound/soc/omap/sdp3430.c
sound/soc/omap/zoom2.c [new file with mode: 0644]
sound/soc/pxa/magician.c
sound/soc/pxa/pxa-ssp.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/txx9/txx9aclc.c

index 352d7ee..05991b0 100644 (file)
@@ -27,8 +27,8 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_I2S             0 /* I2S mode */
 #define SND_SOC_DAIFMT_RIGHT_J         1 /* Right Justified mode */
 #define SND_SOC_DAIFMT_LEFT_J          2 /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A           3 /* L data msb after FRM LRC */
-#define SND_SOC_DAIFMT_DSP_B           4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_DSP_A           3 /* L data MSB after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B           4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97            5 /* AC97 */
 
 /* left and right justified also known as MSB and LSB respectively */
@@ -38,7 +38,7 @@ struct snd_pcm_substream;
 /*
  * DAI Clock gating.
  *
- * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * DAI bit clocks can be be gated (disabled) when the DAI is not
  * sending or receiving PCM data in a frame. This can be used to save power.
  */
 #define SND_SOC_DAIFMT_CONT            (0 << 4) /* continuous clock */
@@ -51,21 +51,21 @@ struct snd_pcm_substream;
  * format.
  */
 #define SND_SOC_DAIFMT_NB_NF           (0 << 8) /* normal bit clock + frame */
-#define SND_SOC_DAIFMT_NB_IF           (1 << 8) /* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF           (2 << 8) /* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF           (3 << 8) /* invert bclk + frm */
+#define SND_SOC_DAIFMT_NB_IF           (1 << 8) /* normal BCLK + inv FRM */
+#define SND_SOC_DAIFMT_IB_NF           (2 << 8) /* invert BCLK + nor FRM */
+#define SND_SOC_DAIFMT_IB_IF           (3 << 8) /* invert BCLK + FRM */
 
 /*
  * DAI hardware clock masters.
  *
  * This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
+ * i.e. if the codec is clk and FRM master then the interface is
  * clk and frame slave.
  */
-#define SND_SOC_DAIFMT_CBM_CFM         (0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM         (1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFM         (0 << 12) /* codec clk & FRM master */
+#define SND_SOC_DAIFMT_CBS_CFM         (1 << 12) /* codec clk slave & FRM master */
 #define SND_SOC_DAIFMT_CBM_CFS         (2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS         (3 << 12) /* codec clk & frm slave */
+#define SND_SOC_DAIFMT_CBS_CFS         (3 << 12) /* codec clk & FRM slave */
 
 #define SND_SOC_DAIFMT_FORMAT_MASK     0x000f
 #define SND_SOC_DAIFMT_CLOCK_MASK      0x00f0
@@ -116,12 +116,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
 /*
  * Digital Audio Interface.
  *
- * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
- * operations an capabilities. Codec and platfom drivers will register a this
+ * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
+ * operations and capabilities. Codec and platform drivers will register this
  * structure for every DAI they have.
  *
  * This structure covers the clocking, formating and ALSA operations for each
- * interface a
+ * interface.
  */
 struct snd_soc_dai_ops {
        /*
index ec8a45f..35814ce 100644 (file)
@@ -279,6 +279,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
        int event);
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
index cf6111d..756fb59 100644 (file)
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, 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), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .shift = shift_left, .rshift = shift_right, \
+               .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, 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_2r, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+               .max = xmax, .invert = xinvert} }
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_bool_ext, \
@@ -191,6 +213,12 @@ int snd_soc_register_platform(struct snd_soc_platform *platform);
 void snd_soc_unregister_platform(struct snd_soc_platform *platform);
 int snd_soc_register_codec(struct snd_soc_codec *codec);
 void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+
+#ifdef CONFIG_PM
+int snd_soc_suspend_device(struct device *dev);
+int snd_soc_resume_device(struct device *dev);
+#endif
 
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
@@ -216,9 +244,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
 
 /* codec register bit access */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value);
+                               unsigned int mask, unsigned int value);
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value);
+                               unsigned int mask, unsigned int value);
 
 int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
        struct snd_ac97_bus_ops *ops, int num);
@@ -356,6 +384,7 @@ struct snd_soc_codec {
        int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
        int (*display_register)(struct snd_soc_codec *, char *,
                                size_t, unsigned int);
+       int (*volatile_register)(unsigned int);
        hw_write_t hw_write;
        hw_read_t hw_read;
        void *reg_cache;
@@ -369,8 +398,6 @@ struct snd_soc_codec {
        enum snd_soc_bias_level bias_level;
        enum snd_soc_bias_level suspend_bias_level;
        struct delayed_work delayed_work;
-       struct list_head up_list;
-       struct list_head down_list;
 
        /* codec DAI's */
        struct snd_soc_dai *dai;
diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h
new file mode 100644 (file)
index 0000000..381319c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * UDA1380 ALSA SoC Codec driver
+ *
+ * Copyright 2009 Philipp Zabel
+ *
+ * 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 __UDA1380_H
+#define __UDA1380_H
+
+struct uda1380_platform_data {
+       int gpio_power;
+       int gpio_reset;
+       int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL  1
+};
+
+#endif /* __UDA1380_H */
diff --git a/include/sound/wm8993.h b/include/sound/wm8993.h
new file mode 100644 (file)
index 0000000..9c661f2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * linux/sound/wm8993.h -- Platform data for WM8993
+ *
+ * Copyright 2009 Wolfson Microelectronics. PLC.
+ *
+ * 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 __LINUX_SND_WM8993_H
+#define __LINUX_SND_WM8993_H
+
+/* Note that EQ1 only contains the enable/disable bit so will be
+   ignored but is included for simplicity.
+ */
+struct wm8993_retune_mobile_setting {
+       const char *name;
+       unsigned int rate;
+       u16 config[24];
+};
+
+struct wm8993_platform_data {
+       struct wm8993_retune_mobile_setting *retune_configs;
+       int num_retune_configs;
+
+       /* LINEOUT can be differential or single ended */
+       unsigned int lineout1_diff:1;
+       unsigned int lineout2_diff:1;
+
+       /* Common mode feedback */
+       unsigned int lineout1fb:1;
+       unsigned int lineout2fb:1;
+
+       /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+       unsigned int micbias1_lvl:1;
+       unsigned int micbias2_lvl:1;
+
+       /* Jack detect threashold levels, see datasheet for values */
+       unsigned int jd_scthr:2;
+       unsigned int jd_thr:2;
+};
+
+#endif
index 173a239..130b121 100644 (file)
 
 #define MCLK_RATE 12000000
 
-static struct clk *mclk;
-
-static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-       int ret;
-
-       ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
-               MCLK_RATE, SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               clk_disable(mclk);
-               return ret;
-       }
-
-       return 0;
-}
-
-static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+/*
+ * As shipped the board does not have inputs.  However, it is relatively
+ * straightforward to modify the board to hook them up so support is left
+ * in the driver.
+ */
+#undef ENABLE_MIC_INPUT
 
-       dev_dbg(rtd->socdev->dev, "shutdown");
-}
+static struct clk *mclk;
 
 static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
@@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-       struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
-       struct ssc_device *ssc = ssc_p->ssc;
        int ret;
 
-       unsigned int rate;
-       int cmr_div, period;
-
-       if (ssc == NULL) {
-               printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
-               return -EINVAL;
-       }
-
        /* set codec DAI configuration */
        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
 
        /* set cpu DAI configuration */
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * The SSC clock dividers depend on the sample rate.  The CMR.DIV
-        * field divides the system master clock MCK to drive the SSC TK
-        * signal which provides the codec BCLK.  The TCMR.PERIOD and
-        * RCMR.PERIOD fields further divide the BCLK signal to drive
-        * the SSC TF and RF signals which provide the codec DACLRC and
-        * ADCLRC clocks.
-        *
-        * The dividers were determined through trial and error, where a
-        * CMR.DIV value is chosen such that the resulting BCLK value is
-        * divisible, or almost divisible, by (2 * sample rate), and then
-        * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
-        */
-       rate = params_rate(params);
-
-       switch (rate) {
-       case 8000:
-               cmr_div = 55;   /* BCLK = 133MHz/(2*55) = 1.209MHz */
-               period = 74;    /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
-               break;
-       case 11025:
-               cmr_div = 67;   /* BCLK = 133MHz/(2*60) = 1.108MHz */
-               period = 45;    /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
-               break;
-       case 16000:
-               cmr_div = 63;   /* BCLK = 133MHz/(2*63) = 1.055MHz */
-               period = 32;    /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
-               break;
-       case 22050:
-               cmr_div = 52;   /* BCLK = 133MHz/(2*52) = 1.278MHz */
-               period = 28;    /* LRC = BCLK/(2*(28+1)) = 22049Hz */
-               break;
-       case 32000:
-               cmr_div = 66;   /* BCLK = 133MHz/(2*66) = 1.007MHz */
-               period = 15;    /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
-               break;
-       case 44100:
-               cmr_div = 29;   /* BCLK = 133MHz/(2*29) = 2.293MHz */
-               period = 25;    /* LRC = BCLK/(2*(25+1)) = 44098Hz */
-               break;
-       case 48000:
-               cmr_div = 33;   /* BCLK = 133MHz/(2*33) = 2.015MHz */
-               period = 20;    /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
-               break;
-       case 88200:
-               cmr_div = 29;   /* BCLK = 133MHz/(2*29) = 2.293MHz */
-               period = 12;    /* LRC = BCLK/(2*(12+1)) = 88196Hz */
-               break;
-       case 96000:
-               cmr_div = 23;   /* BCLK = 133MHz/(2*23) = 2.891MHz */
-               period = 14;    /* LRC = BCLK/(2*(14+1)) = 96376Hz */
-               break;
-       default:
-               printk(KERN_WARNING "unsupported rate %d"
-                               " on at91sam9g20ek board\n", rate);
-               return -EINVAL;
-       }
-
-       /* set the MCK divider for BCLK */
-       ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
-       if (ret < 0)
-               return ret;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               /* set the BCLK divider for DACLRC */
-               ret = snd_soc_dai_set_clkdiv(cpu_dai,
-                                               ATMEL_SSC_TCMR_PERIOD, period);
-       } else {
-               /* set the BCLK divider for ADCLRC */
-               ret = snd_soc_dai_set_clkdiv(cpu_dai,
-                                               ATMEL_SSC_RCMR_PERIOD, period);
-       }
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
 
@@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
 }
 
 static struct snd_soc_ops at91sam9g20ek_ops = {
-       .startup = at91sam9g20ek_startup,
        .hw_params = at91sam9g20ek_hw_params,
-       .shutdown = at91sam9g20ek_shutdown,
 };
 
 static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
@@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = {
  */
 static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
 {
+       struct snd_soc_dai *codec_dai = &codec->dai[0];
+       int ret;
+
        printk(KERN_DEBUG
                        "at91sam9g20ek_wm8731 "
                        ": at91sam9g20ek_wm8731_init() called\n");
 
+       ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+               MCLK_RATE, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+               return ret;
+       }
+
        /* Add specific widgets */
        snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
                                  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
@@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
        snd_soc_dapm_nc_pin(codec, "RLINEIN");
        snd_soc_dapm_nc_pin(codec, "LLINEIN");
 
-       /* always connected */
+#ifdef ENABLE_MIC_INPUT
        snd_soc_dapm_enable_pin(codec, "Int Mic");
+#else
+       snd_soc_dapm_nc_pin(codec, "Int Mic");
+#endif
+
+       /* always connected */
        snd_soc_dapm_enable_pin(codec, "Ext Spk");
 
        snd_soc_dapm_sync(codec);
index b1ed423..2758b90 100644 (file)
@@ -277,28 +277,24 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
        if (!dai->active)
                return 0;
 
-       ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+       ret = sport_set_multichannel(sport, 16, 0x1F, 1);
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+       ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+       ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       if (dai->capture.active)
-               sport_rx_start(sport);
-       if (dai->playback.active)
-               sport_tx_start(sport);
        return 0;
 }
 
index edfbdc0..9825b71 100644 (file)
@@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
        .codec_dev = &soc_codec_dev_ad73311,
 };
 
-static struct platform_device *bf52x_ad73311_snd_device;
+static struct platform_device *bf5xx_ad73311_snd_device;
 
 static int __init bf5xx_ad73311_init(void)
 {
        int ret;
 
        pr_debug("%s enter\n", __func__);
-       bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!bf52x_ad73311_snd_device)
+       bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!bf5xx_ad73311_snd_device)
                return -ENOMEM;
 
-       platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
-       bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
-       ret = platform_device_add(bf52x_ad73311_snd_device);
+       platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
+       bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
+       ret = platform_device_add(bf5xx_ad73311_snd_device);
 
        if (ret)
-               platform_device_put(bf52x_ad73311_snd_device);
+               platform_device_put(bf5xx_ad73311_snd_device);
 
        return ret;
 }
@@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void)
 static void __exit bf5xx_ad73311_exit(void)
 {
        pr_debug("%s enter\n", __func__);
-       platform_device_unregister(bf52x_ad73311_snd_device);
+       platform_device_unregister(bf5xx_ad73311_snd_device);
 }
 
 module_init(bf5xx_ad73311_init);
index af06904..876abad 100644 (file)
@@ -259,22 +259,18 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
        if (!dai->active)
                return 0;
 
-       ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+       ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+       ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
        if (ret) {
                pr_err("SPORT is busy!\n");
                return -EBUSY;
        }
 
-       if (dai->capture.active)
-               sport_rx_start(sport);
-       if (dai->playback.active)
-               sport_tx_start(sport);
        return 0;
 }
 
index bc0cdde..3a00fa4 100644 (file)
@@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
        .codec_data = &bf5xx_ssm2602_setup,
 };
 
-static struct platform_device *bf52x_ssm2602_snd_device;
+static struct platform_device *bf5xx_ssm2602_snd_device;
 
 static int __init bf5xx_ssm2602_init(void)
 {
        int ret;
 
        pr_debug("%s enter\n", __func__);
-       bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!bf52x_ssm2602_snd_device)
+       bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!bf5xx_ssm2602_snd_device)
                return -ENOMEM;
 
-       platform_set_drvdata(bf52x_ssm2602_snd_device,
+       platform_set_drvdata(bf5xx_ssm2602_snd_device,
                                &bf5xx_ssm2602_snd_devdata);
-       bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
-       ret = platform_device_add(bf52x_ssm2602_snd_device);
+       bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
+       ret = platform_device_add(bf5xx_ssm2602_snd_device);
 
        if (ret)
-               platform_device_put(bf52x_ssm2602_snd_device);
+               platform_device_put(bf5xx_ssm2602_snd_device);
 
        return ret;
 }
@@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void)
 static void __exit bf5xx_ssm2602_exit(void)
 {
        pr_debug("%s enter\n", __func__);
-       platform_device_unregister(bf52x_ssm2602_snd_device);
+       platform_device_unregister(bf5xx_ssm2602_snd_device);
 }
 
 module_init(bf5xx_ssm2602_init);
index bbc97fd..57f5b73 100644 (file)
@@ -12,11 +12,13 @@ config SND_SOC_ALL_CODECS
        tristate "Build all ASoC CODEC drivers"
        select SND_SOC_L3
        select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+       select SND_SOC_AD1938 if SPI_MASTER
        select SND_SOC_AD1980 if SND_SOC_AC97_BUS
        select SND_SOC_AD73311 if I2C
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
        select SND_SOC_CS4270 if I2C
+       select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
@@ -30,6 +32,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8350 if MFD_WM8350
        select SND_SOC_WM8400 if MFD_WM8400
        select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
+       select SND_SOC_WM8523 if I2C
        select SND_SOC_WM8580 if I2C
        select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
@@ -39,9 +42,11 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8903 if I2C
        select SND_SOC_WM8940 if I2C
        select SND_SOC_WM8960 if I2C
+       select SND_SOC_WM8961 if I2C
        select SND_SOC_WM8971 if I2C
        select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8990 if I2C
+       select SND_SOC_WM8993 if I2C
        select SND_SOC_WM9081 if I2C
        select SND_SOC_WM9705 if SND_SOC_AC97_BUS
        select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -62,6 +67,9 @@ config SND_SOC_AC97_CODEC
        tristate
        select SND_AC97_CODEC
 
+config SND_SOC_AD1938
+       tristate
+
 config SND_SOC_AD1980
        tristate
 
@@ -129,6 +137,9 @@ config SND_SOC_WM8400
 config SND_SOC_WM8510
        tristate
 
+config SND_SOC_WM8523
+       tristate
+
 config SND_SOC_WM8580
        tristate
 
@@ -156,6 +167,9 @@ config SND_SOC_WM8940
 config SND_SOC_WM8960
        tristate
 
+config SND_SOC_WM8961
+       tristate
+
 config SND_SOC_WM8971
        tristate
 
@@ -165,6 +179,9 @@ config SND_SOC_WM8988
 config SND_SOC_WM8990
        tristate
 
+config SND_SOC_WM8993
+       tristate
+
 config SND_SOC_WM9081
        tristate
 
@@ -176,3 +193,7 @@ config SND_SOC_WM9712
 
 config SND_SOC_WM9713
        tristate
+
+# Amp
+config SND_SOC_MAX9877
+       tristate
index 8b75305..9a92f55 100644 (file)
@@ -1,4 +1,5 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ad1938-objs := ad1938.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4104-objs := ak4104.o
@@ -18,6 +19,7 @@ snd-soc-uda1380-objs := uda1380.o
 snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
+snd-soc-wm8523-objs := wm8523.o
 snd-soc-wm8580-objs := wm8580.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
@@ -27,15 +29,21 @@ snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
 snd-soc-wm8940-objs := wm8940.o
 snd-soc-wm8960-objs := wm8960.o
+snd-soc-wm8961-objs := wm8961.o
 snd-soc-wm8971-objs := wm8971.o
 snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8993-objs := wm8993.o
 snd-soc-wm9081-objs := wm9081.o
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
 
+# Amp
+snd-soc-max9877-objs := max9877.o
+
 obj-$(CONFIG_SND_SOC_AC97_CODEC)       += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1938)   += snd-soc-ad1938.o
 obj-$(CONFIG_SND_SOC_AD1980)   += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
@@ -55,6 +63,7 @@ obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
 obj-$(CONFIG_SND_SOC_WM8350)   += snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8400)   += snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)   += snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8523)   += snd-soc-wm8523.o
 obj-$(CONFIG_SND_SOC_WM8580)   += snd-soc-wm8580.o
 obj-$(CONFIG_SND_SOC_WM8728)   += snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
@@ -65,9 +74,14 @@ obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
 obj-$(CONFIG_SND_SOC_WM8971)   += snd-soc-wm8971.o
 obj-$(CONFIG_SND_SOC_WM8940)   += snd-soc-wm8940.o
 obj-$(CONFIG_SND_SOC_WM8960)   += snd-soc-wm8960.o
+obj-$(CONFIG_SND_SOC_WM8961)   += snd-soc-wm8961.o
 obj-$(CONFIG_SND_SOC_WM8988)   += snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)   += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8993)   += snd-soc-wm8993.o
 obj-$(CONFIG_SND_SOC_WM9081)   += snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9705)   += snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)   += snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)   += snd-soc-wm9713.o
+
+# Amp
+obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
new file mode 100644 (file)
index 0000000..ffc97c9
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * File:         sound/soc/codecs/ad1938.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      June 04 2009
+ * Description:  Driver for AD1938 sound chip
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <linux/spi/spi.h>
+#include "ad1938.h"
+
+/* codec private data */
+struct ad1938_priv {
+       struct snd_soc_codec codec;
+       u8 reg_cache[AD1938_NUM_REGS];
+};
+
+static struct snd_soc_codec *ad1938_codec;
+struct snd_soc_codec_device soc_codec_dev_ad1938;
+static int ad1938_register(struct ad1938_priv *ad1938);
+static void ad1938_unregister(struct ad1938_priv *ad1938);
+
+/*
+ * AD1938 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static const struct soc_enum ad1938_deemp_enum =
+       SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp);
+
+static const struct snd_kcontrol_new ad1938_snd_controls[] = {
+       /* DAC volume control */
+       SOC_DOUBLE_R("DAC1  Volume", AD1938_DAC_L1_VOL,
+                       AD1938_DAC_R1_VOL, 0, 0xFF, 1),
+       SOC_DOUBLE_R("DAC2  Volume", AD1938_DAC_L2_VOL,
+                       AD1938_DAC_R2_VOL, 0, 0xFF, 1),
+       SOC_DOUBLE_R("DAC3  Volume", AD1938_DAC_L3_VOL,
+                       AD1938_DAC_R3_VOL, 0, 0xFF, 1),
+       SOC_DOUBLE_R("DAC4  Volume", AD1938_DAC_L4_VOL,
+                       AD1938_DAC_R4_VOL, 0, 0xFF, 1),
+
+       /* ADC switch control */
+       SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE, AD1938_ADCR1_MUTE, 1, 1),
+       SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE, AD1938_ADCR2_MUTE, 1, 1),
+
+       /* DAC switch control */
+       SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE, AD1938_DACR1_MUTE, 1, 1),
+       SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE, AD1938_DACR2_MUTE, 1, 1),
+       SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE, AD1938_DACR3_MUTE, 1, 1),
+       SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE, AD1938_DACR4_MUTE, 1, 1),
+
+       /* ADC high-pass filter */
+       SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0,
+                       AD1938_ADC_HIGHPASS_FILTER, 1, 0),
+
+       /* DAC de-emphasis */
+       SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
+       SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+       { "DAC", NULL, "ADC_PWR" },
+       { "ADC", NULL, "ADC_PWR" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1938_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int reg;
+
+       reg = codec->read(codec, AD1938_DAC_CTRL2);
+       reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg & (~AD1938_DAC_MASTER_MUTE);
+       codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+       return 0;
+}
+
+static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
+{
+       int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
+       reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg | AD1938_PLL_POWERDOWN;
+       codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
+
+       return 0;
+}
+
+static int ad1938_set_tdm_slot(struct snd_soc_dai *dai,
+               unsigned int mask, int slots)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+       int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+
+       dac_reg &= ~AD1938_DAC_CHAN_MASK;
+       adc_reg &= ~AD1938_ADC_CHAN_MASK;
+
+       switch (slots) {
+       case 2:
+               dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT;
+               adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT;
+               break;
+       case 4:
+               dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT;
+               adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT;
+               break;
+       case 8:
+               dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT;
+               adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT;
+               break;
+       case 16:
+               dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT;
+               adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+       codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+
+       return 0;
+}
+
+static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int adc_reg, dac_reg;
+
+       adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+       dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+
+       /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S with TDM)
+        * and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+        */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+               adc_reg |= AD1938_ADC_SERFMT_TDM;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+               adc_reg |= AD1938_ADC_SERFMT_AUX;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+               adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+               adc_reg &= ~AD1938_ADC_BCLK_INV;
+               dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+               dac_reg &= ~AD1938_DAC_BCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
+               adc_reg |= AD1938_ADC_LEFT_HIGH;
+               adc_reg &= ~AD1938_ADC_BCLK_INV;
+               dac_reg |= AD1938_DAC_LEFT_HIGH;
+               dac_reg &= ~AD1938_DAC_BCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
+               adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+               adc_reg |= AD1938_ADC_BCLK_INV;
+               dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+               dac_reg |= AD1938_DAC_BCLK_INV;
+               break;
+
+       case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
+               adc_reg |= AD1938_ADC_LEFT_HIGH;
+               adc_reg |= AD1938_ADC_BCLK_INV;
+               dac_reg |= AD1938_DAC_LEFT_HIGH;
+               dac_reg |= AD1938_DAC_BCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+               adc_reg |= AD1938_ADC_LCR_MASTER;
+               adc_reg |= AD1938_ADC_BCLK_MASTER;
+               dac_reg |= AD1938_DAC_LCR_MASTER;
+               dac_reg |= AD1938_DAC_BCLK_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+               adc_reg |= AD1938_ADC_LCR_MASTER;
+               adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+               dac_reg |= AD1938_DAC_LCR_MASTER;
+               dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+               adc_reg &= ~AD1938_ADC_LCR_MASTER;
+               adc_reg |= AD1938_ADC_BCLK_MASTER;
+               dac_reg &= ~AD1938_DAC_LCR_MASTER;
+               dac_reg |= AD1938_DAC_BCLK_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+               adc_reg &= ~AD1938_ADC_LCR_MASTER;
+               adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+               dac_reg &= ~AD1938_DAC_LCR_MASTER;
+               dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+       codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+
+       return 0;
+}
+
+static int ad1938_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
+{
+       int word_len = 0, reg = 0;
+
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               word_len = 3;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               word_len = 1;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               word_len = 0;
+               break;
+       }
+
+       reg = codec->read(codec, AD1938_DAC_CTRL2);
+       reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
+       codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+       reg = codec->read(codec, AD1938_ADC_CTRL1);
+       reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
+       codec->write(codec, AD1938_ADC_CTRL1, reg);
+
+       return 0;
+}
+
+static int ad1938_set_bias_level(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               ad1938_pll_powerctrl(codec, 1);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+       case SND_SOC_BIAS_OFF:
+               ad1938_pll_powerctrl(codec, 0);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+/*
+ * interface to read/write ad1938 register
+ */
+
+#define AD1938_SPI_ADDR    0x4
+#define AD1938_SPI_READ    0x1
+#define AD1938_SPI_BUFLEN  3
+
+/*
+ * write to the ad1938 register space
+ */
+
+static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
+               unsigned int value)
+{
+       u8 *reg_cache = codec->reg_cache;
+       int ret = 0;
+
+       if (value != reg_cache[reg]) {
+               uint8_t buf[AD1938_SPI_BUFLEN];
+               struct spi_transfer t = {
+                       .tx_buf = buf,
+                       .len = AD1938_SPI_BUFLEN,
+               };
+               struct spi_message m;
+
+               buf[0] = AD1938_SPI_ADDR << 1;
+               buf[1] = reg;
+               buf[2] = value;
+               spi_message_init(&m);
+               spi_message_add_tail(&t, &m);
+               ret = spi_sync(codec->control_data, &m);
+               if (ret == 0)
+                       reg_cache[reg] = value;
+       }
+
+       return ret;
+}
+
+/*
+ * read from the ad1938 register space cache
+ */
+
+static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
+                                         unsigned int reg)
+{
+       u8 *reg_cache = codec->reg_cache;
+
+       if (reg >= codec->reg_cache_size)
+               return -EINVAL;
+
+       return reg_cache[reg];
+}
+
+/*
+ * read from the ad1938 register space
+ */
+
+static unsigned int ad1938_read_reg(struct snd_soc_codec *codec, unsigned int reg)
+{
+       char w_buf[AD1938_SPI_BUFLEN];
+       char r_buf[AD1938_SPI_BUFLEN];
+       int ret;
+
+       struct spi_transfer t = {
+               .tx_buf = w_buf,
+               .rx_buf = r_buf,
+               .len = AD1938_SPI_BUFLEN,
+       };
+       struct spi_message m;
+
+       w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
+       w_buf[1] = reg;
+       w_buf[2] = 0;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+       ret = spi_sync(codec->control_data, &m);
+       if (ret == 0)
+               return  r_buf[2];
+       else
+               return -EIO;
+}
+
+static int ad1938_fill_cache(struct snd_soc_codec *codec)
+{
+       int i;
+       u8 *reg_cache = codec->reg_cache;
+       struct spi_device *spi = codec->control_data;
+
+       for (i = 0; i < codec->reg_cache_size; i++) {
+               int ret = ad1938_read_reg(codec, i);
+               if (ret == -EIO) {
+                       dev_err(&spi->dev, "AD1938 SPI read failure\n");
+                       return ret;
+               }
+               reg_cache[i] = ret;
+       }
+
+       return 0;
+}
+
+static int __devinit ad1938_spi_probe(struct spi_device *spi)
+{
+       struct snd_soc_codec *codec;
+       struct ad1938_priv *ad1938;
+
+       ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL);
+       if (ad1938 == NULL)
+               return -ENOMEM;
+
+       codec = &ad1938->codec;
+       codec->control_data = spi;
+       codec->dev = &spi->dev;
+
+       dev_set_drvdata(&spi->dev, ad1938);
+
+       return ad1938_register(ad1938);
+}
+
+static int __devexit ad1938_spi_remove(struct spi_device *spi)
+{
+       struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev);
+
+       ad1938_unregister(ad1938);
+       return 0;
+}
+
+static struct spi_driver ad1938_spi_driver = {
+       .driver = {
+               .name   = "ad1938-spi",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad1938_spi_probe,
+       .remove         = __devexit_p(ad1938_spi_remove),
+};
+
+static struct snd_soc_dai_ops ad1938_dai_ops = {
+       .hw_params = ad1938_hw_params,
+       .digital_mute = ad1938_mute,
+       .set_tdm_slot = ad1938_set_tdm_slot,
+       .set_fmt = ad1938_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad1938_dai = {
+       .name = "AD1938",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 4,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+       },
+       .ops = &ad1938_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad1938_dai);
+
+static int ad1938_register(struct ad1938_priv *ad1938)
+{
+       int ret;
+       struct snd_soc_codec *codec = &ad1938->codec;
+
+       if (ad1938_codec) {
+               dev_err(codec->dev, "Another ad1938 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+       codec->private_data = ad1938;
+       codec->reg_cache = ad1938->reg_cache;
+       codec->reg_cache_size = AD1938_NUM_REGS;
+       codec->name = "AD1938";
+       codec->owner = THIS_MODULE;
+       codec->dai = &ad1938_dai;
+       codec->num_dai = 1;
+       codec->write = ad1938_write_reg;
+       codec->read = ad1938_read_reg_cache;
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       ad1938_dai.dev = codec->dev;
+       ad1938_codec = codec;
+
+       /* default setting for ad1938 */
+       codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0); /* unmute dac channels */
+       codec->write(codec, AD1938_DAC_CTRL2, 0x1A); /* de-emphasis: 48kHz, powedown dac */
+       codec->write(codec, AD1938_DAC_CTRL0, 0x21); /* powerdown dac, dac tdm mode */
+       codec->write(codec, AD1938_ADC_CTRL0, 0x3); /* high-pass filter enable */
+       codec->write(codec, AD1938_ADC_CTRL1, 0x43); /* sata delay=1, adc aux mode */
+       codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D); /* pll input:mclki/xi */
+       codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
+
+       ad1938_fill_cache(codec);
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&ad1938_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void ad1938_unregister(struct ad1938_priv *ad1938)
+{
+       ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&ad1938_dai);
+       snd_soc_unregister_codec(&ad1938->codec);
+       kfree(ad1938);
+       ad1938_codec = NULL;
+}
+
+static int ad1938_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (ad1938_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = ad1938_codec;
+       codec = ad1938_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, ad1938_snd_controls,
+                            ARRAY_SIZE(ad1938_snd_controls));
+       snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
+                                 ARRAY_SIZE(ad1938_dapm_widgets));
+       snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+       snd_soc_dapm_new_widgets(codec);
+
+       ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+/* power down chip */
+static int ad1938_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad1938_suspend(struct platform_device *pdev,
+               pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int ad1938_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+               ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+       return 0;
+}
+#else
+#define ad1938_suspend NULL
+#define ad1938_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_ad1938 = {
+       .probe =        ad1938_probe,
+       .remove =       ad1938_remove,
+       .suspend =      ad1938_suspend,
+       .resume =       ad1938_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
+
+static int __init ad1938_init(void)
+{
+       int ret;
+
+       ret = spi_register_driver(&ad1938_spi_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n",
+                               ret);
+       }
+
+       return ret;
+}
+module_init(ad1938_init);
+
+static void __exit ad1938_exit(void)
+{
+       spi_unregister_driver(&ad1938_spi_driver);
+}
+module_exit(ad1938_exit);
+
+MODULE_DESCRIPTION("ASoC ad1938 driver");
+MODULE_AUTHOR("Barry Song ");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h
new file mode 100644 (file)
index 0000000..fe3c48c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * File:         sound/soc/codecs/ad1836.h
+ * Based on:
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      May 25, 2009
+ * Description:  definitions for AD1938 registers
+ *
+ * Modified:
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __AD1938_H__
+#define __AD1938_H__
+
+#define AD1938_PLL_CLK_CTRL0    0
+#define AD1938_PLL_POWERDOWN           0x01
+#define AD1938_PLL_CLK_CTRL1    1
+#define AD1938_DAC_CTRL0        2
+#define AD1938_DAC_POWERDOWN           0x01
+#define AD1938_DAC_SERFMT_MASK         0xC0
+#define AD1938_DAC_SERFMT_STEREO       (0 << 6)
+#define AD1938_DAC_SERFMT_TDM          (1 << 6)
+#define AD1938_DAC_CTRL1        3
+#define AD1938_DAC_2_CHANNELS   0
+#define AD1938_DAC_4_CHANNELS   1
+#define AD1938_DAC_8_CHANNELS   2
+#define AD1938_DAC_16_CHANNELS  3
+#define AD1938_DAC_CHAN_SHFT    1
+#define AD1938_DAC_CHAN_MASK    (3 << AD1938_DAC_CHAN_SHFT)
+#define AD1938_DAC_LCR_MASTER   (1 << 4)
+#define AD1938_DAC_BCLK_MASTER  (1 << 5)
+#define AD1938_DAC_LEFT_HIGH    (1 << 3)
+#define AD1938_DAC_BCLK_INV     (1 << 7)
+#define AD1938_DAC_CTRL2        4
+#define AD1938_DAC_WORD_LEN_MASK       0xC
+#define AD1938_DAC_MASTER_MUTE  1
+#define AD1938_DAC_CHNL_MUTE    5
+#define AD1938_DACL1_MUTE       0
+#define AD1938_DACR1_MUTE       1
+#define AD1938_DACL2_MUTE       2
+#define AD1938_DACR2_MUTE       3
+#define AD1938_DACL3_MUTE       4
+#define AD1938_DACR3_MUTE       5
+#define AD1938_DACL4_MUTE       6
+#define AD1938_DACR4_MUTE       7
+#define AD1938_DAC_L1_VOL       6
+#define AD1938_DAC_R1_VOL       7
+#define AD1938_DAC_L2_VOL       8
+#define AD1938_DAC_R2_VOL       9
+#define AD1938_DAC_L3_VOL       10
+#define AD1938_DAC_R3_VOL       11
+#define AD1938_DAC_L4_VOL       12
+#define AD1938_DAC_R4_VOL       13
+#define AD1938_ADC_CTRL0        14
+#define AD1938_ADC_POWERDOWN           0x01
+#define AD1938_ADC_HIGHPASS_FILTER     1
+#define AD1938_ADCL1_MUTE              2
+#define AD1938_ADCR1_MUTE              3
+#define AD1938_ADCL2_MUTE              4
+#define AD1938_ADCR2_MUTE              5
+#define AD1938_ADC_CTRL1        15
+#define AD1938_ADC_SERFMT_MASK         0x60
+#define AD1938_ADC_SERFMT_STEREO       (0 << 5)
+#define AD1938_ADC_SERFMT_TDM          (1 << 2)
+#define AD1938_ADC_SERFMT_AUX          (2 << 5)
+#define AD1938_ADC_WORD_LEN_MASK       0x3
+#define AD1938_ADC_CTRL2        16
+#define AD1938_ADC_2_CHANNELS   0
+#define AD1938_ADC_4_CHANNELS   1
+#define AD1938_ADC_8_CHANNELS   2
+#define AD1938_ADC_16_CHANNELS  3
+#define AD1938_ADC_CHAN_SHFT    4
+#define AD1938_ADC_CHAN_MASK    (3 << AD1938_ADC_CHAN_SHFT)
+#define AD1938_ADC_LCR_MASTER   (1 << 3)
+#define AD1938_ADC_BCLK_MASTER  (1 << 6)
+#define AD1938_ADC_LEFT_HIGH    (1 << 2)
+#define AD1938_ADC_BCLK_INV     (1 << 1)
+
+#define AD1938_NUM_REGS          17
+
+extern struct snd_soc_dai ad1938_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1938;
+#endif
index dd33802..0abec0d 100644 (file)
@@ -59,21 +59,6 @@ static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
        return cache[reg];
 }
 
-static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       u8 data;
-       data = reg;
-
-       if (codec->hw_write(codec->control_data, &data, 1) != 1)
-               return -EIO;
-
-       if (codec->hw_read(codec->control_data, &data, 1) != 1)
-               return -EIO;
-
-       return data;
-};
-
 /*
  * write ak4535 register cache
  */
@@ -635,7 +620,6 @@ static int ak4535_probe(struct platform_device *pdev)
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
        if (setup->i2c_address) {
                codec->hw_write = (hw_write_t)i2c_master_send;
-               codec->hw_read = (hw_read_t)i2c_master_recv;
                ret = ak4535_add_i2c_device(pdev, setup);
        }
 #endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
new file mode 100644 (file)
index 0000000..a1f1119
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * max9877.c  --  amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9877.h"
+
+static struct i2c_client *i2c;
+
+static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
+
+static void max9877_write_regs(void)
+{
+       if (i2c_master_send(i2c, max9877_regs, 5) != 5)
+               dev_err(&i2c->dev, "i2c write failed\n");
+}
+
+static int max9877_get_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+       unsigned int invert = mc->invert;
+
+       ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       mask - ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static int max9877_set_reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+       unsigned int invert = mc->invert;
+       unsigned int val = (ucontrol->value.integer.value[0] & mask);
+
+       if (invert)
+               val = mask - val;
+
+       if (((max9877_regs[reg] >> shift) & mask) == val)
+               return 0;
+
+       max9877_regs[reg] &= ~(mask << shift);
+       max9877_regs[reg] |= val << shift;
+       max9877_write_regs();
+
+       return 1;
+}
+
+static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+
+       ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+       ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
+
+       return 0;
+}
+
+static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = mc->max;
+       unsigned int val = (ucontrol->value.integer.value[0] & mask);
+       unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
+       unsigned int change = 1;
+
+       if (((max9877_regs[reg] >> shift) & mask) == val)
+               change = 0;
+
+       if (((max9877_regs[reg2] >> shift) & mask) == val2)
+               change = 0;
+
+       if (change) {
+               max9877_regs[reg] &= ~(mask << shift);
+               max9877_regs[reg] |= val << shift;
+               max9877_regs[reg2] &= ~(mask << shift);
+               max9877_regs[reg2] |= val2 << shift;
+               max9877_write_regs();
+       }
+
+       return change;
+}
+
+static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
+
+       if (value)
+               value -= 1;
+
+       ucontrol->value.integer.value[0] = value;
+       return 0;
+}
+
+static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       u8 value = ucontrol->value.integer.value[0];
+
+       if (value)
+               value += 1;
+
+       if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
+               return 0;
+
+       max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+       max9877_write_regs();
+       return 1;
+}
+
+static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
+
+       value = value >> MAX9877_OSC_OFFSET;
+
+       ucontrol->value.integer.value[0] = value;
+       return 0;
+}
+
+static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       u8 value = ucontrol->value.integer.value[0];
+
+       value = value << MAX9877_OSC_OFFSET;
+       if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
+               return 0;
+
+       max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+       max9877_write_regs();
+       return 1;
+}
+
+static const unsigned int max9877_pgain_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
+       2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
+
+static const unsigned int max9877_output_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+       8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+       16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+       24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const char *max9877_out_mode[] = {
+       "INA -> SPK",
+       "INA -> HP",
+       "INA -> SPK and HP",
+       "INB -> SPK",
+       "INB -> HP",
+       "INB -> SPK and HP",
+       "INA + INB -> SPK",
+       "INA + INB -> HP",
+       "INA + INB -> SPK and HP",
+};
+
+static const char *max9877_osc_mode[] = {
+       "1176KHz",
+       "1100KHz",
+       "700KHz",
+};
+
+static const struct soc_enum max9877_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+};
+
+static const struct snd_kcontrol_new max9877_controls[] = {
+       SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
+                       MAX9877_INPUT_MODE, 0, 2, 0,
+                       max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+       SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
+                       MAX9877_INPUT_MODE, 2, 2, 0,
+                       max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+       SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
+                       MAX9877_SPK_VOLUME, 0, 31, 0,
+                       max9877_get_reg, max9877_set_reg, max9877_output_tlv),
+       SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
+                       MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+                       max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
+       SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
+                       MAX9877_INPUT_MODE, 4, 1, 1,
+                       max9877_get_reg, max9877_set_reg),
+       SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
+                       MAX9877_INPUT_MODE, 5, 1, 1,
+                       max9877_get_reg, max9877_set_reg),
+       SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
+                       MAX9877_INPUT_MODE, 6, 1, 0,
+                       max9877_get_reg, max9877_set_reg),
+       SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
+                       MAX9877_OUTPUT_MODE, 6, 1, 0,
+                       max9877_get_reg, max9877_set_reg),
+       SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
+                       MAX9877_OUTPUT_MODE, 7, 1, 1,
+                       max9877_get_reg, max9877_set_reg),
+       SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
+                       max9877_get_out_mode, max9877_set_out_mode),
+       SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
+                       max9877_get_osc_mode, max9877_set_osc_mode),
+};
+
+/* This function is called from ASoC machine driver */
+int max9877_add_controls(struct snd_soc_codec *codec)
+{
+       return snd_soc_add_controls(codec, max9877_controls,
+                       ARRAY_SIZE(max9877_controls));
+}
+EXPORT_SYMBOL_GPL(max9877_add_controls);
+
+static int __devinit max9877_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       i2c = client;
+
+       max9877_write_regs();
+
+       return 0;
+}
+
+static __devexit int max9877_i2c_remove(struct i2c_client *client)
+{
+       i2c = NULL;
+
+       return 0;
+}
+
+static const struct i2c_device_id max9877_i2c_id[] = {
+       { "max9877", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
+
+static struct i2c_driver max9877_i2c_driver = {
+       .driver = {
+               .name = "max9877",
+               .owner = THIS_MODULE,
+       },
+       .probe = max9877_i2c_probe,
+       .remove = __devexit_p(max9877_i2c_remove),
+       .id_table = max9877_i2c_id,
+};
+
+static int __init max9877_init(void)
+{
+       return i2c_add_driver(&max9877_i2c_driver);
+}
+module_init(max9877_init);
+
+static void __exit max9877_exit(void)
+{
+       i2c_del_driver(&max9877_i2c_driver);
+}
+module_exit(max9877_exit);
+
+MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
new file mode 100644 (file)
index 0000000..6da7229
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * max9877.h  --  amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _MAX9877_H
+#define _MAX9877_H
+
+#define MAX9877_INPUT_MODE             0x00
+#define MAX9877_SPK_VOLUME             0x01
+#define MAX9877_HPL_VOLUME             0x02
+#define MAX9877_HPR_VOLUME             0x03
+#define MAX9877_OUTPUT_MODE            0x04
+
+/* MAX9877_INPUT_MODE */
+#define MAX9877_INB                    (1 << 4)
+#define MAX9877_INA                    (1 << 5)
+#define MAX9877_ZCD                    (1 << 6)
+
+/* MAX9877_OUTPUT_MODE */
+#define MAX9877_OUTMODE_MASK           (15 << 0)
+#define MAX9877_OSC_MASK               (3 << 4)
+#define MAX9877_OSC_OFFSET             4
+#define MAX9877_BYPASS                 (1 << 6)
+#define MAX9877_SHDN                   (1 << 7)
+
+extern int max9877_add_controls(struct snd_soc_codec *codec);
+
+#endif
index 218b33a..a631911 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "spdif_transciever.h"
 
+MODULE_LICENSE("GPL");
+
 #define STUB_RATES     SNDRV_PCM_RATE_8000_96000
 #define STUB_FORMATS   SNDRV_PCM_FMTBIT_S16_LE
 
@@ -34,6 +36,7 @@ struct snd_soc_dai dit_stub_dai = {
                .formats        = STUB_FORMATS,
        },
 };
+EXPORT_SYMBOL_GPL(dit_stub_dai);
 
 static int spdif_dit_probe(struct platform_device *pdev)
 {
index 8ad4b7b..befc648 100644 (file)
@@ -149,7 +149,7 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return 0;
        }
-       if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+       if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
                return -EIO;
 
        soc_ac97_ops.write(codec->ac97, reg, val);
@@ -168,7 +168,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return val;
        }
-       if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+       if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
                return -EIO;
 
        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
index 4dbb853..818fb37 100644 (file)
@@ -620,6 +620,9 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
 
 static void headset_ramp(struct snd_soc_codec *codec, int ramp)
 {
+       struct snd_soc_device *socdev = codec->socdev;
+       struct twl4030_setup_data *setup = socdev->codec_data;
+
        unsigned char hs_gain, hs_pop;
        struct twl4030_priv *twl4030 = codec->private_data;
        /* Base values for ramp delay calculation: 2^19 - 2^26 */
@@ -629,6 +632,17 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
        hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
        hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
 
+       /* Enable external mute control, this dramatically reduces
+        * the pop-noise */
+       if (setup && setup->hs_extmute) {
+               if (setup->set_hs_extmute) {
+                       setup->set_hs_extmute(1);
+               } else {
+                       hs_pop |= TWL4030_EXTMUTE;
+                       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               }
+       }
+
        if (ramp) {
                /* Headset ramp-up according to the TRM */
                hs_pop |= TWL4030_VMID_EN;
@@ -636,6 +650,9 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
                twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
                hs_pop |= TWL4030_RAMP_EN;
                twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               /* Wait ramp delay time + 1, so the VMID can settle */
+               mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+                       twl4030->sysclk) + 1);
        } else {
                /* Headset ramp-down _not_ according to
                 * the TRM, but in a way that it is working */
@@ -652,6 +669,16 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
                hs_pop &= ~TWL4030_VMID_EN;
                twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
        }
+
+       /* Disable external mute */
+       if (setup && setup->hs_extmute) {
+               if (setup->set_hs_extmute) {
+                       setup->set_hs_extmute(0);
+               } else {
+                       hs_pop &= ~TWL4030_EXTMUTE;
+                       twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               }
+       }
 }
 
 static int headsetlpga_event(struct snd_soc_dapm_widget *w,
@@ -712,7 +739,19 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
 
        reg = twl4030_read_reg_cache(w->codec, m->reg);
 
-       if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
+       /*
+        * bypass_state[0:3] - analog HiFi bypass
+        * bypass_state[4]   - analog voice bypass
+        * bypass_state[5]   - digital voice bypass
+        * bypass_state[6:7] - digital HiFi bypass
+        */
+       if (m->reg == TWL4030_REG_VSTPGA) {
+               /* Voice digital bypass */
+               if (reg)
+                       twl4030->bypass_state |= (1 << 5);
+               else
+                       twl4030->bypass_state &= ~(1 << 5);
+       } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
                /* Analog bypass */
                if (reg & (1 << m->shift))
                        twl4030->bypass_state |=
@@ -726,12 +765,6 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
                        twl4030->bypass_state |= (1 << 4);
                else
                        twl4030->bypass_state &= ~(1 << 4);
-       } else if (m->reg == TWL4030_REG_VSTPGA) {
-               /* Voice digital bypass */
-               if (reg)
-                       twl4030->bypass_state |= (1 << 5);
-               else
-                       twl4030->bypass_state &= ~(1 << 5);
        } else {
                /* Digital bypass */
                if (reg & (0x7 << m->shift))
@@ -924,7 +957,7 @@ static const struct soc_enum twl4030_op_modes_enum =
                        ARRAY_SIZE(twl4030_op_modes_texts),
                        twl4030_op_modes_texts);
 
-int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
+static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -1005,6 +1038,16 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
  */
 static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
 
+/* AVADC clock priority */
+static const char *twl4030_avadc_clk_priority_texts[] = {
+       "Voice high priority", "HiFi high priority"
+};
+
+static const struct soc_enum twl4030_avadc_clk_priority_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2,
+                       ARRAY_SIZE(twl4030_avadc_clk_priority_texts),
+                       twl4030_avadc_clk_priority_texts);
+
 static const char *twl4030_rampdelay_texts[] = {
        "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
        "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
@@ -1106,6 +1149,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
        SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
                0, 3, 5, 0, input_gain_tlv),
 
+       SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
+
        SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
 
        SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
@@ -1240,14 +1285,14 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        /* HandsfreeL/R */
        SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
                &twl4030_dapm_handsfreel_control),
-       SND_SOC_DAPM_SWITCH("HandsfreeL Switch", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_handsfreelmute_control),
        SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
                        0, 0, NULL, 0, handsfreelpga_event,
                        SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
        SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
                &twl4030_dapm_handsfreer_control),
-       SND_SOC_DAPM_SWITCH("HandsfreeR Switch", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_handsfreermute_control),
        SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
                        0, 0, NULL, 0, handsfreerpga_event,
@@ -1359,15 +1404,15 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
        {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
        {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
-       {"HandsfreeL Switch", "Switch", "HandsfreeL Mux"},
-       {"HandsfreeL PGA", NULL, "HandsfreeL Switch"},
+       {"HandsfreeL", "Switch", "HandsfreeL Mux"},
+       {"HandsfreeL PGA", NULL, "HandsfreeL"},
        /* HandsfreeR */
        {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
        {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
        {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
        {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
-       {"HandsfreeR Switch", "Switch", "HandsfreeR Mux"},
-       {"HandsfreeR PGA", NULL, "HandsfreeR Switch"},
+       {"HandsfreeR", "Switch", "HandsfreeR Mux"},
+       {"HandsfreeR PGA", NULL, "HandsfreeR"},
        /* Vibra */
        {"Vibra Mux", "AudioL1", "DAC Left1"},
        {"Vibra Mux", "AudioR1", "DAC Right1"},
@@ -1609,8 +1654,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
 
         /* If the substream has 4 channel, do the necessary setup */
        if (params_channels(params) == 4) {
-               u8 format, mode;
-
                format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
                mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
 
@@ -1806,6 +1849,19 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
+static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+
+       if (tristate)
+               reg |= TWL4030_AIF_TRI_EN;
+       else
+               reg &= ~TWL4030_AIF_TRI_EN;
+
+       return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg);
+}
+
 /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
  * (VTXL, VTXR) for uplink has to be enabled/disabled. */
 static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
@@ -1948,7 +2004,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
 
        /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBS_CFM:
+       case SND_SOC_DAIFMT_CBM_CFM:
                format &= ~(TWL4030_VIF_SLAVE_EN);
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
@@ -1980,6 +2036,19 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
+static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+
+       if (tristate)
+               reg |= TWL4030_VIF_TRI_EN;
+       else
+               reg &= ~TWL4030_VIF_TRI_EN;
+
+       return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg);
+}
+
 #define TWL4030_RATES   (SNDRV_PCM_RATE_8000_48000)
 #define TWL4030_FORMATS         (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
 
@@ -1989,6 +2058,7 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
        .hw_params      = twl4030_hw_params,
        .set_sysclk     = twl4030_set_dai_sysclk,
        .set_fmt        = twl4030_set_dai_fmt,
+       .set_tristate   = twl4030_set_tristate,
 };
 
 static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
@@ -1997,6 +2067,7 @@ static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
        .hw_params      = twl4030_voice_hw_params,
        .set_sysclk     = twl4030_voice_set_dai_sysclk,
        .set_fmt        = twl4030_voice_set_dai_fmt,
+       .set_tristate   = twl4030_voice_set_tristate,
 };
 
 struct snd_soc_dai twl4030_dai[] = {
index fe5f395..2b4bfa2 100644 (file)
@@ -274,6 +274,8 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030;
 struct twl4030_setup_data {
        unsigned int ramp_delay_value;
        unsigned int sysclk;
+       unsigned int hs_extmute:1;
+       void (*set_hs_extmute)(int mute);
 };
 
 #endif /* End of __TWL4030_AUDIO_H__ */
index 5b21594..92ec034 100644 (file)
@@ -5,9 +5,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
- * Improved support for DAPM and audio routing/mixing capabilities,
- * added TLV support.
+ * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com>
  *
  * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
  * codec model.
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
-#include <linux/ioctl.h>
+#include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/initval.h>
-#include <sound/info.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <sound/uda1380.h>
 
 #include "uda1380.h"
 
-static struct work_struct uda1380_work;
 static struct snd_soc_codec *uda1380_codec;
 
+/* codec private data */
+struct uda1380_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[UDA1380_CACHEREGNUM];
+       unsigned int dac_clk;
+       struct work_struct work;
+};
+
 /*
  * uda1380 register cache
  */
@@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
+       struct uda1380_priv *uda1380 = codec->private_data;
        int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
 
        switch (cmd) {
@@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                uda1380_write_reg_cache(codec, UDA1380_MIXER,
                                        mixer & ~R14_SILENCE);
-               schedule_work(&uda1380_work);
+               schedule_work(&uda1380->work);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                uda1380_write_reg_cache(codec, UDA1380_MIXER,
                                        mixer | R14_SILENCE);
-               schedule_work(&uda1380_work);
+               schedule_work(&uda1380->work);
                break;
        }
        return 0;
@@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev)
        return 0;
 }
 
-/*
- * initialise the UDA1380 driver
- * register mixer and dsp interfaces with the kernel
- */
-static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+static int uda1380_probe(struct platform_device *pdev)
 {
-       struct snd_soc_codec *codec = socdev->card->codec;
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct uda1380_platform_data *pdata;
        int ret = 0;
 
-       codec->name = "UDA1380";
-       codec->owner = THIS_MODULE;
-       codec->read = uda1380_read_reg_cache;
-       codec->write = uda1380_write;
-       codec->set_bias_level = uda1380_set_bias_level;
-       codec->dai = uda1380_dai;
-       codec->num_dai = ARRAY_SIZE(uda1380_dai);
-       codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
-                                  GFP_KERNEL);
-       if (codec->reg_cache == NULL)
-               return -ENOMEM;
-       codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
-       codec->reg_cache_step = 1;
-       uda1380_reset(codec);
+       if (uda1380_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
 
-       uda1380_codec = codec;
-       INIT_WORK(&uda1380_work, uda1380_flush_work);
+       socdev->card->codec = uda1380_codec;
+       codec = uda1380_codec;
+       pdata = codec->dev->platform_data;
 
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0) {
-               pr_err("uda1380: failed to create pcms\n");
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
                goto pcm_err;
        }
 
        /* power on device */
        uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        /* set clock input */
-       switch (dac_clk) {
+       switch (pdata->dac_clk) {
        case UDA1380_DAC_CLK_SYSCLK:
                uda1380_write(codec, UDA1380_CLK, 0);
                break;
@@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
                break;
        }
 
-       /* uda1380 init */
        snd_soc_add_controls(codec, uda1380_snd_controls,
                                ARRAY_SIZE(uda1380_snd_controls));
        uda1380_add_widgets(codec);
        ret = snd_soc_init_card(socdev);
        if (ret < 0) {
-               pr_err("uda1380: failed to register card\n");
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
                goto card_err;
        }
 
@@ -732,165 +725,201 @@ card_err:
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
 pcm_err:
-       kfree(codec->reg_cache);
        return ret;
 }
 
-static struct snd_soc_device *uda1380_socdev;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static int uda1380_i2c_probe(struct i2c_client *i2c,
-                            const struct i2c_device_id *id)
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
 {
-       struct snd_soc_device *socdev = uda1380_socdev;
-       struct uda1380_setup_data *setup = socdev->codec_data;
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
-       int ret;
-
-       i2c_set_clientdata(i2c, codec);
-       codec->control_data = i2c;
 
-       ret = uda1380_init(socdev, setup->dac_clk);
-       if (ret < 0)
-               pr_err("uda1380: failed to initialise UDA1380\n");
+       if (codec->control_data)
+               uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
-       return ret;
-}
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
 
-static int uda1380_i2c_remove(struct i2c_client *client)
-{
-       struct snd_soc_codec *codec = i2c_get_clientdata(client);
-       kfree(codec->reg_cache);
        return 0;
 }
 
-static const struct i2c_device_id uda1380_i2c_id[] = {
-       { "uda1380", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
-
-static struct i2c_driver uda1380_i2c_driver = {
-       .driver = {
-               .name =  "UDA1380 I2C Codec",
-               .owner = THIS_MODULE,
-       },
-       .probe =    uda1380_i2c_probe,
-       .remove =   uda1380_i2c_remove,
-       .id_table = uda1380_i2c_id,
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+       .probe =        uda1380_probe,
+       .remove =       uda1380_remove,
+       .suspend =      uda1380_suspend,
+       .resume =       uda1380_resume,
 };
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
 
-static int uda1380_add_i2c_device(struct platform_device *pdev,
-                                 const struct uda1380_setup_data *setup)
+static int uda1380_register(struct uda1380_priv *uda1380)
 {
-       struct i2c_board_info info;
-       struct i2c_adapter *adapter;
-       struct i2c_client *client;
-       int ret;
+       int ret, i;
+       struct snd_soc_codec *codec = &uda1380->codec;
+       struct uda1380_platform_data *pdata = codec->dev->platform_data;
 
-       ret = i2c_add_driver(&uda1380_i2c_driver);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "can't add i2c driver\n");
-               return ret;
+       if (uda1380_codec) {
+               dev_err(codec->dev, "Another UDA1380 is registered\n");
+               return -EINVAL;
+       }
+
+       if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
+               return -EINVAL;
+
+       ret = gpio_request(pdata->gpio_power, "uda1380 power");
+       if (ret)
+               goto err_out;
+       ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
+       if (ret)
+               goto err_gpio;
+
+       gpio_direction_output(pdata->gpio_power, 1);
+
+       /* we may need to have the clock running here - pH5 */
+       gpio_direction_output(pdata->gpio_reset, 1);
+       udelay(5);
+       gpio_set_value(pdata->gpio_reset, 0);
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = uda1380;
+       codec->name = "UDA1380";
+       codec->owner = THIS_MODULE;
+       codec->read = uda1380_read_reg_cache;
+       codec->write = uda1380_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = uda1380_set_bias_level;
+       codec->dai = uda1380_dai;
+       codec->num_dai = ARRAY_SIZE(uda1380_dai);
+       codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+       codec->reg_cache = &uda1380->reg_cache;
+       codec->reg_cache_step = 1;
+
+       memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg));
+
+       ret = uda1380_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               goto err_reset;
        }
 
-       memset(&info, 0, sizeof(struct i2c_board_info));
-       info.addr = setup->i2c_address;
-       strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
+       INIT_WORK(&uda1380->work, uda1380_flush_work);
+
+       for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++)
+               uda1380_dai[i].dev = codec->dev;
 
-       adapter = i2c_get_adapter(setup->i2c_bus);
-       if (!adapter) {
-               dev_err(&pdev->dev, "can't get i2c adapter %d\n",
-                       setup->i2c_bus);
-               goto err_driver;
+       uda1380_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err_reset;
        }
 
-       client = i2c_new_device(adapter, &info);
-       i2c_put_adapter(adapter);
-       if (!client) {
-               dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
-                       (unsigned int)info.addr);
-               goto err_driver;
+       ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_dai;
        }
 
        return 0;
 
-err_driver:
-       i2c_del_driver(&uda1380_i2c_driver);
-       return -ENODEV;
+err_dai:
+       snd_soc_unregister_codec(codec);
+err_reset:
+       gpio_set_value(pdata->gpio_power, 0);
+       gpio_free(pdata->gpio_reset);
+err_gpio:
+       gpio_free(pdata->gpio_power);
+err_out:
+       return ret;
 }
-#endif
 
-static int uda1380_probe(struct platform_device *pdev)
+static void uda1380_unregister(struct uda1380_priv *uda1380)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct uda1380_setup_data *setup;
+       struct snd_soc_codec *codec = &uda1380->codec;
+       struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+       snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+       snd_soc_unregister_codec(&uda1380->codec);
+
+       gpio_set_value(pdata->gpio_power, 0);
+       gpio_free(pdata->gpio_reset);
+       gpio_free(pdata->gpio_power);
+
+       kfree(uda1380);
+       uda1380_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct uda1380_priv *uda1380;
        struct snd_soc_codec *codec;
        int ret;
 
-       setup = socdev->codec_data;
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
+       uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL);
+       if (uda1380 == NULL)
                return -ENOMEM;
 
-       socdev->card->codec = codec;
-       mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
+       codec = &uda1380->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
 
-       uda1380_socdev = socdev;
-       ret = -ENODEV;
+       i2c_set_clientdata(i2c, uda1380);
+       codec->control_data = i2c;
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       if (setup->i2c_address) {
-               codec->hw_write = (hw_write_t)i2c_master_send;
-               ret = uda1380_add_i2c_device(pdev, setup);
-       }
-#endif
+       codec->dev = &i2c->dev;
 
+       ret = uda1380_register(uda1380);
        if (ret != 0)
-               kfree(codec);
+               kfree(uda1380);
+
        return ret;
 }
 
-/* power down chip */
-static int uda1380_remove(struct platform_device *pdev)
+static int __devexit uda1380_i2c_remove(struct i2c_client *i2c)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = socdev->card->codec;
-
-       if (codec->control_data)
-               uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       snd_soc_free_pcms(socdev);
-       snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-       i2c_unregister_device(codec->control_data);
-       i2c_del_driver(&uda1380_i2c_driver);
-#endif
-       kfree(codec);
-
+       struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c);
+       uda1380_unregister(uda1380);
        return 0;
 }
 
-struct snd_soc_codec_device soc_codec_dev_uda1380 = {
-       .probe =        uda1380_probe,
-       .remove =       uda1380_remove,
-       .suspend =      uda1380_suspend,
-       .resume =       uda1380_resume,
+static const struct i2c_device_id uda1380_i2c_id[] = {
+       { "uda1380", 0 },
+       { }
 };
-EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+
+static struct i2c_driver uda1380_i2c_driver = {
+       .driver = {
+               .name =  "UDA1380 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe =    uda1380_i2c_probe,
+       .remove =   __devexit_p(uda1380_i2c_remove),
+       .id_table = uda1380_i2c_id,
+};
+#endif
 
 static int __init uda1380_modinit(void)
 {
-       return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&uda1380_i2c_driver);
+       if (ret != 0)
+               pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
+#endif
+       return 0;
 }
 module_init(uda1380_modinit);
 
 static void __exit uda1380_exit(void)
 {
-       snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&uda1380_i2c_driver);
+#endif
 }
 module_exit(uda1380_exit);
 
index c55c17a..9cefa8a 100644 (file)
 #define R22_SKIP_DCFIL 0x0002
 #define R23_AGC_EN     0x0001
 
-struct uda1380_setup_data {
-       int            i2c_bus;
-       unsigned short i2c_address;
-       int            dac_clk;
-#define UDA1380_DAC_CLK_SYSCLK 0
-#define UDA1380_DAC_CLK_WSPLL  1
-};
-
 #define UDA1380_DAI_DUPLEX     0 /* playback and capture on single DAI */
 #define UDA1380_DAI_PLAYBACK   1 /* playback DAI */
 #define UDA1380_DAI_CAPTURE    2 /* capture DAI */
index e7348d3..4ded0e3 100644 (file)
@@ -406,7 +406,6 @@ static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
 static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
 static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
 static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
-static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
 static const char *wm8350_adcfilter[] = { "None", "High Pass" };
 static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
 static const char *wm8350_lr[] = { "Left", "Right" };
@@ -416,7 +415,6 @@ static const struct soc_enum wm8350_enum[] = {
        SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
        SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
        SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
-       SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
        SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
        SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
        SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
@@ -444,10 +442,9 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = {
                                0, 255, 0, dac_pcm_tlv),
        SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
        SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
-       SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
-       SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
-       SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
-       SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+       SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
+       SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
+       SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
        SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
                                WM8350_ADC_DIGITAL_VOLUME_L,
                                WM8350_ADC_DIGITAL_VOLUME_R,
@@ -993,6 +990,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *codec_dai)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8350 *wm8350 = codec->control_data;
        u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
            ~WM8350_AIF_WL_MASK;
 
@@ -1012,6 +1010,19 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+
+       /* The sloping stopband filter is recommended for use with
+        * lower sample rates to improve performance.
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (params_rate(params) < 24000)
+                       wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+                                       WM8350_DAC_SB_FILT);
+               else
+                       wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+                                         WM8350_DAC_SB_FILT);
+       }
+
        return 0;
 }
 
@@ -1660,6 +1671,21 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
+{
+       return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8350_codec_resume(struct platform_device *pdev)
+{
+       return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8350_codec_suspend NULL
+#define wm8350_codec_resume NULL
+#endif
+
 static struct platform_driver wm8350_codec_driver = {
        .driver = {
                   .name = "wm8350-codec",
@@ -1667,6 +1693,8 @@ static struct platform_driver wm8350_codec_driver = {
                   },
        .probe = wm8350_codec_probe,
        .remove = __devexit_p(wm8350_codec_remove),
+       .suspend = wm8350_codec_suspend,
+       .resume = wm8350_codec_resume,
 };
 
 static __init int wm8350_init(void)
index 502eefa..b9ef4d9 100644 (file)
@@ -1022,10 +1022,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
        if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out)
                return 0;
 
-       if (freq_out != 0) {
+       if (freq_out) {
                ret = fll_factors(wm8400, &factors, freq_in, freq_out);
                if (ret != 0)
                        return ret;
+       } else {
+               /* Bodge GCC 4.4.0 uninitialised variable warning - it
+                * doesn't seem capable of working out that we exit if
+                * freq_out is 0 before any of the uses. */
+               memset(&factors, 0, sizeof(factors));
        }
 
        wm8400->fll_out = freq_out;
@@ -1040,7 +1045,7 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
        reg &= ~WM8400_FLL_OSC_ENA;
        wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
 
-       if (freq_out == 0)
+       if (!freq_out)
                return 0;
 
        reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
@@ -1553,6 +1558,21 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8400_pdev_resume(struct platform_device *pdev)
+{
+       return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8400_pdev_suspend NULL
+#define wm8400_pdev_resume NULL
+#endif
+
 static struct platform_driver wm8400_codec_driver = {
        .driver = {
                .name = "wm8400-codec",
@@ -1560,6 +1580,8 @@ static struct platform_driver wm8400_codec_driver = {
        },
        .probe = wm8400_codec_probe,
        .remove = __exit_p(wm8400_codec_remove),
+       .suspend = wm8400_pdev_suspend,
+       .resume = wm8400_pdev_resume,
 };
 
 static int __init wm8400_codec_init(void)
index c8b8dba..261d4cb 100644 (file)
@@ -577,6 +577,7 @@ struct snd_soc_dai wm8510_dai = {
                .rates = WM8510_RATES,
                .formats = WM8510_FORMATS,},
        .ops = &wm8510_dai_ops,
+       .symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8510_dai);
 
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
new file mode 100644 (file)
index 0000000..3b499ae
--- /dev/null
@@ -0,0 +1,755 @@
+/*
+ * wm8523.c  --  WM8523 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@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/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8523.h"
+
+static struct snd_soc_codec *wm8523_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#define WM8523_NUM_SUPPLIES 2
+static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = {
+       "AVDD",
+       "LINEVDD",
+};
+
+#define WM8523_NUM_RATES 7
+
+/* codec private data */
+struct wm8523_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM8523_REGISTER_COUNT];
+       struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES];
+       unsigned int sysclk;
+       unsigned int rate_constraint_list[WM8523_NUM_RATES];
+       struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
+       0x8523,     /* R0 - DEVICE_ID */
+       0x0001,     /* R1 - REVISION */
+       0x0000,     /* R2 - PSCTRL1 */
+       0x1812,     /* R3 - AIF_CTRL1 */
+       0x0000,     /* R4 - AIF_CTRL2 */
+       0x0001,     /* R5 - DAC_CTRL3 */
+       0x0190,     /* R6 - DAC_GAINL */
+       0x0190,     /* R7 - DAC_GAINR */
+       0x0000,     /* R8 - ZERO_DETECT */
+};
+
+static int wm8523_volatile(unsigned int reg)
+{
+       switch (reg) {
+       case WM8523_DEVICE_ID:
+       case WM8523_REVISION:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int wm8523_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       struct wm8523_priv *wm8523 = codec->private_data;
+       u8 data[3];
+
+       BUG_ON(reg > WM8523_MAX_REGISTER);
+
+       data[0] = reg;
+       data[1] = (value >> 8) & 0x00ff;
+       data[2] = value & 0x00ff;
+
+       if (!wm8523_volatile(reg))
+               wm8523->reg_cache[reg] = value;
+       if (codec->hw_write(codec->control_data, data, 3) == 3)
+               return 0;
+       else
+               return -EIO;
+}
+
+static int wm8523_reset(struct snd_soc_codec *codec)
+{
+       return wm8523_write(codec, WM8523_DEVICE_ID, 0);
+}
+
+static unsigned int wm8523_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+       struct i2c_msg xfer[2];
+       u16 data;
+       int ret;
+       struct i2c_client *i2c = codec->control_data;
+
+       /* Write register */
+       xfer[0].addr = i2c->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = i2c->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
+               return 0;
+       }
+
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+
+
+static unsigned int wm8523_read(struct snd_soc_codec *codec,
+                               unsigned int reg)
+{
+       u16 *reg_cache = codec->reg_cache;
+
+       BUG_ON(reg > WM8523_MAX_REGISTER);
+
+       if (wm8523_volatile(reg))
+               return wm8523_read_hw(codec, reg);
+       else
+               return reg_cache[reg];
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0);
+
+static const char *wm8523_zd_count_text[] = {
+       "1024",
+       "2048",
+};
+
+static const struct soc_enum wm8523_zc_count =
+       SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
+
+static const struct snd_kcontrol_new wm8523_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
+                0, 448, 0, dac_tlv),
+SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
+SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0),
+SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1),
+SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0),
+SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0),
+SOC_ENUM("Zero Detect Count", wm8523_zc_count),
+};
+
+static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       { "LINEVOUTL", NULL, "DAC" },
+       { "LINEVOUTR", NULL, "DAC" },
+};
+
+static int wm8523_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, wm8523_dapm_widgets,
+                                 ARRAY_SIZE(wm8523_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static struct {
+       int value;
+       int ratio;
+} lrclk_ratios[WM8523_NUM_RATES] = {
+       { 1, 128 },
+       { 2, 192 },
+       { 3, 256 },
+       { 4, 384 },
+       { 5, 512 },
+       { 6, 768 },
+       { 7, 1152 },
+};
+
+static int wm8523_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8523_priv *wm8523 = codec->private_data;
+
+       /* The set of sample rates that can be supported depends on the
+        * MCLK supplied to the CODEC - enforce this.
+        */
+       if (!wm8523->sysclk) {
+               dev_err(codec->dev,
+                       "No MCLK configured, call set_sysclk() on init\n");
+               return -EINVAL;
+       }
+
+       return 0;
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_RATE,
+                                  &wm8523->rate_constraint);
+
+       return 0;
+}
+
+static int wm8523_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8523_priv *wm8523 = codec->private_data;
+       int i;
+       u16 aifctrl1 = wm8523_read(codec, WM8523_AIF_CTRL1);
+       u16 aifctrl2 = wm8523_read(codec, WM8523_AIF_CTRL2);
+
+       /* Find a supported LRCLK ratio */
+       for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+               if (wm8523->sysclk / params_rate(params) ==
+                   lrclk_ratios[i].ratio)
+                       break;
+       }
+
+       /* Should never happen, should be handled by constraints */
+       if (i == ARRAY_SIZE(lrclk_ratios)) {
+               dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+                       wm8523->sysclk / params_rate(params));
+               return -EINVAL;
+       }
+
+       aifctrl2 &= ~WM8523_SR_MASK;
+       aifctrl2 |= lrclk_ratios[i].value;
+
+       aifctrl1 &= ~WM8523_WL_MASK;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               aifctrl1 |= 0x8;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               aifctrl1 |= 0x10;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               aifctrl1 |= 0x18;
+               break;
+       }
+
+       wm8523_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+       wm8523_write(codec, WM8523_AIF_CTRL2, aifctrl2);
+
+       return 0;
+}
+
+static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8523_priv *wm8523 = codec->private_data;
+       unsigned int val;
+       int i;
+
+       wm8523->sysclk = freq;
+
+       wm8523->rate_constraint.count = 0;
+       for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+               val = freq / lrclk_ratios[i].ratio;
+               /* Check that it's a standard rate since core can't
+                * cope with others and having the odd rates confuses
+                * constraint matching.
+                */
+               switch (val) {
+               case 8000:
+               case 11025:
+               case 16000:
+               case 22050:
+               case 32000:
+               case 44100:
+               case 48000:
+               case 64000:
+               case 88200:
+               case 96000:
+               case 176400:
+               case 192000:
+                       dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+                               val);
+                       wm8523->rate_constraint_list[i] = val;
+                       wm8523->rate_constraint.count++;
+                       break;
+               default:
+                       dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
+                               val);
+               }
+       }
+
+       /* Need at least one supported rate... */
+       if (wm8523->rate_constraint.count == 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 aifctrl1 = wm8523_read(codec, WM8523_AIF_CTRL1);
+
+       aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK |
+                     WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aifctrl1 |= WM8523_AIF_MSTR;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               aifctrl1 |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aifctrl1 |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               aifctrl1 |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               aifctrl1 |= 0x0023;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               aifctrl1 |= WM8523_BCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               aifctrl1 |= WM8523_LRCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8523_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+
+       return 0;
+}
+
+static int wm8523_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8523_priv *wm8523 = codec->private_data;
+       int ret, i;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* Full power on */
+               snd_soc_update_bits(codec, WM8523_PSCTRL1,
+                                   WM8523_SYS_ENA_MASK, 3);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+                                                   wm8523->supplies);
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "Failed to enable supplies: %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       /* Initial power up */
+                       snd_soc_update_bits(codec, WM8523_PSCTRL1,
+                                           WM8523_SYS_ENA_MASK, 1);
+
+                       /* Sync back default/cached values */
+                       for (i = WM8523_AIF_CTRL1;
+                            i < WM8523_MAX_REGISTER; i++)
+                               wm8523_write(codec, i, wm8523->reg_cache[i]);
+
+
+                       msleep(100);
+               }
+
+               /* Power up to mute */
+               snd_soc_update_bits(codec, WM8523_PSCTRL1,
+                                   WM8523_SYS_ENA_MASK, 2);
+
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* The chip runs through the power down sequence for us. */
+               snd_soc_update_bits(codec, WM8523_PSCTRL1,
+                                   WM8523_SYS_ENA_MASK, 0);
+               msleep(100);
+
+               regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies),
+                                      wm8523->supplies);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8523_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8523_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_ops wm8523_dai_ops = {
+       .startup        = wm8523_startup,
+       .hw_params      = wm8523_hw_params,
+       .set_sysclk     = wm8523_set_dai_sysclk,
+       .set_fmt        = wm8523_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8523_dai = {
+       .name = "WM8523",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,  /* Mono modes not yet supported */
+               .channels_max = 2,
+               .rates = WM8523_RATES,
+               .formats = WM8523_FORMATS,
+       },
+       .ops = &wm8523_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8523_dai);
+
+#ifdef CONFIG_PM
+static int wm8523_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8523_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define wm8523_suspend NULL
+#define wm8523_resume NULL
+#endif
+
+static int wm8523_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8523_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8523_codec;
+       codec = wm8523_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm8523_snd_controls,
+                            ARRAY_SIZE(wm8523_snd_controls));
+       wm8523_add_widgets(codec);
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int wm8523_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8523 = {
+       .probe =        wm8523_probe,
+       .remove =       wm8523_remove,
+       .suspend =      wm8523_suspend,
+       .resume =       wm8523_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8523);
+
+static int wm8523_register(struct wm8523_priv *wm8523)
+{
+       int ret;
+       struct snd_soc_codec *codec = &wm8523->codec;
+       int i;
+
+       if (wm8523_codec) {
+               dev_err(codec->dev, "Another WM8523 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8523;
+       codec->name = "WM8523";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8523_read;
+       codec->write = wm8523_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8523_set_bias_level;
+       codec->dai = &wm8523_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8523_REGISTER_COUNT;
+       codec->reg_cache = &wm8523->reg_cache;
+
+       wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
+       wm8523->rate_constraint.count =
+               ARRAY_SIZE(wm8523->rate_constraint_list);
+
+       memcpy(codec->reg_cache, wm8523_reg, sizeof(wm8523_reg));
+
+       for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
+               wm8523->supplies[i].supply = wm8523_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies),
+                                wm8523->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+                                   wm8523->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_get;
+       }
+
+       ret = wm8523_read(codec, WM8523_DEVICE_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read ID register\n");
+               goto err_enable;
+       }
+       if (ret != wm8523_reg[WM8523_DEVICE_ID]) {
+               dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret);
+               ret = -EINVAL;
+               goto err_enable;
+       }
+
+       ret = wm8523_read(codec, WM8523_REVISION);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read revision register\n");
+               goto err_enable;
+       }
+       dev_info(codec->dev, "revision %c\n",
+                (ret & WM8523_CHIP_REV_MASK) + 'A');
+
+       ret = wm8523_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               goto err_enable;
+       }
+
+       wm8523_dai.dev = codec->dev;
+
+       /* Change some default settings - latch VU and enable ZC */
+       wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
+       wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+
+       wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Bias level configuration will have done an extra enable */
+       regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+
+       wm8523_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm8523_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+
+err_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err:
+       kfree(wm8523);
+       return ret;
+}
+
+static void wm8523_unregister(struct wm8523_priv *wm8523)
+{
+       wm8523_set_bias_level(&wm8523->codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+       snd_soc_unregister_dai(&wm8523_dai);
+       snd_soc_unregister_codec(&wm8523->codec);
+       kfree(wm8523);
+       wm8523_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8523_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8523_priv *wm8523;
+       struct snd_soc_codec *codec;
+
+       wm8523 = kzalloc(sizeof(struct wm8523_priv), GFP_KERNEL);
+       if (wm8523 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8523->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8523);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8523_register(wm8523);
+}
+
+static __devexit int wm8523_i2c_remove(struct i2c_client *client)
+{
+       struct wm8523_priv *wm8523 = i2c_get_clientdata(client);
+       wm8523_unregister(wm8523);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8523_i2c_resume(struct i2c_client *i2c)
+{
+       return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8523_i2c_suspend NULL
+#define wm8523_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8523_i2c_id[] = {
+       { "wm8523", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
+
+static struct i2c_driver wm8523_i2c_driver = {
+       .driver = {
+               .name = "WM8523",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8523_i2c_probe,
+       .remove =   __devexit_p(wm8523_i2c_remove),
+       .suspend =  wm8523_i2c_suspend,
+       .resume =   wm8523_i2c_resume,
+       .id_table = wm8523_i2c_id,
+};
+#endif
+
+static int __init wm8523_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8523_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
+                      ret);
+       }
+#endif
+       return 0;
+}
+module_init(wm8523_modinit);
+
+static void __exit wm8523_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8523_i2c_driver);
+#endif
+}
+module_exit(wm8523_exit);
+
+MODULE_DESCRIPTION("ASoC WM8523 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h
new file mode 100644 (file)
index 0000000..1aa9ce3
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * wm8523.h  --  WM8423 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Based on wm8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8523_H
+#define _WM8523_H
+
+/*
+ * Register values.
+ */
+#define WM8523_DEVICE_ID                        0x00
+#define WM8523_REVISION                         0x01
+#define WM8523_PSCTRL1                          0x02
+#define WM8523_AIF_CTRL1                        0x03
+#define WM8523_AIF_CTRL2                        0x04
+#define WM8523_DAC_CTRL3                        0x05
+#define WM8523_DAC_GAINL                        0x06
+#define WM8523_DAC_GAINR                        0x07
+#define WM8523_ZERO_DETECT                      0x08
+
+#define WM8523_REGISTER_COUNT                   9
+#define WM8523_MAX_REGISTER                     0x08
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DEVICE_ID
+ */
+#define WM8523_CHIP_ID_MASK                     0xFFFF  /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_SHIFT                         0  /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_WIDTH                        16  /* CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - REVISION
+ */
+#define WM8523_CHIP_REV_MASK                    0x0007  /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_SHIFT                        0  /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_WIDTH                        3  /* CHIP_REV - [2:0] */
+
+/*
+ * R2 (0x02) - PSCTRL1
+ */
+#define WM8523_SYS_ENA_MASK                     0x0003  /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_SHIFT                         0  /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_WIDTH                         2  /* SYS_ENA - [1:0] */
+
+/*
+ * R3 (0x03) - AIF_CTRL1
+ */
+#define WM8523_TDM_MODE_MASK                    0x1800  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_SHIFT                       11  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_WIDTH                        2  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_SLOT_MASK                    0x0600  /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_SHIFT                        9  /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_WIDTH                        2  /* TDM_SLOT - [10:9] */
+#define WM8523_DEEMPH                           0x0100  /* DEEMPH  */
+#define WM8523_DEEMPH_MASK                      0x0100  /* DEEMPH  */
+#define WM8523_DEEMPH_SHIFT                          8  /* DEEMPH  */
+#define WM8523_DEEMPH_WIDTH                          1  /* DEEMPH  */
+#define WM8523_AIF_MSTR                         0x0080  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_MASK                    0x0080  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_SHIFT                        7  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_WIDTH                        1  /* AIF_MSTR  */
+#define WM8523_LRCLK_INV                        0x0040  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_MASK                   0x0040  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_SHIFT                       6  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_WIDTH                       1  /* LRCLK_INV  */
+#define WM8523_BCLK_INV                         0x0020  /* BCLK_INV  */
+#define WM8523_BCLK_INV_MASK                    0x0020  /* BCLK_INV  */
+#define WM8523_BCLK_INV_SHIFT                        5  /* BCLK_INV  */
+#define WM8523_BCLK_INV_WIDTH                        1  /* BCLK_INV  */
+#define WM8523_WL_MASK                          0x0018  /* WL - [4:3] */
+#define WM8523_WL_SHIFT                              3  /* WL - [4:3] */
+#define WM8523_WL_WIDTH                              2  /* WL - [4:3] */
+#define WM8523_FMT_MASK                         0x0007  /* FMT - [2:0] */
+#define WM8523_FMT_SHIFT                             0  /* FMT - [2:0] */
+#define WM8523_FMT_WIDTH                             3  /* FMT - [2:0] */
+
+/*
+ * R4 (0x04) - AIF_CTRL2
+ */
+#define WM8523_DAC_OP_MUX_MASK                  0x00C0  /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_SHIFT                      6  /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_WIDTH                      2  /* DAC_OP_MUX - [7:6] */
+#define WM8523_BCLKDIV_MASK                     0x0038  /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_SHIFT                         3  /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_WIDTH                         3  /* BCLKDIV - [5:3] */
+#define WM8523_SR_MASK                          0x0007  /* SR - [2:0] */
+#define WM8523_SR_SHIFT                              0  /* SR - [2:0] */
+#define WM8523_SR_WIDTH                              3  /* SR - [2:0] */
+
+/*
+ * R5 (0x05) - DAC_CTRL3
+ */
+#define WM8523_ZC                               0x0010  /* ZC  */
+#define WM8523_ZC_MASK                          0x0010  /* ZC  */
+#define WM8523_ZC_SHIFT                              4  /* ZC  */
+#define WM8523_ZC_WIDTH                              1  /* ZC  */
+#define WM8523_DACR                             0x0008  /* DACR  */
+#define WM8523_DACR_MASK                        0x0008  /* DACR  */
+#define WM8523_DACR_SHIFT                            3  /* DACR  */
+#define WM8523_DACR_WIDTH                            1  /* DACR  */
+#define WM8523_DACL                             0x0004  /* DACL  */
+#define WM8523_DACL_MASK                        0x0004  /* DACL  */
+#define WM8523_DACL_SHIFT                            2  /* DACL  */
+#define WM8523_DACL_WIDTH                            1  /* DACL  */
+#define WM8523_VOL_UP_RAMP                      0x0002  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_MASK                 0x0002  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_SHIFT                     1  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_WIDTH                     1  /* VOL_UP_RAMP  */
+#define WM8523_VOL_DOWN_RAMP                    0x0001  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_MASK               0x0001  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_SHIFT                   0  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_WIDTH                   1  /* VOL_DOWN_RAMP  */
+
+/*
+ * R6 (0x06) - DAC_GAINL
+ */
+#define WM8523_DACL_VU                          0x0200  /* DACL_VU  */
+#define WM8523_DACL_VU_MASK                     0x0200  /* DACL_VU  */
+#define WM8523_DACL_VU_SHIFT                         9  /* DACL_VU  */
+#define WM8523_DACL_VU_WIDTH                         1  /* DACL_VU  */
+#define WM8523_DACL_VOL_MASK                    0x01FF  /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_SHIFT                        0  /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_WIDTH                        9  /* DACL_VOL - [8:0] */
+
+/*
+ * R7 (0x07) - DAC_GAINR
+ */
+#define WM8523_DACR_VU                          0x0200  /* DACR_VU  */
+#define WM8523_DACR_VU_MASK                     0x0200  /* DACR_VU  */
+#define WM8523_DACR_VU_SHIFT                         9  /* DACR_VU  */
+#define WM8523_DACR_VU_WIDTH                         1  /* DACR_VU  */
+#define WM8523_DACR_VOL_MASK                    0x01FF  /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_SHIFT                        0  /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_WIDTH                        9  /* DACR_VOL - [8:0] */
+
+/*
+ * R8 (0x08) - ZERO_DETECT
+ */
+#define WM8523_ZD_COUNT_MASK                    0x0003  /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_SHIFT                        0  /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_WIDTH                        2  /* ZD_COUNT - [1:0] */
+
+extern struct snd_soc_dai wm8523_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#endif
index 86c4b24..97b9ed9 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -187,15 +189,22 @@ struct pll_state {
        unsigned int out;
 };
 
+#define WM8580_NUM_SUPPLIES 3
+static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
+       "AVDD",
+       "DVDD",
+       "PVDD",
+};
+
 /* codec private data */
 struct wm8580_priv {
        struct snd_soc_codec codec;
+       struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
        u16 reg_cache[WM8580_MAX_REGISTER + 1];
        struct pll_state a;
        struct pll_state b;
 };
 
-
 /*
  * read wm8580 register cache
  */
@@ -922,11 +931,28 @@ static int wm8580_register(struct wm8580_priv *wm8580)
 
        memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
 
+       for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+               wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
+                                wm8580->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
+                                   wm8580->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_regulator_get;
+       }
+
        /* Get the codec into a known state */
        ret = wm8580_write(codec, WM8580_RESET, 0);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
@@ -939,7 +965,7 @@ static int wm8580_register(struct wm8580_priv *wm8580)
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
@@ -952,6 +978,10 @@ static int wm8580_register(struct wm8580_priv *wm8580)
 
 err_codec:
        snd_soc_unregister_codec(codec);
+err_regulator_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+err_regulator_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 err:
        kfree(wm8580);
        return ret;
@@ -962,6 +992,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580)
        wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
        snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
        snd_soc_unregister_codec(&wm8580->codec);
+       regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+       regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
        kfree(wm8580);
        wm8580_codec = NULL;
 }
@@ -995,6 +1027,21 @@ static int wm8580_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8580_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8580_i2c_suspend NULL
+#define wm8580_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8580_i2c_id[] = {
        { "wm8580", 0 },
        { }
@@ -1008,6 +1055,8 @@ static struct i2c_driver wm8580_i2c_driver = {
        },
        .probe =    wm8580_i2c_probe,
        .remove =   wm8580_i2c_remove,
+       .suspend =  wm8580_i2c_suspend,
+       .resume =   wm8580_i2c_resume,
        .id_table = wm8580_i2c_id,
 };
 #endif
index 7a20587..1560028 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
+#include <sound/tlv.h>
 
 #include "wm8731.h"
 
@@ -113,20 +114,26 @@ static const struct soc_enum wm8731_enum[] = {
        SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
 };
 
+static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
 static const struct snd_kcontrol_new wm8731_snd_controls[] = {
 
-SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
-       0, 127, 0),
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+                0, 127, 0, out_tlv),
 SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
        7, 1, 0),
 
-SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
+                in_tlv),
 SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
 
 SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
-SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
 
-SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
+              sidetone_tlv),
 
 SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
 SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
@@ -457,9 +464,11 @@ struct snd_soc_dai wm8731_dai = {
                .rates = WM8731_RATES,
                .formats = WM8731_FORMATS,},
        .ops = &wm8731_dai_ops,
+       .symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8731_dai);
 
+#ifdef CONFIG_PM
 static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -488,6 +497,10 @@ static int wm8731_resume(struct platform_device *pdev)
        wm8731_set_bias_level(codec, codec->suspend_bias_level);
        return 0;
 }
+#else
+#define wm8731_suspend NULL
+#define wm8731_resume NULL
+#endif
 
 static int wm8731_probe(struct platform_device *pdev)
 {
@@ -555,7 +568,8 @@ static int wm8731_register(struct wm8731_priv *wm8731)
 
        if (wm8731_codec) {
                dev_err(codec->dev, "Another WM8731 is registered\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        mutex_init(&codec->mutex);
@@ -578,8 +592,8 @@ static int wm8731_register(struct wm8731_priv *wm8731)
 
        ret = wm8731_reset(codec);
        if (ret < 0) {
-               dev_err(codec->dev, "Failed to issue reset\n");
-               return ret;
+               dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+               goto err;
        }
 
        wm8731_dai.dev = codec->dev;
@@ -605,17 +619,23 @@ static int wm8731_register(struct wm8731_priv *wm8731)
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               return ret;
+               goto err;
        }
 
        ret = snd_soc_register_dai(&wm8731_dai);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
                snd_soc_unregister_codec(codec);
-               return ret;
+               goto err_codec;
        }
 
        return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(wm8731);
+       return ret;
 }
 
 static void wm8731_unregister(struct wm8731_priv *wm8731)
@@ -680,6 +700,21 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8731_spi_resume(struct spi_device *spi)
+{
+       return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8731_spi_suspend NULL
+#define wm8731_spi_resume NULL
+#endif
+
 static struct spi_driver wm8731_spi_driver = {
        .driver = {
                .name   = "wm8731",
@@ -687,6 +722,8 @@ static struct spi_driver wm8731_spi_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = wm8731_spi_probe,
+       .suspend        = wm8731_spi_suspend,
+       .resume         = wm8731_spi_resume,
        .remove         = __devexit_p(wm8731_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -720,6 +757,21 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8731_i2c_resume(struct i2c_client *i2c)
+{
+       return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8731_i2c_suspend NULL
+#define wm8731_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8731_i2c_id[] = {
        { "wm8731", 0 },
        { }
@@ -733,6 +785,8 @@ static struct i2c_driver wm8731_i2c_driver = {
        },
        .probe =    wm8731_i2c_probe,
        .remove =   __devexit_p(wm8731_i2c_remove),
+       .suspend =  wm8731_i2c_suspend,
+       .resume =   wm8731_i2c_resume,
        .id_table = wm8731_i2c_id,
 };
 #endif
index 49c4b28..d80d414 100644 (file)
@@ -1766,6 +1766,21 @@ static int wm8753_i2c_remove(struct i2c_client *client)
         return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8753_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8753_i2c_suspend NULL
+#define wm8753_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8753_i2c_id[] = {
        { "wm8753", 0 },
        { }
@@ -1779,6 +1794,8 @@ static struct i2c_driver wm8753_i2c_driver = {
        },
        .probe =    wm8753_i2c_probe,
        .remove =   wm8753_i2c_remove,
+       .suspend =  wm8753_i2c_suspend,
+       .resume =   wm8753_i2c_resume,
        .id_table = wm8753_i2c_id,
 };
 #endif
@@ -1834,6 +1851,22 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8753_spi_resume(struct spi_device *spi)
+{
+       return snd_soc_resume_device(&spi->dev);
+}
+
+#else
+#define wm8753_spi_suspend NULL
+#define wm8753_spi_resume NULL
+#endif
+
 static struct spi_driver wm8753_spi_driver = {
        .driver = {
                .name   = "wm8753",
@@ -1842,6 +1875,8 @@ static struct spi_driver wm8753_spi_driver = {
        },
        .probe          = wm8753_spi_probe,
        .remove         = __devexit_p(wm8753_spi_remove),
+       .suspend        = wm8753_spi_suspend,
+       .resume         = wm8753_spi_resume,
 };
 #endif
 
index 3c78945..ac30899 100644 (file)
 #define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
 
 #define WM8900_REG_DACCTRL_MUTE          0x004
+#define WM8900_REG_DACCTRL_DAC_SB_FILT   0x100
 #define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
 
 #define WM8900_REG_AUDIO3_ADCLRC_DIR    0x0800
@@ -439,7 +440,6 @@ SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
 SOC_ENUM("DAC Mute Rate", dac_mute_rate),
 SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
 SOC_ENUM("DAC Deemphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
 SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
           12, 1, 0),
 
@@ -743,6 +743,17 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
 
        wm8900_write(codec, WM8900_REG_AUDIO1, reg);
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+
+               if (params_rate(params) <= 24000)
+                       reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
+               else
+                       reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
+
+               wm8900_write(codec, WM8900_REG_DACCTRL, reg);
+       }
+
        return 0;
 }
 
@@ -1388,6 +1399,21 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8900_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8900_i2c_suspend NULL
+#define wm8900_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8900_i2c_id[] = {
        { "wm8900", 0 },
        { }
@@ -1401,6 +1427,8 @@ static struct i2c_driver wm8900_i2c_driver = {
        },
        .probe = wm8900_i2c_probe,
        .remove = __devexit_p(wm8900_i2c_remove),
+       .suspend = wm8900_i2c_suspend,
+       .resume = wm8900_i2c_resume,
        .id_table = wm8900_i2c_id,
 };
 
index e8d2e3e..c9baeae 100644 (file)
@@ -715,8 +715,6 @@ SOC_ENUM("DAC Soft Mute Rate", soft_mute),
 SOC_ENUM("DAC Mute Mode", mute_mode),
 SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
 SOC_ENUM("DAC De-emphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch",
-          WM8903_DAC_DIGITAL_1, 11, 1, 0),
 SOC_ENUM("DAC Companding Mode", dac_companding),
 SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
 
@@ -1373,12 +1371,19 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
        u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
        u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
        u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
+       u16 dac_digital1 = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
 
        if (substream == wm8903->slave_substream) {
                dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
                return 0;
        }
 
+       /* Enable sloping stopband filter for low sample rates */
+       if (fs <= 24000)
+               dac_digital1 |= WM8903_DAC_SB_FILT;
+       else
+               dac_digital1 &= ~WM8903_DAC_SB_FILT;
+
        /* Configure sample rate logic for DSP - choose nearest rate */
        dsp_config = 0;
        best_val = abs(sample_rates[dsp_config].rate - fs);
@@ -1503,6 +1508,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
        wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
        wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
        wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+       wm8903_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1);
 
        return 0;
 }
@@ -1721,6 +1727,21 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8903_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8903_i2c_suspend NULL
+#define wm8903_i2c_resume NULL
+#endif
+
 /* i2c codec control layer */
 static const struct i2c_device_id wm8903_i2c_id[] = {
        { "wm8903", 0 },
@@ -1735,6 +1756,8 @@ static struct i2c_driver wm8903_i2c_driver = {
        },
        .probe    = wm8903_i2c_probe,
        .remove   = __devexit_p(wm8903_i2c_remove),
+       .suspend  = wm8903_i2c_suspend,
+       .resume   = wm8903_i2c_resume,
        .id_table = wm8903_i2c_id,
 };
 
index b8e17d6..b69210a 100644 (file)
@@ -916,6 +916,21 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8940_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8940_i2c_suspend NULL
+#define wm8940_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8940_i2c_id[] = {
        { "wm8940", 0 },
        { }
@@ -929,6 +944,8 @@ static struct i2c_driver wm8940_i2c_driver = {
        },
        .probe = wm8940_i2c_probe,
        .remove = __devexit_p(wm8940_i2c_remove),
+       .suspend = wm8940_i2c_suspend,
+       .resume = wm8940_i2c_resume,
        .id_table = wm8940_i2c_id,
 };
 
index e224d8a..d1769e6 100644 (file)
@@ -810,7 +810,8 @@ static int wm8960_register(struct wm8960_priv *wm8960)
 
        if (wm8960_codec) {
                dev_err(codec->dev, "Another WM8960 is registered\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        if (!pdata) {
@@ -843,7 +844,7 @@ static int wm8960_register(struct wm8960_priv *wm8960)
        ret = wm8960_reset(codec);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to issue reset\n");
-               return ret;
+               goto err;
        }
 
        wm8960_dai.dev = codec->dev;
@@ -877,17 +878,22 @@ static int wm8960_register(struct wm8960_priv *wm8960)
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               return ret;
+               goto err;
        }
 
        ret = snd_soc_register_dai(&wm8960_dai);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-               snd_soc_unregister_codec(codec);
-               return ret;
+               goto err_codec;
        }
 
        return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(wm8960);
+       return ret;
 }
 
 static void wm8960_unregister(struct wm8960_priv *wm8960)
@@ -927,6 +933,21 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8960_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8960_i2c_suspend NULL
+#define wm8960_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8960_i2c_id[] = {
        { "wm8960", 0 },
        { }
@@ -940,6 +961,8 @@ static struct i2c_driver wm8960_i2c_driver = {
        },
        .probe =    wm8960_i2c_probe,
        .remove =   __devexit_p(wm8960_i2c_remove),
+       .suspend =  wm8960_i2c_suspend,
+       .resume =   wm8960_i2c_resume,
        .id_table = wm8960_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
new file mode 100644 (file)
index 0000000..bd1af92
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * wm8961.c  --  WM8961 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * 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.
+ *
+ * Currently unimplemented features:
+ *  - ALC
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8961.h"
+
+#define WM8961_MAX_REGISTER                     0xFC
+
+static u16 wm8961_reg_defaults[] = {
+       0x009F,     /* R0   - Left Input volume */
+       0x009F,     /* R1   - Right Input volume */
+       0x0000,     /* R2   - LOUT1 volume */
+       0x0000,     /* R3   - ROUT1 volume */
+       0x0020,     /* R4   - Clocking1 */
+       0x0008,     /* R5   - ADC & DAC Control 1 */
+       0x0000,     /* R6   - ADC & DAC Control 2 */
+       0x000A,     /* R7   - Audio Interface 0 */
+       0x01F4,     /* R8   - Clocking2 */
+       0x0000,     /* R9   - Audio Interface 1 */
+       0x00FF,     /* R10  - Left DAC volume */
+       0x00FF,     /* R11  - Right DAC volume */
+       0x0000,     /* R12 */
+       0x0000,     /* R13 */
+       0x0040,     /* R14  - Audio Interface 2 */
+       0x0000,     /* R15  - Software Reset */
+       0x0000,     /* R16 */
+       0x007B,     /* R17  - ALC1 */
+       0x0000,     /* R18  - ALC2 */
+       0x0032,     /* R19  - ALC3 */
+       0x0000,     /* R20  - Noise Gate */
+       0x00C0,     /* R21  - Left ADC volume */
+       0x00C0,     /* R22  - Right ADC volume */
+       0x0120,     /* R23  - Additional control(1) */
+       0x0000,     /* R24  - Additional control(2) */
+       0x0000,     /* R25  - Pwr Mgmt (1) */
+       0x0000,     /* R26  - Pwr Mgmt (2) */
+       0x0000,     /* R27  - Additional Control (3) */
+       0x0000,     /* R28  - Anti-pop */
+       0x0000,     /* R29 */
+       0x005F,     /* R30  - Clocking 3 */
+       0x0000,     /* R31 */
+       0x0000,     /* R32  - ADCL signal path */
+       0x0000,     /* R33  - ADCR signal path */
+       0x0000,     /* R34 */
+       0x0000,     /* R35 */
+       0x0000,     /* R36 */
+       0x0000,     /* R37 */
+       0x0000,     /* R38 */
+       0x0000,     /* R39 */
+       0x0000,     /* R40  - LOUT2 volume */
+       0x0000,     /* R41  - ROUT2 volume */
+       0x0000,     /* R42 */
+       0x0000,     /* R43 */
+       0x0000,     /* R44 */
+       0x0000,     /* R45 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47  - Pwr Mgmt (3) */
+       0x0023,     /* R48  - Additional Control (4) */
+       0x0000,     /* R49  - Class D Control 1 */
+       0x0000,     /* R50 */
+       0x0003,     /* R51  - Class D Control 2 */
+       0x0000,     /* R52 */
+       0x0000,     /* R53 */
+       0x0000,     /* R54 */
+       0x0000,     /* R55 */
+       0x0106,     /* R56  - Clocking 4 */
+       0x0000,     /* R57  - DSP Sidetone 0 */
+       0x0000,     /* R58  - DSP Sidetone 1 */
+       0x0000,     /* R59 */
+       0x0000,     /* R60  - DC Servo 0 */
+       0x0000,     /* R61  - DC Servo 1 */
+       0x0000,     /* R62 */
+       0x015E,     /* R63  - DC Servo 3 */
+       0x0010,     /* R64 */
+       0x0010,     /* R65  - DC Servo 5 */
+       0x0000,     /* R66 */
+       0x0001,     /* R67 */
+       0x0003,     /* R68  - Analogue PGA Bias */
+       0x0000,     /* R69  - Analogue HP 0 */
+       0x0060,     /* R70 */
+       0x01FB,     /* R71  - Analogue HP 2 */
+       0x0000,     /* R72  - Charge Pump 1 */
+       0x0065,     /* R73 */
+       0x005F,     /* R74 */
+       0x0059,     /* R75 */
+       0x006B,     /* R76 */
+       0x0038,     /* R77 */
+       0x000C,     /* R78 */
+       0x000A,     /* R79 */
+       0x006B,     /* R80 */
+       0x0000,     /* R81 */
+       0x0000,     /* R82  - Charge Pump B */
+       0x0087,     /* R83 */
+       0x0000,     /* R84 */
+       0x005C,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87  - Write Sequencer 1 */
+       0x0000,     /* R88  - Write Sequencer 2 */
+       0x0000,     /* R89  - Write Sequencer 3 */
+       0x0000,     /* R90  - Write Sequencer 4 */
+       0x0000,     /* R91  - Write Sequencer 5 */
+       0x0000,     /* R92  - Write Sequencer 6 */
+       0x0000,     /* R93  - Write Sequencer 7 */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96 */
+       0x0000,     /* R97 */
+       0x0000,     /* R98 */
+       0x0000,     /* R99 */
+       0x0000,     /* R100 */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x0000,     /* R104 */
+       0x0000,     /* R105 */
+       0x0000,     /* R106 */
+       0x0000,     /* R107 */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 */
+       0x0000,     /* R112 */
+       0x0000,     /* R113 */
+       0x0000,     /* R114 */
+       0x0000,     /* R115 */
+       0x0000,     /* R116 */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x0000,     /* R128 */
+       0x0000,     /* R129 */
+       0x0000,     /* R130 */
+       0x0000,     /* R131 */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 */
+       0x0000,     /* R134 */
+       0x0000,     /* R135 */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0000,     /* R140 */
+       0x0000,     /* R141 */
+       0x0000,     /* R142 */
+       0x0000,     /* R143 */
+       0x0000,     /* R144 */
+       0x0000,     /* R145 */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x0000,     /* R152 */
+       0x0000,     /* R153 */
+       0x0000,     /* R154 */
+       0x0000,     /* R155 */
+       0x0000,     /* R156 */
+       0x0000,     /* R157 */
+       0x0000,     /* R158 */
+       0x0000,     /* R159 */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 */
+       0x0000,     /* R164 */
+       0x0000,     /* R165 */
+       0x0000,     /* R166 */
+       0x0000,     /* R167 */
+       0x0000,     /* R168 */
+       0x0000,     /* R169 */
+       0x0000,     /* R170 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 */
+       0x0000,     /* R173 */
+       0x0000,     /* R174 */
+       0x0000,     /* R175 */
+       0x0000,     /* R176 */
+       0x0000,     /* R177 */
+       0x0000,     /* R178 */
+       0x0000,     /* R179 */
+       0x0000,     /* R180 */
+       0x0000,     /* R181 */
+       0x0000,     /* R182 */
+       0x0000,     /* R183 */
+       0x0000,     /* R184 */
+       0x0000,     /* R185 */
+       0x0000,     /* R186 */
+       0x0000,     /* R187 */
+       0x0000,     /* R188 */
+       0x0000,     /* R189 */
+       0x0000,     /* R190 */
+       0x0000,     /* R191 */
+       0x0000,     /* R192 */
+       0x0000,     /* R193 */
+       0x0000,     /* R194 */
+       0x0000,     /* R195 */
+       0x0030,     /* R196 */
+       0x0006,     /* R197 */
+       0x0000,     /* R198 */
+       0x0060,     /* R199 */
+       0x0000,     /* R200 */
+       0x003F,     /* R201 */
+       0x0000,     /* R202 */
+       0x0000,     /* R203 */
+       0x0000,     /* R204 */
+       0x0001,     /* R205 */
+       0x0000,     /* R206 */
+       0x0181,     /* R207 */
+       0x0005,     /* R208 */
+       0x0008,     /* R209 */
+       0x0008,     /* R210 */
+       0x0000,     /* R211 */
+       0x013B,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 */
+       0x0000,     /* R216 */
+       0x0070,     /* R217 */
+       0x0000,     /* R218 */
+       0x0000,     /* R219 */
+       0x0000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0003,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 */
+       0x0001,     /* R226 */
+       0x0008,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0x0000,     /* R230 */
+       0x0000,     /* R231 */
+       0x0004,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0080,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0052,     /* R245 */
+       0x0110,     /* R246 */
+       0x0040,     /* R247 */
+       0x0000,     /* R248 */
+       0x0030,     /* R249 */
+       0x0000,     /* R250 */
+       0x0000,     /* R251 */
+       0x0001,     /* R252 - General test 1 */
+};
+
+struct wm8961_priv {
+       struct snd_soc_codec codec;
+       int sysclk;
+       u16 reg_cache[WM8961_MAX_REGISTER];
+};
+
+static int wm8961_reg_is_volatile(int reg)
+{
+       switch (reg) {
+       case WM8961_WRITE_SEQUENCER_7:
+       case WM8961_DC_SERVO_1:
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+static unsigned int wm8961_read_reg_cache(struct snd_soc_codec *codec,
+                                         unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       BUG_ON(reg > WM8961_MAX_REGISTER);
+       return cache[reg];
+}
+
+static unsigned int wm8961_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+       struct i2c_msg xfer[2];
+       u16 data;
+       int ret;
+       struct i2c_client *client = codec->control_data;
+
+       BUG_ON(reg > WM8961_MAX_REGISTER);
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+               return 0;
+       }
+
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static unsigned int wm8961_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       if (wm8961_reg_is_volatile(reg))
+               return wm8961_read_hw(codec, reg);
+       else
+               return wm8961_read_reg_cache(codec, reg);
+}
+
+static int wm8961_write(struct snd_soc_codec *codec, unsigned int reg,
+                       unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       u8 data[3];
+
+       BUG_ON(reg > WM8961_MAX_REGISTER);
+
+       if (!wm8961_reg_is_volatile(reg))
+               cache[reg] = value;
+
+       data[0] = reg;
+       data[1] = value >> 8;
+       data[2] = value & 0x00ff;
+
+       if (codec->hw_write(codec->control_data, data, 3) == 3)
+               return 0;
+       else
+               return -EIO;
+}
+
+static int wm8961_reset(struct snd_soc_codec *codec)
+{
+       return wm8961_write(codec, WM8961_SOFTWARE_RESET, 0);
+}
+
+/*
+ * The headphone output supports special anti-pop sequences giving
+ * silent power up and power down.
+ */
+static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 hp_reg = wm8961_read(codec, WM8961_ANALOGUE_HP_0);
+       u16 cp_reg = wm8961_read(codec, WM8961_CHARGE_PUMP_1);
+       u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2);
+       u16 dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1);
+       int timeout = 500;
+
+       if (event & SND_SOC_DAPM_POST_PMU) {
+               /* Make sure the output is shorted */
+               hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Enable the charge pump */
+               cp_reg |= WM8961_CP_ENA;
+               wm8961_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
+               mdelay(5);
+
+               /* Enable the PGA */
+               pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
+               wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+               /* Enable the amplifier */
+               hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Second stage enable */
+               hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Enable the DC servo & trigger startup */
+               dcs_reg |=
+                       WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
+                       WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
+               dev_dbg(codec->dev, "Enabling DC servo\n");
+
+               wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+               do {
+                       msleep(1);
+                       dcs_reg = wm8961_read(codec, WM8961_DC_SERVO_1);
+               } while (--timeout &&
+                        dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+                               WM8961_DCS_TRIG_STARTUP_HPL));
+               if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+                              WM8961_DCS_TRIG_STARTUP_HPL))
+                       dev_err(codec->dev, "DC servo timed out\n");
+               else
+                       dev_dbg(codec->dev, "DC servo startup complete\n");
+
+               /* Enable the output stage */
+               hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Remove the short on the output stage */
+               hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+       }
+
+       if (event & SND_SOC_DAPM_PRE_PMD) {
+               /* Short the output */
+               hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Disable the output stage */
+               hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Disable DC offset cancellation */
+               dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
+                            WM8961_DCS_ENA_CHAN_HPL);
+               wm8961_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+
+               /* Finish up */
+               hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
+                           WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
+               wm8961_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+               /* Disable the PGA */
+               pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
+               wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+               /* Disable the charge pump */
+               dev_dbg(codec->dev, "Disabling charge pump\n");
+               wm8961_write(codec, WM8961_CHARGE_PUMP_1,
+                            cp_reg & ~WM8961_CP_ENA);
+       }
+
+       return 0;
+}
+
+static int wm8961_spk_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 pwr_reg = wm8961_read(codec, WM8961_PWR_MGMT_2);
+       u16 spk_reg = wm8961_read(codec, WM8961_CLASS_D_CONTROL_1);
+
+       if (event & SND_SOC_DAPM_POST_PMU) {
+               /* Enable the PGA */
+               pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
+               wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+               /* Enable the amplifier */
+               spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA;
+               wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+       }
+
+       if (event & SND_SOC_DAPM_PRE_PMD) {
+               /* Enable the amplifier */
+               spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
+               wm8961_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+
+               /* Enable the PGA */
+               pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
+               wm8961_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+       }
+
+       return 0;
+}
+
+static const char *adc_hpf_text[] = {
+       "Hi-fi", "Voice 1", "Voice 2", "Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+       SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text);
+
+static const char *dac_deemph_text[] = {
+       "None", "32kHz", "44.1kHz", "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+       SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static unsigned int boost_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 0, TLV_DB_SCALE_ITEM(0,  0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
+       3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
+
+static const struct snd_kcontrol_new wm8961_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2,
+              6, 3, 7, 0, hp_sec_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+            7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+          7, 1, 0),
+SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0),
+
+SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0),
+
+SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0,
+                WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Capture Volume",
+                WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME,
+                1, 119, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Boost Volume",
+                WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH,
+                4, 3, 0, boost_tlv),
+SOC_DOUBLE_R_TLV("Capture PGA Volume",
+                WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+                0, 62, 0, pga_tlv),
+SOC_DOUBLE_R("Capture PGA ZC Switch",
+            WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+            6, 1, 1),
+SOC_DOUBLE_R("Capture PGA Switch",
+            WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+            7, 1, 1),
+};
+
+static const char *sidetone_text[] = {
+       "None", "Left", "Right"
+};
+
+static const struct soc_enum dacl_sidetone =
+       SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text);
+
+static const struct soc_enum dacr_sidetone =
+       SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_mux =
+       SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
+
+static const struct snd_kcontrol_new dacr_mux =
+       SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone);
+
+static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT"),
+SND_SOC_DAPM_INPUT("RINPUT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0),
+SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8961_PWR_MGMT_1, 1, 0),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux),
+
+SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0),
+
+/* Handle as a mono path for DCS */
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM,
+                  4, 0, NULL, 0, wm8961_hp_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM,
+                  4, 0, NULL, 0, wm8961_spk_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+       { "DACL", NULL, "CLK_DSP" },
+       { "DACL", NULL, "DACL Sidetone" },
+       { "DACR", NULL, "CLK_DSP" },
+       { "DACR", NULL, "DACR Sidetone" },
+
+       { "DACL Sidetone", "Left", "ADCL" },
+       { "DACL Sidetone", "Right", "ADCR" },
+
+       { "DACR Sidetone", "Left", "ADCL" },
+       { "DACR Sidetone", "Right", "ADCR" },
+
+       { "HP_L", NULL, "Headphone Output" },
+       { "HP_R", NULL, "Headphone Output" },
+       { "Headphone Output", NULL, "DACL" },
+       { "Headphone Output", NULL, "DACR" },
+
+       { "SPK_LN", NULL, "Speaker Output" },
+       { "SPK_LP", NULL, "Speaker Output" },
+       { "SPK_RN", NULL, "Speaker Output" },
+       { "SPK_RP", NULL, "Speaker Output" },
+
+       { "Speaker Output", NULL, "DACL" },
+       { "Speaker Output", NULL, "DACR" },
+
+       { "ADCL", NULL, "Left Input" },
+       { "ADCL", NULL, "CLK_DSP" },
+       { "ADCR", NULL, "Right Input" },
+       { "ADCR", NULL, "CLK_DSP" },
+
+       { "Left Input", NULL, "LINPUT" },
+       { "Right Input", NULL, "RINPUT" },
+
+};
+
+/* Values for CLK_SYS_RATE */
+static struct {
+       int ratio;
+       u16 val;
+} wm8961_clk_sys_ratio[] = {
+       {  64,  0 },
+       {  128, 1 },
+       {  192, 2 },
+       {  256, 3 },
+       {  384, 4 },
+       {  512, 5 },
+       {  768, 6 },
+       { 1024, 7 },
+       { 1408, 8 },
+       { 1536, 9 },
+};
+
+/* Values for SAMPLE_RATE */
+static struct {
+       int rate;
+       u16 val;
+} wm8961_srate[] = {
+       { 48000, 0 },
+       { 44100, 0 },
+       { 32000, 1 },
+       { 22050, 2 },
+       { 24000, 2 },
+       { 16000, 3 },
+       { 11250, 4 },
+       { 12000, 4 },
+       {  8000, 5 },
+};
+
+static int wm8961_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 wm8961_priv *wm8961 = codec->private_data;
+       int i, best, target, fs;
+       u16 reg;
+
+       fs = params_rate(params);
+
+       if (!wm8961->sysclk) {
+               dev_err(codec->dev, "MCLK has not been specified\n");
+               return -EINVAL;
+       }
+
+       /* Find the closest sample rate for the filters */
+       best = 0;
+       for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
+               if (abs(wm8961_srate[i].rate - fs) <
+                   abs(wm8961_srate[best].rate - fs))
+                       best = i;
+       }
+       reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_3);
+       reg &= ~WM8961_SAMPLE_RATE_MASK;
+       reg |= wm8961_srate[best].val;
+       wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
+       dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
+               wm8961_srate[best].rate, fs);
+
+       /* Select a CLK_SYS/fs ratio equal to or higher than required */
+       target = wm8961->sysclk / fs;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
+               dev_err(codec->dev,
+                       "SYSCLK must be at least 64*fs for DAC\n");
+               return -EINVAL;
+       }
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
+               dev_err(codec->dev,
+                       "SYSCLK must be at least 256*fs for ADC\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
+               if (wm8961_clk_sys_ratio[i].ratio >= target)
+                       break;
+       }
+       if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
+               dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
+               return -EINVAL;
+       }
+       dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
+               wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
+               wm8961->sysclk / fs);
+
+       reg = wm8961_read(codec, WM8961_CLOCKING_4);
+       reg &= ~WM8961_CLK_SYS_RATE_MASK;
+       reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
+       wm8961_write(codec, WM8961_CLOCKING_4, reg);
+
+       reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0);
+       reg &= ~WM8961_WL_MASK;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               reg |= 1 << WM8961_WL_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               reg |= 2 << WM8961_WL_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               reg |= 3 << WM8961_WL_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+       wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
+
+       /* Sloping stop-band filter is recommended for <= 24kHz */
+       reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2);
+       if (fs <= 24000)
+               reg |= WM8961_DACSLOPE;
+       else
+               reg &= WM8961_DACSLOPE;
+       wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+       return 0;
+}
+
+static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                            unsigned int freq,
+                            int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8961_priv *wm8961 = codec->private_data;
+       u16 reg = wm8961_read(codec, WM8961_CLOCKING1);
+
+       if (freq > 33000000) {
+               dev_err(codec->dev, "MCLK must be <33MHz\n");
+               return -EINVAL;
+       }
+
+       if (freq > 16500000) {
+               dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
+               reg |= WM8961_MCLKDIV;
+               freq /= 2;
+       } else {
+               dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
+               reg &= WM8961_MCLKDIV;
+       }
+
+       wm8961_write(codec, WM8961_CLOCKING1, reg);
+
+       wm8961->sysclk = freq;
+
+       return 0;
+}
+
+static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 aif = wm8961_read(codec, WM8961_AUDIO_INTERFACE_0);
+
+       aif &= ~(WM8961_BCLKINV | WM8961_LRP |
+                WM8961_MS | WM8961_FORMAT_MASK);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aif |= WM8961_MS;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+
+       case SND_SOC_DAIFMT_LEFT_J:
+               aif |= 1;
+               break;
+
+       case SND_SOC_DAIFMT_I2S:
+               aif |= 2;
+               break;
+
+       case SND_SOC_DAIFMT_DSP_B:
+               aif |= WM8961_LRP;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif |= 3;
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+               case SND_SOC_DAIFMT_IB_NF:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               aif |= WM8961_LRP;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               aif |= WM8961_BCLKINV;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               aif |= WM8961_BCLKINV | WM8961_LRP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return wm8961_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
+}
+
+static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 reg = wm8961_read(codec, WM8961_ADDITIONAL_CONTROL_2);
+
+       if (tristate)
+               reg |= WM8961_TRIS;
+       else
+               reg &= ~WM8961_TRIS;
+
+       return wm8961_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
+}
+
+static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_1);
+
+       if (mute)
+               reg |= WM8961_DACMU;
+       else
+               reg &= ~WM8961_DACMU;
+
+       msleep(17);
+
+       return wm8961_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
+}
+
+static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 reg;
+
+       switch (div_id) {
+       case WM8961_BCLK:
+               reg = wm8961_read(codec, WM8961_CLOCKING2);
+               reg &= ~WM8961_BCLKDIV_MASK;
+               reg |= div;
+               wm8961_write(codec, WM8961_CLOCKING2, reg);
+               break;
+
+       case WM8961_LRCLK:
+               reg = wm8961_read(codec, WM8961_AUDIO_INTERFACE_2);
+               reg &= ~WM8961_LRCLK_RATE_MASK;
+               reg |= div;
+               wm8961_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8961_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 reg;
+
+       /* This is all slightly unusual since we have no bypass paths
+        * and the output amplifier structure means we can just slam
+        * the biases straight up rather than having to ramp them
+        * slowly.
+        */
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               if (codec->bias_level == SND_SOC_BIAS_STANDBY) {
+                       /* Enable bias generation */
+                       reg = wm8961_read(codec, WM8961_ANTI_POP);
+                       reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
+                       wm8961_write(codec, WM8961_ANTI_POP, reg);
+
+                       /* VMID=2*50k, VREF */
+                       reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
+                       reg &= ~WM8961_VMIDSEL_MASK;
+                       reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
+                       wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_PREPARE) {
+                       /* VREF off */
+                       reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
+                       reg &= ~WM8961_VREF;
+                       wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
+
+                       /* Bias generation off */
+                       reg = wm8961_read(codec, WM8961_ANTI_POP);
+                       reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
+                       wm8961_write(codec, WM8961_ANTI_POP, reg);
+
+                       /* VMID off */
+                       reg = wm8961_read(codec, WM8961_PWR_MGMT_1);
+                       reg &= ~WM8961_VMIDSEL_MASK;
+                       wm8961_write(codec, WM8961_PWR_MGMT_1, reg);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+
+       codec->bias_level = level;
+
+       return 0;
+}
+
+
+#define WM8961_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8961_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8961_dai_ops = {
+       .hw_params = wm8961_hw_params,
+       .set_sysclk = wm8961_set_sysclk,
+       .set_fmt = wm8961_set_fmt,
+       .digital_mute = wm8961_digital_mute,
+       .set_tristate = wm8961_set_tristate,
+       .set_clkdiv = wm8961_set_clkdiv,
+};
+
+struct snd_soc_dai wm8961_dai = {
+       .name = "WM8961",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8961_RATES,
+               .formats = WM8961_FORMATS,},
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8961_RATES,
+               .formats = WM8961_FORMATS,},
+       .ops = &wm8961_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8961_dai);
+
+
+static struct snd_soc_codec *wm8961_codec;
+
+static int wm8961_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8961_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8961_codec;
+       codec = wm8961_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm8961_snd_controls,
+                               ARRAY_SIZE(wm8961_snd_controls));
+       snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
+                                 ARRAY_SIZE(wm8961_dapm_widgets));
+       snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int wm8961_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8961_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u16 *reg_cache = codec->reg_cache;
+       int i;
+
+       for (i = 0; i < codec->reg_cache_size; i++) {
+               if (i == WM8961_SOFTWARE_RESET)
+                       continue;
+
+               wm8961_write(codec, i, reg_cache[i]);
+       }
+
+       wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define wm8961_suspend NULL
+#define wm8961_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_wm8961 = {
+       .probe =        wm8961_probe,
+       .remove =       wm8961_remove,
+       .suspend =      wm8961_suspend,
+       .resume =       wm8961_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961);
+
+static int wm8961_register(struct wm8961_priv *wm8961)
+{
+       struct snd_soc_codec *codec = &wm8961->codec;
+       int ret;
+       u16 reg;
+
+       if (wm8961_codec) {
+               dev_err(codec->dev, "Another WM8961 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8961;
+       codec->name = "WM8961";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8961_read;
+       codec->write = wm8961_write;
+       codec->dai = &wm8961_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
+       codec->reg_cache = &wm8961->reg_cache;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8961_set_bias_level;
+
+       memcpy(codec->reg_cache, wm8961_reg_defaults,
+              sizeof(wm8961_reg_defaults));
+
+       reg = wm8961_read_hw(codec, WM8961_SOFTWARE_RESET);
+       if (reg != 0x1801) {
+               dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       reg = wm8961_read_hw(codec, WM8961_RIGHT_INPUT_VOLUME);
+       dev_info(codec->dev, "WM8961 family %d revision %c\n",
+                (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
+                ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+                + 'A');
+
+       ret = wm8961_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       /* Enable class W */
+       reg = wm8961_read(codec, WM8961_CHARGE_PUMP_B);
+       reg |= WM8961_CP_DYN_PWR_MASK;
+       wm8961_write(codec, WM8961_CHARGE_PUMP_B, reg);
+
+       /* Latch volume update bits (right channel only, we always
+        * write both out) and default ZC on. */
+       reg = wm8961_read(codec, WM8961_ROUT1_VOLUME);
+       wm8961_write(codec, WM8961_ROUT1_VOLUME,
+                    reg | WM8961_LO1ZC | WM8961_OUT1VU);
+       wm8961_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
+       reg = wm8961_read(codec, WM8961_ROUT2_VOLUME);
+       wm8961_write(codec, WM8961_ROUT2_VOLUME,
+                    reg | WM8961_SPKRZC | WM8961_SPKVU);
+       wm8961_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
+
+       reg = wm8961_read(codec, WM8961_RIGHT_ADC_VOLUME);
+       wm8961_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
+       reg = wm8961_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+       wm8961_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
+
+       /* Use soft mute by default */
+       reg = wm8961_read(codec, WM8961_ADC_DAC_CONTROL_2);
+       reg |= WM8961_DACSMM;
+       wm8961_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+       /* Use automatic clocking mode by default; for now this is all
+        * we support.
+        */
+       reg = wm8961_read(codec, WM8961_CLOCKING_3);
+       reg &= ~WM8961_MANUAL_MODE;
+       wm8961_write(codec, WM8961_CLOCKING_3, reg);
+
+       wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       wm8961_dai.dev = codec->dev;
+
+       wm8961_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm8961_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+
+err:
+       kfree(wm8961);
+       return ret;
+}
+
+static void wm8961_unregister(struct wm8961_priv *wm8961)
+{
+       wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8961_dai);
+       snd_soc_unregister_codec(&wm8961->codec);
+       kfree(wm8961);
+       wm8961_codec = NULL;
+}
+
+static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8961_priv *wm8961;
+       struct snd_soc_codec *codec;
+
+       wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL);
+       if (wm8961 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8961->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8961);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8961_register(wm8961);
+}
+
+static __devexit int wm8961_i2c_remove(struct i2c_client *client)
+{
+       struct wm8961_priv *wm8961 = i2c_get_clientdata(client);
+       wm8961_unregister(wm8961);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8961_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8961_i2c_suspend NULL
+#define wm8961_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8961_i2c_id[] = {
+       { "wm8961", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
+
+static struct i2c_driver wm8961_i2c_driver = {
+       .driver = {
+               .name = "wm8961",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8961_i2c_probe,
+       .remove =   __devexit_p(wm8961_i2c_remove),
+       .suspend =  wm8961_i2c_suspend,
+       .resume =   wm8961_i2c_resume,
+       .id_table = wm8961_i2c_id,
+};
+
+static int __init wm8961_modinit(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&wm8961_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n",
+                      ret);
+       }
+
+       return ret;
+}
+module_init(wm8961_modinit);
+
+static void __exit wm8961_exit(void)
+{
+       i2c_del_driver(&wm8961_i2c_driver);
+}
+module_exit(wm8961_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8961 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h
new file mode 100644 (file)
index 0000000..5513bfd
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * wm8961.h  --  WM8961 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8961_H
+#define _WM8961_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_codec_device soc_codec_dev_wm8961;
+extern struct snd_soc_dai wm8961_dai;
+
+#define WM8961_BCLK  1
+#define WM8961_LRCLK 2
+
+#define WM8961_BCLK_DIV_1    0
+#define WM8961_BCLK_DIV_1_5  1
+#define WM8961_BCLK_DIV_2    2
+#define WM8961_BCLK_DIV_3    3
+#define WM8961_BCLK_DIV_4    4
+#define WM8961_BCLK_DIV_5_5  5
+#define WM8961_BCLK_DIV_6    6
+#define WM8961_BCLK_DIV_8    7
+#define WM8961_BCLK_DIV_11   8
+#define WM8961_BCLK_DIV_12   9
+#define WM8961_BCLK_DIV_16  10
+#define WM8961_BCLK_DIV_24  11
+#define WM8961_BCLK_DIV_32  13
+
+
+/*
+ * Register values.
+ */
+#define WM8961_LEFT_INPUT_VOLUME                0x00
+#define WM8961_RIGHT_INPUT_VOLUME               0x01
+#define WM8961_LOUT1_VOLUME                     0x02
+#define WM8961_ROUT1_VOLUME                     0x03
+#define WM8961_CLOCKING1                        0x04
+#define WM8961_ADC_DAC_CONTROL_1                0x05
+#define WM8961_ADC_DAC_CONTROL_2                0x06
+#define WM8961_AUDIO_INTERFACE_0                0x07
+#define WM8961_CLOCKING2                        0x08
+#define WM8961_AUDIO_INTERFACE_1                0x09
+#define WM8961_LEFT_DAC_VOLUME                  0x0A
+#define WM8961_RIGHT_DAC_VOLUME                 0x0B
+#define WM8961_AUDIO_INTERFACE_2                0x0E
+#define WM8961_SOFTWARE_RESET                   0x0F
+#define WM8961_ALC1                             0x11
+#define WM8961_ALC2                             0x12
+#define WM8961_ALC3                             0x13
+#define WM8961_NOISE_GATE                       0x14
+#define WM8961_LEFT_ADC_VOLUME                  0x15
+#define WM8961_RIGHT_ADC_VOLUME                 0x16
+#define WM8961_ADDITIONAL_CONTROL_1             0x17
+#define WM8961_ADDITIONAL_CONTROL_2             0x18
+#define WM8961_PWR_MGMT_1                       0x19
+#define WM8961_PWR_MGMT_2                       0x1A
+#define WM8961_ADDITIONAL_CONTROL_3             0x1B
+#define WM8961_ANTI_POP                         0x1C
+#define WM8961_CLOCKING_3                       0x1E
+#define WM8961_ADCL_SIGNAL_PATH                 0x20
+#define WM8961_ADCR_SIGNAL_PATH                 0x21
+#define WM8961_LOUT2_VOLUME                     0x28
+#define WM8961_ROUT2_VOLUME                     0x29
+#define WM8961_PWR_MGMT_3                       0x2F
+#define WM8961_ADDITIONAL_CONTROL_4             0x30
+#define WM8961_CLASS_D_CONTROL_1                0x31
+#define WM8961_CLASS_D_CONTROL_2                0x33
+#define WM8961_CLOCKING_4                       0x38
+#define WM8961_DSP_SIDETONE_0                   0x39
+#define WM8961_DSP_SIDETONE_1                   0x3A
+#define WM8961_DC_SERVO_0                       0x3C
+#define WM8961_DC_SERVO_1                       0x3D
+#define WM8961_DC_SERVO_3                       0x3F
+#define WM8961_DC_SERVO_5                       0x41
+#define WM8961_ANALOGUE_PGA_BIAS                0x44
+#define WM8961_ANALOGUE_HP_0                    0x45
+#define WM8961_ANALOGUE_HP_2                    0x47
+#define WM8961_CHARGE_PUMP_1                    0x48
+#define WM8961_CHARGE_PUMP_B                    0x52
+#define WM8961_WRITE_SEQUENCER_1                0x57
+#define WM8961_WRITE_SEQUENCER_2                0x58
+#define WM8961_WRITE_SEQUENCER_3                0x59
+#define WM8961_WRITE_SEQUENCER_4                0x5A
+#define WM8961_WRITE_SEQUENCER_5                0x5B
+#define WM8961_WRITE_SEQUENCER_6                0x5C
+#define WM8961_WRITE_SEQUENCER_7                0x5D
+#define WM8961_GENERAL_TEST_1                   0xFC
+
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left Input volume
+ */
+#define WM8961_IPVU                             0x0100  /* IPVU */
+#define WM8961_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8961_IPVU_SHIFT                            8  /* IPVU */
+#define WM8961_IPVU_WIDTH                            1  /* IPVU */
+#define WM8961_LINMUTE                          0x0080  /* LINMUTE */
+#define WM8961_LINMUTE_MASK                     0x0080  /* LINMUTE */
+#define WM8961_LINMUTE_SHIFT                         7  /* LINMUTE */
+#define WM8961_LINMUTE_WIDTH                         1  /* LINMUTE */
+#define WM8961_LIZC                             0x0040  /* LIZC */
+#define WM8961_LIZC_MASK                        0x0040  /* LIZC */
+#define WM8961_LIZC_SHIFT                            6  /* LIZC */
+#define WM8961_LIZC_WIDTH                            1  /* LIZC */
+#define WM8961_LINVOL_MASK                      0x003F  /* LINVOL - [5:0] */
+#define WM8961_LINVOL_SHIFT                          0  /* LINVOL - [5:0] */
+#define WM8961_LINVOL_WIDTH                          6  /* LINVOL - [5:0] */
+
+/*
+ * R1 (0x01) - Right Input volume
+ */
+#define WM8961_DEVICE_ID_MASK                   0xF000  /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_SHIFT                      12  /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_WIDTH                       4  /* DEVICE_ID - [15:12] */
+#define WM8961_CHIP_REV_MASK                    0x0E00  /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_SHIFT                        9  /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_WIDTH                        3  /* CHIP_REV - [11:9] */
+#define WM8961_IPVU                             0x0100  /* IPVU */
+#define WM8961_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8961_IPVU_SHIFT                            8  /* IPVU */
+#define WM8961_IPVU_WIDTH                            1  /* IPVU */
+#define WM8961_RINMUTE                          0x0080  /* RINMUTE */
+#define WM8961_RINMUTE_MASK                     0x0080  /* RINMUTE */
+#define WM8961_RINMUTE_SHIFT                         7  /* RINMUTE */
+#define WM8961_RINMUTE_WIDTH                         1  /* RINMUTE */
+#define WM8961_RIZC                             0x0040  /* RIZC */
+#define WM8961_RIZC_MASK                        0x0040  /* RIZC */
+#define WM8961_RIZC_SHIFT                            6  /* RIZC */
+#define WM8961_RIZC_WIDTH                            1  /* RIZC */
+#define WM8961_RINVOL_MASK                      0x003F  /* RINVOL - [5:0] */
+#define WM8961_RINVOL_SHIFT                          0  /* RINVOL - [5:0] */
+#define WM8961_RINVOL_WIDTH                          6  /* RINVOL - [5:0] */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8961_OUT1VU                           0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_MASK                      0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT                          8  /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH                          1  /* OUT1VU */
+#define WM8961_LO1ZC                            0x0080  /* LO1ZC */
+#define WM8961_LO1ZC_MASK                       0x0080  /* LO1ZC */
+#define WM8961_LO1ZC_SHIFT                           7  /* LO1ZC */
+#define WM8961_LO1ZC_WIDTH                           1  /* LO1ZC */
+#define WM8961_LOUT1VOL_MASK                    0x007F  /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_SHIFT                        0  /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_WIDTH                        7  /* LOUT1VOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8961_OUT1VU                           0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_MASK                      0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT                          8  /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH                          1  /* OUT1VU */
+#define WM8961_RO1ZC                            0x0080  /* RO1ZC */
+#define WM8961_RO1ZC_MASK                       0x0080  /* RO1ZC */
+#define WM8961_RO1ZC_SHIFT                           7  /* RO1ZC */
+#define WM8961_RO1ZC_WIDTH                           1  /* RO1ZC */
+#define WM8961_ROUT1VOL_MASK                    0x007F  /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_SHIFT                        0  /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_WIDTH                        7  /* ROUT1VOL - [6:0] */
+
+/*
+ * R4 (0x04) - Clocking1
+ */
+#define WM8961_ADCDIV_MASK                      0x01C0  /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_SHIFT                          6  /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_WIDTH                          3  /* ADCDIV - [8:6] */
+#define WM8961_DACDIV_MASK                      0x0038  /* DACDIV - [5:3] */
+#define WM8961_DACDIV_SHIFT                          3  /* DACDIV - [5:3] */
+#define WM8961_DACDIV_WIDTH                          3  /* DACDIV - [5:3] */
+#define WM8961_MCLKDIV                          0x0004  /* MCLKDIV */
+#define WM8961_MCLKDIV_MASK                     0x0004  /* MCLKDIV */
+#define WM8961_MCLKDIV_SHIFT                         2  /* MCLKDIV */
+#define WM8961_MCLKDIV_WIDTH                         1  /* MCLKDIV */
+
+/*
+ * R5 (0x05) - ADC & DAC Control 1
+ */
+#define WM8961_ADCPOL_MASK                      0x0060  /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_SHIFT                          5  /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_WIDTH                          2  /* ADCPOL - [6:5] */
+#define WM8961_DACMU                            0x0008  /* DACMU */
+#define WM8961_DACMU_MASK                       0x0008  /* DACMU */
+#define WM8961_DACMU_SHIFT                           3  /* DACMU */
+#define WM8961_DACMU_WIDTH                           1  /* DACMU */
+#define WM8961_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+#define WM8961_ADCHPD                           0x0001  /* ADCHPD */
+#define WM8961_ADCHPD_MASK                      0x0001  /* ADCHPD */
+#define WM8961_ADCHPD_SHIFT                          0  /* ADCHPD */
+#define WM8961_ADCHPD_WIDTH                          1  /* ADCHPD */
+
+/*
+ * R6 (0x06) - ADC & DAC Control 2
+ */
+#define WM8961_ADC_HPF_CUT_MASK                 0x0180  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_SHIFT                     7  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_DACPOL_MASK                      0x0060  /* DACPOL - [6:5] */
+#define WM8961_DACPOL_SHIFT                          5  /* DACPOL - [6:5] */
+#define WM8961_DACPOL_WIDTH                          2  /* DACPOL - [6:5] */
+#define WM8961_DACSMM                           0x0008  /* DACSMM */
+#define WM8961_DACSMM_MASK                      0x0008  /* DACSMM */
+#define WM8961_DACSMM_SHIFT                          3  /* DACSMM */
+#define WM8961_DACSMM_WIDTH                          1  /* DACSMM */
+#define WM8961_DACMR                            0x0004  /* DACMR */
+#define WM8961_DACMR_MASK                       0x0004  /* DACMR */
+#define WM8961_DACMR_SHIFT                           2  /* DACMR */
+#define WM8961_DACMR_WIDTH                           1  /* DACMR */
+#define WM8961_DACSLOPE                         0x0002  /* DACSLOPE */
+#define WM8961_DACSLOPE_MASK                    0x0002  /* DACSLOPE */
+#define WM8961_DACSLOPE_SHIFT                        1  /* DACSLOPE */
+#define WM8961_DACSLOPE_WIDTH                        1  /* DACSLOPE */
+#define WM8961_DAC_OSR128                       0x0001  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_MASK                  0x0001  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_SHIFT                      0  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+
+/*
+ * R7 (0x07) - Audio Interface 0
+ */
+#define WM8961_ALRSWAP                          0x0100  /* ALRSWAP */
+#define WM8961_ALRSWAP_MASK                     0x0100  /* ALRSWAP */
+#define WM8961_ALRSWAP_SHIFT                         8  /* ALRSWAP */
+#define WM8961_ALRSWAP_WIDTH                         1  /* ALRSWAP */
+#define WM8961_BCLKINV                          0x0080  /* BCLKINV */
+#define WM8961_BCLKINV_MASK                     0x0080  /* BCLKINV */
+#define WM8961_BCLKINV_SHIFT                         7  /* BCLKINV */
+#define WM8961_BCLKINV_WIDTH                         1  /* BCLKINV */
+#define WM8961_MS                               0x0040  /* MS */
+#define WM8961_MS_MASK                          0x0040  /* MS */
+#define WM8961_MS_SHIFT                              6  /* MS */
+#define WM8961_MS_WIDTH                              1  /* MS */
+#define WM8961_DLRSWAP                          0x0020  /* DLRSWAP */
+#define WM8961_DLRSWAP_MASK                     0x0020  /* DLRSWAP */
+#define WM8961_DLRSWAP_SHIFT                         5  /* DLRSWAP */
+#define WM8961_DLRSWAP_WIDTH                         1  /* DLRSWAP */
+#define WM8961_LRP                              0x0010  /* LRP */
+#define WM8961_LRP_MASK                         0x0010  /* LRP */
+#define WM8961_LRP_SHIFT                             4  /* LRP */
+#define WM8961_LRP_WIDTH                             1  /* LRP */
+#define WM8961_WL_MASK                          0x000C  /* WL - [3:2] */
+#define WM8961_WL_SHIFT                              2  /* WL - [3:2] */
+#define WM8961_WL_WIDTH                              2  /* WL - [3:2] */
+#define WM8961_FORMAT_MASK                      0x0003  /* FORMAT - [1:0] */
+#define WM8961_FORMAT_SHIFT                          0  /* FORMAT - [1:0] */
+#define WM8961_FORMAT_WIDTH                          2  /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking2
+ */
+#define WM8961_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_SHIFT                         6  /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_WIDTH                         3  /* DCLKDIV - [8:6] */
+#define WM8961_CLK_SYS_ENA                      0x0020  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_MASK                 0x0020  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_SHIFT                     5  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+#define WM8961_CLK_DSP_ENA                      0x0010  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_MASK                 0x0010  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_SHIFT                     4  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM8961_BCLKDIV_MASK                     0x000F  /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_SHIFT                         0  /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_WIDTH                         4  /* BCLKDIV - [3:0] */
+
+/*
+ * R9 (0x09) - Audio Interface 1
+ */
+#define WM8961_DACCOMP_MASK                     0x0018  /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_SHIFT                         3  /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_WIDTH                         2  /* DACCOMP - [4:3] */
+#define WM8961_ADCCOMP_MASK                     0x0006  /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_SHIFT                         1  /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_WIDTH                         2  /* ADCCOMP - [2:1] */
+#define WM8961_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8961_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8961_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8961_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8961_DACVU                            0x0100  /* DACVU */
+#define WM8961_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8961_DACVU_SHIFT                           8  /* DACVU */
+#define WM8961_DACVU_WIDTH                           1  /* DACVU */
+#define WM8961_LDACVOL_MASK                     0x00FF  /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_SHIFT                         0  /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_WIDTH                         8  /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8961_DACVU                            0x0100  /* DACVU */
+#define WM8961_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8961_DACVU_SHIFT                           8  /* DACVU */
+#define WM8961_DACVU_WIDTH                           1  /* DACVU */
+#define WM8961_RDACVOL_MASK                     0x00FF  /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_SHIFT                         0  /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_WIDTH                         8  /* RDACVOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Audio Interface 2
+ */
+#define WM8961_LRCLK_RATE_MASK                  0x01FF  /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_WIDTH                      9  /* LRCLK_RATE - [8:0] */
+
+/*
+ * R15 (0x0F) - Software Reset
+ */
+#define WM8961_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R17 (0x11) - ALC1
+ */
+#define WM8961_ALCSEL_MASK                      0x0180  /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_SHIFT                          7  /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_WIDTH                          2  /* ALCSEL - [8:7] */
+#define WM8961_MAXGAIN_MASK                     0x0070  /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_SHIFT                         4  /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_WIDTH                         3  /* MAXGAIN - [6:4] */
+#define WM8961_ALCL_MASK                        0x000F  /* ALCL - [3:0] */
+#define WM8961_ALCL_SHIFT                            0  /* ALCL - [3:0] */
+#define WM8961_ALCL_WIDTH                            4  /* ALCL - [3:0] */
+
+/*
+ * R18 (0x12) - ALC2
+ */
+#define WM8961_ALCZC                            0x0080  /* ALCZC */
+#define WM8961_ALCZC_MASK                       0x0080  /* ALCZC */
+#define WM8961_ALCZC_SHIFT                           7  /* ALCZC */
+#define WM8961_ALCZC_WIDTH                           1  /* ALCZC */
+#define WM8961_MINGAIN_MASK                     0x0070  /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_SHIFT                         4  /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_WIDTH                         3  /* MINGAIN - [6:4] */
+#define WM8961_HLD_MASK                         0x000F  /* HLD - [3:0] */
+#define WM8961_HLD_SHIFT                             0  /* HLD - [3:0] */
+#define WM8961_HLD_WIDTH                             4  /* HLD - [3:0] */
+
+/*
+ * R19 (0x13) - ALC3
+ */
+#define WM8961_ALCMODE                          0x0100  /* ALCMODE */
+#define WM8961_ALCMODE_MASK                     0x0100  /* ALCMODE */
+#define WM8961_ALCMODE_SHIFT                         8  /* ALCMODE */
+#define WM8961_ALCMODE_WIDTH                         1  /* ALCMODE */
+#define WM8961_DCY_MASK                         0x00F0  /* DCY - [7:4] */
+#define WM8961_DCY_SHIFT                             4  /* DCY - [7:4] */
+#define WM8961_DCY_WIDTH                             4  /* DCY - [7:4] */
+#define WM8961_ATK_MASK                         0x000F  /* ATK - [3:0] */
+#define WM8961_ATK_SHIFT                             0  /* ATK - [3:0] */
+#define WM8961_ATK_WIDTH                             4  /* ATK - [3:0] */
+
+/*
+ * R20 (0x14) - Noise Gate
+ */
+#define WM8961_NGTH_MASK                        0x00F8  /* NGTH - [7:3] */
+#define WM8961_NGTH_SHIFT                            3  /* NGTH - [7:3] */
+#define WM8961_NGTH_WIDTH                            5  /* NGTH - [7:3] */
+#define WM8961_NGG                              0x0002  /* NGG */
+#define WM8961_NGG_MASK                         0x0002  /* NGG */
+#define WM8961_NGG_SHIFT                             1  /* NGG */
+#define WM8961_NGG_WIDTH                             1  /* NGG */
+#define WM8961_NGAT                             0x0001  /* NGAT */
+#define WM8961_NGAT_MASK                        0x0001  /* NGAT */
+#define WM8961_NGAT_SHIFT                            0  /* NGAT */
+#define WM8961_NGAT_WIDTH                            1  /* NGAT */
+
+/*
+ * R21 (0x15) - Left ADC volume
+ */
+#define WM8961_ADCVU                            0x0100  /* ADCVU */
+#define WM8961_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8961_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8961_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8961_LADCVOL_MASK                     0x00FF  /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_SHIFT                         0  /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_WIDTH                         8  /* LADCVOL - [7:0] */
+
+/*
+ * R22 (0x16) - Right ADC volume
+ */
+#define WM8961_ADCVU                            0x0100  /* ADCVU */
+#define WM8961_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8961_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8961_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8961_RADCVOL_MASK                     0x00FF  /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_SHIFT                         0  /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_WIDTH                         8  /* RADCVOL - [7:0] */
+
+/*
+ * R23 (0x17) - Additional control(1)
+ */
+#define WM8961_TSDEN                            0x0100  /* TSDEN */
+#define WM8961_TSDEN_MASK                       0x0100  /* TSDEN */
+#define WM8961_TSDEN_SHIFT                           8  /* TSDEN */
+#define WM8961_TSDEN_WIDTH                           1  /* TSDEN */
+#define WM8961_DMONOMIX                         0x0010  /* DMONOMIX */
+#define WM8961_DMONOMIX_MASK                    0x0010  /* DMONOMIX */
+#define WM8961_DMONOMIX_SHIFT                        4  /* DMONOMIX */
+#define WM8961_DMONOMIX_WIDTH                        1  /* DMONOMIX */
+#define WM8961_TOEN                             0x0001  /* TOEN */
+#define WM8961_TOEN_MASK                        0x0001  /* TOEN */
+#define WM8961_TOEN_SHIFT                            0  /* TOEN */
+#define WM8961_TOEN_WIDTH                            1  /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control(2)
+ */
+#define WM8961_TRIS                             0x0008  /* TRIS */
+#define WM8961_TRIS_MASK                        0x0008  /* TRIS */
+#define WM8961_TRIS_SHIFT                            3  /* TRIS */
+#define WM8961_TRIS_WIDTH                            1  /* TRIS */
+
+/*
+ * R25 (0x19) - Pwr Mgmt (1)
+ */
+#define WM8961_VMIDSEL_MASK                     0x0180  /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_SHIFT                         7  /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_WIDTH                         2  /* VMIDSEL - [8:7] */
+#define WM8961_VREF                             0x0040  /* VREF */
+#define WM8961_VREF_MASK                        0x0040  /* VREF */
+#define WM8961_VREF_SHIFT                            6  /* VREF */
+#define WM8961_VREF_WIDTH                            1  /* VREF */
+#define WM8961_AINL                             0x0020  /* AINL */
+#define WM8961_AINL_MASK                        0x0020  /* AINL */
+#define WM8961_AINL_SHIFT                            5  /* AINL */
+#define WM8961_AINL_WIDTH                            1  /* AINL */
+#define WM8961_AINR                             0x0010  /* AINR */
+#define WM8961_AINR_MASK                        0x0010  /* AINR */
+#define WM8961_AINR_SHIFT                            4  /* AINR */
+#define WM8961_AINR_WIDTH                            1  /* AINR */
+#define WM8961_ADCL                             0x0008  /* ADCL */
+#define WM8961_ADCL_MASK                        0x0008  /* ADCL */
+#define WM8961_ADCL_SHIFT                            3  /* ADCL */
+#define WM8961_ADCL_WIDTH                            1  /* ADCL */
+#define WM8961_ADCR                             0x0004  /* ADCR */
+#define WM8961_ADCR_MASK                        0x0004  /* ADCR */
+#define WM8961_ADCR_SHIFT                            2  /* ADCR */
+#define WM8961_ADCR_WIDTH                            1  /* ADCR */
+#define WM8961_MICB                             0x0002  /* MICB */
+#define WM8961_MICB_MASK                        0x0002  /* MICB */
+#define WM8961_MICB_SHIFT                            1  /* MICB */
+#define WM8961_MICB_WIDTH                            1  /* MICB */
+
+/*
+ * R26 (0x1A) - Pwr Mgmt (2)
+ */
+#define WM8961_DACL                             0x0100  /* DACL */
+#define WM8961_DACL_MASK                        0x0100  /* DACL */
+#define WM8961_DACL_SHIFT                            8  /* DACL */
+#define WM8961_DACL_WIDTH                            1  /* DACL */
+#define WM8961_DACR                             0x0080  /* DACR */
+#define WM8961_DACR_MASK                        0x0080  /* DACR */
+#define WM8961_DACR_SHIFT                            7  /* DACR */
+#define WM8961_DACR_WIDTH                            1  /* DACR */
+#define WM8961_LOUT1_PGA                        0x0040  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_MASK                   0x0040  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_SHIFT                       6  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_WIDTH                       1  /* LOUT1_PGA */
+#define WM8961_ROUT1_PGA                        0x0020  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_MASK                   0x0020  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_SHIFT                       5  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_WIDTH                       1  /* ROUT1_PGA */
+#define WM8961_SPKL_PGA                         0x0010  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_MASK                    0x0010  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_SHIFT                        4  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_WIDTH                        1  /* SPKL_PGA */
+#define WM8961_SPKR_PGA                         0x0008  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_MASK                    0x0008  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_SHIFT                        3  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_WIDTH                        1  /* SPKR_PGA */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8961_SAMPLE_RATE_MASK                 0x0007  /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R28 (0x1C) - Anti-pop
+ */
+#define WM8961_BUFDCOPEN                        0x0010  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_MASK                   0x0010  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_SHIFT                       4  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_WIDTH                       1  /* BUFDCOPEN */
+#define WM8961_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8961_BUFIOEN_MASK                     0x0008  /* BUFIOEN */
+#define WM8961_BUFIOEN_SHIFT                         3  /* BUFIOEN */
+#define WM8961_BUFIOEN_WIDTH                         1  /* BUFIOEN */
+#define WM8961_SOFT_ST                          0x0004  /* SOFT_ST */
+#define WM8961_SOFT_ST_MASK                     0x0004  /* SOFT_ST */
+#define WM8961_SOFT_ST_SHIFT                         2  /* SOFT_ST */
+#define WM8961_SOFT_ST_WIDTH                         1  /* SOFT_ST */
+
+/*
+ * R30 (0x1E) - Clocking 3
+ */
+#define WM8961_CLK_TO_DIV_MASK                  0x0180  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_SHIFT                      7  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_WIDTH                      2  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_256K_DIV_MASK                0x007E  /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_SHIFT                    1  /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_WIDTH                    6  /* CLK_256K_DIV - [6:1] */
+#define WM8961_MANUAL_MODE                      0x0001  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_MASK                 0x0001  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_SHIFT                     0  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_WIDTH                     1  /* MANUAL_MODE */
+
+/*
+ * R32 (0x20) - ADCL signal path
+ */
+#define WM8961_LMICBOOST_MASK                   0x0030  /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_SHIFT                       4  /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_WIDTH                       2  /* LMICBOOST - [5:4] */
+
+/*
+ * R33 (0x21) - ADCR signal path
+ */
+#define WM8961_RMICBOOST_MASK                   0x0030  /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_SHIFT                       4  /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_WIDTH                       2  /* RMICBOOST - [5:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8961_SPKVU                            0x0100  /* SPKVU */
+#define WM8961_SPKVU_MASK                       0x0100  /* SPKVU */
+#define WM8961_SPKVU_SHIFT                           8  /* SPKVU */
+#define WM8961_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8961_SPKLZC                           0x0080  /* SPKLZC */
+#define WM8961_SPKLZC_MASK                      0x0080  /* SPKLZC */
+#define WM8961_SPKLZC_SHIFT                          7  /* SPKLZC */
+#define WM8961_SPKLZC_WIDTH                          1  /* SPKLZC */
+#define WM8961_SPKLVOL_MASK                     0x007F  /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_SHIFT                         0  /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_WIDTH                         7  /* SPKLVOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8961_SPKVU                            0x0100  /* SPKVU */
+#define WM8961_SPKVU_MASK                       0x0100  /* SPKVU */
+#define WM8961_SPKVU_SHIFT                           8  /* SPKVU */
+#define WM8961_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8961_SPKRZC                           0x0080  /* SPKRZC */
+#define WM8961_SPKRZC_MASK                      0x0080  /* SPKRZC */
+#define WM8961_SPKRZC_SHIFT                          7  /* SPKRZC */
+#define WM8961_SPKRZC_WIDTH                          1  /* SPKRZC */
+#define WM8961_SPKRVOL_MASK                     0x007F  /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_SHIFT                         0  /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_WIDTH                         7  /* SPKRVOL - [6:0] */
+
+/*
+ * R47 (0x2F) - Pwr Mgmt (3)
+ */
+#define WM8961_TEMP_SHUT                        0x0002  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_MASK                   0x0002  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_SHIFT                       1  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_WIDTH                       1  /* TEMP_SHUT */
+#define WM8961_TEMP_WARN                        0x0001  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_MASK                   0x0001  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_SHIFT                       0  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_WIDTH                       1  /* TEMP_WARN */
+
+/*
+ * R48 (0x30) - Additional Control (4)
+ */
+#define WM8961_TSENSEN                          0x0002  /* TSENSEN */
+#define WM8961_TSENSEN_MASK                     0x0002  /* TSENSEN */
+#define WM8961_TSENSEN_SHIFT                         1  /* TSENSEN */
+#define WM8961_TSENSEN_WIDTH                         1  /* TSENSEN */
+#define WM8961_MBSEL                            0x0001  /* MBSEL */
+#define WM8961_MBSEL_MASK                       0x0001  /* MBSEL */
+#define WM8961_MBSEL_SHIFT                           0  /* MBSEL */
+#define WM8961_MBSEL_WIDTH                           1  /* MBSEL */
+
+/*
+ * R49 (0x31) - Class D Control 1
+ */
+#define WM8961_SPKR_ENA                         0x0080  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_MASK                    0x0080  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_SHIFT                        7  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_WIDTH                        1  /* SPKR_ENA */
+#define WM8961_SPKL_ENA                         0x0040  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_MASK                    0x0040  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_SHIFT                        6  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_WIDTH                        1  /* SPKL_ENA */
+
+/*
+ * R51 (0x33) - Class D Control 2
+ */
+#define WM8961_CLASSD_ACGAIN_MASK               0x0007  /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_SHIFT                   0  /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_WIDTH                   3  /* CLASSD_ACGAIN - [2:0] */
+
+/*
+ * R56 (0x38) - Clocking 4
+ */
+#define WM8961_CLK_DCS_DIV_MASK                 0x01E0  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_SHIFT                     5  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_WIDTH                     4  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_SYS_RATE_MASK                0x001E  /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_SHIFT                    1  /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [4:1] */
+
+/*
+ * R57 (0x39) - DSP Sidetone 0
+ */
+#define WM8961_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACR_MASK                 0x000C  /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_SHIFT                     2  /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [3:2] */
+
+/*
+ * R58 (0x3A) - DSP Sidetone 1
+ */
+#define WM8961_ADCL_DAC_SVOL_MASK               0x00F0  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_SHIFT                   4  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+
+/*
+ * R60 (0x3C) - DC Servo 0
+ */
+#define WM8961_DCS_ENA_CHAN_INL                 0x0080  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_MASK            0x0080  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_SHIFT                7  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_WIDTH                1  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL             0x0040  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_MASK        0x0040  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT            6  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH            1  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_SERIES_INL              0x0010  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_MASK         0x0010  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_SHIFT             4  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_WIDTH             1  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_ENA_CHAN_INR                 0x0008  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_MASK            0x0008  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_SHIFT                3  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_WIDTH                1  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR             0x0004  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_MASK        0x0004  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT            2  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH            1  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_SERIES_INR              0x0001  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_MASK         0x0001  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_SHIFT             0  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_WIDTH             1  /* DCS_TRIG_SERIES_INR */
+
+/*
+ * R61 (0x3D) - DC Servo 1
+ */
+#define WM8961_DCS_ENA_CHAN_HPL                 0x0080  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_MASK            0x0080  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_SHIFT                7  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_WIDTH                1  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL             0x0040  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_MASK        0x0040  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT            6  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH            1  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL              0x0010  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_MASK         0x0010  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT             4  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH             1  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_ENA_CHAN_HPR                 0x0008  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_MASK            0x0008  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_SHIFT                3  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_WIDTH                1  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR             0x0004  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_MASK        0x0004  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT            2  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH            1  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR              0x0001  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_MASK         0x0001  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT             0  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH             1  /* DCS_TRIG_SERIES_HPR */
+
+/*
+ * R63 (0x3F) - DC Servo 3
+ */
+#define WM8961_DCS_FILT_BW_SERIES_MASK          0x0030  /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_SHIFT              4  /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_WIDTH              2  /* DCS_FILT_BW_SERIES - [5:4] */
+
+/*
+ * R65 (0x41) - DC Servo 5
+ */
+#define WM8961_DCS_SERIES_NO_HP_MASK            0x007F  /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_SHIFT                0  /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_WIDTH                7  /* DCS_SERIES_NO_HP - [6:0] */
+
+/*
+ * R68 (0x44) - Analogue PGA Bias
+ */
+#define WM8961_HP_PGAS_BIAS_MASK                0x0007  /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_SHIFT                    0  /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_WIDTH                    3  /* HP_PGAS_BIAS - [2:0] */
+
+/*
+ * R69 (0x45) - Analogue HP 0
+ */
+#define WM8961_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */
+#define WM8961_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA                          0x0010  /* HPL_ENA */
+#define WM8961_HPL_ENA_MASK                     0x0010  /* HPL_ENA */
+#define WM8961_HPL_ENA_SHIFT                         4  /* HPL_ENA */
+#define WM8961_HPL_ENA_WIDTH                         1  /* HPL_ENA */
+#define WM8961_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */
+#define WM8961_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA                          0x0001  /* HPR_ENA */
+#define WM8961_HPR_ENA_MASK                     0x0001  /* HPR_ENA */
+#define WM8961_HPR_ENA_SHIFT                         0  /* HPR_ENA */
+#define WM8961_HPR_ENA_WIDTH                         1  /* HPR_ENA */
+
+/*
+ * R71 (0x47) - Analogue HP 2
+ */
+#define WM8961_HPL_VOL_MASK                     0x01C0  /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_SHIFT                         6  /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_WIDTH                         3  /* HPL_VOL - [8:6] */
+#define WM8961_HPR_VOL_MASK                     0x0038  /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_SHIFT                         3  /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_WIDTH                         3  /* HPR_VOL - [5:3] */
+#define WM8961_HP_BIAS_BOOST_MASK               0x0007  /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_SHIFT                   0  /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_WIDTH                   3  /* HP_BIAS_BOOST - [2:0] */
+
+/*
+ * R72 (0x48) - Charge Pump 1
+ */
+#define WM8961_CP_ENA                           0x0001  /* CP_ENA */
+#define WM8961_CP_ENA_MASK                      0x0001  /* CP_ENA */
+#define WM8961_CP_ENA_SHIFT                          0  /* CP_ENA */
+#define WM8961_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R82 (0x52) - Charge Pump B
+ */
+#define WM8961_CP_DYN_PWR_MASK                  0x0003  /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_SHIFT                      0  /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_WIDTH                      2  /* CP_DYN_PWR - [1:0] */
+
+/*
+ * R87 (0x57) - Write Sequencer 1
+ */
+#define WM8961_WSEQ_ENA                         0x0020  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_MASK                    0x0020  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_SHIFT                        5  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8961_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R88 (0x58) - Write Sequencer 2
+ */
+#define WM8961_WSEQ_EOS                         0x0100  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_MASK                    0x0100  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_SHIFT                        8  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8961_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R89 (0x59) - Write Sequencer 3
+ */
+#define WM8961_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R90 (0x5A) - Write Sequencer 4
+ */
+#define WM8961_WSEQ_ABORT                       0x0100  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_MASK                  0x0100  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_SHIFT                      8  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8961_WSEQ_START                       0x0080  /* WSEQ_START */
+#define WM8961_WSEQ_START_MASK                  0x0080  /* WSEQ_START */
+#define WM8961_WSEQ_START_SHIFT                      7  /* WSEQ_START */
+#define WM8961_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8961_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R91 (0x5B) - Write Sequencer 5
+ */
+#define WM8961_WSEQ_DATA_WIDTH_MASK             0x0070  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_SHIFT                 4  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_START_MASK             0x000F  /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_SHIFT                 0  /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [3:0] */
+
+/*
+ * R92 (0x5C) - Write Sequencer 6
+ */
+#define WM8961_WSEQ_DELAY_MASK                  0x000F  /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_SHIFT                      0  /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [3:0] */
+
+/*
+ * R93 (0x5D) - Write Sequencer 7
+ */
+#define WM8961_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R252 (0xFC) - General test 1
+ */
+#define WM8961_ARA_ENA                          0x0002  /* ARA_ENA */
+#define WM8961_ARA_ENA_MASK                     0x0002  /* ARA_ENA */
+#define WM8961_ARA_ENA_SHIFT                         1  /* ARA_ENA */
+#define WM8961_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM8961_AUTO_INC                         0x0001  /* AUTO_INC */
+#define WM8961_AUTO_INC_MASK                    0x0001  /* AUTO_INC */
+#define WM8961_AUTO_INC_SHIFT                        0  /* AUTO_INC */
+#define WM8961_AUTO_INC_WIDTH                        1  /* AUTO_INC */
+
+#endif
index c05f718..6f15acd 100644 (file)
@@ -902,7 +902,7 @@ static int wm8988_register(struct wm8988_priv *wm8988)
        ret = wm8988_reset(codec);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to issue reset\n");
-               return ret;
+               goto err;
        }
 
        /* set the update bits (we always update left then right) */
@@ -926,18 +926,20 @@ static int wm8988_register(struct wm8988_priv *wm8988)
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               return ret;
+               goto err;
        }
 
        ret = snd_soc_register_dai(&wm8988_dai);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
                snd_soc_unregister_codec(codec);
-               return ret;
+               goto err_codec;
        }
 
        return 0;
 
+err_codec:
+       snd_soc_unregister_codec(codec);
 err:
        kfree(wm8988);
        return ret;
@@ -981,6 +983,21 @@ static int wm8988_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8988_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8988_i2c_suspend NULL
+#define wm8988_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8988_i2c_id[] = {
        { "wm8988", 0 },
        { }
@@ -994,6 +1011,8 @@ static struct i2c_driver wm8988_i2c_driver = {
        },
        .probe = wm8988_i2c_probe,
        .remove = wm8988_i2c_remove,
+       .suspend = wm8988_i2c_suspend,
+       .resume = wm8988_i2c_resume,
        .id_table = wm8988_i2c_id,
 };
 #endif
@@ -1051,6 +1070,21 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8988_spi_resume(struct spi_device *spi)
+{
+       return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8988_spi_suspend NULL
+#define wm8988_spi_resume NULL
+#endif
+
 static struct spi_driver wm8988_spi_driver = {
        .driver = {
                .name   = "wm8988",
@@ -1059,6 +1093,8 @@ static struct spi_driver wm8988_spi_driver = {
        },
        .probe          = wm8988_spi_probe,
        .remove         = __devexit_p(wm8988_spi_remove),
+       .suspend        = wm8988_spi_suspend,
+       .resume         = wm8988_spi_resume,
 };
 #endif
 
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
new file mode 100644 (file)
index 0000000..e246ca0
--- /dev/null
@@ -0,0 +1,2206 @@
+/*
+ * wm8993.c -- WM8993 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@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/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/wm8993.h>
+
+#include "wm8993.h"
+
+static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = {
+       0x8993,     /* R0   - Software Reset */
+       0x0000,     /* R1   - Power Management (1) */
+       0x6000,     /* R2   - Power Management (2) */
+       0x0000,     /* R3   - Power Management (3) */
+       0x4050,     /* R4   - Audio Interface (1) */
+       0x4000,     /* R5   - Audio Interface (2) */
+       0x01C8,     /* R6   - Clocking 1 */
+       0x0000,     /* R7   - Clocking 2 */
+       0x0000,     /* R8   - Audio Interface (3) */
+       0x0040,     /* R9   - Audio Interface (4) */
+       0x0004,     /* R10  - DAC CTRL */
+       0x00C0,     /* R11  - Left DAC Digital Volume */
+       0x00C0,     /* R12  - Right DAC Digital Volume */
+       0x0000,     /* R13  - Digital Side Tone */
+       0x0300,     /* R14  - ADC CTRL */
+       0x00C0,     /* R15  - Left ADC Digital Volume */
+       0x00C0,     /* R16  - Right ADC Digital Volume */
+       0x0000,     /* R17 */
+       0x0000,     /* R18  - GPIO CTRL 1 */
+       0x0010,     /* R19  - GPIO1 */
+       0x0000,     /* R20  - IRQ_DEBOUNCE */
+       0x0000,     /* R21 */
+       0x8000,     /* R22  - GPIOCTRL 2 */
+       0x0800,     /* R23  - GPIO_POL */
+       0x008B,     /* R24  - Left Line Input 1&2 Volume */
+       0x008B,     /* R25  - Left Line Input 3&4 Volume */
+       0x008B,     /* R26  - Right Line Input 1&2 Volume */
+       0x008B,     /* R27  - Right Line Input 3&4 Volume */
+       0x006D,     /* R28  - Left Output Volume */
+       0x006D,     /* R29  - Right Output Volume */
+       0x0066,     /* R30  - Line Outputs Volume */
+       0x0020,     /* R31  - HPOUT2 Volume */
+       0x0079,     /* R32  - Left OPGA Volume */
+       0x0079,     /* R33  - Right OPGA Volume */
+       0x0003,     /* R34  - SPKMIXL Attenuation */
+       0x0003,     /* R35  - SPKMIXR Attenuation */
+       0x0011,     /* R36  - SPKOUT Mixers */
+       0x0100,     /* R37  - SPKOUT Boost */
+       0x0079,     /* R38  - Speaker Volume Left */
+       0x0079,     /* R39  - Speaker Volume Right */
+       0x0000,     /* R40  - Input Mixer2 */
+       0x0000,     /* R41  - Input Mixer3 */
+       0x0000,     /* R42  - Input Mixer4 */
+       0x0000,     /* R43  - Input Mixer5 */
+       0x0000,     /* R44  - Input Mixer6 */
+       0x0000,     /* R45  - Output Mixer1 */
+       0x0000,     /* R46  - Output Mixer2 */
+       0x0000,     /* R47  - Output Mixer3 */
+       0x0000,     /* R48  - Output Mixer4 */
+       0x0000,     /* R49  - Output Mixer5 */
+       0x0000,     /* R50  - Output Mixer6 */
+       0x0000,     /* R51  - HPOUT2 Mixer */
+       0x0000,     /* R52  - Line Mixer1 */
+       0x0000,     /* R53  - Line Mixer2 */
+       0x0000,     /* R54  - Speaker Mixer */
+       0x0000,     /* R55  - Additional Control */
+       0x0000,     /* R56  - AntiPOP1 */
+       0x0000,     /* R57  - AntiPOP2 */
+       0x0000,     /* R58  - MICBIAS */
+       0x0000,     /* R59 */
+       0x0000,     /* R60  - FLL Control 1 */
+       0x0000,     /* R61  - FLL Control 2 */
+       0x0000,     /* R62  - FLL Control 3 */
+       0x2EE0,     /* R63  - FLL Control 4 */
+       0x0002,     /* R64  - FLL Control 5 */
+       0x2287,     /* R65  - Clocking 3 */
+       0x025F,     /* R66  - Clocking 4 */
+       0x0000,     /* R67  - MW Slave Control */
+       0x0000,     /* R68 */
+       0x0002,     /* R69  - Bus Control 1 */
+       0x0000,     /* R70  - Write Sequencer 0 */
+       0x0000,     /* R71  - Write Sequencer 1 */
+       0x0000,     /* R72  - Write Sequencer 2 */
+       0x0000,     /* R73  - Write Sequencer 3 */
+       0x0000,     /* R74  - Write Sequencer 4 */
+       0x0000,     /* R75  - Write Sequencer 5 */
+       0x1F25,     /* R76  - Charge Pump 1 */
+       0x0000,     /* R77 */
+       0x0000,     /* R78 */
+       0x0000,     /* R79 */
+       0x0000,     /* R80 */
+       0x0000,     /* R81  - Class W 0 */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84  - DC Servo 0 */
+       0x054A,     /* R85  - DC Servo 1 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87  - DC Servo 3 */
+       0x0000,     /* R88  - DC Servo Readback 0 */
+       0x0000,     /* R89  - DC Servo Readback 1 */
+       0x0000,     /* R90  - DC Servo Readback 2 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92 */
+       0x0000,     /* R93 */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0100,     /* R96  - Analogue HP 0 */
+       0x0000,     /* R97 */
+       0x0000,     /* R98  - EQ1 */
+       0x000C,     /* R99  - EQ2 */
+       0x000C,     /* R100 - EQ3 */
+       0x000C,     /* R101 - EQ4 */
+       0x000C,     /* R102 - EQ5 */
+       0x000C,     /* R103 - EQ6 */
+       0x0FCA,     /* R104 - EQ7 */
+       0x0400,     /* R105 - EQ8 */
+       0x00D8,     /* R106 - EQ9 */
+       0x1EB5,     /* R107 - EQ10 */
+       0xF145,     /* R108 - EQ11 */
+       0x0B75,     /* R109 - EQ12 */
+       0x01C5,     /* R110 - EQ13 */
+       0x1C58,     /* R111 - EQ14 */
+       0xF373,     /* R112 - EQ15 */
+       0x0A54,     /* R113 - EQ16 */
+       0x0558,     /* R114 - EQ17 */
+       0x168E,     /* R115 - EQ18 */
+       0xF829,     /* R116 - EQ19 */
+       0x07AD,     /* R117 - EQ20 */
+       0x1103,     /* R118 - EQ21 */
+       0x0564,     /* R119 - EQ22 */
+       0x0559,     /* R120 - EQ23 */
+       0x4000,     /* R121 - EQ24 */
+       0x0000,     /* R122 - Digital Pulls */
+       0x0F08,     /* R123 - DRC Control 1 */
+       0x0000,     /* R124 - DRC Control 2 */
+       0x0080,     /* R125 - DRC Control 3 */
+       0x0000,     /* R126 - DRC Control 4 */
+};
+
+static struct {
+       int ratio;
+       int clk_sys_rate;
+} clk_sys_rates[] = {
+       { 64,   0 },
+       { 128,  1 },
+       { 192,  2 },
+       { 256,  3 },
+       { 384,  4 },
+       { 512,  5 },
+       { 768,  6 },
+       { 1024, 7 },
+       { 1408, 8 },
+       { 1536, 9 },
+};
+
+static struct {
+       int rate;
+       int sample_rate;
+} sample_rates[] = {
+       { 8000,  0  },
+       { 11025, 1  },
+       { 12000, 1  },
+       { 16000, 2  },
+       { 22050, 3  },
+       { 24000, 3  },
+       { 32000, 4  },
+       { 44100, 5  },
+       { 48000, 5  },
+};
+
+static struct {
+       int div; /* *10 due to .5s */
+       int bclk_div;
+} bclk_divs[] = {
+       { 10,  0  },
+       { 15,  1  },
+       { 20,  2  },
+       { 30,  3  },
+       { 40,  4  },
+       { 55,  5  },
+       { 60,  6  },
+       { 80,  7  },
+       { 110, 8  },
+       { 120, 9  },
+       { 160, 10 },
+       { 220, 11 },
+       { 240, 12 },
+       { 320, 13 },
+       { 440, 14 },
+       { 480, 15 },
+};
+
+struct wm8993_priv {
+       u16 reg_cache[WM8993_REGISTER_COUNT];
+       struct wm8993_platform_data pdata;
+       struct snd_soc_codec codec;
+       int master;
+       int sysclk_source;
+       unsigned int mclk_rate;
+       unsigned int sysclk_rate;
+       unsigned int fs;
+       unsigned int bclk;
+       int class_w_users;
+       unsigned int fll_fref;
+       unsigned int fll_fout;
+};
+
+static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+       struct i2c_msg xfer[2];
+       u16 data;
+       int ret;
+       struct i2c_client *i2c = codec->control_data;
+
+       /* Write register */
+       xfer[0].addr = i2c->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = i2c->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
+               return 0;
+       }
+
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static int wm8993_volatile(unsigned int reg)
+{
+       switch (reg) {
+       case WM8993_SOFTWARE_RESET:
+       case WM8993_DC_SERVO_0:
+       case WM8993_DC_SERVO_READBACK_0:
+       case WM8993_DC_SERVO_READBACK_1:
+       case WM8993_DC_SERVO_READBACK_2:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static unsigned int wm8993_read(struct snd_soc_codec *codec,
+                               unsigned int reg)
+{
+       u16 *reg_cache = codec->reg_cache;
+
+       BUG_ON(reg > WM8993_MAX_REGISTER);
+
+       if (wm8993_volatile(reg))
+               return wm8993_read_hw(codec, reg);
+       else
+               return reg_cache[reg];
+}
+
+static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg,
+                       unsigned int value)
+{
+       u16 *reg_cache = codec->reg_cache;
+       u8 data[3];
+       int ret;
+
+       BUG_ON(reg > WM8993_MAX_REGISTER);
+
+       /* data is
+        *   D15..D9 WM8993 register offset
+        *   D8...D0 register data
+        */
+       data[0] = reg;
+       data[1] = value >> 8;
+       data[2] = value & 0x00ff;
+
+       if (!wm8993_volatile(reg))
+               reg_cache[reg] = value;
+
+       ret = codec->hw_write(codec->control_data, data, 3);
+
+       if (ret == 3)
+               return 0;
+       if (ret < 0)
+               return ret;
+       return -EIO;
+}
+
+struct _fll_div {
+       u16 fll_fratio;
+       u16 fll_outdiv;
+       u16 fll_clk_ref_div;
+       u16 n;
+       u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+       unsigned int min;
+       unsigned int max;
+       u16 fll_fratio;
+       int ratio;
+} fll_fratios[] = {
+       {       0,    64000, 4, 16 },
+       {   64000,   128000, 3,  8 },
+       {  128000,   256000, 2,  4 },
+       {  256000,  1000000, 1,  2 },
+       { 1000000, 13500000, 0,  1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+                      unsigned int Fout)
+{
+       u64 Kpart;
+       unsigned int K, Ndiv, Nmod, target;
+       unsigned int div;
+       int i;
+
+       /* Fref must be <=13.5MHz */
+       div = 1;
+       fll_div->fll_clk_ref_div = 0;
+       while ((Fref / div) > 13500000) {
+               div *= 2;
+               fll_div->fll_clk_ref_div++;
+
+               if (div > 8) {
+                       pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+                              Fref);
+                       return -EINVAL;
+               }
+       }
+
+       pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+       /* Apply the division for our remaining calculations */
+       Fref /= div;
+
+       /* Fvco should be 90-100MHz; don't check the upper bound */
+       div = 0;
+       target = Fout * 2;
+       while (target < 90000000) {
+               div++;
+               target *= 2;
+               if (div > 7) {
+                       pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+                              Fout);
+                       return -EINVAL;
+               }
+       }
+       fll_div->fll_outdiv = div;
+
+       pr_debug("Fvco=%dHz\n", target);
+
+       /* Find an appropraite FLL_FRATIO and factor it out of the target */
+       for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+               if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+                       fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+                       target /= fll_fratios[i].ratio;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(fll_fratios)) {
+               pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+               return -EINVAL;
+       }
+
+       /* Now, calculate N.K */
+       Ndiv = target / Fref;
+
+       fll_div->n = Ndiv;
+       Nmod = target % Fref;
+       pr_debug("Nmod=%d\n", Nmod);
+
+       /* Calculate fractional part - scale up so we can round. */
+       Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+       do_div(Kpart, Fref);
+
+       K = Kpart & 0xFFFFFFFF;
+
+       if ((K % 10) >= 5)
+               K += 5;
+
+       /* Move down to proper range now rounding is done */
+       fll_div->k = K / 10;
+
+       pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+                fll_div->n, fll_div->k,
+                fll_div->fll_fratio, fll_div->fll_outdiv,
+                fll_div->fll_clk_ref_div);
+
+       return 0;
+}
+
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+                         unsigned int Fref, unsigned int Fout)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       u16 reg1, reg4, reg5;
+       struct _fll_div fll_div;
+       int ret;
+
+       /* Any change? */
+       if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout)
+               return 0;
+
+       /* Disable the FLL */
+       if (Fout == 0) {
+               dev_dbg(codec->dev, "FLL disabled\n");
+               wm8993->fll_fref = 0;
+               wm8993->fll_fout = 0;
+
+               reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+               reg1 &= ~WM8993_FLL_ENA;
+               wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+               return 0;
+       }
+
+       ret = fll_factors(&fll_div, Fref, Fout);
+       if (ret != 0)
+               return ret;
+
+       reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5);
+       reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
+
+       switch (fll_id) {
+       case WM8993_FLL_MCLK:
+               break;
+
+       case WM8993_FLL_LRCLK:
+               reg5 |= 1;
+               break;
+
+       case WM8993_FLL_BCLK:
+               reg5 |= 2;
+               break;
+
+       default:
+               dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+               return -EINVAL;
+       }
+
+       /* Any FLL configuration change requires that the FLL be
+        * disabled first. */
+       reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+       reg1 &= ~WM8993_FLL_ENA;
+       wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+       /* Apply the configuration */
+       if (fll_div.k)
+               reg1 |= WM8993_FLL_FRAC_MASK;
+       else
+               reg1 &= ~WM8993_FLL_FRAC_MASK;
+       wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+       wm8993_write(codec, WM8993_FLL_CONTROL_2,
+                    (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+                    (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+       wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+
+       reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4);
+       reg4 &= ~WM8993_FLL_N_MASK;
+       reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
+       wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4);
+
+       reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
+       reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
+       wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5);
+
+       /* Enable the FLL */
+       wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+
+       dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+       wm8993->fll_fref = Fref;
+       wm8993->fll_fout = Fout;
+
+       return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+       struct wm8993_priv *wm8993 = codec->private_data;
+       unsigned int reg;
+
+       /* This should be done on init() for bypass paths */
+       switch (wm8993->sysclk_source) {
+       case WM8993_SYSCLK_MCLK:
+               dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
+
+               reg = wm8993_read(codec, WM8993_CLOCKING_2);
+               reg &= ~WM8993_SYSCLK_SRC;
+               if (wm8993->mclk_rate > 13500000) {
+                       reg |= WM8993_MCLK_DIV;
+                       wm8993->sysclk_rate = wm8993->mclk_rate / 2;
+               } else {
+                       reg &= ~WM8993_MCLK_DIV;
+                       wm8993->sysclk_rate = wm8993->mclk_rate;
+               }
+               reg &= ~WM8993_MCLK_DIV;
+               reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
+               wm8993_write(codec, WM8993_CLOCKING_2, reg);
+               break;
+
+       case WM8993_SYSCLK_FLL:
+               dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+                       wm8993->fll_fout);
+
+               reg = wm8993_read(codec, WM8993_CLOCKING_2);
+               reg |= WM8993_SYSCLK_SRC;
+               if (wm8993->fll_fout > 13500000) {
+                       reg |= WM8993_MCLK_DIV;
+                       wm8993->sysclk_rate = wm8993->fll_fout / 2;
+               } else {
+                       reg &= ~WM8993_MCLK_DIV;
+                       wm8993->sysclk_rate = wm8993->fll_fout;
+               }
+               wm8993_write(codec, WM8993_CLOCKING_2, reg);
+               break;
+
+       default:
+               dev_err(codec->dev, "System clock not configured\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
+
+       return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, int mask)
+{
+       unsigned int reg;
+       int count = 0;
+
+       dev_dbg(codec->dev, "Waiting for DC servo...\n");
+       do {
+               count++;
+               msleep(1);
+               reg = wm8993_read(codec, WM8993_DC_SERVO_READBACK_0);
+               dev_dbg(codec->dev, "DC servo status: %x\n", reg);
+       } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+                != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
+
+       if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+           != WM8993_DCS_CAL_COMPLETE_MASK)
+               dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static const unsigned int drc_max_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+       3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(spkmix_tlv, -300, 300, 0);
+static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(7),
+       0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+       7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
+
+static const char *speaker_ref_text[] = {
+       "SPKVDD/2",
+       "VMID",
+};
+
+static const struct soc_enum speaker_ref =
+       SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
+
+static const char *speaker_mode_text[] = {
+       "Class D",
+       "Class AB",
+};
+
+static const struct soc_enum speaker_mode =
+       SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
+
+static const char *dac_deemph_text[] = {
+       "None",
+       "32kHz",
+       "44.1kHz",
+       "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+       SOC_ENUM_SINGLE(WM8993_DAC_CTRL, 4, 4, dac_deemph_text);
+
+static const char *adc_hpf_text[] = {
+       "Hi-Fi",
+       "Voice 1",
+       "Voice 2",
+       "Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+       SOC_ENUM_SINGLE(WM8993_ADC_CTRL, 5, 4, adc_hpf_text);
+
+static const char *drc_path_text[] = {
+       "ADC",
+       "DAC"
+};
+
+static const struct soc_enum drc_path =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);
+
+static const char *drc_r0_text[] = {
+       "1",
+       "1/2",
+       "1/4",
+       "1/8",
+       "1/16",
+       "0",
+};
+
+static const struct soc_enum drc_r0 =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 8, 6, drc_r0_text);
+
+static const char *drc_r1_text[] = {
+       "1",
+       "1/2",
+       "1/4",
+       "1/8",
+       "0",
+};
+
+static const struct soc_enum drc_r1 =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_4, 13, 5, drc_r1_text);
+
+static const char *drc_attack_text[] = {
+       "Reserved",
+       "181us",
+       "363us",
+       "726us",
+       "1.45ms",
+       "2.9ms",
+       "5.8ms",
+       "11.6ms",
+       "23.2ms",
+       "46.4ms",
+       "92.8ms",
+       "185.6ms",
+};
+
+static const struct soc_enum drc_attack =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 12, 12, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+       "186ms",
+       "372ms",
+       "743ms",
+       "1.49s",
+       "2.97ms",
+       "5.94ms",
+       "11.89ms",
+       "23.78ms",
+       "47.56ms",
+};
+
+static const struct soc_enum drc_decay =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 8, 9, drc_decay_text);
+
+static const char *drc_ff_text[] = {
+       "5 samples",
+       "9 samples",
+};
+
+static const struct soc_enum drc_ff =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 7, 2, drc_ff_text);
+
+static const char *drc_qr_rate_text[] = {
+       "0.725ms",
+       "1.45ms",
+       "5.8ms",
+};
+
+static const struct soc_enum drc_qr_rate =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 0, 3, drc_qr_rate_text);
+
+static const char *drc_smooth_text[] = {
+       "Low",
+       "Medium",
+       "High",
+};
+
+static const struct soc_enum drc_smooth =
+       SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 4, 3, drc_smooth_text);
+
+
+/*
+ * Update the DC servo calibration on gain changes
+ */
+static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int ret;
+
+       ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+
+       /* Only need to do this if the outputs are active */
+       if (wm8993_read(codec, WM8993_POWER_MANAGEMENT_1)
+           & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
+               snd_soc_update_bits(codec,
+                                   WM8993_DC_SERVO_0,
+                                   WM8993_DCS_TRIG_SINGLE_0 |
+                                   WM8993_DCS_TRIG_SINGLE_1,
+                                   WM8993_DCS_TRIG_SINGLE_0 |
+                                   WM8993_DCS_TRIG_SINGLE_1);
+
+       return ret;
+}
+
+static const struct snd_kcontrol_new wm8993_snd_controls[] = {
+SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+              inpga_tlv),
+SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+              inpga_tlv),
+SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+
+SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+              inpga_tlv),
+SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+              inpga_tlv),
+SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0,
+              inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0,
+              inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0,
+              inmix_tlv),
+SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0,
+              inmix_tlv),
+
+SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0,
+              inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0,
+              inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0,
+              inmix_tlv),
+SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0,
+              inmix_tlv),
+
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
+              5, 9, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8993_DRC_CONTROL_2,
+              2, 60, 1, drc_comp_threash),
+SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3,
+              11, 30, 1, drc_comp_amp),
+SOC_ENUM("DRC R0", drc_r0),
+SOC_ENUM("DRC R1", drc_r1),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1,
+              drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0,
+              drc_max_tlv),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff),
+SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0),
+SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0,
+              drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Rate", drc_qr_rate),
+SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0),
+SOC_ENUM("DRC Smoothing Hysteresis Threashold", drc_smooth),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0,
+              drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME,
+                WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME,
+                WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0,
+              dac_boost_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+
+SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1,
+              outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1,
+              outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1,
+              outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1,
+              outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1,
+              outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Right Input Volume",
+              WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Left Input Volume",
+              WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1,
+              outmix_tlv),
+
+SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume",
+              WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume",
+              WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1L Volume",
+              WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1R Volume",
+              WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume",
+              WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Left Input Volume",
+              WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Right Input Volume",
+              WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer DAC Volume",
+              WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv),
+
+SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME,
+                WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME,
+            WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME,
+            WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv),
+
+SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION,
+              5, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION,
+              4, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION,
+              3, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION,
+              2, 1, 1, spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION,
+              5, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION,
+              4, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION,
+              3, 1, 1, spkmix_tlv),
+SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION,
+              2, 1, 1, spkmix_tlv),
+
+SOC_DOUBLE_R_TLV("Speaker Mixer Volume",
+                WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION,
+                0, 3, 1, spkmixout_tlv),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+                WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+                0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Speaker Switch",
+            WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+            6, 1, 0),
+SOC_DOUBLE_R("Speaker ZC Switch",
+            WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+            7, 1, 0),
+SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0,
+              spkboost_tlv),
+SOC_ENUM("Speaker Reference", speaker_ref),
+SOC_ENUM("Speaker Mode", speaker_mode),
+
+{
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Volume",
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .tlv.p = outpga_tlv,
+       .info = snd_soc_info_volsw_2r,
+       .get = snd_soc_get_volsw_2r, .put = wm8993_put_dc_servo,
+       .private_value = (unsigned long)&(struct soc_mixer_control) {
+               .reg = WM8993_LEFT_OUTPUT_VOLUME,
+               .rreg = WM8993_RIGHT_OUTPUT_VOLUME,
+               .shift = 0, .max = 63
+       },
+},
+SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME,
+            WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME,
+            WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1),
+SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1,
+              line_tlv),
+
+SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1),
+SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1),
+SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
+              line_tlv),
+};
+
+static const struct snd_kcontrol_new wm8993_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int wm8993_earpiece_event(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *control, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 reg = wm8993_read(codec, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               reg |= WM8993_HPOUT2_IN_ENA;
+               wm8993_write(codec, WM8993_ANTIPOP1, reg);
+               udelay(50);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               wm8993_write(codec, WM8993_ANTIPOP1, reg);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+
+       return 0;
+}
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return configure_clock(codec);
+
+       case SND_SOC_DAPM_POST_PMD:
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * When used with DAC outputs only the WM8993 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources.  Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * Currently the only supported paths are the direct DAC->headphone
+ * paths (which provide minimum power consumption anyway).
+ */
+static int wm8993_class_w_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = widget->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       int ret;
+
+       /* Turn it off if we're using the main output mixer */
+       if (ucontrol->value.integer.value[0] == 0) {
+               if (wm8993->class_w_users == 0) {
+                       dev_dbg(codec->dev, "Disabling Class W\n");
+                       snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+                                           WM8993_CP_DYN_FREQ |
+                                           WM8993_CP_DYN_V,
+                                           0);
+               }
+               wm8993->class_w_users++;
+       }
+
+       /* Implement the change */
+       ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+       /* Enable it if we're using the direct DAC path */
+       if (ucontrol->value.integer.value[0] == 1) {
+               if (wm8993->class_w_users == 1) {
+                       dev_dbg(codec->dev, "Enabling Class W\n");
+                       snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+                                           WM8993_CP_DYN_FREQ |
+                                           WM8993_CP_DYN_V,
+                                           WM8993_CP_DYN_FREQ |
+                                           WM8993_CP_DYN_V);
+               }
+               wm8993->class_w_users--;
+       }
+
+       dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
+               wm8993->class_w_users);
+
+       return ret;
+}
+
+#define SOC_DAPM_ENUM_W(xname, xenum) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_enum_double, \
+       .put = wm8993_class_w_put, \
+       .private_value = (unsigned long)&xenum }
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       unsigned int reg = wm8993_read(codec, WM8993_ANALOGUE_HP_0);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+                                   WM8993_CP_ENA, WM8993_CP_ENA);
+
+               msleep(5);
+
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                   WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+                                   WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
+
+               reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
+               wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg);
+
+               /* Start the DC servo */
+               snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+                                   WM8993_DCS_ENA_CHAN_0 |
+                                   WM8993_DCS_ENA_CHAN_1 |
+                                   WM8993_DCS_TRIG_STARTUP_1 |
+                                   WM8993_DCS_TRIG_STARTUP_0,
+                                   WM8993_DCS_ENA_CHAN_0 |
+                                   WM8993_DCS_ENA_CHAN_1 |
+                                   WM8993_DCS_TRIG_STARTUP_1 |
+                                   WM8993_DCS_TRIG_STARTUP_0);
+               wait_for_dc_servo(codec, WM8993_DCS_TRIG_STARTUP_0 |
+                                 WM8993_DCS_TRIG_STARTUP_1);
+               snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+                                   WM8993_DCS_TIMER_PERIOD_01_MASK, 0xa);
+
+               reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
+                       WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
+               wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
+                        WM8993_HPOUT1L_DLY |
+                        WM8993_HPOUT1L_OUTP |
+                        WM8993_HPOUT1R_RMV_SHORT |
+                        WM8993_HPOUT1R_DLY |
+                        WM8993_HPOUT1R_OUTP);
+
+               snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+                                   WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
+               snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+                                   WM8993_DCS_ENA_CHAN_0 |
+                                   WM8993_DCS_ENA_CHAN_1, 0);
+
+               wm8993_write(codec, WM8993_ANALOGUE_HP_0, reg);
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                   WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+                                   0);
+
+               snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+                                   WM8993_CP_ENA, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new in1l_pga[] = {
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new in1r_pga[] = {
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2l_pga[] = {
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2r_pga[] = {
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinl[] = {
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinr[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new earpiece_mixer[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0),
+};
+
+static const char *hp_mux_text[] = {
+       "Mixer",
+       "DAC",
+};
+
+static const struct soc_enum hpl_enum =
+       SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+       SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+       SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+       SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
+
+static const struct snd_kcontrol_new line1_mix[] = {
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1p_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2_mix[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2p_mix[] = {
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
+SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
+
+SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
+                  in1l_pga, ARRAY_SIZE(in1l_pga)),
+SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
+                  in1r_pga, ARRAY_SIZE(in1r_pga)),
+
+SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
+                  in2l_pga, ARRAY_SIZE(in2l_pga)),
+SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
+                  in2r_pga, ARRAY_SIZE(in2r_pga)),
+
+/* Dummy widgets to represent differential paths */
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
+                  mixinl, ARRAY_SIZE(mixinl)),
+SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
+                  mixinr, ARRAY_SIZE(mixinr)),
+
+SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
+                  left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
+                  right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+                  earpiece_mixer, ARRAY_SIZE(earpiece_mixer)),
+SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0,
+                  NULL, 0, wm8993_earpiece_event,
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
+                  left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
+                  right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0,
+                  left_speaker_boost, ARRAY_SIZE(left_speaker_boost)),
+SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0,
+                  right_speaker_boost, ARRAY_SIZE(right_speaker_boost)),
+
+SND_SOC_DAPM_PGA("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0,
+                NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
+                  NULL, 0,
+                  hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+                  line1_mix, ARRAY_SIZE(line1_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+                  line2_mix, ARRAY_SIZE(line2_mix)),
+
+SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0,
+                  line1n_mix, ARRAY_SIZE(line1n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0,
+                  line1p_mix, ARRAY_SIZE(line1p_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0,
+                  line2n_mix, ARRAY_SIZE(line2n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0,
+                  line2p_mix, ARRAY_SIZE(line2p_mix)),
+
+SND_SOC_DAPM_PGA("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0,
+                NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2P"),
+SND_SOC_DAPM_OUTPUT("HPOUT2N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2N"),
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+       { "IN1L PGA", "IN1LP Switch", "IN1LP" },
+       { "IN1L PGA", "IN1LN Switch", "IN1LN" },
+
+       { "IN1R PGA", "IN1RP Switch", "IN1RP" },
+       { "IN1R PGA", "IN1RN Switch", "IN1RN" },
+
+       { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
+       { "IN2L PGA", "IN2LN Switch", "IN2LN" },
+
+       { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
+       { "IN2R PGA", "IN2RN Switch", "IN2RN" },
+
+       { "Direct Voice", NULL, "IN2LP/VXRN" },
+       { "Direct Voice", NULL, "IN2RP/VXRP" },
+
+       { "MIXINL", "IN1L Switch", "IN1L PGA" },
+       { "MIXINL", "IN2L Switch", "IN2L PGA" },
+       { "MIXINL", NULL, "Direct Voice" },
+       { "MIXINL", NULL, "IN1LP" },
+       { "MIXINL", NULL, "Left Output Mixer" },
+
+       { "MIXINR", "IN1R Switch", "IN1R PGA" },
+       { "MIXINR", "IN2R Switch", "IN2R PGA" },
+       { "MIXINR", NULL, "Direct Voice" },
+       { "MIXINR", NULL, "IN1RP" },
+       { "MIXINR", NULL, "Right Output Mixer" },
+
+       { "ADCL", NULL, "MIXINL" },
+       { "ADCL", NULL, "CLK_SYS" },
+       { "ADCL", NULL, "CLK_DSP" },
+       { "ADCR", NULL, "MIXINR" },
+       { "ADCR", NULL, "CLK_SYS" },
+       { "ADCR", NULL, "CLK_DSP" },
+
+       { "DACL", NULL, "CLK_SYS" },
+       { "DACL", NULL, "CLK_DSP" },
+       { "DACR", NULL, "CLK_SYS" },
+       { "DACR", NULL, "CLK_DSP" },
+
+       { "Left Output Mixer", "Left Input Switch", "MIXINL" },
+       { "Left Output Mixer", "Right Input Switch", "MIXINR" },
+       { "Left Output Mixer", "IN2RN Switch", "IN2RN" },
+       { "Left Output Mixer", "IN2LN Switch", "IN2LN" },
+       { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
+       { "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
+       { "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
+       { "Left Output Mixer", "DAC Switch", "DACL" },
+
+       { "Right Output Mixer", "Left Input Switch", "MIXINL" },
+       { "Right Output Mixer", "Right Input Switch", "MIXINR" },
+       { "Right Output Mixer", "IN2LN Switch", "IN2LN" },
+       { "Right Output Mixer", "IN2RN Switch", "IN2RN" },
+       { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
+       { "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
+       { "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
+       { "Right Output Mixer", "DAC Switch", "DACR" },
+
+       { "Left Output PGA", NULL, "Left Output Mixer" },
+       { "Left Output PGA", NULL, "CLK_SYS" },
+       { "Left Output PGA", NULL, "TOCLK" },
+
+       { "Right Output PGA", NULL, "Right Output Mixer" },
+       { "Right Output PGA", NULL, "CLK_SYS" },
+       { "Right Output PGA", NULL, "TOCLK" },
+
+       { "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" },
+       { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
+       { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
+
+       { "Earpiece Driver", NULL, "Earpiece Mixer" },
+       { "HPOUT2N", NULL, "Earpiece Driver" },
+       { "HPOUT2P", NULL, "Earpiece Driver" },
+
+       { "SPKL", "Input Switch", "MIXINL" },
+       { "SPKL", "IN1LP Switch", "IN1LP" },
+       { "SPKL", "Output Switch", "Left Output Mixer" },
+       { "SPKL", "DAC Switch", "DACL" },
+       { "SPKL", NULL, "CLK_SYS" },
+       { "SPKL", NULL, "TOCLK" },
+
+       { "SPKR", "Input Switch", "MIXINR" },
+       { "SPKR", "IN1RP Switch", "IN1RP" },
+       { "SPKR", "Output Switch", "Right Output Mixer" },
+       { "SPKR", "DAC Switch", "DACR" },
+       { "SPKR", NULL, "CLK_SYS" },
+       { "SPKR", NULL, "TOCLK" },
+
+       { "SPKL Boost", "Direct Voice Switch", "Direct Voice" },
+       { "SPKL Boost", "SPKL Switch", "SPKL" },
+       { "SPKL Boost", "SPKR Switch", "SPKR" },
+
+       { "SPKR Boost", "Direct Voice Switch", "Direct Voice" },
+       { "SPKR Boost", "SPKR Switch", "SPKR" },
+       { "SPKR Boost", "SPKL Switch", "SPKL" },
+
+       { "SPKL Driver", NULL, "SPKL Boost" },
+       { "SPKL Driver", NULL, "CLK_SYS" },
+
+       { "SPKR Driver", NULL, "SPKR Boost" },
+       { "SPKR Driver", NULL, "CLK_SYS" },
+
+       { "SPKOUTLP", NULL, "SPKL Driver" },
+       { "SPKOUTLN", NULL, "SPKL Driver" },
+       { "SPKOUTRP", NULL, "SPKR Driver" },
+       { "SPKOUTRN", NULL, "SPKR Driver" },
+
+       { "Left Headphone Mux", "DAC", "DACL" },
+       { "Left Headphone Mux", "Mixer", "Left Output Mixer" },
+       { "Right Headphone Mux", "DAC", "DACR" },
+       { "Right Headphone Mux", "Mixer", "Right Output Mixer" },
+
+       { "Headphone PGA", NULL, "Left Headphone Mux" },
+       { "Headphone PGA", NULL, "Right Headphone Mux" },
+       { "Headphone PGA", NULL, "CLK_SYS" },
+       { "Headphone PGA", NULL, "TOCLK" },
+
+       { "HPOUT1L", NULL, "Headphone PGA" },
+       { "HPOUT1R", NULL, "Headphone PGA" },
+
+       { "LINEOUT1N", NULL, "LINEOUT1N Driver" },
+       { "LINEOUT1P", NULL, "LINEOUT1P Driver" },
+       { "LINEOUT2N", NULL, "LINEOUT2N Driver" },
+       { "LINEOUT2P", NULL, "LINEOUT2P Driver" },
+};
+
+static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
+       { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
+       { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
+       { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+
+       { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
+       { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout1_se_routes[] = {
+       { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
+       { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+       { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+
+       { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
+       { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
+       { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
+       { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
+       { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+
+       { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
+       { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_se_routes[] = {
+       { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
+       { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+       { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+
+       { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
+       { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
+};
+
+static int wm8993_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8993_priv *wm8993 = codec->private_data;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+               /* VMID=2*40k */
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                   WM8993_VMID_SEL_MASK, 0x2);
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+                                   WM8993_TSHUT_ENA, WM8993_TSHUT_ENA);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* Bring up VMID with fast soft start */
+                       snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+                                           WM8993_STARTUP_BIAS_ENA |
+                                           WM8993_VMID_BUF_ENA |
+                                           WM8993_VMID_RAMP_MASK |
+                                           WM8993_BIAS_SRC,
+                                           WM8993_STARTUP_BIAS_ENA |
+                                           WM8993_VMID_BUF_ENA |
+                                           WM8993_VMID_RAMP_MASK |
+                                           WM8993_BIAS_SRC);
+
+                       /* If either line output is single ended we
+                        * need the VMID buffer */
+                       if (!wm8993->pdata.lineout1_diff ||
+                           !wm8993->pdata.lineout2_diff)
+                               snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+                                                WM8993_LINEOUT_VMID_BUF_ENA,
+                                                WM8993_LINEOUT_VMID_BUF_ENA);
+
+                       /* VMID=2*40k */
+                       snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                           WM8993_VMID_SEL_MASK |
+                                           WM8993_BIAS_ENA,
+                                           WM8993_BIAS_ENA | 0x2);
+                       msleep(32);
+
+                       /* Switch to normal bias */
+                       snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+                                           WM8993_BIAS_SRC |
+                                           WM8993_STARTUP_BIAS_ENA, 0);
+               }
+
+               /* VMID=2*240k */
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                   WM8993_VMID_SEL_MASK, 0x4);
+
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+                                   WM8993_TSHUT_ENA, 0);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+                                   WM8993_LINEOUT_VMID_BUF_ENA, 0);
+
+               snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+                                   WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
+                                   0);
+               break;
+       }
+
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai,
+                            int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+
+       switch (clk_id) {
+       case WM8993_SYSCLK_MCLK:
+               wm8993->mclk_rate = freq;
+       case WM8993_SYSCLK_FLL:
+               wm8993->sysclk_source = clk_id;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+       unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+
+       aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
+                 WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
+       aif4 &= ~WM8993_LRCLK_DIR;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               wm8993->master = 0;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM:
+               aif4 |= WM8993_LRCLK_DIR;
+               wm8993->master = 1;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               aif1 |= WM8993_BCLK_DIR;
+               wm8993->master = 1;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aif1 |= WM8993_BCLK_DIR;
+               aif4 |= WM8993_LRCLK_DIR;
+               wm8993->master = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
+               aif1 |= WM8993_AIF_LRCLK_INV;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif1 |= 0x18;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               aif1 |= 0x10;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aif1 |= 0x8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               /* frame inversion not valid for DSP modes */
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif1 |= WM8993_AIF_BCLK_INV;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case SND_SOC_DAIFMT_I2S:
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif1 |= WM8993_AIF_BCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       aif1 |= WM8993_AIF_LRCLK_INV;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+       wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+       return 0;
+}
+
+static int wm8993_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 wm8993_priv *wm8993 = codec->private_data;
+       int ret, i, best, best_val, cur_val;
+       unsigned int clocking1, clocking3, aif1, aif4;
+
+       clocking1 = wm8993_read(codec, WM8993_CLOCKING_1);
+       clocking1 &= ~WM8993_BCLK_DIV_MASK;
+
+       clocking3 = wm8993_read(codec, WM8993_CLOCKING_3);
+       clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
+
+       aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+       aif1 &= ~WM8993_AIF_WL_MASK;
+
+       aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+       aif4 &= ~WM8993_LRCLK_RATE_MASK;
+
+       /* What BCLK do we need? */
+       wm8993->fs = params_rate(params);
+       wm8993->bclk = 2 * wm8993->fs;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               wm8993->bclk *= 16;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               wm8993->bclk *= 20;
+               aif1 |= 0x8;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               wm8993->bclk *= 24;
+               aif1 |= 0x10;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               wm8993->bclk *= 32;
+               aif1 |= 0x18;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);
+
+       ret = configure_clock(codec);
+       if (ret != 0)
+               return ret;
+
+       /* Select nearest CLK_SYS_RATE */
+       best = 0;
+       best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio)
+                      - wm8993->fs);
+       for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+               cur_val = abs((wm8993->sysclk_rate /
+                              clk_sys_rates[i].ratio) - wm8993->fs);;
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+               clk_sys_rates[best].ratio);
+       clocking3 |= (clk_sys_rates[best].clk_sys_rate
+                     << WM8993_CLK_SYS_RATE_SHIFT);
+
+       /* SAMPLE_RATE */
+       best = 0;
+       best_val = abs(wm8993->fs - sample_rates[0].rate);
+       for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+               /* Closest match */
+               cur_val = abs(wm8993->fs - sample_rates[i].rate);
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+               sample_rates[best].rate);
+       clocking3 |= (sample_rates[best].sample_rate
+                     << WM8993_SAMPLE_RATE_SHIFT);
+
+       /* BCLK_DIV */
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+               cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div)
+                       - wm8993->bclk;
+               if (cur_val < 0) /* Table is sorted */
+                       break;
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
+       dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+               bclk_divs[best].div, wm8993->bclk);
+       clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;
+
+       /* LRCLK is a simple fraction of BCLK */
+       dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
+       aif4 |= wm8993->bclk / wm8993->fs;
+
+       wm8993_write(codec, WM8993_CLOCKING_1, clocking1);
+       wm8993_write(codec, WM8993_CLOCKING_3, clocking3);
+       wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+       wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+       /* ReTune Mobile? */
+       if (wm8993->pdata.num_retune_configs) {
+               u16 eq1 = wm8993_read(codec, WM8993_EQ1);
+               struct wm8993_retune_mobile_setting *s;
+
+               best = 0;
+               best_val = abs(wm8993->pdata.retune_configs[0].rate
+                              - wm8993->fs);
+               for (i = 0; i < wm8993->pdata.num_retune_configs; i++) {
+                       cur_val = abs(wm8993->pdata.retune_configs[i].rate
+                                     - wm8993->fs);
+                       if (cur_val < best_val) {
+                               best_val = cur_val;
+                               best = i;
+                       }
+               }
+               s = &wm8993->pdata.retune_configs[best];
+
+               dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+                       s->name, s->rate);
+
+               /* Disable EQ while we reconfigure */
+               snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
+
+               for (i = 1; i < ARRAY_SIZE(s->config); i++)
+                       wm8993_write(codec, WM8993_EQ1 + i, s->config[i]);
+
+               snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
+       }
+
+       return 0;
+}
+
+static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       unsigned int reg;
+
+       reg = wm8993_read(codec, WM8993_DAC_CTRL);
+
+       if (mute)
+               reg |= WM8993_DAC_MUTE;
+       else
+               reg &= ~WM8993_DAC_MUTE;
+
+       wm8993_write(codec, WM8993_DAC_CTRL, reg);
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops wm8993_ops = {
+       .set_sysclk = wm8993_set_sysclk,
+       .set_fmt = wm8993_set_dai_fmt,
+       .hw_params = wm8993_hw_params,
+       .digital_mute = wm8993_digital_mute,
+       .set_pll = wm8993_set_fll,
+};
+
+#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                       SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8993_dai = {
+       .name = "WM8993",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8993_RATES,
+               .formats = WM8993_FORMATS,
+       },
+       .capture = {
+                .stream_name = "Capture",
+                .channels_min = 1,
+                .channels_max = 2,
+                .rates = WM8993_RATES,
+                .formats = WM8993_FORMATS,
+        },
+       .ops = &wm8993_ops,
+       .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8993_dai);
+
+static struct snd_soc_codec *wm8993_codec;
+
+static int wm8993_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct wm8993_priv *wm8993;
+       int ret = 0;
+
+       if (!wm8993_codec) {
+               dev_err(&pdev->dev, "I2C device not yet probed\n");
+               goto err;
+       }
+
+       socdev->card->codec = wm8993_codec;
+       codec = wm8993_codec;
+       wm8993 = codec->private_data;
+
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms\n");
+               goto err;
+       }
+
+       snd_soc_add_controls(codec, wm8993_snd_controls,
+                            ARRAY_SIZE(wm8993_snd_controls));
+       if (wm8993->pdata.num_retune_configs != 0) {
+               dev_dbg(codec->dev, "Using ReTune Mobile\n");
+       } else {
+               dev_dbg(codec->dev, "No ReTune Mobile, using normal EQ\n");
+               snd_soc_add_controls(codec, wm8993_eq_controls,
+                                    ARRAY_SIZE(wm8993_eq_controls));
+       }
+
+       snd_soc_dapm_new_controls(codec, wm8993_dapm_widgets,
+                                 ARRAY_SIZE(wm8993_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
+
+       if (wm8993->pdata.lineout1_diff)
+               snd_soc_dapm_add_routes(codec,
+                                       lineout1_diff_routes,
+                                       ARRAY_SIZE(lineout1_diff_routes));
+       else
+               snd_soc_dapm_add_routes(codec,
+                                       lineout1_se_routes,
+                                       ARRAY_SIZE(lineout1_se_routes));
+
+       if (wm8993->pdata.lineout2_diff)
+               snd_soc_dapm_add_routes(codec,
+                                       lineout2_diff_routes,
+                                       ARRAY_SIZE(lineout2_diff_routes));
+       else
+               snd_soc_dapm_add_routes(codec,
+                                       lineout2_se_routes,
+                                       ARRAY_SIZE(lineout2_se_routes));
+
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+err:
+       return ret;
+}
+
+static int wm8993_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8993 = {
+       .probe =        wm8993_probe,
+       .remove =       wm8993_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);
+
+static int wm8993_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm8993_priv *wm8993;
+       struct snd_soc_codec *codec;
+       unsigned int val;
+       int ret;
+
+       if (wm8993_codec) {
+               dev_err(&i2c->dev, "A WM8993 is already registered\n");
+               return -EINVAL;
+       }
+
+       wm8993 = kzalloc(sizeof(struct wm8993_priv), GFP_KERNEL);
+       if (wm8993 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8993->codec;
+       if (i2c->dev.platform_data)
+               memcpy(&wm8993->pdata, i2c->dev.platform_data,
+                      sizeof(wm8993->pdata));
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->name = "WM8993";
+       codec->read = wm8993_read;
+       codec->write = wm8993_write;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+       codec->reg_cache = wm8993->reg_cache;
+       codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8993_set_bias_level;
+       codec->dai = &wm8993_dai;
+       codec->num_dai = 1;
+       codec->private_data = wm8993;
+
+       memcpy(wm8993->reg_cache, wm8993_reg_defaults,
+              sizeof(wm8993->reg_cache));
+
+       i2c_set_clientdata(i2c, wm8993);
+       codec->control_data = i2c;
+       wm8993_codec = codec;
+
+       codec->dev = &i2c->dev;
+
+       val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET);
+       if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
+               dev_err(codec->dev, "Invalid ID register value %x\n", val);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
+       if (ret != 0)
+               goto err;
+
+       /* By default we're using the output mixers */
+       wm8993->class_w_users = 2;
+
+       /* Latch volume update bits and default ZC on */
+       snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
+                           WM8993_IN1_VU, WM8993_IN1_VU);
+       snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME,
+                           WM8993_IN1_VU, WM8993_IN1_VU);
+       snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_3_4_VOLUME,
+                           WM8993_IN2_VU, WM8993_IN2_VU);
+       snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
+                           WM8993_IN2_VU, WM8993_IN2_VU);
+
+       snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
+                           WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
+
+       snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
+                           WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+       snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+                           WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
+                           WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
+
+       snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
+                           WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+       snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
+                           WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
+                           WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
+
+       snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
+                           WM8993_DAC_VU, WM8993_DAC_VU);
+       snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
+                           WM8993_ADC_VU, WM8993_ADC_VU);
+
+       /* Manualy manage the HPOUT sequencing for independent stereo
+        * control. */
+       snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+                           WM8993_HPOUT1_AUTO_PU, 0);
+
+       /* Use automatic clock configuration */
+       snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
+
+       if (!wm8993->pdata.lineout1_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+                                   WM8993_LINEOUT1_MODE,
+                                   WM8993_LINEOUT1_MODE);
+       if (!wm8993->pdata.lineout2_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+                                   WM8993_LINEOUT2_MODE,
+                                   WM8993_LINEOUT2_MODE);
+
+       if (wm8993->pdata.lineout1fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+       if (wm8993->pdata.lineout2fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+       /* Apply the microphone bias/detection configuration - the
+        * platform data is directly applicable to the register. */
+       snd_soc_update_bits(codec, WM8993_MICBIAS,
+                           WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+                           WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+                           wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
+                           wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
+                           wm8993->pdata.micbias1_lvl |
+                           wm8993->pdata.micbias1_lvl << 1);
+
+       ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (ret != 0)
+               goto err;
+
+       wm8993_dai.dev = codec->dev;
+
+       ret = snd_soc_register_dai(&wm8993_dai);
+       if (ret != 0)
+               goto err_bias;
+
+       ret = snd_soc_register_codec(codec);
+
+       return 0;
+
+err_bias:
+       wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err:
+       wm8993_codec = NULL;
+       kfree(wm8993);
+       return ret;
+}
+
+static int wm8993_i2c_remove(struct i2c_client *client)
+{
+       struct wm8993_priv *wm8993 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&wm8993->codec);
+       snd_soc_unregister_dai(&wm8993_dai);
+
+       wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF);
+       kfree(wm8993);
+
+       return 0;
+}
+
+static const struct i2c_device_id wm8993_i2c_id[] = {
+       { "wm8993", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
+
+static struct i2c_driver wm8993_i2c_driver = {
+       .driver = {
+               .name = "WM8993",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm8993_i2c_probe,
+       .remove = wm8993_i2c_remove,
+       .id_table = wm8993_i2c_id,
+};
+
+
+static int __init wm8993_modinit(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&wm8993_i2c_driver);
+       if (ret != 0)
+               pr_err("WM8993: Unable to register I2C driver: %d\n", ret);
+
+       return ret;
+}
+module_init(wm8993_modinit);
+
+static void __exit wm8993_exit(void)
+{
+       i2c_del_driver(&wm8993_i2c_driver);
+}
+module_exit(wm8993_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8993 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h
new file mode 100644 (file)
index 0000000..30e71ca
--- /dev/null
@@ -0,0 +1,2132 @@
+#ifndef WM8993_H
+#define WM8993_H
+
+extern struct snd_soc_dai wm8993_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8993;
+
+#define WM8993_SYSCLK_MCLK     1
+#define WM8993_SYSCLK_FLL      2
+
+#define WM8993_FLL_MCLK  1
+#define WM8993_FLL_BCLK  2
+#define WM8993_FLL_LRCLK 3
+
+/*
+ * Register values.
+ */
+#define WM8993_SOFTWARE_RESET                   0x00
+#define WM8993_POWER_MANAGEMENT_1               0x01
+#define WM8993_POWER_MANAGEMENT_2               0x02
+#define WM8993_POWER_MANAGEMENT_3               0x03
+#define WM8993_AUDIO_INTERFACE_1                0x04
+#define WM8993_AUDIO_INTERFACE_2                0x05
+#define WM8993_CLOCKING_1                       0x06
+#define WM8993_CLOCKING_2                       0x07
+#define WM8993_AUDIO_INTERFACE_3                0x08
+#define WM8993_AUDIO_INTERFACE_4                0x09
+#define WM8993_DAC_CTRL                         0x0A
+#define WM8993_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8993_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8993_DIGITAL_SIDE_TONE                0x0D
+#define WM8993_ADC_CTRL                         0x0E
+#define WM8993_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8993_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8993_GPIO_CTRL_1                      0x12
+#define WM8993_GPIO1                            0x13
+#define WM8993_IRQ_DEBOUNCE                     0x14
+#define WM8993_GPIOCTRL_2                       0x16
+#define WM8993_GPIO_POL                         0x17
+#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8993_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8993_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8993_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8993_HPOUT2_VOLUME                    0x1F
+#define WM8993_LEFT_OPGA_VOLUME                 0x20
+#define WM8993_RIGHT_OPGA_VOLUME                0x21
+#define WM8993_SPKMIXL_ATTENUATION              0x22
+#define WM8993_SPKMIXR_ATTENUATION              0x23
+#define WM8993_SPKOUT_MIXERS                    0x24
+#define WM8993_SPKOUT_BOOST                     0x25
+#define WM8993_SPEAKER_VOLUME_LEFT              0x26
+#define WM8993_SPEAKER_VOLUME_RIGHT             0x27
+#define WM8993_INPUT_MIXER2                     0x28
+#define WM8993_INPUT_MIXER3                     0x29
+#define WM8993_INPUT_MIXER4                     0x2A
+#define WM8993_INPUT_MIXER5                     0x2B
+#define WM8993_INPUT_MIXER6                     0x2C
+#define WM8993_OUTPUT_MIXER1                    0x2D
+#define WM8993_OUTPUT_MIXER2                    0x2E
+#define WM8993_OUTPUT_MIXER3                    0x2F
+#define WM8993_OUTPUT_MIXER4                    0x30
+#define WM8993_OUTPUT_MIXER5                    0x31
+#define WM8993_OUTPUT_MIXER6                    0x32
+#define WM8993_HPOUT2_MIXER                     0x33
+#define WM8993_LINE_MIXER1                      0x34
+#define WM8993_LINE_MIXER2                      0x35
+#define WM8993_SPEAKER_MIXER                    0x36
+#define WM8993_ADDITIONAL_CONTROL               0x37
+#define WM8993_ANTIPOP1                         0x38
+#define WM8993_ANTIPOP2                         0x39
+#define WM8993_MICBIAS                          0x3A
+#define WM8993_FLL_CONTROL_1                    0x3C
+#define WM8993_FLL_CONTROL_2                    0x3D
+#define WM8993_FLL_CONTROL_3                    0x3E
+#define WM8993_FLL_CONTROL_4                    0x3F
+#define WM8993_FLL_CONTROL_5                    0x40
+#define WM8993_CLOCKING_3                       0x41
+#define WM8993_CLOCKING_4                       0x42
+#define WM8993_MW_SLAVE_CONTROL                 0x43
+#define WM8993_BUS_CONTROL_1                    0x45
+#define WM8993_WRITE_SEQUENCER_0                0x46
+#define WM8993_WRITE_SEQUENCER_1                0x47
+#define WM8993_WRITE_SEQUENCER_2                0x48
+#define WM8993_WRITE_SEQUENCER_3                0x49
+#define WM8993_WRITE_SEQUENCER_4                0x4A
+#define WM8993_WRITE_SEQUENCER_5                0x4B
+#define WM8993_CHARGE_PUMP_1                    0x4C
+#define WM8993_CLASS_W_0                        0x51
+#define WM8993_DC_SERVO_0                       0x54
+#define WM8993_DC_SERVO_1                       0x55
+#define WM8993_DC_SERVO_3                       0x57
+#define WM8993_DC_SERVO_READBACK_0              0x58
+#define WM8993_DC_SERVO_READBACK_1              0x59
+#define WM8993_DC_SERVO_READBACK_2              0x5A
+#define WM8993_ANALOGUE_HP_0                    0x60
+#define WM8993_EQ1                              0x62
+#define WM8993_EQ2                              0x63
+#define WM8993_EQ3                              0x64
+#define WM8993_EQ4                              0x65
+#define WM8993_EQ5                              0x66
+#define WM8993_EQ6                              0x67
+#define WM8993_EQ7                              0x68
+#define WM8993_EQ8                              0x69
+#define WM8993_EQ9                              0x6A
+#define WM8993_EQ10                             0x6B
+#define WM8993_EQ11                             0x6C
+#define WM8993_EQ12                             0x6D
+#define WM8993_EQ13                             0x6E
+#define WM8993_EQ14                             0x6F
+#define WM8993_EQ15                             0x70
+#define WM8993_EQ16                             0x71
+#define WM8993_EQ17                             0x72
+#define WM8993_EQ18                             0x73
+#define WM8993_EQ19                             0x74
+#define WM8993_EQ20                             0x75
+#define WM8993_EQ21                             0x76
+#define WM8993_EQ22                             0x77
+#define WM8993_EQ23                             0x78
+#define WM8993_EQ24                             0x79
+#define WM8993_DIGITAL_PULLS                    0x7A
+#define WM8993_DRC_CONTROL_1                    0x7B
+#define WM8993_DRC_CONTROL_2                    0x7C
+#define WM8993_DRC_CONTROL_3                    0x7D
+#define WM8993_DRC_CONTROL_4                    0x7E
+
+#define WM8993_REGISTER_COUNT                   0x7F
+#define WM8993_MAX_REGISTER                     0x7E
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8993_SW_RESET_MASK                    0xFFFF  /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_SHIFT                        0  /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_WIDTH                       16  /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8993_SPKOUTR_ENA                      0x2000  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_MASK                 0x2000  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_SHIFT                    13  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_WIDTH                     1  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTL_ENA                      0x1000  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_MASK                 0x1000  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_SHIFT                    12  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_WIDTH                     1  /* SPKOUTL_ENA */
+#define WM8993_HPOUT2_ENA                       0x0800  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_MASK                  0x0800  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_SHIFT                     11  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_WIDTH                      1  /* HPOUT2_ENA */
+#define WM8993_HPOUT1L_ENA                      0x0200  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_MASK                 0x0200  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_SHIFT                     9  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_WIDTH                     1  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1R_ENA                      0x0100  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_MASK                 0x0100  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_SHIFT                     8  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_WIDTH                     1  /* HPOUT1R_ENA */
+#define WM8993_MICB2_ENA                        0x0020  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_MASK                   0x0020  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_SHIFT                       5  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_WIDTH                       1  /* MICB2_ENA */
+#define WM8993_MICB1_ENA                        0x0010  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_MASK                   0x0010  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_SHIFT                       4  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_WIDTH                       1  /* MICB1_ENA */
+#define WM8993_VMID_SEL_MASK                    0x0006  /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_SHIFT                        1  /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_WIDTH                        2  /* VMID_SEL - [2:1] */
+#define WM8993_BIAS_ENA                         0x0001  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8993_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_MASK                   0x4000  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_SHIFT                      14  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM8993_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_MASK                 0x2000  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_SHIFT                    13  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_WIDTH                     1  /* TSHUT_OPDIS */
+#define WM8993_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_MASK                   0x0800  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_SHIFT                      11  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8993_MIXINL_ENA                       0x0200  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_MASK                  0x0200  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_SHIFT                      9  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_WIDTH                      1  /* MIXINL_ENA */
+#define WM8993_MIXINR_ENA                       0x0100  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_MASK                  0x0100  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_SHIFT                      8  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_WIDTH                      1  /* MIXINR_ENA */
+#define WM8993_IN2L_ENA                         0x0080  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_MASK                    0x0080  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_SHIFT                        7  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_WIDTH                        1  /* IN2L_ENA */
+#define WM8993_IN1L_ENA                         0x0040  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_MASK                    0x0040  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_SHIFT                        6  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_WIDTH                        1  /* IN1L_ENA */
+#define WM8993_IN2R_ENA                         0x0020  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_MASK                    0x0020  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_SHIFT                        5  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_WIDTH                        1  /* IN2R_ENA */
+#define WM8993_IN1R_ENA                         0x0010  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_MASK                    0x0010  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_SHIFT                        4  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_WIDTH                        1  /* IN1R_ENA */
+#define WM8993_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8993_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8993_LINEOUT1N_ENA                    0x2000  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_MASK               0x2000  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_SHIFT                  13  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_WIDTH                   1  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1P_ENA                    0x1000  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_MASK               0x1000  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_SHIFT                  12  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_WIDTH                   1  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT2N_ENA                    0x0800  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_MASK               0x0800  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_SHIFT                  11  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_WIDTH                   1  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2P_ENA                    0x0400  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_MASK               0x0400  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_SHIFT                  10  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_WIDTH                   1  /* LINEOUT2P_ENA */
+#define WM8993_SPKRVOL_ENA                      0x0200  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_MASK                 0x0200  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_SHIFT                     9  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_WIDTH                     1  /* SPKRVOL_ENA */
+#define WM8993_SPKLVOL_ENA                      0x0100  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_MASK                 0x0100  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_SHIFT                     8  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_WIDTH                     1  /* SPKLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA                   0x0080  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_MASK              0x0080  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_SHIFT                  7  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_WIDTH                  1  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA                   0x0040  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_MASK              0x0040  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_SHIFT                  6  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_WIDTH                  1  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTL_ENA                      0x0020  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_MASK                 0x0020  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_SHIFT                     5  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_WIDTH                     1  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTR_ENA                      0x0010  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_MASK                 0x0010  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_SHIFT                     4  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_WIDTH                     1  /* MIXOUTR_ENA */
+#define WM8993_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8993_DACL_ENA_MASK                    0x0002  /* DACL_ENA */
+#define WM8993_DACL_ENA_SHIFT                        1  /* DACL_ENA */
+#define WM8993_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8993_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8993_DACR_ENA_MASK                    0x0001  /* DACR_ENA */
+#define WM8993_DACR_ENA_SHIFT                        0  /* DACR_ENA */
+#define WM8993_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8993_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_MASK                 0x8000  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_SHIFT                    15  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8993_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_MASK                 0x4000  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_SHIFT                    14  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8993_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_MASK                  0x2000  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_SHIFT                     13  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_MASK             0x1000  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_SHIFT                12  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8993_BCLK_DIR                         0x0200  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_MASK                    0x0200  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_SHIFT                        9  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM8993_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_MASK                0x0100  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_SHIFT                    8  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8993_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_MASK               0x0080  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_SHIFT                   7  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8993_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_SHIFT                          5  /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_WIDTH                          2  /* AIF_WL - [6:5] */
+#define WM8993_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_SHIFT                         3  /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_WIDTH                         2  /* AIF_FMT - [4:3] */
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8993_AIFDACL_SRC                      0x8000  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_MASK                 0x8000  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_SHIFT                    15  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */
+#define WM8993_AIFDACR_SRC                      0x4000  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_MASK                 0x4000  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_SHIFT                    14  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */
+#define WM8993_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8993_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_SHIFT                      10  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8993_DAC_COMP_MASK                    0x0010  /* DAC_COMP */
+#define WM8993_DAC_COMP_SHIFT                        4  /* DAC_COMP */
+#define WM8993_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8993_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_MASK                0x0008  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_SHIFT                    3  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+#define WM8993_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8993_ADC_COMP_MASK                    0x0004  /* ADC_COMP */
+#define WM8993_ADC_COMP_SHIFT                        2  /* ADC_COMP */
+#define WM8993_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8993_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_MASK                0x0002  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_SHIFT                    1  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8993_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8993_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8993_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8993_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM8993_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_MASK                  0x8000  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_SHIFT                     15  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8993_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_MASK                   0x4000  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_SHIFT                      14  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+#define WM8993_OPCLK_DIV_MASK                   0x1E00  /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_SHIFT                       9  /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_WIDTH                       4  /* OPCLK_DIV - [12:9] */
+#define WM8993_DCLK_DIV_MASK                    0x01C0  /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_SHIFT                        6  /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_WIDTH                        3  /* DCLK_DIV - [8:6] */
+#define WM8993_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_SHIFT                        1  /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_WIDTH                        4  /* BCLK_DIV - [4:1] */
+
+/*
+ * R7 (0x07) - Clocking 2
+ */
+#define WM8993_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_MASK                    0x8000  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_SHIFT                       15  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_WIDTH                        1  /* MCLK_SRC */
+#define WM8993_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8993_MCLK_DIV                         0x1000  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_MASK                    0x1000  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_SHIFT                       12  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_WIDTH                        1  /* MCLK_DIV */
+#define WM8993_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8993_MCLK_INV_MASK                    0x0400  /* MCLK_INV */
+#define WM8993_MCLK_INV_SHIFT                       10  /* MCLK_INV */
+#define WM8993_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8993_ADC_DIV_MASK                     0x00E0  /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_SHIFT                         5  /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_WIDTH                         3  /* ADC_DIV - [7:5] */
+#define WM8993_DAC_DIV_MASK                     0x001C  /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_SHIFT                         2  /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_WIDTH                         3  /* DAC_DIV - [4:2] */
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8993_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_MASK                   0x8000  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_SHIFT                      15  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_WIDTH                       1  /* AIF_MSTR1 */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8993_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_MASK                    0x2000  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_SHIFT                       13  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8993_LRCLK_DIR                        0x0800  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_MASK                   0x0800  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_SHIFT                      11  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM8993_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8993_DAC_OSR128                       0x2000  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_MASK                  0x2000  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_SHIFT                     13  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+#define WM8993_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8993_DAC_MONO_MASK                    0x0200  /* DAC_MONO */
+#define WM8993_DAC_MONO_SHIFT                        9  /* DAC_MONO */
+#define WM8993_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8993_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_MASK                 0x0100  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_SHIFT                     8  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8993_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_MASK                0x0080  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_SHIFT                    7  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8993_DAC_UNMUTE_RAMP                  0x0040  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_MASK             0x0040  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_SHIFT                 6  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_WIDTH                 1  /* DAC_UNMUTE_RAMP */
+#define WM8993_DEEMPH_MASK                      0x0030  /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_SHIFT                          4  /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_WIDTH                          2  /* DEEMPH - [5:4] */
+#define WM8993_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_MASK                    0x0004  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_SHIFT                        2  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8993_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_MASK                 0x0002  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_SHIFT                     1  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8993_DACR_DATINV                      0x0001  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_MASK                 0x0001  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_SHIFT                     0  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8993_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8993_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8993_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8993_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8993_ADCL_DAC_SVOL_MASK               0x1E00  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_SHIFT                   9  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCR_DAC_SVOL_MASK               0x01E0  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_SHIFT                   5  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8993_ADC_OSR128                       0x0200  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_MASK                  0x0200  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_SHIFT                      9  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */
+#define WM8993_ADC_HPF                          0x0100  /* ADC_HPF */
+#define WM8993_ADC_HPF_MASK                     0x0100  /* ADC_HPF */
+#define WM8993_ADC_HPF_SHIFT                         8  /* ADC_HPF */
+#define WM8993_ADC_HPF_WIDTH                         1  /* ADC_HPF */
+#define WM8993_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8993_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8993_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8993_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8993_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8993_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8993_JD2_SC_EINT                      0x8000  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_MASK                 0x8000  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_SHIFT                    15  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_WIDTH                     1  /* JD2_SC_EINT */
+#define WM8993_JD2_EINT                         0x4000  /* JD2_EINT */
+#define WM8993_JD2_EINT_MASK                    0x4000  /* JD2_EINT */
+#define WM8993_JD2_EINT_SHIFT                       14  /* JD2_EINT */
+#define WM8993_JD2_EINT_WIDTH                        1  /* JD2_EINT */
+#define WM8993_WSEQ_EINT                        0x2000  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_MASK                   0x2000  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_SHIFT                      13  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_WIDTH                       1  /* WSEQ_EINT */
+#define WM8993_IRQ                              0x1000  /* IRQ */
+#define WM8993_IRQ_MASK                         0x1000  /* IRQ */
+#define WM8993_IRQ_SHIFT                            12  /* IRQ */
+#define WM8993_IRQ_WIDTH                             1  /* IRQ */
+#define WM8993_TEMPOK_EINT                      0x0800  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_MASK                 0x0800  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_SHIFT                    11  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_WIDTH                     1  /* TEMPOK_EINT */
+#define WM8993_JD1_SC_EINT                      0x0400  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_MASK                 0x0400  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_SHIFT                    10  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_WIDTH                     1  /* JD1_SC_EINT */
+#define WM8993_JD1_EINT                         0x0200  /* JD1_EINT */
+#define WM8993_JD1_EINT_MASK                    0x0200  /* JD1_EINT */
+#define WM8993_JD1_EINT_SHIFT                        9  /* JD1_EINT */
+#define WM8993_JD1_EINT_WIDTH                        1  /* JD1_EINT */
+#define WM8993_FLL_LOCK_EINT                    0x0100  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_MASK               0x0100  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_SHIFT                   8  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_WIDTH                   1  /* FLL_LOCK_EINT */
+#define WM8993_GPI8_EINT                        0x0080  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_MASK                   0x0080  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_SHIFT                       7  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_WIDTH                       1  /* GPI8_EINT */
+#define WM8993_GPI7_EINT                        0x0040  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_MASK                   0x0040  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_SHIFT                       6  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_WIDTH                       1  /* GPI7_EINT */
+#define WM8993_GPIO1_EINT                       0x0001  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_MASK                  0x0001  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_SHIFT                      0  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_WIDTH                      1  /* GPIO1_EINT */
+
+/*
+ * R19 (0x13) - GPIO1
+ */
+#define WM8993_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8993_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8993_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - IRQ_DEBOUNCE
+ */
+#define WM8993_JD2_SC_DB                        0x8000  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_MASK                   0x8000  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_SHIFT                      15  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_WIDTH                       1  /* JD2_SC_DB */
+#define WM8993_JD2_DB                           0x4000  /* JD2_DB */
+#define WM8993_JD2_DB_MASK                      0x4000  /* JD2_DB */
+#define WM8993_JD2_DB_SHIFT                         14  /* JD2_DB */
+#define WM8993_JD2_DB_WIDTH                          1  /* JD2_DB */
+#define WM8993_WSEQ_DB                          0x2000  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_MASK                     0x2000  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_SHIFT                        13  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_WIDTH                         1  /* WSEQ_DB */
+#define WM8993_TEMPOK_DB                        0x0800  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_MASK                   0x0800  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_SHIFT                      11  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_WIDTH                       1  /* TEMPOK_DB */
+#define WM8993_JD1_SC_DB                        0x0400  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_MASK                   0x0400  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_SHIFT                      10  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_WIDTH                       1  /* JD1_SC_DB */
+#define WM8993_JD1_DB                           0x0200  /* JD1_DB */
+#define WM8993_JD1_DB_MASK                      0x0200  /* JD1_DB */
+#define WM8993_JD1_DB_SHIFT                          9  /* JD1_DB */
+#define WM8993_JD1_DB_WIDTH                          1  /* JD1_DB */
+#define WM8993_FLL_LOCK_DB                      0x0100  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_MASK                 0x0100  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_SHIFT                     8  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_WIDTH                     1  /* FLL_LOCK_DB */
+#define WM8993_GPI8_DB                          0x0080  /* GPI8_DB */
+#define WM8993_GPI8_DB_MASK                     0x0080  /* GPI8_DB */
+#define WM8993_GPI8_DB_SHIFT                         7  /* GPI8_DB */
+#define WM8993_GPI8_DB_WIDTH                         1  /* GPI8_DB */
+#define WM8993_GPI7_DB                          0x0008  /* GPI7_DB */
+#define WM8993_GPI7_DB_MASK                     0x0008  /* GPI7_DB */
+#define WM8993_GPI7_DB_SHIFT                         3  /* GPI7_DB */
+#define WM8993_GPI7_DB_WIDTH                         1  /* GPI7_DB */
+#define WM8993_GPIO1_DB                         0x0001  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_MASK                    0x0001  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_SHIFT                        0  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_WIDTH                        1  /* GPIO1_DB */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8993_IM_JD2_EINT                      0x2000  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_MASK                 0x2000  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_SHIFT                    13  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_WIDTH                     1  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_SC_EINT                   0x1000  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_MASK              0x1000  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_SHIFT                 12  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_WIDTH                  1  /* IM_JD2_SC_EINT */
+#define WM8993_IM_TEMPOK_EINT                   0x0800  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_MASK              0x0800  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_SHIFT                 11  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_WIDTH                  1  /* IM_TEMPOK_EINT */
+#define WM8993_IM_JD1_SC_EINT                   0x0400  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_MASK              0x0400  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_SHIFT                 10  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_WIDTH                  1  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_EINT                      0x0200  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_MASK                 0x0200  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_SHIFT                     9  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_WIDTH                     1  /* IM_JD1_EINT */
+#define WM8993_IM_FLL_LOCK_EINT                 0x0100  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_MASK            0x0100  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_SHIFT                8  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_WIDTH                1  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_GPI8_EINT                     0x0040  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_MASK                0x0040  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_SHIFT                    6  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_WIDTH                    1  /* IM_GPI8_EINT */
+#define WM8993_IM_GPIO1_EINT                    0x0020  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_MASK               0x0020  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_SHIFT                   5  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_WIDTH                   1  /* IM_GPIO1_EINT */
+#define WM8993_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_MASK                    0x0010  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_SHIFT                        4  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8993_IM_GPI7_EINT                     0x0004  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_MASK                0x0004  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_SHIFT                    2  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_WIDTH                    1  /* IM_GPI7_EINT */
+#define WM8993_IM_WSEQ_EINT                     0x0002  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_MASK                0x0002  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_SHIFT                    1  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_WIDTH                    1  /* IM_WSEQ_EINT */
+#define WM8993_GPI7_ENA                         0x0001  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_MASK                    0x0001  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_SHIFT                        0  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8993_JD2_SC_POL                       0x8000  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_MASK                  0x8000  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_SHIFT                     15  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_WIDTH                      1  /* JD2_SC_POL */
+#define WM8993_JD2_POL                          0x4000  /* JD2_POL */
+#define WM8993_JD2_POL_MASK                     0x4000  /* JD2_POL */
+#define WM8993_JD2_POL_SHIFT                        14  /* JD2_POL */
+#define WM8993_JD2_POL_WIDTH                         1  /* JD2_POL */
+#define WM8993_WSEQ_POL                         0x2000  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_MASK                    0x2000  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_SHIFT                       13  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_WIDTH                        1  /* WSEQ_POL */
+#define WM8993_IRQ_POL                          0x1000  /* IRQ_POL */
+#define WM8993_IRQ_POL_MASK                     0x1000  /* IRQ_POL */
+#define WM8993_IRQ_POL_SHIFT                        12  /* IRQ_POL */
+#define WM8993_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+#define WM8993_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_MASK                  0x0800  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_SHIFT                     11  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_WIDTH                      1  /* TEMPOK_POL */
+#define WM8993_JD1_SC_POL                       0x0400  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_MASK                  0x0400  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_SHIFT                     10  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_WIDTH                      1  /* JD1_SC_POL */
+#define WM8993_JD1_POL                          0x0200  /* JD1_POL */
+#define WM8993_JD1_POL_MASK                     0x0200  /* JD1_POL */
+#define WM8993_JD1_POL_SHIFT                         9  /* JD1_POL */
+#define WM8993_JD1_POL_WIDTH                         1  /* JD1_POL */
+#define WM8993_FLL_LOCK_POL                     0x0100  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_MASK                0x0100  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_SHIFT                    8  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_WIDTH                    1  /* FLL_LOCK_POL */
+#define WM8993_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8993_GPI8_POL_MASK                    0x0080  /* GPI8_POL */
+#define WM8993_GPI8_POL_SHIFT                        7  /* GPI8_POL */
+#define WM8993_GPI8_POL_WIDTH                        1  /* GPI8_POL */
+#define WM8993_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8993_GPI7_POL_MASK                    0x0040  /* GPI7_POL */
+#define WM8993_GPI7_POL_SHIFT                        6  /* GPI7_POL */
+#define WM8993_GPI7_POL_WIDTH                        1  /* GPI7_POL */
+#define WM8993_GPIO1_POL                        0x0001  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_MASK                   0x0001  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_SHIFT                       0  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_WIDTH                       1  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU                           0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_MASK                      0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT                          8  /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH                          1  /* IN1_VU */
+#define WM8993_IN1L_MUTE                        0x0080  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_MASK                   0x0080  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_SHIFT                       7  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_WIDTH                       1  /* IN1L_MUTE */
+#define WM8993_IN1L_ZC                          0x0040  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_MASK                     0x0040  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_SHIFT                         6  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_WIDTH                         1  /* IN1L_ZC */
+#define WM8993_IN1L_VOL_MASK                    0x001F  /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_SHIFT                        0  /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_WIDTH                        5  /* IN1L_VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU                           0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_MASK                      0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT                          8  /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH                          1  /* IN2_VU */
+#define WM8993_IN2L_MUTE                        0x0080  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_MASK                   0x0080  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_SHIFT                       7  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_WIDTH                       1  /* IN2L_MUTE */
+#define WM8993_IN2L_ZC                          0x0040  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_MASK                     0x0040  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_SHIFT                         6  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_WIDTH                         1  /* IN2L_ZC */
+#define WM8993_IN2L_VOL_MASK                    0x001F  /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_SHIFT                        0  /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_WIDTH                        5  /* IN2L_VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU                           0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_MASK                      0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT                          8  /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH                          1  /* IN1_VU */
+#define WM8993_IN1R_MUTE                        0x0080  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_MASK                   0x0080  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_SHIFT                       7  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_WIDTH                       1  /* IN1R_MUTE */
+#define WM8993_IN1R_ZC                          0x0040  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_MASK                     0x0040  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_SHIFT                         6  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_WIDTH                         1  /* IN1R_ZC */
+#define WM8993_IN1R_VOL_MASK                    0x001F  /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_SHIFT                        0  /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_WIDTH                        5  /* IN1R_VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU                           0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_MASK                      0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT                          8  /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH                          1  /* IN2_VU */
+#define WM8993_IN2R_MUTE                        0x0080  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_MASK                   0x0080  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_SHIFT                       7  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_WIDTH                       1  /* IN2R_MUTE */
+#define WM8993_IN2R_ZC                          0x0040  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_MASK                     0x0040  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_SHIFT                         6  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_WIDTH                         1  /* IN2R_ZC */
+#define WM8993_IN2R_VOL_MASK                    0x001F  /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_SHIFT                        0  /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_WIDTH                        5  /* IN2R_VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8993_HPOUT1_VU                        0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK                   0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT                       8  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH                       1  /* HPOUT1_VU */
+#define WM8993_HPOUT1L_ZC                       0x0080  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_MASK                  0x0080  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_SHIFT                      7  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_WIDTH                      1  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_MUTE_N                   0x0040  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_MASK              0x0040  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_SHIFT                  6  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_WIDTH                  1  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_VOL_MASK                 0x003F  /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_SHIFT                     0  /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_WIDTH                     6  /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8993_HPOUT1_VU                        0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK                   0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT                       8  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH                       1  /* HPOUT1_VU */
+#define WM8993_HPOUT1R_ZC                       0x0080  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_MASK                  0x0080  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_SHIFT                      7  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_WIDTH                      1  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_MUTE_N                   0x0040  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_MASK              0x0040  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_SHIFT                  6  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_WIDTH                  1  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_VOL_MASK                 0x003F  /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_SHIFT                     0  /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_WIDTH                     6  /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8993_LINEOUT1N_MUTE                   0x0040  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_MASK              0x0040  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_SHIFT                  6  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_WIDTH                  1  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1P_MUTE                   0x0020  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_MASK              0x0020  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_SHIFT                  5  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_WIDTH                  1  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1_VOL                     0x0010  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_MASK                0x0010  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_SHIFT                    4  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_WIDTH                    1  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT2N_MUTE                   0x0004  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_MASK              0x0004  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_SHIFT                  2  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_WIDTH                  1  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2P_MUTE                   0x0002  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_MASK              0x0002  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_SHIFT                  1  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_WIDTH                  1  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2_VOL                     0x0001  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_MASK                0x0001  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_SHIFT                    0  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_WIDTH                    1  /* LINEOUT2_VOL */
+
+/*
+ * R31 (0x1F) - HPOUT2 Volume
+ */
+#define WM8993_HPOUT2_MUTE                      0x0020  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_MASK                 0x0020  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_SHIFT                     5  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_WIDTH                     1  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_VOL                       0x0010  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_MASK                  0x0010  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_SHIFT                      4  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_WIDTH                      1  /* HPOUT2_VOL */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8993_MIXOUT_VU                        0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK                   0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT                       8  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH                       1  /* MIXOUT_VU */
+#define WM8993_MIXOUTL_ZC                       0x0080  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_MASK                  0x0080  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_SHIFT                      7  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_WIDTH                      1  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_MUTE_N                   0x0040  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_MASK              0x0040  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_SHIFT                  6  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_WIDTH                  1  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_VOL_MASK                 0x003F  /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_SHIFT                     0  /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_WIDTH                     6  /* MIXOUTL_VOL - [5:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8993_MIXOUT_VU                        0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK                   0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT                       8  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH                       1  /* MIXOUT_VU */
+#define WM8993_MIXOUTR_ZC                       0x0080  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_MASK                  0x0080  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_SHIFT                      7  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_WIDTH                      1  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_MUTE_N                   0x0040  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_MASK              0x0040  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_SHIFT                  6  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_WIDTH                  1  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_VOL_MASK                 0x003F  /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_SHIFT                     0  /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_WIDTH                     6  /* MIXOUTR_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM8993_MIXINL_SPKMIXL_VOL               0x0020  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_MASK          0x0020  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT              5  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH              1  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL                0x0010  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_MASK           0x0010  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT               4  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH               1  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL              0x0008  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK         0x0008  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT             3  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH             1  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL                 0x0004  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_MASK            0x0004  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_SHIFT                2  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_WIDTH                1  /* DACL_SPKMIXL_VOL */
+#define WM8993_SPKMIXL_VOL_MASK                 0x0003  /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_SHIFT                     0  /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_WIDTH                     2  /* SPKMIXL_VOL - [1:0] */
+
+/*
+ * R35 (0x23) - SPKMIXR Attenuation
+ */
+#define WM8993_SPKOUT_CLASSAB_MODE              0x0100  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_MASK         0x0100  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT             8  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH             1  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_MIXINR_SPKMIXR_VOL               0x0020  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_MASK          0x0020  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT              5  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH              1  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL                0x0010  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_MASK           0x0010  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT               4  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH               1  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL              0x0008  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK         0x0008  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT             3  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH             1  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL                 0x0004  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_MASK            0x0004  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_SHIFT                2  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_WIDTH                1  /* DACR_SPKMIXR_VOL */
+#define WM8993_SPKMIXR_VOL_MASK                 0x0003  /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_SHIFT                     0  /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_WIDTH                     2  /* SPKMIXR_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM8993_VRX_TO_SPKOUTL                   0x0020  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_MASK              0x0020  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_SHIFT                  5  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_WIDTH                  1  /* VRX_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL               0x0010  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_MASK          0x0010  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT              4  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH              1  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL               0x0008  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_MASK          0x0008  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT              3  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH              1  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTR                   0x0004  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_MASK              0x0004  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_SHIFT                  2  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_WIDTH                  1  /* VRX_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR               0x0002  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_MASK          0x0002  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT              1  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH              1  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR               0x0001  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_MASK          0x0001  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT              0  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH              1  /* SPKMIXR_TO_SPKOUTR */
+
+/*
+ * R37 (0x25) - SPKOUT Boost
+ */
+#define WM8993_SPKOUTL_BOOST_MASK               0x0038  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_SHIFT                   3  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_WIDTH                   3  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTR_BOOST_MASK               0x0007  /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_SHIFT                   0  /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_WIDTH                   3  /* SPKOUTR_BOOST - [2:0] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM8993_SPKOUT_VU                        0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK                   0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT                       8  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH                       1  /* SPKOUT_VU */
+#define WM8993_SPKOUTL_ZC                       0x0080  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_MASK                  0x0080  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_SHIFT                      7  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_WIDTH                      1  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_MUTE_N                   0x0040  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_MASK              0x0040  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_SHIFT                  6  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_WIDTH                  1  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_VOL_MASK                 0x003F  /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_SHIFT                     0  /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_WIDTH                     6  /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R39 (0x27) - Speaker Volume Right
+ */
+#define WM8993_SPKOUT_VU                        0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK                   0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT                       8  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH                       1  /* SPKOUT_VU */
+#define WM8993_SPKOUTR_ZC                       0x0080  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_MASK                  0x0080  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_SHIFT                      7  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_WIDTH                      1  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_MUTE_N                   0x0040  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_MASK              0x0040  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_SHIFT                  6  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_WIDTH                  1  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_VOL_MASK                 0x003F  /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_SHIFT                     0  /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_WIDTH                     6  /* SPKOUTR_VOL - [5:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8993_IN2LP_TO_IN2L                    0x0080  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_MASK               0x0080  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_SHIFT                   7  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_WIDTH                   1  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L                    0x0040  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_MASK               0x0040  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_SHIFT                   6  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_WIDTH                   1  /* IN2LN_TO_IN2L */
+#define WM8993_IN1LP_TO_IN1L                    0x0020  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_MASK               0x0020  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_SHIFT                   5  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_WIDTH                   1  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L                    0x0010  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_MASK               0x0010  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_SHIFT                   4  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_WIDTH                   1  /* IN1LN_TO_IN1L */
+#define WM8993_IN2RP_TO_IN2R                    0x0008  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_MASK               0x0008  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_SHIFT                   3  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_WIDTH                   1  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R                    0x0004  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_MASK               0x0004  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_SHIFT                   2  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_WIDTH                   1  /* IN2RN_TO_IN2R */
+#define WM8993_IN1RP_TO_IN1R                    0x0002  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_MASK               0x0002  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_SHIFT                   1  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_WIDTH                   1  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R                    0x0001  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_MASK               0x0001  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_SHIFT                   0  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_WIDTH                   1  /* IN1RN_TO_IN1R */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8993_IN2L_TO_MIXINL                   0x0100  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_MASK              0x0100  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_SHIFT                  8  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_WIDTH                  1  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_MIXINL_VOL                  0x0080  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_MASK             0x0080  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_SHIFT                 7  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_WIDTH                 1  /* IN2L_MIXINL_VOL */
+#define WM8993_IN1L_TO_MIXINL                   0x0020  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_MASK              0x0020  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_SHIFT                  5  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_WIDTH                  1  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_MIXINL_VOL                  0x0010  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_MASK             0x0010  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_SHIFT                 4  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_WIDTH                 1  /* IN1L_MIXINL_VOL */
+#define WM8993_MIXOUTL_MIXINL_VOL_MASK          0x0007  /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT              0  /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH              3  /* MIXOUTL_MIXINL_VOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8993_IN2R_TO_MIXINR                   0x0100  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_MASK              0x0100  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_SHIFT                  8  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_WIDTH                  1  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_MIXINR_VOL                  0x0080  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_MASK             0x0080  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_SHIFT                 7  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_WIDTH                 1  /* IN2R_MIXINR_VOL */
+#define WM8993_IN1R_TO_MIXINR                   0x0020  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_MASK              0x0020  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_SHIFT                  5  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_WIDTH                  1  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_MIXINR_VOL                  0x0010  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_MASK             0x0010  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_SHIFT                 4  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_WIDTH                 1  /* IN1R_MIXINR_VOL */
+#define WM8993_MIXOUTR_MIXINR_VOL_MASK          0x0007  /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT              0  /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH              3  /* MIXOUTR_MIXINR_VOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8993_IN1LP_MIXINL_VOL_MASK            0x01C0  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_SHIFT                6  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_WIDTH                3  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_VRX_MIXINL_VOL_MASK              0x0007  /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_SHIFT                  0  /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_WIDTH                  3  /* VRX_MIXINL_VOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8993_IN1RP_MIXINR_VOL_MASK            0x01C0  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_SHIFT                6  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_WIDTH                3  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_VRX_MIXINR_VOL_MASK              0x0007  /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_SHIFT                  0  /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_WIDTH                  3  /* VRX_MIXINR_VOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8993_DACL_TO_HPOUT1L                  0x0100  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_MASK             0x0100  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_SHIFT                 8  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_WIDTH                 1  /* DACL_TO_HPOUT1L */
+#define WM8993_MIXINR_TO_MIXOUTL                0x0080  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_MASK           0x0080  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_SHIFT               7  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_WIDTH               1  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL                0x0040  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_MASK           0x0040  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_SHIFT               6  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_WIDTH               1  /* MIXINL_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL                 0x0020  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_MASK            0x0020  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_SHIFT                5  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_WIDTH                1  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL                 0x0010  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_MASK            0x0010  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_SHIFT                4  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_WIDTH                1  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL                  0x0008  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_MASK             0x0008  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_SHIFT                 3  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_WIDTH                 1  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL                  0x0004  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_MASK             0x0004  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_SHIFT                 2  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_WIDTH                 1  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL                 0x0002  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_MASK            0x0002  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_SHIFT                1  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_WIDTH                1  /* IN2LP_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL                  0x0001  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_MASK             0x0001  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_SHIFT                 0  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_WIDTH                 1  /* DACL_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8993_DACR_TO_HPOUT1R                  0x0100  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_MASK             0x0100  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_SHIFT                 8  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_WIDTH                 1  /* DACR_TO_HPOUT1R */
+#define WM8993_MIXINL_TO_MIXOUTR                0x0080  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_MASK           0x0080  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_SHIFT               7  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_WIDTH               1  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR                0x0040  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_MASK           0x0040  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_SHIFT               6  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_WIDTH               1  /* MIXINR_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR                 0x0020  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_MASK            0x0020  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_SHIFT                5  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_WIDTH                1  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR                 0x0010  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_MASK            0x0010  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_SHIFT                4  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_WIDTH                1  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR                  0x0008  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_MASK             0x0008  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_SHIFT                 3  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_WIDTH                 1  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR                  0x0004  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_MASK             0x0004  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_SHIFT                 2  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_WIDTH                 1  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR                 0x0002  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_MASK            0x0002  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_SHIFT                1  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_WIDTH                1  /* IN2RP_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR                  0x0001  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_MASK             0x0001  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_SHIFT                 0  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_WIDTH                 1  /* DACR_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8993_IN2LP_MIXOUTL_VOL_MASK           0x0E00  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT               9  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH               3  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTL_VOL_MASK           0x01C0  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT               6  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH               3  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN1R_MIXOUTL_VOL_MASK            0x0038  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_SHIFT                3  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_WIDTH                3  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTL_VOL_MASK            0x0007  /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_SHIFT                0  /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_WIDTH                3  /* IN1L_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8993_IN2RP_MIXOUTR_VOL_MASK           0x0E00  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT               9  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH               3  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTR_VOL_MASK           0x01C0  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT               6  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH               3  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN1L_MIXOUTR_VOL_MASK            0x0038  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_SHIFT                3  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_WIDTH                3  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTR_VOL_MASK            0x0007  /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_SHIFT                0  /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_WIDTH                3  /* IN1R_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8993_DACL_MIXOUTL_VOL_MASK            0x0E00  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_SHIFT                9  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_WIDTH                3  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTL_VOL_MASK           0x01C0  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT               6  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH               3  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_MIXINR_MIXOUTL_VOL_MASK          0x0038  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT              3  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH              3  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTL_VOL_MASK          0x0007  /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT              0  /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH              3  /* MIXINL_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8993_DACR_MIXOUTR_VOL_MASK            0x0E00  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_SHIFT                9  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_WIDTH                3  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTR_VOL_MASK           0x01C0  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT               6  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH               3  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_MIXINL_MIXOUTR_VOL_MASK          0x0038  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT              3  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH              3  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTR_VOL_MASK          0x0007  /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT              0  /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH              3  /* MIXINR_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R51 (0x33) - HPOUT2 Mixer
+ */
+#define WM8993_VRX_TO_HPOUT2                    0x0020  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_MASK               0x0020  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_SHIFT                   5  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_WIDTH                   1  /* VRX_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2             0x0010  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK        0x0010  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT            4  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH            1  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2             0x0008  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK        0x0008  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT            3  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH            1  /* MIXOUTRVOL_TO_HPOUT2 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8993_MIXOUTL_TO_LINEOUT1N             0x0040  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK        0x0040  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT            6  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH            1  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N             0x0020  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK        0x0020  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT            5  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH            1  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_LINEOUT1_MODE                    0x0010  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_MASK               0x0010  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_SHIFT                   4  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_WIDTH                   1  /* LINEOUT1_MODE */
+#define WM8993_IN1R_TO_LINEOUT1P                0x0004  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_MASK           0x0004  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_SHIFT               2  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_WIDTH               1  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P                0x0002  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_MASK           0x0002  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_SHIFT               1  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_WIDTH               1  /* IN1L_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P             0x0001  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK        0x0001  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT            0  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH            1  /* MIXOUTL_TO_LINEOUT1P */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8993_MIXOUTR_TO_LINEOUT2N             0x0040  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK        0x0040  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT            6  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH            1  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N             0x0020  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK        0x0020  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT            5  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH            1  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_LINEOUT2_MODE                    0x0010  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_MASK               0x0010  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_SHIFT                   4  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_WIDTH                   1  /* LINEOUT2_MODE */
+#define WM8993_IN1L_TO_LINEOUT2P                0x0004  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_MASK           0x0004  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_SHIFT               2  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_WIDTH               1  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P                0x0002  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_MASK           0x0002  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_SHIFT               1  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_WIDTH               1  /* IN1R_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P             0x0001  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK        0x0001  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT            0  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH            1  /* MIXOUTR_TO_LINEOUT2P */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8993_SPKAB_REF_SEL                    0x0100  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_MASK               0x0100  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_SHIFT                   8  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_WIDTH                   1  /* SPKAB_REF_SEL */
+#define WM8993_MIXINL_TO_SPKMIXL                0x0080  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_MASK           0x0080  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_SHIFT               7  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_WIDTH               1  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINR_TO_SPKMIXR                0x0040  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_MASK           0x0040  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_SHIFT               6  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_WIDTH               1  /* MIXINR_TO_SPKMIXR */
+#define WM8993_IN1LP_TO_SPKMIXL                 0x0020  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_MASK            0x0020  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_SHIFT                5  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_WIDTH                1  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1RP_TO_SPKMIXR                 0x0010  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_MASK            0x0010  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_SHIFT                4  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_WIDTH                1  /* IN1RP_TO_SPKMIXR */
+#define WM8993_MIXOUTL_TO_SPKMIXL               0x0008  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_MASK          0x0008  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT              3  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH              1  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTR_TO_SPKMIXR               0x0004  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_MASK          0x0004  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT              2  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH              1  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_DACL_TO_SPKMIXL                  0x0002  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_MASK             0x0002  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_SHIFT                 1  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_WIDTH                 1  /* DACL_TO_SPKMIXL */
+#define WM8993_DACR_TO_SPKMIXR                  0x0001  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_MASK             0x0001  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_SHIFT                 0  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_WIDTH                 1  /* DACR_TO_SPKMIXR */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8993_LINEOUT1_FB                      0x0080  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_MASK                 0x0080  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_SHIFT                     7  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_WIDTH                     1  /* LINEOUT1_FB */
+#define WM8993_LINEOUT2_FB                      0x0040  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_MASK                 0x0040  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_SHIFT                     6  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_WIDTH                     1  /* LINEOUT2_FB */
+#define WM8993_VROI                             0x0001  /* VROI */
+#define WM8993_VROI_MASK                        0x0001  /* VROI */
+#define WM8993_VROI_SHIFT                            0  /* VROI */
+#define WM8993_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8993_LINEOUT_VMID_BUF_ENA             0x0080  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_MASK        0x0080  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT            7  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH            1  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_HPOUT2_IN_ENA                    0x0040  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_MASK               0x0040  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_SHIFT                   6  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_WIDTH                   1  /* HPOUT2_IN_ENA */
+#define WM8993_LINEOUT1_DISCH                   0x0020  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_MASK              0x0020  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_SHIFT                  5  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_WIDTH                  1  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT2_DISCH                   0x0010  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_MASK              0x0010  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_SHIFT                  4  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_WIDTH                  1  /* LINEOUT2_DISCH */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8993_VMID_RAMP_MASK                   0x0060  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_SHIFT                       5  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_WIDTH                       2  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_BUF_ENA                     0x0008  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_MASK                0x0008  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_SHIFT                    3  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM8993_STARTUP_BIAS_ENA                 0x0004  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_MASK            0x0004  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_SHIFT                2  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+#define WM8993_BIAS_SRC                         0x0002  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_MASK                    0x0002  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_SHIFT                        1  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_WIDTH                        1  /* BIAS_SRC */
+#define WM8993_VMID_DISCH                       0x0001  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_MASK                  0x0001  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_SHIFT                      0  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_WIDTH                      1  /* VMID_DISCH */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8993_JD_SCTHR_MASK                    0x00C0  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_SHIFT                        6  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_WIDTH                        2  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_THR_MASK                      0x0030  /* JD_THR - [5:4] */
+#define WM8993_JD_THR_SHIFT                          4  /* JD_THR - [5:4] */
+#define WM8993_JD_THR_WIDTH                          2  /* JD_THR - [5:4] */
+#define WM8993_JD_ENA                           0x0004  /* JD_ENA */
+#define WM8993_JD_ENA_MASK                      0x0004  /* JD_ENA */
+#define WM8993_JD_ENA_SHIFT                          2  /* JD_ENA */
+#define WM8993_JD_ENA_WIDTH                          1  /* JD_ENA */
+#define WM8993_MICB2_LVL                        0x0002  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_MASK                   0x0002  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_SHIFT                       1  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_WIDTH                       1  /* MICB2_LVL */
+#define WM8993_MICB1_LVL                        0x0001  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_MASK                   0x0001  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_SHIFT                       0  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_WIDTH                       1  /* MICB1_LVL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8993_FLL_FRAC                         0x0004  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_MASK                    0x0004  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_SHIFT                        2  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM8993_FLL_OSC_ENA                      0x0002  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_MASK                 0x0002  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_SHIFT                     1  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8993_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM8993_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM8993_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM8993_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8993_FLL_OUTDIV_MASK                  0x0700  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8993_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8993_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8993_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8993_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM8993_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM8993_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM8993_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R64 (0x40) - FLL Control 5
+ */
+#define WM8993_FLL_FRC_NCO_VAL_MASK             0x1F80  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_SHIFT                 7  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_WIDTH                 6  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO                      0x0040  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_MASK                 0x0040  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_SHIFT                     6  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_WIDTH                     1  /* FLL_FRC_NCO */
+#define WM8993_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_SRC_MASK                 0x0003  /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_SHIFT                     0  /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R65 (0x41) - Clocking 3
+ */
+#define WM8993_CLK_DCS_DIV_MASK                 0x3C00  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_SHIFT                    10  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_WIDTH                     4  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_SAMPLE_RATE_MASK                 0x0380  /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_SHIFT                     7  /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [9:7] */
+#define WM8993_CLK_SYS_RATE_MASK                0x001E  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_SHIFT                    1  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_DSP_ENA                      0x0001  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_MASK                 0x0001  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_SHIFT                     0  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+
+/*
+ * R66 (0x42) - Clocking 4
+ */
+#define WM8993_DAC_DIV4                         0x0200  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_MASK                    0x0200  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_SHIFT                        9  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_WIDTH                        1  /* DAC_DIV4 */
+#define WM8993_CLK_256K_DIV_MASK                0x007E  /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_SHIFT                    1  /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_WIDTH                    6  /* CLK_256K_DIV - [6:1] */
+#define WM8993_SR_MODE                          0x0001  /* SR_MODE */
+#define WM8993_SR_MODE_MASK                     0x0001  /* SR_MODE */
+#define WM8993_SR_MODE_SHIFT                         0  /* SR_MODE */
+#define WM8993_SR_MODE_WIDTH                         1  /* SR_MODE */
+
+/*
+ * R67 (0x43) - MW Slave Control
+ */
+#define WM8993_MASK_WRITE_ENA                   0x0001  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_MASK              0x0001  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_SHIFT                  0  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_WIDTH                  1  /* MASK_WRITE_ENA */
+
+/*
+ * R69 (0x45) - Bus Control 1
+ */
+#define WM8993_CLK_SYS_ENA                      0x0002  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_MASK                 0x0002  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_SHIFT                     1  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM8993_WSEQ_ENA                         0x0100  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8993_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM8993_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM8993_WSEQ_EOS                         0x4000  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8993_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM8993_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8993_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM8993_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM8993_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM8993_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8993_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM8993_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM8993_WSEQ_CURRENT_INDEX_MASK          0x003F  /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_SHIFT              0  /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM8993_CP_ENA                           0x8000  /* CP_ENA */
+#define WM8993_CP_ENA_MASK                      0x8000  /* CP_ENA */
+#define WM8993_CP_ENA_SHIFT                         15  /* CP_ENA */
+#define WM8993_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R81 (0x51) - Class W 0
+ */
+#define WM8993_CP_DYN_FREQ                      0x0002  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_MASK                 0x0002  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_SHIFT                     1  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_WIDTH                     1  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_V                         0x0001  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_MASK                    0x0001  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_SHIFT                        0  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_WIDTH                        1  /* CP_DYN_V */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM8993_DCS_TRIG_SINGLE_1                0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_MASK           0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_SHIFT              13  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_WIDTH               1  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_0                0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_MASK           0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_SHIFT              12  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_WIDTH               1  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SERIES_1                0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_MASK           0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_SHIFT               9  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_WIDTH               1  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_0                0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_MASK           0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_SHIFT               8  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_WIDTH               1  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_STARTUP_1               0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_MASK          0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_SHIFT              5  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_WIDTH              1  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_0               0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_MASK          0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_SHIFT              4  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_WIDTH              1  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_DAC_WR_1                0x0008  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_MASK           0x0008  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT               3  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_0                0x0004  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_MASK           0x0004  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT               2  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH               1  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_ENA_CHAN_1                   0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_MASK              0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_SHIFT                  1  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_WIDTH                  1  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_0                   0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_MASK              0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_SHIFT                  0  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_WIDTH                  1  /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM8993_DCS_SERIES_NO_01_MASK            0x0FE0  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_SHIFT                5  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_WIDTH                7  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_TIMER_PERIOD_01_MASK         0x000F  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_SHIFT             0  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_WIDTH             4  /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM8993_DCS_DAC_WR_VAL_1_MASK            0xFF00  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_SHIFT                8  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_WIDTH                8  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_0_MASK            0x00FF  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_SHIFT                0  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_WIDTH                8  /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM8993_DCS_DATAPATH_BUSY                0x4000  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_MASK           0x4000  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_SHIFT              14  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_WIDTH               1  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_CHANNEL_MASK                 0x3000  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_SHIFT                    12  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_WIDTH                     2  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CAL_COMPLETE_MASK            0x0300  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_SHIFT                8  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_WIDTH                2  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_DAC_WR_COMPLETE_MASK         0x0030  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT             4  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH             2  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_STARTUP_COMPLETE_MASK        0x0003  /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_SHIFT            0  /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_WIDTH            2  /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM8993_DCS_INTEG_CHAN_1_MASK            0x00FF  /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_SHIFT                0  /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_WIDTH                8  /* DCS_INTEG_CHAN_1 - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM8993_DCS_INTEG_CHAN_0_MASK            0x00FF  /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_SHIFT                0  /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_WIDTH                8  /* DCS_INTEG_CHAN_0 - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM8993_HPOUT1_AUTO_PU                   0x0100  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_MASK              0x0100  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_SHIFT                  8  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_WIDTH                  1  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1L_RMV_SHORT                0x0080  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_MASK           0x0080  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_SHIFT               7  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_WIDTH               1  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_OUTP                     0x0040  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_MASK                0x0040  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_SHIFT                    6  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_WIDTH                    1  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_DLY                      0x0020  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_MASK                 0x0020  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_SHIFT                     5  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_WIDTH                     1  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1R_RMV_SHORT                0x0008  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_MASK           0x0008  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_SHIFT               3  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_WIDTH               1  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_OUTP                     0x0004  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_MASK                0x0004  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_SHIFT                    2  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_WIDTH                    1  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_DLY                      0x0002  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_MASK                 0x0002  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_SHIFT                     1  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_WIDTH                     1  /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - EQ1
+ */
+#define WM8993_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM8993_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM8993_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM8993_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R99 (0x63) - EQ2
+ */
+#define WM8993_EQ_B1_GAIN_MASK                  0x001F  /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_SHIFT                      0  /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R100 (0x64) - EQ3
+ */
+#define WM8993_EQ_B2_GAIN_MASK                  0x001F  /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_SHIFT                      0  /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R101 (0x65) - EQ4
+ */
+#define WM8993_EQ_B3_GAIN_MASK                  0x001F  /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_SHIFT                      0  /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R102 (0x66) - EQ5
+ */
+#define WM8993_EQ_B4_GAIN_MASK                  0x001F  /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_SHIFT                      0  /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R103 (0x67) - EQ6
+ */
+#define WM8993_EQ_B5_GAIN_MASK                  0x001F  /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_SHIFT                      0  /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R104 (0x68) - EQ7
+ */
+#define WM8993_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R105 (0x69) - EQ8
+ */
+#define WM8993_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R106 (0x6A) - EQ9
+ */
+#define WM8993_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R107 (0x6B) - EQ10
+ */
+#define WM8993_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R108 (0x6C) - EQ11
+ */
+#define WM8993_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R109 (0x6D) - EQ12
+ */
+#define WM8993_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R110 (0x6E) - EQ13
+ */
+#define WM8993_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R111 (0x6F) - EQ14
+ */
+#define WM8993_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R112 (0x70) - EQ15
+ */
+#define WM8993_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R113 (0x71) - EQ16
+ */
+#define WM8993_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R114 (0x72) - EQ17
+ */
+#define WM8993_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R115 (0x73) - EQ18
+ */
+#define WM8993_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R116 (0x74) - EQ19
+ */
+#define WM8993_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R117 (0x75) - EQ20
+ */
+#define WM8993_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R118 (0x76) - EQ21
+ */
+#define WM8993_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R119 (0x77) - EQ22
+ */
+#define WM8993_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R120 (0x78) - EQ23
+ */
+#define WM8993_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R121 (0x79) - EQ24
+ */
+#define WM8993_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+/*
+ * R122 (0x7A) - Digital Pulls
+ */
+#define WM8993_MCLK_PU                          0x0080  /* MCLK_PU */
+#define WM8993_MCLK_PU_MASK                     0x0080  /* MCLK_PU */
+#define WM8993_MCLK_PU_SHIFT                         7  /* MCLK_PU */
+#define WM8993_MCLK_PU_WIDTH                         1  /* MCLK_PU */
+#define WM8993_MCLK_PD                          0x0040  /* MCLK_PD */
+#define WM8993_MCLK_PD_MASK                     0x0040  /* MCLK_PD */
+#define WM8993_MCLK_PD_SHIFT                         6  /* MCLK_PD */
+#define WM8993_MCLK_PD_WIDTH                         1  /* MCLK_PD */
+#define WM8993_DACDAT_PU                        0x0020  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_MASK                   0x0020  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_SHIFT                       5  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_WIDTH                       1  /* DACDAT_PU */
+#define WM8993_DACDAT_PD                        0x0010  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_MASK                   0x0010  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_SHIFT                       4  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_WIDTH                       1  /* DACDAT_PD */
+#define WM8993_LRCLK_PU                         0x0008  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_MASK                    0x0008  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_SHIFT                        3  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_WIDTH                        1  /* LRCLK_PU */
+#define WM8993_LRCLK_PD                         0x0004  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_MASK                    0x0004  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_SHIFT                        2  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_WIDTH                        1  /* LRCLK_PD */
+#define WM8993_BCLK_PU                          0x0002  /* BCLK_PU */
+#define WM8993_BCLK_PU_MASK                     0x0002  /* BCLK_PU */
+#define WM8993_BCLK_PU_SHIFT                         1  /* BCLK_PU */
+#define WM8993_BCLK_PU_WIDTH                         1  /* BCLK_PU */
+#define WM8993_BCLK_PD                          0x0001  /* BCLK_PD */
+#define WM8993_BCLK_PD_MASK                     0x0001  /* BCLK_PD */
+#define WM8993_BCLK_PD_SHIFT                         0  /* BCLK_PD */
+#define WM8993_BCLK_PD_WIDTH                         1  /* BCLK_PD */
+
+/*
+ * R123 (0x7B) - DRC Control 1
+ */
+#define WM8993_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM8993_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM8993_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM8993_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM8993_DRC_DAC_PATH                     0x4000  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_MASK                0x4000  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_SHIFT                   14  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_WIDTH                    1  /* DRC_DAC_PATH */
+#define WM8993_DRC_SMOOTH_ENA                   0x0800  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_MASK              0x0800  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_SHIFT                 11  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_WIDTH                  1  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_QR_ENA                       0x0400  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_MASK                  0x0400  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_SHIFT                     10  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_WIDTH                      1  /* DRC_QR_ENA */
+#define WM8993_DRC_ANTICLIP_ENA                 0x0200  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_MASK            0x0200  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_SHIFT                9  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_WIDTH                1  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_HYST_ENA                     0x0100  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_MASK                0x0100  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_SHIFT                    8  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_WIDTH                    1  /* DRC_HYST_ENA */
+#define WM8993_DRC_THRESH_HYST_MASK             0x0030  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_SHIFT                 4  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_WIDTH                 2  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R124 (0x7C) - DRC Control 2
+ */
+#define WM8993_DRC_ATTACK_RATE_MASK             0xF000  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_SHIFT                12  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_WIDTH                 4  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_DECAY_RATE_MASK              0x0F00  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_SHIFT                  8  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_WIDTH                  4  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_THRESH_COMP_MASK             0x00FC  /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_SHIFT                 2  /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_WIDTH                 6  /* DRC_THRESH_COMP - [7:2] */
+
+/*
+ * R125 (0x7D) - DRC Control 3
+ */
+#define WM8993_DRC_AMP_COMP_MASK                0xF800  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_SHIFT                   11  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_WIDTH                    5  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_R0_SLOPE_COMP_MASK           0x0700  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_SHIFT               8  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_WIDTH               3  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_FF_DELAY                     0x0080  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_MASK                0x0080  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_SHIFT                    7  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */
+#define WM8993_DRC_THRESH_QR_MASK               0x000C  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_SHIFT                   2  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_WIDTH                   2  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_RATE_QR_MASK                 0x0003  /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_SHIFT                     0  /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_WIDTH                     2  /* DRC_RATE_QR - [1:0] */
+
+/*
+ * R126 (0x7E) - DRC Control 4
+ */
+#define WM8993_DRC_R1_SLOPE_COMP_MASK           0xE000  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_SHIFT              13  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_WIDTH               3  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_STARTUP_GAIN_MASK            0x1F00  /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_SHIFT                8  /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [12:8] */
+
+#endif
index 86fc57e..156f2a4 100644 (file)
@@ -707,6 +707,10 @@ static int configure_clock(struct snd_soc_codec *codec)
                                    target > 3000000)
                                        break;
                        }
+
+                       if (i == ARRAY_SIZE(clk_sys_rates))
+                               return -EINVAL;
+
                } else if (wm9081->fs) {
                        for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
                                new_sysclk = clk_sys_rates[i].ratio
@@ -714,6 +718,10 @@ static int configure_clock(struct snd_soc_codec *codec)
                                if (new_sysclk > 3000000)
                                        break;
                        }
+
+                       if (i == ARRAY_SIZE(clk_sys_rates))
+                               return -EINVAL;
+
                } else {
                        new_sysclk = 12288000;
                }
@@ -1492,6 +1500,21 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm9081_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm9081_i2c_suspend NULL
+#define wm9081_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm9081_i2c_id[] = {
        { "wm9081", 0 },
        { }
@@ -1505,6 +1528,8 @@ static struct i2c_driver wm9081_i2c_driver = {
        },
        .probe =    wm9081_i2c_probe,
        .remove =   __devexit_p(wm9081_i2c_remove),
+       .suspend =  wm9081_i2c_suspend,
+       .resume =   wm9081_i2c_resume,
        .id_table = wm9081_i2c_id,
 };
 
index 411a710..6802dd5 100644 (file)
@@ -9,6 +9,9 @@ config SND_DAVINCI_SOC
 config SND_DAVINCI_SOC_I2S
        tristate
 
+config SND_DAVINCI_SOC_MCASP
+       tristate
+
 config SND_DAVINCI_SOC_EVM
        tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
        depends on SND_DAVINCI_SOC
@@ -19,6 +22,16 @@ config SND_DAVINCI_SOC_EVM
          Say Y if you want to add support for SoC audio on TI
          DaVinci DM6446 or DM355 EVM platforms.
 
+config  SND_DM6467_SOC_EVM
+       tristate "SoC Audio support for DaVinci DM6467 EVM"
+       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
+       select SND_DAVINCI_SOC_MCASP
+       select SND_SOC_TLV320AIC3X
+       select SND_SOC_SPDIF
+
+       help
+         Say Y if you want to add support for SoC audio on TI
+
 config SND_DAVINCI_SOC_SFFSDR
        tristate "SoC Audio support for SFFSDR"
        depends on SND_DAVINCI_SOC && MACH_SFFSDR
index ca8bae1..67be54f 100644 (file)
@@ -1,13 +1,16 @@
 # DAVINCI Platform Support
 snd-soc-davinci-objs := davinci-pcm.o
 snd-soc-davinci-i2s-objs := davinci-i2s.o
+snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
 
 obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
 obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
+obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
 
 # DAVINCI Machine Support
 snd-soc-evm-objs := davinci-evm.o
 snd-soc-sffsdr-objs := davinci-sffsdr.o
 
 obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
 obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
index 58fd1cb..f3bb6f6 100644 (file)
 #include <mach/mux.h>
 
 #include "../codecs/tlv320aic3x.h"
+#include "../codecs/spdif_transciever.h"
 #include "davinci-pcm.h"
 #include "davinci-i2s.h"
-
+#include "davinci-mcasp.h"
 
 #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
                SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
@@ -43,7 +44,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        unsigned sysclk;
 
        /* ASP1 on DM355 EVM is clocked by an external oscillator */
-       if (machine_is_davinci_dm355_evm())
+       if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
                sysclk = 27000000;
 
        /* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -144,6 +145,24 @@ static struct snd_soc_dai_link evm_dai = {
        .ops = &evm_ops,
 };
 
+static struct snd_soc_dai_link dm6467_evm_dai[] = {
+       {
+               .name = "TLV320AIC3X",
+               .stream_name = "AIC3X",
+               .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
+               .codec_dai = &aic3x_dai,
+               .init = evm_aic3x_init,
+               .ops = &evm_ops,
+       },
+       {
+               .name = "McASP",
+               .stream_name = "spdif",
+               .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI],
+               .codec_dai = &dit_stub_dai,
+               .ops = &evm_ops,
+       },
+};
+
 /* davinci-evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
        .name = "DaVinci EVM",
@@ -152,12 +171,26 @@ static struct snd_soc_card snd_soc_card_evm = {
        .num_links = 1,
 };
 
+/* davinci dm6467 evm audio machine driver */
+static struct snd_soc_card dm6467_snd_soc_card_evm = {
+       .name = "DaVinci DM6467 EVM",
+       .platform = &davinci_soc_platform,
+       .dai_link = dm6467_evm_dai,
+       .num_links = ARRAY_SIZE(dm6467_evm_dai),
+};
+
 /* evm audio private data */
 static struct aic3x_setup_data evm_aic3x_setup = {
        .i2c_bus = 1,
        .i2c_address = 0x1b,
 };
 
+/* dm6467 evm audio private data */
+static struct aic3x_setup_data dm6467_evm_aic3x_setup = {
+       .i2c_bus = 1,
+       .i2c_address = 0x18,
+};
+
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
        .card = &snd_soc_card_evm,
@@ -165,60 +198,30 @@ static struct snd_soc_device evm_snd_devdata = {
        .codec_data = &evm_aic3x_setup,
 };
 
-/* DM6446 EVM uses ASP0; line-out is a pair of RCA jacks */
-static struct resource evm_snd_resources[] = {
-       {
-               .start = DAVINCI_ASP0_BASE,
-               .end = DAVINCI_ASP0_BASE + SZ_8K - 1,
-               .flags = IORESOURCE_MEM,
-       },
-};
-
-static struct evm_snd_platform_data evm_snd_data = {
-       .tx_dma_ch      = DAVINCI_DMA_ASP0_TX,
-       .rx_dma_ch      = DAVINCI_DMA_ASP0_RX,
-};
-
-/* DM335 EVM uses ASP1; line-out is a stereo mini-jack */
-static struct resource dm335evm_snd_resources[] = {
-       {
-               .start = DAVINCI_ASP1_BASE,
-               .end = DAVINCI_ASP1_BASE + SZ_8K - 1,
-               .flags = IORESOURCE_MEM,
-       },
-};
-
-static struct evm_snd_platform_data dm335evm_snd_data = {
-       .tx_dma_ch      = DAVINCI_DMA_ASP1_TX,
-       .rx_dma_ch      = DAVINCI_DMA_ASP1_RX,
+/* evm audio subsystem */
+static struct snd_soc_device dm6467_evm_snd_devdata = {
+       .card = &dm6467_snd_soc_card_evm,
+       .codec_dev = &soc_codec_dev_aic3x,
+       .codec_data = &dm6467_evm_aic3x_setup,
 };
 
 static struct platform_device *evm_snd_device;
 
 static int __init evm_init(void)
 {
-       struct resource *resources;
-       unsigned num_resources;
-       struct evm_snd_platform_data *data;
+       struct snd_soc_device *evm_snd_dev_data;
        int index;
        int ret;
 
        if (machine_is_davinci_evm()) {
-               davinci_cfg_reg(DM644X_MCBSP);
-
-               resources = evm_snd_resources;
-               num_resources = ARRAY_SIZE(evm_snd_resources);
-               data = &evm_snd_data;
+               evm_snd_dev_data = &evm_snd_devdata;
                index = 0;
        } else if (machine_is_davinci_dm355_evm()) {
-               /* we don't use ASP1 IRQs, or we'd need to mux them ... */
-               davinci_cfg_reg(DM355_EVT8_ASP1_TX);
-               davinci_cfg_reg(DM355_EVT9_ASP1_RX);
-
-               resources = dm335evm_snd_resources;
-               num_resources = ARRAY_SIZE(dm335evm_snd_resources);
-               data = &dm335evm_snd_data;
+               evm_snd_dev_data = &evm_snd_devdata;
                index = 1;
+       } else if (machine_is_davinci_dm6467_evm()) {
+               evm_snd_dev_data = &dm6467_evm_snd_devdata;
+               index = 0;
        } else
                return -EINVAL;
 
@@ -226,17 +229,8 @@ static int __init evm_init(void)
        if (!evm_snd_device)
                return -ENOMEM;
 
-       platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
-       evm_snd_devdata.dev = &evm_snd_device->dev;
-       platform_device_add_data(evm_snd_device, data, sizeof(*data));
-
-       ret = platform_device_add_resources(evm_snd_device, resources,
-                       num_resources);
-       if (ret) {
-               platform_device_put(evm_snd_device);
-               return ret;
-       }
-
+       platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
+       evm_snd_dev_data->dev = &evm_snd_device->dev;
        ret = platform_device_add(evm_snd_device);
        if (ret)
                platform_device_put(evm_snd_device);
index b1ea52f..e5cd97b 100644 (file)
@@ -22,6 +22,8 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+#include <mach/asp.h>
+
 #include "davinci-pcm.h"
 
 
@@ -63,6 +65,7 @@
 #define DAVINCI_MCBSP_RCR_RWDLEN1(v)   ((v) << 5)
 #define DAVINCI_MCBSP_RCR_RFRLEN1(v)   ((v) << 8)
 #define DAVINCI_MCBSP_RCR_RDATDLY(v)   ((v) << 16)
+#define DAVINCI_MCBSP_RCR_RFIG         (1 << 18)
 #define DAVINCI_MCBSP_RCR_RWDLEN2(v)   ((v) << 21)
 
 #define DAVINCI_MCBSP_XCR_XWDLEN1(v)   ((v) << 5)
 #define DAVINCI_MCBSP_PCR_FSRM         (1 << 10)
 #define DAVINCI_MCBSP_PCR_FSXM         (1 << 11)
 
-#define MOD_REG_BIT(val, mask, set) do { \
-       if (set) { \
-               val |= mask; \
-       } else { \
-               val &= ~mask; \
-       } \
-} while (0)
-
 enum {
        DAVINCI_MCBSP_WORD_8 = 0,
        DAVINCI_MCBSP_WORD_12,
@@ -112,6 +107,10 @@ static struct davinci_pcm_dma_params davinci_i2s_pcm_in = {
 
 struct davinci_mcbsp_dev {
        void __iomem                    *base;
+#define MOD_DSP_A      0
+#define MOD_DSP_B      1
+       int                             mode;
+       u32                             pcr;
        struct clk                      *clk;
        struct davinci_pcm_dma_params   *dma_params[2];
 };
@@ -127,96 +126,100 @@ static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
        return __raw_readl(dev->base + reg);
 }
 
-static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
+static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
+{
+       u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
+       /* The clock needs to toggle to complete reset.
+        * So, fake it by toggling the clk polarity.
+        */
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
+}
+
+static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
+               struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_platform *platform = socdev->card->platform;
-       u32 w;
-       int ret;
-
-       /* Start the sample generator and enable transmitter/receiver */
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       u32 spcr;
+       u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
+       spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+       if (spcr & mask) {
+               /* start off disabled */
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+                               spcr & ~mask);
+               toggle_clock(dev, playback);
+       }
+       if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
+                       DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
+               /* Start the sample generator */
+               spcr |= DAVINCI_MCBSP_SPCR_GRST;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+       }
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+       if (playback) {
                /* Stop the DMA to avoid data loss */
                /* while the transmitter is out of reset to handle XSYNCERR */
                if (platform->pcm_ops->trigger) {
-                       ret = platform->pcm_ops->trigger(substream,
+                       int ret = platform->pcm_ops->trigger(substream,
                                SNDRV_PCM_TRIGGER_STOP);
                        if (ret < 0)
                                printk(KERN_DEBUG "Playback DMA stop failed\n");
                }
 
                /* Enable the transmitter */
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               spcr |= DAVINCI_MCBSP_SPCR_XRST;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 
                /* wait for any unexpected frame sync error to occur */
                udelay(100);
 
                /* Disable the transmitter to clear any outstanding XSYNCERR */
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+               spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+               toggle_clock(dev, playback);
 
                /* Restart the DMA */
                if (platform->pcm_ops->trigger) {
-                       ret = platform->pcm_ops->trigger(substream,
+                       int ret = platform->pcm_ops->trigger(substream,
                                SNDRV_PCM_TRIGGER_START);
                        if (ret < 0)
                                printk(KERN_DEBUG "Playback DMA start failed\n");
                }
-               /* Enable the transmitter */
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-
-       } else {
-
-               /* Enable the reciever */
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
        }
 
+       /* Enable transmitter or receiver */
+       spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+       spcr |= mask;
 
-       /* Start frame sync */
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
+               /* Start frame sync */
+               spcr |= DAVINCI_MCBSP_SPCR_FRST;
+       }
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 }
 
-static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
+static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
 {
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-       u32 w;
+       u32 spcr;
 
        /* Reset transmitter/receiver and sample rate/frame sync generators */
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
-                      DAVINCI_MCBSP_SPCR_FRST, 0);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
-       else
-               MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+       spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
+       spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+       toggle_clock(dev, playback);
 }
 
 static int davinci_i2s_startup(struct snd_pcm_substream *substream,
-                              struct snd_soc_dai *dai)
+                              struct snd_soc_dai *cpu_dai)
 {
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-
+       struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
        cpu_dai->dma_data = dev->dma_params[substream->stream];
-
        return 0;
 }
 
@@ -228,12 +231,11 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
        unsigned int pcr;
        unsigned int srgr;
-       unsigned int rcr;
-       unsigned int xcr;
        srgr = DAVINCI_MCBSP_SRGR_FSGM |
                DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
                DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
+       /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBS_CFS:
                /* cpu is master */
@@ -258,11 +260,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
-       rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
-       xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+       /* interface format */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-       case SND_SOC_DAIFMT_DSP_B:
-               break;
        case SND_SOC_DAIFMT_I2S:
                /* Davinci doesn't support TRUE I2S, but some codecs will have
                 * the left and right channels contiguous. This allows
@@ -282,8 +281,10 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                 */
                fmt ^= SND_SOC_DAIFMT_NB_IF;
        case SND_SOC_DAIFMT_DSP_A:
-               rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
-               xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+               dev->mode = MOD_DSP_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               dev->mode = MOD_DSP_B;
                break;
        default:
                printk(KERN_ERR "%s:bad format\n", __func__);
@@ -343,9 +344,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
        davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+       dev->pcr = pcr;
        davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
        return 0;
 }
 
@@ -358,26 +358,36 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
        struct snd_interval *i = NULL;
        int mcbsp_word_length;
-       u32 w;
+       unsigned int rcr, xcr, srgr;
+       u32 spcr;
 
        /* general line settings */
-       w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+       spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
        } else {
-               w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
        }
 
        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-       w = DAVINCI_MCBSP_SRGR_FSGM;
-       MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
+       srgr = DAVINCI_MCBSP_SRGR_FSGM;
+       srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
 
        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-       MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
+       srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
 
+       rcr = DAVINCI_MCBSP_RCR_RFIG;
+       xcr = DAVINCI_MCBSP_XCR_XFIG;
+       if (dev->mode == MOD_DSP_B) {
+               rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
+               xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
+       } else {
+               rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+               xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+       }
        /* Determine xfer data type */
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
@@ -397,18 +407,31 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
-                              DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+       rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
+       xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
 
-       } else {
-               w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
-               MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
-                              DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
-               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+       rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+               DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
+       xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+               DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+       else
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+       return 0;
+}
+
+static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+       int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       davinci_mcbsp_stop(dev, playback);
+       if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) {
+               /* codec is master */
+               davinci_mcbsp_start(dev, substream);
        }
        return 0;
 }
@@ -416,35 +439,74 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
 static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
                               struct snd_soc_dai *dai)
 {
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
        int ret = 0;
+       int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0)
+               return 0;       /* return if codec is master */
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               davinci_mcbsp_start(substream);
+               davinci_mcbsp_start(dev, substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               davinci_mcbsp_stop(substream);
+               davinci_mcbsp_stop(dev, playback);
                break;
        default:
                ret = -EINVAL;
        }
-
        return ret;
 }
 
-static int davinci_i2s_probe(struct platform_device *pdev,
-                            struct snd_soc_dai *dai)
+static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+       int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       davinci_mcbsp_stop(dev, playback);
+}
+
+#define DAVINCI_I2S_RATES      SNDRV_PCM_RATE_8000_96000
+
+static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+       .startup        = davinci_i2s_startup,
+       .shutdown       = davinci_i2s_shutdown,
+       .prepare        = davinci_i2s_prepare,
+       .trigger        = davinci_i2s_trigger,
+       .hw_params      = davinci_i2s_hw_params,
+       .set_fmt        = davinci_i2s_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_i2s_dai = {
+       .name = "davinci-i2s",
+       .id = 0,
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = DAVINCI_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = DAVINCI_I2S_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+       .ops = &davinci_i2s_dai_ops,
+
+};
+EXPORT_SYMBOL_GPL(davinci_i2s_dai);
+
+static int davinci_i2s_probe(struct platform_device *pdev)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
+       struct snd_platform_data *pdata = pdev->dev.platform_data;
        struct davinci_mcbsp_dev *dev;
-       struct resource *mem, *ioarea;
-       struct evm_snd_platform_data *pdata;
+       struct resource *mem, *ioarea, *res;
        int ret;
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -466,8 +528,6 @@ static int davinci_i2s_probe(struct platform_device *pdev,
                goto err_release_region;
        }
 
-       cpu_dai->private_data = dev;
-
        dev->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(dev->clk)) {
                ret = -ENODEV;
@@ -476,18 +536,37 @@ static int davinci_i2s_probe(struct platform_device *pdev,
        clk_enable(dev->clk);
 
        dev->base = (void __iomem *)IO_ADDRESS(mem->start);
-       pdata = pdev->dev.platform_data;
 
        dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out;
-       dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch;
        dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr =
            (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
 
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in;
-       dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch;
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr =
            (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
 
+       /* first TX, then RX */
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no DMA resource\n");
+               ret = -ENXIO;
+               goto err_free_mem;
+       }
+       dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!res) {
+               dev_err(&pdev->dev, "no DMA resource\n");
+               ret = -ENXIO;
+               goto err_free_mem;
+       }
+       dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = res->start;
+
+       davinci_i2s_dai.private_data = dev;
+       ret = snd_soc_register_dai(&davinci_i2s_dai);
+       if (ret != 0)
+               goto err_free_mem;
+
        return 0;
 
 err_free_mem:
@@ -498,62 +577,40 @@ err_release_region:
        return ret;
 }
 
-static void davinci_i2s_remove(struct platform_device *pdev,
-                              struct snd_soc_dai *dai)
+static int davinci_i2s_remove(struct platform_device *pdev)
 {
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
-       struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+       struct davinci_mcbsp_dev *dev = davinci_i2s_dai.private_data;
        struct resource *mem;
 
+       snd_soc_unregister_dai(&davinci_i2s_dai);
        clk_disable(dev->clk);
        clk_put(dev->clk);
        dev->clk = NULL;
-
        kfree(dev);
-
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(mem->start, (mem->end - mem->start) + 1);
-}
-
-#define DAVINCI_I2S_RATES      SNDRV_PCM_RATE_8000_96000
 
-static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
-       .startup        = davinci_i2s_startup,
-       .trigger        = davinci_i2s_trigger,
-       .hw_params      = davinci_i2s_hw_params,
-       .set_fmt        = davinci_i2s_set_dai_fmt,
-};
+       return 0;
+}
 
-struct snd_soc_dai davinci_i2s_dai = {
-       .name = "davinci-i2s",
-       .id = 0,
-       .probe = davinci_i2s_probe,
-       .remove = davinci_i2s_remove,
-       .playback = {
-               .channels_min = 2,
-               .channels_max = 2,
-               .rates = DAVINCI_I2S_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .capture = {
-               .channels_min = 2,
-               .channels_max = 2,
-               .rates = DAVINCI_I2S_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = &davinci_i2s_dai_ops,
+static struct platform_driver davinci_mcbsp_driver = {
+       .probe          = davinci_i2s_probe,
+       .remove         = davinci_i2s_remove,
+       .driver         = {
+               .name   = "davinci-asp",
+               .owner  = THIS_MODULE,
+       },
 };
-EXPORT_SYMBOL_GPL(davinci_i2s_dai);
 
 static int __init davinci_i2s_init(void)
 {
-       return snd_soc_register_dai(&davinci_i2s_dai);
+       return platform_driver_register(&davinci_mcbsp_driver);
 }
 module_init(davinci_i2s_init);
 
 static void __exit davinci_i2s_exit(void)
 {
-       snd_soc_unregister_dai(&davinci_i2s_dai);
+       platform_driver_unregister(&davinci_mcbsp_driver);
 }
 module_exit(davinci_i2s_exit);
 
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
new file mode 100644 (file)
index 0000000..f0c0347
--- /dev/null
@@ -0,0 +1,874 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "davinci-pcm.h"
+#include "davinci-mcasp.h"
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG          0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG    0x04
+
+#define DAVINCI_MCASP_PFUNC_REG                0x10
+#define DAVINCI_MCASP_PDIR_REG         0x14
+#define DAVINCI_MCASP_PDOUT_REG                0x18
+#define DAVINCI_MCASP_PDSET_REG                0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG                0x20
+
+#define DAVINCI_MCASP_TLGC_REG         0x30
+#define DAVINCI_MCASP_TLMR_REG         0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG       0x44
+#define DAVINCI_MCASP_AMUTE_REG                0x48
+#define DAVINCI_MCASP_LBCTL_REG                0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG     0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG      0x60
+#define DAVINCI_MCASP_RXMASK_REG       0x64
+#define DAVINCI_MCASP_RXFMT_REG                0x68
+#define DAVINCI_MCASP_RXFMCTL_REG      0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG     0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG    0x74
+#define DAVINCI_MCASP_RXTDM_REG                0x78
+#define DAVINCI_MCASP_EVTCTLR_REG      0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG       0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG    0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG     0x88
+#define DAVINCI_MCASP_REVTCTL_REG      0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG      0xa0
+#define DAVINCI_MCASP_TXMASK_REG       0xa4
+#define DAVINCI_MCASP_TXFMT_REG                0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG      0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG     0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG    0xb4
+#define DAVINCI_MCASP_TXTDM_REG                0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG      0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG       0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG    0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG     0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG      0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG      0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG      0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG      0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG      0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n)   (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+                                               (n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG                0x200
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG                0x280
+
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ *     Register Bits
+ */
+#define MCASP_FREE     BIT(0)
+#define MCASP_SOFT     BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR(n)         (1<<n)
+#define PFUNC_AMUTE    BIT(25)
+#define ACLKX          BIT(26)
+#define AHCLKX         BIT(27)
+#define AFSX           BIT(28)
+#define ACLKR          BIT(29)
+#define AHCLKR         BIT(30)
+#define AFSR           BIT(31)
+
+/*
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR(n)         (1<<n)
+#define PDIR_AMUTE     BIT(25)
+#define ACLKX          BIT(26)
+#define AHCLKX         BIT(27)
+#define AFSX           BIT(28)
+#define ACLKR          BIT(29)
+#define AHCLKR         BIT(30)
+#define AFSR           BIT(31)
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN  BIT(0)  /* Transmit DIT mode enable/disable */
+#define VA     BIT(2)
+#define VB     BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val)     (val)
+#define TXSEL          BIT(3)
+#define TXSSZ(val)     (val<<4)
+#define TXPBIT(val)    (val<<8)
+#define TXPAD(val)     (val<<13)
+#define TXORD          BIT(15)
+#define FSXDLY(val)    (val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val)     (val)
+#define RXSEL          BIT(3)
+#define RXSSZ(val)     (val<<4)
+#define RXPBIT(val)    (val<<8)
+#define RXPAD(val)     (val<<13)
+#define RXORD          BIT(15)
+#define FSRDLY(val)    (val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG -  Transmit Frame Control Register Bits
+ */
+#define FSXPOL         BIT(0)
+#define AFSXE          BIT(1)
+#define FSXDUR         BIT(4)
+#define FSXMOD(val)    (val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL         BIT(0)
+#define AFSRE          BIT(1)
+#define FSRDUR         BIT(4)
+#define FSRMOD(val)    (val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val)  (val)
+#define ACLKXE         BIT(5)
+#define TX_ASYNC       BIT(6)
+#define ACLKXPOL       BIT(7)
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val)  (val)
+#define ACLKRE         BIT(5)
+#define RX_ASYNC       BIT(6)
+#define ACLKRPOL       BIT(7)
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ *     Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXPOL      BIT(14)
+#define AHCLKXE                BIT(15)
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ *     Register Bits
+ */
+#define AHCLKRDIV(val) (val)
+#define AHCLKRPOL      BIT(14)
+#define AHCLKRE                BIT(15)
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits
+ */
+#define MODE(val)      (val)
+#define DISMOD         (val)(val<<2)
+#define TXSTATE                BIT(4)
+#define RXSTATE                BIT(5)
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN           BIT(0)
+#define LBORD          BIT(1)
+#define LBGENMODE(val) (val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n)      (1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n)      (1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG -  Global Control Register Bits
+ */
+#define RXCLKRST       BIT(0)  /* Receiver Clock Divider Reset */
+#define RXHCLKRST      BIT(1)  /* Receiver High Frequency Clock Divider */
+#define RXSERCLR       BIT(2)  /* Receiver Serializer Clear */
+#define RXSMRST                BIT(3)  /* Receiver State Machine Reset */
+#define RXFSRST                BIT(4)  /* Frame Sync Generator Reset */
+#define TXCLKRST       BIT(8)  /* Transmitter Clock Divider Reset */
+#define TXHCLKRST      BIT(9)  /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR       BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST                BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST                BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
+ */
+#define MUTENA(val)    (val)
+#define MUTEINPOL      BIT(2)
+#define MUTEINENA      BIT(3)
+#define MUTEIN         BIT(4)
+#define MUTER          BIT(5)
+#define MUTEX          BIT(6)
+#define MUTEFSR                BIT(7)
+#define MUTEFSX                BIT(8)
+#define MUTEBADCLKR    BIT(9)
+#define MUTEBADCLKX    BIT(10)
+#define MUTERXDMAERR   BIT(11)
+#define MUTETXDMAERR   BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS   BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS   BIT(0)
+
+#define DAVINCI_MCASP_NUM_SERIALIZER   16
+
+static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+{
+       __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+{
+       __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+{
+       __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+{
+       __raw_writel(val, reg);
+}
+
+static inline u32 mcasp_get_reg(void __iomem *reg)
+{
+       return (unsigned int)__raw_readl(reg);
+}
+
+static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+{
+       int i = 0;
+
+       mcasp_set_bits(regs, val);
+
+       /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+       /* loop count is to avoid the lock-up */
+       for (i = 0; i < 1000; i++) {
+               if ((mcasp_get_reg(regs) & val) == val)
+                       break;
+       }
+
+       if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+               printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+                                               struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_audio_dev *dev = cpu_dai->private_data;
+       cpu_dai->dma_data = dev->dma_params[substream->stream];
+       return 0;
+}
+
+static void mcasp_start_rx(struct davinci_audio_dev *dev)
+{
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+}
+
+static void mcasp_start_tx(struct davinci_audio_dev *dev)
+{
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+       mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+}
+
+static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
+{
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mcasp_start_tx(dev);
+       else
+               mcasp_start_rx(dev);
+}
+
+static void mcasp_stop_rx(struct davinci_audio_dev *dev)
+{
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void mcasp_stop_tx(struct davinci_audio_dev *dev)
+{
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0);
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
+{
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mcasp_stop_tx(dev);
+       else
+               mcasp_stop_rx(dev);
+}
+
+static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+                                        unsigned int fmt)
+{
+       struct davinci_audio_dev *dev = cpu_dai->private_data;
+       void __iomem *base = dev->base;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* codec is clock and frame slave */
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+               mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+               mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+               mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
+               break;
+
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* codec is clock and frame master */
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+               mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+               mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+               mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_IB_NF:
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+               mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+               mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               break;
+
+       case SND_SOC_DAIFMT_NB_IF:
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+               mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+               mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               break;
+
+       case SND_SOC_DAIFMT_IB_IF:
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+               mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+               mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               break;
+
+       case SND_SOC_DAIFMT_NB_NF:
+               mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+               mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+               mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+               mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int davinci_config_channel_size(struct davinci_audio_dev *dev,
+                                      int channel_size)
+{
+       u32 fmt = 0;
+
+       switch (channel_size) {
+       case DAVINCI_AUDIO_WORD_8:
+               fmt = 0x03;
+               break;
+
+       case DAVINCI_AUDIO_WORD_12:
+               fmt = 0x05;
+               break;
+
+       case DAVINCI_AUDIO_WORD_16:
+               fmt = 0x07;
+               break;
+
+       case DAVINCI_AUDIO_WORD_20:
+               fmt = 0x09;
+               break;
+
+       case DAVINCI_AUDIO_WORD_24:
+               fmt = 0x0B;
+               break;
+
+       case DAVINCI_AUDIO_WORD_28:
+               fmt = 0x0D;
+               break;
+
+       case DAVINCI_AUDIO_WORD_32:
+               fmt = 0x0F;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
+                                       RXSSZ(fmt), RXSSZ(0x0F));
+       mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+                                       TXSSZ(fmt), TXSSZ(0x0F));
+       return 0;
+}
+
+static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream)
+{
+       int i;
+
+       /* Default configuration */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+       /* All PINS as McASP */
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG,
+                               TXDATADMADIS);
+       } else {
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG,
+                               RXDATADMADIS);
+       }
+
+       for (i = 0; i < dev->num_serializer; i++) {
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
+                                       dev->serial_dir[i]);
+               if (dev->serial_dir[i] == TX_MODE)
+                       mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+                                       AXR(i));
+               else if (dev->serial_dir[i] == RX_MODE)
+                       mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+                                       AXR(i));
+       }
+}
+
+static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
+{
+       int i, active_slots;
+       u32 mask = 0;
+
+       active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots;
+       for (i = 0; i < active_slots; i++)
+               mask |= (1 << i);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* bit stream is MSB first  with no delay */
+               /* DSP_B mode */
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
+                               AHCLKXE);
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
+
+               if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+                       mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+                                       FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
+               else
+                       printk(KERN_ERR "playback tdm slot %d not supported\n",
+                               dev->tdm_slots);
+
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+       } else {
+               /* bit stream is MSB first with no delay */
+               /* DSP_B mode */
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
+                               AHCLKRE);
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
+
+               if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+                       mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
+                                       FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
+               else
+                       printk(KERN_ERR "capture tdm slot %d not supported\n",
+                               dev->tdm_slots);
+
+               mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+       }
+}
+
+/* S/PDIF */
+static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
+{
+       /* Set the PDIR for Serialiser as output */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX);
+
+       /* TXMASK for 24 bits */
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF);
+
+       /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
+          and LSB first */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+                                               TXROT(6) | TXSSZ(15));
+
+       /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+                                               AFSXE | FSXMOD(0x180));
+
+       /* Set the TX tdm : for all the slots */
+       mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+       /* Set the TX clock controls : div = 1 and internal */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
+                                               ACLKXE | TX_ASYNC);
+
+       mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+
+       /* Only 44100 and 48000 are valid, both have the same setting */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+
+       /* Enable the DIT */
+       mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+}
+
+static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params,
+                                       struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_audio_dev *dev = cpu_dai->private_data;
+       struct davinci_pcm_dma_params *dma_params =
+                                       dev->dma_params[substream->stream];
+       int word_length;
+
+       davinci_hw_common_param(dev, substream->stream);
+
+       if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
+               davinci_hw_dit_param(dev);
+       else
+               davinci_hw_param(dev, substream->stream);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               dma_params->data_type = 1;
+               word_length = DAVINCI_AUDIO_WORD_8;
+               break;
+
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dma_params->data_type = 2;
+               word_length = DAVINCI_AUDIO_WORD_16;
+               break;
+
+       case SNDRV_PCM_FORMAT_S32_LE:
+               dma_params->data_type = 4;
+               word_length = DAVINCI_AUDIO_WORD_32;
+               break;
+
+       default:
+               printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
+               return -EINVAL;
+       }
+       davinci_config_channel_size(dev, word_length);
+
+       return 0;
+}
+
+static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
+                                    int cmd, struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               davinci_mcasp_start(dev, substream->stream);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               davinci_mcasp_stop(dev, substream->stream);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+       .startup        = davinci_mcasp_startup,
+       .trigger        = davinci_mcasp_trigger,
+       .hw_params      = davinci_mcasp_hw_params,
+       .set_fmt        = davinci_mcasp_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_mcasp_dai[] = {
+       {
+               .name           = "davinci-i2s",
+               .id             = 0,
+               .playback       = {
+                       .channels_min   = 2,
+                       .channels_max   = 2,
+                       .rates          = DAVINCI_MCASP_RATES,
+                       .formats        = SNDRV_PCM_FMTBIT_S8 |
+                                               SNDRV_PCM_FMTBIT_S16_LE |
+                                               SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .capture        = {
+                       .channels_min   = 2,
+                       .channels_max   = 2,
+                       .rates          = DAVINCI_MCASP_RATES,
+                       .formats        = SNDRV_PCM_FMTBIT_S8 |
+                                               SNDRV_PCM_FMTBIT_S16_LE |
+                                               SNDRV_PCM_FMTBIT_S32_LE,
+               },
+               .ops            = &davinci_mcasp_dai_ops,
+
+       },
+       {
+               .name           = "davinci-dit",
+               .id             = 1,
+               .playback       = {
+                       .channels_min   = 1,
+                       .channels_max   = 384,
+                       .rates          = DAVINCI_MCASP_RATES,
+                       .formats        = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops            = &davinci_mcasp_dai_ops,
+       },
+
+};
+EXPORT_SYMBOL_GPL(davinci_mcasp_dai);
+
+static int davinci_mcasp_probe(struct platform_device *pdev)
+{
+       struct davinci_pcm_dma_params *dma_data;
+       struct resource *mem, *ioarea, *res;
+       struct snd_platform_data *pdata;
+       struct davinci_audio_dev *dev;
+       int count = 0;
+       int ret = 0;
+
+       dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL);
+       if (!dev)
+               return  -ENOMEM;
+
+       dma_data = kzalloc(sizeof(struct davinci_pcm_dma_params) * 2,
+                                                               GFP_KERNEL);
+       if (!dma_data) {
+               ret = -ENOMEM;
+               goto err_release_dev;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "no mem resource?\n");
+               ret = -ENODEV;
+               goto err_release_data;
+       }
+
+       ioarea = request_mem_region(mem->start,
+                       (mem->end - mem->start) + 1, pdev->name);
+       if (!ioarea) {
+               dev_err(&pdev->dev, "Audio region already claimed\n");
+               ret = -EBUSY;
+               goto err_release_data;
+       }
+
+       pdata = pdev->dev.platform_data;
+       dev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               ret = -ENODEV;
+               goto err_release_region;
+       }
+
+       clk_enable(dev->clk);
+
+       dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+       dev->op_mode = pdata->op_mode;
+       dev->tdm_slots = pdata->tdm_slots;
+       dev->num_serializer = pdata->num_serializer;
+       dev->serial_dir = pdata->serial_dir;
+       dev->codec_fmt = pdata->codec_fmt;
+
+       dma_data[count].name = "I2S PCM Stereo out";
+       dma_data[count].eventq_no = pdata->eventq_no;
+       dma_data[count].dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
+                                                       io_v2p(dev->base));
+       dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &dma_data[count];
+
+       /* first TX, then RX */
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no DMA resource\n");
+               goto err_release_region;
+       }
+
+       dma_data[count].channel = res->start;
+       count++;
+       dma_data[count].name = "I2S PCM Stereo in";
+       dma_data[count].eventq_no = pdata->eventq_no;
+       dma_data[count].dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
+                                                       io_v2p(dev->base));
+       dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &dma_data[count];
+
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!res) {
+               dev_err(&pdev->dev, "no DMA resource\n");
+               goto err_release_region;
+       }
+
+       dma_data[count].channel = res->start;
+       davinci_mcasp_dai[pdev->id].private_data = dev;
+       davinci_mcasp_dai[pdev->id].dev = &pdev->dev;
+       ret = snd_soc_register_dai(&davinci_mcasp_dai[pdev->id]);
+
+       if (ret != 0)
+               goto err_release_region;
+       return 0;
+
+err_release_region:
+       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+err_release_data:
+       kfree(dma_data);
+err_release_dev:
+       kfree(dev);
+
+       return ret;
+}
+
+static int davinci_mcasp_remove(struct platform_device *pdev)
+{
+       struct davinci_pcm_dma_params *dma_data;
+       struct davinci_audio_dev *dev;
+       struct resource *mem;
+
+       snd_soc_unregister_dai(&davinci_mcasp_dai[pdev->id]);
+       dev = davinci_mcasp_dai[pdev->id].private_data;
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       dev->clk = NULL;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+       dma_data = dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+       kfree(dma_data);
+       kfree(dev);
+
+       return 0;
+}
+
+static struct platform_driver davinci_mcasp_driver = {
+       .probe          = davinci_mcasp_probe,
+       .remove         = davinci_mcasp_remove,
+       .driver         = {
+               .name   = "davinci-mcasp",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init davinci_mcasp_init(void)
+{
+       return platform_driver_register(&davinci_mcasp_driver);
+}
+module_init(davinci_mcasp_init);
+
+static void __exit davinci_mcasp_exit(void)
+{
+       platform_driver_unregister(&davinci_mcasp_driver);
+}
+module_exit(davinci_mcasp_exit);
+
+MODULE_AUTHOR("Steve Chen");
+MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
new file mode 100644 (file)
index 0000000..36b7104
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * 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 DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+#include <linux/io.h>
+#include <mach/asp.h>
+#include "davinci-pcm.h"
+
+extern struct snd_soc_dai davinci_mcasp_dai[];
+
+#define DAVINCI_MCASP_RATES    SNDRV_PCM_RATE_8000_96000
+#define DAVINCI_MCASP_I2S_DAI  0
+#define DAVINCI_MCASP_DIT_DAI  1
+
+enum {
+       DAVINCI_AUDIO_WORD_8 = 0,
+       DAVINCI_AUDIO_WORD_12,
+       DAVINCI_AUDIO_WORD_16,
+       DAVINCI_AUDIO_WORD_20,
+       DAVINCI_AUDIO_WORD_24,
+       DAVINCI_AUDIO_WORD_32,
+       DAVINCI_AUDIO_WORD_28,  /* This is only valid for McASP */
+};
+
+struct davinci_audio_dev {
+       void __iomem *base;
+       int sample_rate;
+       struct clk *clk;
+       struct davinci_pcm_dma_params *dma_params[2];
+       unsigned int codec_fmt;
+
+       /* McASP specific data */
+       int     tdm_slots;
+       u8      op_mode;
+       u8      num_serializer;
+       u8      *serial_dir;
+};
+
+#endif /* DAVINCI_MCASP_H */
index a059965..8fd0c3c 100644 (file)
@@ -206,6 +206,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
        /* Copy self-linked parameter RAM entry into master channel */
        edma_read_slot(prtd->slave_lch, &temp);
        edma_write_slot(prtd->master_lch, &temp);
+       davinci_pcm_enqueue_dma(substream);
 
        return 0;
 }
index 62cb4eb..eb4287f 100644 (file)
 #ifndef _DAVINCI_PCM_H
 #define _DAVINCI_PCM_H
 
+#include <mach/edma.h>
+#include <mach/asp.h>
+
+
 struct davinci_pcm_dma_params {
-       char *name;             /* stream identifier */
-       int channel;            /* sync dma channel ID */
-       dma_addr_t dma_addr;    /* device physical address for DMA */
-       unsigned int data_type; /* xfer data type */
+       char *name;                     /* stream identifier */
+       int channel;                    /* sync dma channel ID */
+       dma_addr_t dma_addr;            /* device physical address for DMA */
+       enum dma_event_q eventq_no;     /* event queue number */
+       unsigned char data_type;        /* xfer data type */
+       unsigned char convert_mono_stereo;
 };
 
-struct evm_snd_platform_data {
-       int tx_dma_ch;
-       int rx_dma_ch;
-};
 
 extern struct snd_soc_platform davinci_soc_platform;
 
index 7eb5499..c4ae3e0 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
+#include <linux/delay.h>
 
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -112,7 +113,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
        out_8(&regs->op1, MPC52xx_PSC_OP_RES);
        udelay(10);
        out_8(&regs->op0, MPC52xx_PSC_OP_RES);
-       udelay(50);
+       msleep(1);
        psc_ac97_warm_reset(ac97);
 }
 
index b771238..a5a90e5 100644 (file)
@@ -72,4 +72,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE
        help
          Say Y if you want to add support for SoC audio on the Beagleboard.
 
+config SND_OMAP_SOC_ZOOM2
+       tristate "SoC Audio support for Zoom2"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for Soc audio on Zoom2 board.
 
index a37f498..fefc48f 100644 (file)
@@ -14,6 +14,7 @@ snd-soc-omap3evm-objs := omap3evm.o
 snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap3pandora-objs := omap3pandora.o
 snd-soc-omap3beagle-objs := omap3beagle.o
+snd-soc-zoom2-objs := zoom2.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
@@ -23,3 +24,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
 obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
+obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
index 84a1950..c3c931d 100644 (file)
@@ -330,7 +330,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
        }
 }
 
-int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
                 struct snd_pcm *pcm)
 {
        int ret = 0;
index b719e5d..f7e5b74 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -39,6 +40,9 @@
 #include "omap-pcm.h"
 #include "../codecs/twl4030.h"
 
+#define TWL4030_INTBR_PMBR1    0x0D
+#define EXTMUTE(value)         (value << 2)
+
 static struct snd_soc_card snd_soc_sdp3430;
 
 static int sdp3430_hw_params(struct snd_pcm_substream *substream,
@@ -96,7 +100,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream,
        ret = snd_soc_dai_set_fmt(codec_dai,
                                SND_SOC_DAIFMT_DSP_A |
                                SND_SOC_DAIFMT_IB_NF |
-                               SND_SOC_DAIFMT_CBS_CFM);
+                               SND_SOC_DAIFMT_CBM_CFM);
        if (ret) {
                printk(KERN_ERR "can't set codec DAI configuration\n");
                return ret;
@@ -280,6 +284,7 @@ static struct snd_soc_card snd_soc_sdp3430 = {
 static struct twl4030_setup_data twl4030_setup = {
        .ramp_delay_value = 3,
        .sysclk = 26000,
+       .hs_extmute = 1,
 };
 
 /* Audio subsystem */
@@ -312,6 +317,10 @@ static int __init sdp3430_soc_init(void)
        *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
        *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
 
+       /* Set TWL4030 GPIO6 as EXTMUTE signal */
+       twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, EXTMUTE(0x02),
+                                                       TWL4030_MODULE_INTBR);
+
        ret = platform_device_add(sdp3430_snd_device);
        if (ret)
                goto err1;
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
new file mode 100644 (file)
index 0000000..f90b45f
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * zoom2.c  --  SoC audio for Zoom2
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+#define ZOOM2_HEADSET_MUX_GPIO         (OMAP_MAX_GPIO_LINES + 15)
+#define ZOOM2_HEADSET_EXTMUTE_GPIO     153
+
+static int zoom2_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                       SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops zoom2_ops = {
+       .hw_params = zoom2_hw_params,
+};
+
+static int zoom2_hw_voice_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                               SND_SOC_DAIFMT_DSP_A |
+                               SND_SOC_DAIFMT_IB_NF |
+                               SND_SOC_DAIFMT_CBM_CFM);
+       if (ret) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                               SND_SOC_DAIFMT_DSP_A |
+                               SND_SOC_DAIFMT_IB_NF |
+                               SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                       SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops zoom2_voice_ops = {
+       .hw_params = zoom2_hw_voice_params,
+};
+
+/* Zoom2 machine DAPM */
+static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Ext Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+       SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* External Mics: MAINMIC, SUBMIC with bias*/
+       {"MAINMIC", NULL, "Mic Bias 1"},
+       {"SUBMIC", NULL, "Mic Bias 2"},
+       {"Mic Bias 1", NULL, "Ext Mic"},
+       {"Mic Bias 2", NULL, "Ext Mic"},
+
+       /* External Speakers: HFL, HFR */
+       {"Ext Spk", NULL, "HFL"},
+       {"Ext Spk", NULL, "HFR"},
+
+       /* Headset Stereophone:  HSOL, HSOR */
+       {"Headset Stereophone", NULL, "HSOL"},
+       {"Headset Stereophone", NULL, "HSOR"},
+
+       /* Headset Mic: HSMIC with bias */
+       {"HSMIC", NULL, "Headset Mic Bias"},
+       {"Headset Mic Bias", NULL, "Headset Mic"},
+
+       /* Aux In: AUXL, AUXR */
+       {"Aux In", NULL, "AUXL"},
+       {"Aux In", NULL, "AUXR"},
+};
+
+static int zoom2_twl4030_init(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       /* Add Zoom2 specific widgets */
+       ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets,
+                               ARRAY_SIZE(zoom2_twl4030_dapm_widgets));
+       if (ret)
+               return ret;
+
+       /* Set up Zoom2 specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       /* Zoom2 connected pins */
+       snd_soc_dapm_enable_pin(codec, "Ext Mic");
+       snd_soc_dapm_enable_pin(codec, "Ext Spk");
+       snd_soc_dapm_enable_pin(codec, "Headset Mic");
+       snd_soc_dapm_enable_pin(codec, "Headset Stereophone");
+       snd_soc_dapm_enable_pin(codec, "Aux In");
+
+       /* TWL4030 not connected pins */
+       snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+       snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
+       snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
+
+       snd_soc_dapm_nc_pin(codec, "OUTL");
+       snd_soc_dapm_nc_pin(codec, "OUTR");
+       snd_soc_dapm_nc_pin(codec, "EARPIECE");
+       snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
+       snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+       snd_soc_dapm_nc_pin(codec, "CARKITL");
+       snd_soc_dapm_nc_pin(codec, "CARKITR");
+
+       ret = snd_soc_dapm_sync(codec);
+
+       return ret;
+}
+
+static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec)
+{
+       unsigned short reg;
+
+       /* Enable voice interface */
+       reg = codec->read(codec, TWL4030_REG_VOICE_IF);
+       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
+       codec->write(codec, TWL4030_REG_VOICE_IF, reg);
+
+       return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link zoom2_dai[] = {
+       {
+               .name = "TWL4030 I2S",
+               .stream_name = "TWL4030 Audio",
+               .cpu_dai = &omap_mcbsp_dai[0],
+               .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+               .init = zoom2_twl4030_init,
+               .ops = &zoom2_ops,
+       },
+       {
+               .name = "TWL4030 PCM",
+               .stream_name = "TWL4030 Voice",
+               .cpu_dai = &omap_mcbsp_dai[1],
+               .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
+               .init = zoom2_twl4030_voice_init,
+               .ops = &zoom2_voice_ops,
+       },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_zoom2 = {
+       .name = "Zoom2",
+       .platform = &omap_soc_platform,
+       .dai_link = zoom2_dai,
+       .num_links = ARRAY_SIZE(zoom2_dai),
+};
+
+/* EXTMUTE callback function */
+void zoom2_set_hs_extmute(int mute)
+{
+       gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute);
+}
+
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+       .ramp_delay_value = 3,  /* 161 ms */
+       .sysclk = 26000,
+       .hs_extmute = 1,
+       .set_hs_extmute = zoom2_set_hs_extmute,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device zoom2_snd_devdata = {
+       .card = &snd_soc_zoom2,
+       .codec_dev = &soc_codec_dev_twl4030,
+       .codec_data = &twl4030_setup,
+};
+
+static struct platform_device *zoom2_snd_device;
+
+static int __init zoom2_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap_zoom2()) {
+               pr_debug("Not Zoom2!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "Zoom2 SoC init\n");
+
+       zoom2_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!zoom2_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata);
+       zoom2_snd_devdata.dev = &zoom2_snd_device->dev;
+       *(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
+       *(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
+
+       ret = platform_device_add(zoom2_snd_device);
+       if (ret)
+               goto err1;
+
+       BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0);
+       gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0);
+
+       BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0);
+       gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0);
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(zoom2_snd_device);
+
+       return ret;
+}
+module_init(zoom2_soc_init);
+
+static void __exit zoom2_soc_exit(void)
+{
+       gpio_free(ZOOM2_HEADSET_MUX_GPIO);
+       gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO);
+
+       platform_device_unregister(zoom2_snd_device);
+}
+module_exit(zoom2_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC Zoom2");
+MODULE_LICENSE("GPL");
+
index 326955d..8889cd3 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/i2c.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/uda1380.h>
 
 #include <mach/magician.h>
 #include <asm/mach-types.h>
@@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = {
        .platform = &pxa2xx_soc_platform,
 };
 
-/* magician audio private data */
-static struct uda1380_setup_data magician_uda1380_setup = {
-       .i2c_address = 0x18,
-       .dac_clk = UDA1380_DAC_CLK_WSPLL,
-};
-
 /* magician audio subsystem */
 static struct snd_soc_device magician_snd_devdata = {
        .card = &snd_soc_card_magician,
        .codec_dev = &soc_codec_dev_uda1380,
-       .codec_data = &magician_uda1380_setup,
 };
 
 static struct platform_device *magician_snd_device;
 
+/*
+ * FIXME: move into magician board file once merged into the pxa tree
+ */
+static struct uda1380_platform_data uda1380_info = {
+       .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
+       .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
+       .dac_clk    = UDA1380_DAC_CLK_WSPLL,
+};
+
+static struct i2c_board_info i2c_board_info[] = {
+       {
+               I2C_BOARD_INFO("uda1380", 0x18),
+               .platform_data = &uda1380_info,
+       },
+};
+
 static int __init magician_init(void)
 {
        int ret;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
 
        if (!machine_is_magician())
                return -ENODEV;
 
-       ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER");
-       if (ret)
-               goto err_request_power;
-       ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET");
-       if (ret)
-               goto err_request_reset;
+       adapter = i2c_get_adapter(0);
+       if (!adapter)
+               return -ENODEV;
+       client = i2c_new_device(adapter, i2c_board_info);
+       i2c_put_adapter(adapter);
+       if (!client)
+               return -ENODEV;
+
        ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
        if (ret)
                goto err_request_spk;
@@ -491,14 +506,8 @@ static int __init magician_init(void)
        if (ret)
                goto err_request_in_sel1;
 
-       gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
        gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
 
-       /* we may need to have the clock running here - pH5 */
-       gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
-       udelay(5);
-       gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
-
        magician_snd_device = platform_device_alloc("soc-audio", -1);
        if (!magician_snd_device) {
                ret = -ENOMEM;
@@ -526,10 +535,6 @@ err_request_mic:
 err_request_ep:
        gpio_free(EGPIO_MAGICIAN_SPK_POWER);
 err_request_spk:
-       gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
-err_request_reset:
-       gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
-err_request_power:
        return ret;
 }
 
@@ -540,15 +545,12 @@ static void __exit magician_exit(void)
        gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
        gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
        gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
-       gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
 
        gpio_free(EGPIO_MAGICIAN_IN_SEL1);
        gpio_free(EGPIO_MAGICIAN_IN_SEL0);
        gpio_free(EGPIO_MAGICIAN_MIC_POWER);
        gpio_free(EGPIO_MAGICIAN_EP_POWER);
        gpio_free(EGPIO_MAGICIAN_SPK_POWER);
-       gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
-       gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
 }
 
 module_init(magician_init);
index 19c4540..e22c5ce 100644 (file)
@@ -457,31 +457,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
-       ssp_write_reg(ssp, SSCR0, sscr0);
-       ssp_write_reg(ssp, SSCR1, sscr1);
-       ssp_write_reg(ssp, SSPSP, sspsp);
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               sspsp |= SSPSP_SFRMP;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               sspsp |= SSPSP_SCMODE(2);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
+               break;
+       default:
+               return -EINVAL;
+       }
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                sscr0 |= SSCR0_PSP;
                sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
-
                /* See hw_params() */
-               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-               case SND_SOC_DAIFMT_NB_NF:
-                       sspsp |= SSPSP_SFRMP;
-                       break;
-               case SND_SOC_DAIFMT_NB_IF:
-                       break;
-               case SND_SOC_DAIFMT_IB_IF:
-                       sspsp |= SSPSP_SCMODE(2);
-                       break;
-               case SND_SOC_DAIFMT_IB_NF:
-                       sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
-                       break;
-               default:
-                       return -EINVAL;
-               }
                break;
 
        case SND_SOC_DAIFMT_DSP_A:
@@ -489,22 +485,6 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        case SND_SOC_DAIFMT_DSP_B:
                sscr0 |= SSCR0_MOD | SSCR0_PSP;
                sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
-
-               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-               case SND_SOC_DAIFMT_NB_NF:
-                       sspsp |= SSPSP_SFRMP;
-                       break;
-               case SND_SOC_DAIFMT_NB_IF:
-                       break;
-               case SND_SOC_DAIFMT_IB_IF:
-                       sspsp |= SSPSP_SCMODE(2);
-                       break;
-               case SND_SOC_DAIFMT_IB_NF:
-                       sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
-                       break;
-               default:
-                       return -EINVAL;
-               }
                break;
 
        default:
index 1d70829..8bf49a4 100644 (file)
@@ -619,8 +619,9 @@ static struct snd_pcm_ops soc_pcm_ops = {
 
 #ifdef CONFIG_PM
 /* powers down audio subsystem for suspend */
-static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+static int soc_suspend(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = card->platform;
@@ -656,7 +657,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
                snd_pcm_suspend_all(card->dai_link[i].pcm);
 
        if (card->suspend_pre)
-               card->suspend_pre(pdev, state);
+               card->suspend_pre(pdev, PMSG_SUSPEND);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
@@ -682,7 +683,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        }
 
        if (codec_dev->suspend)
-               codec_dev->suspend(pdev, state);
+               codec_dev->suspend(pdev, PMSG_SUSPEND);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
@@ -691,7 +692,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        }
 
        if (card->suspend_post)
-               card->suspend_post(pdev, state);
+               card->suspend_post(pdev, PMSG_SUSPEND);
 
        return 0;
 }
@@ -765,8 +766,9 @@ static void soc_resume_deferred(struct work_struct *work)
 }
 
 /* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+static int soc_resume(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_card *card = socdev->card;
        struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
@@ -788,6 +790,44 @@ static int soc_resume(struct platform_device *pdev)
        return 0;
 }
 
+/**
+ * snd_soc_suspend_device: Notify core of device suspend
+ *
+ * @dev: Device being suspended.
+ *
+ * In order to ensure that the entire audio subsystem is suspended in a
+ * coordinated fashion ASoC devices should suspend themselves when
+ * called by ASoC.  When the standard kernel suspend process asks the
+ * device to suspend it should call this function to initiate a suspend
+ * of the entire ASoC card.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_suspend_device(struct device *dev)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
+
+/**
+ * snd_soc_resume_device: Notify core of device resume
+ *
+ * @dev: Device being resumed.
+ *
+ * In order to ensure that the entire audio subsystem is resumed in a
+ * coordinated fashion ASoC devices should resume themselves when called
+ * by ASoC.  When the standard kernel resume process asks the device
+ * to resume it should call this function.  Once all the components of
+ * the card have notified that they are ready to be resumed the card
+ * will be resumed.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_resume_device(struct device *dev)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_resume_device);
 #else
 #define soc_suspend    NULL
 #define soc_resume     NULL
@@ -981,16 +1021,39 @@ static int soc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int soc_poweroff(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = socdev->card;
+
+       if (!card->instantiated)
+               return 0;
+
+       /* Flush out pmdown_time work - we actually do want to run it
+        * now, we're shutting down so no imminent restart. */
+       run_delayed_work(&card->delayed_work);
+
+       snd_soc_dapm_shutdown(socdev);
+
+       return 0;
+}
+
+static struct dev_pm_ops soc_pm_ops = {
+       .suspend = soc_suspend,
+       .resume = soc_resume,
+       .poweroff = soc_poweroff,
+};
+
 /* ASoC platform driver */
 static struct platform_driver soc_driver = {
        .driver         = {
                .name           = "soc-audio",
                .owner          = THIS_MODULE,
+               .pm             = &soc_pm_ops,
        },
        .probe          = soc_probe,
        .remove         = soc_remove,
-       .suspend        = soc_suspend,
-       .resume         = soc_resume,
 };
 
 /* create a new pcm */
@@ -1062,6 +1125,23 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        return ret;
 }
 
+/**
+ * snd_soc_codec_volatile_register: Report if a register is volatile.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indiciating if a CODEC register is volatile.
+ */
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
+{
+       if (codec->volatile_register)
+               return codec->volatile_register(reg);
+       else
+               return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
+
 /* codec register dump */
 static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
 {
@@ -1264,10 +1344,10 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
  * Returns 1 for change else 0.
  */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value)
+                               unsigned int mask, unsigned int value)
 {
        int change;
-       unsigned short old, new;
+       unsigned int old, new;
 
        mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
@@ -1294,10 +1374,10 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits);
  * Returns 1 for change else 0.
  */
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value)
+                               unsigned int mask, unsigned int value)
 {
        int change;
-       unsigned short old, new;
+       unsigned int old, new;
 
        mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
@@ -1586,7 +1666,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val, bitmask;
+       unsigned int val, bitmask;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1615,8 +1695,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val;
-       unsigned short mask, bitmask;
+       unsigned int val;
+       unsigned int mask, bitmask;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1652,7 +1732,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short reg_val, val, mux;
+       unsigned int reg_val, val, mux;
 
        reg_val = snd_soc_read(codec, e->reg);
        val = (reg_val >> e->shift_l) & e->mask;
@@ -1691,8 +1771,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val;
-       unsigned short mask;
+       unsigned int val;
+       unsigned int mask;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -1852,7 +1932,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned short val, val2, val_mask;
+       unsigned int val, val2, val_mask;
 
        val = (ucontrol->value.integer.value[0] & mask);
        if (invert)
@@ -1918,7 +1998,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
        int max = mc->max;
-       unsigned int mask = (1<<fls(max))-1;
+       unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
 
        ucontrol->value.integer.value[0] =
@@ -1958,7 +2038,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        int err;
-       unsigned short val, val2, val_mask;
+       unsigned int val, val2, val_mask;
 
        val_mask = mask << shift;
        val = (ucontrol->value.integer.value[0] & mask);
@@ -2050,7 +2130,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        int min = mc->min;
-       unsigned short val;
+       unsigned int val;
 
        val = (ucontrol->value.integer.value[0]+min) & 0xff;
        val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
index 21c6907..c68c204 100644 (file)
 
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
-       snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias,
-       snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
-       snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl,
-       snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
-       snd_soc_dapm_post
+       [snd_soc_dapm_pre] = 0,
+       [snd_soc_dapm_supply] = 1,
+       [snd_soc_dapm_micbias] = 2,
+       [snd_soc_dapm_mic] = 3,
+       [snd_soc_dapm_mux] = 4,
+       [snd_soc_dapm_value_mux] = 4,
+       [snd_soc_dapm_dac] = 5,
+       [snd_soc_dapm_mixer] = 6,
+       [snd_soc_dapm_mixer_named_ctl] = 6,
+       [snd_soc_dapm_pga] = 7,
+       [snd_soc_dapm_adc] = 8,
+       [snd_soc_dapm_hp] = 9,
+       [snd_soc_dapm_spk] = 10,
+       [snd_soc_dapm_post] = 11,
 };
 
 static int dapm_down_seq[] = {
-       snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
-       snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
-       snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
-       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply,
-       snd_soc_dapm_post
+       [snd_soc_dapm_pre] = 0,
+       [snd_soc_dapm_adc] = 1,
+       [snd_soc_dapm_hp] = 2,
+       [snd_soc_dapm_spk] = 3,
+       [snd_soc_dapm_pga] = 4,
+       [snd_soc_dapm_mixer_named_ctl] = 5,
+       [snd_soc_dapm_mixer] = 5,
+       [snd_soc_dapm_dac] = 6,
+       [snd_soc_dapm_mic] = 7,
+       [snd_soc_dapm_micbias] = 8,
+       [snd_soc_dapm_mux] = 9,
+       [snd_soc_dapm_value_mux] = 9,
+       [snd_soc_dapm_supply] = 10,
+       [snd_soc_dapm_post] = 11,
 };
 
 static void pop_wait(u32 pop_time)
@@ -268,7 +286,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec,
 static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 {
        int change, power;
-       unsigned short old, new;
+       unsigned int old, new;
        struct snd_soc_codec *codec = widget->codec;
 
        /* check for valid widgets */
@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
        return power;
 }
 
-/*
- * Scan a single DAPM widget for a complete audio path and update the
- * power status appropriately.
- */
-static int dapm_power_widget(struct snd_soc_codec *codec, int event,
-                            struct snd_soc_dapm_widget *w)
+static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
+                           struct snd_soc_dapm_widget *b,
+                           int sort[])
 {
-       int ret;
+       if (sort[a->id] != sort[b->id])
+               return sort[a->id] - sort[b->id];
+       if (a->reg != b->reg)
+               return a->reg - b->reg;
 
-       switch (w->id) {
-       case snd_soc_dapm_pre:
-               if (!w->event)
-                       return 0;
+       return 0;
+}
 
-               if (event == SND_SOC_DAPM_STREAM_START) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_PRE_PMU);
+/* Insert a widget in order into a DAPM power sequence. */
+static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
+                           struct list_head *list,
+                           int sort[])
+{
+       struct snd_soc_dapm_widget *w;
+
+       list_for_each_entry(w, list, power_list)
+               if (dapm_seq_compare(new_widget, w, sort) < 0) {
+                       list_add_tail(&new_widget->power_list, &w->power_list);
+                       return;
+               }
+
+       list_add_tail(&new_widget->power_list, list);
+}
+
+/* Apply the coalesced changes from a DAPM sequence */
+static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
+                                  struct list_head *pending)
+{
+       struct snd_soc_dapm_widget *w;
+       int reg, power, ret;
+       unsigned int value = 0;
+       unsigned int mask = 0;
+       unsigned int cur_mask;
+
+       reg = list_first_entry(pending, struct snd_soc_dapm_widget,
+                              power_list)->reg;
+
+       list_for_each_entry(w, pending, power_list) {
+               cur_mask = 1 << w->shift;
+               BUG_ON(reg != w->reg);
+
+               if (w->invert)
+                       power = !w->power;
+               else
+                       power = w->power;
+
+               mask |= cur_mask;
+               if (power)
+                       value |= cur_mask;
+
+               pop_dbg(codec->pop_time,
+                       "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
+                       w->name, reg, value, mask);
+
+               /* power up pre event */
+               if (w->power && w->event &&
+                   (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+                       pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
+                               w->name);
+                       ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
                        if (ret < 0)
-                               return ret;
-               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_PRE_PMD);
+                               pr_err("%s: pre event failed: %d\n",
+                                      w->name, ret);
+               }
+
+               /* power down pre event */
+               if (!w->power && w->event &&
+                   (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+                       pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
+                               w->name);
+                       ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
                        if (ret < 0)
-                               return ret;
+                               pr_err("%s: pre event failed: %d\n",
+                                      w->name, ret);
                }
-               return 0;
 
-       case snd_soc_dapm_post:
-               if (!w->event)
-                       return 0;
+               /* Lower PGA volume to reduce pops */
+               if (w->id == snd_soc_dapm_pga && !w->power)
+                       dapm_set_pga(w, w->power);
+       }
 
-               if (event == SND_SOC_DAPM_STREAM_START) {
+       if (reg >= 0) {
+               pop_dbg(codec->pop_time,
+                       "pop test : Applying 0x%x/0x%x to %x in %dms\n",
+                       value, mask, reg, codec->pop_time);
+               pop_wait(codec->pop_time);
+               snd_soc_update_bits(codec, reg, mask, value);
+       }
+
+       list_for_each_entry(w, pending, power_list) {
+               /* Raise PGA volume to reduce pops */
+               if (w->id == snd_soc_dapm_pga && w->power)
+                       dapm_set_pga(w, w->power);
+
+               /* power up post event */
+               if (w->power && w->event &&
+                   (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+                       pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
+                               w->name);
                        ret = w->event(w,
                                       NULL, SND_SOC_DAPM_POST_PMU);
                        if (ret < 0)
-                               return ret;
-               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_POST_PMD);
+                               pr_err("%s: post event failed: %d\n",
+                                      w->name, ret);
+               }
+
+               /* power down post event */
+               if (!w->power && w->event &&
+                   (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+                       pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
+                               w->name);
+                       ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
                        if (ret < 0)
-                               return ret;
+                               pr_err("%s: post event failed: %d\n",
+                                      w->name, ret);
                }
-               return 0;
+       }
+}
 
-       default:
-               return dapm_generic_apply_power(w);
+/* Apply a DAPM power sequence.
+ *
+ * We walk over a pre-sorted list of widgets to apply power to.  In
+ * order to minimise the number of writes to the device required
+ * multiple widgets will be updated in a single write where possible.
+ * Currently anything that requires more than a single write is not
+ * handled.
+ */
+static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
+                        int event, int sort[])
+{
+       struct snd_soc_dapm_widget *w, *n;
+       LIST_HEAD(pending);
+       int cur_sort = -1;
+       int cur_reg = SND_SOC_NOPM;
+       int ret;
+
+       list_for_each_entry_safe(w, n, list, power_list) {
+               ret = 0;
+
+               /* Do we need to apply any queued changes? */
+               if (sort[w->id] != cur_sort || w->reg != cur_reg) {
+                       if (!list_empty(&pending))
+                               dapm_seq_run_coalesced(codec, &pending);
+
+                       INIT_LIST_HEAD(&pending);
+                       cur_sort = -1;
+                       cur_reg = SND_SOC_NOPM;
+               }
+
+               switch (w->id) {
+               case snd_soc_dapm_pre:
+                       if (!w->event)
+                               list_for_each_entry_safe_continue(w, n, list,
+                                                                 power_list);
+
+                       if (event == SND_SOC_DAPM_STREAM_START)
+                               ret = w->event(w,
+                                              NULL, SND_SOC_DAPM_PRE_PMU);
+                       else if (event == SND_SOC_DAPM_STREAM_STOP)
+                               ret = w->event(w,
+                                              NULL, SND_SOC_DAPM_PRE_PMD);
+                       break;
+
+               case snd_soc_dapm_post:
+                       if (!w->event)
+                               list_for_each_entry_safe_continue(w, n, list,
+                                                                 power_list);
+
+                       if (event == SND_SOC_DAPM_STREAM_START)
+                               ret = w->event(w,
+                                              NULL, SND_SOC_DAPM_POST_PMU);
+                       else if (event == SND_SOC_DAPM_STREAM_STOP)
+                               ret = w->event(w,
+                                              NULL, SND_SOC_DAPM_POST_PMD);
+                       break;
+
+               case snd_soc_dapm_input:
+               case snd_soc_dapm_output:
+               case snd_soc_dapm_hp:
+               case snd_soc_dapm_mic:
+               case snd_soc_dapm_line:
+               case snd_soc_dapm_spk:
+                       /* No register support currently */
+                       ret = dapm_generic_apply_power(w);
+                       break;
+
+               default:
+                       /* Queue it up for application */
+                       cur_sort = sort[w->id];
+                       cur_reg = w->reg;
+                       list_move(&w->power_list, &pending);
+                       break;
+               }
+
+               if (ret < 0)
+                       pr_err("Failed to apply widget power: %d\n",
+                              ret);
        }
+
+       if (!list_empty(&pending))
+               dapm_seq_run_coalesced(codec, &pending);
 }
 
 /*
@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 {
        struct snd_soc_device *socdev = codec->socdev;
        struct snd_soc_dapm_widget *w;
+       LIST_HEAD(up_list);
+       LIST_HEAD(down_list);
        int ret = 0;
-       int i, power;
+       int power;
        int sys_power = 0;
 
-       INIT_LIST_HEAD(&codec->up_list);
-       INIT_LIST_HEAD(&codec->down_list);
-
        /* Check which widgets we need to power and store them in
         * lists indicating if they should be powered up or down.
         */
        list_for_each_entry(w, &codec->dapm_widgets, list) {
                switch (w->id) {
                case snd_soc_dapm_pre:
-                       list_add_tail(&codec->down_list, &w->power_list);
+                       dapm_seq_insert(w, &down_list, dapm_down_seq);
                        break;
                case snd_soc_dapm_post:
-                       list_add_tail(&codec->up_list, &w->power_list);
+                       dapm_seq_insert(w, &up_list, dapm_up_seq);
                        break;
 
                default:
@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                                continue;
 
                        if (power)
-                               list_add_tail(&w->power_list, &codec->up_list);
+                               dapm_seq_insert(w, &up_list, dapm_up_seq);
                        else
-                               list_add_tail(&w->power_list,
-                                             &codec->down_list);
+                               dapm_seq_insert(w, &down_list, dapm_down_seq);
 
                        w->power = power;
                        break;
@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
        }
 
        /* Power down widgets first; try to avoid amplifying pops. */
-       for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
-               list_for_each_entry(w, &codec->down_list, power_list) {
-                       /* is widget in stream order */
-                       if (w->id != dapm_down_seq[i])
-                               continue;
-
-                       ret = dapm_power_widget(codec, event, w);
-                       if (ret != 0)
-                               pr_err("Failed to power down %s: %d\n",
-                                      w->name, ret);
-               }
-       }
+       dapm_seq_run(codec, &down_list, event, dapm_down_seq);
 
        /* Now power up. */
-       for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
-               list_for_each_entry(w, &codec->up_list, power_list) {
-                       /* is widget in stream order */
-                       if (w->id != dapm_up_seq[i])
-                               continue;
-
-                       ret = dapm_power_widget(codec, event, w);
-                       if (ret != 0)
-                               pr_err("Failed to power up %s: %d\n",
-                                      w->name, ret);
-               }
-       }
+       dapm_seq_run(codec, &up_list, event, dapm_up_seq);
 
        /* If we just powered the last thing off drop to standby bias */
        if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
@@ -845,6 +997,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                        pr_err("Failed to apply active bias: %d\n", ret);
        }
 
+       pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
+               codec->pop_time);
+
        return 0;
 }
 
@@ -1138,8 +1293,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        if (wsink->id == snd_soc_dapm_input) {
                if (wsource->id == snd_soc_dapm_micbias ||
                        wsource->id == snd_soc_dapm_mic ||
-                       wsink->id == snd_soc_dapm_line ||
-                       wsink->id == snd_soc_dapm_output)
+                       wsource->id == snd_soc_dapm_line ||
+                       wsource->id == snd_soc_dapm_output)
                        wsink->ext = 1;
        }
        if (wsource->id == snd_soc_dapm_output) {
@@ -1372,7 +1527,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned short val, val2, val_mask;
+       unsigned int val, val2, val_mask;
        int ret;
 
        val = (ucontrol->value.integer.value[0] & mask);
@@ -1436,7 +1591,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val, bitmask;
+       unsigned int val, bitmask;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1464,8 +1619,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val, mux;
-       unsigned short mask, bitmask;
+       unsigned int val, mux;
+       unsigned int mask, bitmask;
        int ret = 0;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -1523,7 +1678,7 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short reg_val, val, mux;
+       unsigned int reg_val, val, mux;
 
        reg_val = snd_soc_read(widget->codec, e->reg);
        val = (reg_val >> e->shift_l) & e->mask;
@@ -1563,8 +1718,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val, mux;
-       unsigned short mask;
+       unsigned int val, mux;
+       unsigned int mask;
        int ret = 0;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
@@ -1880,6 +2035,36 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
+/*
+ * snd_soc_dapm_shutdown - callback for system shutdown
+ */
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
+{
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct snd_soc_dapm_widget *w;
+       LIST_HEAD(down_list);
+       int powerdown = 0;
+
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               if (w->power) {
+                       dapm_seq_insert(w, &down_list, dapm_down_seq);
+                       w->power = 0;
+                       powerdown = 1;
+               }
+       }
+
+       /* If there were no widgets to power down we're already in
+        * standby.
+        */
+       if (powerdown) {
+               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
+               dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
+               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
+       }
+
+       snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+}
+
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
index 938a58a..efed64b 100644 (file)
@@ -297,15 +297,17 @@ static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 static bool filter(struct dma_chan *chan, void *param)
 {
        struct txx9aclc_dmadata *dmadata = param;
-       char devname[20 + 2]; /* FIXME: old BUS_ID_SIZE + 2 */
+       char *devname;
+       bool found = false;
 
-       snprintf(devname, sizeof(devname), "%s.%d", dmadata->dma_res->name,
+       devname = kasprintf(GFP_KERNEL, "%s.%d", dmadata->dma_res->name,
                (int)dmadata->dma_res->start);
        if (strcmp(dev_name(chan->device->dev), devname) == 0) {
                chan->private = &dmadata->dma_slave;
-               return true;
+               found = true;
        }
-       return false;
+       kfree(devname);
+       return found;
 }
 
 static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,