]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'for-2.6.33' into for-2.6.34
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 12 Jan 2010 12:59:05 +0000 (12:59 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 12 Jan 2010 12:59:05 +0000 (12:59 +0000)
36 files changed:
include/sound/soc.h
include/sound/tpa6130a2-plat.h
include/sound/wm8904.h [new file with mode: 0644]
include/sound/wm8955.h [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad1836.c
sound/soc/codecs/ad1938.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/da7210.c [new file with mode: 0644]
sound/soc/codecs/da7210.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/wm8727.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8904.c [new file with mode: 0644]
sound/soc/codecs/wm8904.h [new file with mode: 0644]
sound/soc/codecs/wm8955.c [new file with mode: 0644]
sound/soc/codecs/wm8955.h [new file with mode: 0644]
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8993.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/davinci/davinci-pcm.c
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/sh/Kconfig
sound/soc/sh/Makefile
sound/soc/sh/fsi-da7210.c [new file with mode: 0644]
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 0d7718f9280db3e00e3da7e4232fa231510e0673..08909ccd235b93a1549e95cca05b1682425ea0e8 100644 (file)
@@ -253,6 +253,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 int mask, unsigned int value);
+int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+                              unsigned short reg, unsigned int mask,
+                              unsigned int value);
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
                                unsigned int mask, unsigned int value);
 
index e8c901e749d8f72cae77b3fe8ecd247aa2cf0faf..e29fde6b5cbe5214edeecbdc522c98afefd8a977 100644 (file)
 #ifndef TPA6130A2_PLAT_H
 #define TPA6130A2_PLAT_H
 
+enum tpa_model {
+       TPA6130A2,
+       TPA6140A2,
+};
+
 struct tpa6130a2_platform_data {
+       enum tpa_model id;
        int power_gpio;
 };
 
diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h
new file mode 100644 (file)
index 0000000..d66575a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Platform data for WM8904
+ *
+ * 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 as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM8994_PDATA_H__
+#define __MFD_WM8994_PDATA_H__
+
+#define WM8904_DRC_REGS 4
+#define WM8904_EQ_REGS  25
+
+/**
+ * DRC configurations are specified with a label and a set of register
+ * values to write (the enable bits will be ignored).  At runtime an
+ * enumerated control will be presented for each DRC block allowing
+ * the user to choose the configration to use.
+ *
+ * Configurations may be generated by hand or by using the DRC control
+ * panel provided by the WISCE - see  http://www.wolfsonmicro.com/wisce/
+ * for details.
+ */
+struct wm8904_drc_cfg {
+       const char *name;
+       u16 regs[WM8904_DRC_REGS];
+};
+
+/**
+ * ReTune Mobile configurations are specified with a label, sample
+ * rate and set of values to write (the enable bits will be ignored).
+ *
+ * Configurations are expected to be generated using the ReTune Mobile
+ * control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8904_retune_mobile_cfg {
+       const char *name;
+       unsigned int rate;
+       u16 regs[WM8904_EQ_REGS];
+};
+
+struct wm8904_pdata {
+       int num_drc_cfgs;
+       struct wm8904_drc_cfg *drc_cfgs;
+
+       int num_retune_mobile_cfgs;
+       struct wm8904_retune_mobile_cfg *retune_mobile_cfgs;
+};
+
+#endif
diff --git a/include/sound/wm8955.h b/include/sound/wm8955.h
new file mode 100644 (file)
index 0000000..5074ef4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Platform data for WM8955
+ *
+ * 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 as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __WM8955_PDATA_H__
+#define __WM8955_PDATA_H__
+
+struct wm8955_pdata {
+       /* Configure LOUT2/ROUT2 to drive a speaker */
+       unsigned int out2_speaker:1;
+
+       /* Configure MONOIN+/- in differential mode */
+       unsigned int monoin_diff:1;
+};
+
+#endif
index 52b005f8fed413ca052c2e1bc3dca9e7155c6ea2..62ff26a08a2fe0679712ce2ecf9c00baf661fb53 100644 (file)
@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4671 if I2C
        select SND_SOC_CS4270 if I2C
        select SND_SOC_MAX9877 if I2C
+       select SND_SOC_DA7210 if I2C
        select SND_SOC_PCM3008
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
@@ -49,7 +50,9 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8900 if I2C
        select SND_SOC_WM8903 if I2C
+       select SND_SOC_WM8904 if I2C
        select SND_SOC_WM8940 if I2C
+       select SND_SOC_WM8955 if I2C
        select SND_SOC_WM8960 if I2C
        select SND_SOC_WM8961 if I2C
        select SND_SOC_WM8971 if I2C
@@ -112,6 +115,9 @@ config SND_SOC_AK4671
 config SND_SOC_CS4270
        tristate
 
+config SND_SOC_DA7210
+        tristate
+
 # Cirrus Logic CS4270 Codec VD = 3.3V Errata
 # Select if you are affected by the errata where the part will not function
 # if MCLK divide-by-1.5 is selected and VD is set to 3.3V.  The driver will
@@ -203,9 +209,15 @@ config SND_SOC_WM8900
 config SND_SOC_WM8903
        tristate
 
+config SND_SOC_WM8904
+       tristate
+
 config SND_SOC_WM8940
         tristate
 
+config SND_SOC_WM8955
+       tristate
+
 config SND_SOC_WM8960
        tristate
 
index dbaecb133ac72da0331e66cd7c33d5605d370862..ea9835412e6a410ea4ef21eec8dc8ab97626298a 100644 (file)
@@ -10,6 +10,7 @@ snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
+snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
@@ -36,7 +37,9 @@ snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm8776-objs := wm8776.o
 snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8904-objs := wm8904.o
 snd-soc-wm8940-objs := wm8940.o
+snd-soc-wm8955-objs := wm8955.o
 snd-soc-wm8960-objs := wm8960.o
 snd-soc-wm8961-objs := wm8961.o
 snd-soc-wm8971-objs := wm8971.o
@@ -66,6 +69,7 @@ obj-$(CONFIG_SND_SOC_AK4642)  += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
@@ -92,11 +96,13 @@ obj-$(CONFIG_SND_SOC_WM8753)        += snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM8776)   += snd-soc-wm8776.o
 obj-$(CONFIG_SND_SOC_WM8900)   += snd-soc-wm8900.o
 obj-$(CONFIG_SND_SOC_WM8903)   += snd-soc-wm8903.o
-obj-$(CONFIG_SND_SOC_WM8971)   += snd-soc-wm8971.o
-obj-$(CONFIG_SND_SOC_WM8974)   += snd-soc-wm8974.o
+obj-$(CONFIG_SND_SOC_WM8904)   += snd-soc-wm8904.o
 obj-$(CONFIG_SND_SOC_WM8940)   += snd-soc-wm8940.o
+obj-$(CONFIG_SND_SOC_WM8955)   += snd-soc-wm8955.o
 obj-$(CONFIG_SND_SOC_WM8960)   += snd-soc-wm8960.o
 obj-$(CONFIG_SND_SOC_WM8961)   += snd-soc-wm8961.o
+obj-$(CONFIG_SND_SOC_WM8971)   += snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8974)   += snd-soc-wm8974.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
index 2c18e3d1b71e3aa993719f32c6a911ef9295d4f4..83add2f3afbab5de63da7174cd3f85041f2db4ee 100644 (file)
@@ -223,6 +223,36 @@ static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
        return reg_cache[reg];
 }
 
+#ifdef CONFIG_PM
+static int ad1836_soc_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;
+
+       /* reset clock control mode */
+       u16 adc_ctrl2 =  codec->read(codec, AD1836_ADC_CTRL2);
+       adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
+
+       return codec->write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+}
+
+static int ad1836_soc_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* restore clock control mode */
+       u16 adc_ctrl2 = codec->read(codec, AD1836_ADC_CTRL2);
+       adc_ctrl2 |= AD1836_ADC_AUX;
+
+       return codec->write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+}
+#else
+#define ad1836_soc_suspend NULL
+#define ad1836_soc_resume  NULL
+#endif
+
 static int __devinit ad1836_spi_probe(struct spi_device *spi)
 {
        struct snd_soc_codec *codec;
@@ -404,6 +434,8 @@ static int ad1836_remove(struct platform_device *pdev)
 struct snd_soc_codec_device soc_codec_dev_ad1836 = {
        .probe =        ad1836_probe,
        .remove =       ad1836_remove,
+       .suspend =      ad1836_soc_suspend,
+       .resume =       ad1836_soc_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
 
index 5d489186c05bb036d5a98aa2bbaa4b0537ad38df..47d9ac0ec9d92ee91d66b3ec7c25e338e327de06 100644 (file)
@@ -97,6 +97,7 @@ static const struct snd_kcontrol_new ad1938_snd_controls[] = {
 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("PLL_PWR", AD1938_PLL_CLK_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_OUTPUT("DAC1OUT"),
        SND_SOC_DAPM_OUTPUT("DAC2OUT"),
@@ -107,6 +108,8 @@ static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route audio_paths[] = {
+       { "DAC", NULL, "PLL_PWR" },
+       { "ADC", NULL, "PLL_PWR" },
        { "DAC", NULL, "ADC_PWR" },
        { "ADC", NULL, "ADC_PWR" },
        { "DAC1OUT", "DAC1 Switch", "DAC" },
@@ -134,18 +137,8 @@ static int ad1938_mute(struct snd_soc_dai *dai, int mute)
        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 tx_mask,
-                              unsigned int mask, int slots, int width)
+                              unsigned int rx_mask, int slots, int width)
 {
        struct snd_soc_codec *codec = dai->codec;
        int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
@@ -306,24 +299,6 @@ static int ad1938_hw_params(struct snd_pcm_substream *substream,
        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
  */
@@ -514,7 +489,6 @@ static int ad1938_register(struct ad1938_priv *ad1938)
        codec->num_dai = 1;
        codec->write = ad1938_write_reg;
        codec->read = ad1938_read_reg_cache;
-       codec->set_bias_level = ad1938_set_bias_level;
        INIT_LIST_HEAD(&codec->dapm_widgets);
        INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -559,7 +533,6 @@ static int ad1938_register(struct ad1938_priv *ad1938)
 
 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);
@@ -593,7 +566,6 @@ static int ad1938_probe(struct platform_device *pdev)
                                  ARRAY_SIZE(ad1938_dapm_widgets));
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 
-       ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 pcm_err:
        return ret;
@@ -610,37 +582,9 @@ static int ad1938_remove(struct platform_device *pdev)
        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);
 
index ffe122d1cd76af149d510fe3c80fff4380922bbd..8b5457542a0e7c1d138618bc893328f252d282d2 100644 (file)
@@ -28,6 +28,7 @@
 #include <sound/initval.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
+#include <linux/regulator/consumer.h>
 
 #include "cs4270.h"
 
 #define CS4270_MUTE_DAC_A      0x01
 #define CS4270_MUTE_DAC_B      0x02
 
+static const char *supply_names[] = {
+       "va", "vd", "vlc"
+};
+
 /* Private data for the CS4270 */
 struct cs4270_private {
        struct snd_soc_codec codec;
@@ -114,6 +119,9 @@ struct cs4270_private {
        unsigned int mode; /* The mode (I2S or left-justified) */
        unsigned int slave_mode;
        unsigned int manual_mute;
+
+       /* power domain regulators */
+       struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 };
 
 /**
@@ -579,7 +587,8 @@ static int cs4270_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = cs4270_codec;
-       int ret;
+       struct cs4270_private *cs4270 = codec->private_data;
+       int i, ret;
 
        /* Connect the codec to the socdev.  snd_soc_new_pcms() needs this. */
        socdev->card->codec = codec;
@@ -599,6 +608,15 @@ static int cs4270_probe(struct platform_device *pdev)
                goto error_free_pcms;
        }
 
+       /* get the power supply regulators */
+       for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+               cs4270->supplies[i].supply = supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies),
+                                cs4270->supplies);
+       if (ret < 0)
+               goto error_free_pcms;
+
        return 0;
 
 error_free_pcms:
@@ -616,8 +634,11 @@ error_free_pcms:
 static int cs4270_remove(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = cs4270_codec;
+       struct cs4270_private *cs4270 = codec->private_data;
 
        snd_soc_free_pcms(socdev);
+       regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
 
        return 0;
 };
@@ -799,17 +820,33 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
 static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
        struct snd_soc_codec *codec = cs4270_codec;
-       int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+       struct cs4270_private *cs4270 = codec->private_data;
+       int reg, ret;
 
-       return snd_soc_write(codec, CS4270_PWRCTL, reg);
+       reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+       if (reg < 0)
+               return reg;
+
+       ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+       if (ret < 0)
+               return ret;
+
+       regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies),
+                              cs4270->supplies);
+
+       return 0;
 }
 
 static int cs4270_soc_resume(struct platform_device *pdev)
 {
        struct snd_soc_codec *codec = cs4270_codec;
+       struct cs4270_private *cs4270 = codec->private_data;
        struct i2c_client *i2c_client = codec->control_data;
        int reg;
 
+       regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+                             cs4270->supplies);
+
        /* In case the device was put to hard reset during sleep, we need to
         * wait 500ns here before any I2C communication. */
        ndelay(500);
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
new file mode 100644 (file)
index 0000000..fbf3ab4
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * DA7210 ALSA Soc codec driver
+ *
+ * Copyright (c) 2009 Dialog Semiconductor
+ * Written by David Chen <Dajun.chen@diasemi.com>
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
+ *
+ * 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/moduleparam.h>
+#include <linux/kernel.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/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "da7210.h"
+
+/* DA7210 register space */
+#define DA7210_STATUS                  0x02
+#define DA7210_STARTUP1                        0x03
+#define DA7210_MIC_L                   0x07
+#define DA7210_MIC_R                   0x08
+#define DA7210_INMIX_L                 0x0D
+#define DA7210_INMIX_R                 0x0E
+#define DA7210_ADC_HPF                 0x0F
+#define DA7210_ADC                     0x10
+#define DA7210_DAC_HPF                 0x14
+#define DA7210_DAC_L                   0x15
+#define DA7210_DAC_R                   0x16
+#define DA7210_DAC_SEL                 0x17
+#define DA7210_OUTMIX_L                        0x1C
+#define DA7210_OUTMIX_R                        0x1D
+#define DA7210_HP_L_VOL                        0x21
+#define DA7210_HP_R_VOL                        0x22
+#define DA7210_HP_CFG                  0x23
+#define DA7210_DAI_SRC_SEL             0x25
+#define DA7210_DAI_CFG1                        0x26
+#define DA7210_DAI_CFG3                        0x28
+#define DA7210_PLL_DIV3                        0x2B
+#define DA7210_PLL                     0x2C
+
+/* STARTUP1 bit fields */
+#define DA7210_SC_MST_EN               (1 << 0)
+
+/* MIC_L bit fields */
+#define DA7210_MICBIAS_EN              (1 << 6)
+#define DA7210_MIC_L_EN                        (1 << 7)
+
+/* MIC_R bit fields */
+#define DA7210_MIC_R_EN                        (1 << 7)
+
+/* INMIX_L bit fields */
+#define DA7210_IN_L_EN                 (1 << 7)
+
+/* INMIX_R bit fields */
+#define DA7210_IN_R_EN                 (1 << 7)
+
+/* ADC_HPF bit fields */
+#define DA7210_ADC_VOICE_EN            (1 << 7)
+
+/* ADC bit fields */
+#define DA7210_ADC_L_EN                        (1 << 3)
+#define DA7210_ADC_R_EN                        (1 << 7)
+
+/* DAC_HPF fields */
+#define DA7210_DAC_VOICE_EN            (1 << 7)
+
+/* DAC_SEL bit fields */
+#define DA7210_DAC_L_SRC_DAI_L         (4 << 0)
+#define DA7210_DAC_L_EN                        (1 << 3)
+#define DA7210_DAC_R_SRC_DAI_R         (5 << 4)
+#define DA7210_DAC_R_EN                        (1 << 7)
+
+/* OUTMIX_L bit fields */
+#define DA7210_OUT_L_EN                        (1 << 7)
+
+/* OUTMIX_R bit fields */
+#define DA7210_OUT_R_EN                        (1 << 7)
+
+/* HP_CFG bit fields */
+#define DA7210_HP_2CAP_MODE            (1 << 1)
+#define DA7210_HP_SENSE_EN             (1 << 2)
+#define DA7210_HP_L_EN                 (1 << 3)
+#define DA7210_HP_MODE                 (1 << 6)
+#define DA7210_HP_R_EN                 (1 << 7)
+
+/* DAI_SRC_SEL bit fields */
+#define DA7210_DAI_OUT_L_SRC           (6 << 0)
+#define DA7210_DAI_OUT_R_SRC           (7 << 4)
+
+/* DAI_CFG1 bit fields */
+#define DA7210_DAI_WORD_S16_LE         (0 << 0)
+#define DA7210_DAI_WORD_S24_LE         (2 << 0)
+#define DA7210_DAI_FLEN_64BIT          (1 << 2)
+#define DA7210_DAI_MODE_MASTER         (1 << 7)
+
+/* DAI_CFG3 bit fields */
+#define DA7210_DAI_FORMAT_I2SMODE      (0 << 0)
+#define DA7210_DAI_OE                  (1 << 3)
+#define DA7210_DAI_EN                  (1 << 7)
+
+/*PLL_DIV3 bit fields */
+#define DA7210_MCLK_RANGE_10_20_MHZ    (1 << 4)
+#define DA7210_PLL_BYP                 (1 << 6)
+
+/* PLL bit fields */
+#define DA7210_PLL_FS_48000            (11 << 0)
+
+#define DA7210_VERSION "0.0.1"
+
+/* Codec private data */
+struct da7210_priv {
+       struct snd_soc_codec codec;
+};
+
+static struct snd_soc_codec *da7210_codec;
+
+/*
+ * Register cache
+ */
+static const u8 da7210_reg[] = {
+       0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R0  - R7  */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, /* R8  - RF  */
+       0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54, /* R10 - R17 */
+       0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R18 - R1F */
+       0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, /* R20 - R27 */
+       0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00, /* R28 - R2F */
+       0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, /* R30 - R37 */
+       0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, /* R38 - R3F */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R40 - R4F */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R48 - R4F */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R50 - R57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R58 - R5F */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R60 - R67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R68 - R6F */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R70 - R77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, /* R78 - R7F */
+       0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, /* R80 - R87 */
+       0x00,                                           /* R88       */
+};
+
+/*
+ * Read da7210 register cache
+ */
+static inline u32 da7210_read_reg_cache(struct snd_soc_codec *codec, u32 reg)
+{
+       u8 *cache = codec->reg_cache;
+       BUG_ON(reg > ARRAY_SIZE(da7210_reg));
+       return cache[reg];
+}
+
+/*
+ * Write to the da7210 register space
+ */
+static int da7210_write(struct snd_soc_codec *codec, u32 reg, u32 value)
+{
+       u8 *cache = codec->reg_cache;
+       u8 data[2];
+
+       BUG_ON(codec->volatile_register);
+
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       if (reg >= codec->reg_cache_size)
+               return -EIO;
+
+       if (2 != codec->hw_write(codec->control_data, data, 2))
+               return -EIO;
+
+       cache[reg] = value;
+       return 0;
+}
+
+/*
+ * Read from the da7210 register space.
+ */
+static inline u32 da7210_read(struct snd_soc_codec *codec, u32 reg)
+{
+       if (DA7210_STATUS == reg)
+               return i2c_smbus_read_byte_data(codec->control_data, reg);
+
+       return da7210_read_reg_cache(codec, reg);
+}
+
+static int da7210_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (is_play) {
+               /* PlayBack Volume 40 */
+               snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
+               snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
+
+               /* Enable Out */
+               snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
+               snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
+
+       } else {
+               /* Volume 7 */
+               snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7);
+               snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7);
+
+               /* Enable Mic */
+               snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1);
+               snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1);
+       }
+
+       return 0;
+}
+
+/*
+ * Set PCM DAI word length.
+ */
+static int da7210_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;
+       u32 dai_cfg1;
+       u32 reg, mask;
+
+       /* set DAI source to Left and Right ADC */
+       da7210_write(codec, DA7210_DAI_SRC_SEL,
+                    DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
+
+       /* Enable DAI */
+       da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
+
+       dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+
+       /* FIXME
+        *
+        * It support 48K only now
+        */
+       switch (params_rate(params)) {
+       case 48000:
+               if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+                       reg  = DA7210_DAC_HPF;
+                       mask = DA7210_DAC_VOICE_EN;
+               } else {
+                       reg  = DA7210_ADC_HPF;
+                       mask = DA7210_ADC_VOICE_EN;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, reg, mask, 0);
+
+       return 0;
+}
+
+/*
+ * Set DAI mode and Format
+ */
+static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 dai_cfg1;
+       u32 dai_cfg3;
+
+       dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
+       dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               dai_cfg1 |= DA7210_DAI_MODE_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* FIXME
+        *
+        * It support I2S only now
+        */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* FIXME
+        *
+        * It support 64bit data transmission only now
+        */
+       dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
+
+       da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+       da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+
+       return 0;
+}
+
+#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+/* DAI operations */
+static struct snd_soc_dai_ops da7210_dai_ops = {
+       .startup        = da7210_startup,
+       .hw_params      = da7210_hw_params,
+       .set_fmt        = da7210_set_dai_fmt,
+};
+
+struct snd_soc_dai da7210_dai = {
+       .name = "DA7210 IIS",
+       .id = 0,
+       /* playback capabilities */
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = DA7210_FORMATS,
+       },
+       /* capture capabilities */
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = DA7210_FORMATS,
+       },
+       .ops = &da7210_dai_ops,
+};
+EXPORT_SYMBOL_GPL(da7210_dai);
+
+/*
+ * Initialize the DA7210 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int da7210_init(struct da7210_priv *da7210)
+{
+       struct snd_soc_codec *codec = &da7210->codec;
+       int ret = 0;
+
+       if (da7210_codec) {
+               dev_err(codec->dev, "Another da7210 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data     = da7210;
+       codec->name             = "DA7210";
+       codec->owner            = THIS_MODULE;
+       codec->read             = da7210_read;
+       codec->write            = da7210_write;
+       codec->dai              = &da7210_dai;
+       codec->num_dai          = 1;
+       codec->hw_write         = (hw_write_t)i2c_master_send;
+       codec->reg_cache_size   = ARRAY_SIZE(da7210_reg);
+       codec->reg_cache        = kmemdup(da7210_reg,
+                                         sizeof(da7210_reg), GFP_KERNEL);
+
+       if (!codec->reg_cache)
+               return -ENOMEM;
+
+       da7210_dai.dev = codec->dev;
+       da7210_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register CODEC: %d\n", ret);
+               goto init_err;
+       }
+
+       ret = snd_soc_register_dai(&da7210_dai);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               goto init_err;
+       }
+
+       /* FIXME
+        *
+        * This driver use fixed value here
+        */
+
+       /*
+        * ADC settings
+        */
+
+       /* Enable Left & Right MIC PGA and Mic Bias */
+       da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
+       da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
+
+       /* Enable Left and Right input PGA */
+       da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
+       da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
+
+       /* Enable Left and Right ADC */
+       da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+
+       /*
+        * DAC settings
+        */
+
+       /* Enable Left and Right DAC */
+       da7210_write(codec, DA7210_DAC_SEL,
+                    DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
+                    DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
+
+       /* Enable Left and Right out PGA */
+       da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
+       da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
+
+       /* Enable Left and Right HeadPhone PGA */
+       da7210_write(codec, DA7210_HP_CFG,
+                    DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
+                    DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
+
+       /* Diable PLL and bypass it */
+       da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+
+       /* Bypass PLL and set MCLK freq rang to 10-20MHz */
+       da7210_write(codec, DA7210_PLL_DIV3,
+                    DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+
+       /* Activate all enabled subsystem */
+       da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+
+       return ret;
+
+init_err:
+       kfree(codec->reg_cache);
+       codec->reg_cache = NULL;
+
+       return ret;
+
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int da7210_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct da7210_priv *da7210;
+       struct snd_soc_codec *codec;
+       int ret;
+
+       da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
+       if (!da7210)
+               return -ENOMEM;
+
+       codec = &da7210->codec;
+       codec->dev = &i2c->dev;
+
+       i2c_set_clientdata(i2c, da7210);
+       codec->control_data = i2c;
+
+       ret = da7210_init(da7210);
+       if (ret < 0)
+               pr_err("Failed to initialise da7210 audio codec\n");
+
+       return ret;
+}
+
+static int da7210_i2c_remove(struct i2c_client *client)
+{
+       struct da7210_priv *da7210 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_dai(&da7210_dai);
+       kfree(da7210->codec.reg_cache);
+       kfree(da7210);
+       da7210_codec = NULL;
+
+       return 0;
+}
+
+static const struct i2c_device_id da7210_i2c_id[] = {
+       { "da7210", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
+
+/* I2C codec control layer */
+static struct i2c_driver da7210_i2c_driver = {
+       .driver = {
+               .name = "DA7210 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe = da7210_i2c_probe,
+       .remove =  __devexit_p(da7210_i2c_remove),
+       .id_table = da7210_i2c_id,
+};
+#endif
+
+static int da7210_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret;
+
+       if (!da7210_codec) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = da7210_codec;
+       codec = da7210_codec;
+
+       /* Register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0)
+               goto pcm_err;
+
+       dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+
+pcm_err:
+       return ret;
+}
+
+static int da7210_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_da7210 = {
+       .probe =        da7210_probe,
+       .remove =       da7210_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_da7210);
+
+static int __init da7210_modinit(void)
+{
+       int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&da7210_i2c_driver);
+#endif
+       return ret;
+}
+module_init(da7210_modinit);
+
+static void __exit da7210_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&da7210_i2c_driver);
+#endif
+}
+module_exit(da7210_exit);
+
+MODULE_DESCRIPTION("ASoC DA7210 driver");
+MODULE_AUTHOR("David Chen, Kuninori Morimoto");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7210.h b/sound/soc/codecs/da7210.h
new file mode 100644 (file)
index 0000000..390d621
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * da7210.h  --  audio driver for da7210
+ *
+ * Copyright (c) 2009 Dialog Semiconductor
+ * Written by David Chen <Dajun.chen@diasemi.com>
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.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 _DA7210_H
+#define _DA7210_H
+
+extern struct snd_soc_dai da7210_dai;
+extern struct snd_soc_codec_device soc_codec_dev_da7210;
+
+#endif
+
index 2b4dc2b0b01724aa91507e330860d9d7bc7e0eda..e4b946a19ea3e958b46969be2561e03764c1096d 100644 (file)
@@ -765,9 +765,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = socdev->card->codec;
        struct aic3x_priv *aic3x = codec->private_data;
        int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
-       u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
-       u16 pll_d = 1;
+       u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+       u16 d, pll_d = 1;
        u8 reg;
+       int clk;
 
        /* select data word length */
        data =
@@ -833,48 +834,70 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
        if (bypass_pll)
                return 0;
 
-       /* Use PLL
-        * find an apropriate setup for j, d, r and p by iterating over
-        * p and r - j and d are calculated for each fraction.
-        * Up to 128 values are probed, the closest one wins the game.
+       /* Use PLL, compute apropriate setup for j, d, r and p, the closest
+        * one wins the game. Try with d==0 first, next with d!=0.
+        * Constraints for j are according to the datasheet.
         * The sysclk is divided by 1000 to prevent integer overflows.
         */
+
        codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
 
        for (r = 1; r <= 16; r++)
                for (p = 1; p <= 8; p++) {
-                       int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
-                       u8 j = tmp / 10000;
-                       u16 d = tmp % 10000;
+                       for (j = 4; j <= 55; j++) {
+                               /* This is actually 1000*((j+(d/10000))*r)/p
+                                * The term had to be converted to get
+                                * rid of the division by 10000; d = 0 here
+                                */
+                               int tmp_clk = (1000 * j * r) / p;
+
+                               /* Check whether this values get closer than
+                                * the best ones we had before
+                                */
+                               if (abs(codec_clk - tmp_clk) <
+                                       abs(codec_clk - last_clk)) {
+                                       pll_j = j; pll_d = 0;
+                                       pll_r = r; pll_p = p;
+                                       last_clk = tmp_clk;
+                               }
+
+                               /* Early exit for exact matches */
+                               if (tmp_clk == codec_clk)
+                                       goto found;
+                       }
+               }
 
-                       if (j > 63)
-                               continue;
+       /* try with d != 0 */
+       for (p = 1; p <= 8; p++) {
+               j = codec_clk * p / 1000;
 
-                       if (d != 0 && aic3x->sysclk < 10000000)
-                               continue;
+               if (j < 4 || j > 11)
+                       continue;
 
-                       /* This is actually 1000 * ((j + (d/10000)) * r) / p
-                        * The term had to be converted to get rid of the
-                        * division by 10000 */
-                       clk = ((10000 * j * r) + (d * r)) / (10 * p);
+               /* do not use codec_clk here since we'd loose precision */
+               d = ((2048 * p * fsref) - j * aic3x->sysclk)
+                       * 100 / (aic3x->sysclk/100);
 
-                       /* check whether this values get closer than the best
-                        * ones we had before */
-                       if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
-                               pll_j = j; pll_d = d; pll_r = r; pll_p = p;
-                               last_clk = clk;
-                       }
+               clk = (10000 * j + d) / (10 * p);
 
-                       /* Early exit for exact matches */
-                       if (clk == codec_clk)
-                               break;
+               /* check whether this values get closer than the best
+                * ones we had before */
+               if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+                       pll_j = j; pll_d = d; pll_r = 1; pll_p = p;
+                       last_clk = clk;
                }
 
+               /* Early exit for exact matches */
+               if (clk == codec_clk)
+                       goto found;
+       }
+
        if (last_clk == 0) {
                printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
                return -EINVAL;
        }
 
+found:
        data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
        aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
        aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
index 9c8903dbe647c9c658f6bb114c50a5e8df0242da..3ef3255cd1e72e3f200a82a22cfbca3764e84fe8 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -58,11 +59,26 @@ enum dac33_state {
        DAC33_FLUSH,
 };
 
+enum dac33_fifo_modes {
+       DAC33_FIFO_BYPASS = 0,
+       DAC33_FIFO_MODE1,
+       DAC33_FIFO_MODE7,
+       DAC33_FIFO_LAST_MODE,
+};
+
+#define DAC33_NUM_SUPPLIES 3
+static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
+       "AVDD",
+       "DVDD",
+       "IOVDD",
+};
+
 struct tlv320dac33_priv {
        struct mutex mutex;
        struct workqueue_struct *dac33_wq;
        struct work_struct work;
        struct snd_soc_codec codec;
+       struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
        int power_gpio;
        int chip_power;
        int irq;
@@ -73,7 +89,7 @@ struct tlv320dac33_priv {
                                         * this */
        unsigned int nsample_max;       /* nsample should not be higher than
                                         * this */
-       unsigned int nsample_switch;    /* Use FIFO or bypass FIFO switch */
+       enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
        unsigned int nsample;           /* burst read amount from host */
 
        enum dac33_state state;
@@ -297,28 +313,49 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
        dac33_write(codec, DAC33_PWR_CTRL, reg);
 }
 
-static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+static int dac33_hard_power(struct snd_soc_codec *codec, int power)
 {
        struct tlv320dac33_priv *dac33 = codec->private_data;
+       int ret;
 
        mutex_lock(&dac33->mutex);
        if (power) {
-               if (dac33->power_gpio >= 0) {
-                       gpio_set_value(dac33->power_gpio, 1);
-                       dac33->chip_power = 1;
-                       /* Restore registers */
-                       dac33_restore_regs(codec);
+               ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
+                                         dac33->supplies);
+               if (ret != 0) {
+                       dev_err(codec->dev,
+                               "Failed to enable supplies: %d\n", ret);
+                               goto exit;
                }
+
+               if (dac33->power_gpio >= 0)
+                       gpio_set_value(dac33->power_gpio, 1);
+
+               dac33->chip_power = 1;
+
+               /* Restore registers */
+               dac33_restore_regs(codec);
+
                dac33_soft_power(codec, 1);
        } else {
                dac33_soft_power(codec, 0);
-               if (dac33->power_gpio >= 0) {
+               if (dac33->power_gpio >= 0)
                        gpio_set_value(dac33->power_gpio, 0);
-                       dac33->chip_power = 0;
+
+               ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
+                                            dac33->supplies);
+               if (ret != 0) {
+                       dev_err(codec->dev,
+                               "Failed to disable supplies: %d\n", ret);
+                       goto exit;
                }
+
+               dac33->chip_power = 0;
        }
-       mutex_unlock(&dac33->mutex);
 
+exit:
+       mutex_unlock(&dac33->mutex);
+       return ret;
 }
 
 static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
@@ -351,39 +388,48 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
-static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol,
+static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct tlv320dac33_priv *dac33 = codec->private_data;
 
-       ucontrol->value.integer.value[0] = dac33->nsample_switch;
+       ucontrol->value.integer.value[0] = dac33->fifo_mode;
 
        return 0;
 }
 
-static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol,
+static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct tlv320dac33_priv *dac33 = codec->private_data;
        int ret = 0;
 
-       if (dac33->nsample_switch == ucontrol->value.integer.value[0])
+       if (dac33->fifo_mode == ucontrol->value.integer.value[0])
                return 0;
        /* Do not allow changes while stream is running*/
        if (codec->active)
                return -EPERM;
 
        if (ucontrol->value.integer.value[0] < 0 ||
-           ucontrol->value.integer.value[0] > 1)
+           ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE)
                ret = -EINVAL;
        else
-               dac33->nsample_switch = ucontrol->value.integer.value[0];
+               dac33->fifo_mode = ucontrol->value.integer.value[0];
 
        return ret;
 }
 
+/* Codec operation modes */
+static const char *dac33_fifo_mode_texts[] = {
+       "Bypass", "Mode 1", "Mode 7"
+};
+
+static const struct soc_enum dac33_fifo_mode_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts),
+                           dac33_fifo_mode_texts);
+
 /*
  * DACL/R digital volume control:
  * from 0 dB to -63.5 in 0.5 dB steps
@@ -406,8 +452,8 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = {
 static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
        SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
                 dac33_get_nsample, dac33_set_nsample),
-       SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0,
-                dac33_get_nsample_switch, dac33_set_nsample_switch),
+       SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
+                dac33_get_fifo_mode, dac33_set_fifo_mode),
 };
 
 /* Analog bypass */
@@ -469,6 +515,8 @@ static int dac33_add_widgets(struct snd_soc_codec *codec)
 static int dac33_set_bias_level(struct snd_soc_codec *codec,
                                enum snd_soc_bias_level level)
 {
+       int ret;
+
        switch (level) {
        case SND_SOC_BIAS_ON:
                dac33_soft_power(codec, 1);
@@ -476,12 +524,19 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
        case SND_SOC_BIAS_PREPARE:
                break;
        case SND_SOC_BIAS_STANDBY:
-               if (codec->bias_level == SND_SOC_BIAS_OFF)
-                       dac33_hard_power(codec, 1);
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = dac33_hard_power(codec, 1);
+                       if (ret != 0)
+                               return ret;
+               }
+
                dac33_soft_power(codec, 0);
                break;
        case SND_SOC_BIAS_OFF:
-               dac33_hard_power(codec, 0);
+               ret = dac33_hard_power(codec, 0);
+               if (ret != 0)
+                       return ret;
+
                break;
        }
        codec->bias_level = level;
@@ -489,6 +544,51 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
+{
+       struct snd_soc_codec *codec;
+
+       codec = &dac33->codec;
+
+       switch (dac33->fifo_mode) {
+       case DAC33_FIFO_MODE1:
+               dac33_write16(codec, DAC33_NSAMPLE_MSB,
+                               DAC33_THRREG(dac33->nsample));
+               dac33_write16(codec, DAC33_PREFILL_MSB,
+                               DAC33_THRREG(dac33->alarm_threshold));
+               break;
+       case DAC33_FIFO_MODE7:
+               dac33_write16(codec, DAC33_PREFILL_MSB,
+                               DAC33_THRREG(20));
+               break;
+       default:
+               dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+                                                       dac33->fifo_mode);
+               break;
+       }
+}
+
+static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
+{
+       struct snd_soc_codec *codec;
+
+       codec = &dac33->codec;
+
+       switch (dac33->fifo_mode) {
+       case DAC33_FIFO_MODE1:
+               dac33_write16(codec, DAC33_NSAMPLE_MSB,
+                               DAC33_THRREG(dac33->nsample));
+               break;
+       case DAC33_FIFO_MODE7:
+               /* At the moment we are not using interrupts in mode7 */
+               break;
+       default:
+               dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+                                                       dac33->fifo_mode);
+               break;
+       }
+}
+
 static void dac33_work(struct work_struct *work)
 {
        struct snd_soc_codec *codec;
@@ -502,14 +602,10 @@ static void dac33_work(struct work_struct *work)
        switch (dac33->state) {
        case DAC33_PREFILL:
                dac33->state = DAC33_PLAYBACK;
-               dac33_write16(codec, DAC33_NSAMPLE_MSB,
-                               DAC33_THRREG(dac33->nsample));
-               dac33_write16(codec, DAC33_PREFILL_MSB,
-                               DAC33_THRREG(dac33->alarm_threshold));
+               dac33_prefill_handler(dac33);
                break;
        case DAC33_PLAYBACK:
-               dac33_write16(codec, DAC33_NSAMPLE_MSB,
-                               DAC33_THRREG(dac33->nsample));
+               dac33_playback_handler(dac33);
                break;
        case DAC33_IDLE:
                break;
@@ -547,7 +643,7 @@ static void dac33_shutdown(struct snd_pcm_substream *substream,
        unsigned int pwr_ctrl;
 
        /* Stop pending workqueue */
-       if (dac33->nsample_switch)
+       if (dac33->fifo_mode)
                cancel_work_sync(&dac33->work);
 
        mutex_lock(&dac33->mutex);
@@ -619,7 +715,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
        struct snd_soc_codec *codec = socdev->card->codec;
        struct tlv320dac33_priv *dac33 = codec->private_data;
        unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
-       u8 aictrl_a, fifoctrl_a;
+       u8 aictrl_a, aictrl_b, fifoctrl_a;
 
        switch (substream->runtime->rate) {
        case 44100:
@@ -675,7 +771,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
 
        dac33_oscwait(codec);
 
-       if (dac33->nsample_switch) {
+       if (dac33->fifo_mode) {
+               /* Generic for all FIFO modes */
                /* 50-51 : ASRC Control registers */
                dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
                dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
@@ -685,38 +782,91 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
 
                /* Set interrupts to high active */
                dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
-
-               dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
-                           DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
-               dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
        } else {
+               /* FIFO bypass mode */
                /* 50-51 : ASRC Control registers */
                dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
                dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
        }
 
-       if (dac33->nsample_switch)
+       /* Interrupt behaviour configuration */
+       switch (dac33->fifo_mode) {
+       case DAC33_FIFO_MODE1:
+               dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+                           DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+               dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+               break;
+       case DAC33_FIFO_MODE7:
+               /* Disable all interrupts */
+               dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+               break;
+       default:
+               /* in FIFO bypass mode, the interrupts are not used */
+               break;
+       }
+
+       aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+
+       switch (dac33->fifo_mode) {
+       case DAC33_FIFO_MODE1:
+               /*
+                * For mode1:
+                * Disable the FIFO bypass (Enable the use of FIFO)
+                * Select nSample mode
+                * BCLK is only running when data is needed by DAC33
+                */
                fifoctrl_a &= ~DAC33_FBYPAS;
-       else
+               fifoctrl_a &= ~DAC33_FAUTO;
+               aictrl_b &= ~DAC33_BCLKON;
+               break;
+       case DAC33_FIFO_MODE7:
+               /*
+                * For mode1:
+                * Disable the FIFO bypass (Enable the use of FIFO)
+                * Select Threshold mode
+                * BCLK is only running when data is needed by DAC33
+                */
+               fifoctrl_a &= ~DAC33_FBYPAS;
+               fifoctrl_a |= DAC33_FAUTO;
+               aictrl_b &= ~DAC33_BCLKON;
+               break;
+       default:
+               /*
+                * For FIFO bypass mode:
+                * Enable the FIFO bypass (Disable the FIFO use)
+                * Set the BCLK as continous
+                */
                fifoctrl_a |= DAC33_FBYPAS;
-       dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
+               aictrl_b |= DAC33_BCLKON;
+               break;
+       }
 
+       dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
        dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
-       reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
-       if (dac33->nsample_switch)
-               reg_tmp &= ~DAC33_BCLKON;
-       else
-               reg_tmp |= DAC33_BCLKON;
-       dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+       dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
 
-       if (dac33->nsample_switch) {
+       switch (dac33->fifo_mode) {
+       case DAC33_FIFO_MODE1:
                /* 20: BCLK divide ratio */
                dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
 
                dac33_write16(codec, DAC33_ATHR_MSB,
                              DAC33_THRREG(dac33->alarm_threshold));
-       } else {
+               break;
+       case DAC33_FIFO_MODE7:
+               /*
+                * Configure the threshold levels, and leave 10 sample space
+                * at the bottom, and also at the top of the FIFO
+                */
+               dac33_write16(codec, DAC33_UTHR_MSB,
+                       DAC33_THRREG(DAC33_BUFFER_SIZE_SAMPLES - 10));
+               dac33_write16(codec, DAC33_LTHR_MSB,
+                       DAC33_THRREG(10));
+               break;
+       default:
+               /* BYPASS mode */
                dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+               break;
        }
 
        mutex_unlock(&dac33->mutex);
@@ -789,7 +939,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (dac33->nsample_switch) {
+               if (dac33->fifo_mode) {
                        dac33->state = DAC33_PREFILL;
                        queue_work(dac33->dac33_wq, &dac33->work);
                }
@@ -797,7 +947,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (dac33->nsample_switch) {
+               if (dac33->fifo_mode) {
                        dac33->state = DAC33_FLUSH;
                        queue_work(dac33->dac33_wq, &dac33->work);
                }
@@ -843,6 +993,7 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
                             unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct tlv320dac33_priv *dac33 = codec->private_data;
        u8 aictrl_a, aictrl_b;
 
        aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
@@ -855,7 +1006,11 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
                /* Codec Slave */
-               aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+               if (dac33->fifo_mode) {
+                       dev_err(codec->dev, "FIFO mode requires master mode\n");
+                       return -EINVAL;
+               } else
+                       aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
                break;
        default:
                return -EINVAL;
@@ -959,6 +1114,9 @@ static int dac33_soc_probe(struct platform_device *pdev)
        /* power on device */
        dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
+       /* Bias level configuration has enabled regulator an extra time */
+       regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+
        return 0;
 
 pcm_err:
@@ -1039,7 +1197,7 @@ static int dac33_i2c_probe(struct i2c_client *client,
        struct tlv320dac33_platform_data *pdata;
        struct tlv320dac33_priv *dac33;
        struct snd_soc_codec *codec;
-       int ret = 0;
+       int ret, i;
 
        if (client->dev.platform_data == NULL) {
                dev_err(&client->dev, "Platform data not set\n");
@@ -1083,7 +1241,7 @@ static int dac33_i2c_probe(struct i2c_client *client,
        dac33->irq = client->irq;
        dac33->nsample = NSAMPLE_MAX;
        /* Disable FIFO use by default */
-       dac33->nsample_switch = 0;
+       dac33->fifo_mode = DAC33_FIFO_BYPASS;
 
        tlv320dac33_codec = codec;
 
@@ -1130,6 +1288,24 @@ static int dac33_i2c_probe(struct i2c_client *client,
                }
        }
 
+       for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
+               dac33->supplies[i].supply = dac33_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(dac33->supplies),
+                                dac33->supplies);
+
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err_get;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
+                                   dac33->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_enable;
+       }
+
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
@@ -1149,6 +1325,10 @@ static int dac33_i2c_probe(struct i2c_client *client,
        return ret;
 
 error_codec:
+       regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+err_enable:
+       regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+err_get:
        if (dac33->irq >= 0) {
                free_irq(dac33->irq, &dac33->codec);
                destroy_workqueue(dac33->dac33_wq);
@@ -1177,6 +1357,8 @@ static int dac33_i2c_remove(struct i2c_client *client)
        if (dac33->irq >= 0)
                free_irq(dac33->irq, &dac33->codec);
 
+       regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+
        destroy_workqueue(dac33->dac33_wq);
        snd_soc_unregister_dai(&dac33_dai);
        snd_soc_unregister_codec(&dac33->codec);
index 6b650c1aa3d1feba709009736bc84017f687911a..8b27281e62a1f4d34f2b1c74e011eb66169d3880 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/tpa6130a2-plat.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 
 static struct i2c_client *tpa6130a2_client;
 
+#define TPA6130A2_NUM_SUPPLIES 2
+static const char *tpa6130a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
+       "CPVSS",
+       "Vdd",
+};
+
+static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
+       "HPVdd",
+       "AVdd",
+};
+
 /* This struct is used to save the context */
 struct tpa6130a2_data {
        struct mutex mutex;
        unsigned char regs[TPA6130A2_CACHEREGNUM];
+       struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES];
        int power_gpio;
        unsigned char power_state;
 };
@@ -106,10 +119,11 @@ static void tpa6130a2_initialize(void)
                tpa6130a2_i2c_write(i, data->regs[i]);
 }
 
-static void tpa6130a2_power(int power)
+static int tpa6130a2_power(int power)
 {
        struct  tpa6130a2_data *data;
        u8      val;
+       int     ret;
 
        BUG_ON(tpa6130a2_client == NULL);
        data = i2c_get_clientdata(tpa6130a2_client);
@@ -117,11 +131,20 @@ static void tpa6130a2_power(int power)
        mutex_lock(&data->mutex);
        if (power) {
                /* Power on */
-               if (data->power_gpio >= 0) {
+               if (data->power_gpio >= 0)
                        gpio_set_value(data->power_gpio, 1);
-                       data->power_state = 1;
-                       tpa6130a2_initialize();
+
+               ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies),
+                                           data->supplies);
+               if (ret != 0) {
+                       dev_err(&tpa6130a2_client->dev,
+                               "Failed to enable supplies: %d\n", ret);
+                       goto exit;
                }
+
+               data->power_state = 1;
+               tpa6130a2_initialize();
+
                /* Clear SWS */
                val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
                val &= ~TPA6130A2_SWS;
@@ -131,13 +154,25 @@ static void tpa6130a2_power(int power)
                val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
                val |= TPA6130A2_SWS;
                tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
                /* Power off */
-               if (data->power_gpio >= 0) {
+               if (data->power_gpio >= 0)
                        gpio_set_value(data->power_gpio, 0);
-                       data->power_state = 0;
+
+               ret = regulator_bulk_disable(ARRAY_SIZE(data->supplies),
+                                            data->supplies);
+               if (ret != 0) {
+                       dev_err(&tpa6130a2_client->dev,
+                               "Failed to disable supplies: %d\n", ret);
+                       goto exit;
                }
+
+               data->power_state = 0;
        }
+
+exit:
        mutex_unlock(&data->mutex);
+       return ret;
 }
 
 static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
@@ -237,12 +272,8 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = {
  */
 static void tpa6130a2_channel_enable(u8 channel, int enable)
 {
-       struct  tpa6130a2_data *data;
        u8      val;
 
-       BUG_ON(tpa6130a2_client == NULL);
-       data = i2c_get_clientdata(tpa6130a2_client);
-
        if (enable) {
                /* Enable channel */
                /* Enable amplifier */
@@ -299,15 +330,17 @@ static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
 static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
                struct snd_kcontrol *kcontrol, int event)
 {
+       int ret = 0;
+
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
-               tpa6130a2_power(1);
+               ret = tpa6130a2_power(1);
                break;
        case SND_SOC_DAPM_POST_PMD:
-               tpa6130a2_power(0);
+               ret = tpa6130a2_power(0);
                break;
        }
-       return 0;
+       return ret;
 }
 
 static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
@@ -352,7 +385,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
        struct device *dev;
        struct tpa6130a2_data *data;
        struct tpa6130a2_platform_data *pdata;
-       int ret;
+       int i, ret;
 
        dev = &client->dev;
 
@@ -387,15 +420,38 @@ static int tpa6130a2_probe(struct i2c_client *client,
                if (ret < 0) {
                        dev_err(dev, "Failed to request power GPIO (%d)\n",
                                data->power_gpio);
-                       goto fail;
+                       goto err_gpio;
                }
                gpio_direction_output(data->power_gpio, 0);
-       } else {
-               data->power_state = 1;
-               tpa6130a2_initialize();
        }
 
-       tpa6130a2_power(1);
+       switch (pdata->id) {
+       case TPA6130A2:
+               for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+                       data->supplies[i].supply = tpa6130a2_supply_names[i];
+               break;
+       case TPA6140A2:
+               for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+                       data->supplies[i].supply = tpa6140a2_supply_names[i];;
+               break;
+       default:
+               dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
+                        pdata->id);
+               for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+                       data->supplies[i].supply = tpa6130a2_supply_names[i];
+       }
+
+       ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+                                data->supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to request supplies: %d\n", ret);
+               goto err_regulator;
+       }
+
+       ret = tpa6130a2_power(1);
+       if (ret != 0)
+               goto err_power;
+
 
        /* Read version */
        ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
@@ -404,10 +460,18 @@ static int tpa6130a2_probe(struct i2c_client *client,
                dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
 
        /* Disable the chip */
-       tpa6130a2_power(0);
+       ret = tpa6130a2_power(0);
+       if (ret != 0)
+               goto err_power;
 
        return 0;
-fail:
+
+err_power:
+       regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+err_regulator:
+       if (data->power_gpio >= 0)
+               gpio_free(data->power_gpio);
+err_gpio:
        kfree(data);
        i2c_set_clientdata(tpa6130a2_client, NULL);
        tpa6130a2_client = NULL;
@@ -423,6 +487,9 @@ static int tpa6130a2_remove(struct i2c_client *client)
 
        if (data->power_gpio >= 0)
                gpio_free(data->power_gpio);
+
+       regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+
        kfree(data);
        tpa6130a2_client = NULL;
 
index d8ffbd641d717d2e74ff38b134b5030338bd664f..63a254e293cae381d3f9c6c2d27ec985fd1d35ad 100644 (file)
@@ -44,23 +44,16 @@ struct snd_soc_dai wm8727_dai = {
 };
 EXPORT_SYMBOL_GPL(wm8727_dai);
 
+static struct snd_soc_codec *wm8727_codec;
+
 static int wm8727_soc_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec;
        int ret = 0;
 
-       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-       if (codec == NULL)
-               return -ENOMEM;
-       mutex_init(&codec->mutex);
-       codec->name = "WM8727";
-       codec->owner = THIS_MODULE;
-       codec->dai = &wm8727_dai;
-       codec->num_dai = 1;
-       socdev->card->codec = codec;
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
+       BUG_ON(!wm8727_codec);
+
+       socdev->card->codec = wm8727_codec;
 
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -80,12 +73,9 @@ pcm_err:
 static int wm8727_soc_remove(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_codec *codec = socdev->card->codec;
 
-       if (codec == NULL)
-               return 0;
        snd_soc_free_pcms(socdev);
-       kfree(codec);
+
        return 0;
 }
 
@@ -98,13 +88,55 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
 
 static __devinit int wm8727_platform_probe(struct platform_device *pdev)
 {
+       struct snd_soc_codec *codec;
+       int ret;
+
+       if (wm8727_codec) {
+               dev_err(&pdev->dev, "Another WM8727 is registered\n");
+               return -EBUSY;
+       }
+
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+       wm8727_codec = codec;
+
+       platform_set_drvdata(pdev, codec);
+
+       mutex_init(&codec->mutex);
+       codec->dev = &pdev->dev;
+       codec->name = "WM8727";
+       codec->owner = THIS_MODULE;
+       codec->dai = &wm8727_dai;
+       codec->num_dai = 1;
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
        wm8727_dai.dev = &pdev->dev;
-       return snd_soc_register_dai(&wm8727_dai);
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to register CODEC: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dai(&wm8727_dai);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
+       }
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(codec);
+       return ret;
 }
 
 static int __devexit wm8727_platform_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_dai(&wm8727_dai);
+       snd_soc_unregister_codec(platform_get_drvdata(pdev));
        return 0;
 }
 
index 3a497810f9391c36f9918f0a3c93e20dce5779e5..5a2619dbf283b8a6195eb912dae6fb2c9ae96149 100644 (file)
@@ -456,6 +456,9 @@ static int wm8731_resume(struct platform_device *pdev)
 
        /* Sync reg_cache with the hardware */
        for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
+               if (cache[i] == wm8731_reg[i])
+                       continue;
+
                data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
                data[1] = cache[i] & 0x00ff;
                codec->hw_write(codec->control_data, data, 2);
index d6850dacda29fbc9e36f3155fb9cd0557d59168e..c2444e7c848025524ada66f2ef8631112d15f908 100644 (file)
@@ -1507,10 +1507,6 @@ static int wm8753_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;
 
-       /* we only need to suspend if we are a valid card */
-       if (!codec->card)
-               return 0;
-
        wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
@@ -1523,10 +1519,6 @@ static int wm8753_resume(struct platform_device *pdev)
        u8 data[2];
        u16 *cache = codec->reg_cache;
 
-       /* we only need to resume if we are a valid card */
-       if (!codec->card)
-               return 0;
-
        /* Sync reg_cache with the hardware */
        for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
                if (i + 1 == WM8753_RESET)
index ab2c0da1809102396cc357ca78be1d3946a356a2..44e7d9d82f87d7b7e658272fc7fcedd67129c832 100644 (file)
@@ -406,6 +406,8 @@ static int wm8776_resume(struct platform_device *pdev)
 
        /* Sync reg_cache with the hardware */
        for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
+               if (cache[i] == wm8776_reg[i])
+                       continue;
                data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
                data[1] = cache[i] & 0x00ff;
                codec->hw_write(codec->control_data, data, 2);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
new file mode 100644 (file)
index 0000000..992a7f2
--- /dev/null
@@ -0,0 +1,2556 @@
+/*
+ * wm8904.c  --  WM8904 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 <sound/wm8904.h>
+
+#include "wm8904.h"
+
+static struct snd_soc_codec *wm8904_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8904;
+
+#define WM8904_NUM_DCS_CHANNELS 4
+
+#define WM8904_NUM_SUPPLIES 5
+static const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = {
+       "DCVDD",
+       "DBVDD",
+       "AVDD",
+       "CPVDD",
+       "MICVDD",
+};
+
+/* codec private data */
+struct wm8904_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM8904_MAX_REGISTER + 1];
+
+       struct regulator_bulk_data supplies[WM8904_NUM_SUPPLIES];
+
+       struct wm8904_pdata *pdata;
+
+       int deemph;
+
+       /* Platform provided DRC configuration */
+       const char **drc_texts;
+       int drc_cfg;
+       struct soc_enum drc_enum;
+
+       /* Platform provided ReTune mobile configuration */
+       int num_retune_mobile_texts;
+       const char **retune_mobile_texts;
+       int retune_mobile_cfg;
+       struct soc_enum retune_mobile_enum;
+
+       /* FLL setup */
+       int fll_src;
+       int fll_fref;
+       int fll_fout;
+
+       /* Clocking configuration */
+       unsigned int mclk_rate;
+       int sysclk_src;
+       unsigned int sysclk_rate;
+
+       int tdm_width;
+       int tdm_slots;
+       int bclk;
+       int fs;
+
+       /* DC servo configuration - cached offset values */
+       int dcs_state[WM8904_NUM_DCS_CHANNELS];
+};
+
+static const u16 wm8904_reg[WM8904_MAX_REGISTER + 1] = {
+       0x8904,     /* R0   - SW Reset and ID */
+       0x0000,     /* R1   - Revision */
+       0x0000,     /* R2 */
+       0x0000,     /* R3 */
+       0x0018,     /* R4   - Bias Control 0 */
+       0x0000,     /* R5   - VMID Control 0 */
+       0x0000,     /* R6   - Mic Bias Control 0 */
+       0x0000,     /* R7   - Mic Bias Control 1 */
+       0x0001,     /* R8   - Analogue DAC 0 */
+       0x9696,     /* R9   - mic Filter Control */
+       0x0001,     /* R10  - Analogue ADC 0 */
+       0x0000,     /* R11 */
+       0x0000,     /* R12  - Power Management 0 */
+       0x0000,     /* R13 */
+       0x0000,     /* R14  - Power Management 2 */
+       0x0000,     /* R15  - Power Management 3 */
+       0x0000,     /* R16 */
+       0x0000,     /* R17 */
+       0x0000,     /* R18  - Power Management 6 */
+       0x0000,     /* R19 */
+       0x945E,     /* R20  - Clock Rates 0 */
+       0x0C05,     /* R21  - Clock Rates 1 */
+       0x0006,     /* R22  - Clock Rates 2 */
+       0x0000,     /* R23 */
+       0x0050,     /* R24  - Audio Interface 0 */
+       0x000A,     /* R25  - Audio Interface 1 */
+       0x00E4,     /* R26  - Audio Interface 2 */
+       0x0040,     /* R27  - Audio Interface 3 */
+       0x0000,     /* R28 */
+       0x0000,     /* R29 */
+       0x00C0,     /* R30  - DAC Digital Volume Left */
+       0x00C0,     /* R31  - DAC Digital Volume Right */
+       0x0000,     /* R32  - DAC Digital 0 */
+       0x0008,     /* R33  - DAC Digital 1 */
+       0x0000,     /* R34 */
+       0x0000,     /* R35 */
+       0x00C0,     /* R36  - ADC Digital Volume Left */
+       0x00C0,     /* R37  - ADC Digital Volume Right */
+       0x0010,     /* R38  - ADC Digital 0 */
+       0x0000,     /* R39  - Digital Microphone 0 */
+       0x01AF,     /* R40  - DRC 0 */
+       0x3248,     /* R41  - DRC 1 */
+       0x0000,     /* R42  - DRC 2 */
+       0x0000,     /* R43  - DRC 3 */
+       0x0085,     /* R44  - Analogue Left Input 0 */
+       0x0085,     /* R45  - Analogue Right Input 0 */
+       0x0044,     /* R46  - Analogue Left Input 1 */
+       0x0044,     /* R47  - Analogue Right Input 1 */
+       0x0000,     /* R48 */
+       0x0000,     /* R49 */
+       0x0000,     /* R50 */
+       0x0000,     /* R51 */
+       0x0000,     /* R52 */
+       0x0000,     /* R53 */
+       0x0000,     /* R54 */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x002D,     /* R57  - Analogue OUT1 Left */
+       0x002D,     /* R58  - Analogue OUT1 Right */
+       0x0039,     /* R59  - Analogue OUT2 Left */
+       0x0039,     /* R60  - Analogue OUT2 Right */
+       0x0000,     /* R61  - Analogue OUT12 ZC */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x0000,     /* R64 */
+       0x0000,     /* R65 */
+       0x0000,     /* R66 */
+       0x0000,     /* R67  - DC Servo 0 */
+       0x0000,     /* R68  - DC Servo 1 */
+       0xAAAA,     /* R69  - DC Servo 2 */
+       0x0000,     /* R70 */
+       0xAAAA,     /* R71  - DC Servo 4 */
+       0xAAAA,     /* R72  - DC Servo 5 */
+       0x0000,     /* R73  - DC Servo 6 */
+       0x0000,     /* R74  - DC Servo 7 */
+       0x0000,     /* R75  - DC Servo 8 */
+       0x0000,     /* R76  - DC Servo 9 */
+       0x0000,     /* R77  - DC Servo Readback 0 */
+       0x0000,     /* R78 */
+       0x0000,     /* R79 */
+       0x0000,     /* R80 */
+       0x0000,     /* R81 */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0000,     /* R88 */
+       0x0000,     /* R89 */
+       0x0000,     /* R90  - Analogue HP 0 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92 */
+       0x0000,     /* R93 */
+       0x0000,     /* R94  - Analogue Lineout 0 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96 */
+       0x0000,     /* R97 */
+       0x0000,     /* R98  - Charge Pump 0 */
+       0x0000,     /* R99 */
+       0x0000,     /* R100 */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x0004,     /* R104 - Class W 0 */
+       0x0000,     /* R105 */
+       0x0000,     /* R106 */
+       0x0000,     /* R107 */
+       0x0000,     /* R108 - Write Sequencer 0 */
+       0x0000,     /* R109 - Write Sequencer 1 */
+       0x0000,     /* R110 - Write Sequencer 2 */
+       0x0000,     /* R111 - Write Sequencer 3 */
+       0x0000,     /* R112 - Write Sequencer 4 */
+       0x0000,     /* R113 */
+       0x0000,     /* R114 */
+       0x0000,     /* R115 */
+       0x0000,     /* R116 - FLL Control 1 */
+       0x0007,     /* R117 - FLL Control 2 */
+       0x0000,     /* R118 - FLL Control 3 */
+       0x2EE0,     /* R119 - FLL Control 4 */
+       0x0004,     /* R120 - FLL Control 5 */
+       0x0014,     /* R121 - GPIO Control 1 */
+       0x0010,     /* R122 - GPIO Control 2 */
+       0x0010,     /* R123 - GPIO Control 3 */
+       0x0000,     /* R124 - GPIO Control 4 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 - Digital Pulls */
+       0x0000,     /* R127 - Interrupt Status */
+       0xFFFF,     /* R128 - Interrupt Status Mask */
+       0x0000,     /* R129 - Interrupt Polarity */
+       0x0000,     /* R130 - Interrupt Debounce */
+       0x0000,     /* R131 */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 */
+       0x0000,     /* R134 - EQ1 */
+       0x000C,     /* R135 - EQ2 */
+       0x000C,     /* R136 - EQ3 */
+       0x000C,     /* R137 - EQ4 */
+       0x000C,     /* R138 - EQ5 */
+       0x000C,     /* R139 - EQ6 */
+       0x0FCA,     /* R140 - EQ7 */
+       0x0400,     /* R141 - EQ8 */
+       0x00D8,     /* R142 - EQ9 */
+       0x1EB5,     /* R143 - EQ10 */
+       0xF145,     /* R144 - EQ11 */
+       0x0B75,     /* R145 - EQ12 */
+       0x01C5,     /* R146 - EQ13 */
+       0x1C58,     /* R147 - EQ14 */
+       0xF373,     /* R148 - EQ15 */
+       0x0A54,     /* R149 - EQ16 */
+       0x0558,     /* R150 - EQ17 */
+       0x168E,     /* R151 - EQ18 */
+       0xF829,     /* R152 - EQ19 */
+       0x07AD,     /* R153 - EQ20 */
+       0x1103,     /* R154 - EQ21 */
+       0x0564,     /* R155 - EQ22 */
+       0x0559,     /* R156 - EQ23 */
+       0x4000,     /* R157 - EQ24 */
+       0x0000,     /* R158 */
+       0x0000,     /* R159 */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 - Control Interface Test 1 */
+       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 */
+       0x0000,     /* R196 */
+       0x0000,     /* R197 */
+       0x0000,     /* R198 */
+       0x0000,     /* R199 */
+       0x0000,     /* R200 */
+       0x0000,     /* R201 */
+       0x0000,     /* R202 */
+       0x0000,     /* R203 */
+       0x0000,     /* R204 - Analogue Output Bias 0 */
+       0x0000,     /* R205 */
+       0x0000,     /* R206 */
+       0x0000,     /* R207 */
+       0x0000,     /* R208 */
+       0x0000,     /* R209 */
+       0x0000,     /* R210 */
+       0x0000,     /* R211 */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 */
+       0x0000,     /* R216 */
+       0x0000,     /* R217 */
+       0x0000,     /* R218 */
+       0x0000,     /* R219 */
+       0x0000,     /* R220 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 */
+       0x0000,     /* R226 */
+       0x0000,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0x0000,     /* R230 */
+       0x0000,     /* R231 */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0000,     /* R245 */
+       0x0000,     /* R246 */
+       0x0000,     /* R247 - FLL NCO Test 0 */
+       0x0019,     /* R248 - FLL NCO Test 1 */
+};
+
+static struct {
+       int readable;
+       int writable;
+       int vol;
+} wm8904_access[] = {
+       { 0xFFFF, 0xFFFF, 1 }, /* R0   - SW Reset and ID */
+       { 0x0000, 0x0000, 0 }, /* R1   - Revision */
+       { 0x0000, 0x0000, 0 }, /* R2 */
+       { 0x0000, 0x0000, 0 }, /* R3 */
+       { 0x001F, 0x001F, 0 }, /* R4   - Bias Control 0 */
+       { 0x0047, 0x0047, 0 }, /* R5   - VMID Control 0 */
+       { 0x007F, 0x007F, 0 }, /* R6   - Mic Bias Control 0 */
+       { 0xC007, 0xC007, 0 }, /* R7   - Mic Bias Control 1 */
+       { 0x001E, 0x001E, 0 }, /* R8   - Analogue DAC 0 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R9   - mic Filter Control */
+       { 0x0001, 0x0001, 0 }, /* R10  - Analogue ADC 0 */
+       { 0x0000, 0x0000, 0 }, /* R11 */
+       { 0x0003, 0x0003, 0 }, /* R12  - Power Management 0 */
+       { 0x0000, 0x0000, 0 }, /* R13 */
+       { 0x0003, 0x0003, 0 }, /* R14  - Power Management 2 */
+       { 0x0003, 0x0003, 0 }, /* R15  - Power Management 3 */
+       { 0x0000, 0x0000, 0 }, /* R16 */
+       { 0x0000, 0x0000, 0 }, /* R17 */
+       { 0x000F, 0x000F, 0 }, /* R18  - Power Management 6 */
+       { 0x0000, 0x0000, 0 }, /* R19 */
+       { 0x7001, 0x7001, 0 }, /* R20  - Clock Rates 0 */
+       { 0x3C07, 0x3C07, 0 }, /* R21  - Clock Rates 1 */
+       { 0xD00F, 0xD00F, 0 }, /* R22  - Clock Rates 2 */
+       { 0x0000, 0x0000, 0 }, /* R23 */
+       { 0x1FFF, 0x1FFF, 0 }, /* R24  - Audio Interface 0 */
+       { 0x3DDF, 0x3DDF, 0 }, /* R25  - Audio Interface 1 */
+       { 0x0F1F, 0x0F1F, 0 }, /* R26  - Audio Interface 2 */
+       { 0x0FFF, 0x0FFF, 0 }, /* R27  - Audio Interface 3 */
+       { 0x0000, 0x0000, 0 }, /* R28 */
+       { 0x0000, 0x0000, 0 }, /* R29 */
+       { 0x00FF, 0x01FF, 0 }, /* R30  - DAC Digital Volume Left */
+       { 0x00FF, 0x01FF, 0 }, /* R31  - DAC Digital Volume Right */
+       { 0x0FFF, 0x0FFF, 0 }, /* R32  - DAC Digital 0 */
+       { 0x1E4E, 0x1E4E, 0 }, /* R33  - DAC Digital 1 */
+       { 0x0000, 0x0000, 0 }, /* R34 */
+       { 0x0000, 0x0000, 0 }, /* R35 */
+       { 0x00FF, 0x01FF, 0 }, /* R36  - ADC Digital Volume Left */
+       { 0x00FF, 0x01FF, 0 }, /* R37  - ADC Digital Volume Right */
+       { 0x0073, 0x0073, 0 }, /* R38  - ADC Digital 0 */
+       { 0x1800, 0x1800, 0 }, /* R39  - Digital Microphone 0 */
+       { 0xDFEF, 0xDFEF, 0 }, /* R40  - DRC 0 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R41  - DRC 1 */
+       { 0x003F, 0x003F, 0 }, /* R42  - DRC 2 */
+       { 0x07FF, 0x07FF, 0 }, /* R43  - DRC 3 */
+       { 0x009F, 0x009F, 0 }, /* R44  - Analogue Left Input 0 */
+       { 0x009F, 0x009F, 0 }, /* R45  - Analogue Right Input 0 */
+       { 0x007F, 0x007F, 0 }, /* R46  - Analogue Left Input 1 */
+       { 0x007F, 0x007F, 0 }, /* R47  - Analogue Right Input 1 */
+       { 0x0000, 0x0000, 0 }, /* R48 */
+       { 0x0000, 0x0000, 0 }, /* R49 */
+       { 0x0000, 0x0000, 0 }, /* R50 */
+       { 0x0000, 0x0000, 0 }, /* R51 */
+       { 0x0000, 0x0000, 0 }, /* R52 */
+       { 0x0000, 0x0000, 0 }, /* R53 */
+       { 0x0000, 0x0000, 0 }, /* R54 */
+       { 0x0000, 0x0000, 0 }, /* R55 */
+       { 0x0000, 0x0000, 0 }, /* R56 */
+       { 0x017F, 0x01FF, 0 }, /* R57  - Analogue OUT1 Left */
+       { 0x017F, 0x01FF, 0 }, /* R58  - Analogue OUT1 Right */
+       { 0x017F, 0x01FF, 0 }, /* R59  - Analogue OUT2 Left */
+       { 0x017F, 0x01FF, 0 }, /* R60  - Analogue OUT2 Right */
+       { 0x000F, 0x000F, 0 }, /* R61  - Analogue OUT12 ZC */
+       { 0x0000, 0x0000, 0 }, /* R62 */
+       { 0x0000, 0x0000, 0 }, /* R63 */
+       { 0x0000, 0x0000, 0 }, /* R64 */
+       { 0x0000, 0x0000, 0 }, /* R65 */
+       { 0x0000, 0x0000, 0 }, /* R66 */
+       { 0x000F, 0x000F, 0 }, /* R67  - DC Servo 0 */
+       { 0xFFFF, 0xFFFF, 1 }, /* R68  - DC Servo 1 */
+       { 0x0F0F, 0x0F0F, 0 }, /* R69  - DC Servo 2 */
+       { 0x0000, 0x0000, 0 }, /* R70 */
+       { 0x007F, 0x007F, 0 }, /* R71  - DC Servo 4 */
+       { 0x007F, 0x007F, 0 }, /* R72  - DC Servo 5 */
+       { 0x00FF, 0x00FF, 1 }, /* R73  - DC Servo 6 */
+       { 0x00FF, 0x00FF, 1 }, /* R74  - DC Servo 7 */
+       { 0x00FF, 0x00FF, 1 }, /* R75  - DC Servo 8 */
+       { 0x00FF, 0x00FF, 1 }, /* R76  - DC Servo 9 */
+       { 0x0FFF, 0x0000, 1 }, /* R77  - DC Servo Readback 0 */
+       { 0x0000, 0x0000, 0 }, /* R78 */
+       { 0x0000, 0x0000, 0 }, /* R79 */
+       { 0x0000, 0x0000, 0 }, /* R80 */
+       { 0x0000, 0x0000, 0 }, /* R81 */
+       { 0x0000, 0x0000, 0 }, /* R82 */
+       { 0x0000, 0x0000, 0 }, /* R83 */
+       { 0x0000, 0x0000, 0 }, /* R84 */
+       { 0x0000, 0x0000, 0 }, /* R85 */
+       { 0x0000, 0x0000, 0 }, /* R86 */
+       { 0x0000, 0x0000, 0 }, /* R87 */
+       { 0x0000, 0x0000, 0 }, /* R88 */
+       { 0x0000, 0x0000, 0 }, /* R89 */
+       { 0x00FF, 0x00FF, 0 }, /* R90  - Analogue HP 0 */
+       { 0x0000, 0x0000, 0 }, /* R91 */
+       { 0x0000, 0x0000, 0 }, /* R92 */
+       { 0x0000, 0x0000, 0 }, /* R93 */
+       { 0x00FF, 0x00FF, 0 }, /* R94  - Analogue Lineout 0 */
+       { 0x0000, 0x0000, 0 }, /* R95 */
+       { 0x0000, 0x0000, 0 }, /* R96 */
+       { 0x0000, 0x0000, 0 }, /* R97 */
+       { 0x0001, 0x0001, 0 }, /* R98  - Charge Pump 0 */
+       { 0x0000, 0x0000, 0 }, /* R99 */
+       { 0x0000, 0x0000, 0 }, /* R100 */
+       { 0x0000, 0x0000, 0 }, /* R101 */
+       { 0x0000, 0x0000, 0 }, /* R102 */
+       { 0x0000, 0x0000, 0 }, /* R103 */
+       { 0x0001, 0x0001, 0 }, /* R104 - Class W 0 */
+       { 0x0000, 0x0000, 0 }, /* R105 */
+       { 0x0000, 0x0000, 0 }, /* R106 */
+       { 0x0000, 0x0000, 0 }, /* R107 */
+       { 0x011F, 0x011F, 0 }, /* R108 - Write Sequencer 0 */
+       { 0x7FFF, 0x7FFF, 0 }, /* R109 - Write Sequencer 1 */
+       { 0x4FFF, 0x4FFF, 0 }, /* R110 - Write Sequencer 2 */
+       { 0x003F, 0x033F, 0 }, /* R111 - Write Sequencer 3 */
+       { 0x03F1, 0x0000, 0 }, /* R112 - Write Sequencer 4 */
+       { 0x0000, 0x0000, 0 }, /* R113 */
+       { 0x0000, 0x0000, 0 }, /* R114 */
+       { 0x0000, 0x0000, 0 }, /* R115 */
+       { 0x0007, 0x0007, 0 }, /* R116 - FLL Control 1 */
+       { 0x3F77, 0x3F77, 0 }, /* R117 - FLL Control 2 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R118 - FLL Control 3 */
+       { 0x7FEF, 0x7FEF, 0 }, /* R119 - FLL Control 4 */
+       { 0x001B, 0x001B, 0 }, /* R120 - FLL Control 5 */
+       { 0x003F, 0x003F, 0 }, /* R121 - GPIO Control 1 */
+       { 0x003F, 0x003F, 0 }, /* R122 - GPIO Control 2 */
+       { 0x003F, 0x003F, 0 }, /* R123 - GPIO Control 3 */
+       { 0x038F, 0x038F, 0 }, /* R124 - GPIO Control 4 */
+       { 0x0000, 0x0000, 0 }, /* R125 */
+       { 0x00FF, 0x00FF, 0 }, /* R126 - Digital Pulls */
+       { 0x07FF, 0x03FF, 1 }, /* R127 - Interrupt Status */
+       { 0x03FF, 0x03FF, 0 }, /* R128 - Interrupt Status Mask */
+       { 0x03FF, 0x03FF, 0 }, /* R129 - Interrupt Polarity */
+       { 0x03FF, 0x03FF, 0 }, /* R130 - Interrupt Debounce */
+       { 0x0000, 0x0000, 0 }, /* R131 */
+       { 0x0000, 0x0000, 0 }, /* R132 */
+       { 0x0000, 0x0000, 0 }, /* R133 */
+       { 0x0001, 0x0001, 0 }, /* R134 - EQ1 */
+       { 0x001F, 0x001F, 0 }, /* R135 - EQ2 */
+       { 0x001F, 0x001F, 0 }, /* R136 - EQ3 */
+       { 0x001F, 0x001F, 0 }, /* R137 - EQ4 */
+       { 0x001F, 0x001F, 0 }, /* R138 - EQ5 */
+       { 0x001F, 0x001F, 0 }, /* R139 - EQ6 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R140 - EQ7 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R141 - EQ8 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R142 - EQ9 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R143 - EQ10 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R144 - EQ11 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R145 - EQ12 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R146 - EQ13 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R147 - EQ14 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R148 - EQ15 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R149 - EQ16 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R150 - EQ17 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R151wm8523_dai - EQ18 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R152 - EQ19 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R153 - EQ20 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R154 - EQ21 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R155 - EQ22 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R156 - EQ23 */
+       { 0xFFFF, 0xFFFF, 0 }, /* R157 - EQ24 */
+       { 0x0000, 0x0000, 0 }, /* R158 */
+       { 0x0000, 0x0000, 0 }, /* R159 */
+       { 0x0000, 0x0000, 0 }, /* R160 */
+       { 0x0002, 0x0002, 0 }, /* R161 - Control Interface Test 1 */
+       { 0x0000, 0x0000, 0 }, /* R162 */
+       { 0x0000, 0x0000, 0 }, /* R163 */
+       { 0x0000, 0x0000, 0 }, /* R164 */
+       { 0x0000, 0x0000, 0 }, /* R165 */
+       { 0x0000, 0x0000, 0 }, /* R166 */
+       { 0x0000, 0x0000, 0 }, /* R167 */
+       { 0x0000, 0x0000, 0 }, /* R168 */
+       { 0x0000, 0x0000, 0 }, /* R169 */
+       { 0x0000, 0x0000, 0 }, /* R170 */
+       { 0x0000, 0x0000, 0 }, /* R171 */
+       { 0x0000, 0x0000, 0 }, /* R172 */
+       { 0x0000, 0x0000, 0 }, /* R173 */
+       { 0x0000, 0x0000, 0 }, /* R174 */
+       { 0x0000, 0x0000, 0 }, /* R175 */
+       { 0x0000, 0x0000, 0 }, /* R176 */
+       { 0x0000, 0x0000, 0 }, /* R177 */
+       { 0x0000, 0x0000, 0 }, /* R178 */
+       { 0x0000, 0x0000, 0 }, /* R179 */
+       { 0x0000, 0x0000, 0 }, /* R180 */
+       { 0x0000, 0x0000, 0 }, /* R181 */
+       { 0x0000, 0x0000, 0 }, /* R182 */
+       { 0x0000, 0x0000, 0 }, /* R183 */
+       { 0x0000, 0x0000, 0 }, /* R184 */
+       { 0x0000, 0x0000, 0 }, /* R185 */
+       { 0x0000, 0x0000, 0 }, /* R186 */
+       { 0x0000, 0x0000, 0 }, /* R187 */
+       { 0x0000, 0x0000, 0 }, /* R188 */
+       { 0x0000, 0x0000, 0 }, /* R189 */
+       { 0x0000, 0x0000, 0 }, /* R190 */
+       { 0x0000, 0x0000, 0 }, /* R191 */
+       { 0x0000, 0x0000, 0 }, /* R192 */
+       { 0x0000, 0x0000, 0 }, /* R193 */
+       { 0x0000, 0x0000, 0 }, /* R194 */
+       { 0x0000, 0x0000, 0 }, /* R195 */
+       { 0x0000, 0x0000, 0 }, /* R196 */
+       { 0x0000, 0x0000, 0 }, /* R197 */
+       { 0x0000, 0x0000, 0 }, /* R198 */
+       { 0x0000, 0x0000, 0 }, /* R199 */
+       { 0x0000, 0x0000, 0 }, /* R200 */
+       { 0x0000, 0x0000, 0 }, /* R201 */
+       { 0x0000, 0x0000, 0 }, /* R202 */
+       { 0x0000, 0x0000, 0 }, /* R203 */
+       { 0x0070, 0x0070, 0 }, /* R204 - Analogue Output Bias 0 */
+       { 0x0000, 0x0000, 0 }, /* R205 */
+       { 0x0000, 0x0000, 0 }, /* R206 */
+       { 0x0000, 0x0000, 0 }, /* R207 */
+       { 0x0000, 0x0000, 0 }, /* R208 */
+       { 0x0000, 0x0000, 0 }, /* R209 */
+       { 0x0000, 0x0000, 0 }, /* R210 */
+       { 0x0000, 0x0000, 0 }, /* R211 */
+       { 0x0000, 0x0000, 0 }, /* R212 */
+       { 0x0000, 0x0000, 0 }, /* R213 */
+       { 0x0000, 0x0000, 0 }, /* R214 */
+       { 0x0000, 0x0000, 0 }, /* R215 */
+       { 0x0000, 0x0000, 0 }, /* R216 */
+       { 0x0000, 0x0000, 0 }, /* R217 */
+       { 0x0000, 0x0000, 0 }, /* R218 */
+       { 0x0000, 0x0000, 0 }, /* R219 */
+       { 0x0000, 0x0000, 0 }, /* R220 */
+       { 0x0000, 0x0000, 0 }, /* R221 */
+       { 0x0000, 0x0000, 0 }, /* R222 */
+       { 0x0000, 0x0000, 0 }, /* R223 */
+       { 0x0000, 0x0000, 0 }, /* R224 */
+       { 0x0000, 0x0000, 0 }, /* R225 */
+       { 0x0000, 0x0000, 0 }, /* R226 */
+       { 0x0000, 0x0000, 0 }, /* R227 */
+       { 0x0000, 0x0000, 0 }, /* R228 */
+       { 0x0000, 0x0000, 0 }, /* R229 */
+       { 0x0000, 0x0000, 0 }, /* R230 */
+       { 0x0000, 0x0000, 0 }, /* R231 */
+       { 0x0000, 0x0000, 0 }, /* R232 */
+       { 0x0000, 0x0000, 0 }, /* R233 */
+       { 0x0000, 0x0000, 0 }, /* R234 */
+       { 0x0000, 0x0000, 0 }, /* R235 */
+       { 0x0000, 0x0000, 0 }, /* R236 */
+       { 0x0000, 0x0000, 0 }, /* R237 */
+       { 0x0000, 0x0000, 0 }, /* R238 */
+       { 0x0000, 0x0000, 0 }, /* R239 */
+       { 0x0000, 0x0000, 0 }, /* R240 */
+       { 0x0000, 0x0000, 0 }, /* R241 */
+       { 0x0000, 0x0000, 0 }, /* R242 */
+       { 0x0000, 0x0000, 0 }, /* R243 */
+       { 0x0000, 0x0000, 0 }, /* R244 */
+       { 0x0000, 0x0000, 0 }, /* R245 */
+       { 0x0000, 0x0000, 0 }, /* R246 */
+       { 0x0001, 0x0001, 0 }, /* R247 - FLL NCO Test 0 */
+       { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
+};
+
+static int wm8904_volatile_register(unsigned int reg)
+{
+       return wm8904_access[reg].vol;
+}
+
+static int wm8904_reset(struct snd_soc_codec *codec)
+{
+       return snd_soc_write(codec, WM8904_SW_RESET_AND_ID, 0);
+}
+
+static int wm8904_configure_clocking(struct snd_soc_codec *codec)
+{
+       struct wm8904_priv *wm8904 = codec->private_data;
+       unsigned int clock0, clock2, rate;
+
+       /* Gate the clock while we're updating to avoid misclocking */
+       clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+                           WM8904_SYSCLK_SRC, 0);
+
+       /* This should be done on init() for bypass paths */
+       switch (wm8904->sysclk_src) {
+       case WM8904_CLK_MCLK:
+               dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+
+               clock2 &= ~WM8904_SYSCLK_SRC;
+               rate = wm8904->mclk_rate;
+
+               /* Ensure the FLL is stopped */
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                                   WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+               break;
+
+       case WM8904_CLK_FLL:
+               dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+                       wm8904->fll_fout);
+
+               clock2 |= WM8904_SYSCLK_SRC;
+               rate = wm8904->fll_fout;
+               break;
+
+       default:
+               dev_err(codec->dev, "System clock not configured\n");
+               return -EINVAL;
+       }
+
+       /* SYSCLK shouldn't be over 13.5MHz */
+       if (rate > 13500000) {
+               clock0 = WM8904_MCLK_DIV;
+               wm8904->sysclk_rate = rate / 2;
+       } else {
+               clock0 = 0;
+               wm8904->sysclk_rate = rate;
+       }
+
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
+                           clock0);
+
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+                           WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
+
+       dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
+
+       return 0;
+}
+
+static void wm8904_set_drc(struct snd_soc_codec *codec)
+{
+       struct wm8904_priv *wm8904 = codec->private_data;
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       int save, i;
+
+       /* Save any enables; the configuration should clear them. */
+       save = snd_soc_read(codec, WM8904_DRC_0);
+
+       for (i = 0; i < WM8904_DRC_REGS; i++)
+               snd_soc_update_bits(codec, WM8904_DRC_0 + i, 0xffff,
+                                   pdata->drc_cfgs[wm8904->drc_cfg].regs[i]);
+
+       /* Reenable the DRC */
+       snd_soc_update_bits(codec, WM8904_DRC_0,
+                           WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save);
+}
+
+static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;       
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       int value = ucontrol->value.integer.value[0];
+
+       if (value >= pdata->num_drc_cfgs)
+               return -EINVAL;
+
+       wm8904->drc_cfg = value;
+
+       wm8904_set_drc(codec);
+
+       return 0;
+}
+
+static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;
+
+       ucontrol->value.enumerated.item[0] = wm8904->drc_cfg;
+
+       return 0;
+}
+
+static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
+{
+       struct wm8904_priv *wm8904 = codec->private_data;
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       int best, best_val, save, i, cfg;
+
+       if (!pdata || !wm8904->num_retune_mobile_texts)
+               return;
+
+       /* Find the version of the currently selected configuration
+        * with the nearest sample rate. */
+       cfg = wm8904->retune_mobile_cfg;
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+               if (strcmp(pdata->retune_mobile_cfgs[i].name,
+                          wm8904->retune_mobile_texts[cfg]) == 0 &&
+                   abs(pdata->retune_mobile_cfgs[i].rate
+                       - wm8904->fs) < best_val) {
+                       best = i;
+                       best_val = abs(pdata->retune_mobile_cfgs[i].rate
+                                      - wm8904->fs);
+               }
+       }
+
+       dev_dbg(codec->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n",
+               pdata->retune_mobile_cfgs[best].name,
+               pdata->retune_mobile_cfgs[best].rate,
+               wm8904->fs);
+
+       /* The EQ will be disabled while reconfiguring it, remember the
+        * current configuration. 
+        */
+       save = snd_soc_read(codec, WM8904_EQ1);
+
+       for (i = 0; i < WM8904_EQ_REGS; i++)
+               snd_soc_update_bits(codec, WM8904_EQ1 + i, 0xffff,
+                               pdata->retune_mobile_cfgs[best].regs[i]);
+
+       snd_soc_update_bits(codec, WM8904_EQ1, WM8904_EQ_ENA, save);
+}
+
+static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;       
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       int value = ucontrol->value.integer.value[0];
+
+       if (value >= pdata->num_retune_mobile_cfgs)
+               return -EINVAL;
+
+       wm8904->retune_mobile_cfg = value;
+
+       wm8904_set_retune_mobile(codec);
+
+       return 0;
+}
+
+static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;
+
+       ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg;
+
+       return 0;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8904_set_deemph(struct snd_soc_codec *codec)
+{
+       struct wm8904_priv *wm8904 = codec->private_data;
+       int val, i, best;
+
+       /* If we're using deemphasis select the nearest available sample 
+        * rate.
+        */
+       if (wm8904->deemph) {
+               best = 1;
+               for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+                       if (abs(deemph_settings[i] - wm8904->fs) <
+                           abs(deemph_settings[best] - wm8904->fs))
+                               best = i;
+               }
+
+               val = best << WM8904_DEEMPH_SHIFT;
+       } else {
+               val = 0;
+       }
+
+       dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+       return snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+                                  WM8904_DEEMPH_MASK, val);
+}
+
+static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;
+
+       return wm8904->deemph;
+}
+
+static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8904_priv *wm8904 = codec->private_data;
+       int deemph = ucontrol->value.enumerated.item[0];
+
+       if (deemph > 1)
+               return -EINVAL;
+
+       wm8904->deemph = deemph;
+
+       return wm8904_set_deemph(codec);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *input_mode_text[] = {
+       "Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static const struct soc_enum lin_mode =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+
+static const struct soc_enum rin_mode =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+
+static const char *hpf_mode_text[] = {
+       "Hi-fi", "Voice 1", "Voice 2", "Voice 3"
+};
+
+static const struct soc_enum hpf_mode =
+       SOC_ENUM_SINGLE(WM8904_ADC_DIGITAL_0, 5, 4, hpf_mode_text);
+
+static const struct snd_kcontrol_new wm8904_adc_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT,
+                WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv),
+
+SOC_ENUM("Left Caputure Mode", lin_mode),
+SOC_ENUM("Right Capture Mode", rin_mode),
+
+/* No TLV since it depends on mode */
+SOC_DOUBLE_R("Capture Volume", WM8904_ANALOGUE_LEFT_INPUT_0,
+            WM8904_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0),
+SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
+            WM8904_ANALOGUE_RIGHT_INPUT_0, 7, 1, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
+SOC_ENUM("High Pass Filter Mode", hpf_mode),
+
+SOC_SINGLE("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0),
+};
+
+static const char *drc_path_text[] = {
+       "ADC", "DAC"
+};
+
+static const struct soc_enum drc_path =
+       SOC_ENUM_SINGLE(WM8904_DRC_0, 14, 2, drc_path_text);
+
+static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = {
+SOC_SINGLE_TLV("Digital Playback Boost Volume", 
+              WM8904_AUDIO_INTERFACE_0, 9, 3, 0, dac_boost_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8904_DAC_DIGITAL_VOLUME_LEFT,
+                WM8904_DAC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8904_ANALOGUE_OUT1_LEFT,
+                WM8904_ANALOGUE_OUT1_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Switch", WM8904_ANALOGUE_OUT1_LEFT,
+            WM8904_ANALOGUE_OUT1_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8904_ANALOGUE_OUT1_LEFT,
+            WM8904_ANALOGUE_OUT1_RIGHT, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("Line Output Volume", WM8904_ANALOGUE_OUT2_LEFT,
+                WM8904_ANALOGUE_OUT2_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Line Output Switch", WM8904_ANALOGUE_OUT2_LEFT,
+            WM8904_ANALOGUE_OUT2_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Line Output ZC Switch", WM8904_ANALOGUE_OUT2_LEFT,
+            WM8904_ANALOGUE_OUT2_RIGHT, 6, 1, 0),
+
+SOC_SINGLE("EQ Switch", WM8904_EQ1, 0, 1, 0),
+SOC_SINGLE("DRC Switch", WM8904_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE("DAC OSRx2 Switch", WM8904_DAC_DIGITAL_1, 6, 1, 0),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+                   wm8904_get_deemph, wm8904_put_deemph),
+};
+
+static const struct snd_kcontrol_new wm8904_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8904_DAC_DIGITAL_0, 4, 8, 15, 0,
+              sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new wm8904_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8904_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8904_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8904_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8904_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       BUG_ON(event != SND_SOC_DAPM_POST_PMU);
+
+       /* Maximum startup time */
+       udelay(500);
+
+       return 0;
+}
+
+static int sysclk_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm8904_priv *wm8904 = codec->private_data;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* If we're using the FLL then we only start it when
+                * required; we assume that the configuration has been
+                * done previously and all we need to do is kick it
+                * off.
+                */
+               switch (wm8904->sysclk_src) {
+               case WM8904_CLK_FLL:
+                       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                                           WM8904_FLL_OSC_ENA,
+                                           WM8904_FLL_OSC_ENA);
+
+                       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                                           WM8904_FLL_ENA,
+                                           WM8904_FLL_ENA);
+                       break;
+
+               default:
+                       break;
+               }
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                                   WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static int out_pga_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm8904_priv *wm8904 = codec->private_data;
+       int reg, val;
+       int dcs_mask;
+       int dcs_l, dcs_r;
+       int dcs_l_reg, dcs_r_reg;
+       int timeout;
+
+       /* This code is shared between HP and LINEOUT; we do all our
+        * power management in stereo pairs to avoid latency issues so
+        * we reuse shift to identify which rather than strcmp() the
+        * name. */
+       reg = w->shift;
+
+       switch (reg) {
+       case WM8904_ANALOGUE_HP_0:
+               dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
+               dcs_r_reg = WM8904_DC_SERVO_8;
+               dcs_l_reg = WM8904_DC_SERVO_9;
+               dcs_l = 0;
+               dcs_r = 1;
+               break;
+       case WM8904_ANALOGUE_LINEOUT_0:
+               dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
+               dcs_r_reg = WM8904_DC_SERVO_6;
+               dcs_l_reg = WM8904_DC_SERVO_7;
+               dcs_l = 2;
+               dcs_r = 3;
+               break;
+       default:
+               BUG();
+               return -EINVAL;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* Power on the amplifier */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_ENA | WM8904_HPR_ENA,
+                                   WM8904_HPL_ENA | WM8904_HPR_ENA);
+
+               /* Enable the first stage */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY,
+                                   WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY);
+
+               /* Power up the DC servo */
+               snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+                                   dcs_mask, dcs_mask);
+
+               /* Either calibrate the DC servo or restore cached state
+                * if we have that.
+                */
+               if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) {
+                       dev_dbg(codec->dev, "Restoring DC servo state\n");
+
+                       snd_soc_write(codec, dcs_l_reg,
+                                     wm8904->dcs_state[dcs_l]);
+                       snd_soc_write(codec, dcs_r_reg,
+                                     wm8904->dcs_state[dcs_r]);
+
+                       snd_soc_write(codec, WM8904_DC_SERVO_1, dcs_mask);
+
+                       timeout = 20;
+               } else {
+                       dev_dbg(codec->dev, "Calibrating DC servo\n");
+
+                       snd_soc_write(codec, WM8904_DC_SERVO_1,
+                               dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT);
+
+                       timeout = 500;
+               }
+
+               /* Wait for DC servo to complete */
+               dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT;
+               do {
+                       val = snd_soc_read(codec, WM8904_DC_SERVO_READBACK_0);
+                       if ((val & dcs_mask) == dcs_mask)
+                               break;
+
+                       msleep(1);
+               } while (--timeout);
+
+               if ((val & dcs_mask) != dcs_mask)
+                       dev_warn(codec->dev, "DC servo timed out\n");
+               else
+                       dev_dbg(codec->dev, "DC servo ready\n");
+
+               /* Enable the output stage */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+                                   WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
+
+               /* Unshort the output itself */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_RMV_SHORT |
+                                   WM8904_HPR_RMV_SHORT,
+                                   WM8904_HPL_RMV_SHORT |
+                                   WM8904_HPR_RMV_SHORT);
+
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /* Short the output */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_RMV_SHORT |
+                                   WM8904_HPR_RMV_SHORT, 0);
+
+               /* Cache the DC servo configuration; this will be
+                * invalidated if we change the configuration. */
+               wm8904->dcs_state[dcs_l] = snd_soc_read(codec, dcs_l_reg);
+               wm8904->dcs_state[dcs_r] = snd_soc_read(codec, dcs_r_reg);
+
+               snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+                                   dcs_mask, 0);
+
+               /* Disable the amplifier input and output stages */
+               snd_soc_update_bits(codec, reg,
+                                   WM8904_HPL_ENA | WM8904_HPR_ENA |
+                                   WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY |
+                                   WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+                                   0);
+               break;
+       }
+
+       return 0;
+}
+
+static const char *lin_text[] = {
+       "IN1L", "IN2L", "IN3L"
+};
+
+static const struct soc_enum lin_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 2, 3, lin_text);
+
+static const struct snd_kcontrol_new lin_mux =
+       SOC_DAPM_ENUM("Left Capture Mux", lin_enum);
+
+static const struct soc_enum lin_inv_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 4, 3, lin_text);
+
+static const struct snd_kcontrol_new lin_inv_mux =
+       SOC_DAPM_ENUM("Left Capture Inveting Mux", lin_inv_enum);
+
+static const char *rin_text[] = {
+       "IN1R", "IN2R", "IN3R"
+};
+
+static const struct soc_enum rin_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 2, 3, rin_text);
+
+static const struct snd_kcontrol_new rin_mux =
+       SOC_DAPM_ENUM("Right Capture Mux", rin_enum);
+
+static const struct soc_enum rin_inv_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 4, 3, rin_text);
+
+static const struct snd_kcontrol_new rin_inv_mux =
+       SOC_DAPM_ENUM("Right Capture Inveting Mux", rin_inv_enum);
+
+static const char *aif_text[] = {
+       "Left", "Right"
+};
+
+static const struct soc_enum aifoutl_enum =
+       SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 7, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+       SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static const struct soc_enum aifoutr_enum =
+       SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 6, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+       SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static const struct soc_enum aifinl_enum =
+       SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 5, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+       SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static const struct soc_enum aifinr_enum =
+       SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 4, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+       SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const struct snd_soc_dapm_widget wm8904_core_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8904_CLOCK_RATES_2, 2, 0, sysclk_event,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8904_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8904_CLOCK_RATES_2, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux),
+SND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+                &lin_inv_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux),
+SND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+                &rin_inv_mux),
+
+SND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("Right Capture PGA", WM8904_POWER_MANAGEMENT_0, 0, 0,
+                NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8904_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8904_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8904_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8904_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_SUPPLY("Charge pump", WM8904_CHARGE_PUMP_0, 0, 0, cp_event,
+                   SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("HPL PGA", WM8904_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("HPR PGA", WM8904_POWER_MANAGEMENT_2, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LINEL PGA", WM8904_POWER_MANAGEMENT_3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINER PGA", WM8904_POWER_MANAGEMENT_3, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, WM8904_ANALOGUE_HP_0,
+                  0, NULL, 0, out_pga_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0,
+                  0, NULL, 0, out_pga_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+};
+
+static const char *out_mux_text[] = {
+       "DAC", "Bypass"
+};
+
+static const struct soc_enum hpl_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 3, 2, out_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+       SOC_DAPM_ENUM("HPL Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 2, 2, out_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+       SOC_DAPM_ENUM("HPR Mux", hpr_enum);
+
+static const struct soc_enum linel_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 1, 2, out_mux_text);
+
+static const struct snd_kcontrol_new linel_mux =
+       SOC_DAPM_ENUM("LINEL Mux", linel_enum);
+
+static const struct soc_enum liner_enum =
+       SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text);
+
+static const struct snd_kcontrol_new liner_mux =
+       SOC_DAPM_ENUM("LINEL Mux", liner_enum);
+
+static const char *sidetone_text[] = {
+       "None", "Left", "Right"
+};
+
+static const struct soc_enum dacl_sidetone_enum =
+       SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_sidetone_mux =
+       SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum);
+
+static const struct soc_enum dacr_sidetone_enum =
+       SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 0, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacr_sidetone_mux =
+       SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum);
+
+static const struct snd_soc_dapm_widget wm8904_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Class G", WM8904_CLASS_W_0, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &dacl_sidetone_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &dacr_sidetone_mux),
+
+SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("LINEL Mux", SND_SOC_NOPM, 0, 0, &linel_mux),
+SND_SOC_DAPM_MUX("LINER Mux", SND_SOC_NOPM, 0, 0, &liner_mux),
+};
+
+static const struct snd_soc_dapm_route core_intercon[] = {
+       { "CLK_DSP", NULL, "SYSCLK" },
+       { "TOCLK", NULL, "SYSCLK" },
+};
+
+static const struct snd_soc_dapm_route adc_intercon[] = {
+       { "Left Capture Mux", "IN1L", "IN1L" },
+       { "Left Capture Mux", "IN2L", "IN2L" },
+       { "Left Capture Mux", "IN3L", "IN3L" },
+
+       { "Left Capture Inverting Mux", "IN1L", "IN1L" },
+       { "Left Capture Inverting Mux", "IN2L", "IN2L" },
+       { "Left Capture Inverting Mux", "IN3L", "IN3L" },
+
+       { "Right Capture Mux", "IN1R", "IN1R" },
+       { "Right Capture Mux", "IN2R", "IN2R" },
+       { "Right Capture Mux", "IN3R", "IN3R" },
+
+       { "Right Capture Inverting Mux", "IN1R", "IN1R" },
+       { "Right Capture Inverting Mux", "IN2R", "IN2R" },
+       { "Right Capture Inverting Mux", "IN3R", "IN3R" },
+
+       { "Left Capture PGA", NULL, "Left Capture Mux" },
+       { "Left Capture PGA", NULL, "Left Capture Inverting Mux" },
+
+       { "Right Capture PGA", NULL, "Right Capture Mux" },
+       { "Right Capture PGA", NULL, "Right Capture Inverting Mux" },
+
+       { "AIFOUTL", "Left",  "ADCL" },
+       { "AIFOUTL", "Right", "ADCR" },
+       { "AIFOUTR", "Left",  "ADCL" },
+       { "AIFOUTR", "Right", "ADCR" },
+
+       { "ADCL", NULL, "CLK_DSP" },
+       { "ADCL", NULL, "Left Capture PGA" },
+
+       { "ADCR", NULL, "CLK_DSP" },
+       { "ADCR", NULL, "Right Capture PGA" },
+};
+
+static const struct snd_soc_dapm_route dac_intercon[] = {
+       { "DACL", "Right", "AIFINR" },
+       { "DACL", "Left",  "AIFINL" },
+       { "DACL", NULL, "CLK_DSP" },
+
+       { "DACR", "Right", "AIFINR" },
+       { "DACR", "Left",  "AIFINL" },
+       { "DACR", NULL, "CLK_DSP" },
+
+       { "Charge pump", NULL, "SYSCLK" },
+
+       { "Headphone Output", NULL, "HPL PGA" },
+       { "Headphone Output", NULL, "HPR PGA" },
+       { "Headphone Output", NULL, "Charge pump" },
+       { "Headphone Output", NULL, "TOCLK" },
+
+       { "Line Output", NULL, "LINEL PGA" },
+       { "Line Output", NULL, "LINER PGA" },
+       { "Line Output", NULL, "Charge pump" },
+       { "Line Output", NULL, "TOCLK" },
+
+       { "HPOUTL", NULL, "Headphone Output" },
+       { "HPOUTR", NULL, "Headphone Output" },
+
+       { "LINEOUTL", NULL, "Line Output" },
+       { "LINEOUTR", NULL, "Line Output" },
+};
+
+static const struct snd_soc_dapm_route wm8904_intercon[] = {
+       { "Left Sidetone", "Left", "ADCL" },
+       { "Left Sidetone", "Right", "ADCR" },
+       { "DACL", NULL, "Left Sidetone" },
+       
+       { "Right Sidetone", "Left", "ADCL" },
+       { "Right Sidetone", "Right", "ADCR" },
+       { "DACR", NULL, "Right Sidetone" },
+
+       { "Left Bypass", NULL, "Class G" },
+       { "Left Bypass", NULL, "Left Capture PGA" },
+
+       { "Right Bypass", NULL, "Class G" },
+       { "Right Bypass", NULL, "Right Capture PGA" },
+
+       { "HPL Mux", "DAC", "DACL" },
+       { "HPL Mux", "Bypass", "Left Bypass" },
+
+       { "HPR Mux", "DAC", "DACR" },
+       { "HPR Mux", "Bypass", "Right Bypass" },
+
+       { "LINEL Mux", "DAC", "DACL" },
+       { "LINEL Mux", "Bypass", "Left Bypass" },
+
+       { "LINER Mux", "DAC", "DACR" },
+       { "LINER Mux", "Bypass", "Right Bypass" },
+
+       { "HPL PGA", NULL, "HPL Mux" },
+       { "HPR PGA", NULL, "HPR Mux" },
+
+       { "LINEL PGA", NULL, "LINEL Mux" },
+       { "LINER PGA", NULL, "LINER Mux" },
+};
+
+static int wm8904_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_add_controls(codec, wm8904_adc_snd_controls,
+                            ARRAY_SIZE(wm8904_adc_snd_controls));
+       snd_soc_add_controls(codec, wm8904_dac_snd_controls,
+                            ARRAY_SIZE(wm8904_dac_snd_controls));
+       snd_soc_add_controls(codec, wm8904_snd_controls,
+                            ARRAY_SIZE(wm8904_snd_controls));
+
+       snd_soc_dapm_new_controls(codec, wm8904_core_dapm_widgets,
+                                 ARRAY_SIZE(wm8904_core_dapm_widgets));
+       snd_soc_dapm_new_controls(codec, wm8904_adc_dapm_widgets,
+                                 ARRAY_SIZE(wm8904_adc_dapm_widgets));
+       snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets,
+                                 ARRAY_SIZE(wm8904_dac_dapm_widgets));
+       snd_soc_dapm_new_controls(codec, wm8904_dapm_widgets,
+                                 ARRAY_SIZE(wm8904_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, core_intercon,
+                               ARRAY_SIZE(core_intercon));
+       snd_soc_dapm_add_routes(codec, adc_intercon, ARRAY_SIZE(adc_intercon));
+       snd_soc_dapm_add_routes(codec, dac_intercon, ARRAY_SIZE(dac_intercon));
+       snd_soc_dapm_add_routes(codec, wm8904_intercon,
+                               ARRAY_SIZE(wm8904_intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static struct {
+       int ratio;
+       unsigned int clk_sys_rate;
+} clk_sys_rates[] = {
+       {   64,  0 },
+       {  128,  1 },
+       {  192,  2 },
+       {  256,  3 },
+       {  384,  4 },
+       {  512,  5 },
+       {  786,  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  },
+       { 50,  5  },
+       { 55,  6  },
+       { 60,  7  },
+       { 80,  8  },
+       { 100, 9  },
+       { 110, 10 },
+       { 120, 11 },
+       { 160, 12 },
+       { 200, 13 },
+       { 220, 14 },
+       { 240, 16 },
+       { 200, 17 },
+       { 320, 18 },
+       { 440, 19 },
+       { 480, 20 },
+};
+
+
+static int wm8904_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 wm8904_priv *wm8904 = codec->private_data;
+       int ret, i, best, best_val, cur_val;
+       unsigned int aif1 = 0;
+       unsigned int aif2 = 0;
+       unsigned int aif3 = 0;
+       unsigned int clock1 = 0;
+       unsigned int dac_digital1 = 0;
+
+       /* What BCLK do we need? */
+       wm8904->fs = params_rate(params);
+       if (wm8904->tdm_slots) {
+               dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+                       wm8904->tdm_slots, wm8904->tdm_width);
+               wm8904->bclk = snd_soc_calc_bclk(wm8904->fs,
+                                                wm8904->tdm_width, 2,
+                                                wm8904->tdm_slots);
+       } else {
+               wm8904->bclk = snd_soc_params_to_bclk(params);
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               aif1 |= 0x40;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               aif1 |= 0x80;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               aif1 |= 0xc0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+
+       dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8904->bclk);
+
+       ret = wm8904_configure_clocking(codec);
+       if (ret != 0)
+               return ret;
+
+       /* Select nearest CLK_SYS_RATE */
+       best = 0;
+       best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio)
+                      - wm8904->fs);
+       for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+               cur_val = abs((wm8904->sysclk_rate /
+                              clk_sys_rates[i].ratio) - wm8904->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);
+       clock1 |= (clk_sys_rates[best].clk_sys_rate
+                  << WM8904_CLK_SYS_RATE_SHIFT);
+
+       /* SAMPLE_RATE */
+       best = 0;
+       best_val = abs(wm8904->fs - sample_rates[0].rate);
+       for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+               /* Closest match */
+               cur_val = abs(wm8904->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);
+       clock1 |= (sample_rates[best].sample_rate
+                  << WM8904_SAMPLE_RATE_SHIFT);
+
+       /* Enable sloping stopband filter for low sample rates */
+       if (wm8904->fs <= 24000)
+               dac_digital1 |= WM8904_DAC_SB_FILT;
+
+       /* BCLK_DIV */
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+               cur_val = ((wm8904->sysclk_rate * 10) / bclk_divs[i].div)
+                       - wm8904->bclk;
+               if (cur_val < 0) /* Table is sorted */
+                       break;
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
+       dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+               bclk_divs[best].div, wm8904->bclk);
+       aif2 |= bclk_divs[best].bclk_div;
+
+       /* LRCLK is a simple fraction of BCLK */
+       dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs);
+       aif3 |= wm8904->bclk / wm8904->fs;
+
+       /* Apply the settings */
+       snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+                           WM8904_DAC_SB_FILT, dac_digital1);
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+                           WM8904_AIF_WL_MASK, aif1);
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_2,
+                           WM8904_BCLK_DIV_MASK, aif2);
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+                           WM8904_LRCLK_RATE_MASK, aif3);
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_1,
+                           WM8904_SAMPLE_RATE_MASK |
+                           WM8904_CLK_SYS_RATE_MASK, clock1);
+
+       /* Update filters for the new settings */
+       wm8904_set_retune_mobile(codec);
+       wm8904_set_deemph(codec);
+
+       return 0;
+}
+
+
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                            unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8904_priv *priv = codec->private_data;
+
+       switch (clk_id) {
+       case WM8904_CLK_MCLK:
+               priv->sysclk_src = clk_id;
+               priv->mclk_rate = freq;
+               break;
+
+       case WM8904_CLK_FLL:
+               priv->sysclk_src = clk_id;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       wm8904_configure_clocking(codec);
+
+       return 0;
+}
+
+static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int aif1 = 0;
+       unsigned int aif3 = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM:
+               aif3 |= WM8904_LRCLK_DIR;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               aif1 |= WM8904_BCLK_DIR;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aif1 |= WM8904_BCLK_DIR;
+               aif3 |= WM8904_LRCLK_DIR;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
+               aif1 |= WM8904_AIF_LRCLK_INV;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif1 |= 0x3;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               aif1 |= 0x2;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aif1 |= 0x1;
+               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 |= WM8904_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 |= WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif1 |= WM8904_AIF_BCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       aif1 |= WM8904_AIF_LRCLK_INV;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+                           WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV |
+                           WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1);
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+                           WM8904_LRCLK_DIR, aif3);
+
+       return 0;
+}
+
+
+static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                              unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8904_priv *wm8904 = codec->private_data;
+       int aif1 = 0;
+
+       /* Don't need to validate anything if we're turning off TDM */
+       if (slots == 0)
+               goto out;
+
+       /* Note that we allow configurations we can't handle ourselves - 
+        * for example, we can generate clocks for slots 2 and up even if
+        * we can't use those slots ourselves.
+        */
+       aif1 |= WM8904_AIFADC_TDM | WM8904_AIFDAC_TDM;
+
+       switch (rx_mask) {
+       case 3:
+               break;
+       case 0xc:
+               aif1 |= WM8904_AIFADC_TDM_CHAN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+
+       switch (tx_mask) {
+       case 3:
+               break;
+       case 0xc:
+               aif1 |= WM8904_AIFDAC_TDM_CHAN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+out:
+       wm8904->tdm_width = slot_width;
+       wm8904->tdm_slots = slots / 2;
+
+       snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+                           WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN |
+                           WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1);
+
+       return 0;
+}
+
+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 = 4;
+       while (Fout * div < 90000000) {
+               div++;
+               if (div > 64) {
+                       pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+                              Fout);
+                       return -EINVAL;
+               }
+       }
+       target = Fout * div;
+       fll_div->fll_outdiv = div - 1;
+
+       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 wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+                         unsigned int Fref, unsigned int Fout)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8904_priv *wm8904 = codec->private_data;
+       struct _fll_div fll_div;
+       int ret, val;
+       int clock2, fll1;
+
+       /* Any change? */
+       if (source == wm8904->fll_src && Fref == wm8904->fll_fref &&
+           Fout == wm8904->fll_fout)
+               return 0;
+
+       clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+
+       if (Fout == 0) {
+               dev_dbg(codec->dev, "FLL disabled\n");
+
+               wm8904->fll_fref = 0;
+               wm8904->fll_fout = 0;
+
+               /* Gate SYSCLK to avoid glitches */
+               snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+                                   WM8904_CLK_SYS_ENA, 0);
+
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                                   WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+               goto out;
+       }
+
+       /* Validate the FLL ID */
+       switch (source) {
+       case WM8904_FLL_MCLK:
+       case WM8904_FLL_LRCLK:
+       case WM8904_FLL_BCLK:
+               ret = fll_factors(&fll_div, Fref, Fout);
+               if (ret != 0)
+                       return ret;
+               break;
+
+       case WM8904_FLL_FREE_RUNNING:
+               dev_dbg(codec->dev, "Using free running FLL\n");
+               /* Force 12MHz and output/4 for now */
+               Fout = 12000000;
+               Fref = 12000000;
+
+               memset(&fll_div, 0, sizeof(fll_div));
+               fll_div.fll_outdiv = 3;
+               break;
+
+       default:
+               dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+               return -EINVAL;
+       }
+
+       /* Save current state then disable the FLL and SYSCLK to avoid
+        * misclocking */
+       fll1 = snd_soc_read(codec, WM8904_FLL_CONTROL_1);
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+                           WM8904_CLK_SYS_ENA, 0);
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                           WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+       /* Unlock forced oscilator control to switch it on/off */
+       snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+                           WM8904_USER_KEY, WM8904_USER_KEY);
+
+       if (fll_id == WM8904_FLL_FREE_RUNNING) {
+               val = WM8904_FLL_FRC_NCO;
+       } else {
+               val = 0;
+       }
+
+       snd_soc_update_bits(codec, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
+                           val);
+       snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+                           WM8904_USER_KEY, 0);
+
+       switch (fll_id) {
+       case WM8904_FLL_MCLK:
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+                                   WM8904_FLL_CLK_REF_SRC_MASK, 0);
+               break;
+
+       case WM8904_FLL_LRCLK:
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+                                   WM8904_FLL_CLK_REF_SRC_MASK, 1);
+               break;
+
+       case WM8904_FLL_BCLK:
+               snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+                                   WM8904_FLL_CLK_REF_SRC_MASK, 2);
+               break;
+       }
+
+       if (fll_div.k)
+               val = WM8904_FLL_FRACN_ENA;
+       else
+               val = 0;
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                           WM8904_FLL_FRACN_ENA, val);
+
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_2,
+                           WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
+                           (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
+                           (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
+
+       snd_soc_write(codec, WM8904_FLL_CONTROL_3, fll_div.k);
+
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
+                           fll_div.n << WM8904_FLL_N_SHIFT);
+
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+                           WM8904_FLL_CLK_REF_DIV_MASK,
+                           fll_div.fll_clk_ref_div 
+                           << WM8904_FLL_CLK_REF_DIV_SHIFT);
+
+       dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+       wm8904->fll_fref = Fref;
+       wm8904->fll_fout = Fout;
+       wm8904->fll_src = source;
+
+       /* Enable the FLL if it was previously active */
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                           WM8904_FLL_OSC_ENA, fll1);
+       snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+                           WM8904_FLL_ENA, fll1);
+
+out:
+       /* Reenable SYSCLK if it was previously active */
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+                           WM8904_CLK_SYS_ENA, clock2);
+
+       return 0;
+}
+
+static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int val;
+
+       if (mute)
+               val = WM8904_DAC_MUTE;
+       else
+               val = 0;
+
+       snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val);
+
+       return 0;
+}
+
+static int wm8904_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8904_priv *wm8904 = codec->private_data;
+       int ret, i;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VMID resistance 2*50k */
+               snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+                                   WM8904_VMID_RES_MASK,
+                                   0x1 << WM8904_VMID_RES_SHIFT);
+
+               /* Normal bias current */
+               snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+                                   WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+                                                   wm8904->supplies);
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "Failed to enable supplies: %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       /* Sync back cached values if they're
+                        * different from the hardware default.
+                        */
+                       for (i = 1; i < ARRAY_SIZE(wm8904->reg_cache); i++) {
+                               if (!wm8904_access[i].writable)
+                                       continue;
+
+                               if (wm8904->reg_cache[i] == wm8904_reg[i])
+                                       continue;
+
+                               snd_soc_write(codec, i, wm8904->reg_cache[i]);
+                       }
+
+                       /* Enable bias */
+                       snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+                                           WM8904_BIAS_ENA, WM8904_BIAS_ENA);
+
+                       /* Enable VMID, VMID buffering, 2*5k resistance */
+                       snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+                                           WM8904_VMID_ENA |
+                                           WM8904_VMID_RES_MASK,
+                                           WM8904_VMID_ENA |
+                                           0x3 << WM8904_VMID_RES_SHIFT);
+
+                       /* Let VMID ramp */
+                       msleep(1);
+               }
+
+               /* Maintain VMID with 2*250k */
+               snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+                                   WM8904_VMID_RES_MASK,
+                                   0x2 << WM8904_VMID_RES_SHIFT);
+
+               /* Bias current *0.5 */
+               snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+                                   WM8904_ISEL_MASK, 0);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* Turn off VMID */
+               snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+                                   WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0);
+
+               /* Stop bias generation */
+               snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+                                   WM8904_BIAS_ENA, 0);
+
+               regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
+                                      wm8904->supplies);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8904_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8904_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 wm8904_dai_ops = {
+       .set_sysclk = wm8904_set_sysclk,
+       .set_fmt = wm8904_set_fmt,
+       .set_tdm_slot = wm8904_set_tdm_slot,
+       .set_pll = wm8904_set_fll,
+       .hw_params = wm8904_hw_params,
+       .digital_mute = wm8904_digital_mute,
+};
+
+struct snd_soc_dai wm8904_dai = {
+       .name = "WM8904",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = WM8904_RATES,
+               .formats = WM8904_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = WM8904_RATES,
+               .formats = WM8904_FORMATS,
+       },
+       .ops = &wm8904_dai_ops,
+       .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8904_dai);
+
+#ifdef CONFIG_PM
+static int wm8904_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;
+
+       wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8904_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define wm8904_suspend NULL
+#define wm8904_resume NULL
+#endif
+
+static void wm8904_handle_retune_mobile_pdata(struct wm8904_priv *wm8904)
+{
+       struct snd_soc_codec *codec = &wm8904->codec;
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       struct snd_kcontrol_new control =
+               SOC_ENUM_EXT("EQ Mode",
+                            wm8904->retune_mobile_enum,
+                            wm8904_get_retune_mobile_enum,
+                            wm8904_put_retune_mobile_enum);
+       int ret, i, j;
+       const char **t;
+
+       /* We need an array of texts for the enum API but the number
+        * of texts is likely to be less than the number of
+        * configurations due to the sample rate dependency of the
+        * configurations. */
+       wm8904->num_retune_mobile_texts = 0;
+       wm8904->retune_mobile_texts = NULL;
+       for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+               for (j = 0; j < wm8904->num_retune_mobile_texts; j++) {
+                       if (strcmp(pdata->retune_mobile_cfgs[i].name,
+                                  wm8904->retune_mobile_texts[j]) == 0)
+                               break;
+               }
+
+               if (j != wm8904->num_retune_mobile_texts)
+                       continue;
+
+               /* Expand the array... */
+               t = krealloc(wm8904->retune_mobile_texts,
+                            sizeof(char *) * 
+                            (wm8904->num_retune_mobile_texts + 1),
+                            GFP_KERNEL);
+               if (t == NULL)
+                       continue;
+
+               /* ...store the new entry... */
+               t[wm8904->num_retune_mobile_texts] = 
+                       pdata->retune_mobile_cfgs[i].name;
+
+               /* ...and remember the new version. */
+               wm8904->num_retune_mobile_texts++;
+               wm8904->retune_mobile_texts = t;
+       }
+
+       dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+               wm8904->num_retune_mobile_texts);
+
+       wm8904->retune_mobile_enum.max = wm8904->num_retune_mobile_texts;
+       wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts;
+
+       ret = snd_soc_add_controls(&wm8904->codec, &control, 1);
+       if (ret != 0)
+               dev_err(wm8904->codec.dev,
+                       "Failed to add ReTune Mobile control: %d\n", ret);
+}
+
+static void wm8904_handle_pdata(struct wm8904_priv *wm8904)
+{
+       struct snd_soc_codec *codec = &wm8904->codec;
+       struct wm8904_pdata *pdata = wm8904->pdata;
+       int ret, i;
+
+       if (!pdata) {
+               snd_soc_add_controls(&wm8904->codec, wm8904_eq_controls,
+                                    ARRAY_SIZE(wm8904_eq_controls));
+               return;
+       }
+
+       dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+       if (pdata->num_drc_cfgs) {
+               struct snd_kcontrol_new control =
+                       SOC_ENUM_EXT("DRC Mode", wm8904->drc_enum,
+                                    wm8904_get_drc_enum, wm8904_put_drc_enum);
+
+               /* We need an array of texts for the enum API */
+               wm8904->drc_texts = kmalloc(sizeof(char *)
+                                           * pdata->num_drc_cfgs, GFP_KERNEL);
+               if (!wm8904->drc_texts) {
+                       dev_err(wm8904->codec.dev,
+                               "Failed to allocate %d DRC config texts\n",
+                               pdata->num_drc_cfgs);
+                       return;
+               }
+
+               for (i = 0; i < pdata->num_drc_cfgs; i++)
+                       wm8904->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+               wm8904->drc_enum.max = pdata->num_drc_cfgs;
+               wm8904->drc_enum.texts = wm8904->drc_texts;
+
+               ret = snd_soc_add_controls(&wm8904->codec, &control, 1);
+               if (ret != 0)
+                       dev_err(wm8904->codec.dev,
+                               "Failed to add DRC mode control: %d\n", ret);
+
+               wm8904_set_drc(codec);
+       }
+
+       dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+               pdata->num_retune_mobile_cfgs);
+
+       if (pdata->num_retune_mobile_cfgs)
+               wm8904_handle_retune_mobile_pdata(wm8904);
+       else
+               snd_soc_add_controls(&wm8904->codec, wm8904_eq_controls,
+                                    ARRAY_SIZE(wm8904_eq_controls));
+}
+
+static int wm8904_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8904_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8904_codec;
+       codec = wm8904_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;
+       }
+
+       wm8904_handle_pdata(codec->private_data);
+
+       wm8904_add_widgets(codec);
+
+       return ret;
+
+pcm_err:
+       return ret;
+}
+
+static int wm8904_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_wm8904 = {
+       .probe =        wm8904_probe,
+       .remove =       wm8904_remove,
+       .suspend =      wm8904_suspend,
+       .resume =       wm8904_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8904);
+
+static int wm8904_register(struct wm8904_priv *wm8904,
+                          enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &wm8904->codec;
+       int i;
+
+       if (wm8904_codec) {
+               dev_err(codec->dev, "Another WM8904 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8904;
+       codec->name = "WM8904";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8904_set_bias_level;
+       codec->dai = &wm8904_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8904_MAX_REGISTER;
+       codec->reg_cache = &wm8904->reg_cache;
+       codec->volatile_register = wm8904_volatile_register;
+
+       memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++)
+               wm8904->supplies[i].supply = wm8904_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8904->supplies),
+                                wm8904->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+                                   wm8904->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_get;
+       }
+
+       ret = snd_soc_read(codec, WM8904_SW_RESET_AND_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read ID register\n");
+               goto err_enable;
+       }
+       if (ret != wm8904_reg[WM8904_SW_RESET_AND_ID]) {
+               dev_err(codec->dev, "Device is not a WM8904, ID is %x\n", ret);
+               ret = -EINVAL;
+               goto err_enable;
+       }
+
+       ret = snd_soc_read(codec, WM8904_REVISION);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_enable;
+       }
+       dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+       ret = wm8904_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               goto err_enable;
+       }
+
+       wm8904_dai.dev = codec->dev;
+
+       /* Change some default settings - latch VU and enable ZC */
+       wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
+       wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
+       wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
+       wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
+       wm8904->reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
+               WM8904_HPOUTLZC;
+       wm8904->reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
+               WM8904_HPOUTRZC;
+       wm8904->reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
+               WM8904_LINEOUTLZC;
+       wm8904->reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
+               WM8904_LINEOUTRZC;
+       wm8904->reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+
+       /* Set Class W by default - this will be managed by the Class
+        * G widget at runtime where bypass paths are available.
+        */
+       wm8904->reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+
+       /* Use normal bias source */
+       wm8904->reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+
+       wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Bias level configuration will have done an extra enable */
+       regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+
+       wm8904_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(&wm8904_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(wm8904->supplies), wm8904->supplies);
+err_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+err:
+       kfree(wm8904);
+       return ret;
+}
+
+static void wm8904_unregister(struct wm8904_priv *wm8904)
+{
+       wm8904_set_bias_level(&wm8904->codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+       snd_soc_unregister_dai(&wm8904_dai);
+       snd_soc_unregister_codec(&wm8904->codec);
+       kfree(wm8904);
+       wm8904_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8904_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8904_priv *wm8904;
+       struct snd_soc_codec *codec;
+
+       wm8904 = kzalloc(sizeof(struct wm8904_priv), GFP_KERNEL);
+       if (wm8904 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8904->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8904);
+       codec->control_data = i2c;
+       wm8904->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return wm8904_register(wm8904, SND_SOC_I2C);
+}
+
+static __devexit int wm8904_i2c_remove(struct i2c_client *client)
+{
+       struct wm8904_priv *wm8904 = i2c_get_clientdata(client);
+       wm8904_unregister(wm8904);
+       return 0;
+}
+
+static const struct i2c_device_id wm8904_i2c_id[] = {
+       { "wm8904", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
+
+static struct i2c_driver wm8904_i2c_driver = {
+       .driver = {
+               .name = "WM8904",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8904_i2c_probe,
+       .remove =   __devexit_p(wm8904_i2c_remove),
+       .id_table = wm8904_i2c_id,
+};
+#endif
+
+static int __init wm8904_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8904_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8904 I2C driver: %d\n",
+                      ret);
+       }
+#endif
+       return 0;
+}
+module_init(wm8904_modinit);
+
+static void __exit wm8904_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8904_i2c_driver);
+#endif
+}
+module_exit(wm8904_exit);
+
+MODULE_DESCRIPTION("ASoC WM8904 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
new file mode 100644 (file)
index 0000000..b68886d
--- /dev/null
@@ -0,0 +1,1681 @@
+/*
+ * wm8904.h  --  WM8904 ASoC 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.
+ */
+
+#ifndef _WM8904_H
+#define _WM8904_H
+
+#define WM8904_CLK_MCLK 1
+#define WM8904_CLK_FLL  2
+
+#define WM8904_FLL_MCLK          1
+#define WM8904_FLL_BCLK          2
+#define WM8904_FLL_LRCLK         3
+#define WM8904_FLL_FREE_RUNNING  4
+
+extern struct snd_soc_dai wm8904_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8904;
+
+/*
+ * Register values.
+ */
+#define WM8904_SW_RESET_AND_ID                  0x00
+#define WM8904_REVISION                                0x01
+#define WM8904_BIAS_CONTROL_0                   0x04
+#define WM8904_VMID_CONTROL_0                   0x05
+#define WM8904_MIC_BIAS_CONTROL_0               0x06
+#define WM8904_MIC_BIAS_CONTROL_1               0x07
+#define WM8904_ANALOGUE_DAC_0                   0x08
+#define WM8904_MIC_FILTER_CONTROL               0x09
+#define WM8904_ANALOGUE_ADC_0                   0x0A
+#define WM8904_POWER_MANAGEMENT_0               0x0C
+#define WM8904_POWER_MANAGEMENT_2               0x0E
+#define WM8904_POWER_MANAGEMENT_3               0x0F
+#define WM8904_POWER_MANAGEMENT_6               0x12
+#define WM8904_CLOCK_RATES_0                    0x14
+#define WM8904_CLOCK_RATES_1                    0x15
+#define WM8904_CLOCK_RATES_2                    0x16
+#define WM8904_AUDIO_INTERFACE_0                0x18
+#define WM8904_AUDIO_INTERFACE_1                0x19
+#define WM8904_AUDIO_INTERFACE_2                0x1A
+#define WM8904_AUDIO_INTERFACE_3                0x1B
+#define WM8904_DAC_DIGITAL_VOLUME_LEFT          0x1E
+#define WM8904_DAC_DIGITAL_VOLUME_RIGHT         0x1F
+#define WM8904_DAC_DIGITAL_0                    0x20
+#define WM8904_DAC_DIGITAL_1                    0x21
+#define WM8904_ADC_DIGITAL_VOLUME_LEFT          0x24
+#define WM8904_ADC_DIGITAL_VOLUME_RIGHT         0x25
+#define WM8904_ADC_DIGITAL_0                    0x26
+#define WM8904_DIGITAL_MICROPHONE_0             0x27
+#define WM8904_DRC_0                            0x28
+#define WM8904_DRC_1                            0x29
+#define WM8904_DRC_2                            0x2A
+#define WM8904_DRC_3                            0x2B
+#define WM8904_ANALOGUE_LEFT_INPUT_0            0x2C
+#define WM8904_ANALOGUE_RIGHT_INPUT_0           0x2D
+#define WM8904_ANALOGUE_LEFT_INPUT_1            0x2E
+#define WM8904_ANALOGUE_RIGHT_INPUT_1           0x2F
+#define WM8904_ANALOGUE_OUT1_LEFT               0x39
+#define WM8904_ANALOGUE_OUT1_RIGHT              0x3A
+#define WM8904_ANALOGUE_OUT2_LEFT               0x3B
+#define WM8904_ANALOGUE_OUT2_RIGHT              0x3C
+#define WM8904_ANALOGUE_OUT12_ZC                0x3D
+#define WM8904_DC_SERVO_0                       0x43
+#define WM8904_DC_SERVO_1                       0x44
+#define WM8904_DC_SERVO_2                       0x45
+#define WM8904_DC_SERVO_4                       0x47
+#define WM8904_DC_SERVO_5                       0x48
+#define WM8904_DC_SERVO_6                       0x49
+#define WM8904_DC_SERVO_7                       0x4A
+#define WM8904_DC_SERVO_8                       0x4B
+#define WM8904_DC_SERVO_9                       0x4C
+#define WM8904_DC_SERVO_READBACK_0              0x4D
+#define WM8904_ANALOGUE_HP_0                    0x5A
+#define WM8904_ANALOGUE_LINEOUT_0               0x5E
+#define WM8904_CHARGE_PUMP_0                    0x62
+#define WM8904_CLASS_W_0                        0x68
+#define WM8904_WRITE_SEQUENCER_0                0x6C
+#define WM8904_WRITE_SEQUENCER_1                0x6D
+#define WM8904_WRITE_SEQUENCER_2                0x6E
+#define WM8904_WRITE_SEQUENCER_3                0x6F
+#define WM8904_WRITE_SEQUENCER_4                0x70
+#define WM8904_FLL_CONTROL_1                    0x74
+#define WM8904_FLL_CONTROL_2                    0x75
+#define WM8904_FLL_CONTROL_3                    0x76
+#define WM8904_FLL_CONTROL_4                    0x77
+#define WM8904_FLL_CONTROL_5                    0x78
+#define WM8904_GPIO_CONTROL_1                   0x79
+#define WM8904_GPIO_CONTROL_2                   0x7A
+#define WM8904_GPIO_CONTROL_3                   0x7B
+#define WM8904_GPIO_CONTROL_4                   0x7C
+#define WM8904_DIGITAL_PULLS                    0x7E
+#define WM8904_INTERRUPT_STATUS                 0x7F
+#define WM8904_INTERRUPT_STATUS_MASK            0x80
+#define WM8904_INTERRUPT_POLARITY               0x81
+#define WM8904_INTERRUPT_DEBOUNCE               0x82
+#define WM8904_EQ1                              0x86
+#define WM8904_EQ2                              0x87
+#define WM8904_EQ3                              0x88
+#define WM8904_EQ4                              0x89
+#define WM8904_EQ5                              0x8A
+#define WM8904_EQ6                              0x8B
+#define WM8904_EQ7                              0x8C
+#define WM8904_EQ8                              0x8D
+#define WM8904_EQ9                              0x8E
+#define WM8904_EQ10                             0x8F
+#define WM8904_EQ11                             0x90
+#define WM8904_EQ12                             0x91
+#define WM8904_EQ13                             0x92
+#define WM8904_EQ14                             0x93
+#define WM8904_EQ15                             0x94
+#define WM8904_EQ16                             0x95
+#define WM8904_EQ17                             0x96
+#define WM8904_EQ18                             0x97
+#define WM8904_EQ19                             0x98
+#define WM8904_EQ20                             0x99
+#define WM8904_EQ21                             0x9A
+#define WM8904_EQ22                             0x9B
+#define WM8904_EQ23                             0x9C
+#define WM8904_EQ24                             0x9D
+#define WM8904_CONTROL_INTERFACE_TEST_1         0xA1
+#define WM8904_ANALOGUE_OUTPUT_BIAS_0           0xCC
+#define WM8904_FLL_NCO_TEST_0                   0xF7
+#define WM8904_FLL_NCO_TEST_1                   0xF8
+
+#define WM8904_REGISTER_COUNT                   101
+#define WM8904_MAX_REGISTER                     0xF8
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8904_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision
+ */
+#define WM8904_REVISION_MASK                   0x000F  /* REVISION - [3:0] */
+#define WM8904_REVISION_SHIFT                       0  /* REVISION - [3:0] */
+#define WM8904_REVISION_WIDTH                      16  /* REVISION - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8904_POBCTRL                          0x0010  /* POBCTRL */
+#define WM8904_POBCTRL_MASK                     0x0010  /* POBCTRL */
+#define WM8904_POBCTRL_SHIFT                         4  /* POBCTRL */
+#define WM8904_POBCTRL_WIDTH                         1  /* POBCTRL */
+#define WM8904_ISEL_MASK                        0x000C  /* ISEL - [3:2] */
+#define WM8904_ISEL_SHIFT                            2  /* ISEL - [3:2] */
+#define WM8904_ISEL_WIDTH                            2  /* ISEL - [3:2] */
+#define WM8904_STARTUP_BIAS_ENA                 0x0002  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_MASK            0x0002  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_SHIFT                1  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+#define WM8904_BIAS_ENA                         0x0001  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8904_VMID_BUF_ENA                     0x0040  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_MASK                0x0040  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_SHIFT                    6  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM8904_VMID_RES_MASK                    0x0006  /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_SHIFT                        1  /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_WIDTH                        2  /* VMID_RES - [2:1] */
+#define WM8904_VMID_ENA                         0x0001  /* VMID_ENA */
+#define WM8904_VMID_ENA_MASK                    0x0001  /* VMID_ENA */
+#define WM8904_VMID_ENA_SHIFT                        0  /* VMID_ENA */
+#define WM8904_VMID_ENA_WIDTH                        1  /* VMID_ENA */
+
+/*
+ * R6 (0x06) - Mic Bias Control 0
+ */
+#define WM8904_MICDET_THR_MASK                  0x0070  /* MICDET_THR - [6:4] */
+#define WM8904_MICDET_THR_SHIFT                      4  /* MICDET_THR - [6:4] */
+#define WM8904_MICDET_THR_WIDTH                      3  /* MICDET_THR - [6:4] */
+#define WM8904_MICSHORT_THR_MASK                0x000C  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICSHORT_THR_SHIFT                    2  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICSHORT_THR_WIDTH                    2  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICDET_ENA                       0x0002  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_MASK                  0x0002  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_SHIFT                      1  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_WIDTH                      1  /* MICDET_ENA */
+#define WM8904_MICBIAS_ENA                      0x0001  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_MASK                 0x0001  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
+
+/*
+ * R7 (0x07) - Mic Bias Control 1
+ */
+#define WM8904_MIC_DET_FILTER_ENA               0x8000  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_MASK          0x8000  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_SHIFT             15  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_WIDTH              1  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA             0x4000  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_MASK        0x4000  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_SHIFT           14  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_WIDTH            1  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MICBIAS_SEL_MASK                 0x0007  /* MICBIAS_SEL - [2:0] */
+#define WM8904_MICBIAS_SEL_SHIFT                     0  /* MICBIAS_SEL - [2:0] */
+#define WM8904_MICBIAS_SEL_WIDTH                     3  /* MICBIAS_SEL - [2:0] */
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8904_DAC_BIAS_SEL_MASK                0x0018  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_SHIFT                    3  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_WIDTH                    2  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_VMID_BIAS_SEL_MASK           0x0006  /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_SHIFT               1  /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_WIDTH               2  /* DAC_VMID_BIAS_SEL - [2:1] */
+
+/*
+ * R9 (0x09) - mic Filter Control
+ */
+#define WM8904_MIC_DET_SET_THRESHOLD_MASK       0xF000  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_SHIFT          12  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_WIDTH           4  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_MASK     0x0F00  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_SHIFT         8  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_WIDTH         4  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_MASK     0x00F0  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_SHIFT         4  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_WIDTH         4  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_MASK   0x000F  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_SHIFT       0  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_WIDTH       4  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8904_ADC_OSR128                       0x0001  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_MASK                  0x0001  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_SHIFT                      0  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8904_INL_ENA                          0x0002  /* INL_ENA */
+#define WM8904_INL_ENA_MASK                     0x0002  /* INL_ENA */
+#define WM8904_INL_ENA_SHIFT                         1  /* INL_ENA */
+#define WM8904_INL_ENA_WIDTH                         1  /* INL_ENA */
+#define WM8904_INR_ENA                          0x0001  /* INR_ENA */
+#define WM8904_INR_ENA_MASK                     0x0001  /* INR_ENA */
+#define WM8904_INR_ENA_SHIFT                         0  /* INR_ENA */
+#define WM8904_INR_ENA_WIDTH                         1  /* INR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8904_HPL_PGA_ENA                      0x0002  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_MASK                 0x0002  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_SHIFT                     1  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_WIDTH                     1  /* HPL_PGA_ENA */
+#define WM8904_HPR_PGA_ENA                      0x0001  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_MASK                 0x0001  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_SHIFT                     0  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_WIDTH                     1  /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8904_LINEOUTL_PGA_ENA                 0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_MASK            0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_SHIFT                1  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_WIDTH                1  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA                 0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_MASK            0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_SHIFT                0  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_WIDTH                1  /* LINEOUTR_PGA_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8904_DACL_ENA                         0x0008  /* DACL_ENA */
+#define WM8904_DACL_ENA_MASK                    0x0008  /* DACL_ENA */
+#define WM8904_DACL_ENA_SHIFT                        3  /* DACL_ENA */
+#define WM8904_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8904_DACR_ENA                         0x0004  /* DACR_ENA */
+#define WM8904_DACR_ENA_MASK                    0x0004  /* DACR_ENA */
+#define WM8904_DACR_ENA_SHIFT                        2  /* DACR_ENA */
+#define WM8904_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+#define WM8904_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8904_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8904_TOCLK_RATE_DIV16                 0x4000  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_MASK            0x4000  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_SHIFT               14  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_WIDTH                1  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_X4                    0x2000  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_MASK               0x2000  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_SHIFT                  13  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_WIDTH                   1  /* TOCLK_RATE_X4 */
+#define WM8904_SR_MODE                          0x1000  /* SR_MODE */
+#define WM8904_SR_MODE_MASK                     0x1000  /* SR_MODE */
+#define WM8904_SR_MODE_SHIFT                        12  /* SR_MODE */
+#define WM8904_SR_MODE_WIDTH                         1  /* SR_MODE */
+#define WM8904_MCLK_DIV                         0x0001  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_MASK                    0x0001  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_SHIFT                        0  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_WIDTH                        1  /* MCLK_DIV */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8904_CLK_SYS_RATE_MASK                0x3C00  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_SHIFT                   10  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_SAMPLE_RATE_MASK                 0x0007  /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8904_MCLK_INV                         0x8000  /* MCLK_INV */
+#define WM8904_MCLK_INV_MASK                    0x8000  /* MCLK_INV */
+#define WM8904_MCLK_INV_SHIFT                       15  /* MCLK_INV */
+#define WM8904_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8904_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8904_TOCLK_RATE                       0x1000  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_MASK                  0x1000  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_SHIFT                     12  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8904_OPCLK_ENA                        0x0008  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_MASK                   0x0008  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_SHIFT                       3  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8904_CLK_SYS_ENA                      0x0004  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_MASK                 0x0004  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_SHIFT                     2  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+#define WM8904_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM8904_TOCLK_ENA                        0x0001  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_MASK                   0x0001  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_SHIFT                       0  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8904_DACL_DATINV                      0x1000  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_MASK                 0x1000  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_SHIFT                    12  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8904_DACR_DATINV                      0x0800  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_MASK                 0x0800  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_SHIFT                    11  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+#define WM8904_DAC_BOOST_MASK                   0x0600  /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_SHIFT                       9  /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [10:9] */
+#define WM8904_LOOPBACK                         0x0100  /* LOOPBACK */
+#define WM8904_LOOPBACK_MASK                    0x0100  /* LOOPBACK */
+#define WM8904_LOOPBACK_SHIFT                        8  /* LOOPBACK */
+#define WM8904_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+#define WM8904_AIFADCL_SRC                      0x0080  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_MASK                 0x0080  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_SHIFT                     7  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8904_AIFADCR_SRC                      0x0040  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_MASK                 0x0040  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_SHIFT                     6  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8904_AIFDACL_SRC                      0x0020  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_MASK                 0x0020  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_SHIFT                     5  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */
+#define WM8904_AIFDACR_SRC                      0x0010  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_MASK                 0x0010  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_SHIFT                     4  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */
+#define WM8904_ADC_COMP                         0x0008  /* ADC_COMP */
+#define WM8904_ADC_COMP_MASK                    0x0008  /* ADC_COMP */
+#define WM8904_ADC_COMP_SHIFT                        3  /* ADC_COMP */
+#define WM8904_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8904_ADC_COMPMODE                     0x0004  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_MASK                0x0004  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_SHIFT                    2  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8904_DAC_COMP                         0x0002  /* DAC_COMP */
+#define WM8904_DAC_COMP_MASK                    0x0002  /* DAC_COMP */
+#define WM8904_DAC_COMP_SHIFT                        1  /* DAC_COMP */
+#define WM8904_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8904_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8904_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFADC_TDM                       0x0800  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_MASK                  0x0800  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_SHIFT                     11  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_CHAN                  0x0400  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_MASK             0x0400  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_SHIFT                10  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8904_AIF_TRIS                         0x0100  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_MASK                    0x0100  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_SHIFT                        8  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8904_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8904_BCLK_DIR                         0x0040  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM8904_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8904_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */
+#define WM8904_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8904_OPCLK_DIV_MASK                   0x0F00  /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_SHIFT                       8  /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_WIDTH                       4  /* OPCLK_DIV - [11:8] */
+#define WM8904_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8904_LRCLK_DIR                        0x0800  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_MASK                   0x0800  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_SHIFT                      11  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM8904_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8904_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8904_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8904_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8904_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8904_ADCL_DAC_SVOL_MASK               0x0F00  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_SHIFT                   8  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8904_DAC_MONO                         0x1000  /* DAC_MONO */
+#define WM8904_DAC_MONO_MASK                    0x1000  /* DAC_MONO */
+#define WM8904_DAC_MONO_SHIFT                       12  /* DAC_MONO */
+#define WM8904_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8904_DAC_SB_FILT                      0x0800  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_MASK                 0x0800  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_SHIFT                    11  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8904_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8904_DAC_UNMUTE_RAMP                  0x0200  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_MASK             0x0200  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_SHIFT                 9  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_WIDTH                 1  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_OSR128                       0x0040  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_MASK                  0x0040  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_SHIFT                      6  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+#define WM8904_DAC_MUTE                         0x0008  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8904_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8904_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8904_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8904_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8904_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8904_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF                          0x0010  /* ADC_HPF */
+#define WM8904_ADC_HPF_MASK                     0x0010  /* ADC_HPF */
+#define WM8904_ADC_HPF_SHIFT                         4  /* ADC_HPF */
+#define WM8904_ADC_HPF_WIDTH                         1  /* ADC_HPF */
+#define WM8904_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8904_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8904_DMIC_ENA                         0x1000  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_MASK                    0x1000  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_SHIFT                       12  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_WIDTH                        1  /* DMIC_ENA */
+#define WM8904_DMIC_SRC                         0x0800  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_MASK                    0x0800  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_SHIFT                       11  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_WIDTH                        1  /* DMIC_SRC */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8904_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM8904_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM8904_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM8904_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM8904_DRC_DAC_PATH                     0x4000  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_MASK                0x4000  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_SHIFT                   14  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_WIDTH                    1  /* DRC_DAC_PATH */
+#define WM8904_DRC_GS_HYST_LVL_MASK             0x1800  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_SHIFT                11  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_WIDTH                 2  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_FF_DELAY                     0x0020  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_MASK                0x0020  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_SHIFT                    5  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */
+#define WM8904_DRC_GS_ENA                       0x0008  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_MASK                  0x0008  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_SHIFT                      3  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_WIDTH                      1  /* DRC_GS_ENA */
+#define WM8904_DRC_QR                           0x0004  /* DRC_QR */
+#define WM8904_DRC_QR_MASK                      0x0004  /* DRC_QR */
+#define WM8904_DRC_QR_SHIFT                          2  /* DRC_QR */
+#define WM8904_DRC_QR_WIDTH                          1  /* DRC_QR */
+#define WM8904_DRC_ANTICLIP                     0x0002  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_MASK                0x0002  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_SHIFT                    1  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_WIDTH                    1  /* DRC_ANTICLIP */
+#define WM8904_DRC_GS_HYST                      0x0001  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_MASK                 0x0001  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_SHIFT                     0  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_WIDTH                     1  /* DRC_GS_HYST */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8904_DRC_ATK_MASK                     0xF000  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_SHIFT                        12  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_WIDTH                         4  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_DCY_MASK                     0x0F00  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_SHIFT                         8  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_WIDTH                         4  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_QR_THR_MASK                  0x00C0  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_SHIFT                      6  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_WIDTH                      2  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_DCY_MASK                  0x0030  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_SHIFT                      4  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_WIDTH                      2  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8904_DRC_HI_COMP_MASK                 0x0038  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_SHIFT                     3  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_WIDTH                     3  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_LO_COMP_MASK                 0x0007  /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_SHIFT                     0  /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_WIDTH                     3  /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8904_DRC_KNEE_IP_MASK                 0x07E0  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_SHIFT                     5  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_WIDTH                     6  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_OP_MASK                 0x001F  /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_SHIFT                     0  /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_WIDTH                     5  /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8904_LINMUTE                          0x0080  /* LINMUTE */
+#define WM8904_LINMUTE_MASK                     0x0080  /* LINMUTE */
+#define WM8904_LINMUTE_SHIFT                         7  /* LINMUTE */
+#define WM8904_LINMUTE_WIDTH                         1  /* LINMUTE */
+#define WM8904_LIN_VOL_MASK                     0x001F  /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_SHIFT                         0  /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_WIDTH                         5  /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8904_RINMUTE                          0x0080  /* RINMUTE */
+#define WM8904_RINMUTE_MASK                     0x0080  /* RINMUTE */
+#define WM8904_RINMUTE_SHIFT                         7  /* RINMUTE */
+#define WM8904_RINMUTE_WIDTH                         1  /* RINMUTE */
+#define WM8904_RIN_VOL_MASK                     0x001F  /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_SHIFT                         0  /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_WIDTH                         5  /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8904_INL_CM_ENA                       0x0040  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_MASK                  0x0040  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_SHIFT                      6  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_WIDTH                      1  /* INL_CM_ENA */
+#define WM8904_L_IP_SEL_N_MASK                  0x0030  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_SHIFT                      4  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_WIDTH                      2  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_P_MASK                  0x000C  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_SHIFT                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_WIDTH                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_MODE_MASK                      0x0003  /* L_MODE - [1:0] */
+#define WM8904_L_MODE_SHIFT                          0  /* L_MODE - [1:0] */
+#define WM8904_L_MODE_WIDTH                          2  /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8904_INR_CM_ENA                       0x0040  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_MASK                  0x0040  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_SHIFT                      6  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_WIDTH                      1  /* INR_CM_ENA */
+#define WM8904_R_IP_SEL_N_MASK                  0x0030  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_SHIFT                      4  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_WIDTH                      2  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_P_MASK                  0x000C  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_SHIFT                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_WIDTH                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_MODE_MASK                      0x0003  /* R_MODE - [1:0] */
+#define WM8904_R_MODE_SHIFT                          0  /* R_MODE - [1:0] */
+#define WM8904_R_MODE_WIDTH                          2  /* R_MODE - [1:0] */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8904_HPOUTL_MUTE                      0x0100  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_MASK                 0x0100  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_SHIFT                     8  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_WIDTH                     1  /* HPOUTL_MUTE */
+#define WM8904_HPOUT_VU                         0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK                    0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT                        7  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH                        1  /* HPOUT_VU */
+#define WM8904_HPOUTLZC                         0x0040  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_MASK                    0x0040  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_SHIFT                        6  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_WIDTH                        1  /* HPOUTLZC */
+#define WM8904_HPOUTL_VOL_MASK                  0x003F  /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_SHIFT                      0  /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_WIDTH                      6  /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8904_HPOUTR_MUTE                      0x0100  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_MASK                 0x0100  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_SHIFT                     8  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_WIDTH                     1  /* HPOUTR_MUTE */
+#define WM8904_HPOUT_VU                         0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK                    0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT                        7  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH                        1  /* HPOUT_VU */
+#define WM8904_HPOUTRZC                         0x0040  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_MASK                    0x0040  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_SHIFT                        6  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_WIDTH                        1  /* HPOUTRZC */
+#define WM8904_HPOUTR_VOL_MASK                  0x003F  /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_SHIFT                      0  /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_WIDTH                      6  /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8904_LINEOUTL_MUTE                    0x0100  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_MASK               0x0100  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_SHIFT                   8  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_WIDTH                   1  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUT_VU                       0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK                  0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT                      7  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH                      1  /* LINEOUT_VU */
+#define WM8904_LINEOUTLZC                       0x0040  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_MASK                  0x0040  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_SHIFT                      6  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_WIDTH                      1  /* LINEOUTLZC */
+#define WM8904_LINEOUTL_VOL_MASK                0x003F  /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_SHIFT                    0  /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_WIDTH                    6  /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8904_LINEOUTR_MUTE                    0x0100  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_MASK               0x0100  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_SHIFT                   8  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_WIDTH                   1  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUT_VU                       0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK                  0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT                      7  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH                      1  /* LINEOUT_VU */
+#define WM8904_LINEOUTRZC                       0x0040  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_MASK                  0x0040  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_SHIFT                      6  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_WIDTH                      1  /* LINEOUTRZC */
+#define WM8904_LINEOUTR_VOL_MASK                0x003F  /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_SHIFT                    0  /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_WIDTH                    6  /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R61 (0x3D) - Analogue OUT12 ZC
+ */
+#define WM8904_HPL_BYP_ENA                      0x0008  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_MASK                 0x0008  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_SHIFT                     3  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_WIDTH                     1  /* HPL_BYP_ENA */
+#define WM8904_HPR_BYP_ENA                      0x0004  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_MASK                 0x0004  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_SHIFT                     2  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_WIDTH                     1  /* HPR_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA                 0x0002  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_MASK            0x0002  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_SHIFT                1  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_WIDTH                1  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA                 0x0001  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_MASK            0x0001  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_SHIFT                0  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_WIDTH                1  /* LINEOUTR_BYP_ENA */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8904_DCS_ENA_CHAN_3                   0x0008  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_MASK              0x0008  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_SHIFT                  3  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_WIDTH                  1  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_2                   0x0004  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_MASK              0x0004  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_SHIFT                  2  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_WIDTH                  1  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_1                   0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_MASK              0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_SHIFT                  1  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_WIDTH                  1  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_0                   0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_MASK              0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_SHIFT                  0  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_WIDTH                  1  /* DCS_ENA_CHAN_0 */
+
+/*
+ * R68 (0x44) - DC Servo 1
+ */
+#define WM8904_DCS_TRIG_SINGLE_3                0x8000  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_MASK           0x8000  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_SHIFT              15  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_WIDTH               1  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_2                0x4000  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_MASK           0x4000  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_SHIFT              14  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_WIDTH               1  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_1                0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_MASK           0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_SHIFT              13  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_WIDTH               1  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_0                0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_MASK           0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_SHIFT              12  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_WIDTH               1  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SERIES_3                0x0800  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_MASK           0x0800  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_SHIFT              11  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_WIDTH               1  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_2                0x0400  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_MASK           0x0400  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_SHIFT              10  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_WIDTH               1  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_1                0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_MASK           0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_SHIFT               9  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_WIDTH               1  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_0                0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_MASK           0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_SHIFT               8  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_WIDTH               1  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_STARTUP_3               0x0080  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_MASK          0x0080  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_SHIFT              7  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_WIDTH              1  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_2               0x0040  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_MASK          0x0040  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_SHIFT              6  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_WIDTH              1  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_1               0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_MASK          0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_SHIFT              5  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_WIDTH              1  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_0               0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_MASK          0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_SHIFT              4  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_WIDTH              1  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_DAC_WR_3                0x0008  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_MASK           0x0008  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_SHIFT               3  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_WIDTH               1  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_2                0x0004  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_MASK           0x0004  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_SHIFT               2  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_WIDTH               1  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_1                0x0002  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_MASK           0x0002  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_SHIFT               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_WIDTH               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_0                0x0001  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_MASK           0x0001  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_SHIFT               0  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_WIDTH               1  /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8904_DCS_TIMER_PERIOD_23_MASK         0x0F00  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_SHIFT             8  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_WIDTH             4  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_01_MASK         0x000F  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_SHIFT             0  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_WIDTH             4  /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R71 (0x47) - DC Servo 4
+ */
+#define WM8904_DCS_SERIES_NO_23_MASK            0x007F  /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_SHIFT                0  /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_WIDTH                7  /* DCS_SERIES_NO_23 - [6:0] */
+
+/*
+ * R72 (0x48) - DC Servo 5
+ */
+#define WM8904_DCS_SERIES_NO_01_MASK            0x007F  /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_SHIFT                0  /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_WIDTH                7  /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R73 (0x49) - DC Servo 6
+ */
+#define WM8904_DCS_DAC_WR_VAL_3_MASK            0x00FF  /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_SHIFT                0  /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_WIDTH                8  /* DCS_DAC_WR_VAL_3 - [7:0] */
+
+/*
+ * R74 (0x4A) - DC Servo 7
+ */
+#define WM8904_DCS_DAC_WR_VAL_2_MASK            0x00FF  /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_SHIFT                0  /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_WIDTH                8  /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R75 (0x4B) - DC Servo 8
+ */
+#define WM8904_DCS_DAC_WR_VAL_1_MASK            0x00FF  /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_SHIFT                0  /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_WIDTH                8  /* DCS_DAC_WR_VAL_1 - [7:0] */
+
+/*
+ * R76 (0x4C) - DC Servo 9
+ */
+#define WM8904_DCS_DAC_WR_VAL_0_MASK            0x00FF  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_SHIFT                0  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_WIDTH                8  /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R77 (0x4D) - DC Servo Readback 0
+ */
+#define WM8904_DCS_CAL_COMPLETE_MASK            0x0F00  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_SHIFT                8  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_WIDTH                4  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_DAC_WR_COMPLETE_MASK         0x00F0  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_SHIFT             4  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_WIDTH             4  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_STARTUP_COMPLETE_MASK        0x000F  /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_SHIFT            0  /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_WIDTH            4  /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8904_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */
+#define WM8904_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA                          0x0010  /* HPL_ENA */
+#define WM8904_HPL_ENA_MASK                     0x0010  /* HPL_ENA */
+#define WM8904_HPL_ENA_SHIFT                         4  /* HPL_ENA */
+#define WM8904_HPL_ENA_WIDTH                         1  /* HPL_ENA */
+#define WM8904_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */
+#define WM8904_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA                          0x0001  /* HPR_ENA */
+#define WM8904_HPR_ENA_MASK                     0x0001  /* HPR_ENA */
+#define WM8904_HPR_ENA_SHIFT                         0  /* HPR_ENA */
+#define WM8904_HPR_ENA_WIDTH                         1  /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8904_LINEOUTL_RMV_SHORT               0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_MASK          0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_SHIFT              7  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_WIDTH              1  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_ENA_OUTP                0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_MASK           0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_SHIFT               6  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_WIDTH               1  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_DLY                 0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_MASK            0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_SHIFT                5  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_WIDTH                1  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA                     0x0010  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_MASK                0x0010  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_SHIFT                    4  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_WIDTH                    1  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTR_RMV_SHORT               0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_MASK          0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_SHIFT              3  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_WIDTH              1  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_ENA_OUTP                0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_MASK           0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_SHIFT               2  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_WIDTH               1  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_DLY                 0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_MASK            0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_SHIFT                1  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_WIDTH                1  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA                     0x0001  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_MASK                0x0001  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_SHIFT                    0  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_WIDTH                    1  /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8904_CP_ENA                           0x0001  /* CP_ENA */
+#define WM8904_CP_ENA_MASK                      0x0001  /* CP_ENA */
+#define WM8904_CP_ENA_SHIFT                          0  /* CP_ENA */
+#define WM8904_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8904_CP_DYN_PWR                       0x0001  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_MASK                  0x0001  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_SHIFT                      0  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_WIDTH                      1  /* CP_DYN_PWR */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8904_WSEQ_ENA                         0x0100  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8904_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8904_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8904_WSEQ_EOS                         0x4000  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8904_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8904_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8904_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM8904_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM8904_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM8904_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8904_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8904_WSEQ_CURRENT_INDEX_MASK          0x03F0  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R116 (0x74) - FLL Control 1
+ */
+#define WM8904_FLL_FRACN_ENA                    0x0004  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_MASK               0x0004  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_SHIFT                   2  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_WIDTH                   1  /* FLL_FRACN_ENA */
+#define WM8904_FLL_OSC_ENA                      0x0002  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_MASK                 0x0002  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_SHIFT                     1  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8904_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM8904_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM8904_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM8904_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R117 (0x75) - FLL Control 2
+ */
+#define WM8904_FLL_OUTDIV_MASK                  0x3F00  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_WIDTH                      6  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R118 (0x76) - FLL Control 3
+ */
+#define WM8904_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8904_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8904_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R119 (0x77) - FLL Control 4
+ */
+#define WM8904_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM8904_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM8904_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM8904_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R120 (0x78) - FLL Control 5
+ */
+#define WM8904_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_SRC_MASK             0x0003  /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_SHIFT                 0  /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_WIDTH                 2  /* FLL_CLK_REF_SRC - [1:0] */
+
+/*
+ * R121 (0x79) - GPIO Control 1
+ */
+#define WM8904_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8904_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8904_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8904_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8904_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R122 (0x7A) - GPIO Control 2
+ */
+#define WM8904_GPIO2_PU                         0x0020  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_MASK                    0x0020  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_SHIFT                        5  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_WIDTH                        1  /* GPIO2_PU */
+#define WM8904_GPIO2_PD                         0x0010  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_MASK                    0x0010  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_SHIFT                        4  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_WIDTH                        1  /* GPIO2_PD */
+#define WM8904_GPIO2_SEL_MASK                   0x000F  /* GPIO2_SEL - [3:0] */
+#define WM8904_GPIO2_SEL_SHIFT                       0  /* GPIO2_SEL - [3:0] */
+#define WM8904_GPIO2_SEL_WIDTH                       4  /* GPIO2_SEL - [3:0] */
+
+/*
+ * R123 (0x7B) - GPIO Control 3
+ */
+#define WM8904_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_MASK                    0x0020  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_SHIFT                        5  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_WIDTH                        1  /* GPIO3_PU */
+#define WM8904_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_MASK                    0x0010  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_SHIFT                        4  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_WIDTH                        1  /* GPIO3_PD */
+#define WM8904_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+#define WM8904_GPIO3_SEL_SHIFT                       0  /* GPIO3_SEL - [3:0] */
+#define WM8904_GPIO3_SEL_WIDTH                       4  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R124 (0x7C) - GPIO Control 4
+ */
+#define WM8904_GPI7_ENA                         0x0200  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_MASK                    0x0200  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_SHIFT                        9  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+#define WM8904_GPI8_ENA                         0x0100  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_MASK                    0x0100  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_SHIFT                        8  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA               0x0080  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_MASK          0x0080  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_SHIFT              7  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_WIDTH              1  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_SEL_MASK               0x000F  /* GPIO_BCLK_SEL - [3:0] */
+#define WM8904_GPIO_BCLK_SEL_SHIFT                   0  /* GPIO_BCLK_SEL - [3:0] */
+#define WM8904_GPIO_BCLK_SEL_WIDTH                   4  /* GPIO_BCLK_SEL - [3:0] */
+
+/*
+ * R126 (0x7E) - Digital Pulls
+ */
+#define WM8904_MCLK_PU                          0x0080  /* MCLK_PU */
+#define WM8904_MCLK_PU_MASK                     0x0080  /* MCLK_PU */
+#define WM8904_MCLK_PU_SHIFT                         7  /* MCLK_PU */
+#define WM8904_MCLK_PU_WIDTH                         1  /* MCLK_PU */
+#define WM8904_MCLK_PD                          0x0040  /* MCLK_PD */
+#define WM8904_MCLK_PD_MASK                     0x0040  /* MCLK_PD */
+#define WM8904_MCLK_PD_SHIFT                         6  /* MCLK_PD */
+#define WM8904_MCLK_PD_WIDTH                         1  /* MCLK_PD */
+#define WM8904_DACDAT_PU                        0x0020  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_MASK                   0x0020  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_SHIFT                       5  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_WIDTH                       1  /* DACDAT_PU */
+#define WM8904_DACDAT_PD                        0x0010  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_MASK                   0x0010  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_SHIFT                       4  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_WIDTH                       1  /* DACDAT_PD */
+#define WM8904_LRCLK_PU                         0x0008  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_MASK                    0x0008  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_SHIFT                        3  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_WIDTH                        1  /* LRCLK_PU */
+#define WM8904_LRCLK_PD                         0x0004  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_MASK                    0x0004  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_SHIFT                        2  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_WIDTH                        1  /* LRCLK_PD */
+#define WM8904_BCLK_PU                          0x0002  /* BCLK_PU */
+#define WM8904_BCLK_PU_MASK                     0x0002  /* BCLK_PU */
+#define WM8904_BCLK_PU_SHIFT                         1  /* BCLK_PU */
+#define WM8904_BCLK_PU_WIDTH                         1  /* BCLK_PU */
+#define WM8904_BCLK_PD                          0x0001  /* BCLK_PD */
+#define WM8904_BCLK_PD_MASK                     0x0001  /* BCLK_PD */
+#define WM8904_BCLK_PD_SHIFT                         0  /* BCLK_PD */
+#define WM8904_BCLK_PD_WIDTH                         1  /* BCLK_PD */
+
+/*
+ * R127 (0x7F) - Interrupt Status
+ */
+#define WM8904_IRQ                              0x0400  /* IRQ */
+#define WM8904_IRQ_MASK                         0x0400  /* IRQ */
+#define WM8904_IRQ_SHIFT                            10  /* IRQ */
+#define WM8904_IRQ_WIDTH                             1  /* IRQ */
+#define WM8904_GPIO_BCLK_EINT                   0x0200  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_MASK              0x0200  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_SHIFT                  9  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_WIDTH                  1  /* GPIO_BCLK_EINT */
+#define WM8904_WSEQ_EINT                        0x0100  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_MASK                   0x0100  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_SHIFT                       8  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_WIDTH                       1  /* WSEQ_EINT */
+#define WM8904_GPIO3_EINT                       0x0080  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_MASK                  0x0080  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_SHIFT                      7  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_WIDTH                      1  /* GPIO3_EINT */
+#define WM8904_GPIO2_EINT                       0x0040  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_MASK                  0x0040  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_SHIFT                      6  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_WIDTH                      1  /* GPIO2_EINT */
+#define WM8904_GPIO1_EINT                       0x0020  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_MASK                  0x0020  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_SHIFT                      5  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_WIDTH                      1  /* GPIO1_EINT */
+#define WM8904_GPI8_EINT                        0x0010  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_MASK                   0x0010  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_SHIFT                       4  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_WIDTH                       1  /* GPI8_EINT */
+#define WM8904_GPI7_EINT                        0x0008  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_MASK                   0x0008  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_SHIFT                       3  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_WIDTH                       1  /* GPI7_EINT */
+#define WM8904_FLL_LOCK_EINT                    0x0004  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_MASK               0x0004  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_SHIFT                   2  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_WIDTH                   1  /* FLL_LOCK_EINT */
+#define WM8904_MIC_SHRT_EINT                    0x0002  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_MASK               0x0002  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_SHIFT                   1  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_WIDTH                   1  /* MIC_SHRT_EINT */
+#define WM8904_MIC_DET_EINT                     0x0001  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_MASK                0x0001  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_SHIFT                    0  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_WIDTH                    1  /* MIC_DET_EINT */
+
+/*
+ * R128 (0x80) - Interrupt Status Mask
+ */
+#define WM8904_IM_GPIO_BCLK_EINT                0x0200  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_MASK           0x0200  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_SHIFT               9  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_WIDTH               1  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_WSEQ_EINT                     0x0100  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_MASK                0x0100  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_SHIFT                    8  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_WIDTH                    1  /* IM_WSEQ_EINT */
+#define WM8904_IM_GPIO3_EINT                    0x0080  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_MASK               0x0080  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_SHIFT                   7  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_WIDTH                   1  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO2_EINT                    0x0040  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_MASK               0x0040  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_SHIFT                   6  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_WIDTH                   1  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO1_EINT                    0x0020  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_MASK               0x0020  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_SHIFT                   5  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_WIDTH                   1  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPI8_EINT                     0x0010  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_MASK                0x0010  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_SHIFT                    4  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_WIDTH                    1  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI7_EINT                     0x0008  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_MASK                0x0008  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_SHIFT                    3  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_WIDTH                    1  /* IM_GPI7_EINT */
+#define WM8904_IM_FLL_LOCK_EINT                 0x0004  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_MASK            0x0004  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_SHIFT                2  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_WIDTH                1  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_MIC_SHRT_EINT                 0x0002  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_MASK            0x0002  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_SHIFT                1  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_WIDTH                1  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_DET_EINT                  0x0001  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_MASK             0x0001  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_SHIFT                 0  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_WIDTH                 1  /* IM_MIC_DET_EINT */
+
+/*
+ * R129 (0x81) - Interrupt Polarity
+ */
+#define WM8904_GPIO_BCLK_EINT_POL               0x0200  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_MASK          0x0200  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_SHIFT              9  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_WIDTH              1  /* GPIO_BCLK_EINT_POL */
+#define WM8904_WSEQ_EINT_POL                    0x0100  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_MASK               0x0100  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_SHIFT                   8  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_WIDTH                   1  /* WSEQ_EINT_POL */
+#define WM8904_GPIO3_EINT_POL                   0x0080  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_MASK              0x0080  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_SHIFT                  7  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_WIDTH                  1  /* GPIO3_EINT_POL */
+#define WM8904_GPIO2_EINT_POL                   0x0040  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_MASK              0x0040  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_SHIFT                  6  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_WIDTH                  1  /* GPIO2_EINT_POL */
+#define WM8904_GPIO1_EINT_POL                   0x0020  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_MASK              0x0020  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_SHIFT                  5  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_WIDTH                  1  /* GPIO1_EINT_POL */
+#define WM8904_GPI8_EINT_POL                    0x0010  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_MASK               0x0010  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_SHIFT                   4  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_WIDTH                   1  /* GPI8_EINT_POL */
+#define WM8904_GPI7_EINT_POL                    0x0008  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_MASK               0x0008  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_SHIFT                   3  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_WIDTH                   1  /* GPI7_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL                0x0004  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_MASK           0x0004  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_SHIFT               2  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_WIDTH               1  /* FLL_LOCK_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL                0x0002  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_MASK           0x0002  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_SHIFT               1  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_WIDTH               1  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL                 0x0001  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_MASK            0x0001  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_SHIFT                0  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_WIDTH                1  /* MIC_DET_EINT_POL */
+
+/*
+ * R130 (0x82) - Interrupt Debounce
+ */
+#define WM8904_GPIO_BCLK_EINT_DB                0x0200  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_MASK           0x0200  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_SHIFT               9  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_WIDTH               1  /* GPIO_BCLK_EINT_DB */
+#define WM8904_WSEQ_EINT_DB                     0x0100  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_MASK                0x0100  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_SHIFT                    8  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_WIDTH                    1  /* WSEQ_EINT_DB */
+#define WM8904_GPIO3_EINT_DB                    0x0080  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_MASK               0x0080  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_SHIFT                   7  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_WIDTH                   1  /* GPIO3_EINT_DB */
+#define WM8904_GPIO2_EINT_DB                    0x0040  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_MASK               0x0040  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_SHIFT                   6  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_WIDTH                   1  /* GPIO2_EINT_DB */
+#define WM8904_GPIO1_EINT_DB                    0x0020  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_MASK               0x0020  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_SHIFT                   5  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_WIDTH                   1  /* GPIO1_EINT_DB */
+#define WM8904_GPI8_EINT_DB                     0x0010  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_MASK                0x0010  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_SHIFT                    4  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_WIDTH                    1  /* GPI8_EINT_DB */
+#define WM8904_GPI7_EINT_DB                     0x0008  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_MASK                0x0008  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_SHIFT                    3  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_WIDTH                    1  /* GPI7_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB                 0x0004  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_MASK            0x0004  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_SHIFT                2  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_WIDTH                1  /* FLL_LOCK_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB                 0x0002  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_MASK            0x0002  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_SHIFT                1  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_WIDTH                1  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB                  0x0001  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_MASK             0x0001  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_SHIFT                 0  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_WIDTH                 1  /* MIC_DET_EINT_DB */
+
+/*
+ * R134 (0x86) - EQ1
+ */
+#define WM8904_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM8904_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM8904_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM8904_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R135 (0x87) - EQ2
+ */
+#define WM8904_EQ_B1_GAIN_MASK                  0x001F  /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_SHIFT                      0  /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R136 (0x88) - EQ3
+ */
+#define WM8904_EQ_B2_GAIN_MASK                  0x001F  /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_SHIFT                      0  /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R137 (0x89) - EQ4
+ */
+#define WM8904_EQ_B3_GAIN_MASK                  0x001F  /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_SHIFT                      0  /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R138 (0x8A) - EQ5
+ */
+#define WM8904_EQ_B4_GAIN_MASK                  0x001F  /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_SHIFT                      0  /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R139 (0x8B) - EQ6
+ */
+#define WM8904_EQ_B5_GAIN_MASK                  0x001F  /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_SHIFT                      0  /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R140 (0x8C) - EQ7
+ */
+#define WM8904_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R141 (0x8D) - EQ8
+ */
+#define WM8904_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R142 (0x8E) - EQ9
+ */
+#define WM8904_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R143 (0x8F) - EQ10
+ */
+#define WM8904_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R144 (0x90) - EQ11
+ */
+#define WM8904_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R145 (0x91) - EQ12
+ */
+#define WM8904_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R146 (0x92) - EQ13
+ */
+#define WM8904_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R147 (0x93) - EQ14
+ */
+#define WM8904_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R148 (0x94) - EQ15
+ */
+#define WM8904_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R149 (0x95) - EQ16
+ */
+#define WM8904_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R150 (0x96) - EQ17
+ */
+#define WM8904_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R151 (0x97) - EQ18
+ */
+#define WM8904_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R152 (0x98) - EQ19
+ */
+#define WM8904_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R153 (0x99) - EQ20
+ */
+#define WM8904_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R154 (0x9A) - EQ21
+ */
+#define WM8904_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R155 (0x9B) - EQ22
+ */
+#define WM8904_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R156 (0x9C) - EQ23
+ */
+#define WM8904_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R157 (0x9D) - EQ24
+ */
+#define WM8904_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+/*
+ * R161 (0xA1) - Control Interface Test 1
+ */
+#define WM8904_USER_KEY                         0x0002  /* USER_KEY */
+#define WM8904_USER_KEY_MASK                    0x0002  /* USER_KEY */
+#define WM8904_USER_KEY_SHIFT                        1  /* USER_KEY */
+#define WM8904_USER_KEY_WIDTH                        1  /* USER_KEY */
+
+/*
+ * R204 (0xCC) - Analogue Output Bias 0
+ */
+#define WM8904_PGA_BIAS_MASK                    0x0070  /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_SHIFT                        4  /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_WIDTH                        3  /* PGA_BIAS - [6:4] */
+
+/*
+ * R247 (0xF7) - FLL NCO Test 0
+ */
+#define WM8904_FLL_FRC_NCO                      0x0001  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_MASK                 0x0001  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_SHIFT                     0  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_WIDTH                     1  /* FLL_FRC_NCO */
+
+/*
+ * R248 (0xF8) - FLL NCO Test 1
+ */
+#define WM8904_FLL_FRC_NCO_VAL_MASK             0x003F  /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_SHIFT                 0  /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_WIDTH                 6  /* FLL_FRC_NCO_VAL - [5:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
new file mode 100644 (file)
index 0000000..615dab2
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+ * wm8955.c  --  WM8955 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 <sound/wm8955.h>
+
+#include "wm8955.h"
+
+static struct snd_soc_codec *wm8955_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8955;
+
+#define WM8955_NUM_SUPPLIES 4
+static const char *wm8955_supply_names[WM8955_NUM_SUPPLIES] = {
+       "DCVDD",
+       "DBVDD",
+       "HPVDD",
+       "AVDD",
+};
+
+/* codec private data */
+struct wm8955_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM8955_MAX_REGISTER + 1];
+
+       unsigned int mclk_rate;
+
+       int deemph;
+       int fs;
+
+       struct regulator_bulk_data supplies[WM8955_NUM_SUPPLIES];
+
+       struct wm8955_pdata *pdata;
+};
+
+static const u16 wm8955_reg[WM8955_MAX_REGISTER + 1] = {
+       0x0000,     /* R0 */
+       0x0000,     /* R1 */
+       0x0079,     /* R2  - LOUT1 volume */
+       0x0079,     /* R3  - ROUT1 volume */
+       0x0000,     /* R4 */
+       0x0008,     /* R5  - DAC Control */
+       0x0000,     /* R6 */
+       0x000A,     /* R7  - Audio Interface */
+       0x0000,     /* R8  - Sample Rate */
+       0x0000,     /* R9 */
+       0x00FF,     /* R10 - Left DAC volume */
+       0x00FF,     /* R11 - Right DAC volume */
+       0x000F,     /* R12 - Bass control */
+       0x000F,     /* R13 - Treble control */
+       0x0000,     /* R14 */
+       0x0000,     /* R15 - Reset */
+       0x0000,     /* R16 */
+       0x0000,     /* R17 */
+       0x0000,     /* R18 */
+       0x0000,     /* R19 */
+       0x0000,     /* R20 */
+       0x0000,     /* R21 */
+       0x0000,     /* R22 */
+       0x00C1,     /* R23 - Additional control (1) */
+       0x0000,     /* R24 - Additional control (2) */
+       0x0000,     /* R25 - Power Management (1) */
+       0x0000,     /* R26 - Power Management (2) */
+       0x0000,     /* R27 - Additional Control (3) */
+       0x0000,     /* R28 */
+       0x0000,     /* R29 */
+       0x0000,     /* R30 */
+       0x0000,     /* R31 */
+       0x0000,     /* R32 */
+       0x0000,     /* R33 */
+       0x0050,     /* R34 - Left out Mix (1) */
+       0x0050,     /* R35 - Left out Mix (2) */
+       0x0050,     /* R36 - Right out Mix (1) */
+       0x0050,     /* R37 - Right Out Mix (2) */
+       0x0050,     /* R38 - Mono out Mix (1) */
+       0x0050,     /* R39 - Mono out Mix (2) */
+       0x0079,     /* R40 - LOUT2 volume */
+       0x0079,     /* R41 - ROUT2 volume */
+       0x0079,     /* R42 - MONOOUT volume */
+       0x0000,     /* R43 - Clocking / PLL */
+       0x0103,     /* R44 - PLL Control 1 */
+       0x0024,     /* R45 - PLL Control 2 */
+       0x01BA,     /* R46 - PLL Control 3 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48 */
+       0x0000,     /* R49 */
+       0x0000,     /* R50 */
+       0x0000,     /* R51 */
+       0x0000,     /* R52 */
+       0x0000,     /* R53 */
+       0x0000,     /* R54 */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x0000,     /* R58 */
+       0x0000,     /* R59 - PLL Control 4 */
+};
+
+static int wm8955_reset(struct snd_soc_codec *codec)
+{
+       return snd_soc_write(codec, WM8955_RESET, 0);
+}
+
+struct pll_factors {
+       int n;
+       int k;
+       int outdiv;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 22) * 10)
+
+static int wm8995_pll_factors(struct device *dev,
+                             int Fref, int Fout, struct pll_factors *pll)
+{
+       u64 Kpart;
+       unsigned int K, Ndiv, Nmod, target;
+
+       dev_dbg(dev, "Fref=%u Fout=%u\n", Fref, Fout);
+
+       /* The oscilator should run at should be 90-100MHz, and
+        * there's a divide by 4 plus an optional divide by 2 in the
+        * output path to generate the system clock.  The clock table
+        * is sortd so we should always generate a suitable target. */
+       target = Fout * 4;
+       if (target < 90000000) {
+               pll->outdiv = 1;
+               target *= 2;
+       } else {
+               pll->outdiv = 0;
+       }
+
+       WARN_ON(target < 90000000 || target > 100000000);
+
+       dev_dbg(dev, "Fvco=%dHz\n", target);
+
+       /* Now, calculate N.K */
+       Ndiv = target / Fref;
+
+       pll->n = Ndiv;
+       Nmod = target % Fref;
+       dev_dbg(dev, "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 */
+       pll->k = K / 10;
+
+       dev_dbg(dev, "N=%x K=%x OUTDIV=%x\n", pll->n, pll->k, pll->outdiv);
+
+       return 0;
+}
+
+/* Lookup table specifiying SRATE (table 25 in datasheet); some of the
+ * output frequencies have been rounded to the standard frequencies
+ * they are intended to match where the error is slight. */
+static struct {
+       int mclk;
+       int fs;
+       int usb;
+       int sr;
+} clock_cfgs[] = {
+       { 18432000,  8000, 0,  3, },
+       { 18432000, 12000, 0,  9, },
+       { 18432000, 16000, 0, 11, },
+       { 18432000, 24000, 0, 29, },
+       { 18432000, 32000, 0, 13, },
+       { 18432000, 48000, 0,  1, },
+       { 18432000, 96000, 0, 15, },
+
+       { 16934400,  8018, 0, 19, },
+       { 16934400, 11025, 0, 25, },
+       { 16934400, 22050, 0, 27, },
+       { 16934400, 44100, 0, 17, },
+       { 16934400, 88200, 0, 31, },
+
+       { 12000000,  8000, 1,  2, },
+       { 12000000, 11025, 1, 25, },
+       { 12000000, 12000, 1,  8, },
+       { 12000000, 16000, 1, 10, },
+       { 12000000, 22050, 1, 27, },
+       { 12000000, 24000, 1, 28, },
+       { 12000000, 32000, 1, 12, },
+       { 12000000, 44100, 1, 17, },
+       { 12000000, 48000, 1,  0, },
+       { 12000000, 88200, 1, 31, },
+       { 12000000, 96000, 1, 14, },
+
+       { 12288000,  8000, 0,  2, },
+       { 12288000, 12000, 0,  8, },
+       { 12288000, 16000, 0, 10, },
+       { 12288000, 24000, 0, 28, },
+       { 12288000, 32000, 0, 12, },
+       { 12288000, 48000, 0,  0, },
+       { 12288000, 96000, 0, 14, },
+
+       { 12289600,  8018, 0, 18, },
+       { 12289600, 11025, 0, 24, },
+       { 12289600, 22050, 0, 26, },
+       { 11289600, 44100, 0, 16, },
+       { 11289600, 88200, 0, 31, },
+};
+
+static int wm8955_configure_clocking(struct snd_soc_codec *codec)
+{
+       struct wm8955_priv *wm8955 = codec->private_data;
+       int i, ret, val;
+       int clocking = 0;
+       int srate = 0;
+       int sr = -1;
+       struct pll_factors pll;
+
+       /* If we're not running a sample rate currently just pick one */
+       if (wm8955->fs == 0)
+               wm8955->fs = 8000;
+
+       /* Can we generate an exact output? */
+       for (i = 0; i < ARRAY_SIZE(clock_cfgs); i++) {
+               if (wm8955->fs != clock_cfgs[i].fs)
+                       continue;
+               sr = i;
+
+               if (wm8955->mclk_rate == clock_cfgs[i].mclk)
+                       break;
+       }
+
+       /* We should never get here with an unsupported sample rate */
+       if (sr == -1) {
+               dev_err(codec->dev, "Sample rate %dHz unsupported\n",
+                       wm8955->fs);
+               WARN_ON(sr == -1);
+               return -EINVAL;
+       }
+
+       if (i == ARRAY_SIZE(clock_cfgs)) {
+               /* If we can't generate the right clock from MCLK then
+                * we should configure the PLL to supply us with an
+                * appropriate clock.
+                */
+               clocking |= WM8955_MCLKSEL;
+
+               /* Use the last divider configuration we saw for the
+                * sample rate. */
+               ret = wm8995_pll_factors(codec->dev, wm8955->mclk_rate,
+                                        clock_cfgs[sr].mclk, &pll);
+               if (ret != 0) {
+                       dev_err(codec->dev,
+                               "Unable to generate %dHz from %dHz MCLK\n",
+                               wm8955->fs, wm8955->mclk_rate);
+                       return -EINVAL;
+               }
+
+               snd_soc_update_bits(codec, WM8955_PLL_CONTROL_1,
+                                   WM8955_N_MASK | WM8955_K_21_18_MASK,
+                                   (pll.n << WM8955_N_SHIFT) |
+                                   pll.k >> 18);
+               snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+                                   WM8955_K_17_9_MASK,
+                                   (pll.k >> 9) & WM8955_K_17_9_MASK);
+               snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+                                   WM8955_K_8_0_MASK,
+                                   pll.k & WM8955_K_8_0_MASK);
+               if (pll.k)
+                       snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+                                           WM8955_KEN, WM8955_KEN);
+               else
+                       snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+                                           WM8955_KEN, 0);
+
+               if (pll.outdiv)
+                       val = WM8955_PLL_RB | WM8955_PLLOUTDIV2;
+               else
+                       val = WM8955_PLL_RB;
+
+               /* Now start the PLL running */
+               snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+                                   WM8955_PLL_RB | WM8955_PLLOUTDIV2, val);
+               snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+                                   WM8955_PLLEN, WM8955_PLLEN);
+       }
+
+       srate = clock_cfgs[sr].usb | (clock_cfgs[sr].sr << WM8955_SR_SHIFT);
+
+       snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+                           WM8955_USB | WM8955_SR_MASK, srate);
+       snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+                           WM8955_MCLKSEL, clocking);
+
+       return 0;
+}
+
+static int wm8955_sysclk(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       int ret = 0;
+
+       /* Always disable the clocks - if we're doing reconfiguration this
+        * avoids misclocking.
+        */
+       snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                           WM8955_DIGENB, 0);
+       snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+                           WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMD:
+               break;
+       case SND_SOC_DAPM_PRE_PMU:
+               ret = wm8955_configure_clocking(codec);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8955_set_deemph(struct snd_soc_codec *codec)
+{
+       struct wm8955_priv *wm8955 = codec->private_data;
+       int val, i, best;
+
+       /* If we're using deemphasis select the nearest available sample
+        * rate.
+        */
+       if (wm8955->deemph) {
+               best = 1;
+               for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+                       if (abs(deemph_settings[i] - wm8955->fs) <
+                           abs(deemph_settings[best] - wm8955->fs))
+                               best = i;
+               }
+
+               val = best << WM8955_DEEMPH_SHIFT;
+       } else {
+               val = 0;
+       }
+
+       dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+       return snd_soc_update_bits(codec, WM8955_DAC_CONTROL,
+                                  WM8955_DEEMPH_MASK, val);
+}
+
+static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8955_priv *wm8955 = codec->private_data;
+
+       return wm8955->deemph;
+}
+
+static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8955_priv *wm8955 = codec->private_data;
+       int deemph = ucontrol->value.enumerated.item[0];
+
+       if (deemph > 1)
+               return -EINVAL;
+
+       wm8955->deemph = deemph;
+
+       return wm8955_set_deemph(codec);
+}
+
+static const char *bass_mode_text[] = {
+       "Linear", "Adaptive",
+};
+
+static const struct soc_enum bass_mode =
+       SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 7, 2, bass_mode_text);
+
+static const char *bass_cutoff_text[] = {
+       "Low", "High"
+};
+
+static const struct soc_enum bass_cutoff =
+       SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 6, 2, bass_cutoff_text);
+
+static const char *treble_cutoff_text[] = {
+       "High", "Low"
+};
+
+static const struct soc_enum treble_cutoff =
+       SOC_ENUM_SINGLE(WM8955_TREBLE_CONTROL, 6, 2, treble_cutoff_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(treble_tlv, -1200, 150, 1);
+
+static const struct snd_kcontrol_new wm8955_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8955_LEFT_DAC_VOLUME,
+                WM8955_RIGHT_DAC_VOLUME, 0, 255, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Attenuation Volume", WM8955_DAC_CONTROL, 7, 1, 1,
+              atten_tlv),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+                   wm8955_get_deemph, wm8955_put_deemph),
+
+SOC_ENUM("Bass Mode", bass_mode),
+SOC_ENUM("Bass Cutoff", bass_cutoff),
+SOC_SINGLE("Bass Volume", WM8955_BASS_CONTROL, 0, 15, 1),
+
+SOC_ENUM("Treble Cutoff", treble_cutoff),
+SOC_SINGLE_TLV("Treble Volume", WM8955_TREBLE_CONTROL, 0, 14, 1, treble_tlv),
+
+SOC_SINGLE_TLV("Left Bypass Volume", WM8955_LEFT_OUT_MIX_1, 4, 7, 1,
+              bypass_tlv),
+SOC_SINGLE_TLV("Left Mono Volume", WM8955_LEFT_OUT_MIX_2, 4, 7, 1,
+              bypass_tlv),
+
+SOC_SINGLE_TLV("Right Mono Volume", WM8955_RIGHT_OUT_MIX_1, 4, 7, 1,
+              bypass_tlv),
+SOC_SINGLE_TLV("Right Bypass Volume", WM8955_RIGHT_OUT_MIX_2, 4, 7, 1,
+              bypass_tlv),
+
+/* Not a stereo pair so they line up with the DAPM switches */
+SOC_SINGLE_TLV("Mono Left Bypass Volume", WM8955_MONO_OUT_MIX_1, 4, 7, 1,
+              mono_tlv),
+SOC_SINGLE_TLV("Mono Right Bypass Volume", WM8955_MONO_OUT_MIX_2, 4, 7, 1,
+              mono_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8955_LOUT1_VOLUME,
+                WM8955_ROUT1_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8955_LOUT1_VOLUME,
+            WM8955_ROUT1_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8955_LOUT2_VOLUME,
+                WM8955_ROUT2_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8955_LOUT2_VOLUME,
+            WM8955_ROUT2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("Mono Volume", WM8955_MONOOUT_VOLUME, 0, 127, 0, out_tlv),
+SOC_SINGLE("Mono ZC Switch", WM8955_MONOOUT_VOLUME, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lmixer[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8955_LEFT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_LEFT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_LEFT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_LEFT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new rmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_RIGHT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_RIGHT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8955_RIGHT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_RIGHT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_MONO_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8955_MONO_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_MONO_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8955_MONO_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8955_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MONOIN-"),
+SND_SOC_DAPM_INPUT("MONOIN+"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+
+SND_SOC_DAPM_PGA("Mono Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8955_POWER_MANAGEMENT_1, 0, 1, wm8955_sysclk,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TSDEN", WM8955_ADDITIONAL_CONTROL_1, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8955_POWER_MANAGEMENT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8955_POWER_MANAGEMENT_2, 7, 0),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8955_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8955_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LOUT2 PGA", WM8955_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT2 PGA", WM8955_POWER_MANAGEMENT_2, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MOUT PGA", WM8955_POWER_MANAGEMENT_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("OUT3 PGA", WM8955_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+
+/* The names are chosen to make the control names nice */
+SND_SOC_DAPM_MIXER("Left", SND_SOC_NOPM, 0, 0,
+                  lmixer, ARRAY_SIZE(lmixer)),
+SND_SOC_DAPM_MIXER("Right", SND_SOC_NOPM, 0, 0,
+                  rmixer, ARRAY_SIZE(rmixer)),
+SND_SOC_DAPM_MIXER("Mono", SND_SOC_NOPM, 0, 0,
+                  mmixer, ARRAY_SIZE(mmixer)),
+
+SND_SOC_DAPM_OUTPUT("LOUT1"),
+SND_SOC_DAPM_OUTPUT("ROUT1"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route wm8955_intercon[] = {
+       { "DACL", NULL, "SYSCLK" },
+       { "DACR", NULL, "SYSCLK" },
+
+       { "Mono Input", NULL, "MONOIN-" },
+       { "Mono Input", NULL, "MONOIN+" },
+
+       { "Left", "Playback Switch", "DACL" },
+       { "Left", "Right Playback Switch", "DACR" },
+       { "Left", "Bypass Switch", "LINEINL" },
+       { "Left", "Mono Switch", "Mono Input" },
+
+       { "Right", "Playback Switch", "DACR" },
+       { "Right", "Left Playback Switch", "DACL" },
+       { "Right", "Bypass Switch", "LINEINR" },
+       { "Right", "Mono Switch", "Mono Input" },
+
+       { "Mono", "Left Playback Switch", "DACL" },
+       { "Mono", "Right Playback Switch", "DACR" },
+       { "Mono", "Left Bypass Switch", "LINEINL" },
+       { "Mono", "Right Bypass Switch", "LINEINR" },
+
+       { "LOUT1 PGA", NULL, "Left" },
+       { "LOUT1", NULL, "TSDEN" },
+       { "LOUT1", NULL, "LOUT1 PGA" },
+
+       { "ROUT1 PGA", NULL, "Right" },
+       { "ROUT1", NULL, "TSDEN" },
+       { "ROUT1", NULL, "ROUT1 PGA" },
+
+       { "LOUT2 PGA", NULL, "Left" },
+       { "LOUT2", NULL, "TSDEN" },
+       { "LOUT2", NULL, "LOUT2 PGA" },
+
+       { "ROUT2 PGA", NULL, "Right" },
+       { "ROUT2", NULL, "TSDEN" },
+       { "ROUT2", NULL, "ROUT2 PGA" },
+
+       { "MOUT PGA", NULL, "Mono" },
+       { "MONOOUT", NULL, "MOUT PGA" },
+
+       /* OUT3 not currently implemented */
+       { "OUT3", NULL, "OUT3 PGA" },
+};
+
+static int wm8955_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_add_controls(codec, wm8955_snd_controls,
+                            ARRAY_SIZE(wm8955_snd_controls));
+
+       snd_soc_dapm_new_controls(codec, wm8955_dapm_widgets,
+                                 ARRAY_SIZE(wm8955_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, wm8955_intercon,
+                               ARRAY_SIZE(wm8955_intercon));
+
+       return 0;
+}
+
+static int wm8955_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 wm8955_priv *wm8955 = codec->private_data;
+       int ret;
+       int wl;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               wl = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               wl = 0x4;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               wl = 0x8;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               wl = 0xc;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+                           WM8955_WL_MASK, wl);
+
+       wm8955->fs = params_rate(params);
+       wm8955_set_deemph(codec);
+
+       /* If the chip is clocked then disable the clocks and force a
+        * reconfiguration, otherwise DAPM will power up the
+        * clocks for us later. */
+       ret = snd_soc_read(codec, WM8955_POWER_MANAGEMENT_1);
+       if (ret < 0)
+               return ret;
+       if (ret & WM8955_DIGENB) {
+               snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                                   WM8955_DIGENB, 0);
+               snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+                                   WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+               wm8955_configure_clocking(codec);
+       }
+
+       return 0;
+}
+
+
+static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                            unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8955_priv *priv = codec->private_data;
+       int div;
+
+       switch (clk_id) {
+       case WM8955_CLK_MCLK:
+               if (freq > 15000000) {
+                       priv->mclk_rate = freq /= 2;
+                       div = WM8955_MCLKDIV2;
+               } else {
+                       priv->mclk_rate = freq;
+                       div = 0;
+               }
+
+               snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+                                   WM8955_MCLKDIV2, div);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+       return 0;
+}
+
+static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 aif = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aif |= WM8955_MS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
+               aif |= WM8955_LRP;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif |= 0x3;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               aif |= 0x2;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aif |= 0x1;
+               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:
+                       aif |= WM8955_BCLKINV;
+                       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:
+                       aif |= WM8955_BCLKINV | WM8955_LRP;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif |= WM8955_BCLKINV;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       aif |= WM8955_LRP;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+                           WM8955_MS | WM8955_FORMAT_MASK | WM8955_BCLKINV |
+                           WM8955_LRP, aif);
+
+       return 0;
+}
+
+
+static int wm8955_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int val;
+
+       if (mute)
+               val = WM8955_DACMU;
+       else
+               val = 0;
+
+       snd_soc_update_bits(codec, WM8955_DAC_CONTROL, WM8955_DACMU, val);
+
+       return 0;
+}
+
+static int wm8955_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8955_priv *wm8955 = codec->private_data;
+       int ret, i;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VMID resistance 2*50k */
+               snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                                   WM8955_VMIDSEL_MASK,
+                                   0x1 << WM8955_VMIDSEL_SHIFT);
+
+               /* Default bias current */
+               snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+                                   WM8955_VSEL_MASK,
+                                   0x2 << WM8955_VSEL_SHIFT);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+                                                   wm8955->supplies);
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "Failed to enable supplies: %d\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       /* Sync back cached values if they're
+                        * different from the hardware default.
+                        */
+                       for (i = 0; i < ARRAY_SIZE(wm8955->reg_cache); i++) {
+                               if (i == WM8955_RESET)
+                                       continue;
+
+                               if (wm8955->reg_cache[i] == wm8955_reg[i])
+                                       continue;
+
+                               snd_soc_write(codec, i, wm8955->reg_cache[i]);
+                       }
+
+                       /* Enable VREF and VMID */
+                       snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                                           WM8955_VREF |
+                                           WM8955_VMIDSEL_MASK,
+                                           WM8955_VREF |
+                                           0x3 << WM8955_VREF_SHIFT);
+
+                       /* Let VMID ramp */
+                       msleep(500);
+
+                       /* High resistance VROI to maintain outputs */
+                       snd_soc_update_bits(codec,
+                                           WM8955_ADDITIONAL_CONTROL_3,
+                                           WM8955_VROI, WM8955_VROI);
+               }
+
+               /* Maintain VMID with 2*250k */
+               snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                                   WM8955_VMIDSEL_MASK,
+                                   0x2 << WM8955_VMIDSEL_SHIFT);
+
+               /* Minimum bias current */
+               snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+                                   WM8955_VSEL_MASK, 0);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* Low resistance VROI to help discharge */
+               snd_soc_update_bits(codec,
+                                   WM8955_ADDITIONAL_CONTROL_3,
+                                   WM8955_VROI, 0);
+
+               /* Turn off VMID and VREF */
+               snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+                                   WM8955_VREF |
+                                   WM8955_VMIDSEL_MASK, 0);
+
+               regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies),
+                                      wm8955->supplies);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8955_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8955_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 wm8955_dai_ops = {
+       .set_sysclk = wm8955_set_sysclk,
+       .set_fmt = wm8955_set_fmt,
+       .hw_params = wm8955_hw_params,
+       .digital_mute = wm8955_digital_mute,
+};
+
+struct snd_soc_dai wm8955_dai = {
+       .name = "WM8955",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = WM8955_RATES,
+               .formats = WM8955_FORMATS,
+       },
+       .ops = &wm8955_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8955_dai);
+
+#ifdef CONFIG_PM
+static int wm8955_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;
+
+       wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8955_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define wm8955_suspend NULL
+#define wm8955_resume NULL
+#endif
+
+static int wm8955_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8955_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8955_codec;
+       codec = wm8955_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;
+       }
+
+       wm8955_add_widgets(codec);
+
+       return ret;
+
+pcm_err:
+       return ret;
+}
+
+static int wm8955_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_wm8955 = {
+       .probe =        wm8955_probe,
+       .remove =       wm8955_remove,
+       .suspend =      wm8955_suspend,
+       .resume =       wm8955_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8955);
+
+static int wm8955_register(struct wm8955_priv *wm8955,
+                          enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &wm8955->codec;
+       int i;
+
+       if (wm8955_codec) {
+               dev_err(codec->dev, "Another WM8955 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8955;
+       codec->name = "WM8955";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8955_set_bias_level;
+       codec->dai = &wm8955_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8955_MAX_REGISTER;
+       codec->reg_cache = &wm8955->reg_cache;
+
+       memcpy(codec->reg_cache, wm8955_reg, sizeof(wm8955_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
+               wm8955->supplies[i].supply = wm8955_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
+                                wm8955->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+                                   wm8955->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_get;
+       }
+
+       ret = wm8955_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+               goto err_enable;
+       }
+
+       wm8955_dai.dev = codec->dev;
+
+       /* Change some default settings - latch VU and enable ZC */
+       wm8955->reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
+       wm8955->reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
+       wm8955->reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
+       wm8955->reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
+       wm8955->reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
+       wm8955->reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
+       wm8955->reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+
+       /* Also enable adaptive bass boost by default */
+       wm8955->reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+
+       /* Set platform data values */
+       if (wm8955->pdata) {
+               if (wm8955->pdata->out2_speaker)
+                       wm8955->reg_cache[WM8955_ADDITIONAL_CONTROL_2]
+                               |= WM8955_ROUT2INV;
+
+               if (wm8955->pdata->monoin_diff)
+                       wm8955->reg_cache[WM8955_MONO_OUT_MIX_1]
+                               |= WM8955_DMEN;
+       }
+
+       wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Bias level configuration will have done an extra enable */
+       regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+
+       wm8955_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(&wm8955_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(wm8955->supplies), wm8955->supplies);
+err_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+err:
+       kfree(wm8955);
+       return ret;
+}
+
+static void wm8955_unregister(struct wm8955_priv *wm8955)
+{
+       wm8955_set_bias_level(&wm8955->codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+       snd_soc_unregister_dai(&wm8955_dai);
+       snd_soc_unregister_codec(&wm8955->codec);
+       kfree(wm8955);
+       wm8955_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8955_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8955_priv *wm8955;
+       struct snd_soc_codec *codec;
+
+       wm8955 = kzalloc(sizeof(struct wm8955_priv), GFP_KERNEL);
+       if (wm8955 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8955->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8955);
+       codec->control_data = i2c;
+       wm8955->pdata = i2c->dev.platform_data;
+
+       codec->dev = &i2c->dev;
+
+       return wm8955_register(wm8955, SND_SOC_I2C);
+}
+
+static __devexit int wm8955_i2c_remove(struct i2c_client *client)
+{
+       struct wm8955_priv *wm8955 = i2c_get_clientdata(client);
+       wm8955_unregister(wm8955);
+       return 0;
+}
+
+static const struct i2c_device_id wm8955_i2c_id[] = {
+       { "wm8955", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
+
+static struct i2c_driver wm8955_i2c_driver = {
+       .driver = {
+               .name = "wm8955",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8955_i2c_probe,
+       .remove =   __devexit_p(wm8955_i2c_remove),
+       .id_table = wm8955_i2c_id,
+};
+#endif
+
+static int __init wm8955_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8955_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8955 I2C driver: %d\n",
+                      ret);
+       }
+#endif
+       return 0;
+}
+module_init(wm8955_modinit);
+
+static void __exit wm8955_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8955_i2c_driver);
+#endif
+}
+module_exit(wm8955_exit);
+
+MODULE_DESCRIPTION("ASoC WM8955 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8955.h b/sound/soc/codecs/wm8955.h
new file mode 100644 (file)
index 0000000..ae349c8
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * wm8955.h  --  WM8904 ASoC 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.
+ */
+
+#ifndef _WM8955_H
+#define _WM8955_H
+
+#define WM8955_CLK_MCLK 1
+
+extern struct snd_soc_dai wm8955_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8955;
+
+/*
+ * Register values.
+ */
+#define WM8955_LOUT1_VOLUME                     0x02
+#define WM8955_ROUT1_VOLUME                     0x03
+#define WM8955_DAC_CONTROL                      0x05
+#define WM8955_AUDIO_INTERFACE                  0x07
+#define WM8955_SAMPLE_RATE                      0x08
+#define WM8955_LEFT_DAC_VOLUME                  0x0A
+#define WM8955_RIGHT_DAC_VOLUME                 0x0B
+#define WM8955_BASS_CONTROL                     0x0C
+#define WM8955_TREBLE_CONTROL                   0x0D
+#define WM8955_RESET                            0x0F
+#define WM8955_ADDITIONAL_CONTROL_1             0x17
+#define WM8955_ADDITIONAL_CONTROL_2             0x18
+#define WM8955_POWER_MANAGEMENT_1               0x19
+#define WM8955_POWER_MANAGEMENT_2               0x1A
+#define WM8955_ADDITIONAL_CONTROL_3             0x1B
+#define WM8955_LEFT_OUT_MIX_1                   0x22
+#define WM8955_LEFT_OUT_MIX_2                   0x23
+#define WM8955_RIGHT_OUT_MIX_1                  0x24
+#define WM8955_RIGHT_OUT_MIX_2                  0x25
+#define WM8955_MONO_OUT_MIX_1                   0x26
+#define WM8955_MONO_OUT_MIX_2                   0x27
+#define WM8955_LOUT2_VOLUME                     0x28
+#define WM8955_ROUT2_VOLUME                     0x29
+#define WM8955_MONOOUT_VOLUME                   0x2A
+#define WM8955_CLOCKING_PLL                     0x2B
+#define WM8955_PLL_CONTROL_1                    0x2C
+#define WM8955_PLL_CONTROL_2                    0x2D
+#define WM8955_PLL_CONTROL_3                    0x2E
+#define WM8955_PLL_CONTROL_4                    0x3B
+
+#define WM8955_REGISTER_COUNT                   29
+#define WM8955_MAX_REGISTER                     0x3B
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8955_LO1VU                            0x0100  /* LO1VU */
+#define WM8955_LO1VU_MASK                       0x0100  /* LO1VU */
+#define WM8955_LO1VU_SHIFT                           8  /* LO1VU */
+#define WM8955_LO1VU_WIDTH                           1  /* LO1VU */
+#define WM8955_LO1ZC                            0x0080  /* LO1ZC */
+#define WM8955_LO1ZC_MASK                       0x0080  /* LO1ZC */
+#define WM8955_LO1ZC_SHIFT                           7  /* LO1ZC */
+#define WM8955_LO1ZC_WIDTH                           1  /* LO1ZC */
+#define WM8955_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_SHIFT                         0  /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_WIDTH                         7  /* LOUTVOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8955_RO1VU                            0x0100  /* RO1VU */
+#define WM8955_RO1VU_MASK                       0x0100  /* RO1VU */
+#define WM8955_RO1VU_SHIFT                           8  /* RO1VU */
+#define WM8955_RO1VU_WIDTH                           1  /* RO1VU */
+#define WM8955_RO1ZC                            0x0080  /* RO1ZC */
+#define WM8955_RO1ZC_MASK                       0x0080  /* RO1ZC */
+#define WM8955_RO1ZC_SHIFT                           7  /* RO1ZC */
+#define WM8955_RO1ZC_WIDTH                           1  /* RO1ZC */
+#define WM8955_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_SHIFT                         0  /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_WIDTH                         7  /* ROUTVOL - [6:0] */
+
+/*
+ * R5 (0x05) - DAC Control
+ */
+#define WM8955_DAT                              0x0080  /* DAT */
+#define WM8955_DAT_MASK                         0x0080  /* DAT */
+#define WM8955_DAT_SHIFT                             7  /* DAT */
+#define WM8955_DAT_WIDTH                             1  /* DAT */
+#define WM8955_DACMU                            0x0008  /* DACMU */
+#define WM8955_DACMU_MASK                       0x0008  /* DACMU */
+#define WM8955_DACMU_SHIFT                           3  /* DACMU */
+#define WM8955_DACMU_WIDTH                           1  /* DACMU */
+#define WM8955_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R7 (0x07) - Audio Interface
+ */
+#define WM8955_BCLKINV                          0x0080  /* BCLKINV */
+#define WM8955_BCLKINV_MASK                     0x0080  /* BCLKINV */
+#define WM8955_BCLKINV_SHIFT                         7  /* BCLKINV */
+#define WM8955_BCLKINV_WIDTH                         1  /* BCLKINV */
+#define WM8955_MS                               0x0040  /* MS */
+#define WM8955_MS_MASK                          0x0040  /* MS */
+#define WM8955_MS_SHIFT                              6  /* MS */
+#define WM8955_MS_WIDTH                              1  /* MS */
+#define WM8955_LRSWAP                           0x0020  /* LRSWAP */
+#define WM8955_LRSWAP_MASK                      0x0020  /* LRSWAP */
+#define WM8955_LRSWAP_SHIFT                          5  /* LRSWAP */
+#define WM8955_LRSWAP_WIDTH                          1  /* LRSWAP */
+#define WM8955_LRP                              0x0010  /* LRP */
+#define WM8955_LRP_MASK                         0x0010  /* LRP */
+#define WM8955_LRP_SHIFT                             4  /* LRP */
+#define WM8955_LRP_WIDTH                             1  /* LRP */
+#define WM8955_WL_MASK                          0x000C  /* WL - [3:2] */
+#define WM8955_WL_SHIFT                              2  /* WL - [3:2] */
+#define WM8955_WL_WIDTH                              2  /* WL - [3:2] */
+#define WM8955_FORMAT_MASK                      0x0003  /* FORMAT - [1:0] */
+#define WM8955_FORMAT_SHIFT                          0  /* FORMAT - [1:0] */
+#define WM8955_FORMAT_WIDTH                          2  /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Sample Rate
+ */
+#define WM8955_BCLKDIV2                         0x0080  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_MASK                    0x0080  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_SHIFT                        7  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_WIDTH                        1  /* BCLKDIV2 */
+#define WM8955_MCLKDIV2                         0x0040  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_MASK                    0x0040  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_SHIFT                        6  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */
+#define WM8955_SR_MASK                          0x003E  /* SR - [5:1] */
+#define WM8955_SR_SHIFT                              1  /* SR - [5:1] */
+#define WM8955_SR_WIDTH                              5  /* SR - [5:1] */
+#define WM8955_USB                              0x0001  /* USB */
+#define WM8955_USB_MASK                         0x0001  /* USB */
+#define WM8955_USB_SHIFT                             0  /* USB */
+#define WM8955_USB_WIDTH                             1  /* USB */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8955_LDVU                             0x0100  /* LDVU */
+#define WM8955_LDVU_MASK                        0x0100  /* LDVU */
+#define WM8955_LDVU_SHIFT                            8  /* LDVU */
+#define WM8955_LDVU_WIDTH                            1  /* LDVU */
+#define WM8955_LDACVOL_MASK                     0x00FF  /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_SHIFT                         0  /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_WIDTH                         8  /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8955_RDVU                             0x0100  /* RDVU */
+#define WM8955_RDVU_MASK                        0x0100  /* RDVU */
+#define WM8955_RDVU_SHIFT                            8  /* RDVU */
+#define WM8955_RDVU_WIDTH                            1  /* RDVU */
+#define WM8955_RDACVOL_MASK                     0x00FF  /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_SHIFT                         0  /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_WIDTH                         8  /* RDACVOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Bass control
+ */
+#define WM8955_BB                               0x0080  /* BB */
+#define WM8955_BB_MASK                          0x0080  /* BB */
+#define WM8955_BB_SHIFT                              7  /* BB */
+#define WM8955_BB_WIDTH                              1  /* BB */
+#define WM8955_BC                               0x0040  /* BC */
+#define WM8955_BC_MASK                          0x0040  /* BC */
+#define WM8955_BC_SHIFT                              6  /* BC */
+#define WM8955_BC_WIDTH                              1  /* BC */
+#define WM8955_BASS_MASK                        0x000F  /* BASS - [3:0] */
+#define WM8955_BASS_SHIFT                            0  /* BASS - [3:0] */
+#define WM8955_BASS_WIDTH                            4  /* BASS - [3:0] */
+
+/*
+ * R13 (0x0D) - Treble control
+ */
+#define WM8955_TC                               0x0040  /* TC */
+#define WM8955_TC_MASK                          0x0040  /* TC */
+#define WM8955_TC_SHIFT                              6  /* TC */
+#define WM8955_TC_WIDTH                              1  /* TC */
+#define WM8955_TRBL_MASK                        0x000F  /* TRBL - [3:0] */
+#define WM8955_TRBL_SHIFT                            0  /* TRBL - [3:0] */
+#define WM8955_TRBL_WIDTH                            4  /* TRBL - [3:0] */
+
+/*
+ * R15 (0x0F) - Reset
+ */
+#define WM8955_RESET_MASK                       0x01FF  /* RESET - [8:0] */
+#define WM8955_RESET_SHIFT                           0  /* RESET - [8:0] */
+#define WM8955_RESET_WIDTH                           9  /* RESET - [8:0] */
+
+/*
+ * R23 (0x17) - Additional control (1)
+ */
+#define WM8955_TSDEN                            0x0100  /* TSDEN */
+#define WM8955_TSDEN_MASK                       0x0100  /* TSDEN */
+#define WM8955_TSDEN_SHIFT                           8  /* TSDEN */
+#define WM8955_TSDEN_WIDTH                           1  /* TSDEN */
+#define WM8955_VSEL_MASK                        0x00C0  /* VSEL - [7:6] */
+#define WM8955_VSEL_SHIFT                            6  /* VSEL - [7:6] */
+#define WM8955_VSEL_WIDTH                            2  /* VSEL - [7:6] */
+#define WM8955_DMONOMIX_MASK                    0x0030  /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_SHIFT                        4  /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_WIDTH                        2  /* DMONOMIX - [5:4] */
+#define WM8955_DACINV                           0x0002  /* DACINV */
+#define WM8955_DACINV_MASK                      0x0002  /* DACINV */
+#define WM8955_DACINV_SHIFT                          1  /* DACINV */
+#define WM8955_DACINV_WIDTH                          1  /* DACINV */
+#define WM8955_TOEN                             0x0001  /* TOEN */
+#define WM8955_TOEN_MASK                        0x0001  /* TOEN */
+#define WM8955_TOEN_SHIFT                            0  /* TOEN */
+#define WM8955_TOEN_WIDTH                            1  /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control (2)
+ */
+#define WM8955_OUT3SW_MASK                      0x0180  /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_SHIFT                          7  /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_WIDTH                          2  /* OUT3SW - [8:7] */
+#define WM8955_ROUT2INV                         0x0010  /* ROUT2INV */
+#define WM8955_ROUT2INV_MASK                    0x0010  /* ROUT2INV */
+#define WM8955_ROUT2INV_SHIFT                        4  /* ROUT2INV */
+#define WM8955_ROUT2INV_WIDTH                        1  /* ROUT2INV */
+#define WM8955_DACOSR                           0x0001  /* DACOSR */
+#define WM8955_DACOSR_MASK                      0x0001  /* DACOSR */
+#define WM8955_DACOSR_SHIFT                          0  /* DACOSR */
+#define WM8955_DACOSR_WIDTH                          1  /* DACOSR */
+
+/*
+ * R25 (0x19) - Power Management (1)
+ */
+#define WM8955_VMIDSEL_MASK                     0x0180  /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_SHIFT                         7  /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_WIDTH                         2  /* VMIDSEL - [8:7] */
+#define WM8955_VREF                             0x0040  /* VREF */
+#define WM8955_VREF_MASK                        0x0040  /* VREF */
+#define WM8955_VREF_SHIFT                            6  /* VREF */
+#define WM8955_VREF_WIDTH                            1  /* VREF */
+#define WM8955_DIGENB                           0x0001  /* DIGENB */
+#define WM8955_DIGENB_MASK                      0x0001  /* DIGENB */
+#define WM8955_DIGENB_SHIFT                          0  /* DIGENB */
+#define WM8955_DIGENB_WIDTH                          1  /* DIGENB */
+
+/*
+ * R26 (0x1A) - Power Management (2)
+ */
+#define WM8955_DACL                             0x0100  /* DACL */
+#define WM8955_DACL_MASK                        0x0100  /* DACL */
+#define WM8955_DACL_SHIFT                            8  /* DACL */
+#define WM8955_DACL_WIDTH                            1  /* DACL */
+#define WM8955_DACR                             0x0080  /* DACR */
+#define WM8955_DACR_MASK                        0x0080  /* DACR */
+#define WM8955_DACR_SHIFT                            7  /* DACR */
+#define WM8955_DACR_WIDTH                            1  /* DACR */
+#define WM8955_LOUT1                            0x0040  /* LOUT1 */
+#define WM8955_LOUT1_MASK                       0x0040  /* LOUT1 */
+#define WM8955_LOUT1_SHIFT                           6  /* LOUT1 */
+#define WM8955_LOUT1_WIDTH                           1  /* LOUT1 */
+#define WM8955_ROUT1                            0x0020  /* ROUT1 */
+#define WM8955_ROUT1_MASK                       0x0020  /* ROUT1 */
+#define WM8955_ROUT1_SHIFT                           5  /* ROUT1 */
+#define WM8955_ROUT1_WIDTH                           1  /* ROUT1 */
+#define WM8955_LOUT2                            0x0010  /* LOUT2 */
+#define WM8955_LOUT2_MASK                       0x0010  /* LOUT2 */
+#define WM8955_LOUT2_SHIFT                           4  /* LOUT2 */
+#define WM8955_LOUT2_WIDTH                           1  /* LOUT2 */
+#define WM8955_ROUT2                            0x0008  /* ROUT2 */
+#define WM8955_ROUT2_MASK                       0x0008  /* ROUT2 */
+#define WM8955_ROUT2_SHIFT                           3  /* ROUT2 */
+#define WM8955_ROUT2_WIDTH                           1  /* ROUT2 */
+#define WM8955_MONO                             0x0004  /* MONO */
+#define WM8955_MONO_MASK                        0x0004  /* MONO */
+#define WM8955_MONO_SHIFT                            2  /* MONO */
+#define WM8955_MONO_WIDTH                            1  /* MONO */
+#define WM8955_OUT3                             0x0002  /* OUT3 */
+#define WM8955_OUT3_MASK                        0x0002  /* OUT3 */
+#define WM8955_OUT3_SHIFT                            1  /* OUT3 */
+#define WM8955_OUT3_WIDTH                            1  /* OUT3 */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8955_VROI                             0x0040  /* VROI */
+#define WM8955_VROI_MASK                        0x0040  /* VROI */
+#define WM8955_VROI_SHIFT                            6  /* VROI */
+#define WM8955_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R34 (0x22) - Left out Mix (1)
+ */
+#define WM8955_LD2LO                            0x0100  /* LD2LO */
+#define WM8955_LD2LO_MASK                       0x0100  /* LD2LO */
+#define WM8955_LD2LO_SHIFT                           8  /* LD2LO */
+#define WM8955_LD2LO_WIDTH                           1  /* LD2LO */
+#define WM8955_LI2LO                            0x0080  /* LI2LO */
+#define WM8955_LI2LO_MASK                       0x0080  /* LI2LO */
+#define WM8955_LI2LO_SHIFT                           7  /* LI2LO */
+#define WM8955_LI2LO_WIDTH                           1  /* LI2LO */
+#define WM8955_LI2LOVOL_MASK                    0x0070  /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_SHIFT                        4  /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_WIDTH                        3  /* LI2LOVOL - [6:4] */
+
+/*
+ * R35 (0x23) - Left out Mix (2)
+ */
+#define WM8955_RD2LO                            0x0100  /* RD2LO */
+#define WM8955_RD2LO_MASK                       0x0100  /* RD2LO */
+#define WM8955_RD2LO_SHIFT                           8  /* RD2LO */
+#define WM8955_RD2LO_WIDTH                           1  /* RD2LO */
+#define WM8955_RI2LO                            0x0080  /* RI2LO */
+#define WM8955_RI2LO_MASK                       0x0080  /* RI2LO */
+#define WM8955_RI2LO_SHIFT                           7  /* RI2LO */
+#define WM8955_RI2LO_WIDTH                           1  /* RI2LO */
+#define WM8955_RI2LOVOL_MASK                    0x0070  /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_SHIFT                        4  /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_WIDTH                        3  /* RI2LOVOL - [6:4] */
+
+/*
+ * R36 (0x24) - Right out Mix (1)
+ */
+#define WM8955_LD2RO                            0x0100  /* LD2RO */
+#define WM8955_LD2RO_MASK                       0x0100  /* LD2RO */
+#define WM8955_LD2RO_SHIFT                           8  /* LD2RO */
+#define WM8955_LD2RO_WIDTH                           1  /* LD2RO */
+#define WM8955_LI2RO                            0x0080  /* LI2RO */
+#define WM8955_LI2RO_MASK                       0x0080  /* LI2RO */
+#define WM8955_LI2RO_SHIFT                           7  /* LI2RO */
+#define WM8955_LI2RO_WIDTH                           1  /* LI2RO */
+#define WM8955_LI2ROVOL_MASK                    0x0070  /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_SHIFT                        4  /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_WIDTH                        3  /* LI2ROVOL - [6:4] */
+
+/*
+ * R37 (0x25) - Right Out Mix (2)
+ */
+#define WM8955_RD2RO                            0x0100  /* RD2RO */
+#define WM8955_RD2RO_MASK                       0x0100  /* RD2RO */
+#define WM8955_RD2RO_SHIFT                           8  /* RD2RO */
+#define WM8955_RD2RO_WIDTH                           1  /* RD2RO */
+#define WM8955_RI2RO                            0x0080  /* RI2RO */
+#define WM8955_RI2RO_MASK                       0x0080  /* RI2RO */
+#define WM8955_RI2RO_SHIFT                           7  /* RI2RO */
+#define WM8955_RI2RO_WIDTH                           1  /* RI2RO */
+#define WM8955_RI2ROVOL_MASK                    0x0070  /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_SHIFT                        4  /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_WIDTH                        3  /* RI2ROVOL - [6:4] */
+
+/*
+ * R38 (0x26) - Mono out Mix (1)
+ */
+#define WM8955_LD2MO                            0x0100  /* LD2MO */
+#define WM8955_LD2MO_MASK                       0x0100  /* LD2MO */
+#define WM8955_LD2MO_SHIFT                           8  /* LD2MO */
+#define WM8955_LD2MO_WIDTH                           1  /* LD2MO */
+#define WM8955_LI2MO                            0x0080  /* LI2MO */
+#define WM8955_LI2MO_MASK                       0x0080  /* LI2MO */
+#define WM8955_LI2MO_SHIFT                           7  /* LI2MO */
+#define WM8955_LI2MO_WIDTH                           1  /* LI2MO */
+#define WM8955_LI2MOVOL_MASK                    0x0070  /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_SHIFT                        4  /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_WIDTH                        3  /* LI2MOVOL - [6:4] */
+#define WM8955_DMEN                             0x0001  /* DMEN */
+#define WM8955_DMEN_MASK                        0x0001  /* DMEN */
+#define WM8955_DMEN_SHIFT                            0  /* DMEN */
+#define WM8955_DMEN_WIDTH                            1  /* DMEN */
+
+/*
+ * R39 (0x27) - Mono out Mix (2)
+ */
+#define WM8955_RD2MO                            0x0100  /* RD2MO */
+#define WM8955_RD2MO_MASK                       0x0100  /* RD2MO */
+#define WM8955_RD2MO_SHIFT                           8  /* RD2MO */
+#define WM8955_RD2MO_WIDTH                           1  /* RD2MO */
+#define WM8955_RI2MO                            0x0080  /* RI2MO */
+#define WM8955_RI2MO_MASK                       0x0080  /* RI2MO */
+#define WM8955_RI2MO_SHIFT                           7  /* RI2MO */
+#define WM8955_RI2MO_WIDTH                           1  /* RI2MO */
+#define WM8955_RI2MOVOL_MASK                    0x0070  /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_SHIFT                        4  /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_WIDTH                        3  /* RI2MOVOL - [6:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8955_LO2VU                            0x0100  /* LO2VU */
+#define WM8955_LO2VU_MASK                       0x0100  /* LO2VU */
+#define WM8955_LO2VU_SHIFT                           8  /* LO2VU */
+#define WM8955_LO2VU_WIDTH                           1  /* LO2VU */
+#define WM8955_LO2ZC                            0x0080  /* LO2ZC */
+#define WM8955_LO2ZC_MASK                       0x0080  /* LO2ZC */
+#define WM8955_LO2ZC_SHIFT                           7  /* LO2ZC */
+#define WM8955_LO2ZC_WIDTH                           1  /* LO2ZC */
+#define WM8955_LOUT2VOL_MASK                    0x007F  /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_SHIFT                        0  /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_WIDTH                        7  /* LOUT2VOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8955_RO2VU                            0x0100  /* RO2VU */
+#define WM8955_RO2VU_MASK                       0x0100  /* RO2VU */
+#define WM8955_RO2VU_SHIFT                           8  /* RO2VU */
+#define WM8955_RO2VU_WIDTH                           1  /* RO2VU */
+#define WM8955_RO2ZC                            0x0080  /* RO2ZC */
+#define WM8955_RO2ZC_MASK                       0x0080  /* RO2ZC */
+#define WM8955_RO2ZC_SHIFT                           7  /* RO2ZC */
+#define WM8955_RO2ZC_WIDTH                           1  /* RO2ZC */
+#define WM8955_ROUT2VOL_MASK                    0x007F  /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_SHIFT                        0  /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_WIDTH                        7  /* ROUT2VOL - [6:0] */
+
+/*
+ * R42 (0x2A) - MONOOUT volume
+ */
+#define WM8955_MOZC                             0x0080  /* MOZC */
+#define WM8955_MOZC_MASK                        0x0080  /* MOZC */
+#define WM8955_MOZC_SHIFT                            7  /* MOZC */
+#define WM8955_MOZC_WIDTH                            1  /* MOZC */
+#define WM8955_MOUTVOL_MASK                     0x007F  /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_SHIFT                         0  /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_WIDTH                         7  /* MOUTVOL - [6:0] */
+
+/*
+ * R43 (0x2B) - Clocking / PLL
+ */
+#define WM8955_MCLKSEL                          0x0100  /* MCLKSEL */
+#define WM8955_MCLKSEL_MASK                     0x0100  /* MCLKSEL */
+#define WM8955_MCLKSEL_SHIFT                         8  /* MCLKSEL */
+#define WM8955_MCLKSEL_WIDTH                         1  /* MCLKSEL */
+#define WM8955_PLLOUTDIV2                       0x0020  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_MASK                  0x0020  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_SHIFT                      5  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_WIDTH                      1  /* PLLOUTDIV2 */
+#define WM8955_PLL_RB                           0x0010  /* PLL_RB */
+#define WM8955_PLL_RB_MASK                      0x0010  /* PLL_RB */
+#define WM8955_PLL_RB_SHIFT                          4  /* PLL_RB */
+#define WM8955_PLL_RB_WIDTH                          1  /* PLL_RB */
+#define WM8955_PLLEN                            0x0008  /* PLLEN */
+#define WM8955_PLLEN_MASK                       0x0008  /* PLLEN */
+#define WM8955_PLLEN_SHIFT                           3  /* PLLEN */
+#define WM8955_PLLEN_WIDTH                           1  /* PLLEN */
+
+/*
+ * R44 (0x2C) - PLL Control 1
+ */
+#define WM8955_N_MASK                           0x01E0  /* N - [8:5] */
+#define WM8955_N_SHIFT                               5  /* N - [8:5] */
+#define WM8955_N_WIDTH                               4  /* N - [8:5] */
+#define WM8955_K_21_18_MASK                     0x000F  /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_SHIFT                         0  /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_WIDTH                         4  /* K(21:18) - [3:0] */
+
+/*
+ * R45 (0x2D) - PLL Control 2
+ */
+#define WM8955_K_17_9_MASK                      0x01FF  /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_SHIFT                          0  /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_WIDTH                          9  /* K(17:9) - [8:0] */
+
+/*
+ * R46 (0x2E) - PLL Control 3
+ */
+#define WM8955_K_8_0_MASK                       0x01FF  /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_SHIFT                           0  /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_WIDTH                           9  /* K(8:0) - [8:0] */
+
+/*
+ * R59 (0x3B) - PLL Control 4
+ */
+#define WM8955_KEN                              0x0080  /* KEN */
+#define WM8955_KEN_MASK                         0x0080  /* KEN */
+#define WM8955_KEN_SHIFT                             7  /* KEN */
+#define WM8955_KEN_WIDTH                             1  /* KEN */
+
+#endif
index a8007d58813f77b4687223fe40f48820e4b71561..d2342c5e0425f2e861d8861d80cffd2a3b81ef1a 100644 (file)
@@ -1022,6 +1022,9 @@ static int wm8961_resume(struct platform_device *pdev)
        int i;
 
        for (i = 0; i < codec->reg_cache_size; i++) {
+               if (reg_cache[i] == wm8961_reg_defaults[i])
+                       continue;
+
                if (i == WM8961_SOFTWARE_RESET)
                        continue;
 
index 341481e0e83004c285bdaf634838647cfcc12a14..a54dc77b7f343f649b24ff58b9f401a6e8333235 100644 (file)
@@ -1319,10 +1319,6 @@ static int wm8990_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;
 
-       /* we only need to suspend if we are a valid card */
-       if (!codec->card)
-               return 0;
-
        wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
@@ -1335,10 +1331,6 @@ static int wm8990_resume(struct platform_device *pdev)
        u8 data[2];
        u16 *cache = codec->reg_cache;
 
-       /* we only need to resume if we are a valid card */
-       if (!codec->card)
-               return 0;
-
        /* Sync reg_cache with the hardware */
        for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
                if (i + 1 == WM8990_RESET)
index 2981afae842c9c48220410ac9c5e8d24cbbe2f5f..828d8174d5b71e1bca02332f11dbc6b6def34320 100644 (file)
@@ -227,6 +227,7 @@ struct wm8993_priv {
        int class_w_users;
        unsigned int fll_fref;
        unsigned int fll_fout;
+       int fll_src;
 };
 
 static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
@@ -506,6 +507,7 @@ static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
 
        wm8993->fll_fref = Fref;
        wm8993->fll_fout = Fout;
+       wm8993->fll_src = source;
 
        return 0;
 }
@@ -1480,9 +1482,74 @@ static int wm8993_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8993_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;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       int fll_fout = wm8993->fll_fout;
+       int fll_fref  = wm8993->fll_fref;
+       int ret;
+
+       /* Stop the FLL in an orderly fashion */
+       ret = wm8993_set_fll(codec->dai, 0, 0, 0, 0);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to stop FLL\n");
+               return ret;
+       }
+
+       wm8993->fll_fout = fll_fout;
+       wm8993->fll_fref = fll_fref;
+
+       wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8993_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       u16 *cache = wm8993->reg_cache;
+       int i, ret;
+
+       /* Restore the register settings */
+       for (i = 1; i < WM8993_MAX_REGISTER; i++) {
+               if (cache[i] == wm8993_reg_defaults[i])
+                       continue;
+               snd_soc_write(codec, i, cache[i]);
+       }
+
+       wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Restart the FLL? */
+       if (wm8993->fll_fout) {
+               int fll_fout = wm8993->fll_fout;
+               int fll_fref  = wm8993->fll_fref;
+
+               wm8993->fll_fref = 0;
+               wm8993->fll_fout = 0;
+
+               ret = wm8993_set_fll(codec->dai, 0, wm8993->fll_src,
+                                    fll_fref, fll_fout);
+               if (ret != 0)
+                       dev_err(codec->dev, "Failed to restart FLL\n");
+       }
+
+       return 0;
+}
+#else
+#define wm8993_suspend NULL
+#define wm8993_resume NULL
+#endif
+
 struct snd_soc_codec_device soc_codec_dev_wm8993 = {
        .probe =        wm8993_probe,
        .remove =       wm8993_remove,
+       .suspend =      wm8993_suspend,
+       .resume =       wm8993_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);
 
index 0a302e1080d9ec06553d77dfb12125b4fcece380..a613bbb0bc91a2929a6061383628c7c35713ef20 100644 (file)
@@ -767,14 +767,27 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
        int ret = 0;
 
        switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
+               if (!dev->clk_active) {
+                       clk_enable(dev->clk);
+                       dev->clk_active = 1;
+               }
+               /* Fall through */
+       case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                davinci_mcasp_start(dev, substream->stream);
                break;
 
-       case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
+               davinci_mcasp_stop(dev, substream->stream);
+               if (dev->clk_active) {
+                       clk_disable(dev->clk);
+                       dev->clk_active = 0;
+               }
+
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                davinci_mcasp_stop(dev, substream->stream);
                break;
@@ -866,6 +879,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        }
 
        clk_enable(dev->clk);
+       dev->clk_active = 1;
 
        dev->base = (void __iomem *)IO_ADDRESS(mem->start);
        dev->op_mode = pdata->op_mode;
index 582c9249ef09b4cb0a4a53222ab7dd4b323a58cb..e755b5121ec728be29aed547b662c02994b2dc50 100644 (file)
@@ -44,6 +44,7 @@ struct davinci_audio_dev {
        int sample_rate;
        struct clk *clk;
        unsigned int codec_fmt;
+       u8 clk_active;
 
        /* McASP specific data */
        int     tdm_slots;
index ad4d7f47a86b01f985ffebeea5818f27d1f67a3c..80c7fdf2f521542d83babc9855d3a362de545a19 100644 (file)
@@ -49,7 +49,7 @@ static void print_buf_info(int slot, char *name)
 static struct snd_pcm_hardware pcm_hardware_playback = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-                SNDRV_PCM_INFO_PAUSE),
+                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
        .formats = (SNDRV_PCM_FMTBIT_S16_LE),
        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
                  SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
index cc7edb5f792d86fef69a04ae713587721f2272f9..93ed3aad1631bd2090a5da8bc3af312a6f24b9fa 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
 #include <linux/gpio.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
 
 #include <plat/regs-s3c2412-iis.h>
 #include "s3c-dma.h"
 #include "s3c64xx-i2s.h"
 
+/* The value should be set to maximum of the total number
+ * of I2Sv3 controllers that any supported SoC has.
+ */
+#define MAX_I2SV3      2
+
 static struct s3c2410_dma_client s3c64xx_dma_client_out = {
        .name           = "I2S PCM Stereo out"
 };
@@ -46,37 +45,12 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = {
        .name           = "I2S PCM Stereo in"
 };
 
-static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
-       [0] = {
-               .channel        = DMACH_I2S0_OUT,
-               .client         = &s3c64xx_dma_client_out,
-               .dma_addr       = S3C64XX_PA_IIS0 + S3C2412_IISTXD,
-               .dma_size       = 4,
-       },
-       [1] = {
-               .channel        = DMACH_I2S1_OUT,
-               .client         = &s3c64xx_dma_client_out,
-               .dma_addr       = S3C64XX_PA_IIS1 + S3C2412_IISTXD,
-               .dma_size       = 4,
-       },
-};
-
-static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
-       [0] = {
-               .channel        = DMACH_I2S0_IN,
-               .client         = &s3c64xx_dma_client_in,
-               .dma_addr       = S3C64XX_PA_IIS0 + S3C2412_IISRXD,
-               .dma_size       = 4,
-       },
-       [1] = {
-               .channel        = DMACH_I2S1_IN,
-               .client         = &s3c64xx_dma_client_in,
-               .dma_addr       = S3C64XX_PA_IIS1 + S3C2412_IISRXD,
-               .dma_size       = 4,
-       },
-};
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[MAX_I2SV3];
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3];
+static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3];
 
-static struct s3c_i2sv2_info s3c64xx_i2s[2];
+struct snd_soc_dai s3c64xx_i2s_dai[MAX_I2SV3];
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
 
 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
 {
@@ -169,55 +143,13 @@ static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = {
        .set_sysclk     = s3c64xx_i2s_set_sysclk,       
 };
 
-struct snd_soc_dai s3c64xx_i2s_dai[] = {
-       {
-               .name           = "s3c64xx-i2s",
-               .id             = 0,
-               .probe          = s3c64xx_i2s_probe,
-               .playback = {
-                       .channels_min   = 2,
-                       .channels_max   = 2,
-                       .rates          = S3C64XX_I2S_RATES,
-                       .formats        = S3C64XX_I2S_FMTS,
-               },
-               .capture = {
-                        .channels_min  = 2,
-                        .channels_max  = 2,
-                        .rates         = S3C64XX_I2S_RATES,
-                        .formats       = S3C64XX_I2S_FMTS,
-                },
-               .ops = &s3c64xx_i2s_dai_ops,
-               .symmetric_rates = 1,
-       },
-       {
-               .name           = "s3c64xx-i2s",
-               .id             = 1,
-               .probe          = s3c64xx_i2s_probe,
-               .playback = {
-                       .channels_min   = 2,
-                       .channels_max   = 2,
-                       .rates          = S3C64XX_I2S_RATES,
-                       .formats        = S3C64XX_I2S_FMTS,
-               },
-               .capture = {
-                        .channels_min  = 2,
-                        .channels_max  = 2,
-                        .rates         = S3C64XX_I2S_RATES,
-                        .formats       = S3C64XX_I2S_FMTS,
-                },
-               .ops = &s3c64xx_i2s_dai_ops,
-               .symmetric_rates = 1,
-       },
-};
-EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
-
 static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
 {
        struct s3c_i2sv2_info *i2s;
        struct snd_soc_dai *dai;
        int ret;
 
-       if (pdev->id >= ARRAY_SIZE(s3c64xx_i2s)) {
+       if (pdev->id >= MAX_I2SV3) {
                dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
                return -EINVAL;
        }
@@ -225,10 +157,40 @@ static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
        i2s = &s3c64xx_i2s[pdev->id];
        dai = &s3c64xx_i2s_dai[pdev->id];
        dai->dev = &pdev->dev;
+       dai->name = "s3c64xx-i2s";
+       dai->id = pdev->id;
+       dai->symmetric_rates = 1;
+       dai->playback.channels_min = 2;
+       dai->playback.channels_max = 2;
+       dai->playback.rates = S3C64XX_I2S_RATES;
+       dai->playback.formats = S3C64XX_I2S_FMTS;
+       dai->capture.channels_min = 2;
+       dai->capture.channels_max = 2;
+       dai->capture.rates = S3C64XX_I2S_RATES;
+       dai->capture.formats = S3C64XX_I2S_FMTS;
+       dai->probe = s3c64xx_i2s_probe;
+       dai->ops = &s3c64xx_i2s_dai_ops;
 
        i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
        i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
 
+       if (pdev->id == 0) {
+               i2s->dma_capture->channel = DMACH_I2S0_IN;
+               i2s->dma_capture->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD;
+               i2s->dma_playback->channel = DMACH_I2S0_OUT;
+               i2s->dma_playback->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD;
+       } else {
+               i2s->dma_capture->channel = DMACH_I2S1_IN;
+               i2s->dma_capture->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD;
+               i2s->dma_playback->channel = DMACH_I2S1_OUT;
+               i2s->dma_playback->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD;
+       }
+
+       i2s->dma_capture->client = &s3c64xx_dma_client_in;
+       i2s->dma_capture->dma_size = 4;
+       i2s->dma_playback->client = &s3c64xx_dma_client_out;
+       i2s->dma_playback->dma_size = 4;
+
        i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus");
        if (IS_ERR(i2s->iis_cclk)) {
                dev_err(&pdev->dev, "failed to get audio-bus\n");
index 9e69765865547b98da7043da493a41c8f63f9078..8072a6d1c4db78586f0b941e0f77d146a3fc5e9f 100644 (file)
@@ -47,4 +47,12 @@ config SND_FSI_AK4642
          This option enables generic sound support for the
          FSI - AK4642 unit
 
+config SND_FSI_DA7210
+       bool "FSI-DA7210 sound support"
+       depends on SND_SOC_SH4_FSI
+       select SND_SOC_DA7210
+       help
+         This option enables generic sound support for the
+         FSI - DA7210 unit
+
 endmenu
index a6997872f24ede65524ee4b83eb31737975f7e21..1d0ec0af74b7868f94a99cb9f398ed65bb6bcea5 100644 (file)
@@ -13,6 +13,8 @@ obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o
 ## boards
 snd-soc-sh7760-ac97-objs       := sh7760-ac97.o
 snd-soc-fsi-ak4642-objs                := fsi-ak4642.o
+snd-soc-fsi-da7210-objs                := fsi-da7210.o
 
 obj-$(CONFIG_SND_SH7760_AC97)  += snd-soc-sh7760-ac97.o
 obj-$(CONFIG_SND_FSI_AK4642)   += snd-soc-fsi-ak4642.o
+obj-$(CONFIG_SND_FSI_DA7210)   += snd-soc-fsi-da7210.o
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
new file mode 100644 (file)
index 0000000..33b4d17
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * fsi-da7210.c
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.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/sh_fsi.h>
+#include "../codecs/da7210.h"
+
+static int fsi_da7210_init(struct snd_soc_codec *codec)
+{
+       return snd_soc_dai_set_fmt(&da7210_dai,
+                                  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                                  SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static struct snd_soc_dai_link fsi_da7210_dai = {
+       .name           = "DA7210",
+       .stream_name    = "DA7210",
+       .cpu_dai        = &fsi_soc_dai[1], /* FSI B */
+       .codec_dai      = &da7210_dai,
+       .init           = fsi_da7210_init,
+};
+
+static struct snd_soc_card fsi_soc_card = {
+       .name           = "FSI",
+       .platform       = &fsi_soc_platform,
+       .dai_link       = &fsi_da7210_dai,
+       .num_links      = 1,
+};
+
+static struct snd_soc_device fsi_da7210_snd_devdata = {
+       .card           = &fsi_soc_card,
+       .codec_dev      = &soc_codec_dev_da7210,
+};
+
+static struct platform_device *fsi_da7210_snd_device;
+
+static int __init fsi_da7210_sound_init(void)
+{
+       int ret;
+
+       fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!fsi_da7210_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(fsi_da7210_snd_device, &fsi_da7210_snd_devdata);
+       fsi_da7210_snd_devdata.dev = &fsi_da7210_snd_device->dev;
+       ret = platform_device_add(fsi_da7210_snd_device);
+       if (ret)
+               platform_device_put(fsi_da7210_snd_device);
+
+       return ret;
+}
+
+static void __exit fsi_da7210_sound_exit(void)
+{
+       platform_device_unregister(fsi_da7210_snd_device);
+}
+
+module_init(fsi_da7210_sound_init);
+module_exit(fsi_da7210_sound_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
index 42813b80838988c96b62cf5a91f06d870dbc2f6e..5f9f2693f4ebff34d4183cc4d723608f75e59952 100644 (file)
@@ -67,6 +67,7 @@
 /* DOFF_ST */
 #define ERR_OVER       0x00000010
 #define ERR_UNDER      0x00000001
+#define ST_ERR         (ERR_OVER | ERR_UNDER)
 
 /* CLK_RST */
 #define B_CLK          0x00000010
@@ -92,6 +93,7 @@
 struct fsi_priv {
        void __iomem *base;
        struct snd_pcm_substream *substream;
+       struct fsi_master *master;
 
        int fifo_max;
        int chan;
@@ -110,8 +112,6 @@ struct fsi_master {
        struct sh_fsi_platform_info *info;
 };
 
-static struct fsi_master *master;
-
 /************************************************************************
 
 
@@ -166,7 +166,7 @@ static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
        return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
 }
 
-static int fsi_master_write(u32 reg, u32 data)
+static int fsi_master_write(struct fsi_master *master, u32 reg, u32 data)
 {
        if ((reg < MREG_START) ||
            (reg > MREG_END))
@@ -175,7 +175,7 @@ static int fsi_master_write(u32 reg, u32 data)
        return __fsi_reg_write((u32)(master->base + reg), data);
 }
 
-static u32 fsi_master_read(u32 reg)
+static u32 fsi_master_read(struct fsi_master *master, u32 reg)
 {
        if ((reg < MREG_START) ||
            (reg > MREG_END))
@@ -184,7 +184,8 @@ static u32 fsi_master_read(u32 reg)
        return __fsi_reg_read((u32)(master->base + reg));
 }
 
-static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+static int fsi_master_mask_set(struct fsi_master *master,
+                              u32 reg, u32 mask, u32 data)
 {
        if ((reg < MREG_START) ||
            (reg > MREG_END))
@@ -200,43 +201,35 @@ static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
 
 
 ************************************************************************/
-static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
+static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
 {
-       struct snd_soc_pcm_runtime *rtd;
-       struct fsi_priv *fsi = NULL;
+       return fsi->master;
+}
 
-       if (!substream || !master)
-               return NULL;
+static int fsi_is_port_a(struct fsi_priv *fsi)
+{
+       return fsi->master->base == fsi->base;
+}
 
-       rtd = substream->private_data;
-       switch (rtd->dai->cpu_dai->id) {
-       case 0:
-               fsi = &master->fsia;
-               break;
-       case 1:
-               fsi = &master->fsib;
-               break;
-       }
+static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai_link *machine = rtd->dai;
 
-       return fsi;
+       return  machine->cpu_dai;
 }
 
-static int fsi_is_port_a(struct fsi_priv *fsi)
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
 {
-       /* return
-        * 1 : port a
-        * 0 : port b
-        */
-
-       if (fsi == &master->fsia)
-               return 1;
+       struct snd_soc_dai *dai = fsi_get_dai(substream);
 
-       return 0;
+       return dai->private_data;
 }
 
 static u32 fsi_get_info_flags(struct fsi_priv *fsi)
 {
        int is_porta = fsi_is_port_a(fsi);
+       struct fsi_master *master = fsi_get_master(fsi);
 
        return is_porta ? master->info->porta_flags :
                master->info->portb_flags;
@@ -314,27 +307,30 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
 static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
 {
        u32 data = fsi_port_ab_io_bit(fsi, is_play);
+       struct fsi_master *master = fsi_get_master(fsi);
 
-       fsi_master_mask_set(IMSK,  data, data);
-       fsi_master_mask_set(IEMSK, data, data);
+       fsi_master_mask_set(master, IMSK,  data, data);
+       fsi_master_mask_set(master, IEMSK, data, data);
 }
 
 static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
 {
        u32 data = fsi_port_ab_io_bit(fsi, is_play);
+       struct fsi_master *master = fsi_get_master(fsi);
 
-       fsi_master_mask_set(IMSK,  data, 0);
-       fsi_master_mask_set(IEMSK, data, 0);
+       fsi_master_mask_set(master, IMSK,  data, 0);
+       fsi_master_mask_set(master, IEMSK, data, 0);
 }
 
 static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
 {
        u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
+       struct fsi_master *master = fsi_get_master(fsi);
 
        if (enable)
-               fsi_master_mask_set(CLK_RST, val, val);
+               fsi_master_mask_set(master, CLK_RST, val, val);
        else
-               fsi_master_mask_set(CLK_RST, val, 0);
+               fsi_master_mask_set(master, CLK_RST, val, 0);
 }
 
 static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
@@ -355,23 +351,23 @@ static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
        fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);
 
        /* clear interrupt factor */
-       fsi_master_mask_set(INT_ST, data, 0);
+       fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0);
 }
 
-static void fsi_soft_all_reset(void)
+static void fsi_soft_all_reset(struct fsi_master *master)
 {
-       u32 status = fsi_master_read(SOFT_RST);
+       u32 status = fsi_master_read(master, SOFT_RST);
 
        /* port AB reset */
        status &= 0x000000ff;
-       fsi_master_write(SOFT_RST, status);
+       fsi_master_write(master, SOFT_RST, status);
        mdelay(10);
 
        /* soft reset */
        status &= 0x000000f0;
-       fsi_master_write(SOFT_RST, status);
+       fsi_master_write(master, SOFT_RST, status);
        status |= 0x00000001;
-       fsi_master_write(SOFT_RST, status);
+       fsi_master_write(master, SOFT_RST, status);
        mdelay(10);
 }
 
@@ -380,18 +376,21 @@ static int fsi_data_push(struct fsi_priv *fsi)
 {
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_substream *substream = NULL;
+       u32 status;
        int send;
        int fifo_free;
        int width;
        u8 *start;
-       int i;
+       int i, ret, over_period;
 
        if (!fsi                        ||
            !fsi->substream             ||
            !fsi->substream->runtime)
                return -EINVAL;
 
-       runtime = fsi->substream->runtime;
+       over_period     = 0;
+       substream       = fsi->substream;
+       runtime         = substream->runtime;
 
        /* FSI FIFO has limit.
         * So, this driver can not send periods data at a time
@@ -399,7 +398,7 @@ static int fsi_data_push(struct fsi_priv *fsi)
        if (fsi->byte_offset >=
            fsi->period_len * (fsi->periods + 1)) {
 
-               substream = fsi->substream;
+               over_period = 1;
                fsi->periods = (fsi->periods + 1) % runtime->periods;
 
                if (0 == fsi->periods)
@@ -438,30 +437,42 @@ static int fsi_data_push(struct fsi_priv *fsi)
 
        fsi->byte_offset += send * width;
 
+       ret = 0;
+       status = fsi_reg_read(fsi, DOFF_ST);
+       if (status & ERR_OVER) {
+               struct snd_soc_dai *dai = fsi_get_dai(substream);
+               dev_err(dai->dev, "over run error\n");
+               fsi_reg_write(fsi, DOFF_ST, status & ~ST_ERR);
+               ret = -EIO;
+       }
+
        fsi_irq_enable(fsi, 1);
 
-       if (substream)
+       if (over_period)
                snd_pcm_period_elapsed(substream);
 
-       return 0;
+       return ret;
 }
 
 static int fsi_data_pop(struct fsi_priv *fsi)
 {
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_substream *substream = NULL;
+       u32 status;
        int free;
        int fifo_fill;
        int width;
        u8 *start;
-       int i;
+       int i, ret, over_period;
 
        if (!fsi                        ||
            !fsi->substream             ||
            !fsi->substream->runtime)
                return -EINVAL;
 
-       runtime = fsi->substream->runtime;
+       over_period     = 0;
+       substream       = fsi->substream;
+       runtime         = substream->runtime;
 
        /* FSI FIFO has limit.
         * So, this driver can not send periods data at a time
@@ -469,7 +480,7 @@ static int fsi_data_pop(struct fsi_priv *fsi)
        if (fsi->byte_offset >=
            fsi->period_len * (fsi->periods + 1)) {
 
-               substream = fsi->substream;
+               over_period = 1;
                fsi->periods = (fsi->periods + 1) % runtime->periods;
 
                if (0 == fsi->periods)
@@ -507,22 +518,32 @@ static int fsi_data_pop(struct fsi_priv *fsi)
 
        fsi->byte_offset += fifo_fill * width;
 
+       ret = 0;
+       status = fsi_reg_read(fsi, DIFF_ST);
+       if (status & ERR_UNDER) {
+               struct snd_soc_dai *dai = fsi_get_dai(substream);
+               dev_err(dai->dev, "under run error\n");
+               fsi_reg_write(fsi, DIFF_ST, status & ~ST_ERR);
+               ret = -EIO;
+       }
+
        fsi_irq_enable(fsi, 0);
 
-       if (substream)
+       if (over_period)
                snd_pcm_period_elapsed(substream);
 
-       return 0;
+       return ret;
 }
 
 static irqreturn_t fsi_interrupt(int irq, void *data)
 {
-       u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
-       u32 int_st = fsi_master_read(INT_ST);
+       struct fsi_master *master = data;
+       u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010;
+       u32 int_st = fsi_master_read(master, INT_ST);
 
        /* clear irq status */
-       fsi_master_write(SOFT_RST, status);
-       fsi_master_write(SOFT_RST, status | 0x00000010);
+       fsi_master_write(master, SOFT_RST, status);
+       fsi_master_write(master, SOFT_RST, status | 0x00000010);
 
        if (int_st & INT_A_OUT)
                fsi_data_push(&master->fsia);
@@ -533,7 +554,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
        if (int_st & INT_B_IN)
                fsi_data_pop(&master->fsib);
 
-       fsi_master_write(INT_ST, 0x0000000);
+       fsi_master_write(master, INT_ST, 0x0000000);
 
        return IRQ_HANDLED;
 }
@@ -548,7 +569,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
 static int fsi_dai_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
-       struct fsi_priv *fsi = fsi_get(substream);
+       struct fsi_priv *fsi = fsi_get_priv(substream);
        const char *msg;
        u32 flags = fsi_get_info_flags(fsi);
        u32 fmt;
@@ -667,7 +688,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
 static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
-       struct fsi_priv *fsi = fsi_get(substream);
+       struct fsi_priv *fsi = fsi_get_priv(substream);
        int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
        fsi_irq_disable(fsi, is_play);
@@ -679,7 +700,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
 static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                           struct snd_soc_dai *dai)
 {
-       struct fsi_priv *fsi = fsi_get(substream);
+       struct fsi_priv *fsi = fsi_get_priv(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        int ret = 0;
@@ -760,7 +781,7 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)
 static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct fsi_priv *fsi = fsi_get(substream);
+       struct fsi_priv *fsi = fsi_get_priv(substream);
        long location;
 
        location = (fsi->byte_offset - 1);
@@ -870,10 +891,16 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform);
 ************************************************************************/
 static int fsi_probe(struct platform_device *pdev)
 {
+       struct fsi_master *master;
        struct resource *res;
        unsigned int irq;
        int ret;
 
+       if (0 != pdev->id) {
+               dev_err(&pdev->dev, "current fsi support id 0 only now\n");
+               return -ENODEV;
+       }
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
        if (!res || (int)irq <= 0) {
@@ -899,15 +926,19 @@ static int fsi_probe(struct platform_device *pdev)
        master->irq             = irq;
        master->info            = pdev->dev.platform_data;
        master->fsia.base       = master->base;
+       master->fsia.master     = master;
        master->fsib.base       = master->base + 0x40;
+       master->fsib.master     = master;
 
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
 
        fsi_soc_dai[0].dev              = &pdev->dev;
+       fsi_soc_dai[0].private_data     = &master->fsia;
        fsi_soc_dai[1].dev              = &pdev->dev;
+       fsi_soc_dai[1].private_data     = &master->fsib;
 
-       fsi_soft_all_reset();
+       fsi_soft_all_reset(master);
 
        ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
        if (ret) {
@@ -937,6 +968,10 @@ exit:
 
 static int fsi_remove(struct platform_device *pdev)
 {
+       struct fsi_master *master;
+
+       master = fsi_get_master(fsi_soc_dai[0].private_data);
+
        snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
        snd_soc_unregister_platform(&fsi_soc_platform);
 
@@ -946,7 +981,12 @@ static int fsi_remove(struct platform_device *pdev)
 
        iounmap(master->base);
        kfree(master);
-       master = NULL;
+
+       fsi_soc_dai[0].dev              = NULL;
+       fsi_soc_dai[0].private_data     = NULL;
+       fsi_soc_dai[1].dev              = NULL;
+       fsi_soc_dai[1].private_data     = NULL;
+
        return 0;
 }
 
index d2505e8b06c9783dcb89a6226f7f2e3393c6bf63..02c235711bb8ca84d002ea3d161991f7c9b4dce0 100644 (file)
@@ -182,7 +182,7 @@ static struct {
        {
                .addr_bits = 7, .data_bits = 9,
                .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
-               .spi_write = snd_soc_7_9_spi_write 
+               .spi_write = snd_soc_7_9_spi_write,
        },
        {
                .addr_bits = 8, .data_bits = 8,
index 0a6440c6f54a80a9af52aa10ffe9d898fe5c456d..9085b40fa04b412e2cc7b4a9fdc61fe8a95a193d 100644 (file)
@@ -1276,8 +1276,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        codec_dai->codec = card->codec;
 
        /* check client and interface hw capabilities */
-       sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
-               num);
+       snprintf(new_name, sizeof(new_name), "%s %s-%d",
+                dai_link->stream_name, codec_dai->name, num);
 
        if (codec_dai->playback.channels_min)
                playback = 1;
@@ -1427,9 +1427,9 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits);
  *
  * Returns 1 for change else 0.
  */
-static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
-                               unsigned short reg, unsigned int mask,
-                               unsigned int value)
+int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+                              unsigned short reg, unsigned int mask,
+                              unsigned int value)
 {
        int change;
 
@@ -1439,6 +1439,7 @@ static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
 
        return change;
 }
+EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
 
 /**
  * snd_soc_test_bits - test register for change
index 0d294ef7259006e0681a9b1bb2092c608fde1925..de22c2f1842ec855c5ceeb27bd3361029b16542c 100644 (file)
@@ -739,6 +739,8 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
                            struct snd_soc_dapm_widget *b,
                            int sort[])
 {
+       if (a->codec != b->codec)
+               return (unsigned long)a - (unsigned long)b;
        if (sort[a->id] != sort[b->id])
                return sort[a->id] - sort[b->id];
        if (a->reg != b->reg)
@@ -1147,9 +1149,16 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        out = is_connected_output_ep(w);
        dapm_clear_walk(w->codec);
 
-       ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d\n",
+       ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d",
                       w->name, w->power ? "On" : "Off", in, out);
 
+       if (w->reg >= 0)
+               ret += snprintf(buf + ret, PAGE_SIZE - ret,
+                               " - R%d(0x%x) bit %d",
+                               w->reg, w->reg, w->shift);
+
+       ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
        if (w->sname)
                ret += snprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",
                                w->sname,
@@ -1255,8 +1264,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
 
 /* test and update the power status of a mixer or switch widget */
 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
-                                  struct snd_kcontrol *kcontrol, int reg,
-                                  int val_mask, int val, int invert)
+                                  struct snd_kcontrol *kcontrol, int connect)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
@@ -1266,9 +1274,6 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
            widget->id != snd_soc_dapm_switch)
                return -ENODEV;
 
-       if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
-               return 0;
-
        /* find dapm widget path assoc with kcontrol */
        list_for_each_entry(path, &widget->codec->dapm_paths, list) {
                if (path->kcontrol != kcontrol)
@@ -1276,12 +1281,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 
                /* found, now check type */
                found = 1;
-               if (val)
-                       /* new connection */
-                       path->connect = invert ? 0:1;
-               else
-                       /* old connection must be powered down */
-                       path->connect = invert ? 1:0;
+               path->connect = connect;
                break;
        }
 
@@ -1688,6 +1688,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        unsigned int val, val2, val_mask;
+       int connect;
        int ret;
 
        val = (ucontrol->value.integer.value[0] & mask);
@@ -1714,7 +1715,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                return 1;
        }
 
-       dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+       if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
+               if (val)
+                       /* new connection */
+                       connect = invert ? 0:1;
+               else
+                       /* old connection must be powered down */
+                       connect = invert ? 1:0;
+
+               dapm_mixer_update_power(widget, kcontrol, connect);
+       }
+
        if (widget->event) {
                if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
                        ret = widget->event(widget, kcontrol,