summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig44
-rw-r--r--sound/soc/codecs/Makefile26
-rw-r--r--sound/soc/codecs/ac97.c16
-rw-r--r--sound/soc/codecs/ad1836.c99
-rw-r--r--sound/soc/codecs/ad1836.h1
-rw-r--r--sound/soc/codecs/ad1938.c669
-rw-r--r--sound/soc/codecs/ad1938.h100
-rw-r--r--sound/soc/codecs/ad193x.c547
-rw-r--r--sound/soc/codecs/ad193x.h81
-rw-r--r--sound/soc/codecs/ad1980.c1
-rw-r--r--sound/soc/codecs/ad73311.c1
-rw-r--r--sound/soc/codecs/ads117x.c1
-rw-r--r--sound/soc/codecs/ak4104.c13
-rw-r--r--sound/soc/codecs/ak4535.c12
-rw-r--r--sound/soc/codecs/ak4642.c178
-rw-r--r--sound/soc/codecs/ak4671.c3
-rw-r--r--sound/soc/codecs/cq93vc.c299
-rw-r--r--sound/soc/codecs/cq93vc.h29
-rw-r--r--sound/soc/codecs/cs4270.c104
-rw-r--r--sound/soc/codecs/cx20442.c3
-rw-r--r--sound/soc/codecs/da7210.c701
-rw-r--r--sound/soc/codecs/da7210.h24
-rw-r--r--sound/soc/codecs/pcm3008.c1
-rw-r--r--sound/soc/codecs/ssm2602.c18
-rw-r--r--sound/soc/codecs/stac9766.c6
-rw-r--r--sound/soc/codecs/tlv320aic23.c4
-rw-r--r--sound/soc/codecs/tlv320aic26.c15
-rw-r--r--sound/soc/codecs/tlv320aic3x.c194
-rw-r--r--sound/soc/codecs/tlv320dac33.c803
-rw-r--r--sound/soc/codecs/tpa6130a2.c147
-rw-r--r--sound/soc/codecs/twl4030.c233
-rw-r--r--sound/soc/codecs/twl4030.h2
-rw-r--r--sound/soc/codecs/twl6040.c1246
-rw-r--r--sound/soc/codecs/twl6040.h141
-rw-r--r--sound/soc/codecs/uda134x.c30
-rw-r--r--sound/soc/codecs/uda1380.c7
-rw-r--r--sound/soc/codecs/wm2000.c888
-rw-r--r--sound/soc/codecs/wm2000.h79
-rw-r--r--sound/soc/codecs/wm8350.c116
-rw-r--r--sound/soc/codecs/wm8350.h3
-rw-r--r--sound/soc/codecs/wm8400.c35
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c11
-rw-r--r--sound/soc/codecs/wm8580.c5
-rw-r--r--sound/soc/codecs/wm8711.c9
-rw-r--r--sound/soc/codecs/wm8727.c69
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731.c64
-rw-r--r--sound/soc/codecs/wm8750.c348
-rw-r--r--sound/soc/codecs/wm8753.c17
-rw-r--r--sound/soc/codecs/wm8776.c10
-rw-r--r--sound/soc/codecs/wm8900.c11
-rw-r--r--sound/soc/codecs/wm8903.c218
-rw-r--r--sound/soc/codecs/wm8903.h221
-rw-r--r--sound/soc/codecs/wm8904.c2674
-rw-r--r--sound/soc/codecs/wm8904.h1584
-rw-r--r--sound/soc/codecs/wm8940.c6
-rw-r--r--sound/soc/codecs/wm8955.c1152
-rw-r--r--sound/soc/codecs/wm8955.h489
-rw-r--r--sound/soc/codecs/wm8960.c216
-rw-r--r--sound/soc/codecs/wm8960.h11
-rw-r--r--sound/soc/codecs/wm8961.c10
-rw-r--r--sound/soc/codecs/wm8971.c13
-rw-r--r--sound/soc/codecs/wm8974.c19
-rw-r--r--sound/soc/codecs/wm8974.h12
-rw-r--r--sound/soc/codecs/wm8978.c1150
-rw-r--r--sound/soc/codecs/wm8978.h86
-rw-r--r--sound/soc/codecs/wm8988.c10
-rw-r--r--sound/soc/codecs/wm8990.c37
-rw-r--r--sound/soc/codecs/wm8993.c328
-rw-r--r--sound/soc/codecs/wm8994.c4080
-rw-r--r--sound/soc/codecs/wm8994.h34
-rw-r--r--sound/soc/codecs/wm9081.c19
-rw-r--r--sound/soc/codecs/wm9090.c773
-rw-r--r--sound/soc/codecs/wm9090.h715
-rw-r--r--sound/soc/codecs/wm9705.c1
-rw-r--r--sound/soc/codecs/wm9712.c4
-rw-r--r--sound/soc/codecs/wm9713.c81
-rw-r--r--sound/soc/codecs/wm_hubs.c175
-rw-r--r--sound/soc/codecs/wm_hubs.h7
80 files changed, 19422 insertions, 2173 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 52b005f8fed4..5da30eb6ad00 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -13,7 +13,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_L3
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1836 if SPI_MASTER
- select SND_SOC_AD1938 if SPI_MASTER
+ select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
@@ -21,8 +21,10 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
+ select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
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
@@ -33,8 +35,10 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
select SND_SOC_TWL4030 if TWL4030_CORE
+ select SND_SOC_TWL6040 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
+ select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
@@ -49,15 +53,20 @@ 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
select SND_SOC_WM8974 if I2C
+ select SND_SOC_WM8978 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
select SND_SOC_WM8993 if I2C
+ select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM9081 if I2C
+ select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
@@ -74,8 +83,8 @@ config SND_SOC_ALL_CODECS
config SND_SOC_WM_HUBS
tristate
- default y if SND_SOC_WM8993=y
- default m if SND_SOC_WM8993=m
+ default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
+ default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
config SND_SOC_AC97_CODEC
tristate
@@ -84,7 +93,7 @@ config SND_SOC_AC97_CODEC
config SND_SOC_AD1836
tristate
-config SND_SOC_AD1938
+config SND_SOC_AD193X
tristate
config SND_SOC_AD1980
@@ -108,10 +117,16 @@ config SND_SOC_AK4642
config SND_SOC_AK4671
tristate
+config SND_SOC_CQ0093VC
+ tristate
+
# Cirrus Logic CS4270 Codec
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
@@ -155,6 +170,9 @@ config SND_SOC_TWL4030
select TWL4030_CODEC
tristate
+config SND_SOC_TWL6040
+ tristate
+
config SND_SOC_UDA134X
tristate
@@ -203,9 +221,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
@@ -218,6 +242,9 @@ config SND_SOC_WM8971
config SND_SOC_WM8974
tristate
+config SND_SOC_WM8978
+ tristate
+
config SND_SOC_WM8988
tristate
@@ -227,6 +254,9 @@ config SND_SOC_WM8990
config SND_SOC_WM8993
tristate
+config SND_SOC_WM8994
+ tristate
+
config SND_SOC_WM9081
tristate
@@ -245,3 +275,9 @@ config SND_SOC_MAX9877
config SND_SOC_TPA6130A2
tristate
+
+config SND_SOC_WM2000
+ tristate
+
+config SND_SOC_WM9090
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index dbaecb133ac7..91429eab0707 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,6 +1,6 @@
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
-snd-soc-ad1938-objs := ad1938.o
+snd-soc-ad193x-objs := ad193x.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o
@@ -8,8 +8,10 @@ snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
+snd-soc-cq93vc-objs := cq93vc.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
@@ -20,6 +22,7 @@ snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
+snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wm8350-objs := wm8350.o
@@ -36,14 +39,18 @@ 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
snd-soc-wm8974-objs := wm8974.o
+snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8993-objs := wm8993.o
+snd-soc-wm8994-objs := wm8994.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
@@ -53,10 +60,12 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
+snd-soc-wm2000-objs := wm2000.o
+snd-soc-wm9090-objs := wm9090.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
-obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o
+obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
@@ -64,8 +73,10 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.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
@@ -76,6 +87,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
+obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
@@ -92,14 +104,18 @@ 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_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
+obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
@@ -109,3 +125,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
+obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
+obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index a1bbe16b7f96..1f5e57a4bb7a 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -13,6 +13,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -80,9 +81,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
static int ac97_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
+ int i;
int ret = 0;
printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
@@ -102,12 +105,6 @@ static int ac97_soc_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "ASoC: failed to init gen ac97 glue\n");
- goto err;
- }
-
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0)
@@ -123,6 +120,13 @@ static int ac97_soc_probe(struct platform_device *pdev)
if (ret < 0)
goto bus_err;
+ for (i = 0; i < card->num_links; i++) {
+ if (card->dai_link[i].codec_dai->ac97_control) {
+ snd_ac97_dev_add_pdata(codec->ac97,
+ card->dai_link[i].cpu_dai->ac97_pdata);
+ }
+ }
+
return 0;
bus_err:
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 2c18e3d1b71e..217538423225 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -17,6 +17,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -171,57 +172,35 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-
-/*
- * interface to read/write ad1836 register
- */
-#define AD1836_SPI_REG_SHFT 12
-#define AD1836_SPI_READ (1 << 11)
-#define AD1836_SPI_VAL_MSK 0x3FF
-
-/*
- * write to the ad1836 register space
- */
-
-static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+#ifdef CONFIG_PM
+static int ad1836_soc_suspend(struct platform_device *pdev,
+ pm_message_t state)
{
- u16 *reg_cache = codec->reg_cache;
- int ret = 0;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
- if (value != reg_cache[reg]) {
- unsigned short buf;
- struct spi_transfer t = {
- .tx_buf = &buf,
- .len = 2,
- };
- struct spi_message m;
-
- buf = (reg << AD1836_SPI_REG_SHFT) |
- (value & AD1836_SPI_VAL_MSK);
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- ret = spi_sync(codec->control_data, &m);
- if (ret == 0)
- reg_cache[reg] = value;
- }
+ /* reset clock control mode */
+ u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
+ adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
- return ret;
+ return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
}
-/*
- * read from the ad1836 register space cache
- */
-static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static int ad1836_soc_resume(struct platform_device *pdev)
{
- u16 *reg_cache = codec->reg_cache;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
- if (reg >= codec->reg_cache_size)
- return -EINVAL;
+ /* restore clock control mode */
+ u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
+ adc_ctrl2 |= AD1836_ADC_AUX;
- return reg_cache[reg];
+ return snd_soc_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)
{
@@ -299,39 +278,45 @@ static int ad1836_register(struct ad1836_priv *ad1836)
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = ad1836;
+ snd_soc_codec_set_drvdata(codec, ad1836);
codec->reg_cache = ad1836->reg_cache;
codec->reg_cache_size = AD1836_NUM_REGS;
codec->name = "AD1836";
codec->owner = THIS_MODULE;
codec->dai = &ad1836_dai;
codec->num_dai = 1;
- codec->write = ad1836_write_reg;
- codec->read = ad1836_read_reg_cache;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
ad1836_dai.dev = codec->dev;
ad1836_codec = codec;
+ ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to set cache I/O: %d\n",
+ ret);
+ kfree(ad1836);
+ return ret;
+ }
+
/* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */
- codec->write(codec, AD1836_DAC_CTRL1, 0x300);
+ snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */
- codec->write(codec, AD1836_DAC_CTRL2, 0x0);
+ snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */
- codec->write(codec, AD1836_ADC_CTRL1, 0x100);
+ snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
- codec->write(codec, AD1836_ADC_CTRL2, 0x180);
+ snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
/* left/right diff:PGA/MUX */
- codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
+ snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */
- codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
- codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
- codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
- codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
- codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
- codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
+ snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
@@ -404,6 +389,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);
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index 7660ee6973c0..e9d90d3951c5 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -54,6 +54,7 @@
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
+#define AD1836_ADC_AUX (0x6 << 6)
#define AD1836_ADC_CTRL3 14
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
deleted file mode 100644
index 5d489186c05b..000000000000
--- a/sound/soc/codecs/ad1938.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * File: sound/soc/codecs/ad1938.c
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: June 04 2009
- * Description: Driver for AD1938 sound chip
- *
- * Modified:
- * Copyright 2009 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include <sound/soc-dapm.h>
-#include <linux/spi/spi.h>
-#include "ad1938.h"
-
-/* codec private data */
-struct ad1938_priv {
- struct snd_soc_codec codec;
- u8 reg_cache[AD1938_NUM_REGS];
-};
-
-static struct snd_soc_codec *ad1938_codec;
-struct snd_soc_codec_device soc_codec_dev_ad1938;
-static int ad1938_register(struct ad1938_priv *ad1938);
-static void ad1938_unregister(struct ad1938_priv *ad1938);
-
-/*
- * AD1938 volume/mute/de-emphasis etc. controls
- */
-static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
-
-static const struct soc_enum ad1938_deemp_enum =
- SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp);
-
-static const struct snd_kcontrol_new ad1938_snd_controls[] = {
- /* DAC volume control */
- SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL,
- AD1938_DAC_R1_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL,
- AD1938_DAC_R2_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL,
- AD1938_DAC_R3_VOL, 0, 0xFF, 1),
- SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL,
- AD1938_DAC_R4_VOL, 0, 0xFF, 1),
-
- /* ADC switch control */
- SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE,
- AD1938_ADCR1_MUTE, 1, 1),
- SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE,
- AD1938_ADCR2_MUTE, 1, 1),
-
- /* DAC switch control */
- SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE,
- AD1938_DACR1_MUTE, 1, 1),
- SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE,
- AD1938_DACR2_MUTE, 1, 1),
- SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE,
- AD1938_DACR3_MUTE, 1, 1),
- SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE,
- AD1938_DACR4_MUTE, 1, 1),
-
- /* ADC high-pass filter */
- SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0,
- AD1938_ADC_HIGHPASS_FILTER, 1, 0),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum),
-};
-
-static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_OUTPUT("DAC1OUT"),
- SND_SOC_DAPM_OUTPUT("DAC2OUT"),
- SND_SOC_DAPM_OUTPUT("DAC3OUT"),
- SND_SOC_DAPM_OUTPUT("DAC4OUT"),
- SND_SOC_DAPM_INPUT("ADC1IN"),
- SND_SOC_DAPM_INPUT("ADC2IN"),
-};
-
-static const struct snd_soc_dapm_route audio_paths[] = {
- { "DAC", NULL, "ADC_PWR" },
- { "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", "DAC1 Switch", "DAC" },
- { "DAC2OUT", "DAC2 Switch", "DAC" },
- { "DAC3OUT", "DAC3 Switch", "DAC" },
- { "DAC4OUT", "DAC4 Switch", "DAC" },
- { "ADC", "ADC1 Switch", "ADC1IN" },
- { "ADC", "ADC2 Switch", "ADC2IN" },
-};
-
-/*
- * DAI ops entries
- */
-
-static int ad1938_mute(struct snd_soc_dai *dai, int mute)
-{
- struct snd_soc_codec *codec = dai->codec;
- int reg;
-
- reg = codec->read(codec, AD1938_DAC_CTRL2);
- reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
- (~AD1938_DAC_MASTER_MUTE);
- codec->write(codec, AD1938_DAC_CTRL2, reg);
-
- return 0;
-}
-
-static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
-{
- int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
- reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
- AD1938_PLL_POWERDOWN;
- codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
-
- return 0;
-}
-
-static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int mask, int slots, int width)
-{
- struct snd_soc_codec *codec = dai->codec;
- int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
- int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
-
- dac_reg &= ~AD1938_DAC_CHAN_MASK;
- adc_reg &= ~AD1938_ADC_CHAN_MASK;
-
- switch (slots) {
- case 2:
- dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT;
- adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT;
- break;
- case 4:
- dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT;
- adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT;
- break;
- case 8:
- dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT;
- adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT;
- break;
- case 16:
- dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT;
- adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT;
- break;
- default:
- return -EINVAL;
- }
-
- codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
- codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
-
- return 0;
-}
-
-static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
- int adc_reg, dac_reg;
-
- adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
- dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
-
- /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
- * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
- */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- adc_reg &= ~AD1938_ADC_SERFMT_MASK;
- adc_reg |= AD1938_ADC_SERFMT_TDM;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- adc_reg &= ~AD1938_ADC_SERFMT_MASK;
- adc_reg |= AD1938_ADC_SERFMT_AUX;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- adc_reg &= ~AD1938_ADC_LEFT_HIGH;
- adc_reg &= ~AD1938_ADC_BCLK_INV;
- dac_reg &= ~AD1938_DAC_LEFT_HIGH;
- dac_reg &= ~AD1938_DAC_BCLK_INV;
- break;
- case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
- adc_reg |= AD1938_ADC_LEFT_HIGH;
- adc_reg &= ~AD1938_ADC_BCLK_INV;
- dac_reg |= AD1938_DAC_LEFT_HIGH;
- dac_reg &= ~AD1938_DAC_BCLK_INV;
- break;
- case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
- adc_reg &= ~AD1938_ADC_LEFT_HIGH;
- adc_reg |= AD1938_ADC_BCLK_INV;
- dac_reg &= ~AD1938_DAC_LEFT_HIGH;
- dac_reg |= AD1938_DAC_BCLK_INV;
- break;
-
- case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
- adc_reg |= AD1938_ADC_LEFT_HIGH;
- adc_reg |= AD1938_ADC_BCLK_INV;
- dac_reg |= AD1938_DAC_LEFT_HIGH;
- dac_reg |= AD1938_DAC_BCLK_INV;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
- adc_reg |= AD1938_ADC_LCR_MASTER;
- adc_reg |= AD1938_ADC_BCLK_MASTER;
- dac_reg |= AD1938_DAC_LCR_MASTER;
- dac_reg |= AD1938_DAC_BCLK_MASTER;
- break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
- adc_reg |= AD1938_ADC_LCR_MASTER;
- adc_reg &= ~AD1938_ADC_BCLK_MASTER;
- dac_reg |= AD1938_DAC_LCR_MASTER;
- dac_reg &= ~AD1938_DAC_BCLK_MASTER;
- break;
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
- adc_reg &= ~AD1938_ADC_LCR_MASTER;
- adc_reg |= AD1938_ADC_BCLK_MASTER;
- dac_reg &= ~AD1938_DAC_LCR_MASTER;
- dac_reg |= AD1938_DAC_BCLK_MASTER;
- break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
- adc_reg &= ~AD1938_ADC_LCR_MASTER;
- adc_reg &= ~AD1938_ADC_BCLK_MASTER;
- dac_reg &= ~AD1938_DAC_LCR_MASTER;
- dac_reg &= ~AD1938_DAC_BCLK_MASTER;
- break;
- default:
- return -EINVAL;
- }
-
- codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
- codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
-
- return 0;
-}
-
-static int ad1938_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- int word_len = 0, reg = 0;
-
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
-
- /* bit size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- word_len = 3;
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- word_len = 1;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- case SNDRV_PCM_FORMAT_S32_LE:
- word_len = 0;
- break;
- }
-
- reg = codec->read(codec, AD1938_DAC_CTRL2);
- reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
- codec->write(codec, AD1938_DAC_CTRL2, reg);
-
- reg = codec->read(codec, AD1938_ADC_CTRL1);
- reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
- codec->write(codec, AD1938_ADC_CTRL1, reg);
-
- return 0;
-}
-
-static int ad1938_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- switch (level) {
- case SND_SOC_BIAS_ON:
- ad1938_pll_powerctrl(codec, 1);
- break;
- case SND_SOC_BIAS_PREPARE:
- break;
- case SND_SOC_BIAS_STANDBY:
- case SND_SOC_BIAS_OFF:
- ad1938_pll_powerctrl(codec, 0);
- break;
- }
- codec->bias_level = level;
- return 0;
-}
-
-/*
- * interface to read/write ad1938 register
- */
-
-#define AD1938_SPI_ADDR 0x4
-#define AD1938_SPI_READ 0x1
-#define AD1938_SPI_BUFLEN 3
-
-/*
- * write to the ad1938 register space
- */
-
-static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 *reg_cache = codec->reg_cache;
- int ret = 0;
-
- if (value != reg_cache[reg]) {
- uint8_t buf[AD1938_SPI_BUFLEN];
- struct spi_transfer t = {
- .tx_buf = buf,
- .len = AD1938_SPI_BUFLEN,
- };
- struct spi_message m;
-
- buf[0] = AD1938_SPI_ADDR << 1;
- buf[1] = reg;
- buf[2] = value;
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- ret = spi_sync(codec->control_data, &m);
- if (ret == 0)
- reg_cache[reg] = value;
- }
-
- return ret;
-}
-
-/*
- * read from the ad1938 register space cache
- */
-
-static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 *reg_cache = codec->reg_cache;
-
- if (reg >= codec->reg_cache_size)
- return -EINVAL;
-
- return reg_cache[reg];
-}
-
-/*
- * read from the ad1938 register space
- */
-
-static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- char w_buf[AD1938_SPI_BUFLEN];
- char r_buf[AD1938_SPI_BUFLEN];
- int ret;
-
- struct spi_transfer t = {
- .tx_buf = w_buf,
- .rx_buf = r_buf,
- .len = AD1938_SPI_BUFLEN,
- };
- struct spi_message m;
-
- w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
- w_buf[1] = reg;
- w_buf[2] = 0;
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- ret = spi_sync(codec->control_data, &m);
- if (ret == 0)
- return r_buf[2];
- else
- return -EIO;
-}
-
-static int ad1938_fill_cache(struct snd_soc_codec *codec)
-{
- int i;
- u8 *reg_cache = codec->reg_cache;
- struct spi_device *spi = codec->control_data;
-
- for (i = 0; i < codec->reg_cache_size; i++) {
- int ret = ad1938_read_reg(codec, i);
- if (ret == -EIO) {
- dev_err(&spi->dev, "AD1938 SPI read failure\n");
- return ret;
- }
- reg_cache[i] = ret;
- }
-
- return 0;
-}
-
-static int __devinit ad1938_spi_probe(struct spi_device *spi)
-{
- struct snd_soc_codec *codec;
- struct ad1938_priv *ad1938;
-
- ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL);
- if (ad1938 == NULL)
- return -ENOMEM;
-
- codec = &ad1938->codec;
- codec->control_data = spi;
- codec->dev = &spi->dev;
-
- dev_set_drvdata(&spi->dev, ad1938);
-
- return ad1938_register(ad1938);
-}
-
-static int __devexit ad1938_spi_remove(struct spi_device *spi)
-{
- struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev);
-
- ad1938_unregister(ad1938);
- return 0;
-}
-
-static struct spi_driver ad1938_spi_driver = {
- .driver = {
- .name = "ad1938",
- .owner = THIS_MODULE,
- },
- .probe = ad1938_spi_probe,
- .remove = __devexit_p(ad1938_spi_remove),
-};
-
-static struct snd_soc_dai_ops ad1938_dai_ops = {
- .hw_params = ad1938_hw_params,
- .digital_mute = ad1938_mute,
- .set_tdm_slot = ad1938_set_tdm_slot,
- .set_fmt = ad1938_set_dai_fmt,
-};
-
-/* codec DAI instance */
-struct snd_soc_dai ad1938_dai = {
- .name = "AD1938",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 4,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &ad1938_dai_ops,
-};
-EXPORT_SYMBOL_GPL(ad1938_dai);
-
-static int ad1938_register(struct ad1938_priv *ad1938)
-{
- int ret;
- struct snd_soc_codec *codec = &ad1938->codec;
-
- if (ad1938_codec) {
- dev_err(codec->dev, "Another ad1938 is registered\n");
- return -EINVAL;
- }
-
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = ad1938;
- codec->reg_cache = ad1938->reg_cache;
- codec->reg_cache_size = AD1938_NUM_REGS;
- codec->name = "AD1938";
- codec->owner = THIS_MODULE;
- codec->dai = &ad1938_dai;
- codec->num_dai = 1;
- codec->write = ad1938_write_reg;
- codec->read = ad1938_read_reg_cache;
- codec->set_bias_level = ad1938_set_bias_level;
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
-
- ad1938_dai.dev = codec->dev;
- ad1938_codec = codec;
-
- /* default setting for ad1938 */
-
- /* unmute dac channels */
- codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
- /* de-emphasis: 48kHz, powedown dac */
- codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
- /* powerdown dac, dac in tdm mode */
- codec->write(codec, AD1938_DAC_CTRL0, 0x41);
- /* high-pass filter enable */
- codec->write(codec, AD1938_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- codec->write(codec, AD1938_ADC_CTRL1, 0x43);
- /* pll input: mclki/xi */
- codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
- codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
-
- ad1938_fill_cache(codec);
-
- ret = snd_soc_register_codec(codec);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- kfree(ad1938);
- return ret;
- }
-
- ret = snd_soc_register_dai(&ad1938_dai);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
- snd_soc_unregister_codec(codec);
- kfree(ad1938);
- return ret;
- }
-
- return 0;
-}
-
-static void ad1938_unregister(struct ad1938_priv *ad1938)
-{
- ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
- snd_soc_unregister_dai(&ad1938_dai);
- snd_soc_unregister_codec(&ad1938->codec);
- kfree(ad1938);
- ad1938_codec = NULL;
-}
-
-static int ad1938_probe(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec;
- int ret = 0;
-
- if (ad1938_codec == NULL) {
- dev_err(&pdev->dev, "Codec device not registered\n");
- return -ENODEV;
- }
-
- socdev->card->codec = ad1938_codec;
- codec = ad1938_codec;
-
- /* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0) {
- dev_err(codec->dev, "failed to create pcms: %d\n", ret);
- goto pcm_err;
- }
-
- snd_soc_add_controls(codec, ad1938_snd_controls,
- ARRAY_SIZE(ad1938_snd_controls));
- snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
- ARRAY_SIZE(ad1938_dapm_widgets));
- snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
-
- ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-pcm_err:
- return ret;
-}
-
-/* power down chip */
-static int ad1938_remove(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int ad1938_suspend(struct platform_device *pdev,
- pm_message_t state)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
-
- ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
-static int ad1938_resume(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
-
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
- ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
-
- return 0;
-}
-#else
-#define ad1938_suspend NULL
-#define ad1938_resume NULL
-#endif
-
-struct snd_soc_codec_device soc_codec_dev_ad1938 = {
- .probe = ad1938_probe,
- .remove = ad1938_remove,
- .suspend = ad1938_suspend,
- .resume = ad1938_resume,
-};
-EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
-
-static int __init ad1938_init(void)
-{
- int ret;
-
- ret = spi_register_driver(&ad1938_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n",
- ret);
- }
-
- return ret;
-}
-module_init(ad1938_init);
-
-static void __exit ad1938_exit(void)
-{
- spi_unregister_driver(&ad1938_spi_driver);
-}
-module_exit(ad1938_exit);
-
-MODULE_DESCRIPTION("ASoC ad1938 driver");
-MODULE_AUTHOR("Barry Song ");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h
deleted file mode 100644
index fe3c48cd2d5b..000000000000
--- a/sound/soc/codecs/ad1938.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * File: sound/soc/codecs/ad1836.h
- * Based on:
- * Author: Barry Song <Barry.Song@analog.com>
- *
- * Created: May 25, 2009
- * Description: definitions for AD1938 registers
- *
- * Modified:
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __AD1938_H__
-#define __AD1938_H__
-
-#define AD1938_PLL_CLK_CTRL0 0
-#define AD1938_PLL_POWERDOWN 0x01
-#define AD1938_PLL_CLK_CTRL1 1
-#define AD1938_DAC_CTRL0 2
-#define AD1938_DAC_POWERDOWN 0x01
-#define AD1938_DAC_SERFMT_MASK 0xC0
-#define AD1938_DAC_SERFMT_STEREO (0 << 6)
-#define AD1938_DAC_SERFMT_TDM (1 << 6)
-#define AD1938_DAC_CTRL1 3
-#define AD1938_DAC_2_CHANNELS 0
-#define AD1938_DAC_4_CHANNELS 1
-#define AD1938_DAC_8_CHANNELS 2
-#define AD1938_DAC_16_CHANNELS 3
-#define AD1938_DAC_CHAN_SHFT 1
-#define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT)
-#define AD1938_DAC_LCR_MASTER (1 << 4)
-#define AD1938_DAC_BCLK_MASTER (1 << 5)
-#define AD1938_DAC_LEFT_HIGH (1 << 3)
-#define AD1938_DAC_BCLK_INV (1 << 7)
-#define AD1938_DAC_CTRL2 4
-#define AD1938_DAC_WORD_LEN_MASK 0xC
-#define AD1938_DAC_MASTER_MUTE 1
-#define AD1938_DAC_CHNL_MUTE 5
-#define AD1938_DACL1_MUTE 0
-#define AD1938_DACR1_MUTE 1
-#define AD1938_DACL2_MUTE 2
-#define AD1938_DACR2_MUTE 3
-#define AD1938_DACL3_MUTE 4
-#define AD1938_DACR3_MUTE 5
-#define AD1938_DACL4_MUTE 6
-#define AD1938_DACR4_MUTE 7
-#define AD1938_DAC_L1_VOL 6
-#define AD1938_DAC_R1_VOL 7
-#define AD1938_DAC_L2_VOL 8
-#define AD1938_DAC_R2_VOL 9
-#define AD1938_DAC_L3_VOL 10
-#define AD1938_DAC_R3_VOL 11
-#define AD1938_DAC_L4_VOL 12
-#define AD1938_DAC_R4_VOL 13
-#define AD1938_ADC_CTRL0 14
-#define AD1938_ADC_POWERDOWN 0x01
-#define AD1938_ADC_HIGHPASS_FILTER 1
-#define AD1938_ADCL1_MUTE 2
-#define AD1938_ADCR1_MUTE 3
-#define AD1938_ADCL2_MUTE 4
-#define AD1938_ADCR2_MUTE 5
-#define AD1938_ADC_CTRL1 15
-#define AD1938_ADC_SERFMT_MASK 0x60
-#define AD1938_ADC_SERFMT_STEREO (0 << 5)
-#define AD1938_ADC_SERFMT_TDM (1 << 2)
-#define AD1938_ADC_SERFMT_AUX (2 << 5)
-#define AD1938_ADC_WORD_LEN_MASK 0x3
-#define AD1938_ADC_CTRL2 16
-#define AD1938_ADC_2_CHANNELS 0
-#define AD1938_ADC_4_CHANNELS 1
-#define AD1938_ADC_8_CHANNELS 2
-#define AD1938_ADC_16_CHANNELS 3
-#define AD1938_ADC_CHAN_SHFT 4
-#define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT)
-#define AD1938_ADC_LCR_MASTER (1 << 3)
-#define AD1938_ADC_BCLK_MASTER (1 << 6)
-#define AD1938_ADC_LEFT_HIGH (1 << 2)
-#define AD1938_ADC_BCLK_INV (1 << 1)
-
-#define AD1938_NUM_REGS 17
-
-extern struct snd_soc_dai ad1938_dai;
-extern struct snd_soc_codec_device soc_codec_dev_ad1938;
-#endif
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
new file mode 100644
index 000000000000..c8ca1142b2f4
--- /dev/null
+++ b/sound/soc/codecs/ad193x.c
@@ -0,0 +1,547 @@
+/*
+ * AD193X Audio Codec driver supporting AD1936/7/8/9
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include "ad193x.h"
+
+/* codec private data */
+struct ad193x_priv {
+ struct snd_soc_codec codec;
+ u8 reg_cache[AD193X_NUM_REGS];
+};
+
+/* ad193x register cache & default register settings */
+static const u8 ad193x_reg[AD193X_NUM_REGS] = {
+ 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0,
+};
+
+static struct snd_soc_codec *ad193x_codec;
+struct snd_soc_codec_device soc_codec_dev_ad193x;
+
+/*
+ * AD193X volume/mute/de-emphasis etc. controls
+ */
+static const char *ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static const struct soc_enum ad193x_deemp_enum =
+ SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp);
+
+static const struct snd_kcontrol_new ad193x_snd_controls[] = {
+ /* DAC volume control */
+ SOC_DOUBLE_R("DAC1 Volume", AD193X_DAC_L1_VOL,
+ AD193X_DAC_R1_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC2 Volume", AD193X_DAC_L2_VOL,
+ AD193X_DAC_R2_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC3 Volume", AD193X_DAC_L3_VOL,
+ AD193X_DAC_R3_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC4 Volume", AD193X_DAC_L4_VOL,
+ AD193X_DAC_R4_VOL, 0, 0xFF, 1),
+
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+ AD193X_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+ AD193X_ADCR2_MUTE, 1, 1),
+
+ /* DAC switch control */
+ SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
+ AD193X_DACR1_MUTE, 1, 1),
+ SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE,
+ AD193X_DACR2_MUTE, 1, 1),
+ SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE,
+ AD193X_DACR3_MUTE, 1, 1),
+ SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
+ AD193X_DACR4_MUTE, 1, 1),
+
+ /* ADC high-pass filter */
+ SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
+ AD193X_ADC_HIGHPASS_FILTER, 1, 0),
+
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+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" },
+ { "DAC2OUT", "DAC2 Switch", "DAC" },
+ { "DAC3OUT", "DAC3 Switch", "DAC" },
+ { "DAC4OUT", "DAC4 Switch", "DAC" },
+ { "ADC", "ADC1 Switch", "ADC1IN" },
+ { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad193x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+
+ reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
+ reg = (mute > 0) ? reg | AD193X_DAC_MASTER_MUTE : reg &
+ (~AD193X_DAC_MASTER_MUTE);
+ snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
+
+ return 0;
+}
+
+static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1);
+ int adc_reg = snd_soc_read(codec, AD193X_ADC_CTRL2);
+
+ dac_reg &= ~AD193X_DAC_CHAN_MASK;
+ adc_reg &= ~AD193X_ADC_CHAN_MASK;
+
+ switch (slots) {
+ case 2:
+ dac_reg |= AD193X_DAC_2_CHANNELS << AD193X_DAC_CHAN_SHFT;
+ adc_reg |= AD193X_ADC_2_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ break;
+ case 4:
+ dac_reg |= AD193X_DAC_4_CHANNELS << AD193X_DAC_CHAN_SHFT;
+ adc_reg |= AD193X_ADC_4_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ break;
+ case 8:
+ dac_reg |= AD193X_DAC_8_CHANNELS << AD193X_DAC_CHAN_SHFT;
+ adc_reg |= AD193X_ADC_8_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ break;
+ case 16:
+ dac_reg |= AD193X_DAC_16_CHANNELS << AD193X_DAC_CHAN_SHFT;
+ adc_reg |= AD193X_ADC_16_CHANNELS << AD193X_ADC_CHAN_SHFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg);
+ snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg);
+
+ return 0;
+}
+
+static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int adc_reg1, adc_reg2, dac_reg;
+
+ adc_reg1 = snd_soc_read(codec, AD193X_ADC_CTRL1);
+ adc_reg2 = snd_soc_read(codec, AD193X_ADC_CTRL2);
+ dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1);
+
+ /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
+ * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adc_reg1 &= ~AD193X_ADC_SERFMT_MASK;
+ adc_reg1 |= AD193X_ADC_SERFMT_TDM;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_reg1 &= ~AD193X_ADC_SERFMT_MASK;
+ adc_reg1 |= AD193X_ADC_SERFMT_AUX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+ adc_reg2 &= ~AD193X_ADC_LEFT_HIGH;
+ adc_reg2 &= ~AD193X_ADC_BCLK_INV;
+ dac_reg &= ~AD193X_DAC_LEFT_HIGH;
+ dac_reg &= ~AD193X_DAC_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
+ adc_reg2 |= AD193X_ADC_LEFT_HIGH;
+ adc_reg2 &= ~AD193X_ADC_BCLK_INV;
+ dac_reg |= AD193X_DAC_LEFT_HIGH;
+ dac_reg &= ~AD193X_DAC_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
+ adc_reg2 &= ~AD193X_ADC_LEFT_HIGH;
+ adc_reg2 |= AD193X_ADC_BCLK_INV;
+ dac_reg &= ~AD193X_DAC_LEFT_HIGH;
+ dac_reg |= AD193X_DAC_BCLK_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
+ adc_reg2 |= AD193X_ADC_LEFT_HIGH;
+ adc_reg2 |= AD193X_ADC_BCLK_INV;
+ dac_reg |= AD193X_DAC_LEFT_HIGH;
+ dac_reg |= AD193X_DAC_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ adc_reg2 |= AD193X_ADC_LCR_MASTER;
+ adc_reg2 |= AD193X_ADC_BCLK_MASTER;
+ dac_reg |= AD193X_DAC_LCR_MASTER;
+ dac_reg |= AD193X_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+ adc_reg2 |= AD193X_ADC_LCR_MASTER;
+ adc_reg2 &= ~AD193X_ADC_BCLK_MASTER;
+ dac_reg |= AD193X_DAC_LCR_MASTER;
+ dac_reg &= ~AD193X_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ adc_reg2 &= ~AD193X_ADC_LCR_MASTER;
+ adc_reg2 |= AD193X_ADC_BCLK_MASTER;
+ dac_reg &= ~AD193X_DAC_LCR_MASTER;
+ dac_reg |= AD193X_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ adc_reg2 &= ~AD193X_ADC_LCR_MASTER;
+ adc_reg2 &= ~AD193X_ADC_BCLK_MASTER;
+ dac_reg &= ~AD193X_DAC_LCR_MASTER;
+ dac_reg &= ~AD193X_DAC_BCLK_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AD193X_ADC_CTRL1, adc_reg1);
+ snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg2);
+ snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg);
+
+ return 0;
+}
+
+static int ad193x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int word_len = 0, reg = 0;
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_len = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ word_len = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ word_len = 0;
+ break;
+ }
+
+ reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
+ reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len;
+ snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
+
+ reg = snd_soc_read(codec, AD193X_ADC_CTRL1);
+ reg = (reg & (~AD193X_ADC_WORD_LEN_MASK)) | word_len;
+ snd_soc_write(codec, AD193X_ADC_CTRL1, reg);
+
+ return 0;
+}
+
+static int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type)
+{
+ struct snd_soc_codec *codec;
+ struct ad193x_priv *ad193x;
+ int ret;
+
+ if (ad193x_codec) {
+ dev_err(dev, "Another ad193x is registered\n");
+ return -EINVAL;
+ }
+
+ ad193x = kzalloc(sizeof(struct ad193x_priv), GFP_KERNEL);
+ if (ad193x == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ad193x);
+
+ codec = &ad193x->codec;
+ mutex_init(&codec->mutex);
+ codec->control_data = ctrl_data;
+ codec->dev = dev;
+ snd_soc_codec_set_drvdata(codec, ad193x);
+ codec->reg_cache = ad193x->reg_cache;
+ codec->reg_cache_size = AD193X_NUM_REGS;
+ codec->name = "AD193X";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad193x_dai;
+ codec->num_dai = 1;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ad193x_dai.dev = codec->dev;
+ ad193x_codec = codec;
+
+ memcpy(codec->reg_cache, ad193x_reg, AD193X_NUM_REGS);
+
+ if (bus_type == SND_SOC_I2C)
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, bus_type);
+ else
+ ret = snd_soc_codec_set_cache_io(codec, 16, 8, bus_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to set cache I/O: %d\n",
+ ret);
+ kfree(ad193x);
+ return ret;
+ }
+
+ /* default setting for ad193x */
+
+ /* unmute dac channels */
+ snd_soc_write(codec, AD193X_DAC_CHNL_MUTE, 0x0);
+ /* de-emphasis: 48kHz, powedown dac */
+ snd_soc_write(codec, AD193X_DAC_CTRL2, 0x1A);
+ /* powerdown dac, dac in tdm mode */
+ snd_soc_write(codec, AD193X_DAC_CTRL0, 0x41);
+ /* high-pass filter enable */
+ snd_soc_write(codec, AD193X_ADC_CTRL0, 0x3);
+ /* sata delay=1, adc aux mode */
+ snd_soc_write(codec, AD193X_ADC_CTRL1, 0x43);
+ /* pll input: mclki/xi */
+ snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
+ snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ kfree(ad193x);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&ad193x_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ kfree(ad193x);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad193x_bus_remove(struct device *dev)
+{
+ struct ad193x_priv *ad193x = dev_get_drvdata(dev);
+
+ snd_soc_unregister_dai(&ad193x_dai);
+ snd_soc_unregister_codec(&ad193x->codec);
+ kfree(ad193x);
+ ad193x_codec = NULL;
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops ad193x_dai_ops = {
+ .hw_params = ad193x_hw_params,
+ .digital_mute = ad193x_mute,
+ .set_tdm_slot = ad193x_set_tdm_slot,
+ .set_fmt = ad193x_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad193x_dai = {
+ .name = "AD193X",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad193x_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad193x_dai);
+
+static int ad193x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (ad193x_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ad193x_codec;
+ codec = ad193x_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, ad193x_snd_controls,
+ ARRAY_SIZE(ad193x_snd_controls));
+ snd_soc_dapm_new_controls(codec, ad193x_dapm_widgets,
+ ARRAY_SIZE(ad193x_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int ad193x_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_ad193x = {
+ .probe = ad193x_probe,
+ .remove = ad193x_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad193x);
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit ad193x_spi_probe(struct spi_device *spi)
+{
+ return ad193x_bus_probe(&spi->dev, spi, SND_SOC_SPI);
+}
+
+static int __devexit ad193x_spi_remove(struct spi_device *spi)
+{
+ return ad193x_bus_remove(&spi->dev);
+}
+
+static struct spi_driver ad193x_spi_driver = {
+ .driver = {
+ .name = "ad193x",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad193x_spi_probe,
+ .remove = __devexit_p(ad193x_spi_remove),
+};
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static const struct i2c_device_id ad193x_id[] = {
+ { "ad1936", 0 },
+ { "ad1937", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad193x_id);
+
+static int __devinit ad193x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return ad193x_bus_probe(&client->dev, client, SND_SOC_I2C);
+}
+
+static int __devexit ad193x_i2c_remove(struct i2c_client *client)
+{
+ return ad193x_bus_remove(&client->dev);
+}
+
+static struct i2c_driver ad193x_i2c_driver = {
+ .driver = {
+ .name = "ad193x",
+ },
+ .probe = ad193x_i2c_probe,
+ .remove = __devexit_p(ad193x_i2c_remove),
+ .id_table = ad193x_id,
+};
+#endif
+
+static int __init ad193x_modinit(void)
+{
+ int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&ad193x_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n",
+ ret);
+ }
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&ad193x_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register AD193X SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(ad193x_modinit);
+
+static void __exit ad193x_modexit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&ad193x_spi_driver);
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&ad193x_i2c_driver);
+#endif
+}
+module_exit(ad193x_modexit);
+
+MODULE_DESCRIPTION("ASoC ad193x driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
new file mode 100644
index 000000000000..a03c880d52f9
--- /dev/null
+++ b/sound/soc/codecs/ad193x.h
@@ -0,0 +1,81 @@
+/*
+ * AD193X Audio Codec driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __AD193X_H__
+#define __AD193X_H__
+
+#define AD193X_PLL_CLK_CTRL0 0x800
+#define AD193X_PLL_POWERDOWN 0x01
+#define AD193X_PLL_CLK_CTRL1 0x801
+#define AD193X_DAC_CTRL0 0x802
+#define AD193X_DAC_POWERDOWN 0x01
+#define AD193X_DAC_SERFMT_MASK 0xC0
+#define AD193X_DAC_SERFMT_STEREO (0 << 6)
+#define AD193X_DAC_SERFMT_TDM (1 << 6)
+#define AD193X_DAC_CTRL1 0x803
+#define AD193X_DAC_2_CHANNELS 0
+#define AD193X_DAC_4_CHANNELS 1
+#define AD193X_DAC_8_CHANNELS 2
+#define AD193X_DAC_16_CHANNELS 3
+#define AD193X_DAC_CHAN_SHFT 1
+#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT)
+#define AD193X_DAC_LCR_MASTER (1 << 4)
+#define AD193X_DAC_BCLK_MASTER (1 << 5)
+#define AD193X_DAC_LEFT_HIGH (1 << 3)
+#define AD193X_DAC_BCLK_INV (1 << 7)
+#define AD193X_DAC_CTRL2 0x804
+#define AD193X_DAC_WORD_LEN_MASK 0xC
+#define AD193X_DAC_MASTER_MUTE 1
+#define AD193X_DAC_CHNL_MUTE 0x805
+#define AD193X_DACL1_MUTE 0
+#define AD193X_DACR1_MUTE 1
+#define AD193X_DACL2_MUTE 2
+#define AD193X_DACR2_MUTE 3
+#define AD193X_DACL3_MUTE 4
+#define AD193X_DACR3_MUTE 5
+#define AD193X_DACL4_MUTE 6
+#define AD193X_DACR4_MUTE 7
+#define AD193X_DAC_L1_VOL 0x806
+#define AD193X_DAC_R1_VOL 0x807
+#define AD193X_DAC_L2_VOL 0x808
+#define AD193X_DAC_R2_VOL 0x809
+#define AD193X_DAC_L3_VOL 0x80a
+#define AD193X_DAC_R3_VOL 0x80b
+#define AD193X_DAC_L4_VOL 0x80c
+#define AD193X_DAC_R4_VOL 0x80d
+#define AD193X_ADC_CTRL0 0x80e
+#define AD193X_ADC_POWERDOWN 0x01
+#define AD193X_ADC_HIGHPASS_FILTER 1
+#define AD193X_ADCL1_MUTE 2
+#define AD193X_ADCR1_MUTE 3
+#define AD193X_ADCL2_MUTE 4
+#define AD193X_ADCR2_MUTE 5
+#define AD193X_ADC_CTRL1 0x80f
+#define AD193X_ADC_SERFMT_MASK 0x60
+#define AD193X_ADC_SERFMT_STEREO (0 << 5)
+#define AD193X_ADC_SERFMT_TDM (1 << 2)
+#define AD193X_ADC_SERFMT_AUX (2 << 5)
+#define AD193X_ADC_WORD_LEN_MASK 0x3
+#define AD193X_ADC_CTRL2 0x810
+#define AD193X_ADC_2_CHANNELS 0
+#define AD193X_ADC_4_CHANNELS 1
+#define AD193X_ADC_8_CHANNELS 2
+#define AD193X_ADC_16_CHANNELS 3
+#define AD193X_ADC_CHAN_SHFT 4
+#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT)
+#define AD193X_ADC_LCR_MASTER (1 << 3)
+#define AD193X_ADC_BCLK_MASTER (1 << 6)
+#define AD193X_ADC_LEFT_HIGH (1 << 2)
+#define AD193X_ADC_BCLK_INV (1 << 1)
+
+#define AD193X_NUM_REGS 17
+
+extern struct snd_soc_dai ad193x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad193x;
+
+#endif
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 39c0f7584e65..042072738cdc 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -12,6 +12,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index d2fcc601722c..475807bea2c2 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -11,6 +11,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index cc96411ca3e6..f8e75edb27b7 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <sound/core.h>
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 3a14c6fc4f5e..192aebda3029 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -10,6 +10,7 @@
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
@@ -90,12 +91,10 @@ static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg >= codec->reg_cache_size)
return -EINVAL;
- reg &= AK4104_REG_MASK;
- reg |= AK4104_WRITE;
-
/* only write to the hardware if value has changed */
if (cache[reg] != value) {
- u8 tmp[2] = { reg, value };
+ u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value };
+
if (spi_write(spi, tmp, sizeof(tmp))) {
dev_err(&spi->dev, "SPI write failed\n");
return -EIO;
@@ -185,9 +184,7 @@ struct snd_soc_dai ak4104_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_32000,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
@@ -225,7 +222,7 @@ static int ak4104_spi_probe(struct spi_device *spi)
codec->owner = THIS_MODULE;
codec->dai = &ak4104_dai;
codec->num_dai = 1;
- codec->private_data = ak4104;
+ snd_soc_codec_set_drvdata(codec, ak4104);
codec->control_data = spi;
codec->reg_cache = ak4104->reg_cache;
codec->reg_cache_size = AK4104_NUM_REGS;
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index ff966567e2ba..d4253675b2d3 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -301,7 +302,7 @@ static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4535_priv *ak4535 = codec->private_data;
+ struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
ak4535->sysclk = freq;
return 0;
@@ -314,7 +315,7 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct ak4535_priv *ak4535 = codec->private_data;
+ struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
@@ -445,7 +446,6 @@ static int ak4535_resume(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->card->codec;
ak4535_sync(codec);
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ak4535_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -599,7 +599,7 @@ static int ak4535_probe(struct platform_device *pdev)
return -ENOMEM;
}
- codec->private_data = ak4535;
+ snd_soc_codec_set_drvdata(codec, ak4535);
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -616,7 +616,7 @@ static int ak4535_probe(struct platform_device *pdev)
#endif
if (ret != 0) {
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
}
return ret;
@@ -638,7 +638,7 @@ static int ak4535_remove(struct platform_device *pdev)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&ak4535_i2c_driver);
#endif
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 3ef16bbc8c83..7528a54102b5 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -29,6 +29,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -80,12 +81,39 @@
#define AK4642_CACHEREGNUM 0x25
+/* PW_MGMT2 */
+#define HPMTN (1 << 6)
+#define PMHPL (1 << 5)
+#define PMHPR (1 << 4)
+#define MS (1 << 3) /* master/slave select */
+#define MCKO (1 << 1)
+#define PMPLL (1 << 0)
+
+#define PMHP_MASK (PMHPL | PMHPR)
+#define PMHP PMHP_MASK
+
+/* MD_CTL1 */
+#define PLL3 (1 << 7)
+#define PLL2 (1 << 6)
+#define PLL1 (1 << 5)
+#define PLL0 (1 << 4)
+#define PLL_MASK (PLL3 | PLL2 | PLL1 | PLL0)
+
+#define BCKO_MASK (1 << 3)
+#define BCKO_64 BCKO_MASK
+
+/* MD_CTL2 */
+#define FS0 (1 << 0)
+#define FS1 (1 << 1)
+#define FS2 (1 << 2)
+#define FS3 (1 << 5)
+#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+
struct snd_soc_codec_device soc_codec_dev_ak4642;
/* codec private data */
struct ak4642_priv {
struct snd_soc_codec codec;
- unsigned int sysclk;
};
static struct snd_soc_codec *ak4642_codec;
@@ -176,17 +204,12 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
*
* PLL, Master Mode
* Audio I/F Format :MSB justified (ADC & DAC)
- * Sampling Frequency: 44.1kHz
- * Digital Volume: −8dB
+ * Digital Volume: -8dB
* Bass Boost Level : Middle
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
- *
- * Example code use 0x39, 0x79 value for 0x01 address,
- * But we need MCKO (0x02) bit now
*/
- ak4642_write(codec, 0x05, 0x27);
ak4642_write(codec, 0x0f, 0x09);
ak4642_write(codec, 0x0e, 0x19);
ak4642_write(codec, 0x09, 0x91);
@@ -194,15 +217,14 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
ak4642_write(codec, 0x0a, 0x28);
ak4642_write(codec, 0x0d, 0x28);
ak4642_write(codec, 0x00, 0x64);
- ak4642_write(codec, 0x01, 0x3b); /* + MCKO bit */
- ak4642_write(codec, 0x01, 0x7b); /* + MCKO bit */
+ snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP);
+ snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN);
} else {
/*
* start stereo input
*
* PLL Master Mode
* Audio I/F Format:MSB justified (ADC & DAC)
- * Sampling Frequency:44.1kHz
* Pre MIC AMP:+20dB
* MIC Power On
* ALC setting:Refer to Table 35
@@ -211,7 +233,6 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
- ak4642_write(codec, 0x05, 0x27);
ak4642_write(codec, 0x02, 0x05);
ak4642_write(codec, 0x06, 0x3c);
ak4642_write(codec, 0x08, 0xe1);
@@ -232,8 +253,8 @@ static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
if (is_play) {
/* stop headphone output */
- ak4642_write(codec, 0x01, 0x3b);
- ak4642_write(codec, 0x01, 0x0b);
+ snd_soc_update_bits(codec, PW_MGMT2, HPMTN, 0);
+ snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, 0);
ak4642_write(codec, 0x00, 0x40);
ak4642_write(codec, 0x0e, 0x11);
ak4642_write(codec, 0x0f, 0x08);
@@ -249,9 +270,111 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4642_priv *ak4642 = codec->private_data;
+ u8 pll;
+
+ switch (freq) {
+ case 11289600:
+ pll = PLL2;
+ break;
+ case 12288000:
+ pll = PLL2 | PLL0;
+ break;
+ case 12000000:
+ pll = PLL2 | PLL1;
+ break;
+ case 24000000:
+ pll = PLL2 | PLL1 | PLL0;
+ break;
+ case 13500000:
+ pll = PLL3 | PLL2;
+ break;
+ case 27000000:
+ pll = PLL3 | PLL2 | PLL0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
+
+ return 0;
+}
+
+static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 data;
+ u8 bcko;
+
+ data = MCKO | PMPLL; /* use MCKO */
+ bcko = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ data |= MS;
+ bcko = BCKO_64;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, PW_MGMT2, MS, data);
+ snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
+
+ return 0;
+}
+
+static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 rate;
+
+ switch (params_rate(params)) {
+ case 7350:
+ rate = FS2;
+ break;
+ case 8000:
+ rate = 0;
+ break;
+ case 11025:
+ rate = FS2 | FS0;
+ break;
+ case 12000:
+ rate = FS0;
+ break;
+ case 14700:
+ rate = FS2 | FS1;
+ break;
+ case 16000:
+ rate = FS1;
+ break;
+ case 22050:
+ rate = FS2 | FS1 | FS0;
+ break;
+ case 24000:
+ rate = FS1 | FS0;
+ break;
+ case 29400:
+ rate = FS3 | FS2 | FS1;
+ break;
+ case 32000:
+ rate = FS3 | FS1;
+ break;
+ case 44100:
+ rate = FS3 | FS2 | FS1 | FS0;
+ break;
+ case 48000:
+ rate = FS3 | FS1 | FS0;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
- ak4642->sysclk = freq;
return 0;
}
@@ -259,6 +382,8 @@ static struct snd_soc_dai_ops ak4642_dai_ops = {
.startup = ak4642_dai_startup,
.shutdown = ak4642_dai_shutdown,
.set_sysclk = ak4642_dai_set_sysclk,
+ .set_fmt = ak4642_dai_set_fmt,
+ .hw_params = ak4642_dai_hw_params,
};
struct snd_soc_dai ak4642_dai = {
@@ -276,6 +401,7 @@ struct snd_soc_dai ak4642_dai = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
+ .symmetric_rates = 1,
};
EXPORT_SYMBOL_GPL(ak4642_dai);
@@ -306,7 +432,7 @@ static int ak4642_init(struct ak4642_priv *ak4642)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = ak4642;
+ snd_soc_codec_set_drvdata(codec, ak4642);
codec->name = "AK4642";
codec->owner = THIS_MODULE;
codec->read = ak4642_read_reg_cache;
@@ -337,26 +463,6 @@ static int ak4642_init(struct ak4642_priv *ak4642)
goto reg_cache_err;
}
- /*
- * clock setting
- *
- * Audio I/F Format: MSB justified (ADC & DAC)
- * BICK frequency at Master Mode: 64fs
- * Input Master Clock Select at PLL Mode: 11.2896MHz
- * MCKO: Enable
- * Sampling Frequency: 44.1kHz
- *
- * This operation came from example code of
- * "ASAHI KASEI AK4642" (japanese) manual p89.
- *
- * please fix-me
- */
- ak4642_write(codec, 0x01, 0x08);
- ak4642_write(codec, 0x04, 0x4a);
- ak4642_write(codec, 0x05, 0x27);
- ak4642_write(codec, 0x00, 0x40);
- ak4642_write(codec, 0x01, 0x0b);
-
return ret;
reg_cache_err:
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 82fca284d007..87566932a3b1 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
@@ -701,7 +702,7 @@ static int ak4671_register(struct ak4671_priv *ak4671,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = ak4671;
+ snd_soc_codec_set_drvdata(codec, ak4671);
codec->name = "AK4671";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
new file mode 100644
index 000000000000..a320fb5a0e26
--- /dev/null
+++ b/sound/soc/codecs/cq93vc.c
@@ -0,0 +1,299 @@
+/*
+ * ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms
+ *
+ * Copyright (C) 2010 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/mfd/davinci_voicecodec.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/dm365.h>
+
+#include "cq93vc.h"
+
+static inline unsigned int cq93vc_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct davinci_vc *davinci_vc = codec->control_data;
+
+ return readl(davinci_vc->base + reg);
+}
+
+static inline int cq93vc_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct davinci_vc *davinci_vc = codec->control_data;
+
+ writel(value, davinci_vc->base + reg);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
+ SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0),
+ SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
+};
+
+static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = cq93vc_read(codec, DAVINCI_VC_REG09) & ~DAVINCI_VC_REG09_MUTE;
+
+ if (mute)
+ cq93vc_write(codec, DAVINCI_VC_REG09,
+ reg | DAVINCI_VC_REG09_MUTE);
+ else
+ cq93vc_write(codec, DAVINCI_VC_REG09, reg);
+
+ return 0;
+}
+
+static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct davinci_vc *davinci_vc = codec->control_data;
+
+ switch (freq) {
+ case 22579200:
+ case 27000000:
+ case 33868800:
+ davinci_vc->cq93vc.sysclk = freq;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ cq93vc_write(codec, DAVINCI_VC_REG12,
+ DAVINCI_VC_REG12_POWER_ALL_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ cq93vc_write(codec, DAVINCI_VC_REG12,
+ DAVINCI_VC_REG12_POWER_ALL_OFF);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* force all power off */
+ cq93vc_write(codec, DAVINCI_VC_REG12,
+ DAVINCI_VC_REG12_POWER_ALL_OFF);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+#define CQ93VC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_ops cq93vc_dai_ops = {
+ .digital_mute = cq93vc_mute,
+ .set_sysclk = cq93vc_set_dai_sysclk,
+};
+
+struct snd_soc_dai cq93vc_dai = {
+ .name = "CQ93VC",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CQ93VC_RATES,
+ .formats = CQ93VC_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CQ93VC_RATES,
+ .formats = CQ93VC_FORMATS,},
+ .ops = &cq93vc_dai_ops,
+};
+EXPORT_SYMBOL_GPL(cq93vc_dai);
+
+static int cq93vc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static struct snd_soc_codec *cq93vc_codec;
+
+static int cq93vc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct snd_soc_codec *codec;
+ int ret;
+
+ socdev->card->codec = cq93vc_codec;
+ codec = socdev->card->codec;
+
+ /* Register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to create pcms\n", pdev->name);
+ return ret;
+ }
+
+ /* Set controls */
+ snd_soc_add_controls(codec, cq93vc_snd_controls,
+ ARRAY_SIZE(cq93vc_snd_controls));
+
+ /* Off, with power on */
+ cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static int cq93vc_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_cq93vc = {
+ .probe = cq93vc_probe,
+ .remove = cq93vc_remove,
+ .resume = cq93vc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_cq93vc);
+
+static __init int cq93vc_codec_probe(struct platform_device *pdev)
+{
+ struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL) {
+ dev_dbg(davinci_vc->dev,
+ "could not allocate memory for codec data\n");
+ return -ENOMEM;
+ }
+
+ davinci_vc->cq93vc.codec = codec;
+
+ cq93vc_dai.dev = &pdev->dev;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->dev = &pdev->dev;
+ codec->name = "CQ93VC";
+ codec->owner = THIS_MODULE;
+ codec->read = cq93vc_read;
+ codec->write = cq93vc_write;
+ codec->set_bias_level = cq93vc_set_bias_level;
+ codec->dai = &cq93vc_dai;
+ codec->num_dai = 1;
+ codec->control_data = davinci_vc;
+
+ cq93vc_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret) {
+ dev_err(davinci_vc->dev, "failed to register codec\n");
+ goto fail1;
+ }
+
+ ret = snd_soc_register_dai(&cq93vc_dai);
+ if (ret) {
+ dev_err(davinci_vc->dev, "could register dai\n");
+ goto fail2;
+ }
+ return 0;
+
+fail2:
+ snd_soc_unregister_codec(codec);
+
+fail1:
+ kfree(codec);
+ cq93vc_codec = NULL;
+
+ return ret;
+}
+
+static int __devexit cq93vc_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ snd_soc_unregister_dai(&cq93vc_dai);
+ snd_soc_unregister_codec(&codec);
+
+ kfree(codec);
+ cq93vc_codec = NULL;
+
+ return 0;
+}
+
+static struct platform_driver cq93vc_codec_driver = {
+ .driver = {
+ .name = "cq93vc",
+ .owner = THIS_MODULE,
+ },
+ .probe = cq93vc_codec_probe,
+ .remove = __devexit_p(cq93vc_codec_remove),
+};
+
+static __init int cq93vc_init(void)
+{
+ return platform_driver_probe(&cq93vc_codec_driver, cq93vc_codec_probe);
+}
+module_init(cq93vc_init);
+
+static __exit void cq93vc_exit(void)
+{
+ platform_driver_unregister(&cq93vc_codec_driver);
+}
+module_exit(cq93vc_exit);
+
+MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC CQ0093 Voice Codec Driver");
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cq93vc.h b/sound/soc/codecs/cq93vc.h
new file mode 100644
index 000000000000..845b1968ef9c
--- /dev/null
+++ b/sound/soc/codecs/cq93vc.h
@@ -0,0 +1,29 @@
+/*
+ * ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms
+ *
+ * Copyright (C) 2010 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CQ93VC_H
+#define _CQ93VC_H
+
+extern struct snd_soc_dai cq93vc_dai;
+extern struct snd_soc_codec_device soc_codec_dev_cq93vc;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ffe122d1cd76..30d949239def 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -23,11 +23,13 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
#include "cs4270.h"
@@ -106,6 +108,10 @@
#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 +120,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)];
};
/**
@@ -192,12 +201,17 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* time for ALSA.
+ *
+ * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
+ * theoretically possible sample rates to be enabled. Call it again with a
+ * proper value set one the external clock is set (most probably you would do
+ * that from a machine's driver 'hw_param' hook.
*/
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = codec->private_data;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
unsigned int rates = 0;
unsigned int rate_min = -1;
unsigned int rate_max = 0;
@@ -205,20 +219,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
cs4270->mclk = freq;
- for (i = 0; i < NUM_MCLK_RATIOS; i++) {
- unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
- rates |= snd_pcm_rate_to_rate_bit(rate);
- if (rate < rate_min)
- rate_min = rate;
- if (rate > rate_max)
- rate_max = rate;
- }
- /* FIXME: soc should support a rate list */
- rates &= ~SNDRV_PCM_RATE_KNOT;
+ if (cs4270->mclk) {
+ for (i = 0; i < NUM_MCLK_RATIOS; i++) {
+ unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
+ rates |= snd_pcm_rate_to_rate_bit(rate);
+ if (rate < rate_min)
+ rate_min = rate;
+ if (rate > rate_max)
+ rate_max = rate;
+ }
+ /* FIXME: soc should support a rate list */
+ rates &= ~SNDRV_PCM_RATE_KNOT;
- if (!rates) {
- dev_err(codec->dev, "could not find a valid sample rate\n");
- return -EINVAL;
+ if (!rates) {
+ dev_err(codec->dev, "could not find a valid sample rate\n");
+ return -EINVAL;
+ }
+ } else {
+ /* enable all possible rates */
+ rates = SNDRV_PCM_RATE_8000_192000;
+ rate_min = 8000;
+ rate_max = 192000;
}
codec_dai->playback.rates = rates;
@@ -249,7 +270,7 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = codec->private_data;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
/* set DAI format */
@@ -391,7 +412,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct cs4270_private *cs4270 = codec->private_data;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
unsigned int rate;
@@ -470,7 +491,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = codec->private_data;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int reg6;
reg6 = snd_soc_read(codec, CS4270_MUTE);
@@ -503,7 +524,7 @@ static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs4270_private *cs4270 = codec->private_data;
+ struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int left = !ucontrol->value.integer.value[0];
int right = !ucontrol->value.integer.value[1];
@@ -579,7 +600,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 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
/* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
socdev->card->codec = codec;
@@ -599,8 +621,26 @@ 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;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret < 0)
+ goto error_free_regulators;
+
return 0;
+error_free_regulators:
+ regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+
error_free_pcms:
snd_soc_free_pcms(socdev);
@@ -616,8 +656,12 @@ 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 = snd_soc_codec_get_drvdata(codec);
snd_soc_free_pcms(socdev);
+ regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
+ regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
return 0;
};
@@ -686,7 +730,7 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
codec->owner = THIS_MODULE;
codec->dai = &cs4270_dai;
codec->num_dai = 1;
- codec->private_data = cs4270;
+ snd_soc_codec_set_drvdata(codec, cs4270);
codec->control_data = i2c_client;
codec->read = cs4270_read_reg_cache;
codec->write = cs4270_i2c_write;
@@ -799,17 +843,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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
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/cx20442.c b/sound/soc/codecs/cx20442.c
index e000cdfec1ec..f07a415c753f 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -14,6 +14,7 @@
*/
#include <linux/tty.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -386,7 +387,7 @@ static int cx20442_register(struct cx20442_priv *cx20442)
codec->name = "CX20442";
codec->owner = THIS_MODULE;
- codec->private_data = cx20442;
+ snd_soc_codec_set_drvdata(codec, cx20442);
codec->dai = &cx20442_dai;
codec->num_dai = 1;
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
new file mode 100644
index 000000000000..75af2d6e0e78
--- /dev/null
+++ b/sound/soc/codecs/da7210.c
@@ -0,0 +1,701 @@
+/*
+ * 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 <linux/slab.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_DIV1 0x29
+#define DA7210_PLL_DIV2 0x2A
+#define DA7210_PLL_DIV3 0x2B
+#define DA7210_PLL 0x2C
+#define DA7210_A_HID_UNLOCK 0x8A
+#define DA7210_A_TEST_UNLOCK 0x8B
+#define DA7210_A_PLL1 0x90
+#define DA7210_A_CP_MODE 0xA7
+
+/* 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 bit fields */
+#define DA7210_ADC_L_EN (1 << 3)
+#define DA7210_ADC_R_EN (1 << 7)
+
+/* DAC/ADC HPF fields */
+#define DA7210_VOICE_F0_MASK (0x7 << 4)
+#define DA7210_VOICE_F0_25 (1 << 4)
+#define DA7210_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_MASK (0xF << 0)
+#define DA7210_PLL_FS_8000 (0x1 << 0)
+#define DA7210_PLL_FS_11025 (0x2 << 0)
+#define DA7210_PLL_FS_12000 (0x3 << 0)
+#define DA7210_PLL_FS_16000 (0x5 << 0)
+#define DA7210_PLL_FS_22050 (0x6 << 0)
+#define DA7210_PLL_FS_24000 (0x7 << 0)
+#define DA7210_PLL_FS_32000 (0x9 << 0)
+#define DA7210_PLL_FS_44100 (0xA << 0)
+#define DA7210_PLL_FS_48000 (0xB << 0)
+#define DA7210_PLL_FS_88200 (0xE << 0)
+#define DA7210_PLL_FS_96000 (0xF << 0)
+#define DA7210_PLL_EN (0x1 << 7)
+
+#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 hpf_reg, hpf_mask, hpf_value;
+ u32 fs, bypass;
+
+ /* 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);
+
+ hpf_reg = (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) ?
+ DA7210_DAC_HPF : DA7210_ADC_HPF;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA7210_PLL_FS_8000;
+ hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
+ hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
+ bypass = DA7210_PLL_BYP;
+ break;
+ case 11025:
+ fs = DA7210_PLL_FS_11025;
+ hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
+ hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
+ bypass = 0;
+ break;
+ case 12000:
+ fs = DA7210_PLL_FS_12000;
+ hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
+ hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
+ bypass = DA7210_PLL_BYP;
+ break;
+ case 16000:
+ fs = DA7210_PLL_FS_16000;
+ hpf_mask = DA7210_VOICE_F0_MASK | DA7210_VOICE_EN;
+ hpf_value = DA7210_VOICE_F0_25 | DA7210_VOICE_EN;
+ bypass = DA7210_PLL_BYP;
+ break;
+ case 22050:
+ fs = DA7210_PLL_FS_22050;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = 0;
+ break;
+ case 32000:
+ fs = DA7210_PLL_FS_32000;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = DA7210_PLL_BYP;
+ break;
+ case 44100:
+ fs = DA7210_PLL_FS_44100;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = 0;
+ break;
+ case 48000:
+ fs = DA7210_PLL_FS_48000;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = DA7210_PLL_BYP;
+ break;
+ case 88200:
+ fs = DA7210_PLL_FS_88200;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = 0;
+ break;
+ case 96000:
+ fs = DA7210_PLL_FS_96000;
+ hpf_mask = DA7210_VOICE_EN;
+ hpf_value = 0;
+ bypass = DA7210_PLL_BYP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Disable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+
+ snd_soc_update_bits(codec, hpf_reg, hpf_mask, hpf_value);
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
+ snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, bypass);
+
+ /* Enable active mode */
+ snd_soc_update_bits(codec, DA7210_STARTUP1,
+ DA7210_SC_MST_EN, DA7210_SC_MST_EN);
+
+ 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,
+ .symmetric_rates = 1,
+};
+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);
+
+ snd_soc_codec_set_drvdata(codec, 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
+ * And below settings expects MCLK = 12.288MHz
+ *
+ * When you select different MCLK, please check...
+ * DA7210_PLL_DIV1 val
+ * DA7210_PLL_DIV2 val
+ * DA7210_PLL_DIV3 val
+ * DA7210_PLL_DIV3 :: DA7210_MCLK_RANGExxx
+ */
+
+ /*
+ * make sure that DA7210 use bypass mode before start up
+ */
+ da7210_write(codec, DA7210_STARTUP1, 0);
+ da7210_write(codec, DA7210_PLL_DIV3,
+ DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+
+ /*
+ * 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);
+
+ /*
+ * If 48kHz sound came, it use bypass mode,
+ * and when it is 44.1kHz, it use PLL.
+ *
+ * This time, this driver sets PLL always ON
+ * and controls bypass/PLL mode by switching
+ * DA7210_PLL_DIV3 :: DA7210_PLL_BYP bit.
+ * see da7210_hw_params
+ */
+ da7210_write(codec, DA7210_PLL_DIV1, 0xE5); /* MCLK = 12.288MHz */
+ da7210_write(codec, DA7210_PLL_DIV2, 0x99);
+ da7210_write(codec, DA7210_PLL_DIV3, 0x0A |
+ DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+ snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+
+ /* As suggested by Dialog */
+ da7210_write(codec, DA7210_A_HID_UNLOCK, 0x8B); /* unlock */
+ da7210_write(codec, DA7210_A_TEST_UNLOCK, 0xB4);
+ da7210_write(codec, DA7210_A_PLL1, 0x01);
+ da7210_write(codec, DA7210_A_CP_MODE, 0x7C);
+ da7210_write(codec, DA7210_A_HID_UNLOCK, 0x00); /* re-lock */
+ da7210_write(codec, DA7210_A_TEST_UNLOCK, 0x00);
+
+ /* 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 __devinit 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 __devexit 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
index 000000000000..390d621eb742
--- /dev/null
+++ b/sound/soc/codecs/da7210.h
@@ -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
+
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 2afcd0a8669d..5a5f187a2657 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/gpio.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index d2ff1cde6883..b47ed4f6ab20 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -139,6 +140,7 @@ SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
@@ -276,7 +278,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data;
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
int i = get_coeff(ssm2602->sysclk, params_rate(params));
@@ -321,7 +323,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@@ -372,7 +374,7 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
/* deactivate */
if (!codec->active)
@@ -400,7 +402,7 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
case 12000000:
@@ -558,7 +560,6 @@ static int ssm2602_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ssm2602_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -604,8 +605,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
/*select Line in as default input*/
- ssm2602_write(codec, SSM2602_APANA,
- APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC |
+ ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST);
ssm2602_write(codec, SSM2602_PWR, 0);
@@ -726,7 +726,7 @@ static int ssm2602_probe(struct platform_device *pdev)
return -ENOMEM;
}
- codec->private_data = ssm2602;
+ snd_soc_codec_set_drvdata(codec, ssm2602);
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -759,7 +759,7 @@ static int ssm2602_remove(struct platform_device *pdev)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&ssm2602_i2c_driver);
#endif
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 81b8c9dfe7fc..ee86568545c2 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -15,6 +15,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -288,9 +289,6 @@ reset:
}
stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
- stac9766_set_bias_level(codec, SND_SOC_BIAS_ON);
-
return 0;
}
@@ -409,7 +407,7 @@ reset_err:
pcm_err:
snd_soc_free_ac97_codec(codec);
codec_err:
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
cache_err:
kfree(socdev->card->codec);
socdev->card->codec = NULL;
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index a9dc5fb54774..b0bae3508b29 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -25,6 +25,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -627,13 +628,12 @@ static int tlv320aic23_resume(struct platform_device *pdev)
u16 reg;
/* Sync reg_cache with the hardware */
- for (reg = 0; reg < TLV320AIC23_RESET; reg++) {
+ for (reg = 0; reg <= TLV320AIC23_ACTIVE; reg++) {
u16 val = tlv320aic23_read_reg_cache(codec, reg);
tlv320aic23_write(codec, reg, val);
}
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- tlv320aic23_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 357b609196e3..f0e00fd4b435 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -48,7 +49,7 @@ struct aic26 {
static unsigned int aic26_reg_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
u16 cmd, value;
u8 buffer[2];
@@ -92,7 +93,7 @@ static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec,
static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
u16 cmd;
u8 buffer[4];
@@ -131,7 +132,7 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
int fsref, divisor, wlen, pval, jval, dval, qval;
u16 reg;
@@ -198,7 +199,7 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
static int aic26_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN);
dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
@@ -217,7 +218,7 @@ static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
" freq=%i, dir=%i)\n",
@@ -234,7 +235,7 @@ static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct aic26 *aic26 = codec->private_data;
+ struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
codec_dai, fmt);
@@ -430,7 +431,7 @@ static int aic26_spi_probe(struct spi_device *spi)
/* Setup what we can in the codec structure so that the register
* access functions will work as expected. More will be filled
* out when it is probed by the SoC CODEC part of this driver */
- aic26->codec.private_data = aic26;
+ snd_soc_codec_set_drvdata(&aic26->codec, aic26);
aic26->codec.name = "aic26";
aic26->codec.owner = THIS_MODULE;
aic26->codec.dai = &aic26_dai;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 2b4dc2b0b017..71a69908ccf6 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -38,7 +38,10 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -46,16 +49,25 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/tlv320aic3x.h>
#include "tlv320aic3x.h"
-#define AIC3X_VERSION "0.2"
+#define AIC3X_NUM_SUPPLIES 4
+static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
+ "IOVDD", /* I/O Voltage */
+ "DVDD", /* Digital Core Voltage */
+ "AVDD", /* Analog DAC Voltage */
+ "DRVDD", /* ADC Analog and Output Driver Voltage */
+};
/* codec private data */
struct aic3x_priv {
struct snd_soc_codec codec;
+ struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
unsigned int sysclk;
int master;
+ int gpio_reset;
};
/*
@@ -763,11 +775,12 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct aic3x_priv *aic3x = codec->private_data;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
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 +846,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);
@@ -907,7 +942,7 @@ static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct aic3x_priv *aic3x = codec->private_data;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
aic3x->sysclk = freq;
return 0;
@@ -917,7 +952,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct aic3x_priv *aic3x = codec->private_data;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
u8 iface_areg, iface_breg;
int delay = 0;
@@ -971,12 +1006,13 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int aic3x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct aic3x_priv *aic3x = codec->private_data;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
u8 reg;
switch (level) {
case SND_SOC_BIAS_ON:
- /* all power is driven by DAPM system */
+ break;
+ case SND_SOC_BIAS_PREPARE:
if (aic3x->master) {
/* enable pll */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
@@ -984,48 +1020,9 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
reg | PLL_ENABLE);
}
break;
- case SND_SOC_BIAS_PREPARE:
- break;
case SND_SOC_BIAS_STANDBY:
- /*
- * all power is driven by DAPM system,
- * so output power is safe if bypass was set
- */
- if (aic3x->master) {
- /* disable pll */
- reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
- aic3x_write(codec, AIC3X_PLL_PROGA_REG,
- reg & ~PLL_ENABLE);
- }
- break;
+ /* fall through and disable pll */
case SND_SOC_BIAS_OFF:
- /* force all power off */
- reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
- aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
- reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL);
- aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON);
-
- reg = aic3x_read_reg_cache(codec, DAC_PWR);
- aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON));
-
- reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
- aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON);
- reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
- aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON);
-
- reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
- aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON);
- reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
- aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON);
-
- reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
- aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON);
-
- reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
- aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON);
- reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
- aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON);
-
if (aic3x->master) {
/* disable pll */
reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
@@ -1147,7 +1144,7 @@ static int aic3x_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- aic3x_set_bias_level(codec, codec->suspend_bias_level);
+ aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -1285,6 +1282,13 @@ static int aic3x_unregister(struct aic3x_priv *aic3x)
snd_soc_unregister_dai(&aic3x_dai);
snd_soc_unregister_codec(&aic3x->codec);
+ if (aic3x->gpio_reset >= 0) {
+ gpio_set_value(aic3x->gpio_reset, 0);
+ gpio_free(aic3x->gpio_reset);
+ }
+ regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
+ regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
+
kfree(aic3x);
aic3x_codec = NULL;
@@ -1306,6 +1310,8 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
{
struct snd_soc_codec *codec;
struct aic3x_priv *aic3x;
+ struct aic3x_pdata *pdata = i2c->dev.platform_data;
+ int ret, i;
aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
if (aic3x == NULL) {
@@ -1315,13 +1321,53 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
codec = &aic3x->codec;
codec->dev = &i2c->dev;
- codec->private_data = aic3x;
+ snd_soc_codec_set_drvdata(codec, aic3x);
codec->control_data = i2c;
codec->hw_write = (hw_write_t) i2c_master_send;
i2c_set_clientdata(i2c, aic3x);
+ aic3x->gpio_reset = -1;
+ if (pdata && pdata->gpio_reset >= 0) {
+ ret = gpio_request(pdata->gpio_reset, "tlv320aic3x reset");
+ if (ret != 0)
+ goto err_gpio;
+ aic3x->gpio_reset = pdata->gpio_reset;
+ gpio_direction_output(aic3x->gpio_reset, 0);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
+ aic3x->supplies[i].supply = aic3x_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
+ aic3x->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_enable;
+ }
+
+ if (aic3x->gpio_reset >= 0) {
+ udelay(1);
+ gpio_set_value(aic3x->gpio_reset, 1);
+ }
+
return aic3x_register(codec);
+
+err_enable:
+ regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
+err_get:
+ if (aic3x->gpio_reset >= 0)
+ gpio_free(aic3x->gpio_reset);
+err_gpio:
+ kfree(aic3x);
+ return ret;
}
static int aic3x_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 9c8903dbe647..65adc77eada1 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -30,6 +30,8 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -49,6 +51,20 @@
#define LATENCY_TIME_MS 20
+#define MODE7_LTHR 10
+#define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10)
+
+#define BURST_BASEFREQ_HZ 49152000
+
+#define SAMPLES_TO_US(rate, samples) \
+ (1000000000 / ((rate * 1000) / samples))
+
+#define US_TO_SAMPLES(rate, us) \
+ (rate / (1000000 / us))
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream);
+static int dac33_prepare_chip(struct snd_pcm_substream *substream);
+
static struct snd_soc_codec *tlv320dac33_codec;
enum dac33_state {
@@ -58,11 +74,27 @@ 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];
+ struct snd_pcm_substream *substream;
int power_gpio;
int chip_power;
int irq;
@@ -73,8 +105,20 @@ 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 */
+ u8 burst_bclkdiv; /* BCLK divider value in burst mode */
+ unsigned int burst_rate; /* Interface speed in Burst modes */
+
+ int keep_bclk; /* Keep the BCLK continuously running
+ * in FIFO modes */
+ spinlock_t lock;
+ unsigned long long t_stamp1; /* Time stamp for FIFO modes to */
+ unsigned long long t_stamp2; /* calculate the FIFO caused delay */
+
+ unsigned int mode1_us_burst; /* Time to burst read n number of
+ * samples */
+ unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */
enum dac33_state state;
};
@@ -148,7 +192,7 @@ static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
u8 *value)
{
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int val;
*value = reg & 0xff;
@@ -173,7 +217,7 @@ static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
u8 data[2];
int ret = 0;
@@ -200,7 +244,7 @@ static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret;
mutex_lock(&dac33->mutex);
@@ -214,7 +258,7 @@ static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
u8 data[3];
int ret = 0;
@@ -244,45 +288,47 @@ static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
return ret;
}
-static void dac33_restore_regs(struct snd_soc_codec *codec)
+static void dac33_init_chip(struct snd_soc_codec *codec)
{
- struct tlv320dac33_priv *dac33 = codec->private_data;
- u8 *cache = codec->reg_cache;
- u8 data[2];
- int i, ret;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
- if (!dac33->chip_power)
+ if (unlikely(!dac33->chip_power))
return;
- for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
- data[0] = i;
- data[1] = cache[i];
- /* Skip the read only registers */
- if ((i >= DAC33_INT_OSC_STATUS &&
- i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
- (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
- i == DAC33_DAC_STATUS_FLAGS ||
- i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
- i == DAC33_SRC_EST_REF_CLK_RATIO_B)
- continue;
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret != 2)
- dev_err(codec->dev, "Write failed (%d)\n", ret);
- }
- for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
- data[0] = i;
- data[1] = cache[i];
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret != 2)
- dev_err(codec->dev, "Write failed (%d)\n", ret);
- }
- for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
- data[0] = i;
- data[1] = cache[i];
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret != 2)
- dev_err(codec->dev, "Write failed (%d)\n", ret);
- }
+ /* 44-46: DAC Control Registers */
+ /* A : DAC sample rate Fsref/1.5 */
+ dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
+ /* B : DAC src=normal, not muted */
+ dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+ DAC33_DACSRCL_LEFT);
+ /* C : (defaults) */
+ dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+ /* 73 : volume soft stepping control,
+ clock source = internal osc (?) */
+ dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+ dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
+
+ /* Restore only selected registers (gains mostly) */
+ dac33_write(codec, DAC33_LDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(codec, DAC33_LDAC_DIG_VOL_CTRL));
+ dac33_write(codec, DAC33_RDAC_DIG_VOL_CTRL,
+ dac33_read_reg_cache(codec, DAC33_RDAC_DIG_VOL_CTRL));
+
+ dac33_write(codec, DAC33_LINEL_TO_LLO_VOL,
+ dac33_read_reg_cache(codec, DAC33_LINEL_TO_LLO_VOL));
+ dac33_write(codec, DAC33_LINER_TO_RLO_VOL,
+ dac33_read_reg_cache(codec, DAC33_LINER_TO_RLO_VOL));
+}
+
+static inline void dac33_read_id(struct snd_soc_codec *codec)
+{
+ u8 reg;
+
+ dac33_read(codec, DAC33_DEVICE_ID_MSB, &reg);
+ dac33_read(codec, DAC33_DEVICE_ID_LSB, &reg);
+ dac33_read(codec, DAC33_DEVICE_REV_ID, &reg);
}
static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
@@ -293,39 +339,80 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
if (power)
reg |= DAC33_PDNALLB;
else
- reg &= ~DAC33_PDNALLB;
+ reg &= ~(DAC33_PDNALLB | DAC33_OSCPDNB |
+ DAC33_DACRPDNB | DAC33_DACLPDNB);
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;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
mutex_lock(&dac33->mutex);
+
+ /* Safety check */
+ if (unlikely(power == dac33->chip_power)) {
+ dev_dbg(codec->dev, "Trying to set the same power state: %s\n",
+ power ? "ON" : "OFF");
+ goto exit;
+ }
+
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;
}
- dac33_soft_power(codec, 1);
+
+ if (dac33->power_gpio >= 0)
+ gpio_set_value(dac33->power_gpio, 1);
+
+ dac33->chip_power = 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;
}
+
+exit:
mutex_unlock(&dac33->mutex);
+ return ret;
+}
+
+static int playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(w->codec);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (likely(dac33->substream)) {
+ dac33_calculate_times(dac33->substream);
+ dac33_prepare_chip(dac33->substream);
+ }
+ break;
+ }
+ return 0;
}
static int dac33_get_nsample(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;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = dac33->nsample;
@@ -336,54 +423,67 @@ static int dac33_set_nsample(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;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (dac33->nsample == ucontrol->value.integer.value[0])
return 0;
if (ucontrol->value.integer.value[0] < dac33->nsample_min ||
- ucontrol->value.integer.value[0] > dac33->nsample_max)
+ ucontrol->value.integer.value[0] > dac33->nsample_max) {
ret = -EINVAL;
- else
+ } else {
dac33->nsample = ucontrol->value.integer.value[0];
+ /* Re calculate the burst time */
+ dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate,
+ dac33->nsample);
+ }
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;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
- 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;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
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 +506,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 */
@@ -437,6 +537,8 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+
+ SND_SOC_DAPM_PRE("Prepare Playback", playback_event),
};
static const struct snd_soc_dapm_route audio_map[] = {
@@ -469,6 +571,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 +580,22 @@ 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);
- dac33_soft_power(codec, 0);
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Coming from OFF, switch on the codec */
+ ret = dac33_hard_power(codec, 1);
+ if (ret != 0)
+ return ret;
+
+ dac33_init_chip(codec);
+ }
break;
case SND_SOC_BIAS_OFF:
- dac33_hard_power(codec, 0);
+ /* Do not power off, when the codec is already off */
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ return 0;
+ ret = dac33_hard_power(codec, 0);
+ if (ret != 0)
+ return ret;
break;
}
codec->bias_level = level;
@@ -489,6 +603,77 @@ 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->alarm_threshold));
+
+ /* Take the timestamps */
+ spin_lock_irq(&dac33->lock);
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
+ dac33->t_stamp1 = dac33->t_stamp2;
+ spin_unlock_irq(&dac33->lock);
+
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ /* Enable Alarm Threshold IRQ with a delay */
+ udelay(SAMPLES_TO_US(dac33->burst_rate,
+ dac33->alarm_threshold));
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+ break;
+ case DAC33_FIFO_MODE7:
+ /* Take the timestamp */
+ spin_lock_irq(&dac33->lock);
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
+ /* Move back the timestamp with drain time */
+ dac33->t_stamp1 -= dac33->mode7_us_to_lthr;
+ spin_unlock_irq(&dac33->lock);
+
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(MODE7_LTHR));
+
+ /* Enable Upper Threshold IRQ */
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MUT);
+ 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:
+ /* Take the timestamp */
+ spin_lock_irq(&dac33->lock);
+ dac33->t_stamp2 = ktime_to_us(ktime_get());
+ spin_unlock_irq(&dac33->lock);
+
+ 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 +687,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;
@@ -530,31 +711,17 @@ static void dac33_work(struct work_struct *work)
static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
{
struct snd_soc_codec *codec = dev;
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
- queue_work(dac33->dac33_wq, &dac33->work);
+ spin_lock(&dac33->lock);
+ dac33->t_stamp1 = ktime_to_us(ktime_get());
+ spin_unlock(&dac33->lock);
- return IRQ_HANDLED;
-}
+ /* Do not schedule the workqueue in Mode7 */
+ if (dac33->fifo_mode != DAC33_FIFO_MODE7)
+ queue_work(dac33->dac33_wq, &dac33->work);
-static void dac33_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- struct tlv320dac33_priv *dac33 = codec->private_data;
- unsigned int pwr_ctrl;
-
- /* Stop pending workqueue */
- if (dac33->nsample_switch)
- cancel_work_sync(&dac33->work);
-
- mutex_lock(&dac33->mutex);
- pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
- pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
- dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
- mutex_unlock(&dac33->mutex);
+ return IRQ_HANDLED;
}
static void dac33_oscwait(struct snd_soc_codec *codec)
@@ -571,6 +738,31 @@ static void dac33_oscwait(struct snd_soc_codec *codec)
"internal oscillator calibration failed\n");
}
+static int dac33_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ /* Stream started, save the substream pointer */
+ dac33->substream = substream;
+
+ return 0;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+ dac33->substream = NULL;
+}
+
static int dac33_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -603,7 +795,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
}
#define CALC_OSCSET(rate, refclk) ( \
- ((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+ ((((rate * 10000) / refclk) * 4096) + 7000) / 10000)
#define CALC_RATIOSET(rate, refclk) ( \
((((refclk * 100000) / rate) * 16384) + 50000) / 100000)
@@ -617,9 +809,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
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:
@@ -637,7 +829,10 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+ /* Read FIFO control A, and clear FIFO flush bit */
fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ fifoctrl_a &= ~DAC33_FIFOFLUSH;
+
fifoctrl_a &= ~DAC33_WIDTH;
switch (substream->runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -651,6 +846,17 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
}
mutex_lock(&dac33->mutex);
+
+ if (!dac33->chip_power) {
+ /*
+ * Chip is not powered yet.
+ * Do the init in the dac33_set_bias_level later.
+ */
+ mutex_unlock(&dac33->mutex);
+ return 0;
+ }
+
+ dac33_soft_power(codec, 0);
dac33_soft_power(codec, 1);
reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
@@ -675,9 +881,10 @@ 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_A, DAC33_SRCLKDIV(1));
dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
/* Write registers 0x34 and 0x35 (MSB, LSB) */
@@ -685,38 +892,104 @@ 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));
+ break;
+ case DAC33_FIFO_MODE7:
+ dac33_write(codec, DAC33_FIFO_IRQ_MODE_A,
+ DAC33_UTM(DAC33_FIFO_IRQ_MODE_LEVEL));
+ 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;
+ if (dac33->keep_bclk)
+ aictrl_b |= DAC33_BCLKON;
+ else
+ 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;
+ if (dac33->keep_bclk)
+ aictrl_b |= DAC33_BCLKON;
+ else
+ 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) {
- /* 20: BCLK divide ratio */
- dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
+ /*
+ * BCLK divide ratio
+ * 0: 1.5
+ * 1: 1
+ * 2: 2
+ * ...
+ * 254: 254
+ * 255: 255
+ */
+ if (dac33->fifo_mode)
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C,
+ dac33->burst_bclkdiv);
+ else
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
dac33_write16(codec, DAC33_ATHR_MSB,
DAC33_THRREG(dac33->alarm_threshold));
- } else {
- dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+ 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(MODE7_UTHR));
+ dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR));
+ break;
+ default:
+ break;
}
mutex_unlock(&dac33->mutex);
@@ -729,9 +1002,13 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned int nsample_limit;
+ /* In bypass mode we don't need to calculate */
+ if (!dac33->fifo_mode)
+ return;
+
/* Number of samples (16bit, stereo) in one period */
dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
@@ -765,15 +1042,24 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream)
if (dac33->nsample > dac33->nsample_max)
dac33->nsample = dac33->nsample_max;
-}
-static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- dac33_calculate_times(substream);
- dac33_prepare_chip(substream);
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_MODE1:
+ dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate,
+ dac33->nsample);
+ dac33->t_stamp1 = 0;
+ dac33->t_stamp2 = 0;
+ break;
+ case DAC33_FIFO_MODE7:
+ dac33->mode7_us_to_lthr =
+ SAMPLES_TO_US(substream->runtime->rate,
+ MODE7_UTHR - MODE7_LTHR + 1);
+ dac33->t_stamp1 = 0;
+ break;
+ default:
+ break;
+ }
- return 0;
}
static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -782,14 +1068,14 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
switch (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 +1083,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);
}
@@ -809,11 +1095,156 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
+static snd_pcm_sframes_t dac33_dai_delay(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+ unsigned long long t0, t1, t_now;
+ unsigned int time_delta;
+ int samples_out, samples_in, samples;
+ snd_pcm_sframes_t delay = 0;
+
+ switch (dac33->fifo_mode) {
+ case DAC33_FIFO_BYPASS:
+ break;
+ case DAC33_FIFO_MODE1:
+ spin_lock(&dac33->lock);
+ t0 = dac33->t_stamp1;
+ t1 = dac33->t_stamp2;
+ spin_unlock(&dac33->lock);
+ t_now = ktime_to_us(ktime_get());
+
+ /* We have not started to fill the FIFO yet, delay is 0 */
+ if (!t1)
+ goto out;
+
+ if (t0 > t1) {
+ /*
+ * Phase 1:
+ * After Alarm threshold, and before nSample write
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ if (likely(dac33->alarm_threshold > samples_out))
+ delay = dac33->alarm_threshold - samples_out;
+ else
+ delay = 0;
+ } else if ((t_now - t1) <= dac33->mode1_us_burst) {
+ /*
+ * Phase 2:
+ * After nSample write (during burst operation)
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ time_delta = t_now - t1;
+ samples_in = time_delta ? US_TO_SAMPLES(
+ dac33->burst_rate,
+ time_delta) : 0;
+
+ samples = dac33->alarm_threshold;
+ samples += (samples_in - samples_out);
+
+ if (likely(samples > 0))
+ delay = samples;
+ else
+ delay = 0;
+ } else {
+ /*
+ * Phase 3:
+ * After burst operation, before next alarm threshold
+ */
+ time_delta = t_now - t0;
+ samples_out = time_delta ? US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta) : 0;
+
+ samples_in = dac33->nsample;
+ samples = dac33->alarm_threshold;
+ samples += (samples_in - samples_out);
+
+ if (likely(samples > 0))
+ delay = samples > DAC33_BUFFER_SIZE_SAMPLES ?
+ DAC33_BUFFER_SIZE_SAMPLES : samples;
+ else
+ delay = 0;
+ }
+ break;
+ case DAC33_FIFO_MODE7:
+ spin_lock(&dac33->lock);
+ t0 = dac33->t_stamp1;
+ spin_unlock(&dac33->lock);
+ t_now = ktime_to_us(ktime_get());
+
+ /* We have not started to fill the FIFO yet, delay is 0 */
+ if (!t0)
+ goto out;
+
+ if (t_now <= t0) {
+ /*
+ * Either the timestamps are messed or equal. Report
+ * maximum delay
+ */
+ delay = MODE7_UTHR;
+ goto out;
+ }
+
+ time_delta = t_now - t0;
+ if (time_delta <= dac33->mode7_us_to_lthr) {
+ /*
+ * Phase 1:
+ * After burst (draining phase)
+ */
+ samples_out = US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta);
+
+ if (likely(MODE7_UTHR > samples_out))
+ delay = MODE7_UTHR - samples_out;
+ else
+ delay = 0;
+ } else {
+ /*
+ * Phase 2:
+ * During burst operation
+ */
+ time_delta = time_delta - dac33->mode7_us_to_lthr;
+
+ samples_out = US_TO_SAMPLES(
+ substream->runtime->rate,
+ time_delta);
+ samples_in = US_TO_SAMPLES(
+ dac33->burst_rate,
+ time_delta);
+ delay = MODE7_LTHR + samples_in - samples_out;
+
+ if (unlikely(delay > MODE7_UTHR))
+ delay = MODE7_UTHR;
+ }
+ break;
+ default:
+ dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+ dac33->fifo_mode);
+ break;
+ }
+out:
+ return delay;
+}
+
static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct tlv320dac33_priv *dac33 = codec->private_data;
+ struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
u8 ioc_reg, asrcb_reg;
ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
@@ -843,6 +1274,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 = snd_soc_codec_get_drvdata(codec);
u8 aictrl_a, aictrl_b;
aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
@@ -855,7 +1287,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;
@@ -869,11 +1305,7 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
case SND_SOC_DAIFMT_DSP_A:
aictrl_a |= DAC33_AFMT_DSP;
aictrl_b &= ~DAC33_DATA_DELAY_MASK;
- aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */
- break;
- case SND_SOC_DAIFMT_DSP_B:
- aictrl_a |= DAC33_AFMT_DSP;
- aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */
+ aictrl_b |= DAC33_DATA_DELAY(0);
break;
case SND_SOC_DAIFMT_RIGHT_J:
aictrl_a |= DAC33_AFMT_RIGHT_J;
@@ -893,35 +1325,6 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static void dac33_init_chip(struct snd_soc_codec *codec)
-{
- /* 44-46: DAC Control Registers */
- /* A : DAC sample rate Fsref/1.5 */
- dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1));
- /* B : DAC src=normal, not muted */
- dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
- DAC33_DACSRCL_LEFT);
- /* C : (defaults) */
- dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
-
- /* 64-65 : L&R DAC power control
- Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
- dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
- dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
-
- /* 73 : volume soft stepping control,
- clock source = internal osc (?) */
- dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
-
- /* 66 : LOP/LOM Modes */
- dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
-
- /* 68 : LOM inverted from LOP */
- dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
-
- dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
-}
-
static int dac33_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -933,12 +1336,7 @@ static int dac33_soc_probe(struct platform_device *pdev)
codec = tlv320dac33_codec;
socdev->card->codec = codec;
- dac33 = codec->private_data;
-
- /* Power up the codec */
- dac33_hard_power(codec, 1);
- /* Set default configuration */
- dac33_init_chip(codec);
+ dac33 = snd_soc_codec_get_drvdata(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -956,9 +1354,6 @@ static int dac33_soc_probe(struct platform_device *pdev)
dac33_add_widgets(codec);
- /* power on device */
- dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
return 0;
pcm_err:
@@ -995,7 +1390,6 @@ static int dac33_soc_resume(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->card->codec;
dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- dac33_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -1013,10 +1407,11 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static struct snd_soc_dai_ops dac33_dai_ops = {
+ .startup = dac33_startup,
.shutdown = dac33_shutdown,
.hw_params = dac33_hw_params,
- .prepare = dac33_pcm_prepare,
.trigger = dac33_pcm_trigger,
+ .delay = dac33_dai_delay,
.set_sysclk = dac33_set_dai_sysclk,
.set_fmt = dac33_set_dai_fmt,
};
@@ -1033,13 +1428,13 @@ struct snd_soc_dai dac33_dai = {
};
EXPORT_SYMBOL_GPL(dac33_dai);
-static int dac33_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int __devinit dac33_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
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");
@@ -1052,11 +1447,12 @@ static int dac33_i2c_probe(struct i2c_client *client,
return -ENOMEM;
codec = &dac33->codec;
- codec->private_data = dac33;
+ snd_soc_codec_set_drvdata(codec, dac33);
codec->control_data = client;
mutex_init(&codec->mutex);
mutex_init(&dac33->mutex);
+ spin_lock_init(&dac33->lock);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1067,6 +1463,7 @@ static int dac33_i2c_probe(struct i2c_client *client,
codec->hw_write = (hw_write_t) i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = dac33_set_bias_level;
+ codec->idle_bias_off = 1;
codec->dai = &dac33_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
@@ -1080,10 +1477,15 @@ static int dac33_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, dac33);
dac33->power_gpio = pdata->power_gpio;
+ dac33->burst_bclkdiv = pdata->burst_bclkdiv;
+ /* Pre calculate the burst rate */
+ dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32;
+ dac33->keep_bclk = pdata->keep_bclk;
dac33->irq = client->irq;
dac33->nsample = NSAMPLE_MAX;
+ dac33->nsample_max = NSAMPLE_MAX;
/* Disable FIFO use by default */
- dac33->nsample_switch = 0;
+ dac33->fifo_mode = DAC33_FIFO_BYPASS;
tlv320dac33_codec = codec;
@@ -1102,8 +1504,6 @@ static int dac33_i2c_probe(struct i2c_client *client,
goto error_gpio;
}
gpio_direction_output(dac33->power_gpio, 0);
- } else {
- dac33->chip_power = 1;
}
/* Check if the IRQ number is valid and request it */
@@ -1130,6 +1530,26 @@ 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;
+ }
+
+ /* Read the tlv320dac33 ID registers */
+ ret = dac33_hard_power(codec, 1);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to power up codec: %d\n", ret);
+ goto error_codec;
+ }
+ dac33_read_id(codec);
+ dac33_hard_power(codec, 0);
+
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
@@ -1143,12 +1563,11 @@ static int dac33_i2c_probe(struct i2c_client *client,
goto error_codec;
}
- /* Shut down the codec for now */
- dac33_hard_power(codec, 0);
-
return ret;
error_codec:
+ 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);
@@ -1165,18 +1584,22 @@ error_reg:
return ret;
}
-static int dac33_i2c_remove(struct i2c_client *client)
+static int __devexit dac33_i2c_remove(struct i2c_client *client)
{
struct tlv320dac33_priv *dac33;
dac33 = i2c_get_clientdata(client);
- dac33_hard_power(&dac33->codec, 0);
+
+ if (unlikely(dac33->chip_power))
+ dac33_hard_power(&dac33->codec, 0);
if (dac33->power_gpio >= 0)
gpio_free(dac33->power_gpio);
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);
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 6b650c1aa3d1..99b70e5978a2 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -25,6 +25,8 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/tpa6130a2-plat.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -38,8 +40,10 @@ static struct i2c_client *tpa6130a2_client;
struct tpa6130a2_data {
struct mutex mutex;
unsigned char regs[TPA6130A2_CACHEREGNUM];
+ struct regulator *supply;
int power_gpio;
unsigned char power_state;
+ enum tpa_model id;
};
static int tpa6130a2_i2c_read(int reg)
@@ -106,10 +110,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 +122,19 @@ 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_enable(data->supply);
+ if (ret != 0) {
+ dev_err(&tpa6130a2_client->dev,
+ "Failed to enable supply: %d\n", ret);
+ goto exit;
}
+
+ data->power_state = 1;
+ tpa6130a2_initialize();
+
/* Clear SWS */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val &= ~TPA6130A2_SWS;
@@ -131,16 +144,27 @@ 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_disable(data->supply);
+ if (ret != 0) {
+ dev_err(&tpa6130a2_client->dev,
+ "Failed to disable supply: %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,
+static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
@@ -148,7 +172,8 @@ static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
struct tpa6130a2_data *data;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
BUG_ON(tpa6130a2_client == NULL);
@@ -161,13 +186,13 @@ static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
if (invert)
ucontrol->value.integer.value[0] =
- mask - ucontrol->value.integer.value[0];
+ max - ucontrol->value.integer.value[0];
mutex_unlock(&data->mutex);
return 0;
}
-static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
+static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
@@ -175,7 +200,8 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
struct tpa6130a2_data *data;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val = (ucontrol->value.integer.value[0] & mask);
unsigned int val_reg;
@@ -184,7 +210,7 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
data = i2c_get_clientdata(tpa6130a2_client);
if (invert)
- val = mask - val;
+ val = max - val;
mutex_lock(&data->mutex);
@@ -224,10 +250,24 @@ static const unsigned int tpa6130_tlv[] = {
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
- tpa6130a2_get_reg, tpa6130a2_set_reg,
+ tpa6130a2_get_volsw, tpa6130a2_put_volsw,
tpa6130_tlv),
};
+static const unsigned int tpa6140_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
+ 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0),
+};
+
+static const struct snd_kcontrol_new tpa6140a2_controls[] = {
+ SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
+ tpa6130a2_get_volsw, tpa6130a2_put_volsw,
+ tpa6140_tlv),
+};
+
/*
* Enable or disable channel (left or right)
* The bit number for mute and amplifier are the same per channel:
@@ -237,12 +277,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 +335,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[] = {
@@ -321,8 +359,8 @@ static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
0, 0, tpa6130a2_supply_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Outputs */
- SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
- SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
+ SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Left"),
+ SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Right"),
};
static const struct snd_soc_dapm_route audio_map[] = {
@@ -335,23 +373,33 @@ static const struct snd_soc_dapm_route audio_map[] = {
int tpa6130a2_add_controls(struct snd_soc_codec *codec)
{
+ struct tpa6130a2_data *data;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
ARRAY_SIZE(tpa6130a2_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- return snd_soc_add_controls(codec, tpa6130a2_controls,
- ARRAY_SIZE(tpa6130a2_controls));
+ if (data->id == TPA6140A2)
+ return snd_soc_add_controls(codec, tpa6140a2_controls,
+ ARRAY_SIZE(tpa6140a2_controls));
+ else
+ return snd_soc_add_controls(codec, tpa6130a2_controls,
+ ARRAY_SIZE(tpa6130a2_controls));
}
EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
-static int tpa6130a2_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int __devinit tpa6130a2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct device *dev;
struct tpa6130a2_data *data;
struct tpa6130a2_platform_data *pdata;
+ const char *regulator;
int ret;
dev = &client->dev;
@@ -374,6 +422,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
pdata = client->dev.platform_data;
data->power_gpio = pdata->power_gpio;
+ data->id = pdata->id;
mutex_init(&data->mutex);
@@ -387,15 +436,34 @@ 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 (data->id) {
+ default:
+ dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
+ pdata->id);
+ case TPA6130A2:
+ regulator = "Vdd";
+ break;
+ case TPA6140A2:
+ regulator = "AVdd";
+ break;
+ }
+
+ data->supply = regulator_get(dev, regulator);
+ if (IS_ERR(data->supply)) {
+ ret = PTR_ERR(data->supply);
+ dev_err(dev, "Failed to request supply: %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 +472,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_put(data->supply);
+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;
@@ -415,7 +491,7 @@ fail:
return ret;
}
-static int tpa6130a2_remove(struct i2c_client *client)
+static int __devexit tpa6130a2_remove(struct i2c_client *client)
{
struct tpa6130a2_data *data = i2c_get_clientdata(client);
@@ -423,6 +499,9 @@ static int tpa6130a2_remove(struct i2c_client *client)
if (data->power_gpio >= 0)
gpio_free(data->power_gpio);
+
+ regulator_put(data->supply);
+
kfree(data);
tpa6130a2_client = NULL;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 2a27f7b56726..b4fcdb01fc49 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -27,6 +27,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -55,7 +56,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x0c, /* REG_ATXR1PGA (0xB) */
0x00, /* REG_AVTXL2PGA (0xC) */
0x00, /* REG_AVTXR2PGA (0xD) */
- 0x01, /* REG_AUDIO_IF (0xE) */
+ 0x00, /* REG_AUDIO_IF (0xE) */
0x00, /* REG_VOICE_IF (0xF) */
0x00, /* REG_ARXR1PGA (0x10) */
0x00, /* REG_ARXL1PGA (0x11) */
@@ -64,19 +65,19 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_VRXPGA (0x14) */
0x00, /* REG_VSTPGA (0x15) */
0x00, /* REG_VRX2ARXPGA (0x16) */
- 0x0c, /* REG_AVDAC_CTL (0x17) */
+ 0x00, /* REG_AVDAC_CTL (0x17) */
0x00, /* REG_ARX2VTXPGA (0x18) */
0x00, /* REG_ARXL1_APGA_CTL (0x19) */
0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
- 0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */
- 0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */
+ 0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */
+ 0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */
0x00, /* REG_ATX2ARXPGA (0x1D) */
0x00, /* REG_BT_IF (0x1E) */
0x00, /* REG_BTPGA (0x1F) */
0x00, /* REG_BTSTPGA (0x20) */
0x00, /* REG_EAR_CTL (0x21) */
- 0x24, /* REG_HS_SEL (0x22) */
- 0x0a, /* REG_HS_GAIN_SET (0x23) */
+ 0x00, /* REG_HS_SEL (0x22) */
+ 0x00, /* REG_HS_GAIN_SET (0x23) */
0x00, /* REG_HS_POPN_SET (0x24) */
0x00, /* REG_PREDL_CTL (0x25) */
0x00, /* REG_PREDR_CTL (0x26) */
@@ -99,7 +100,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
- 0x16, /* REG_APLL_CTL (0x3A) */
+ 0x06, /* REG_APLL_CTL (0x3A) */
0x00, /* REG_DTMF_CTL (0x3B) */
0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
@@ -123,6 +124,8 @@ struct twl4030_priv {
struct snd_soc_codec codec;
unsigned int codec_powered;
+
+ /* reference counts of AIF/APLL users */
unsigned int apll_enabled;
struct snd_pcm_substream *master_substream;
@@ -135,9 +138,11 @@ struct twl4030_priv {
unsigned int sysclk;
- /* Headset output state handling */
- unsigned int hsl_enabled;
- unsigned int hsr_enabled;
+ /* Output (with associated amp) states */
+ u8 hsl_enabled, hsr_enabled;
+ u8 earpiece_enabled;
+ u8 predrivel_enabled, predriver_enabled;
+ u8 carkitl_enabled, carkitr_enabled;
};
/*
@@ -173,17 +178,52 @@ static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
static int twl4030_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ int write_to_reg = 0;
+
twl4030_write_reg_cache(codec, reg, value);
- if (likely(reg < TWL4030_REG_SW_SHADOW))
- return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
- reg);
- else
- return 0;
+ if (likely(reg < TWL4030_REG_SW_SHADOW)) {
+ /* Decide if the given register can be written */
+ switch (reg) {
+ case TWL4030_REG_EAR_CTL:
+ if (twl4030->earpiece_enabled)
+ write_to_reg = 1;
+ break;
+ case TWL4030_REG_PREDL_CTL:
+ if (twl4030->predrivel_enabled)
+ write_to_reg = 1;
+ break;
+ case TWL4030_REG_PREDR_CTL:
+ if (twl4030->predriver_enabled)
+ write_to_reg = 1;
+ break;
+ case TWL4030_REG_PRECKL_CTL:
+ if (twl4030->carkitl_enabled)
+ write_to_reg = 1;
+ break;
+ case TWL4030_REG_PRECKR_CTL:
+ if (twl4030->carkitr_enabled)
+ write_to_reg = 1;
+ break;
+ case TWL4030_REG_HS_GAIN_SET:
+ if (twl4030->hsl_enabled || twl4030->hsr_enabled)
+ write_to_reg = 1;
+ break;
+ default:
+ /* All other register can be written */
+ write_to_reg = 1;
+ break;
+ }
+ if (write_to_reg)
+ return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ value, reg);
+ }
+ return 0;
}
static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
int mode;
if (enable == twl4030->codec_powered)
@@ -221,28 +261,28 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
- struct twl4030_priv *twl4030 = codec->private_data;
- int status;
-
- if (enable == twl4030->apll_enabled)
- return;
-
- if (enable)
- /* Enable PLL */
- status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
- else
- /* Disable PLL */
- status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ int status = -1;
+
+ if (enable) {
+ twl4030->apll_enabled++;
+ if (twl4030->apll_enabled == 1)
+ status = twl4030_codec_enable_resource(
+ TWL4030_CODEC_RES_APLL);
+ } else {
+ twl4030->apll_enabled--;
+ if (!twl4030->apll_enabled)
+ status = twl4030_codec_disable_resource(
+ TWL4030_CODEC_RES_APLL);
+ }
if (status >= 0)
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
-
- twl4030->apll_enabled = enable;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
{
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 anamicl, regmisc1, byte;
int i = 0;
@@ -525,26 +565,26 @@ static int micpath_event(struct snd_soc_dapm_widget *w,
* Output PGA builder:
* Handle the muting and unmuting of the given output (turning off the
* amplifier associated with the output pin)
- * On mute bypass the reg_cache and mute the volume
- * On unmute: restore the register content
+ * On mute bypass the reg_cache and write 0 to the register
+ * On unmute: restore the register content from the reg_cache
* Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
*/
#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
struct snd_kcontrol *kcontrol, int event) \
{ \
- u8 reg_val; \
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); \
\
switch (event) { \
case SND_SOC_DAPM_POST_PMU: \
+ twl4030->pin_name##_enabled = 1; \
twl4030_write(w->codec, reg, \
twl4030_read_reg_cache(w->codec, reg)); \
break; \
case SND_SOC_DAPM_POST_PMD: \
- reg_val = twl4030_read_reg_cache(w->codec, reg); \
- twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
- reg_val & (~mask), \
- reg); \
+ twl4030->pin_name##_enabled = 0; \
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
+ 0, reg); \
break; \
} \
return 0; \
@@ -635,13 +675,38 @@ static int apll_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int aif_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u8 audio_if;
+
+ audio_if = twl4030_read_reg_cache(w->codec, TWL4030_REG_AUDIO_IF);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable AIF */
+ /* enable the PLL before we use it to clock the DAI */
+ twl4030_apll_enable(w->codec, 1);
+
+ twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
+ audio_if | TWL4030_AIF_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* disable the DAI before we stop it's source PLL */
+ twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
+ audio_if & ~TWL4030_AIF_EN);
+ twl4030_apll_enable(w->codec, 0);
+ break;
+ }
+ return 0;
+}
+
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
struct snd_soc_device *socdev = codec->socdev;
struct twl4030_setup_data *setup = socdev->codec_data;
unsigned char hs_gain, hs_pop;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
/* Base values for ramp delay calculation: 2^19 - 2^26 */
unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
8388608, 16777216, 33554432, 67108864};
@@ -664,7 +729,10 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
/* Headset ramp-up according to the TRM */
hs_pop |= TWL4030_VMID_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
- twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
+ /* Actually write to the register */
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ hs_gain,
+ TWL4030_REG_HS_GAIN_SET);
hs_pop |= TWL4030_RAMP_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Wait ramp delay time + 1, so the VMID can settle */
@@ -701,7 +769,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
static int headsetlpga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct twl4030_priv *twl4030 = w->codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -725,7 +793,7 @@ static int headsetlpga_event(struct snd_soc_dapm_widget *w,
static int headsetrpga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct twl4030_priv *twl4030 = w->codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -917,7 +985,7 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val;
unsigned short mask, bitmask;
@@ -1035,6 +1103,16 @@ static const struct soc_enum twl4030_vibradir_enum =
ARRAY_SIZE(twl4030_vibradir_texts),
twl4030_vibradir_texts);
+/* Digimic Left and right swapping */
+static const char *twl4030_digimicswap_texts[] = {
+ "Not swapped", "Swapped",
+};
+
+static const struct soc_enum twl4030_digimicswap_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_MISC_SET_1, 0,
+ ARRAY_SIZE(twl4030_digimicswap_texts),
+ twl4030_digimicswap_texts);
+
static const struct snd_kcontrol_new twl4030_snd_controls[] = {
/* Codec operation mode control */
SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum,
@@ -1111,6 +1189,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum),
+
+ SOC_ENUM("Digimic LR Swap", twl4030_digimicswap_enum),
};
static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
@@ -1127,8 +1207,6 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DIGIMIC1"),
/* Outputs */
- SND_SOC_DAPM_OUTPUT("OUTL"),
- SND_SOC_DAPM_OUTPUT("OUTR"),
SND_SOC_DAPM_OUTPUT("EARPIECE"),
SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
SND_SOC_DAPM_OUTPUT("PREDRIVER"),
@@ -1140,6 +1218,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("HFR"),
SND_SOC_DAPM_OUTPUT("VIBRA"),
+ /* AIF and APLL clocks for running DAIs (including loopback) */
+ SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"),
+ SND_SOC_DAPM_INPUT("Virtual HiFi IN"),
+ SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"),
+
/* DACs */
SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback",
SND_SOC_NOPM, 0, 0),
@@ -1203,6 +1286,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event,
+ SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
/* Output MIXER controls */
/* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1331,12 +1417,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
/* Supply for the digital part (APLL) */
- {"Digital R1 Playback Mixer", NULL, "APLL Enable"},
- {"Digital L1 Playback Mixer", NULL, "APLL Enable"},
- {"Digital R2 Playback Mixer", NULL, "APLL Enable"},
- {"Digital L2 Playback Mixer", NULL, "APLL Enable"},
{"Digital Voice Playback Mixer", NULL, "APLL Enable"},
+ {"Digital R1 Playback Mixer", NULL, "AIF Enable"},
+ {"Digital L1 Playback Mixer", NULL, "AIF Enable"},
+ {"Digital R2 Playback Mixer", NULL, "AIF Enable"},
+ {"Digital L2 Playback Mixer", NULL, "AIF Enable"},
+
{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
@@ -1403,8 +1490,14 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Vibra Mux", "AudioR2", "DAC Right2"},
/* outputs */
- {"OUTL", NULL, "Analog L2 Playback Mixer"},
- {"OUTR", NULL, "Analog R2 Playback Mixer"},
+ /* Must be always connected (for AIF and APLL) */
+ {"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"},
+ {"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"},
+ {"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"},
+ {"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"},
+ /* Must be always connected (for APLL) */
+ {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
+ /* Physical outputs */
{"EARPIECE", NULL, "Earpiece PGA"},
{"PREDRIVEL", NULL, "PredriveL PGA"},
{"PREDRIVER", NULL, "PredriveR PGA"},
@@ -1418,6 +1511,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VIBRA", NULL, "Vibra Route"},
/* Capture path */
+ /* Must be always connected (for AIF and APLL) */
+ {"ADC Virtual Left1", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Right1", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Left2", NULL, "Virtual HiFi IN"},
+ {"ADC Virtual Right2", NULL, "Virtual HiFi IN"},
+ /* Physical inputs */
{"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
{"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
{"Analog Left", "AUXL Capture Switch", "AUXL"},
@@ -1450,10 +1549,10 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
- {"ADC Virtual Left1", NULL, "APLL Enable"},
- {"ADC Virtual Right1", NULL, "APLL Enable"},
- {"ADC Virtual Left2", NULL, "APLL Enable"},
- {"ADC Virtual Right2", NULL, "APLL Enable"},
+ {"ADC Virtual Left1", NULL, "AIF Enable"},
+ {"ADC Virtual Right1", NULL, "AIF Enable"},
+ {"ADC Virtual Left2", NULL, "AIF Enable"},
+ {"ADC Virtual Right2", NULL, "AIF Enable"},
/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right"},
@@ -1575,7 +1674,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
if (twl4030->master_substream) {
twl4030->slave_substream = substream;
@@ -1606,7 +1705,7 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
if (twl4030->master_substream == substream)
twl4030->master_substream = twl4030->slave_substream;
@@ -1632,7 +1731,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 mode, old_mode, format, old_format;
/* If the substream has 4 channel, do the necessary setup */
@@ -1752,7 +1851,7 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 19200000:
@@ -1867,7 +1966,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 mode;
/* If the system master clock is not 26MHz, the voice PCM interface is
@@ -1949,7 +2048,7 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct twl4030_priv *twl4030 = codec->private_data;
+ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
if (freq != 26000000) {
dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice"
@@ -2095,7 +2194,6 @@ static int twl4030_soc_resume(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->card->codec;
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- twl4030_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -2112,7 +2210,7 @@ static int twl4030_soc_probe(struct platform_device *pdev)
BUG_ON(!twl4030_codec);
codec = twl4030_codec;
- twl4030 = codec->private_data;
+ twl4030 = snd_soc_codec_get_drvdata(codec);
socdev->card->codec = codec;
/* Configuration for headset ramp delay from setup data */
@@ -2152,8 +2250,6 @@ static int twl4030_soc_remove(struct platform_device *pdev)
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
- kfree(codec->private_data);
- kfree(codec);
return 0;
}
@@ -2177,7 +2273,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
}
codec = &twl4030->codec;
- codec->private_data = twl4030;
+ snd_soc_codec_set_drvdata(codec, twl4030);
codec->dev = &pdev->dev;
twl4030_dai[0].dev = &pdev->dev;
twl4030_dai[1].dev = &pdev->dev;
@@ -2192,7 +2288,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
codec->write = twl4030_write;
codec->set_bias_level = twl4030_set_bias_level;
codec->dai = twl4030_dai;
- codec->num_dai = ARRAY_SIZE(twl4030_dai),
+ codec->num_dai = ARRAY_SIZE(twl4030_dai);
codec->reg_cache_size = sizeof(twl4030_reg);
codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
GFP_KERNEL);
@@ -2237,6 +2333,9 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
+ snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ snd_soc_unregister_codec(&twl4030->codec);
+ kfree(twl4030->codec.reg_cache);
kfree(twl4030);
twl4030_codec = NULL;
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index dd6396ec9c79..f206d242ca31 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -25,7 +25,7 @@
/* Register descriptions are here */
#include <linux/mfd/twl4030-codec.h>
-/* Sgadow register used by the audio driver */
+/* Shadow register used by the audio driver */
#define TWL4030_REG_SW_SHADOW 0x4A
#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
new file mode 100644
index 000000000000..af36346ff336
--- /dev/null
+++ b/sound/soc/codecs/twl6040.c
@@ -0,0 +1,1246 @@
+/*
+ * ALSA SoC TWL6040 codec driver
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl.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 "twl6040.h"
+
+#define TWL6040_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+/* codec private data */
+struct twl6040_data {
+ struct snd_soc_codec codec;
+ int audpwron;
+ int naudint;
+ int codec_powered;
+ int pll;
+ int non_lp;
+ unsigned int sysclk;
+ struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ struct completion ready;
+};
+
+/*
+ * twl6040 register cache & default register settings
+ */
+static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
+ 0x00, /* not used 0x00 */
+ 0x4B, /* TWL6040_ASICID (ro) 0x01 */
+ 0x00, /* TWL6040_ASICREV (ro) 0x02 */
+ 0x00, /* TWL6040_INTID 0x03 */
+ 0x00, /* TWL6040_INTMR 0x04 */
+ 0x00, /* TWL6040_NCPCTRL 0x05 */
+ 0x00, /* TWL6040_LDOCTL 0x06 */
+ 0x60, /* TWL6040_HPPLLCTL 0x07 */
+ 0x00, /* TWL6040_LPPLLCTL 0x08 */
+ 0x4A, /* TWL6040_LPPLLDIV 0x09 */
+ 0x00, /* TWL6040_AMICBCTL 0x0A */
+ 0x00, /* TWL6040_DMICBCTL 0x0B */
+ 0x18, /* TWL6040_MICLCTL 0x0C - No input selected on Left Mic */
+ 0x18, /* TWL6040_MICRCTL 0x0D - No input selected on Right Mic */
+ 0x00, /* TWL6040_MICGAIN 0x0E */
+ 0x1B, /* TWL6040_LINEGAIN 0x0F */
+ 0x00, /* TWL6040_HSLCTL 0x10 */
+ 0x00, /* TWL6040_HSRCTL 0x11 */
+ 0x00, /* TWL6040_HSGAIN 0x12 */
+ 0x00, /* TWL6040_EARCTL 0x13 */
+ 0x00, /* TWL6040_HFLCTL 0x14 */
+ 0x00, /* TWL6040_HFLGAIN 0x15 */
+ 0x00, /* TWL6040_HFRCTL 0x16 */
+ 0x00, /* TWL6040_HFRGAIN 0x17 */
+ 0x00, /* TWL6040_VIBCTLL 0x18 */
+ 0x00, /* TWL6040_VIBDATL 0x19 */
+ 0x00, /* TWL6040_VIBCTLR 0x1A */
+ 0x00, /* TWL6040_VIBDATR 0x1B */
+ 0x00, /* TWL6040_HKCTL1 0x1C */
+ 0x00, /* TWL6040_HKCTL2 0x1D */
+ 0x00, /* TWL6040_GPOCTL 0x1E */
+ 0x00, /* TWL6040_ALB 0x1F */
+ 0x00, /* TWL6040_DLB 0x20 */
+ 0x00, /* not used 0x21 */
+ 0x00, /* not used 0x22 */
+ 0x00, /* not used 0x23 */
+ 0x00, /* not used 0x24 */
+ 0x00, /* not used 0x25 */
+ 0x00, /* not used 0x26 */
+ 0x00, /* not used 0x27 */
+ 0x00, /* TWL6040_TRIM1 0x28 */
+ 0x00, /* TWL6040_TRIM2 0x29 */
+ 0x00, /* TWL6040_TRIM3 0x2A */
+ 0x00, /* TWL6040_HSOTRIM 0x2B */
+ 0x00, /* TWL6040_HFOTRIM 0x2C */
+ 0x09, /* TWL6040_ACCCTL 0x2D */
+ 0x00, /* TWL6040_STATUS (ro) 0x2E */
+};
+
+/*
+ * twl6040 vio/gnd registers:
+ * registers under vio/gnd supply can be accessed
+ * before the power-up sequence, after NRESPWRON goes high
+ */
+static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
+ TWL6040_REG_ASICID,
+ TWL6040_REG_ASICREV,
+ TWL6040_REG_INTID,
+ TWL6040_REG_INTMR,
+ TWL6040_REG_NCPCTL,
+ TWL6040_REG_LDOCTL,
+ TWL6040_REG_AMICBCTL,
+ TWL6040_REG_DMICBCTL,
+ TWL6040_REG_HKCTL1,
+ TWL6040_REG_HKCTL2,
+ TWL6040_REG_GPOCTL,
+ TWL6040_REG_TRIM1,
+ TWL6040_REG_TRIM2,
+ TWL6040_REG_TRIM3,
+ TWL6040_REG_HSOTRIM,
+ TWL6040_REG_HFOTRIM,
+ TWL6040_REG_ACCCTL,
+ TWL6040_REG_STATUS,
+};
+
+/*
+ * twl6040 vdd/vss registers:
+ * registers under vdd/vss supplies can only be accessed
+ * after the power-up sequence
+ */
+static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
+ TWL6040_REG_HPPLLCTL,
+ TWL6040_REG_LPPLLCTL,
+ TWL6040_REG_LPPLLDIV,
+ TWL6040_REG_MICLCTL,
+ TWL6040_REG_MICRCTL,
+ TWL6040_REG_MICGAIN,
+ TWL6040_REG_LINEGAIN,
+ TWL6040_REG_HSLCTL,
+ TWL6040_REG_HSRCTL,
+ TWL6040_REG_HSGAIN,
+ TWL6040_REG_EARCTL,
+ TWL6040_REG_HFLCTL,
+ TWL6040_REG_HFLGAIN,
+ TWL6040_REG_HFRCTL,
+ TWL6040_REG_HFRGAIN,
+ TWL6040_REG_VIBCTLL,
+ TWL6040_REG_VIBDATL,
+ TWL6040_REG_VIBCTLR,
+ TWL6040_REG_VIBDATR,
+ TWL6040_REG_ALB,
+ TWL6040_REG_DLB,
+};
+
+/*
+ * read twl6040 register cache
+ */
+static inline unsigned int twl6040_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= TWL6040_CACHEREGNUM)
+ return -EIO;
+
+ return cache[reg];
+}
+
+/*
+ * write twl6040 register cache
+ */
+static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= TWL6040_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * read from twl6040 hardware register
+ */
+static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 value;
+
+ if (reg >= TWL6040_CACHEREGNUM)
+ return -EIO;
+
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
+ twl6040_write_reg_cache(codec, reg, value);
+
+ return value;
+}
+
+/*
+ * write to the twl6040 register space
+ */
+static int twl6040_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ if (reg >= TWL6040_CACHEREGNUM)
+ return -EIO;
+
+ twl6040_write_reg_cache(codec, reg, value);
+ return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+}
+
+static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
+{
+ u8 *cache = codec->reg_cache;
+ int reg, i;
+
+ /* allow registers to be accessed by i2c */
+ twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]);
+
+ for (i = 0; i < TWL6040_VIOREGNUM; i++) {
+ reg = twl6040_vio_reg[i];
+ /* skip read-only registers (ASICID, ASICREV, STATUS) */
+ switch (reg) {
+ case TWL6040_REG_ASICID:
+ case TWL6040_REG_ASICREV:
+ case TWL6040_REG_STATUS:
+ continue;
+ default:
+ break;
+ }
+ twl6040_write(codec, reg, cache[reg]);
+ }
+}
+
+static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
+{
+ u8 *cache = codec->reg_cache;
+ int reg, i;
+
+ for (i = 0; i < TWL6040_VDDREGNUM; i++) {
+ reg = twl6040_vdd_reg[i];
+ twl6040_write(codec, reg, cache[reg]);
+ }
+}
+
+/* twl6040 codec manual power-up sequence */
+static void twl6040_power_up(struct snd_soc_codec *codec)
+{
+ u8 ncpctl, ldoctl, lppllctl, accctl;
+
+ ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
+ ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
+ lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
+ accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
+
+ /* enable reference system */
+ ldoctl |= TWL6040_REFENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ msleep(10);
+ /* enable internal oscillator */
+ ldoctl |= TWL6040_OSCENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(10);
+ /* enable high-side ldo */
+ ldoctl |= TWL6040_HSLDOENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(244);
+ /* enable negative charge pump */
+ ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN;
+ twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
+ udelay(488);
+ /* enable low-side ldo */
+ ldoctl |= TWL6040_LSLDOENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(244);
+ /* enable low-power pll */
+ lppllctl |= TWL6040_LPLLENA;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+ /* reset state machine */
+ accctl |= TWL6040_RESETSPLIT;
+ twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
+ mdelay(5);
+ accctl &= ~TWL6040_RESETSPLIT;
+ twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
+ /* disable internal oscillator */
+ ldoctl &= ~TWL6040_OSCENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+}
+
+/* twl6040 codec manual power-down sequence */
+static void twl6040_power_down(struct snd_soc_codec *codec)
+{
+ u8 ncpctl, ldoctl, lppllctl, accctl;
+
+ ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
+ ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
+ lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
+ accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
+
+ /* enable internal oscillator */
+ ldoctl |= TWL6040_OSCENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(10);
+ /* disable low-power pll */
+ lppllctl &= ~TWL6040_LPLLENA;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+ /* disable low-side ldo */
+ ldoctl &= ~TWL6040_LSLDOENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(244);
+ /* disable negative charge pump */
+ ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN);
+ twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
+ udelay(488);
+ /* disable high-side ldo */
+ ldoctl &= ~TWL6040_HSLDOENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ udelay(244);
+ /* disable internal oscillator */
+ ldoctl &= ~TWL6040_OSCENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ /* disable reference system */
+ ldoctl &= ~TWL6040_REFENA;
+ twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
+ msleep(10);
+}
+
+/* set headset dac and driver power mode */
+static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
+{
+ int hslctl, hsrctl;
+ int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL;
+
+ hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
+
+ if (high_perf) {
+ hslctl &= ~mask;
+ hsrctl &= ~mask;
+ } else {
+ hslctl |= mask;
+ hsrctl |= mask;
+ }
+
+ twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
+
+ return 0;
+}
+
+static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->non_lp++;
+ else
+ priv->non_lp--;
+
+ return 0;
+}
+
+/* audio interrupt handler */
+static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ u8 intid;
+
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
+
+ switch (intid) {
+ case TWL6040_THINT:
+ dev_alert(codec->dev, "die temp over-limit detection\n");
+ break;
+ case TWL6040_PLUGINT:
+ case TWL6040_UNPLUGINT:
+ case TWL6040_HOOKINT:
+ break;
+ case TWL6040_HFINT:
+ dev_alert(codec->dev, "hf drivers over current detection\n");
+ break;
+ case TWL6040_VIBINT:
+ dev_alert(codec->dev, "vib drivers over current detection\n");
+ break;
+ case TWL6040_READYINT:
+ complete(&priv->ready);
+ break;
+ default:
+ dev_err(codec->dev, "unknown audio interrupt %d\n", intid);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * MICATT volume control:
+ * from -6 to 0 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
+
+/*
+ * MICGAIN volume control:
+ * from 6 to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
+
+/*
+ * HSGAIN volume control:
+ * from -30 to 0 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0);
+
+/*
+ * HFGAIN volume control:
+ * from -52 to 6 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0);
+
+/*
+ * EPGAIN volume control:
+ * from -24 to 6 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0);
+
+/* Left analog microphone selection */
+static const char *twl6040_amicl_texts[] =
+ {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"};
+
+/* Right analog microphone selection */
+static const char *twl6040_amicr_texts[] =
+ {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
+
+static const struct soc_enum twl6040_enum[] = {
+ SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 3, twl6040_amicl_texts),
+ SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 3, twl6040_amicr_texts),
+};
+
+static const struct snd_kcontrol_new amicl_control =
+ SOC_DAPM_ENUM("Route", twl6040_enum[0]);
+
+static const struct snd_kcontrol_new amicr_control =
+ SOC_DAPM_ENUM("Route", twl6040_enum[1]);
+
+/* Headset DAC playback switches */
+static const struct snd_kcontrol_new hsdacl_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 5, 1, 0);
+
+static const struct snd_kcontrol_new hsdacr_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 5, 1, 0);
+
+/* Handsfree DAC playback switches */
+static const struct snd_kcontrol_new hfdacl_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 2, 1, 0);
+
+static const struct snd_kcontrol_new hfdacr_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0);
+
+/* Headset driver switches */
+static const struct snd_kcontrol_new hsl_driver_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 2, 1, 0);
+
+static const struct snd_kcontrol_new hsr_driver_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 2, 1, 0);
+
+/* Handsfree driver switches */
+static const struct snd_kcontrol_new hfl_driver_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 4, 1, 0);
+
+static const struct snd_kcontrol_new hfr_driver_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 4, 1, 0);
+
+static const struct snd_kcontrol_new ep_driver_switch_controls =
+ SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
+
+static const struct snd_kcontrol_new twl6040_snd_controls[] = {
+ /* Capture gains */
+ SOC_DOUBLE_TLV("Capture Preamplifier Volume",
+ TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
+ SOC_DOUBLE_TLV("Capture Volume",
+ TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),
+
+ /* Playback gains */
+ SOC_DOUBLE_TLV("Headset Playback Volume",
+ TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
+ SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+ TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
+ SOC_SINGLE_TLV("Earphone Playback Volume",
+ TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
+};
+
+static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MAINMIC"),
+ SND_SOC_DAPM_INPUT("HSMIC"),
+ SND_SOC_DAPM_INPUT("SUBMIC"),
+ SND_SOC_DAPM_INPUT("AFML"),
+ SND_SOC_DAPM_INPUT("AFMR"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+ SND_SOC_DAPM_OUTPUT("EP"),
+
+ /* Analog input muxes for the capture amplifiers */
+ SND_SOC_DAPM_MUX("Analog Left Capture Route",
+ SND_SOC_NOPM, 0, 0, &amicl_control),
+ SND_SOC_DAPM_MUX("Analog Right Capture Route",
+ SND_SOC_NOPM, 0, 0, &amicr_control),
+
+ /* Analog capture PGAs */
+ SND_SOC_DAPM_PGA("MicAmpL",
+ TWL6040_REG_MICLCTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MicAmpR",
+ TWL6040_REG_MICRCTL, 0, 0, NULL, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture",
+ TWL6040_REG_MICLCTL, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture",
+ TWL6040_REG_MICRCTL, 2, 0),
+
+ /* Microphone bias */
+ SND_SOC_DAPM_MICBIAS("Headset Mic Bias",
+ TWL6040_REG_AMICBCTL, 0, 0),
+ SND_SOC_DAPM_MICBIAS("Main Mic Bias",
+ TWL6040_REG_AMICBCTL, 4, 0),
+ SND_SOC_DAPM_MICBIAS("Digital Mic1 Bias",
+ TWL6040_REG_DMICBCTL, 0, 0),
+ SND_SOC_DAPM_MICBIAS("Digital Mic2 Bias",
+ TWL6040_REG_DMICBCTL, 4, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback",
+ TWL6040_REG_HSLCTL, 0, 0),
+ SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback",
+ TWL6040_REG_HSRCTL, 0, 0),
+ SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback",
+ TWL6040_REG_HFLCTL, 0, 0,
+ twl6040_power_mode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback",
+ TWL6040_REG_HFRCTL, 0, 0,
+ twl6040_power_mode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Analog playback switches */
+ SND_SOC_DAPM_SWITCH("HSDAC Left Playback",
+ SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls),
+ SND_SOC_DAPM_SWITCH("HSDAC Right Playback",
+ SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls),
+ SND_SOC_DAPM_SWITCH("HFDAC Left Playback",
+ SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls),
+ SND_SOC_DAPM_SWITCH("HFDAC Right Playback",
+ SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls),
+
+ SND_SOC_DAPM_SWITCH("Headset Left Driver",
+ SND_SOC_NOPM, 0, 0, &hsl_driver_switch_controls),
+ SND_SOC_DAPM_SWITCH("Headset Right Driver",
+ SND_SOC_NOPM, 0, 0, &hsr_driver_switch_controls),
+ SND_SOC_DAPM_SWITCH_E("Handsfree Left Driver",
+ SND_SOC_NOPM, 0, 0, &hfl_driver_switch_controls,
+ twl6040_power_mode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("Handsfree Right Driver",
+ SND_SOC_NOPM, 0, 0, &hfr_driver_switch_controls,
+ twl6040_power_mode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH_E("Earphone Driver",
+ SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls,
+ twl6040_power_mode_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Analog playback PGAs */
+ SND_SOC_DAPM_PGA("HFDAC Left PGA",
+ TWL6040_REG_HFLCTL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HFDAC Right PGA",
+ TWL6040_REG_HFRCTL, 1, 0, NULL, 0),
+
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Capture path */
+ {"Analog Left Capture Route", "Headset Mic", "HSMIC"},
+ {"Analog Left Capture Route", "Main Mic", "MAINMIC"},
+ {"Analog Left Capture Route", "Aux/FM Left", "AFML"},
+
+ {"Analog Right Capture Route", "Headset Mic", "HSMIC"},
+ {"Analog Right Capture Route", "Sub Mic", "SUBMIC"},
+ {"Analog Right Capture Route", "Aux/FM Right", "AFMR"},
+
+ {"MicAmpL", NULL, "Analog Left Capture Route"},
+ {"MicAmpR", NULL, "Analog Right Capture Route"},
+
+ {"ADC Left", NULL, "MicAmpL"},
+ {"ADC Right", NULL, "MicAmpR"},
+
+ /* Headset playback path */
+ {"HSDAC Left Playback", "Switch", "HSDAC Left"},
+ {"HSDAC Right Playback", "Switch", "HSDAC Right"},
+
+ {"Headset Left Driver", "Switch", "HSDAC Left Playback"},
+ {"Headset Right Driver", "Switch", "HSDAC Right Playback"},
+
+ {"HSOL", NULL, "Headset Left Driver"},
+ {"HSOR", NULL, "Headset Right Driver"},
+
+ /* Earphone playback path */
+ {"Earphone Driver", "Switch", "HSDAC Left"},
+ {"EP", NULL, "Earphone Driver"},
+
+ /* Handsfree playback path */
+ {"HFDAC Left Playback", "Switch", "HFDAC Left"},
+ {"HFDAC Right Playback", "Switch", "HFDAC Right"},
+
+ {"HFDAC Left PGA", NULL, "HFDAC Left Playback"},
+ {"HFDAC Right PGA", NULL, "HFDAC Right Playback"},
+
+ {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"},
+ {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"},
+
+ {"HFL", NULL, "Handsfree Left Driver"},
+ {"HFR", NULL, "Handsfree Right Driver"},
+};
+
+static int twl6040_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets,
+ ARRAY_SIZE(twl6040_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int twl6040_power_up_completion(struct snd_soc_codec *codec,
+ int naudint)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int time_left;
+ u8 intid;
+
+ time_left = wait_for_completion_timeout(&priv->ready,
+ msecs_to_jiffies(48));
+
+ if (!time_left) {
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid,
+ TWL6040_REG_INTID);
+ if (!(intid & TWL6040_READYINT)) {
+ dev_err(codec->dev, "timeout waiting for READYINT\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ priv->codec_powered = 1;
+
+ return 0;
+}
+
+static int twl6040_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int audpwron = priv->audpwron;
+ int naudint = priv->naudint;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (priv->codec_powered)
+ break;
+
+ if (gpio_is_valid(audpwron)) {
+ /* use AUDPWRON line */
+ gpio_set_value(audpwron, 1);
+
+ /* wait for power-up completion */
+ ret = twl6040_power_up_completion(codec, naudint);
+ if (ret)
+ return ret;
+
+ /* sync registers updated during power-up sequence */
+ twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
+ twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
+ twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
+ } else {
+ /* use manual power-up sequence */
+ twl6040_power_up(codec);
+ priv->codec_powered = 1;
+ }
+
+ /* initialize vdd/vss registers with reg_cache */
+ twl6040_init_vdd_regs(codec);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (!priv->codec_powered)
+ break;
+
+ if (gpio_is_valid(audpwron)) {
+ /* use AUDPWRON line */
+ gpio_set_value(audpwron, 0);
+
+ /* power-down sequence latency */
+ udelay(500);
+
+ /* sync registers updated during power-down sequence */
+ twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
+ twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
+ twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
+ 0x00);
+ } else {
+ /* use manual power-down sequence */
+ twl6040_power_down(codec);
+ }
+
+ priv->codec_powered = 0;
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+/* set of rates for each pll: low-power and high-performance */
+
+static unsigned int lp_rates[] = {
+ 88200,
+ 96000,
+};
+
+static struct snd_pcm_hw_constraint_list lp_constraints = {
+ .count = ARRAY_SIZE(lp_rates),
+ .list = lp_rates,
+};
+
+static unsigned int hp_rates[] = {
+ 96000,
+};
+
+static struct snd_pcm_hw_constraint_list hp_constraints = {
+ .count = ARRAY_SIZE(hp_rates),
+ .list = hp_rates,
+};
+
+static int twl6040_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (!priv->sysclk) {
+ dev_err(codec->dev,
+ "no mclk configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ /*
+ * capture is not supported at 17.64 MHz,
+ * it's reserved for headset low-power playback scenario
+ */
+ if ((priv->sysclk == 17640000) && substream->stream) {
+ dev_err(codec->dev,
+ "capture mode is not supported at %dHz\n",
+ priv->sysclk);
+ return -EINVAL;
+ }
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ priv->sysclk_constraints);
+
+ return 0;
+}
+
+static int twl6040_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ u8 lppllctl;
+ int rate;
+
+ /* nothing to do for high-perf pll, it supports only 48 kHz */
+ if (priv->pll == TWL6040_HPPLL_ID)
+ return 0;
+
+ lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
+
+ rate = params_rate(params);
+ switch (rate) {
+ case 88200:
+ lppllctl |= TWL6040_LPLLFIN;
+ priv->sysclk = 17640000;
+ break;
+ case 96000:
+ lppllctl &= ~TWL6040_LPLLFIN;
+ priv->sysclk = 19200000;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n", rate);
+ return -EINVAL;
+ }
+
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+
+ return 0;
+}
+
+static int twl6040_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /*
+ * low-power playback mode is restricted
+ * for headset path only
+ */
+ if ((priv->sysclk == 17640000) && priv->non_lp) {
+ dev_err(codec->dev,
+ "some enabled paths aren't supported at %dHz\n",
+ priv->sysclk);
+ return -EPERM;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ u8 hppllctl, lppllctl;
+
+ hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL);
+ lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
+
+ switch (clk_id) {
+ case TWL6040_SYSCLK_SEL_LPPLL:
+ switch (freq) {
+ case 32768:
+ /* headset dac and driver must be in low-power mode */
+ headset_power_mode(codec, 0);
+
+ /* clk32k input requires low-power pll */
+ lppllctl |= TWL6040_LPLLENA;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+ mdelay(5);
+ lppllctl &= ~TWL6040_HPLLSEL;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+ hppllctl &= ~TWL6040_HPLLENA;
+ twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
+ break;
+ default:
+ dev_err(codec->dev, "unknown mclk freq %d\n", freq);
+ return -EINVAL;
+ }
+
+ /* lppll divider */
+ switch (priv->sysclk) {
+ case 17640000:
+ lppllctl |= TWL6040_LPLLFIN;
+ break;
+ case 19200000:
+ lppllctl &= ~TWL6040_LPLLFIN;
+ break;
+ default:
+ /* sysclk not yet configured */
+ lppllctl &= ~TWL6040_LPLLFIN;
+ priv->sysclk = 19200000;
+ break;
+ }
+
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+
+ priv->pll = TWL6040_LPPLL_ID;
+ priv->sysclk_constraints = &lp_constraints;
+ break;
+ case TWL6040_SYSCLK_SEL_HPPLL:
+ hppllctl &= ~TWL6040_MCLK_MSK;
+
+ switch (freq) {
+ case 12000000:
+ /* mclk input, pll enabled */
+ hppllctl |= TWL6040_MCLK_12000KHZ |
+ TWL6040_HPLLSQRBP |
+ TWL6040_HPLLENA;
+ break;
+ case 19200000:
+ /* mclk input, pll disabled */
+ hppllctl |= TWL6040_MCLK_19200KHZ |
+ TWL6040_HPLLSQRBP |
+ TWL6040_HPLLBP;
+ break;
+ case 26000000:
+ /* mclk input, pll enabled */
+ hppllctl |= TWL6040_MCLK_26000KHZ |
+ TWL6040_HPLLSQRBP |
+ TWL6040_HPLLENA;
+ break;
+ case 38400000:
+ /* clk slicer, pll disabled */
+ hppllctl |= TWL6040_MCLK_38400KHZ |
+ TWL6040_HPLLSQRENA |
+ TWL6040_HPLLBP;
+ break;
+ default:
+ dev_err(codec->dev, "unknown mclk freq %d\n", freq);
+ return -EINVAL;
+ }
+
+ /* headset dac and driver must be in high-performance mode */
+ headset_power_mode(codec, 1);
+
+ twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
+ udelay(500);
+ lppllctl |= TWL6040_HPLLSEL;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+ lppllctl &= ~TWL6040_LPLLENA;
+ twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
+
+ /* high-performance pll can provide only 19.2 MHz */
+ priv->pll = TWL6040_HPPLL_ID;
+ priv->sysclk = 19200000;
+ priv->sysclk_constraints = &hp_constraints;
+ break;
+ default:
+ dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops twl6040_dai_ops = {
+ .startup = twl6040_startup,
+ .hw_params = twl6040_hw_params,
+ .trigger = twl6040_trigger,
+ .set_sysclk = twl6040_set_dai_sysclk,
+};
+
+struct snd_soc_dai twl6040_dai = {
+ .name = "twl6040",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL6040_RATES,
+ .formats = TWL6040_FORMATS,
+ },
+ .ops = &twl6040_dai_ops,
+};
+EXPORT_SYMBOL_GPL(twl6040_dai);
+
+#ifdef CONFIG_PM
+static int twl6040_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;
+
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int twl6040_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define twl6040_suspend NULL
+#define twl6040_resume NULL
+#endif
+
+static struct snd_soc_codec *twl6040_codec;
+
+static int twl6040_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ BUG_ON(!twl6040_codec);
+
+ codec = twl6040_codec;
+ socdev->card->codec = codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ return ret;
+ }
+
+ snd_soc_add_controls(codec, twl6040_snd_controls,
+ ARRAY_SIZE(twl6040_snd_controls));
+ twl6040_add_widgets(codec);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+}
+
+static int twl6040_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_twl6040 = {
+ .probe = twl6040_probe,
+ .remove = twl6040_remove,
+ .suspend = twl6040_suspend,
+ .resume = twl6040_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl6040);
+
+static int __devinit twl6040_codec_probe(struct platform_device *pdev)
+{
+ struct twl4030_codec_data *twl_codec = pdev->dev.platform_data;
+ struct snd_soc_codec *codec;
+ struct twl6040_data *priv;
+ int audpwron, naudint;
+ int ret = 0;
+
+ priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ if (twl_codec) {
+ audpwron = twl_codec->audpwron_gpio;
+ naudint = twl_codec->naudint_irq;
+ } else {
+ audpwron = -EINVAL;
+ naudint = 0;
+ }
+
+ priv->audpwron = audpwron;
+ priv->naudint = naudint;
+
+ codec = &priv->codec;
+ codec->dev = &pdev->dev;
+ twl6040_dai.dev = &pdev->dev;
+
+ codec->name = "twl6040";
+ codec->owner = THIS_MODULE;
+ codec->read = twl6040_read_reg_cache;
+ codec->write = twl6040_write;
+ codec->set_bias_level = twl6040_set_bias_level;
+ snd_soc_codec_set_drvdata(codec, priv);
+ codec->dai = &twl6040_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(twl6040_reg);
+ codec->reg_cache = kmemdup(twl6040_reg, sizeof(twl6040_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto cache_err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ init_completion(&priv->ready);
+
+ if (gpio_is_valid(audpwron)) {
+ ret = gpio_request(audpwron, "audpwron");
+ if (ret)
+ goto gpio1_err;
+
+ ret = gpio_direction_output(audpwron, 0);
+ if (ret)
+ goto gpio2_err;
+
+ priv->codec_powered = 0;
+ }
+
+ if (naudint) {
+ /* audio interrupt */
+ ret = request_threaded_irq(naudint, NULL,
+ twl6040_naudint_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "twl6040_codec", codec);
+ if (ret)
+ goto gpio2_err;
+ } else {
+ if (gpio_is_valid(audpwron)) {
+ /* enable only codec ready interrupt */
+ twl6040_write_reg_cache(codec, TWL6040_REG_INTMR,
+ ~TWL6040_READYMSK & TWL6040_ALLINT_MSK);
+ } else {
+ /* no interrupts at all */
+ twl6040_write_reg_cache(codec, TWL6040_REG_INTMR,
+ TWL6040_ALLINT_MSK);
+ }
+ }
+
+ /* init vio registers */
+ twl6040_init_vio_regs(codec);
+
+ /* power on device */
+ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (ret)
+ goto irq_err;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret)
+ goto reg_err;
+
+ twl6040_codec = codec;
+
+ ret = snd_soc_register_dai(&twl6040_dai);
+ if (ret)
+ goto dai_err;
+
+ return 0;
+
+dai_err:
+ snd_soc_unregister_codec(codec);
+ twl6040_codec = NULL;
+reg_err:
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
+irq_err:
+ if (naudint)
+ free_irq(naudint, codec);
+gpio2_err:
+ if (gpio_is_valid(audpwron))
+ gpio_free(audpwron);
+gpio1_err:
+ kfree(codec->reg_cache);
+cache_err:
+ kfree(priv);
+ return ret;
+}
+
+static int __devexit twl6040_codec_remove(struct platform_device *pdev)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(twl6040_codec);
+ int audpwron = priv->audpwron;
+ int naudint = priv->naudint;
+
+ if (gpio_is_valid(audpwron))
+ gpio_free(audpwron);
+
+ if (naudint)
+ free_irq(naudint, twl6040_codec);
+
+ snd_soc_unregister_dai(&twl6040_dai);
+ snd_soc_unregister_codec(twl6040_codec);
+
+ kfree(twl6040_codec);
+ twl6040_codec = NULL;
+
+ return 0;
+}
+
+static struct platform_driver twl6040_codec_driver = {
+ .driver = {
+ .name = "twl6040_codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = twl6040_codec_probe,
+ .remove = __devexit_p(twl6040_codec_remove),
+};
+
+static int __init twl6040_codec_init(void)
+{
+ return platform_driver_register(&twl6040_codec_driver);
+}
+module_init(twl6040_codec_init);
+
+static void __exit twl6040_codec_exit(void)
+{
+ platform_driver_unregister(&twl6040_codec_driver);
+}
+module_exit(twl6040_codec_exit);
+
+MODULE_DESCRIPTION("ASoC TWL6040 codec driver");
+MODULE_AUTHOR("Misael Lopez Cruz");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h
new file mode 100644
index 000000000000..c472070a1da2
--- /dev/null
+++ b/sound/soc/codecs/twl6040.h
@@ -0,0 +1,141 @@
+/*
+ * ALSA SoC TWL6040 codec driver
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL6040_H__
+#define __TWL6040_H__
+
+#define TWL6040_REG_ASICID 0x01
+#define TWL6040_REG_ASICREV 0x02
+#define TWL6040_REG_INTID 0x03
+#define TWL6040_REG_INTMR 0x04
+#define TWL6040_REG_NCPCTL 0x05
+#define TWL6040_REG_LDOCTL 0x06
+#define TWL6040_REG_HPPLLCTL 0x07
+#define TWL6040_REG_LPPLLCTL 0x08
+#define TWL6040_REG_LPPLLDIV 0x09
+#define TWL6040_REG_AMICBCTL 0x0A
+#define TWL6040_REG_DMICBCTL 0x0B
+#define TWL6040_REG_MICLCTL 0x0C
+#define TWL6040_REG_MICRCTL 0x0D
+#define TWL6040_REG_MICGAIN 0x0E
+#define TWL6040_REG_LINEGAIN 0x0F
+#define TWL6040_REG_HSLCTL 0x10
+#define TWL6040_REG_HSRCTL 0x11
+#define TWL6040_REG_HSGAIN 0x12
+#define TWL6040_REG_EARCTL 0x13
+#define TWL6040_REG_HFLCTL 0x14
+#define TWL6040_REG_HFLGAIN 0x15
+#define TWL6040_REG_HFRCTL 0x16
+#define TWL6040_REG_HFRGAIN 0x17
+#define TWL6040_REG_VIBCTLL 0x18
+#define TWL6040_REG_VIBDATL 0x19
+#define TWL6040_REG_VIBCTLR 0x1A
+#define TWL6040_REG_VIBDATR 0x1B
+#define TWL6040_REG_HKCTL1 0x1C
+#define TWL6040_REG_HKCTL2 0x1D
+#define TWL6040_REG_GPOCTL 0x1E
+#define TWL6040_REG_ALB 0x1F
+#define TWL6040_REG_DLB 0x20
+#define TWL6040_REG_TRIM1 0x28
+#define TWL6040_REG_TRIM2 0x29
+#define TWL6040_REG_TRIM3 0x2A
+#define TWL6040_REG_HSOTRIM 0x2B
+#define TWL6040_REG_HFOTRIM 0x2C
+#define TWL6040_REG_ACCCTL 0x2D
+#define TWL6040_REG_STATUS 0x2E
+
+#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
+
+#define TWL6040_VIOREGNUM 18
+#define TWL6040_VDDREGNUM 21
+
+/* INTID (0x03) fields */
+
+#define TWL6040_THINT 0x01
+#define TWL6040_PLUGINT 0x02
+#define TWL6040_UNPLUGINT 0x04
+#define TWL6040_HOOKINT 0x08
+#define TWL6040_HFINT 0x10
+#define TWL6040_VIBINT 0x20
+#define TWL6040_READYINT 0x40
+
+/* INTMR (0x04) fields */
+
+#define TWL6040_READYMSK 0x40
+#define TWL6040_ALLINT_MSK 0x7B
+
+/* NCPCTL (0x05) fields */
+
+#define TWL6040_NCPENA 0x01
+#define TWL6040_NCPOPEN 0x40
+
+/* LDOCTL (0x06) fields */
+
+#define TWL6040_LSLDOENA 0x01
+#define TWL6040_HSLDOENA 0x04
+#define TWL6040_REFENA 0x40
+#define TWL6040_OSCENA 0x80
+
+/* HPPLLCTL (0x07) fields */
+
+#define TWL6040_HPLLENA 0x01
+#define TWL6040_HPLLRST 0x02
+#define TWL6040_HPLLBP 0x04
+#define TWL6040_HPLLSQRENA 0x08
+#define TWL6040_HPLLSQRBP 0x10
+#define TWL6040_MCLK_12000KHZ (0 << 5)
+#define TWL6040_MCLK_19200KHZ (1 << 5)
+#define TWL6040_MCLK_26000KHZ (2 << 5)
+#define TWL6040_MCLK_38400KHZ (3 << 5)
+#define TWL6040_MCLK_MSK 0x60
+
+/* LPPLLCTL (0x08) fields */
+
+#define TWL6040_LPLLENA 0x01
+#define TWL6040_LPLLRST 0x02
+#define TWL6040_LPLLSEL 0x04
+#define TWL6040_LPLLFIN 0x08
+#define TWL6040_HPLLSEL 0x10
+
+/* HSLCTL (0x10) fields */
+
+#define TWL6040_HSDACMODEL 0x02
+#define TWL6040_HSDRVMODEL 0x08
+
+/* HSRCTL (0x11) fields */
+
+#define TWL6040_HSDACMODER 0x02
+#define TWL6040_HSDRVMODER 0x08
+
+/* ACCCTL (0x2D) fields */
+
+#define TWL6040_RESETSPLIT 0x04
+
+#define TWL6040_SYSCLK_SEL_LPPLL 1
+#define TWL6040_SYSCLK_SEL_HPPLL 2
+
+#define TWL6040_HPPLL_ID 1
+#define TWL6040_LPPLL_ID 2
+
+extern struct snd_soc_dai twl6040_dai;
+extern struct snd_soc_codec_device soc_codec_dev_twl6040;
+
+#endif /* End of __TWL6040_H__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 3e99fe5131dd..28aac53c97bb 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -174,7 +175,7 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct uda134x_priv *uda134x = codec->private_data;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_pcm_runtime *master_runtime;
if (uda134x->master_substream) {
@@ -207,7 +208,7 @@ static void uda134x_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct uda134x_priv *uda134x = codec->private_data;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
if (uda134x->master_substream == substream)
uda134x->master_substream = uda134x->slave_substream;
@@ -222,7 +223,7 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct uda134x_priv *uda134x = codec->private_data;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
u8 hw_params;
if (substream == uda134x->slave_substream) {
@@ -294,7 +295,7 @@ static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct uda134x_priv *uda134x = codec->private_data;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
clk_id, freq, dir);
@@ -316,7 +317,7 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct uda134x_priv *uda134x = codec->private_data;
+ struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
pr_debug("%s fmt: %08X\n", __func__, fmt);
@@ -431,6 +432,14 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
+static const struct snd_kcontrol_new uda1345_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
static struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
@@ -486,6 +495,7 @@ static int uda134x_soc_probe(struct platform_device *pdev)
case UDA134X_UDA1340:
case UDA134X_UDA1341:
case UDA134X_UDA1344:
+ case UDA134X_UDA1345:
break;
default:
printk(KERN_ERR "UDA134X SoC codec: "
@@ -503,7 +513,7 @@ static int uda134x_soc_probe(struct platform_device *pdev)
uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
if (uda134x == NULL)
goto priv_err;
- codec->private_data = uda134x;
+ snd_soc_codec_set_drvdata(codec, uda134x);
codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
GFP_KERNEL);
@@ -551,6 +561,10 @@ static int uda134x_soc_probe(struct platform_device *pdev)
ret = snd_soc_add_controls(codec, uda1341_snd_controls,
ARRAY_SIZE(uda1341_snd_controls));
break;
+ case UDA134X_UDA1345:
+ ret = snd_soc_add_controls(codec, uda1345_snd_controls,
+ ARRAY_SIZE(uda1345_snd_controls));
+ break;
default:
printk(KERN_ERR "%s unknown codec type: %d",
__func__, pd->model);
@@ -567,7 +581,7 @@ static int uda134x_soc_probe(struct platform_device *pdev)
pcm_err:
kfree(codec->reg_cache);
reg_err:
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
priv_err:
kfree(codec);
return ret;
@@ -585,7 +599,7 @@ static int uda134x_soc_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec->reg_cache);
kfree(codec);
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index a2763c2e7348..2f925a27dcde 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -137,7 +137,7 @@ static void uda1380_flush_work(struct work_struct *work)
{
int bit, reg;
- for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
+ for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
reg = 0x10 + bit;
pr_debug("uda1380: flush reg %x val %x:\n", reg,
uda1380_read_reg_cache(uda1380_codec, reg));
@@ -476,7 +476,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct uda1380_priv *uda1380 = codec->private_data;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
switch (cmd) {
@@ -670,7 +670,6 @@ static int uda1380_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- uda1380_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -774,7 +773,7 @@ static int uda1380_register(struct uda1380_priv *uda1380)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = uda1380;
+ snd_soc_codec_set_drvdata(codec, uda1380);
codec->name = "UDA1380";
codec->owner = THIS_MODULE;
codec->read = uda1380_read_reg_cache;
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
new file mode 100644
index 000000000000..002e289d1255
--- /dev/null
+++ b/sound/soc/codecs/wm2000.c
@@ -0,0 +1,888 @@
+/*
+ * wm2000.c -- WM2000 ALSA Soc Audio driver
+ *
+ * Copyright 2008-2010 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.
+ *
+ * The download image for the WM2000 will be requested as
+ * 'wm2000_anc.bin' by default (overridable via platform data) at
+ * runtime and is expected to be in flat binary format. This is
+ * generated by Wolfson configuration tools and includes
+ * system-specific callibration information. If supplied as a
+ * sequence of ASCII-encoded hexidecimal bytes this can be converted
+ * into a flat binary with a command such as this on the command line:
+ *
+ * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }'
+ * < file > wm2000_anc.bin
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/slab.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/wm2000.h>
+
+#include "wm2000.h"
+
+enum wm2000_anc_mode {
+ ANC_ACTIVE = 0,
+ ANC_BYPASS = 1,
+ ANC_STANDBY = 2,
+ ANC_OFF = 3,
+};
+
+struct wm2000_priv {
+ struct i2c_client *i2c;
+
+ enum wm2000_anc_mode anc_mode;
+
+ unsigned int anc_active:1;
+ unsigned int anc_eng_ena:1;
+ unsigned int spk_ena:1;
+
+ unsigned int mclk_div:1;
+ unsigned int speech_clarity:1;
+
+ int anc_download_size;
+ char *anc_download;
+};
+
+static struct i2c_client *wm2000_i2c;
+
+static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[3];
+ int ret;
+
+ data[0] = (reg >> 8) & 0xff;
+ data[1] = reg & 0xff;
+ data[2] = value & 0xff;
+
+ dev_vdbg(&i2c->dev, "write %x = %x\n", reg, value);
+
+ ret = i2c_master_send(i2c, data, 3);
+ if (ret == 3)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static unsigned int wm2000_read(struct i2c_client *i2c, unsigned int r)
+{
+ struct i2c_msg xfer[2];
+ u8 reg[2];
+ u8 data;
+ int ret;
+
+ /* Write register */
+ reg[0] = (r >> 8) & 0xff;
+ reg[1] = r & 0xff;
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = sizeof(reg);
+ xfer[0].buf = &reg[0];
+
+ /* Read data */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 1;
+ xfer[1].buf = &data;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret != 2) {
+ dev_err(&i2c->dev, "i2c_transfer() returned %d\n", ret);
+ return 0;
+ }
+
+ dev_vdbg(&i2c->dev, "read %x from %x\n", data, r);
+
+ return data;
+}
+
+static void wm2000_reset(struct wm2000_priv *wm2000)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+ wm2000_write(i2c, WM2000_REG_ID1, 0);
+
+ wm2000->anc_mode = ANC_OFF;
+}
+
+static int wm2000_poll_bit(struct i2c_client *i2c,
+ unsigned int reg, u8 mask, int timeout)
+{
+ int val;
+
+ val = wm2000_read(i2c, reg);
+
+ while (!(val & mask) && --timeout) {
+ msleep(1);
+ val = wm2000_read(i2c, reg);
+ }
+
+ if (timeout == 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int wm2000_power_up(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+ int ret, timeout;
+
+ BUG_ON(wm2000->anc_mode != ANC_OFF);
+
+ dev_dbg(&i2c->dev, "Beginning power up\n");
+
+ if (!wm2000->mclk_div) {
+ dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+ WM2000_MCLK_DIV2_ENA_CLR);
+ } else {
+ dev_dbg(&i2c->dev, "Enabling MCLK divider\n");
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+ WM2000_MCLK_DIV2_ENA_SET);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET);
+
+ /* Wait for ANC engine to become ready */
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE, 1)) {
+ dev_err(&i2c->dev, "ANC engine failed to reset\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_BOOT_COMPLETE, 1)) {
+ dev_err(&i2c->dev, "ANC engine failed to initialise\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+
+ /* Open code download of the data since it is the only bulk
+ * write we do. */
+ dev_dbg(&i2c->dev, "Downloading %d bytes\n",
+ wm2000->anc_download_size - 2);
+
+ ret = i2c_master_send(i2c, wm2000->anc_download,
+ wm2000->anc_download_size);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret);
+ return ret;
+ }
+ if (ret != wm2000->anc_download_size) {
+ dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n",
+ ret, wm2000->anc_download_size);
+ return -EIO;
+ }
+
+ dev_dbg(&i2c->dev, "Download complete\n");
+
+ if (analogue) {
+ timeout = 248;
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, timeout / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ } else {
+ timeout = 10;
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ }
+
+ ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
+ if (wm2000->speech_clarity)
+ ret &= ~WM2000_SPEECH_CLARITY;
+ else
+ ret |= WM2000_SPEECH_CLARITY;
+ wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
+
+ wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
+ wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02);
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE, timeout)) {
+ dev_err(&i2c->dev, "Timed out waiting for device after %dms\n",
+ timeout * 10);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(&i2c->dev, "ANC active\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue active\n");
+ wm2000->anc_mode = ANC_ACTIVE;
+
+ return 0;
+}
+
+static int wm2000_power_down(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+ int timeout;
+
+ if (analogue) {
+ timeout = 248;
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, timeout / 4);
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_POWER_DOWN);
+ } else {
+ timeout = 10;
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_POWER_DOWN);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_POWER_DOWN_COMPLETE, timeout)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC power down\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE, 1)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(&i2c->dev, "powered off\n");
+ wm2000->anc_mode = ANC_OFF;
+
+ return 0;
+}
+
+static int wm2000_enter_bypass(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ BUG_ON(wm2000->anc_mode != ANC_ACTIVE);
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_BYPASS_ENTRY);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_BYPASS_ENTRY);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_ANC_DISABLED, 10)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC disable\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+ WM2000_ANC_ENG_IDLE, 1)) {
+ dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+ wm2000->anc_mode = ANC_BYPASS;
+ dev_dbg(&i2c->dev, "bypass enabled\n");
+
+ return 0;
+}
+
+static int wm2000_exit_bypass(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ BUG_ON(wm2000->anc_mode != ANC_BYPASS);
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+ if (analogue) {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ } else {
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_MOUSE_ENABLE |
+ WM2000_MODE_THERMAL_ENABLE);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE, 10)) {
+ dev_err(&i2c->dev, "Timed out waiting for MOUSE\n");
+ return -ETIMEDOUT;
+ }
+
+ wm2000->anc_mode = ANC_ACTIVE;
+ dev_dbg(&i2c->dev, "MOUSE active\n");
+
+ return 0;
+}
+
+static int wm2000_enter_standby(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+ int timeout;
+
+ BUG_ON(wm2000->anc_mode != ANC_ACTIVE);
+
+ if (analogue) {
+ timeout = 248;
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, timeout / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_STANDBY_ENTRY);
+ } else {
+ timeout = 10;
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_STANDBY_ENTRY);
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_ANC_DISABLED, timeout)) {
+ dev_err(&i2c->dev,
+ "Timed out waiting for ANC disable after 1ms\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE,
+ 1)) {
+ dev_err(&i2c->dev,
+ "Timed out waiting for standby after %dms\n",
+ timeout * 10);
+ return -ETIMEDOUT;
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+ wm2000->anc_mode = ANC_STANDBY;
+ dev_dbg(&i2c->dev, "standby\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue disabled\n");
+
+ return 0;
+}
+
+static int wm2000_exit_standby(struct i2c_client *i2c, int analogue)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+ int timeout;
+
+ BUG_ON(wm2000->anc_mode != ANC_STANDBY);
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+ if (analogue) {
+ timeout = 248;
+ wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, timeout / 4);
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_ANA_SEQ_INCLUDE |
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_MOUSE_ENABLE);
+ } else {
+ timeout = 10;
+
+ wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+ WM2000_MODE_THERMAL_ENABLE |
+ WM2000_MODE_MOUSE_ENABLE);
+ }
+
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+ wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+ if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+ WM2000_STATUS_MOUSE_ACTIVE, timeout)) {
+ dev_err(&i2c->dev, "Timed out waiting for MOUSE after %dms\n",
+ timeout * 10);
+ return -ETIMEDOUT;
+ }
+
+ wm2000->anc_mode = ANC_ACTIVE;
+ dev_dbg(&i2c->dev, "MOUSE active\n");
+ if (analogue)
+ dev_dbg(&i2c->dev, "Analogue enabled\n");
+
+ return 0;
+}
+
+typedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue);
+
+static struct {
+ enum wm2000_anc_mode source;
+ enum wm2000_anc_mode dest;
+ int analogue;
+ wm2000_mode_fn step[2];
+} anc_transitions[] = {
+ {
+ .source = ANC_OFF,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_power_up,
+ },
+ },
+ {
+ .source = ANC_OFF,
+ .dest = ANC_STANDBY,
+ .step = {
+ wm2000_power_up,
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_OFF,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_power_up,
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_STANDBY,
+ .analogue = 1,
+ .step = {
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_ACTIVE,
+ .dest = ANC_OFF,
+ .analogue = 1,
+ .step = {
+ wm2000_power_down,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_bypass,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_STANDBY,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_bypass,
+ wm2000_enter_standby,
+ },
+ },
+ {
+ .source = ANC_BYPASS,
+ .dest = ANC_OFF,
+ .step = {
+ wm2000_exit_bypass,
+ wm2000_power_down,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_ACTIVE,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_standby,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_BYPASS,
+ .analogue = 1,
+ .step = {
+ wm2000_exit_standby,
+ wm2000_enter_bypass,
+ },
+ },
+ {
+ .source = ANC_STANDBY,
+ .dest = ANC_OFF,
+ .step = {
+ wm2000_exit_standby,
+ wm2000_power_down,
+ },
+ },
+};
+
+static int wm2000_anc_transition(struct wm2000_priv *wm2000,
+ enum wm2000_anc_mode mode)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+ int i, j;
+ int ret;
+
+ if (wm2000->anc_mode == mode)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(anc_transitions); i++)
+ if (anc_transitions[i].source == wm2000->anc_mode &&
+ anc_transitions[i].dest == mode)
+ break;
+ if (i == ARRAY_SIZE(anc_transitions)) {
+ dev_err(&i2c->dev, "No transition for %d->%d\n",
+ wm2000->anc_mode, mode);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) {
+ if (!anc_transitions[i].step[j])
+ break;
+ ret = anc_transitions[i].step[j](i2c,
+ anc_transitions[i].analogue);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
+{
+ struct i2c_client *i2c = wm2000->i2c;
+ enum wm2000_anc_mode mode;
+
+ if (wm2000->anc_eng_ena && wm2000->spk_ena)
+ if (wm2000->anc_active)
+ mode = ANC_ACTIVE;
+ else
+ mode = ANC_BYPASS;
+ else
+ mode = ANC_STANDBY;
+
+ dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n",
+ mode, wm2000->anc_eng_ena, !wm2000->spk_ena,
+ wm2000->anc_active);
+
+ return wm2000_anc_transition(wm2000, mode);
+}
+
+static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+ ucontrol->value.enumerated.item[0] = wm2000->anc_active;
+
+ return 0;
+}
+
+static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+ int anc_active = ucontrol->value.enumerated.item[0];
+
+ if (anc_active > 1)
+ return -EINVAL;
+
+ wm2000->anc_active = anc_active;
+
+ return wm2000_anc_set_mode(wm2000);
+}
+
+static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+ ucontrol->value.enumerated.item[0] = wm2000->spk_ena;
+
+ return 0;
+}
+
+static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+ int val = ucontrol->value.enumerated.item[0];
+
+ if (val > 1)
+ return -EINVAL;
+
+ wm2000->spk_ena = val;
+
+ return wm2000_anc_set_mode(wm2000);
+}
+
+static const struct snd_kcontrol_new wm2000_controls[] = {
+ SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0,
+ wm2000_anc_mode_get,
+ wm2000_anc_mode_put),
+ SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0,
+ wm2000_speaker_get,
+ wm2000_speaker_put),
+};
+
+static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ wm2000->anc_eng_ena = 1;
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ wm2000->anc_eng_ena = 0;
+
+ return wm2000_anc_set_mode(wm2000);
+}
+
+static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("WM2000 SPKN"),
+SND_SOC_DAPM_OUTPUT("WM2000 SPKP"),
+
+SND_SOC_DAPM_INPUT("WM2000 LINN"),
+SND_SOC_DAPM_INPUT("WM2000 LINP"),
+
+SND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0,
+ wm2000_anc_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "WM2000 SPKN", NULL, "ANC Engine" },
+ { "WM2000 SPKP", NULL, "ANC Engine" },
+ { "ANC Engine", NULL, "WM2000 LINN" },
+ { "ANC Engine", NULL, "WM2000 LINP" },
+};
+
+/* Called from the machine driver */
+int wm2000_add_controls(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ if (!wm2000_i2c) {
+ pr_err("WM2000 not yet probed\n");
+ return -ENODEV;
+ }
+
+ ret = snd_soc_dapm_new_controls(codec, wm2000_dapm_widgets,
+ ARRAY_SIZE(wm2000_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ if (ret < 0)
+ return ret;
+
+ return snd_soc_add_controls(codec, wm2000_controls,
+ ARRAY_SIZE(wm2000_controls));
+}
+EXPORT_SYMBOL_GPL(wm2000_add_controls);
+
+static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct wm2000_priv *wm2000;
+ struct wm2000_platform_data *pdata;
+ const char *filename;
+ const struct firmware *fw;
+ int reg, ret;
+ u16 id;
+
+ if (wm2000_i2c) {
+ dev_err(&i2c->dev, "Another WM2000 is already registered\n");
+ return -EINVAL;
+ }
+
+ wm2000 = kzalloc(sizeof(struct wm2000_priv), GFP_KERNEL);
+ if (wm2000 == NULL) {
+ dev_err(&i2c->dev, "Unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ /* Verify that this is a WM2000 */
+ reg = wm2000_read(i2c, WM2000_REG_ID1);
+ id = reg << 8;
+ reg = wm2000_read(i2c, WM2000_REG_ID2);
+ id |= reg & 0xff;
+
+ if (id != 0x2000) {
+ dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ reg = wm2000_read(i2c, WM2000_REG_REVISON);
+ dev_info(&i2c->dev, "revision %c\n", reg + 'A');
+
+ filename = "wm2000_anc.bin";
+ pdata = dev_get_platdata(&i2c->dev);
+ if (pdata) {
+ wm2000->mclk_div = pdata->mclkdiv2;
+ wm2000->speech_clarity = !pdata->speech_enh_disable;
+
+ if (pdata->download_file)
+ filename = pdata->download_file;
+ }
+
+ ret = request_firmware(&fw, filename, &i2c->dev);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
+ goto err;
+ }
+
+ /* Pre-cook the concatenation of the register address onto the image */
+ wm2000->anc_download_size = fw->size + 2;
+ wm2000->anc_download = kmalloc(wm2000->anc_download_size, GFP_KERNEL);
+ if (wm2000->anc_download == NULL) {
+ dev_err(&i2c->dev, "Out of memory\n");
+ ret = -ENOMEM;
+ goto err_fw;
+ }
+
+ wm2000->anc_download[0] = 0x80;
+ wm2000->anc_download[1] = 0x00;
+ memcpy(wm2000->anc_download + 2, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ dev_set_drvdata(&i2c->dev, wm2000);
+ wm2000->anc_eng_ena = 1;
+ wm2000->i2c = i2c;
+
+ wm2000_reset(wm2000);
+
+ /* This will trigger a transition to standby mode by default */
+ wm2000_anc_set_mode(wm2000);
+
+ wm2000_i2c = i2c;
+
+ return 0;
+
+err_fw:
+ release_firmware(fw);
+err:
+ kfree(wm2000);
+ return ret;
+}
+
+static __devexit int wm2000_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ wm2000_anc_transition(wm2000, ANC_OFF);
+
+ wm2000_i2c = NULL;
+ kfree(wm2000->anc_download);
+ kfree(wm2000);
+
+ return 0;
+}
+
+static void wm2000_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+#ifdef CONFIG_PM
+static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ return wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+static int wm2000_i2c_resume(struct i2c_client *i2c)
+{
+ struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+ return wm2000_anc_set_mode(wm2000);
+}
+#else
+#define wm2000_i2c_suspend NULL
+#define wm2000_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm2000_i2c_id[] = {
+ { "wm2000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
+
+static struct i2c_driver wm2000_i2c_driver = {
+ .driver = {
+ .name = "wm2000",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm2000_i2c_probe,
+ .remove = __devexit_p(wm2000_i2c_remove),
+ .suspend = wm2000_i2c_suspend,
+ .resume = wm2000_i2c_resume,
+ .shutdown = wm2000_i2c_shutdown,
+ .id_table = wm2000_i2c_id,
+};
+
+static int __init wm2000_init(void)
+{
+ return i2c_add_driver(&wm2000_i2c_driver);
+}
+module_init(wm2000_init);
+
+static void __exit wm2000_exit(void)
+{
+ i2c_del_driver(&wm2000_i2c_driver);
+}
+module_exit(wm2000_exit);
+
+MODULE_DESCRIPTION("ASoC WM2000 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm2000.h b/sound/soc/codecs/wm2000.h
new file mode 100644
index 000000000000..c18e261c3c7f
--- /dev/null
+++ b/sound/soc/codecs/wm2000.h
@@ -0,0 +1,79 @@
+/*
+ * wm2000.h -- WM2000 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM2000_H
+#define _WM2000_H
+
+struct wm2000_setup_data {
+ unsigned short i2c_address;
+ int mclk_div; /* Set to a non-zero value if MCLK_DIV_2 required */
+};
+
+extern int wm2000_add_controls(struct snd_soc_codec *codec);
+
+extern struct snd_soc_dai wm2000_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm2000;
+
+#define WM2000_REG_SYS_START 0x8000
+#define WM2000_REG_SPEECH_CLARITY 0x8fef
+#define WM2000_REG_SYS_WATCHDOG 0x8ff6
+#define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
+#define WM2000_REG_ANA_VMID_PU_TIME 0x8ff8
+#define WM2000_REG_CAT_FLTR_INDX 0x8ff9
+#define WM2000_REG_CAT_GAIN_0 0x8ffa
+#define WM2000_REG_SYS_STATUS 0x8ffc
+#define WM2000_REG_SYS_MODE_CNTRL 0x8ffd
+#define WM2000_REG_SYS_START0 0x8ffe
+#define WM2000_REG_SYS_START1 0x8fff
+#define WM2000_REG_ID1 0xf000
+#define WM2000_REG_ID2 0xf001
+#define WM2000_REG_REVISON 0xf002
+#define WM2000_REG_SYS_CTL1 0xf003
+#define WM2000_REG_SYS_CTL2 0xf004
+#define WM2000_REG_ANC_STAT 0xf005
+#define WM2000_REG_IF_CTL 0xf006
+
+/* SPEECH_CLARITY */
+#define WM2000_SPEECH_CLARITY 0x01
+
+/* SYS_STATUS */
+#define WM2000_STATUS_MOUSE_ACTIVE 0x40
+#define WM2000_STATUS_CAT_FREQ_COMPLETE 0x20
+#define WM2000_STATUS_CAT_GAIN_COMPLETE 0x10
+#define WM2000_STATUS_THERMAL_SHUTDOWN_COMPLETE 0x08
+#define WM2000_STATUS_ANC_DISABLED 0x04
+#define WM2000_STATUS_POWER_DOWN_COMPLETE 0x02
+#define WM2000_STATUS_BOOT_COMPLETE 0x01
+
+/* SYS_MODE_CNTRL */
+#define WM2000_MODE_ANA_SEQ_INCLUDE 0x80
+#define WM2000_MODE_MOUSE_ENABLE 0x40
+#define WM2000_MODE_CAT_FREQ_ENABLE 0x20
+#define WM2000_MODE_CAT_GAIN_ENABLE 0x10
+#define WM2000_MODE_BYPASS_ENTRY 0x08
+#define WM2000_MODE_STANDBY_ENTRY 0x04
+#define WM2000_MODE_THERMAL_ENABLE 0x02
+#define WM2000_MODE_POWER_DOWN 0x01
+
+/* SYS_CTL1 */
+#define WM2000_SYS_STBY 0x01
+
+/* SYS_CTL2 */
+#define WM2000_MCLK_DIV2_ENA_CLR 0x80
+#define WM2000_MCLK_DIV2_ENA_SET 0x40
+#define WM2000_ANC_ENG_CLR 0x20
+#define WM2000_ANC_ENG_SET 0x10
+#define WM2000_ANC_INT_N_CLR 0x08
+#define WM2000_ANC_INT_N_SET 0x04
+#define WM2000_RAM_CLR 0x02
+#define WM2000_RAM_SET 0x01
+
+/* ANC_STAT */
+#define WM2000_ANC_ENG_IDLE 0x01
+
+#endif
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 718ef912e758..0221ca79b3ae 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
@@ -54,6 +55,7 @@ struct wm8350_output {
struct wm8350_jack_data {
struct snd_soc_jack *jack;
int report;
+ int short_report;
};
struct wm8350_data {
@@ -62,6 +64,7 @@ struct wm8350_data {
struct wm8350_output out2;
struct wm8350_jack_data hpl;
struct wm8350_jack_data hpr;
+ struct wm8350_jack_data mic;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
int fll_freq_out;
int fll_freq_in;
@@ -93,7 +96,7 @@ static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
*/
static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
{
- struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out1 = &wm8350_data->out1;
struct wm8350 *wm8350 = codec->control_data;
int left_complete = 0, right_complete = 0;
@@ -159,7 +162,7 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
*/
static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
{
- struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out2 = &wm8350_data->out2;
struct wm8350 *wm8350 = codec->control_data;
int left_complete = 0, right_complete = 0;
@@ -229,7 +232,7 @@ static void wm8350_pga_work(struct work_struct *work)
{
struct snd_soc_codec *codec =
container_of(work, struct snd_soc_codec, delayed_work.work);
- struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out1 = &wm8350_data->out1,
*out2 = &wm8350_data->out2;
int i, out1_complete, out2_complete;
@@ -276,7 +279,7 @@ static int pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out;
switch (w->shift) {
@@ -321,7 +324,7 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out = NULL;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
@@ -364,7 +367,7 @@ static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out1 = &wm8350_priv->out1;
struct wm8350_output *out2 = &wm8350_priv->out2;
struct soc_mixer_control *mc =
@@ -423,8 +426,8 @@ static const struct soc_enum wm8350_enum[] = {
SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
};
-static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
-static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(pre_amp_tlv, -1200, 3525, 0);
+static DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 600, 0);
static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
@@ -1106,7 +1109,7 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8350 *wm8350 = codec->control_data;
- struct wm8350_data *priv = codec->private_data;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div;
int ret = 0;
u16 fll_1, fll_4;
@@ -1158,7 +1161,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct wm8350 *wm8350 = codec->control_data;
- struct wm8350_data *priv = codec->private_data;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
struct wm8350_audio_platform_data *platform =
wm8350->codec.platform_data;
u16 pm1;
@@ -1334,9 +1337,6 @@ static int wm8350_resume(struct platform_device *pdev)
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
- wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
-
return 0;
}
@@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
int mask;
struct wm8350_jack_data *jack = NULL;
- switch (irq) {
+ switch (irq - wm8350->irq_base) {
case WM8350_IRQ_CODEC_JCK_DET_L:
jack = &priv->hpl;
mask = WM8350_JACK_L_LVL;
@@ -1391,12 +1391,13 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
* @jack: jack to report detection events on
* @report: value to report
*
- * Enables the headphone jack detection of the WM8350.
+ * Enables the headphone jack detection of the WM8350. If no report
+ * is specified then detection is disabled.
*/
int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
struct snd_soc_jack *jack, int report)
{
- struct wm8350_data *priv = codec->private_data;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
struct wm8350 *wm8350 = codec->control_data;
int irq;
int ena;
@@ -1420,16 +1421,74 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
return -EINVAL;
}
- wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
- wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
+ if (report) {
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+ wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
+ } else {
+ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, ena);
+ }
/* Sync status */
- wm8350_hp_jack_handler(irq, priv);
+ wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
+static irqreturn_t wm8350_mic_handler(int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ struct wm8350 *wm8350 = priv->codec.control_data;
+ u16 reg;
+ int report = 0;
+
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
+ if (reg & WM8350_JACK_MICSCD_LVL)
+ report |= priv->mic.short_report;
+ if (reg & WM8350_JACK_MICSD_LVL)
+ report |= priv->mic.report;
+
+ snd_soc_jack_report(priv->mic.jack, report,
+ priv->mic.report | priv->mic.short_report);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * wm8350_mic_jack_detect - Enable microphone jack detection.
+ *
+ * @codec: WM8350 codec
+ * @jack: jack to report detection events on
+ * @detect_report: value to report when presence detected
+ * @short_report: value to report when microphone short detected
+ *
+ * Enables the microphone jack detection of the WM8350. If both reports
+ * are specified as zero then detection is disabled.
+ */
+int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int detect_report, int short_report)
+{
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct wm8350 *wm8350 = codec->control_data;
+
+ priv->mic.jack = jack;
+ priv->mic.report = detect_report;
+ priv->mic.short_report = short_report;
+
+ if (detect_report || short_report) {
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_1,
+ WM8350_MIC_DET_ENA);
+ } else {
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_1,
+ WM8350_MIC_DET_ENA);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
+
static struct snd_soc_codec *wm8350_codec;
static int wm8350_probe(struct platform_device *pdev)
@@ -1447,7 +1506,7 @@ static int wm8350_probe(struct platform_device *pdev)
socdev->card->codec = wm8350_codec;
codec = socdev->card->codec;
wm8350 = codec->control_data;
- priv = codec->private_data;
+ priv = snd_soc_codec_get_drvdata(codec);
/* Enable the codec */
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
@@ -1493,6 +1552,10 @@ static int wm8350_probe(struct platform_device *pdev)
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
wm8350_hp_jack_handler, 0, "Right jack detect",
priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
+ wm8350_mic_handler, 0, "Microphone short", priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
+ wm8350_mic_handler, 0, "Microphone detect", priv);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@@ -1514,18 +1577,21 @@ static int wm8350_remove(struct platform_device *pdev)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8350 *wm8350 = codec->control_data;
- struct wm8350_data *priv = codec->private_data;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
int ret;
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
- wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
- wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICD, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
priv->hpl.jack = NULL;
priv->hpr.jack = NULL;
+ priv->mic.jack = NULL;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(&codec->delayed_work);
@@ -1630,7 +1696,7 @@ static __devinit int wm8350_codec_probe(struct platform_device *pdev)
codec->dai = &wm8350_dai;
codec->num_dai = 1;
codec->reg_cache_size = WM8350_MAX_REGISTER;
- codec->private_data = priv;
+ snd_soc_codec_set_drvdata(codec, priv);
codec->control_data = wm8350;
/* Put the codec into reset if it wasn't already */
@@ -1662,7 +1728,7 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
{
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = wm8350->codec.codec;
- struct wm8350_data *priv = codec->private_data;
+ struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
snd_soc_unregister_dai(&wm8350_dai);
snd_soc_unregister_codec(codec);
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
index d088eb4b88bb..9ed0467c71db 100644
--- a/sound/soc/codecs/wm8350.h
+++ b/sound/soc/codecs/wm8350.h
@@ -25,5 +25,8 @@ enum wm8350_jack {
int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
struct snd_soc_jack *jack, int report);
+int wm8350_mic_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int detect_report, int short_report);
#endif
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b432f4d4a324..8f294066b0ed 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
@@ -76,7 +77,7 @@ struct wm8400_priv {
static inline unsigned int wm8400_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
if (reg == WM8400_INTDRIVBITS)
return wm8400->fake_register;
@@ -90,7 +91,7 @@ static inline unsigned int wm8400_read(struct snd_soc_codec *codec,
static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
if (reg == WM8400_INTDRIVBITS) {
wm8400->fake_register = value;
@@ -101,26 +102,26 @@ static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg,
static void wm8400_codec_reset(struct snd_soc_codec *codec)
{
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
wm8400_reset_codec_reg_cache(wm8400->wm8400);
}
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
-static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
-static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0);
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -2100, 0, 0);
-static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
-static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
-static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
-static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
-static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -439,7 +440,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
/* INMIX dB values */
static const unsigned int in_mix_tlv[] = {
TLV_DB_RANGE_HEAD(1),
- 0,7, TLV_DB_LINEAR_ITEM(-1200, 600),
+ 0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
};
/* Left In PGA Connections */
@@ -925,7 +926,7 @@ static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
wm8400->sysclk = freq;
return 0;
@@ -1014,7 +1015,7 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
struct fll_factors factors;
int ret;
u16 reg;
@@ -1203,7 +1204,7 @@ static int wm8400_mute(struct snd_soc_dai *dai, int mute)
static int wm8400_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct wm8400_priv *wm8400 = codec->private_data;
+ struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec);
u16 val;
int ret;
@@ -1466,7 +1467,7 @@ static int wm8400_codec_probe(struct platform_device *dev)
return -ENOMEM;
codec = &priv->codec;
- codec->private_data = priv;
+ snd_soc_codec_set_drvdata(codec, priv);
codec->control_data = dev_get_drvdata(&dev->dev);
priv->wm8400 = dev_get_drvdata(&dev->dev);
@@ -1529,7 +1530,7 @@ err:
static int __exit wm8400_codec_remove(struct platform_device *dev)
{
- struct wm8400_priv *priv = wm8400_codec->private_data;
+ struct wm8400_priv *priv = snd_soc_codec_get_drvdata(wm8400_codec);
u16 reg;
snd_soc_unregister_dai(&wm8400_dai);
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index af8cb6995a1f..0f7bcb61071a 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -556,7 +557,7 @@ static int wm8510_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8510_set_bias_level(codec, codec->suspend_bias_level);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index d3a61d7ea0c5..37242a7d3077 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -137,7 +138,7 @@ static int wm8523_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8523_priv *wm8523 = codec->private_data;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
@@ -163,7 +164,7 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8523_priv *wm8523 = codec->private_data;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
int i;
u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
u16 aifctrl2 = snd_soc_read(codec, WM8523_AIF_CTRL2);
@@ -210,7 +211,7 @@ static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8523_priv *wm8523 = codec->private_data;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
int i;
@@ -317,7 +318,7 @@ static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8523_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct wm8523_priv *wm8523 = codec->private_data;
+ struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
int ret, i;
switch (level) {
@@ -488,7 +489,7 @@ static int wm8523_register(struct wm8523_priv *wm8523,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8523;
+ snd_soc_codec_set_drvdata(codec, wm8523);
codec->name = "WM8523";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index d077df6f5e75..c3571ee5c11b 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -25,6 +25,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -411,7 +412,7 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
{
int offset;
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8580_priv *wm8580 = codec->private_data;
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
struct pll_state *state;
struct _pll_div pll_div;
unsigned int reg;
@@ -839,7 +840,7 @@ static int wm8580_register(struct wm8580_priv *wm8580,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8580;
+ snd_soc_codec_set_drvdata(codec, wm8580);
codec->name = "WM8580";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 24a35603bcf7..effb14eee7d4 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -162,7 +163,7 @@ static int wm8711_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8711_priv *wm8711 = codec->private_data;
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
int i = get_coeff(wm8711->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
@@ -226,7 +227,7 @@ static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8711_priv *wm8711 = codec->private_data;
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -375,7 +376,7 @@ static int wm8711_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8711_set_bias_level(codec, codec->suspend_bias_level);
+
return 0;
}
@@ -445,7 +446,7 @@ static int wm8711_register(struct wm8711_priv *wm8711,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8711;
+ snd_soc_codec_set_drvdata(codec, wm8711);
codec->name = "WM8711";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index d8ffbd641d71..9d1df2628136 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -13,6 +13,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -44,23 +45,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 +74,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 +89,57 @@ 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;
+ }
+
+ return 0;
+
+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;
}
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 3fb653ba363a..34be2d2b69ef 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -237,7 +238,7 @@ static int wm8728_resume(struct platform_device *pdev)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- wm8728_set_bias_level(codec, codec->suspend_bias_level);
+ wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 3a497810f939..0ab9b6355297 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -224,7 +225,7 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8731_priv *wm8731 = codec->private_data;
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
@@ -291,7 +292,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8731_priv *wm8731 = codec->private_data;
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -368,6 +369,10 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8731_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
u16 reg;
switch (level) {
@@ -376,6 +381,24 @@ static int wm8731_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) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* 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);
+ }
+ }
+
/* Clear PWROFF, gate CLKOUT, everything else as-is */
reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
@@ -383,17 +406,15 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
snd_soc_write(codec, WM8731_PWR, 0xffff);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
break;
}
codec->bias_level = level;
return 0;
}
-#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
- SNDRV_PCM_RATE_96000)
+#define WM8731_RATES SNDRV_PCM_RATE_8000_96000
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
@@ -431,12 +452,9 @@ static int wm8731_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 wm8731_priv *wm8731 = codec->private_data;
- snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
- regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
- wm8731->supplies);
+
return 0;
}
@@ -444,24 +462,8 @@ static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8731_priv *wm8731 = codec->private_data;
- int i, ret;
- u8 data[2];
- u16 *cache = codec->reg_cache;
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
- wm8731->supplies);
- if (ret != 0)
- return ret;
-
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
@@ -536,7 +538,7 @@ static int wm8731_register(struct wm8731_priv *wm8731,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8731;
+ snd_soc_codec_set_drvdata(codec, wm8731);
codec->name = "WM8731";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
@@ -605,6 +607,9 @@ static int wm8731_register(struct wm8731_priv *wm8731,
goto err_codec;
}
+ /* Regulators will have been enabled by bias management */
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+
return 0;
err_codec:
@@ -623,7 +628,6 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8731_dai);
snd_soc_unregister_codec(&wm8731->codec);
- regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
kfree(wm8731);
wm8731_codec = NULL;
@@ -704,7 +708,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
- .name = "WM8731 I2C Codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 475c67ac7818..9407e193fcc3 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -29,13 +30,6 @@
#include "wm8750.h"
-#define WM8750_VERSION "0.12"
-
-/* codec private data */
-struct wm8750_priv {
- unsigned int sysclk;
-};
-
/*
* wm8750 register cache
* We can't read the WM8750 register space when we
@@ -55,6 +49,13 @@ static const u16 wm8750_reg[] = {
0x0079, 0x0079, 0x0079, /* 40 */
};
+/* codec private data */
+struct wm8750_priv {
+ unsigned int sysclk;
+ struct snd_soc_codec codec;
+ u16 reg_cache[ARRAY_SIZE(wm8750_reg)];
+};
+
#define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0)
/*
@@ -482,7 +483,7 @@ static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8750_priv *wm8750 = codec->private_data;
+ struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -561,7 +562,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8750_priv *wm8750 = codec->private_data;
+ struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
int coeff = get_coeff(wm8750->sysclk, params_rate(params));
@@ -613,10 +614,16 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
- /* set vmid to 5k for quick power up */
- snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
break;
case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Set VMID to 5k */
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+
+ /* ...and ramp */
+ msleep(1000);
+ }
+
/* mute dac and set vmid to 500k, enable VREF */
snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
break;
@@ -660,13 +667,6 @@ struct snd_soc_dai wm8750_dai = {
};
EXPORT_SYMBOL_GPL(wm8750_dai);
-static void wm8750_work(struct work_struct *work)
-{
- struct snd_soc_codec *codec =
- container_of(work, struct snd_soc_codec, delayed_work.work);
- wm8750_set_bias_level(codec, codec->bias_level);
-}
-
static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -695,36 +695,92 @@ static int wm8750_resume(struct platform_device *pdev)
wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- /* charge wm8750 caps */
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
- wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
- codec->bias_level = SND_SOC_BIAS_ON;
- schedule_delayed_work(&codec->delayed_work,
- msecs_to_jiffies(1000));
+ return 0;
+}
+
+static struct snd_soc_codec *wm8750_codec;
+
+static int wm8750_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (!wm8750_codec) {
+ dev_err(&pdev->dev, "WM8750 codec not yet registered\n");
+ return -EINVAL;
+ }
+
+ socdev->card->codec = wm8750_codec;
+ codec = wm8750_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to create pcms\n");
+ goto err;
}
+ snd_soc_add_controls(codec, wm8750_snd_controls,
+ ARRAY_SIZE(wm8750_snd_controls));
+ wm8750_add_widgets(codec);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8750_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_wm8750 = {
+ .probe = wm8750_probe,
+ .remove = wm8750_remove,
+ .suspend = wm8750_suspend,
+ .resume = wm8750_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
+
/*
* initialise the WM8750 driver
* register the mixer and dsp interfaces with the kernel
*/
-static int wm8750_init(struct snd_soc_device *socdev,
- enum snd_soc_control_type control)
+static int wm8750_register(struct wm8750_priv *wm8750,
+ enum snd_soc_control_type control)
{
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_codec *codec = &wm8750->codec;
int reg, ret = 0;
+ if (wm8750_codec) {
+ dev_err(codec->dev, "Multiple WM8750 devices not supported\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
codec->name = "WM8750";
codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
codec->set_bias_level = wm8750_set_bias_level;
codec->dai = &wm8750_dai;
codec->num_dai = 1;
- codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
- codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ codec->reg_cache_size = ARRAY_SIZE(wm8750->reg_cache) + 1;
+ codec->reg_cache = &wm8750->reg_cache;
+ snd_soc_codec_set_drvdata(codec, wm8750);
+
+ memcpy(codec->reg_cache, wm8750_reg, sizeof(wm8750->reg_cache));
ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
if (ret < 0) {
@@ -738,17 +794,8 @@ static int wm8750_init(struct snd_soc_device *socdev,
goto err;
}
- /* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0) {
- printk(KERN_ERR "wm8750: failed to create pcms\n");
- goto err;
- }
-
/* charge output caps */
- wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
- codec->bias_level = SND_SOC_BIAS_STANDBY;
- schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
+ wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set the update bits */
reg = snd_soc_read(codec, WM8750_LDAC);
@@ -768,19 +815,37 @@ static int wm8750_init(struct snd_soc_device *socdev,
reg = snd_soc_read(codec, WM8750_RINVOL);
snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100);
- snd_soc_add_controls(codec, wm8750_snd_controls,
- ARRAY_SIZE(wm8750_snd_controls));
- wm8750_add_widgets(codec);
- return ret;
+ wm8750_codec = codec;
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dais(&wm8750_dai, 1);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
err:
- kfree(codec->reg_cache);
+ kfree(wm8750);
return ret;
}
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-static struct snd_soc_device *wm8750_socdev;
+static void wm8750_unregister(struct wm8750_priv *wm8750)
+{
+ wm8750_set_bias_level(&wm8750->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dais(&wm8750_dai, 1);
+ snd_soc_unregister_codec(&wm8750->codec);
+ kfree(wm8750);
+ wm8750_codec = NULL;
+}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -794,24 +859,26 @@ static struct snd_soc_device *wm8750_socdev;
static int wm8750_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct snd_soc_device *socdev = wm8750_socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- int ret;
+ struct snd_soc_codec *codec;
+ struct wm8750_priv *wm8750;
+
+ wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
+ if (wm8750 == NULL)
+ return -ENOMEM;
- i2c_set_clientdata(i2c, codec);
+ codec = &wm8750->codec;
codec->control_data = i2c;
+ i2c_set_clientdata(i2c, wm8750);
- ret = wm8750_init(socdev, SND_SOC_I2C);
- if (ret < 0)
- pr_err("failed to initialise WM8750\n");
+ codec->dev = &i2c->dev;
- return ret;
+ return wm8750_register(wm8750, SND_SOC_I2C);
}
static int wm8750_i2c_remove(struct i2c_client *client)
{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
+ struct wm8750_priv *wm8750 = i2c_get_clientdata(client);
+ wm8750_unregister(wm8750);
return 0;
}
@@ -830,66 +897,31 @@ static struct i2c_driver wm8750_i2c_driver = {
.remove = wm8750_i2c_remove,
.id_table = wm8750_i2c_id,
};
-
-static int wm8750_add_i2c_device(struct platform_device *pdev,
- const struct wm8750_setup_data *setup)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- int ret;
-
- ret = i2c_add_driver(&wm8750_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- return ret;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "wm8750", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
- }
-
- return 0;
-
-err_driver:
- i2c_del_driver(&wm8750_i2c_driver);
- return -ENODEV;
-}
#endif
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8750_spi_probe(struct spi_device *spi)
{
- struct snd_soc_device *socdev = wm8750_socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- int ret;
+ struct snd_soc_codec *codec;
+ struct wm8750_priv *wm8750;
+
+ wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
+ if (wm8750 == NULL)
+ return -ENOMEM;
+ codec = &wm8750->codec;
codec->control_data = spi;
+ codec->dev = &spi->dev;
- ret = wm8750_init(socdev, SND_SOC_SPI);
- if (ret < 0)
- dev_err(&spi->dev, "failed to initialise WM8750\n");
+ dev_set_drvdata(&spi->dev, wm8750);
- return ret;
+ return wm8750_register(wm8750, SND_SOC_SPI);
}
static int __devexit wm8750_spi_remove(struct spi_device *spi)
{
+ struct wm8750_priv *wm8750 = dev_get_drvdata(&spi->dev);
+ wm8750_unregister(wm8750);
return 0;
}
@@ -904,115 +936,31 @@ static struct spi_driver wm8750_spi_driver = {
};
#endif
-static int wm8750_probe(struct platform_device *pdev)
+static int __init wm8750_modinit(void)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8750_setup_data *setup = socdev->codec_data;
- struct snd_soc_codec *codec;
- struct wm8750_priv *wm8750;
int ret;
-
- pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
-
- wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
- if (wm8750 == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
-
- codec->private_data = wm8750;
- socdev->card->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
- wm8750_socdev = socdev;
- INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
-
- ret = -ENODEV;
-
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- ret = wm8750_add_i2c_device(pdev, setup);
- }
+ ret = i2c_add_driver(&wm8750_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8750 I2C driver: %d\n", ret);
#endif
#if defined(CONFIG_SPI_MASTER)
- if (setup->spi) {
- ret = spi_register_driver(&wm8750_spi_driver);
- if (ret != 0)
- printk(KERN_ERR "can't add spi driver");
- }
+ ret = spi_register_driver(&wm8750_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8750 SPI driver: %d\n", ret);
#endif
-
- if (ret != 0) {
- kfree(codec->private_data);
- kfree(codec);
- }
- return ret;
-}
-
-/*
- * This function forces any delayed work to be queued and run.
- */
-static int run_delayed_work(struct delayed_work *dwork)
-{
- int ret;
-
- /* cancel any work waiting to be queued. */
- ret = cancel_delayed_work(dwork);
-
- /* if there was any work waiting then we run it now and
- * wait for it's completion */
- if (ret) {
- schedule_delayed_work(dwork, 0);
- flush_scheduled_work();
- }
- return ret;
+ return 0;
}
+module_init(wm8750_modinit);
-/* power down chip */
-static int wm8750_remove(struct platform_device *pdev)
+static void __exit wm8750_exit(void)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
-
- if (codec->control_data)
- wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
- run_delayed_work(&codec->delayed_work);
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8750_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8750_spi_driver);
#endif
- kfree(codec->private_data);
- kfree(codec);
-
- return 0;
-}
-
-struct snd_soc_codec_device soc_codec_dev_wm8750 = {
- .probe = wm8750_probe,
- .remove = wm8750_remove,
- .suspend = wm8750_suspend,
- .resume = wm8750_resume,
-};
-EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
-
-static int __init wm8750_modinit(void)
-{
- return snd_soc_register_dai(&wm8750_dai);
-}
-module_init(wm8750_modinit);
-
-static void __exit wm8750_exit(void)
-{
- snd_soc_unregister_dai(&wm8750_dai);
}
module_exit(wm8750_exit);
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d6850dacda29..b59f349c5218 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -40,6 +40,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -850,7 +851,7 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8753_priv *wm8753 = codec->private_data;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -913,7 +914,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8753_priv *wm8753 = codec->private_data;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3;
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
@@ -1147,7 +1148,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8753_priv *wm8753 = codec->private_data;
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3;
int coeff;
@@ -1507,10 +1508,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 +1520,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)
@@ -1653,7 +1646,7 @@ static int wm8753_register(struct wm8753_priv *wm8753)
codec->num_dai = 2;
codec->reg_cache_size = ARRAY_SIZE(wm8753->reg_cache) + 1;
codec->reg_cache = &wm8753->reg_cache;
- codec->private_data = wm8753;
+ snd_soc_codec_set_drvdata(codec, wm8753);
memcpy(codec->reg_cache, wm8753_reg, sizeof(wm8753->reg_cache));
INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index ab2c0da18091..4e212ed62ea6 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -93,7 +94,6 @@ SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0),
static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AUX"),
-SND_SOC_DAPM_INPUT("AUX"),
SND_SOC_DAPM_INPUT("AIN1"),
SND_SOC_DAPM_INPUT("AIN2"),
@@ -226,7 +226,7 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8776_priv *wm8776 = codec->private_data;
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
int iface_reg, iface;
int ratio_shift, master;
int i;
@@ -303,7 +303,7 @@ static int wm8776_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8776_priv *wm8776 = codec->private_data;
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
BUG_ON(dai->id >= ARRAY_SIZE(wm8776->sysclk));
@@ -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);
@@ -488,7 +490,7 @@ static int wm8776_register(struct wm8776_priv *wm8776,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8776;
+ snd_soc_codec_set_drvdata(codec, wm8776);
codec->name = "WM8776";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index dbc368c08263..5da17a704e5a 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -744,7 +745,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
static int wm8900_set_fll(struct snd_soc_codec *codec,
int fll_id, unsigned int freq_in, unsigned int freq_out)
{
- struct wm8900_priv *wm8900 = codec->private_data;
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div;
unsigned int reg;
@@ -1131,7 +1132,7 @@ static int wm8900_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 wm8900_priv *wm8900 = codec->private_data;
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
int fll_out = wm8900->fll_out;
int fll_in = wm8900->fll_in;
int ret;
@@ -1155,7 +1156,7 @@ static int wm8900_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8900_priv *wm8900 = codec->private_data;
+ struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
u16 *cache;
int i, ret;
@@ -1205,7 +1206,7 @@ static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
codec = &wm8900->codec;
- codec->private_data = wm8900;
+ snd_soc_codec_set_drvdata(codec, wm8900);
codec->reg_cache = &wm8900->reg_cache[0];
codec->reg_cache_size = WM8900_MAXREG;
@@ -1304,7 +1305,7 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
wm8900_dai.dev = NULL;
- kfree(wm8900_codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(wm8900_codec));
wm8900_codec = NULL;
return 0;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 3595bd57c4eb..bf08282d5ee5 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -11,25 +11,27 @@
*
* TODO:
* - TDM mode configuration.
- * - Mic detect.
* - Digital microphone support.
- * - Interrupt support (mic detect and sequencer).
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
+#include <sound/wm8903.h>
#include "wm8903.h"
@@ -221,6 +223,14 @@ struct wm8903_priv {
int playback_active;
int capture_active;
+ struct completion wseq;
+
+ struct snd_soc_jack *mic_jack;
+ int mic_det;
+ int mic_short;
+ int mic_last_report;
+ int mic_delay;
+
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
};
@@ -243,13 +253,14 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
{
u16 reg[5];
struct i2c_client *i2c = codec->control_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
BUG_ON(start > 48);
- /* Enable the sequencer */
+ /* Enable the sequencer if it's not already on */
reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
- reg[0] |= WM8903_WSEQ_ENA;
- snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
+ reg[0] | WM8903_WSEQ_ENA);
dev_dbg(&i2c->dev, "Starting sequence at %d\n", start);
@@ -257,20 +268,19 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
start | WM8903_WSEQ_START);
/* Wait for it to complete. If we have the interrupt wired up then
- * we could block waiting for an interrupt, though polling may still
- * be desirable for diagnostic purposes.
+ * that will break us out of the poll early.
*/
do {
- msleep(10);
+ wait_for_completion_timeout(&wm8903->wseq,
+ msecs_to_jiffies(10));
reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
} while (reg[4] & WM8903_WSEQ_BUSY);
dev_dbg(&i2c->dev, "Sequence complete\n");
- /* Disable the sequencer again */
- snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
- reg[0] & ~WM8903_WSEQ_ENA);
+ /* Disable the sequencer again if we enabled it */
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
return 0;
}
@@ -411,7 +421,7 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct snd_soc_codec *codec = widget->codec;
- struct wm8903_priv *wm8903 = codec->private_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data;
u16 reg;
int ret;
@@ -992,7 +1002,7 @@ static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8903_priv *wm8903 = codec->private_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
wm8903->sysclk = freq;
@@ -1220,7 +1230,7 @@ static int wm8903_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8903_priv *wm8903 = codec->private_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@@ -1256,7 +1266,7 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8903_priv *wm8903 = codec->private_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
wm8903->playback_active--;
@@ -1276,7 +1286,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8903_priv *wm8903 = codec->private_data;
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data;
int fs = params_rate(params);
int bclk;
@@ -1435,6 +1445,116 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/**
+ * wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ
+ *
+ * @codec: WM8903 codec
+ * @jack: jack to report detection events on
+ * @det: value to report for presence detection
+ * @shrt: value to report for short detection
+ *
+ * Enable microphone detection via IRQ on the WM8903. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for WM8903 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * The current threasholds for detection should be configured using
+ * micdet_cfg in the platform data. Using this function will force on
+ * the microphone bias for the device.
+ */
+int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int det, int shrt)
+{
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+ int irq_mask = WM8903_MICDET_EINT | WM8903_MICSHRT_EINT;
+
+ dev_dbg(codec->dev, "Enabling microphone detection: %x %x\n",
+ det, shrt);
+
+ /* Store the configuration */
+ wm8903->mic_jack = jack;
+ wm8903->mic_det = det;
+ wm8903->mic_short = shrt;
+
+ /* Enable interrupts we've got a report configured for */
+ if (det)
+ irq_mask &= ~WM8903_MICDET_EINT;
+ if (shrt)
+ irq_mask &= ~WM8903_MICSHRT_EINT;
+
+ snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
+ WM8903_MICDET_EINT | WM8903_MICSHRT_EINT,
+ irq_mask);
+
+ if (det && shrt) {
+ /* Enable mic detection, this may not have been set through
+ * platform data (eg, if the defaults are OK). */
+ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
+ WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
+ snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ WM8903_MICDET_ENA, WM8903_MICDET_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
+ WM8903_MICDET_ENA, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8903_mic_detect);
+
+static irqreturn_t wm8903_irq(int irq, void *data)
+{
+ struct wm8903_priv *wm8903 = data;
+ struct snd_soc_codec *codec = &wm8903->codec;
+ int mic_report;
+ int int_pol;
+ int int_val = 0;
+ int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK);
+
+ int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;
+
+ if (int_val & WM8903_WSEQ_BUSY_EINT) {
+ dev_dbg(codec->dev, "Write sequencer done\n");
+ complete(&wm8903->wseq);
+ }
+
+ /*
+ * The rest is microphone jack detection. We need to manually
+ * invert the polarity of the interrupt after each event - to
+ * simplify the code keep track of the last state we reported
+ * and just invert the relevant bits in both the report and
+ * the polarity register.
+ */
+ mic_report = wm8903->mic_last_report;
+ int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1);
+
+ if (int_val & WM8903_MICSHRT_EINT) {
+ dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol);
+
+ mic_report ^= wm8903->mic_short;
+ int_pol ^= WM8903_MICSHRT_INV;
+ }
+
+ if (int_val & WM8903_MICDET_EINT) {
+ dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol);
+
+ mic_report ^= wm8903->mic_det;
+ int_pol ^= WM8903_MICDET_INV;
+
+ msleep(wm8903->mic_delay);
+ }
+
+ snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1,
+ WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol);
+
+ snd_soc_jack_report(wm8903->mic_jack, mic_report,
+ wm8903->mic_short | wm8903->mic_det);
+
+ wm8903->mic_last_report = mic_report;
+
+ return IRQ_HANDLED;
+}
+
#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_16000 | \
@@ -1509,7 +1629,6 @@ static int wm8903_resume(struct platform_device *pdev)
/* Bring the codec back up to standby first to minimise pop/clicks */
wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8903_set_bias_level(codec, codec->suspend_bias_level);
/* Sync back everything else */
if (tmp_cache) {
@@ -1529,9 +1648,11 @@ static struct snd_soc_codec *wm8903_codec;
static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8903_priv *wm8903;
struct snd_soc_codec *codec;
- int ret;
+ int ret, i;
+ int trigger, irq_pol;
u16 val;
wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
@@ -1553,8 +1674,9 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
codec->reg_cache = &wm8903->reg_cache[0];
- codec->private_data = wm8903;
+ snd_soc_codec_set_drvdata(codec, wm8903);
codec->volatile_register = wm8903_volatile_register;
+ init_completion(&wm8903->wseq);
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
@@ -1578,6 +1700,53 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
wm8903_reset(codec);
+ /* Set up GPIOs and microphone detection */
+ if (pdata) {
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
+ if (!pdata->gpio_cfg[i])
+ continue;
+
+ snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
+ pdata->gpio_cfg[i] & 0xffff);
+ }
+
+ snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0,
+ pdata->micdet_cfg);
+
+ /* Microphone detection needs the WSEQ clock */
+ if (pdata->micdet_cfg)
+ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
+ WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
+
+ wm8903->mic_delay = pdata->micdet_delay;
+ }
+
+ if (i2c->irq) {
+ if (pdata && pdata->irq_active_low) {
+ trigger = IRQF_TRIGGER_LOW;
+ irq_pol = WM8903_IRQ_POL;
+ } else {
+ trigger = IRQF_TRIGGER_HIGH;
+ irq_pol = 0;
+ }
+
+ snd_soc_update_bits(codec, WM8903_INTERRUPT_CONTROL,
+ WM8903_IRQ_POL, irq_pol);
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq,
+ trigger | IRQF_ONESHOT,
+ "wm8903", wm8903);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n",
+ ret);
+ goto err;
+ }
+
+ /* Enable write sequencer interrupts */
+ snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
+ WM8903_IM_WSEQ_BUSY_EINT, 0);
+ }
+
/* power on device */
wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1618,7 +1787,7 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
- goto err;
+ goto err_irq;
}
ret = snd_soc_register_dai(&wm8903_dai);
@@ -1631,6 +1800,9 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
err_codec:
snd_soc_unregister_codec(codec);
+err_irq:
+ if (i2c->irq)
+ free_irq(i2c->irq, wm8903);
err:
wm8903_codec = NULL;
kfree(wm8903);
@@ -1640,13 +1812,17 @@ err:
static __devexit int wm8903_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ struct wm8903_priv *priv = snd_soc_codec_get_drvdata(codec);
snd_soc_unregister_dai(&wm8903_dai);
snd_soc_unregister_codec(codec);
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
- kfree(codec->private_data);
+ if (client->irq)
+ free_irq(client->irq, priv);
+
+ kfree(priv);
wm8903_codec = NULL;
wm8903_dai.dev = NULL;
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
index 0ea27e2b9963..ce384a2ad820 100644
--- a/sound/soc/codecs/wm8903.h
+++ b/sound/soc/codecs/wm8903.h
@@ -18,6 +18,10 @@
extern struct snd_soc_dai wm8903_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8903;
+extern int wm8903_mic_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ int det, int shrt);
+
#define WM8903_MCLK_DIV_2 1
#define WM8903_CLK_SYS 2
#define WM8903_BCLK 3
@@ -173,28 +177,6 @@ extern struct snd_soc_codec_device soc_codec_dev_wm8903;
#define WM8903_VMID_RES_5K 4
/*
- * R6 (0x06) - Mic Bias Control 0
- */
-#define WM8903_MICDET_HYST_ENA 0x0080 /* MICDET_HYST_ENA */
-#define WM8903_MICDET_HYST_ENA_MASK 0x0080 /* MICDET_HYST_ENA */
-#define WM8903_MICDET_HYST_ENA_SHIFT 7 /* MICDET_HYST_ENA */
-#define WM8903_MICDET_HYST_ENA_WIDTH 1 /* MICDET_HYST_ENA */
-#define WM8903_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */
-#define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */
-#define WM8903_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */
-#define WM8903_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */
-#define WM8903_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */
-#define WM8903_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */
-#define WM8903_MICDET_ENA 0x0002 /* MICDET_ENA */
-#define WM8903_MICDET_ENA_MASK 0x0002 /* MICDET_ENA */
-#define WM8903_MICDET_ENA_SHIFT 1 /* MICDET_ENA */
-#define WM8903_MICDET_ENA_WIDTH 1 /* MICDET_ENA */
-#define WM8903_MICBIAS_ENA 0x0001 /* MICBIAS_ENA */
-#define WM8903_MICBIAS_ENA_MASK 0x0001 /* MICBIAS_ENA */
-#define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */
-#define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
-
-/*
* R8 (0x08) - Analogue DAC 0
*/
#define WM8903_DACBIAS_SEL_MASK 0x0018 /* DACBIAS_SEL - [4:3] */
@@ -1135,201 +1117,6 @@ extern struct snd_soc_codec_device soc_codec_dev_wm8903;
#define WM8903_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */
/*
- * R116 (0x74) - GPIO Control 1
- */
-#define WM8903_GP1_FN_MASK 0x1F00 /* GP1_FN - [12:8] */
-#define WM8903_GP1_FN_SHIFT 8 /* GP1_FN - [12:8] */
-#define WM8903_GP1_FN_WIDTH 5 /* GP1_FN - [12:8] */
-#define WM8903_GP1_DIR 0x0080 /* GP1_DIR */
-#define WM8903_GP1_DIR_MASK 0x0080 /* GP1_DIR */
-#define WM8903_GP1_DIR_SHIFT 7 /* GP1_DIR */
-#define WM8903_GP1_DIR_WIDTH 1 /* GP1_DIR */
-#define WM8903_GP1_OP_CFG 0x0040 /* GP1_OP_CFG */
-#define WM8903_GP1_OP_CFG_MASK 0x0040 /* GP1_OP_CFG */
-#define WM8903_GP1_OP_CFG_SHIFT 6 /* GP1_OP_CFG */
-#define WM8903_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
-#define WM8903_GP1_IP_CFG 0x0020 /* GP1_IP_CFG */
-#define WM8903_GP1_IP_CFG_MASK 0x0020 /* GP1_IP_CFG */
-#define WM8903_GP1_IP_CFG_SHIFT 5 /* GP1_IP_CFG */
-#define WM8903_GP1_IP_CFG_WIDTH 1 /* GP1_IP_CFG */
-#define WM8903_GP1_LVL 0x0010 /* GP1_LVL */
-#define WM8903_GP1_LVL_MASK 0x0010 /* GP1_LVL */
-#define WM8903_GP1_LVL_SHIFT 4 /* GP1_LVL */
-#define WM8903_GP1_LVL_WIDTH 1 /* GP1_LVL */
-#define WM8903_GP1_PD 0x0008 /* GP1_PD */
-#define WM8903_GP1_PD_MASK 0x0008 /* GP1_PD */
-#define WM8903_GP1_PD_SHIFT 3 /* GP1_PD */
-#define WM8903_GP1_PD_WIDTH 1 /* GP1_PD */
-#define WM8903_GP1_PU 0x0004 /* GP1_PU */
-#define WM8903_GP1_PU_MASK 0x0004 /* GP1_PU */
-#define WM8903_GP1_PU_SHIFT 2 /* GP1_PU */
-#define WM8903_GP1_PU_WIDTH 1 /* GP1_PU */
-#define WM8903_GP1_INTMODE 0x0002 /* GP1_INTMODE */
-#define WM8903_GP1_INTMODE_MASK 0x0002 /* GP1_INTMODE */
-#define WM8903_GP1_INTMODE_SHIFT 1 /* GP1_INTMODE */
-#define WM8903_GP1_INTMODE_WIDTH 1 /* GP1_INTMODE */
-#define WM8903_GP1_DB 0x0001 /* GP1_DB */
-#define WM8903_GP1_DB_MASK 0x0001 /* GP1_DB */
-#define WM8903_GP1_DB_SHIFT 0 /* GP1_DB */
-#define WM8903_GP1_DB_WIDTH 1 /* GP1_DB */
-
-/*
- * R117 (0x75) - GPIO Control 2
- */
-#define WM8903_GP2_FN_MASK 0x1F00 /* GP2_FN - [12:8] */
-#define WM8903_GP2_FN_SHIFT 8 /* GP2_FN - [12:8] */
-#define WM8903_GP2_FN_WIDTH 5 /* GP2_FN - [12:8] */
-#define WM8903_GP2_DIR 0x0080 /* GP2_DIR */
-#define WM8903_GP2_DIR_MASK 0x0080 /* GP2_DIR */
-#define WM8903_GP2_DIR_SHIFT 7 /* GP2_DIR */
-#define WM8903_GP2_DIR_WIDTH 1 /* GP2_DIR */
-#define WM8903_GP2_OP_CFG 0x0040 /* GP2_OP_CFG */
-#define WM8903_GP2_OP_CFG_MASK 0x0040 /* GP2_OP_CFG */
-#define WM8903_GP2_OP_CFG_SHIFT 6 /* GP2_OP_CFG */
-#define WM8903_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
-#define WM8903_GP2_IP_CFG 0x0020 /* GP2_IP_CFG */
-#define WM8903_GP2_IP_CFG_MASK 0x0020 /* GP2_IP_CFG */
-#define WM8903_GP2_IP_CFG_SHIFT 5 /* GP2_IP_CFG */
-#define WM8903_GP2_IP_CFG_WIDTH 1 /* GP2_IP_CFG */
-#define WM8903_GP2_LVL 0x0010 /* GP2_LVL */
-#define WM8903_GP2_LVL_MASK 0x0010 /* GP2_LVL */
-#define WM8903_GP2_LVL_SHIFT 4 /* GP2_LVL */
-#define WM8903_GP2_LVL_WIDTH 1 /* GP2_LVL */
-#define WM8903_GP2_PD 0x0008 /* GP2_PD */
-#define WM8903_GP2_PD_MASK 0x0008 /* GP2_PD */
-#define WM8903_GP2_PD_SHIFT 3 /* GP2_PD */
-#define WM8903_GP2_PD_WIDTH 1 /* GP2_PD */
-#define WM8903_GP2_PU 0x0004 /* GP2_PU */
-#define WM8903_GP2_PU_MASK 0x0004 /* GP2_PU */
-#define WM8903_GP2_PU_SHIFT 2 /* GP2_PU */
-#define WM8903_GP2_PU_WIDTH 1 /* GP2_PU */
-#define WM8903_GP2_INTMODE 0x0002 /* GP2_INTMODE */
-#define WM8903_GP2_INTMODE_MASK 0x0002 /* GP2_INTMODE */
-#define WM8903_GP2_INTMODE_SHIFT 1 /* GP2_INTMODE */
-#define WM8903_GP2_INTMODE_WIDTH 1 /* GP2_INTMODE */
-#define WM8903_GP2_DB 0x0001 /* GP2_DB */
-#define WM8903_GP2_DB_MASK 0x0001 /* GP2_DB */
-#define WM8903_GP2_DB_SHIFT 0 /* GP2_DB */
-#define WM8903_GP2_DB_WIDTH 1 /* GP2_DB */
-
-/*
- * R118 (0x76) - GPIO Control 3
- */
-#define WM8903_GP3_FN_MASK 0x1F00 /* GP3_FN - [12:8] */
-#define WM8903_GP3_FN_SHIFT 8 /* GP3_FN - [12:8] */
-#define WM8903_GP3_FN_WIDTH 5 /* GP3_FN - [12:8] */
-#define WM8903_GP3_DIR 0x0080 /* GP3_DIR */
-#define WM8903_GP3_DIR_MASK 0x0080 /* GP3_DIR */
-#define WM8903_GP3_DIR_SHIFT 7 /* GP3_DIR */
-#define WM8903_GP3_DIR_WIDTH 1 /* GP3_DIR */
-#define WM8903_GP3_OP_CFG 0x0040 /* GP3_OP_CFG */
-#define WM8903_GP3_OP_CFG_MASK 0x0040 /* GP3_OP_CFG */
-#define WM8903_GP3_OP_CFG_SHIFT 6 /* GP3_OP_CFG */
-#define WM8903_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
-#define WM8903_GP3_IP_CFG 0x0020 /* GP3_IP_CFG */
-#define WM8903_GP3_IP_CFG_MASK 0x0020 /* GP3_IP_CFG */
-#define WM8903_GP3_IP_CFG_SHIFT 5 /* GP3_IP_CFG */
-#define WM8903_GP3_IP_CFG_WIDTH 1 /* GP3_IP_CFG */
-#define WM8903_GP3_LVL 0x0010 /* GP3_LVL */
-#define WM8903_GP3_LVL_MASK 0x0010 /* GP3_LVL */
-#define WM8903_GP3_LVL_SHIFT 4 /* GP3_LVL */
-#define WM8903_GP3_LVL_WIDTH 1 /* GP3_LVL */
-#define WM8903_GP3_PD 0x0008 /* GP3_PD */
-#define WM8903_GP3_PD_MASK 0x0008 /* GP3_PD */
-#define WM8903_GP3_PD_SHIFT 3 /* GP3_PD */
-#define WM8903_GP3_PD_WIDTH 1 /* GP3_PD */
-#define WM8903_GP3_PU 0x0004 /* GP3_PU */
-#define WM8903_GP3_PU_MASK 0x0004 /* GP3_PU */
-#define WM8903_GP3_PU_SHIFT 2 /* GP3_PU */
-#define WM8903_GP3_PU_WIDTH 1 /* GP3_PU */
-#define WM8903_GP3_INTMODE 0x0002 /* GP3_INTMODE */
-#define WM8903_GP3_INTMODE_MASK 0x0002 /* GP3_INTMODE */
-#define WM8903_GP3_INTMODE_SHIFT 1 /* GP3_INTMODE */
-#define WM8903_GP3_INTMODE_WIDTH 1 /* GP3_INTMODE */
-#define WM8903_GP3_DB 0x0001 /* GP3_DB */
-#define WM8903_GP3_DB_MASK 0x0001 /* GP3_DB */
-#define WM8903_GP3_DB_SHIFT 0 /* GP3_DB */
-#define WM8903_GP3_DB_WIDTH 1 /* GP3_DB */
-
-/*
- * R119 (0x77) - GPIO Control 4
- */
-#define WM8903_GP4_FN_MASK 0x1F00 /* GP4_FN - [12:8] */
-#define WM8903_GP4_FN_SHIFT 8 /* GP4_FN - [12:8] */
-#define WM8903_GP4_FN_WIDTH 5 /* GP4_FN - [12:8] */
-#define WM8903_GP4_DIR 0x0080 /* GP4_DIR */
-#define WM8903_GP4_DIR_MASK 0x0080 /* GP4_DIR */
-#define WM8903_GP4_DIR_SHIFT 7 /* GP4_DIR */
-#define WM8903_GP4_DIR_WIDTH 1 /* GP4_DIR */
-#define WM8903_GP4_OP_CFG 0x0040 /* GP4_OP_CFG */
-#define WM8903_GP4_OP_CFG_MASK 0x0040 /* GP4_OP_CFG */
-#define WM8903_GP4_OP_CFG_SHIFT 6 /* GP4_OP_CFG */
-#define WM8903_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
-#define WM8903_GP4_IP_CFG 0x0020 /* GP4_IP_CFG */
-#define WM8903_GP4_IP_CFG_MASK 0x0020 /* GP4_IP_CFG */
-#define WM8903_GP4_IP_CFG_SHIFT 5 /* GP4_IP_CFG */
-#define WM8903_GP4_IP_CFG_WIDTH 1 /* GP4_IP_CFG */
-#define WM8903_GP4_LVL 0x0010 /* GP4_LVL */
-#define WM8903_GP4_LVL_MASK 0x0010 /* GP4_LVL */
-#define WM8903_GP4_LVL_SHIFT 4 /* GP4_LVL */
-#define WM8903_GP4_LVL_WIDTH 1 /* GP4_LVL */
-#define WM8903_GP4_PD 0x0008 /* GP4_PD */
-#define WM8903_GP4_PD_MASK 0x0008 /* GP4_PD */
-#define WM8903_GP4_PD_SHIFT 3 /* GP4_PD */
-#define WM8903_GP4_PD_WIDTH 1 /* GP4_PD */
-#define WM8903_GP4_PU 0x0004 /* GP4_PU */
-#define WM8903_GP4_PU_MASK 0x0004 /* GP4_PU */
-#define WM8903_GP4_PU_SHIFT 2 /* GP4_PU */
-#define WM8903_GP4_PU_WIDTH 1 /* GP4_PU */
-#define WM8903_GP4_INTMODE 0x0002 /* GP4_INTMODE */
-#define WM8903_GP4_INTMODE_MASK 0x0002 /* GP4_INTMODE */
-#define WM8903_GP4_INTMODE_SHIFT 1 /* GP4_INTMODE */
-#define WM8903_GP4_INTMODE_WIDTH 1 /* GP4_INTMODE */
-#define WM8903_GP4_DB 0x0001 /* GP4_DB */
-#define WM8903_GP4_DB_MASK 0x0001 /* GP4_DB */
-#define WM8903_GP4_DB_SHIFT 0 /* GP4_DB */
-#define WM8903_GP4_DB_WIDTH 1 /* GP4_DB */
-
-/*
- * R120 (0x78) - GPIO Control 5
- */
-#define WM8903_GP5_FN_MASK 0x1F00 /* GP5_FN - [12:8] */
-#define WM8903_GP5_FN_SHIFT 8 /* GP5_FN - [12:8] */
-#define WM8903_GP5_FN_WIDTH 5 /* GP5_FN - [12:8] */
-#define WM8903_GP5_DIR 0x0080 /* GP5_DIR */
-#define WM8903_GP5_DIR_MASK 0x0080 /* GP5_DIR */
-#define WM8903_GP5_DIR_SHIFT 7 /* GP5_DIR */
-#define WM8903_GP5_DIR_WIDTH 1 /* GP5_DIR */
-#define WM8903_GP5_OP_CFG 0x0040 /* GP5_OP_CFG */
-#define WM8903_GP5_OP_CFG_MASK 0x0040 /* GP5_OP_CFG */
-#define WM8903_GP5_OP_CFG_SHIFT 6 /* GP5_OP_CFG */
-#define WM8903_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
-#define WM8903_GP5_IP_CFG 0x0020 /* GP5_IP_CFG */
-#define WM8903_GP5_IP_CFG_MASK 0x0020 /* GP5_IP_CFG */
-#define WM8903_GP5_IP_CFG_SHIFT 5 /* GP5_IP_CFG */
-#define WM8903_GP5_IP_CFG_WIDTH 1 /* GP5_IP_CFG */
-#define WM8903_GP5_LVL 0x0010 /* GP5_LVL */
-#define WM8903_GP5_LVL_MASK 0x0010 /* GP5_LVL */
-#define WM8903_GP5_LVL_SHIFT 4 /* GP5_LVL */
-#define WM8903_GP5_LVL_WIDTH 1 /* GP5_LVL */
-#define WM8903_GP5_PD 0x0008 /* GP5_PD */
-#define WM8903_GP5_PD_MASK 0x0008 /* GP5_PD */
-#define WM8903_GP5_PD_SHIFT 3 /* GP5_PD */
-#define WM8903_GP5_PD_WIDTH 1 /* GP5_PD */
-#define WM8903_GP5_PU 0x0004 /* GP5_PU */
-#define WM8903_GP5_PU_MASK 0x0004 /* GP5_PU */
-#define WM8903_GP5_PU_SHIFT 2 /* GP5_PU */
-#define WM8903_GP5_PU_WIDTH 1 /* GP5_PU */
-#define WM8903_GP5_INTMODE 0x0002 /* GP5_INTMODE */
-#define WM8903_GP5_INTMODE_MASK 0x0002 /* GP5_INTMODE */
-#define WM8903_GP5_INTMODE_SHIFT 1 /* GP5_INTMODE */
-#define WM8903_GP5_INTMODE_WIDTH 1 /* GP5_INTMODE */
-#define WM8903_GP5_DB 0x0001 /* GP5_DB */
-#define WM8903_GP5_DB_MASK 0x0001 /* GP5_DB */
-#define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */
-#define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */
-
-/*
* R121 (0x79) - Interrupt Status 1
*/
#define WM8903_MICSHRT_EINT 0x8000 /* MICSHRT_EINT */
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
new file mode 100644
index 000000000000..87f14f8675fa
--- /dev/null
+++ b/sound/soc/codecs/wm8904.c
@@ -0,0 +1,2674 @@
+/*
+ * 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 <linux/slab.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;
+
+enum wm8904_type {
+ WM8904,
+ WM8912,
+};
+
+#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];
+
+ enum wm8904_type devtype;
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ int reg, val;
+ int dcs_mask;
+ int dcs_l, dcs_r;
+ int dcs_l_reg, dcs_r_reg;
+ int timeout;
+ int pwr_reg;
+
+ /* 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:
+ pwr_reg = WM8904_POWER_MANAGEMENT_2;
+ 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:
+ pwr_reg = WM8904_POWER_MANAGEMENT_3;
+ 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_PRE_PMU:
+ /* Power on the PGAs */
+ snd_soc_update_bits(codec, pwr_reg,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA);
+
+ /* 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);
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ /* 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);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ /* 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);
+
+ /* PGAs too */
+ snd_soc_update_bits(codec, pwr_reg,
+ WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+ 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", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("HPR PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LINEL PGA", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINER PGA", SND_SOC_NOPM, 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_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0,
+ 0, NULL, 0, out_pga_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_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 const struct snd_soc_dapm_route wm8912_intercon[] = {
+ { "HPL PGA", NULL, "DACL" },
+ { "HPR PGA", NULL, "DACR" },
+
+ { "LINEL PGA", NULL, "DACL" },
+ { "LINER PGA", NULL, "DACR" },
+};
+
+static int wm8904_add_widgets(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_dapm_new_controls(codec, wm8904_core_dapm_widgets,
+ ARRAY_SIZE(wm8904_core_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, core_intercon,
+ ARRAY_SIZE(core_intercon));
+
+ switch (wm8904->devtype) {
+ case WM8904:
+ 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_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));
+ break;
+
+ case WM8912:
+ snd_soc_add_controls(codec, wm8904_dac_snd_controls,
+ ARRAY_SIZE(wm8904_dac_snd_controls));
+
+ snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets,
+ ARRAY_SIZE(wm8904_dac_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, dac_intercon,
+ ARRAY_SIZE(dac_intercon));
+ snd_soc_dapm_add_routes(codec, wm8912_intercon,
+ ARRAY_SIZE(wm8912_intercon));
+ break;
+ }
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 void wm8904_sync_cache(struct snd_soc_codec *codec)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ if (!codec->cache_sync)
+ return;
+
+ codec->cache_only = 0;
+
+ /* 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]);
+ }
+
+ codec->cache_sync = 0;
+}
+
+static int wm8904_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ 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;
+ }
+
+ wm8904_sync_cache(codec);
+
+ /* 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);
+
+#ifdef CONFIG_REGULATOR
+ /* Post 2.6.34 we will be able to get a callback when
+ * the regulators are disabled which we can use but
+ * for now just assume that the power will be cut if
+ * the regulator API is in use.
+ */
+ codec->cache_sync = 1;
+#endif
+
+ 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(snd_soc_codec_get_drvdata(codec));
+
+ 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)
+{
+ struct wm8904_pdata *pdata = wm8904->pdata;
+ 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);
+
+ snd_soc_codec_set_drvdata(codec, 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;
+ codec->cache_sync = 1;
+ codec->idle_bias_off = 1;
+
+ switch (wm8904->devtype) {
+ case WM8904:
+ break;
+ case WM8912:
+ memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture));
+ break;
+ default:
+ dev_err(codec->dev, "Unknown device type %d\n",
+ wm8904->devtype);
+ return -EINVAL;
+ }
+
+ 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;
+
+ /* Apply configuration from the platform data. */
+ if (wm8904->pdata) {
+ for (i = 0; i < WM8904_GPIO_REGS; i++) {
+ if (!pdata->gpio_cfg[i])
+ continue;
+
+ wm8904->reg_cache[WM8904_GPIO_CONTROL_1 + i]
+ = pdata->gpio_cfg[i] & 0xffff;
+ }
+
+ /* Zero is the default value for these anyway */
+ for (i = 0; i < WM8904_MIC_REGS; i++)
+ wm8904->reg_cache[WM8904_MIC_BIAS_CONTROL_0 + i]
+ = pdata->mic_cfg[i];
+ }
+
+ /* 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;
+
+ wm8904->devtype = id->driver_data;
+
+ 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", WM8904 },
+ { "wm8912", WM8912 },
+ { }
+};
+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
index 000000000000..abe5059b3004
--- /dev/null
+++ b/sound/soc/codecs/wm8904.h
@@ -0,0 +1,1584 @@
+/*
+ * 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 */
+
+/*
+ * 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] */
+
+/*
+ * 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/wm8940.c b/sound/soc/codecs/wm8940.c
index 31e39ffd1d8e..e3c4bbfaae27 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -30,6 +30,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -580,7 +581,7 @@ static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8940_priv *wm8940 = codec->private_data;
+ struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -691,7 +692,6 @@ static int wm8940_resume(struct platform_device *pdev)
ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret)
goto error_ret;
- ret = wm8940_set_bias_level(codec, codec->suspend_bias_level);
error_ret:
return ret;
@@ -772,7 +772,7 @@ static int wm8940_register(struct wm8940_priv *wm8940,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8940;
+ snd_soc_codec_set_drvdata(codec, wm8940);
codec->name = "WM8940";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
new file mode 100644
index 000000000000..fedb76452f1b
--- /dev/null
+++ b/sound/soc/codecs/wm8955.c
@@ -0,0 +1,1152 @@
+/*
+ * 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 <linux/slab.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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ 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);
+
+ snd_soc_codec_set_drvdata(codec, 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
index 000000000000..ae349c8531f6
--- /dev/null
+++ b/sound/soc/codecs/wm8955.h
@@ -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
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index d07bcc1e1c60..7233cc68435a 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -15,6 +15,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -22,6 +23,7 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/wm8960.h>
#include "wm8960.h"
@@ -30,8 +32,14 @@
struct snd_soc_codec_device soc_codec_dev_wm8960;
/* R25 - Power 1 */
+#define WM8960_VMID_MASK 0x180
#define WM8960_VREF 0x40
+/* R26 - Power 2 */
+#define WM8960_PWR2_LOUT1 0x40
+#define WM8960_PWR2_ROUT1 0x20
+#define WM8960_PWR2_OUT3 0x02
+
/* R28 - Anti-pop 1 */
#define WM8960_POBCTRL 0x80
#define WM8960_BUFDCOPEN 0x10
@@ -41,6 +49,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8960;
/* R29 - Anti-pop 2 */
#define WM8960_DISOP 0x40
+#define WM8960_DRES_MASK 0x30
/*
* wm8960 register cache
@@ -67,6 +76,9 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
struct wm8960_priv {
u16 reg_cache[WM8960_CACHEREGNUM];
struct snd_soc_codec codec;
+ struct snd_soc_dapm_widget *lout1;
+ struct snd_soc_dapm_widget *rout1;
+ struct snd_soc_dapm_widget *out3;
};
#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
@@ -225,10 +237,6 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
&wm8960_routput_mixer[0],
ARRAY_SIZE(wm8960_routput_mixer)),
-SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
- &wm8960_mono_out[0],
- ARRAY_SIZE(wm8960_mono_out)),
-
SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
@@ -247,6 +255,17 @@ SND_SOC_DAPM_OUTPUT("SPK_RN"),
SND_SOC_DAPM_OUTPUT("OUT3"),
};
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
+SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
+ &wm8960_mono_out[0],
+ ARRAY_SIZE(wm8960_mono_out)),
+};
+
+/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
+SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
+};
+
static const struct snd_soc_dapm_route audio_paths[] = {
{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
@@ -277,9 +296,6 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
- { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
- { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
-
{ "LOUT1 PGA", NULL, "Left Output Mixer" },
{ "ROUT1 PGA", NULL, "Right Output Mixer" },
@@ -296,17 +312,65 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "SPK_LP", NULL, "Left Speaker Output" },
{ "SPK_RN", NULL, "Right Speaker Output" },
{ "SPK_RP", NULL, "Right Speaker Output" },
+};
+
+static const struct snd_soc_dapm_route audio_paths_out3[] = {
+ { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
+ { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
{ "OUT3", NULL, "Mono Output Mixer", }
};
+static const struct snd_soc_dapm_route audio_paths_capless[] = {
+ { "HP_L", NULL, "OUT3 VMID" },
+ { "HP_R", NULL, "OUT3 VMID" },
+
+ { "OUT3 VMID", NULL, "Left Output Mixer" },
+ { "OUT3 VMID", NULL, "Right Output Mixer" },
+};
+
static int wm8960_add_widgets(struct snd_soc_codec *codec)
{
+ struct wm8960_data *pdata = codec->dev->platform_data;
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_widget *w;
+
snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
ARRAY_SIZE(wm8960_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+ /* In capless mode OUT3 is used to provide VMID for the
+ * headphone outputs, otherwise it is used as a mono mixer.
+ */
+ if (pdata && pdata->capless) {
+ snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless,
+ ARRAY_SIZE(wm8960_dapm_widgets_capless));
+
+ snd_soc_dapm_add_routes(codec, audio_paths_capless,
+ ARRAY_SIZE(audio_paths_capless));
+ } else {
+ snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3,
+ ARRAY_SIZE(wm8960_dapm_widgets_out3));
+
+ snd_soc_dapm_add_routes(codec, audio_paths_out3,
+ ARRAY_SIZE(audio_paths_out3));
+ }
+
+ /* We need to power up the headphone output stage out of
+ * sequence for capless mode. To save scanning the widget
+ * list each time to find the desired power state do so now
+ * and save the result.
+ */
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (strcmp(w->name, "LOUT1 PGA") == 0)
+ wm8960->lout1 = w;
+ if (strcmp(w->name, "ROUT1 PGA") == 0)
+ wm8960->rout1 = w;
+ if (strcmp(w->name, "OUT3 VMID") == 0)
+ wm8960->out3 = w;
+ }
+
return 0;
}
@@ -407,10 +471,9 @@ static int wm8960_mute(struct snd_soc_dai *dai, int mute)
return 0;
}
-static int wm8960_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
+static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
- struct wm8960_data *pdata = codec->dev->platform_data;
u16 reg;
switch (level) {
@@ -429,18 +492,8 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Enable anti-pop features */
snd_soc_write(codec, WM8960_APOP1,
- WM8960_POBCTRL | WM8960_SOFT_ST |
- WM8960_BUFDCOPEN | WM8960_BUFIOEN);
-
- /* Discharge HP output */
- reg = WM8960_DISOP;
- if (pdata)
- reg |= pdata->dres << 4;
- snd_soc_write(codec, WM8960_APOP2, reg);
-
- msleep(400);
-
- snd_soc_write(codec, WM8960_APOP2, 0);
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN | WM8960_BUFIOEN);
/* Enable & ramp VMID at 2x50k */
reg = snd_soc_read(codec, WM8960_POWER1);
@@ -471,8 +524,101 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
/* Disable VMID and VREF, let them discharge */
snd_soc_write(codec, WM8960_POWER1, 0);
msleep(600);
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ switch (codec->bias_level) {
+ case SND_SOC_BIAS_STANDBY:
+ /* Enable anti pop mode */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+
+ /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
+ reg = 0;
+ if (wm8960->lout1 && wm8960->lout1->power)
+ reg |= WM8960_PWR2_LOUT1;
+ if (wm8960->rout1 && wm8960->rout1->power)
+ reg |= WM8960_PWR2_ROUT1;
+ if (wm8960->out3 && wm8960->out3->power)
+ reg |= WM8960_PWR2_OUT3;
+ snd_soc_update_bits(codec, WM8960_POWER2,
+ WM8960_PWR2_LOUT1 |
+ WM8960_PWR2_ROUT1 |
+ WM8960_PWR2_OUT3, reg);
+
+ /* Enable VMID at 2*50k */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VMID_MASK, 0x80);
+
+ /* Ramp */
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VREF, WM8960_VREF);
+
+ msleep(100);
+ break;
+
+ case SND_SOC_BIAS_ON:
+ /* Enable anti-pop mode */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+
+ /* Disable VMID and VREF */
+ snd_soc_update_bits(codec, WM8960_POWER1,
+ WM8960_VREF | WM8960_VMID_MASK, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
- snd_soc_write(codec, WM8960_APOP1, 0);
+ case SND_SOC_BIAS_STANDBY:
+ switch (codec->bias_level) {
+ case SND_SOC_BIAS_PREPARE:
+ /* Disable HP discharge */
+ snd_soc_update_bits(codec, WM8960_APOP2,
+ WM8960_DISOP | WM8960_DRES_MASK,
+ 0);
+
+ /* Disable anti-pop features */
+ snd_soc_update_bits(codec, WM8960_APOP1,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN,
+ WM8960_POBCTRL | WM8960_SOFT_ST |
+ WM8960_BUFDCOPEN);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
break;
}
@@ -593,10 +739,6 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
u16 reg;
switch (div_id) {
- case WM8960_SYSCLKSEL:
- reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
- snd_soc_write(codec, WM8960_CLOCK1, reg | div);
- break;
case WM8960_SYSCLKDIV:
reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
snd_soc_write(codec, WM8960_CLOCK1, reg | div);
@@ -662,7 +804,7 @@ static int wm8960_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;
- wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ codec->set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -681,8 +823,8 @@ static int wm8960_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
- wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8960_set_bias_level(codec, codec->suspend_bias_level);
+ codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
return 0;
}
@@ -752,6 +894,8 @@ static int wm8960_register(struct wm8960_priv *wm8960,
goto err;
}
+ codec->set_bias_level = wm8960_set_bias_level_out3;
+
if (!pdata) {
dev_warn(codec->dev, "No platform data supplied\n");
} else {
@@ -759,17 +903,19 @@ static int wm8960_register(struct wm8960_priv *wm8960,
dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
pdata->dres = 0;
}
+
+ if (pdata->capless)
+ codec->set_bias_level = wm8960_set_bias_level_capless;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8960;
+ snd_soc_codec_set_drvdata(codec, wm8960);
codec->name = "WM8960";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
- codec->set_bias_level = wm8960_set_bias_level;
codec->dai = &wm8960_dai;
codec->num_dai = 1;
codec->reg_cache_size = WM8960_CACHEREGNUM;
@@ -791,7 +937,7 @@ static int wm8960_register(struct wm8960_priv *wm8960,
wm8960_dai.dev = codec->dev;
- wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
reg = snd_soc_read(codec, WM8960_LINVOL);
@@ -840,7 +986,7 @@ err:
static void wm8960_unregister(struct wm8960_priv *wm8960)
{
- wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
+ wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8960_dai);
snd_soc_unregister_codec(&wm8960->codec);
kfree(wm8960);
@@ -882,7 +1028,7 @@ MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
static struct i2c_driver wm8960_i2c_driver = {
.driver = {
- .name = "WM8960 I2C Codec",
+ .name = "wm8960",
.owner = THIS_MODULE,
},
.probe = wm8960_i2c_probe,
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
index c9af56c9d9d4..a5ef65481b86 100644
--- a/sound/soc/codecs/wm8960.h
+++ b/sound/soc/codecs/wm8960.h
@@ -76,7 +76,6 @@
#define WM8960_OPCLKDIV 2
#define WM8960_DCLKDIV 3
#define WM8960_TOCLKSEL 4
-#define WM8960_SYSCLKSEL 5
#define WM8960_SYSCLK_DIV_1 (0 << 1)
#define WM8960_SYSCLK_DIV_2 (2 << 1)
@@ -114,14 +113,4 @@
extern struct snd_soc_dai wm8960_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8960;
-#define WM8960_DRES_400R 0
-#define WM8960_DRES_200R 1
-#define WM8960_DRES_600R 2
-#define WM8960_DRES_150R 3
-#define WM8960_DRES_MAX 3
-
-struct wm8960_data {
- int dres;
-};
-
#endif
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index a8007d58813f..5b9a756242f1 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -630,7 +631,7 @@ static int wm8961_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8961_priv *wm8961 = codec->private_data;
+ struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
int i, best, target, fs;
u16 reg;
@@ -721,7 +722,7 @@ static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
int dir)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8961_priv *wm8961 = codec->private_data;
+ struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec);
u16 reg = snd_soc_read(codec, WM8961_CLOCKING1);
if (freq > 33000000) {
@@ -1022,6 +1023,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;
@@ -1061,7 +1065,7 @@ static int wm8961_register(struct wm8961_priv *wm8961)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8961;
+ snd_soc_codec_set_drvdata(codec, wm8961);
codec->name = "WM8961";
codec->owner = THIS_MODULE;
codec->dai = &wm8961_dai;
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index d9540d55fc89..a99620f335d2 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -20,6 +20,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -414,7 +415,7 @@ static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8971_priv *wm8971 = codec->private_data;
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -493,7 +494,7 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8971_priv *wm8971 = codec->private_data;
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
int coeff = get_coeff(wm8971->sysclk, params_rate(params));
@@ -819,7 +820,7 @@ static int wm8971_probe(struct platform_device *pdev)
return -ENOMEM;
}
- codec->private_data = wm8971;
+ snd_soc_codec_set_drvdata(codec, wm8971);
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -829,7 +830,7 @@ static int wm8971_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
wm8971_workq = create_workqueue("wm8971");
if (wm8971_workq == NULL) {
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
return -ENOMEM;
}
@@ -843,7 +844,7 @@ static int wm8971_probe(struct platform_device *pdev)
if (ret != 0) {
destroy_workqueue(wm8971_workq);
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
}
@@ -866,7 +867,7 @@ static int wm8971_remove(struct platform_device *pdev)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8971_i2c_driver);
#endif
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 8812751da8c9..a2c4b2f37cca 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -170,13 +171,17 @@ SOC_ENUM("Aux Mode", wm8974_auxmode),
SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
+
+/* DAC / ADC oversampling */
+SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
+SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
};
/* Speaker Output Mixer */
static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
};
/* Mono Output Mixer */
@@ -381,14 +386,6 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
snd_soc_write(codec, WM8974_CLOCK, reg | div);
break;
- case WM8974_ADCCLK:
- reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
- snd_soc_write(codec, WM8974_ADC, reg | div);
- break;
- case WM8974_DACCLK:
- reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
- snd_soc_write(codec, WM8974_DAC, reg | div);
- break;
case WM8974_BCLKDIV:
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
snd_soc_write(codec, WM8974_CLOCK, reg | div);
@@ -612,7 +609,7 @@ static int wm8974_resume(struct platform_device *pdev)
codec->hw_write(codec->control_data, data, 2);
}
wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8974_set_bias_level(codec, codec->suspend_bias_level);
+
return 0;
}
@@ -680,7 +677,7 @@ static __devinit int wm8974_register(struct wm8974_priv *wm8974)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8974;
+ snd_soc_codec_set_drvdata(codec, wm8974);
codec->name = "WM8974";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h
index 98de9562d4d2..896a7f0f3fc4 100644
--- a/sound/soc/codecs/wm8974.h
+++ b/sound/soc/codecs/wm8974.h
@@ -57,17 +57,7 @@
/* Clock divider Id's */
#define WM8974_OPCLKDIV 0
#define WM8974_MCLKDIV 1
-#define WM8974_ADCCLK 2
-#define WM8974_DACCLK 3
-#define WM8974_BCLKDIV 4
-
-/* DAC clock dividers */
-#define WM8974_DACCLK_F2 (1 << 3)
-#define WM8974_DACCLK_F4 (0 << 3)
-
-/* ADC clock dividers */
-#define WM8974_ADCCLK_F2 (1 << 3)
-#define WM8974_ADCCLK_F4 (0 << 3)
+#define WM8974_BCLKDIV 2
/* PLL Out dividers */
#define WM8974_OPCLKDIV_1 (0 << 4)
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
new file mode 100644
index 000000000000..51d5f433215c
--- /dev/null
+++ b/sound/soc/codecs/wm8978.c
@@ -0,0 +1,1150 @@
+/*
+ * wm8978.c -- WM8978 ALSA SoC Audio Codec driver
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2007 Carlos Munoz <carlos@kenati.com>
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ * Based on wm8974 and wm8990 by Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.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 <asm/div64.h>
+
+#include "wm8978.h"
+
+static struct snd_soc_codec *wm8978_codec;
+
+/* wm8978 register cache. Note that register 0 is not included in the cache. */
+static const u16 wm8978_reg[WM8978_CACHEREGNUM] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x00...0x03 */
+ 0x0050, 0x0000, 0x0140, 0x0000, /* 0x04...0x07 */
+ 0x0000, 0x0000, 0x0000, 0x00ff, /* 0x08...0x0b */
+ 0x00ff, 0x0000, 0x0100, 0x00ff, /* 0x0c...0x0f */
+ 0x00ff, 0x0000, 0x012c, 0x002c, /* 0x10...0x13 */
+ 0x002c, 0x002c, 0x002c, 0x0000, /* 0x14...0x17 */
+ 0x0032, 0x0000, 0x0000, 0x0000, /* 0x18...0x1b */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x1c...0x1f */
+ 0x0038, 0x000b, 0x0032, 0x0000, /* 0x20...0x23 */
+ 0x0008, 0x000c, 0x0093, 0x00e9, /* 0x24...0x27 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x28...0x2b */
+ 0x0033, 0x0010, 0x0010, 0x0100, /* 0x2c...0x2f */
+ 0x0100, 0x0002, 0x0001, 0x0001, /* 0x30...0x33 */
+ 0x0039, 0x0039, 0x0039, 0x0039, /* 0x34...0x37 */
+ 0x0001, 0x0001, /* 0x38...0x3b */
+};
+
+/* codec private data */
+struct wm8978_priv {
+ struct snd_soc_codec codec;
+ unsigned int f_pllout;
+ unsigned int f_mclk;
+ unsigned int f_256fs;
+ unsigned int f_opclk;
+ int mclk_idx;
+ enum wm8978_sysclk_src sysclk;
+ u16 reg_cache[WM8978_CACHEREGNUM];
+};
+
+static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"};
+static const char *wm8978_eqmode[] = {"Capture", "Playback"};
+static const char *wm8978_bw[] = {"Narrow", "Wide"};
+static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"};
+static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"};
+static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"};
+static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"};
+static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"};
+static const char *wm8978_alc3[] = {"ALC", "Limiter"};
+static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"};
+
+static const SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1,
+ wm8978_companding);
+static const SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3,
+ wm8978_companding);
+static const SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode);
+static const SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1);
+static const SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2);
+static const SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3);
+static const SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4);
+static const SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5);
+static const SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3);
+static const SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+
+static const struct snd_kcontrol_new wm8978_snd_controls[] = {
+
+ SOC_SINGLE("Digital Loopback Switch",
+ WM8978_COMPANDING_CONTROL, 0, 1, 0),
+
+ SOC_ENUM("ADC Companding", adc_compand),
+ SOC_ENUM("DAC Companding", dac_compand),
+
+ SOC_DOUBLE("DAC Inversion Switch", WM8978_DAC_CONTROL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ WM8978_LEFT_DAC_DIGITAL_VOLUME, WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+ 0, 255, 0, digital_tlv),
+
+ SOC_SINGLE("High Pass Filter Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+ SOC_SINGLE("High Pass Cut Off", WM8978_ADC_CONTROL, 4, 7, 0),
+ SOC_DOUBLE("ADC Inversion Switch", WM8978_ADC_CONTROL, 0, 1, 1, 0),
+
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ WM8978_LEFT_ADC_DIGITAL_VOLUME, WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+ 0, 255, 0, digital_tlv),
+
+ SOC_ENUM("Equaliser Function", eqmode),
+ SOC_ENUM("EQ1 Cut Off", eq1),
+ SOC_SINGLE_TLV("EQ1 Volume", WM8978_EQ1, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ2 Bandwith", eq2bw),
+ SOC_ENUM("EQ2 Cut Off", eq2),
+ SOC_SINGLE_TLV("EQ2 Volume", WM8978_EQ2, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ3 Bandwith", eq3bw),
+ SOC_ENUM("EQ3 Cut Off", eq3),
+ SOC_SINGLE_TLV("EQ3 Volume", WM8978_EQ3, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("Equaliser EQ4 Bandwith", eq4bw),
+ SOC_ENUM("EQ4 Cut Off", eq4),
+ SOC_SINGLE_TLV("EQ4 Volume", WM8978_EQ4, 0, 24, 1, eq_tlv),
+
+ SOC_ENUM("EQ5 Cut Off", eq5),
+ SOC_SINGLE_TLV("EQ5 Volume", WM8978_EQ5, 0, 24, 1, eq_tlv),
+
+ SOC_SINGLE("DAC Playback Limiter Switch",
+ WM8978_DAC_LIMITER_1, 8, 1, 0),
+ SOC_SINGLE("DAC Playback Limiter Decay",
+ WM8978_DAC_LIMITER_1, 4, 15, 0),
+ SOC_SINGLE("DAC Playback Limiter Attack",
+ WM8978_DAC_LIMITER_1, 0, 15, 0),
+
+ SOC_SINGLE("DAC Playback Limiter Threshold",
+ WM8978_DAC_LIMITER_2, 4, 7, 0),
+ SOC_SINGLE("DAC Playback Limiter Boost",
+ WM8978_DAC_LIMITER_2, 0, 15, 0),
+
+ SOC_ENUM("ALC Enable Switch", alc1),
+ SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
+ SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0),
+
+ SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0),
+ SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0),
+
+ SOC_ENUM("ALC Capture Mode", alc3),
+ SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0),
+
+ SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
+ SOC_SINGLE("ALC Capture Noise Gate Threshold",
+ WM8978_NOISE_GATE, 0, 7, 0),
+
+ SOC_DOUBLE_R("Capture PGA ZC Switch",
+ WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+ 7, 1, 0),
+
+ /* OUT1 - Headphones */
+ SOC_DOUBLE_R("Headphone Playback ZC Switch",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 7, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL,
+ 0, 63, 0, spk_tlv),
+
+ /* OUT2 - Speakers */
+ SOC_DOUBLE_R("Speaker Playback ZC Switch",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 7, 1, 0),
+
+ SOC_DOUBLE_R_TLV("Speaker Playback Volume",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL,
+ 0, 63, 0, spk_tlv),
+
+ /* OUT3/4 - Line Output */
+ SOC_DOUBLE_R("Line Playback Switch",
+ WM8978_OUT3_MIXER_CONTROL, WM8978_OUT4_MIXER_CONTROL, 6, 1, 1),
+
+ /* Mixer #3: Boost (Input) mixer */
+ SOC_DOUBLE_R("PGA Boost (+20dB)",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 8, 1, 0),
+ SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 4, 7, 0, boost_tlv),
+ SOC_DOUBLE_R_TLV("Aux Boost Volume",
+ WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+ 0, 7, 0, boost_tlv),
+
+ /* Input PGA volume */
+ SOC_DOUBLE_R_TLV("Input PGA Volume",
+ WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+ 0, 63, 0, inpga_tlv),
+
+ /* Headphone */
+ SOC_DOUBLE_R("Headphone Switch",
+ WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 6, 1, 1),
+
+ /* Speaker */
+ SOC_DOUBLE_R("Speaker Switch",
+ WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
+
+ /* DAC / ADC oversampling */
+ SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0),
+ SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+};
+
+/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
+static const struct snd_kcontrol_new wm8978_left_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_LEFT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_LEFT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_LEFT_MIXER_CONTROL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8978_right_out_mixer[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_RIGHT_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* OUT3/OUT4 Mixer not implemented */
+
+/* Mixer #2: Input PGA Mute */
+static const struct snd_kcontrol_new wm8978_left_input_mixer[] = {
+ SOC_DAPM_SINGLE("L2 Switch", WM8978_INPUT_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new wm8978_right_input_mixer[] = {
+ SOC_DAPM_SINGLE("R2 Switch", WM8978_INPUT_CONTROL, 6, 1, 0),
+ SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 5, 1, 0),
+ SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8978_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ WM8978_POWER_MANAGEMENT_3, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ WM8978_POWER_MANAGEMENT_3, 1, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+ WM8978_POWER_MANAGEMENT_2, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+ WM8978_POWER_MANAGEMENT_2, 1, 0),
+
+ /* Mixer #1: OUT1,2 */
+ SOC_MIXER_ARRAY("Left Output Mixer", WM8978_POWER_MANAGEMENT_3,
+ 2, 0, wm8978_left_out_mixer),
+ SOC_MIXER_ARRAY("Right Output Mixer", WM8978_POWER_MANAGEMENT_3,
+ 3, 0, wm8978_right_out_mixer),
+
+ SOC_MIXER_ARRAY("Left Input Mixer", WM8978_POWER_MANAGEMENT_2,
+ 2, 0, wm8978_left_input_mixer),
+ SOC_MIXER_ARRAY("Right Input Mixer", WM8978_POWER_MANAGEMENT_2,
+ 3, 0, wm8978_right_input_mixer),
+
+ SND_SOC_DAPM_PGA("Left Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Capture PGA", WM8978_LEFT_INP_PGA_CONTROL,
+ 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Capture PGA", WM8978_RIGHT_INP_PGA_CONTROL,
+ 6, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Headphone Out", WM8978_POWER_MANAGEMENT_2,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Headphone Out", WM8978_POWER_MANAGEMENT_2,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("Left Speaker Out", WM8978_POWER_MANAGEMENT_3,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Speaker Out", WM8978_POWER_MANAGEMENT_3,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("OUT4 VMID", WM8978_POWER_MANAGEMENT_3,
+ 8, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8978_POWER_MANAGEMENT_1, 4, 0),
+
+ SND_SOC_DAPM_INPUT("LMICN"),
+ SND_SOC_DAPM_INPUT("LMICP"),
+ SND_SOC_DAPM_INPUT("RMICN"),
+ SND_SOC_DAPM_INPUT("RMICP"),
+ SND_SOC_DAPM_INPUT("LAUX"),
+ SND_SOC_DAPM_INPUT("RAUX"),
+ SND_SOC_DAPM_INPUT("L2"),
+ SND_SOC_DAPM_INPUT("R2"),
+ SND_SOC_DAPM_OUTPUT("LHP"),
+ SND_SOC_DAPM_OUTPUT("RHP"),
+ SND_SOC_DAPM_OUTPUT("LSPK"),
+ SND_SOC_DAPM_OUTPUT("RSPK"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Output mixer */
+ {"Right Output Mixer", "PCM Playback Switch", "Right DAC"},
+ {"Right Output Mixer", "Aux Playback Switch", "RAUX"},
+ {"Right Output Mixer", "Line Bypass Switch", "Right Boost Mixer"},
+
+ {"Left Output Mixer", "PCM Playback Switch", "Left DAC"},
+ {"Left Output Mixer", "Aux Playback Switch", "LAUX"},
+ {"Left Output Mixer", "Line Bypass Switch", "Left Boost Mixer"},
+
+ /* Outputs */
+ {"Right Headphone Out", NULL, "Right Output Mixer"},
+ {"RHP", NULL, "Right Headphone Out"},
+
+ {"Left Headphone Out", NULL, "Left Output Mixer"},
+ {"LHP", NULL, "Left Headphone Out"},
+
+ {"Right Speaker Out", NULL, "Right Output Mixer"},
+ {"RSPK", NULL, "Right Speaker Out"},
+
+ {"Left Speaker Out", NULL, "Left Output Mixer"},
+ {"LSPK", NULL, "Left Speaker Out"},
+
+ /* Boost Mixer */
+ {"Right ADC", NULL, "Right Boost Mixer"},
+
+ {"Right Boost Mixer", NULL, "RAUX"},
+ {"Right Boost Mixer", NULL, "Right Capture PGA"},
+ {"Right Boost Mixer", NULL, "R2"},
+
+ {"Left ADC", NULL, "Left Boost Mixer"},
+
+ {"Left Boost Mixer", NULL, "LAUX"},
+ {"Left Boost Mixer", NULL, "Left Capture PGA"},
+ {"Left Boost Mixer", NULL, "L2"},
+
+ /* Input PGA */
+ {"Right Capture PGA", NULL, "Right Input Mixer"},
+ {"Left Capture PGA", NULL, "Left Input Mixer"},
+
+ {"Right Input Mixer", "R2 Switch", "R2"},
+ {"Right Input Mixer", "MicN Switch", "RMICN"},
+ {"Right Input Mixer", "MicP Switch", "RMICP"},
+
+ {"Left Input Mixer", "L2 Switch", "L2"},
+ {"Left Input Mixer", "MicN Switch", "LMICN"},
+ {"Left Input Mixer", "MicP Switch", "LMICP"},
+};
+
+static int wm8978_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8978_dapm_widgets,
+ ARRAY_SIZE(wm8978_dapm_widgets));
+
+ /* set up the WM8978 audio map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ return 0;
+}
+
+/* PLL divisors */
+struct wm8978_pll_div {
+ u32 k;
+ u8 n;
+ u8 div2;
+};
+
+#define FIXED_PLL_SIZE (1 << 24)
+
+static void pll_factors(struct wm8978_pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 k_part;
+ unsigned int k, n_div, n_mod;
+
+ n_div = target / source;
+ if (n_div < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ n_div = target / source;
+ } else {
+ pll_div->div2 = 0;
+ }
+
+ if (n_div < 6 || n_div > 12)
+ dev_warn(wm8978_codec->dev,
+ "WM8978 N value exceeds recommended range! N = %u\n",
+ n_div);
+
+ pll_div->n = n_div;
+ n_mod = target - source * n_div;
+ k_part = FIXED_PLL_SIZE * (long long)n_mod + source / 2;
+
+ do_div(k_part, source);
+
+ k = k_part & 0xFFFFFFFF;
+
+ pll_div->k = k;
+}
+
+/* MCLK dividers */
+static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12};
+static const int mclk_denominator[] = {1, 2, 1, 1, 1, 1, 1, 1};
+
+/*
+ * find index >= idx, such that, for a given f_out,
+ * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
+ * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be
+ * generalised for f_opclk with suitable coefficient arrays, but currently
+ * the OPCLK divisor is calculated directly, not iteratively.
+ */
+static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
+ unsigned int *f_pllout)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+ unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] /
+ mclk_denominator[i];
+ if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) {
+ *f_pllout = f_pllout_x4 / 4;
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Calculate internal frequencies and dividers, according to Figure 40
+ * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6
+ */
+static int wm8978_configure_pll(struct snd_soc_codec *codec)
+{
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ struct wm8978_pll_div pll_div;
+ unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
+ f_256fs = wm8978->f_256fs;
+ unsigned int f2;
+
+ if (!f_mclk)
+ return -EINVAL;
+
+ if (f_opclk) {
+ unsigned int opclk_div;
+ /* Cannot set up MCLK divider now, do later */
+ wm8978->mclk_idx = -1;
+
+ /*
+ * The user needs OPCLK. Choose OPCLKDIV to put
+ * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4.
+ * f_opclk = f_mclk * prescale * R / 4 / OPCLKDIV, where
+ * prescale = 1, or prescale = 2. Prescale is calculated inside
+ * pll_factors(). We have to select f_PLLOUT, such that
+ * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+ * f_mclk * 3 / 16 <= f_opclk < f_mclk * 13 / 4.
+ */
+ if (16 * f_opclk < 3 * f_mclk || 4 * f_opclk >= 13 * f_mclk)
+ return -EINVAL;
+
+ if (4 * f_opclk < 3 * f_mclk)
+ /* Have to use OPCLKDIV */
+ opclk_div = (3 * f_mclk / 4 + f_opclk - 1) / f_opclk;
+ else
+ opclk_div = 1;
+
+ dev_dbg(codec->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div);
+
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 0x30,
+ (opclk_div - 1) << 4);
+
+ wm8978->f_pllout = f_opclk * opclk_div;
+ } else if (f_256fs) {
+ /*
+ * Not using OPCLK, but PLL is used for the codec, choose R:
+ * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12.
+ * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where
+ * prescale = 1, or prescale = 2. Prescale is calculated inside
+ * pll_factors(). We have to select f_PLLOUT, such that
+ * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+ * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK
+ * must be 3.781MHz <= f_MCLK <= 32.768MHz
+ */
+ int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout);
+ if (idx < 0)
+ return idx;
+
+ wm8978->mclk_idx = idx;
+
+ /* GPIO1 into default mode as input - before configuring PLL */
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+ } else {
+ return -EINVAL;
+ }
+
+ f2 = wm8978->f_pllout * 4;
+
+ dev_dbg(codec->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__,
+ wm8978->f_mclk, wm8978->f_pllout);
+
+ pll_factors(&pll_div, f2, wm8978->f_mclk);
+
+ dev_dbg(codec->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n",
+ __func__, pll_div.n, pll_div.k, pll_div.div2);
+
+ /* Turn PLL off for configuration... */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+
+ snd_soc_write(codec, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n);
+ snd_soc_write(codec, WM8978_PLL_K1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8978_PLL_K2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8978_PLL_K3, pll_div.k & 0x1ff);
+
+ /* ...and on again */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+ if (f_opclk)
+ /* Output PLL (OPCLK) to GPIO1 */
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 4);
+
+ return 0;
+}
+
+/*
+ * Configure WM8978 clock dividers.
+ */
+static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (div_id) {
+ case WM8978_OPCLKRATE:
+ wm8978->f_opclk = div;
+
+ if (wm8978->f_mclk)
+ /*
+ * We know the MCLK frequency, the user has requested
+ * OPCLK, configure the PLL based on that and start it
+ * and OPCLK immediately. We will configure PLL to match
+ * user-requested OPCLK frquency as good as possible.
+ * In fact, it is likely, that matching the sampling
+ * rate, when it becomes known, is more important, and
+ * we will not be reconfiguring PLL then, because we
+ * must not interrupt OPCLK. But it should be fine,
+ * because typically the user will request OPCLK to run
+ * at 256fs or 512fs, and for these cases we will also
+ * find an exact MCLK divider configuration - it will
+ * be equal to or double the OPCLK divisor.
+ */
+ ret = wm8978_configure_pll(codec);
+ break;
+ case WM8978_BCLKDIV:
+ if (div & ~0x1c)
+ return -EINVAL;
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x1c, div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: ID %d, value %u\n", __func__, div_id, div);
+
+ return ret;
+}
+
+/*
+ * @freq: when .set_pll() us not used, freq is codec MCLK input frequency
+ */
+static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(codec->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq);
+
+ if (freq) {
+ wm8978->f_mclk = freq;
+
+ /* Even if MCLK is used for system clock, might have to drive OPCLK */
+ if (wm8978->f_opclk)
+ ret = wm8978_configure_pll(codec);
+
+ /* Our sysclk is fixed to 256 * fs, will configure in .hw_params() */
+
+ if (!ret)
+ wm8978->sysclk = clk_id;
+ }
+
+ if (wm8978->sysclk == WM8978_PLL && (!freq || clk_id == WM8978_MCLK)) {
+ /* Clock CODEC directly from MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+
+ /* GPIO1 into default mode as input - before configuring PLL */
+ snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+
+ /* Turn off PLL */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+ wm8978->sysclk = WM8978_MCLK;
+ wm8978->f_pllout = 0;
+ wm8978->f_opclk = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Set ADC and Voice DAC format.
+ */
+static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ /*
+ * BCLK polarity mask = 0x100, LRC clock polarity mask = 0x80,
+ * Data Format mask = 0x18: all will be calculated anew
+ */
+ u16 iface = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x198;
+ u16 clk = snd_soc_read(codec, WM8978_CLOCKING);
+
+ dev_dbg(codec->dev, "%s\n", __func__);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ clk &= ~1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface);
+ snd_soc_write(codec, WM8978_CLOCKING, clk);
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8978_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ /* Word length mask = 0x60 */
+ u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
+ /* Sampling rate mask = 0xe (for filters) */
+ u16 add_ctl = snd_soc_read(codec, WM8978_ADDITIONAL_CONTROL) & ~0xe;
+ u16 clking = snd_soc_read(codec, WM8978_CLOCKING);
+ enum wm8978_sysclk_src current_clk_id = clking & 0x100 ?
+ WM8978_PLL : WM8978_MCLK;
+ unsigned int f_sel, diff, diff_best = INT_MAX;
+ int i, best = 0;
+
+ if (!wm8978->f_mclk)
+ return -EINVAL;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface_ctl |= 0x20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface_ctl |= 0x40;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface_ctl |= 0x60;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case 8000:
+ add_ctl |= 0x5 << 1;
+ break;
+ case 11025:
+ add_ctl |= 0x4 << 1;
+ break;
+ case 16000:
+ add_ctl |= 0x3 << 1;
+ break;
+ case 22050:
+ add_ctl |= 0x2 << 1;
+ break;
+ case 32000:
+ add_ctl |= 0x1 << 1;
+ break;
+ case 44100:
+ case 48000:
+ break;
+ }
+
+ /* Sampling rate is known now, can configure the MCLK divider */
+ wm8978->f_256fs = params_rate(params) * 256;
+
+ if (wm8978->sysclk == WM8978_MCLK) {
+ wm8978->mclk_idx = -1;
+ f_sel = wm8978->f_mclk;
+ } else {
+ if (!wm8978->f_pllout) {
+ /* We only enter here, if OPCLK is not used */
+ int ret = wm8978_configure_pll(codec);
+ if (ret < 0)
+ return ret;
+ }
+ f_sel = wm8978->f_pllout;
+ }
+
+ if (wm8978->mclk_idx < 0) {
+ /* Either MCLK is used directly, or OPCLK is used */
+ if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+ diff = abs(wm8978->f_256fs * 3 -
+ f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
+
+ if (diff < diff_best) {
+ diff_best = diff;
+ best = i;
+ }
+
+ if (!diff)
+ break;
+ }
+ } else {
+ /* OPCLK not used, codec driven by PLL */
+ best = wm8978->mclk_idx;
+ diff = 0;
+ }
+
+ if (diff)
+ dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
+ f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
+ wm8978->sysclk == WM8978_MCLK ?
+ ", consider using PLL" : "");
+
+ dev_dbg(codec->dev, "%s: fmt %d, rate %u, MCLK divisor #%d\n", __func__,
+ params_format(params), params_rate(params), best);
+
+ /* MCLK divisor mask = 0xe0 */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0xe0, best << 5);
+
+ snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface_ctl);
+ snd_soc_write(codec, WM8978_ADDITIONAL_CONTROL, add_ctl);
+
+ if (wm8978->sysclk != current_clk_id) {
+ if (wm8978->sysclk == WM8978_PLL)
+ /* Run CODEC from PLL instead of MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING,
+ 0x100, 0x100);
+ else
+ /* Clock CODEC directly from MCLK */
+ snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+ }
+
+ return 0;
+}
+
+static int wm8978_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ dev_dbg(codec->dev, "%s: %d\n", __func__, mute);
+
+ if (mute)
+ snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0x40);
+ else
+ snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0);
+
+ return 0;
+}
+
+static int wm8978_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 power1 = snd_soc_read(codec, WM8978_POWER_MANAGEMENT_1) & ~3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 1; /* VMID 75k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* bit 3: enable bias, bit 2: enable I/O tie off buffer */
+ power1 |= 0xc;
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Initial cap charge at VMID 5k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
+ power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Preserve PLL - OPCLK may be used by someone */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, ~0x20, 0);
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_2, 0);
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_3, 0);
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8978_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 wm8978_dai_ops = {
+ .hw_params = wm8978_hw_params,
+ .digital_mute = wm8978_mute,
+ .set_fmt = wm8978_set_dai_fmt,
+ .set_clkdiv = wm8978_set_dai_clkdiv,
+ .set_sysclk = wm8978_set_dai_sysclk,
+};
+
+/* Also supports 12kHz */
+struct snd_soc_dai wm8978_dai = {
+ .name = "WM8978 HiFi",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8978_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = WM8978_FORMATS,
+ },
+ .ops = &wm8978_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8978_dai);
+
+static int wm8978_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;
+
+ wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ /* Also switch PLL off */
+ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
+
+ return 0;
+}
+
+static int wm8978_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8978_reg); i++) {
+ if (i == WM8978_RESET)
+ continue;
+ if (cache[i] != wm8978_reg[i])
+ snd_soc_write(codec, i, cache[i]);
+ }
+
+ wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (wm8978->f_pllout)
+ /* Switch PLL on */
+ snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+ return 0;
+}
+
+static int wm8978_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8978_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8978_codec;
+ codec = wm8978_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8978_snd_controls,
+ ARRAY_SIZE(wm8978_snd_controls));
+ wm8978_add_widgets(codec);
+
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8978_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_wm8978 = {
+ .probe = wm8978_probe,
+ .remove = wm8978_remove,
+ .suspend = wm8978_suspend,
+ .resume = wm8978_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8978);
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+ WM8978_LEFT_DAC_DIGITAL_VOLUME,
+ WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8978_LEFT_ADC_DIGITAL_VOLUME,
+ WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8978_LEFT_INP_PGA_CONTROL,
+ WM8978_RIGHT_INP_PGA_CONTROL,
+ WM8978_LOUT1_HP_CONTROL,
+ WM8978_ROUT1_HP_CONTROL,
+ WM8978_LOUT2_SPK_CONTROL,
+ WM8978_ROUT2_SPK_CONTROL,
+};
+
+static __devinit int wm8978_register(struct wm8978_priv *wm8978)
+{
+ int ret, i;
+ struct snd_soc_codec *codec = &wm8978->codec;
+
+ if (wm8978_codec) {
+ dev_err(codec->dev, "Another WM8978 is registered\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Set default system clock to PLL, it is more precise, this is also the
+ * default hardware setting
+ */
+ wm8978->sysclk = WM8978_PLL;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ snd_soc_codec_set_drvdata(codec, wm8978);
+ codec->name = "WM8978";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8978_set_bias_level;
+ codec->dai = &wm8978_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8978_CACHEREGNUM;
+ codec->reg_cache = &wm8978->reg_cache;
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ memcpy(codec->reg_cache, wm8978_reg, sizeof(wm8978_reg));
+
+ /*
+ * Set the update bit in all registers, that have one. This way all
+ * writes to those registers will also cause the update bit to be
+ * written.
+ */
+ for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+ ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+
+ /* Reset the codec */
+ ret = snd_soc_write(codec, WM8978_RESET, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err;
+ }
+
+ wm8978_dai.dev = codec->dev;
+
+ wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8978_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8978_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8978);
+ return ret;
+}
+
+static __devexit void wm8978_unregister(struct wm8978_priv *wm8978)
+{
+ wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8978_dai);
+ snd_soc_unregister_codec(&wm8978->codec);
+ kfree(wm8978);
+ wm8978_codec = NULL;
+}
+
+static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8978_priv *wm8978;
+ struct snd_soc_codec *codec;
+
+ wm8978 = kzalloc(sizeof(struct wm8978_priv), GFP_KERNEL);
+ if (wm8978 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8978->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8978);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8978_register(wm8978);
+}
+
+static __devexit int wm8978_i2c_remove(struct i2c_client *client)
+{
+ struct wm8978_priv *wm8978 = i2c_get_clientdata(client);
+ wm8978_unregister(wm8978);
+ return 0;
+}
+
+static const struct i2c_device_id wm8978_i2c_id[] = {
+ { "wm8978", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
+
+static struct i2c_driver wm8978_i2c_driver = {
+ .driver = {
+ .name = "WM8978",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8978_i2c_probe,
+ .remove = __devexit_p(wm8978_i2c_remove),
+ .id_table = wm8978_i2c_id,
+};
+
+static int __init wm8978_modinit(void)
+{
+ return i2c_add_driver(&wm8978_i2c_driver);
+}
+module_init(wm8978_modinit);
+
+static void __exit wm8978_exit(void)
+{
+ i2c_del_driver(&wm8978_i2c_driver);
+}
+module_exit(wm8978_exit);
+
+MODULE_DESCRIPTION("ASoC WM8978 codec driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8978.h b/sound/soc/codecs/wm8978.h
new file mode 100644
index 000000000000..56ec83270917
--- /dev/null
+++ b/sound/soc/codecs/wm8978.h
@@ -0,0 +1,86 @@
+/*
+ * wm8978.h -- codec driver for WM8978
+ *
+ * Copyright 2009 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 __WM8978_H__
+#define __WM8978_H__
+
+/*
+ * Register values.
+ */
+#define WM8978_RESET 0x00
+#define WM8978_POWER_MANAGEMENT_1 0x01
+#define WM8978_POWER_MANAGEMENT_2 0x02
+#define WM8978_POWER_MANAGEMENT_3 0x03
+#define WM8978_AUDIO_INTERFACE 0x04
+#define WM8978_COMPANDING_CONTROL 0x05
+#define WM8978_CLOCKING 0x06
+#define WM8978_ADDITIONAL_CONTROL 0x07
+#define WM8978_GPIO_CONTROL 0x08
+#define WM8978_JACK_DETECT_CONTROL_1 0x09
+#define WM8978_DAC_CONTROL 0x0A
+#define WM8978_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8978_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8978_JACK_DETECT_CONTROL_2 0x0D
+#define WM8978_ADC_CONTROL 0x0E
+#define WM8978_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8978_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8978_EQ1 0x12
+#define WM8978_EQ2 0x13
+#define WM8978_EQ3 0x14
+#define WM8978_EQ4 0x15
+#define WM8978_EQ5 0x16
+#define WM8978_DAC_LIMITER_1 0x18
+#define WM8978_DAC_LIMITER_2 0x19
+#define WM8978_NOTCH_FILTER_1 0x1b
+#define WM8978_NOTCH_FILTER_2 0x1c
+#define WM8978_NOTCH_FILTER_3 0x1d
+#define WM8978_NOTCH_FILTER_4 0x1e
+#define WM8978_ALC_CONTROL_1 0x20
+#define WM8978_ALC_CONTROL_2 0x21
+#define WM8978_ALC_CONTROL_3 0x22
+#define WM8978_NOISE_GATE 0x23
+#define WM8978_PLL_N 0x24
+#define WM8978_PLL_K1 0x25
+#define WM8978_PLL_K2 0x26
+#define WM8978_PLL_K3 0x27
+#define WM8978_3D_CONTROL 0x29
+#define WM8978_BEEP_CONTROL 0x2b
+#define WM8978_INPUT_CONTROL 0x2c
+#define WM8978_LEFT_INP_PGA_CONTROL 0x2d
+#define WM8978_RIGHT_INP_PGA_CONTROL 0x2e
+#define WM8978_LEFT_ADC_BOOST_CONTROL 0x2f
+#define WM8978_RIGHT_ADC_BOOST_CONTROL 0x30
+#define WM8978_OUTPUT_CONTROL 0x31
+#define WM8978_LEFT_MIXER_CONTROL 0x32
+#define WM8978_RIGHT_MIXER_CONTROL 0x33
+#define WM8978_LOUT1_HP_CONTROL 0x34
+#define WM8978_ROUT1_HP_CONTROL 0x35
+#define WM8978_LOUT2_SPK_CONTROL 0x36
+#define WM8978_ROUT2_SPK_CONTROL 0x37
+#define WM8978_OUT3_MIXER_CONTROL 0x38
+#define WM8978_OUT4_MIXER_CONTROL 0x39
+
+#define WM8978_CACHEREGNUM 58
+
+/* Clock divider Id's */
+enum wm8978_clk_id {
+ WM8978_OPCLKRATE,
+ WM8978_BCLKDIV,
+};
+
+enum wm8978_sysclk_src {
+ WM8978_PLL,
+ WM8978_MCLK
+};
+
+extern struct snd_soc_dai wm8978_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8978;
+
+#endif /* __WM8978_H__ */
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 2862e4dced27..19ad590ca0b3 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -494,7 +495,7 @@ static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8988_priv *wm8988 = codec->private_data;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 11289600:
@@ -584,7 +585,7 @@ static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8988_priv *wm8988 = codec->private_data;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
@@ -609,7 +610,7 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm8988_priv *wm8988 = codec->private_data;
+ struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
int coeff;
@@ -832,7 +833,7 @@ static int wm8988_register(struct wm8988_priv *wm8988,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm8988;
+ snd_soc_codec_set_drvdata(codec, wm8988);
codec->name = "WM8988";
codec->owner = THIS_MODULE;
codec->dai = &wm8988_dai;
@@ -884,7 +885,6 @@ static int wm8988_register(struct wm8988_priv *wm8988,
ret = snd_soc_register_dai(&wm8988_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
- snd_soc_unregister_codec(codec);
goto err_codec;
}
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 341481e0e830..c018772cc430 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -110,21 +111,21 @@ static const u16 wm8990_reg[] = {
#define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
-static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
-static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, 0, -2100, 0);
-static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
-static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
-static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
-static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
-static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -450,7 +451,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
/* INMIX dB values */
static const unsigned int in_mix_tlv[] = {
TLV_DB_RANGE_HEAD(1),
- 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+ 0, 7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
};
/* Left In PGA Connections */
@@ -990,7 +991,7 @@ static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
reg = snd_soc_read(codec, WM8990_CLOCKING_2);
snd_soc_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
- /* set up N , fractional mode and pre-divisor if neccessary */
+ /* set up N , fractional mode and pre-divisor if necessary */
snd_soc_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
(pll_div.div2?WM8990_PRESCALE:0));
snd_soc_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
@@ -1011,7 +1012,7 @@ static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8990_priv *wm8990 = codec->private_data;
+ struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec);
wm8990->sysclk = freq;
return 0;
@@ -1319,10 +1320,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 +1332,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)
@@ -1531,7 +1524,7 @@ static int wm8990_probe(struct platform_device *pdev)
return -ENOMEM;
}
- codec->private_data = wm8990;
+ snd_soc_codec_set_drvdata(codec, wm8990);
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -1548,7 +1541,7 @@ static int wm8990_probe(struct platform_device *pdev)
#endif
if (ret != 0) {
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
}
return ret;
@@ -1568,7 +1561,7 @@ static int wm8990_remove(struct platform_device *pdev)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8990_i2c_driver);
#endif
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2981afae842c..d8d300c6175f 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1,7 +1,7 @@
/*
* wm8993.c -- WM8993 ALSA SoC audio driver
*
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009, 2010 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
@@ -16,7 +16,9 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -29,6 +31,16 @@
#include "wm8993.h"
#include "wm_hubs.h"
+#define WM8993_NUM_SUPPLIES 6
+static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "SPKVDD",
+};
+
static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = {
0x8993, /* R0 - Software Reset */
0x0000, /* R1 - Power Management (1) */
@@ -213,7 +225,9 @@ static struct {
};
struct wm8993_priv {
+ struct wm_hubs_data hubs_data;
u16 reg_cache[WM8993_REGISTER_COUNT];
+ struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES];
struct wm8993_platform_data pdata;
struct snd_soc_codec codec;
int master;
@@ -227,36 +241,9 @@ 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)
-{
- struct i2c_msg xfer[2];
- u16 data;
- int ret;
- struct i2c_client *i2c = codec->control_data;
-
- /* Write register */
- xfer[0].addr = i2c->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
- return 0;
- }
-
- return (data >> 8) | ((data & 0xff) << 8);
-}
-
static int wm8993_volatile(unsigned int reg)
{
switch (reg) {
@@ -271,48 +258,6 @@ static int wm8993_volatile(unsigned int reg)
}
}
-static unsigned int wm8993_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *reg_cache = codec->reg_cache;
-
- BUG_ON(reg > WM8993_MAX_REGISTER);
-
- if (wm8993_volatile(reg))
- return wm8993_read_hw(codec, reg);
- else
- return reg_cache[reg];
-}
-
-static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u16 *reg_cache = codec->reg_cache;
- u8 data[3];
- int ret;
-
- BUG_ON(reg > WM8993_MAX_REGISTER);
-
- /* data is
- * D15..D9 WM8993 register offset
- * D8...D0 register data
- */
- data[0] = reg;
- data[1] = value >> 8;
- data[2] = value & 0x00ff;
-
- if (!wm8993_volatile(reg))
- reg_cache[reg] = value;
-
- ret = codec->hw_write(codec->control_data, data, 3);
-
- if (ret == 3)
- return 0;
- if (ret < 0)
- return ret;
- return -EIO;
-}
-
struct _fll_div {
u16 fll_fratio;
u16 fll_outdiv;
@@ -426,7 +371,7 @@ static int wm8993_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 wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
u16 reg1, reg4, reg5;
struct _fll_div fll_div;
int ret;
@@ -441,9 +386,9 @@ static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
wm8993->fll_fref = 0;
wm8993->fll_fout = 0;
- reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
reg1 &= ~WM8993_FLL_ENA;
- wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
return 0;
}
@@ -452,7 +397,7 @@ static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
if (ret != 0)
return ret;
- reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5);
+ reg5 = snd_soc_read(codec, WM8993_FLL_CONTROL_5);
reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
switch (fll_id) {
@@ -474,45 +419,46 @@ static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
/* Any FLL configuration change requires that the FLL be
* disabled first. */
- reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
reg1 &= ~WM8993_FLL_ENA;
- wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
/* Apply the configuration */
if (fll_div.k)
reg1 |= WM8993_FLL_FRAC_MASK;
else
reg1 &= ~WM8993_FLL_FRAC_MASK;
- wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
- wm8993_write(codec, WM8993_FLL_CONTROL_2,
- (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
- (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
- wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_2,
+ (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+ snd_soc_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
- reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4);
+ reg4 = snd_soc_read(codec, WM8993_FLL_CONTROL_4);
reg4 &= ~WM8993_FLL_N_MASK;
reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
- wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_4, reg4);
reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
- wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);
/* Enable the FLL */
- wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
wm8993->fll_fref = Fref;
wm8993->fll_fout = Fout;
+ wm8993->fll_src = source;
return 0;
}
static int configure_clock(struct snd_soc_codec *codec)
{
- struct wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
/* This should be done on init() for bypass paths */
@@ -520,7 +466,7 @@ static int configure_clock(struct snd_soc_codec *codec)
case WM8993_SYSCLK_MCLK:
dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
- reg = wm8993_read(codec, WM8993_CLOCKING_2);
+ reg = snd_soc_read(codec, WM8993_CLOCKING_2);
reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
if (wm8993->mclk_rate > 13500000) {
reg |= WM8993_MCLK_DIV;
@@ -529,14 +475,14 @@ static int configure_clock(struct snd_soc_codec *codec)
reg &= ~WM8993_MCLK_DIV;
wm8993->sysclk_rate = wm8993->mclk_rate;
}
- wm8993_write(codec, WM8993_CLOCKING_2, reg);
+ snd_soc_write(codec, WM8993_CLOCKING_2, reg);
break;
case WM8993_SYSCLK_FLL:
dev_dbg(codec->dev, "Using %dHz FLL clock\n",
wm8993->fll_fout);
- reg = wm8993_read(codec, WM8993_CLOCKING_2);
+ reg = snd_soc_read(codec, WM8993_CLOCKING_2);
reg |= WM8993_SYSCLK_SRC;
if (wm8993->fll_fout > 13500000) {
reg |= WM8993_MCLK_DIV;
@@ -545,7 +491,7 @@ static int configure_clock(struct snd_soc_codec *codec)
reg &= ~WM8993_MCLK_DIV;
wm8993->sysclk_rate = wm8993->fll_fout;
}
- wm8993_write(codec, WM8993_CLOCKING_2, reg);
+ snd_soc_write(codec, WM8993_CLOCKING_2, reg);
break;
default:
@@ -771,7 +717,7 @@ static int class_w_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct snd_soc_codec *codec = widget->codec;
- struct wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret;
/* Turn it off if we're using the main output mixer */
@@ -978,10 +924,33 @@ static const struct snd_soc_dapm_route routes[] = {
{ "Right Headphone Mux", "DAC", "DACR" },
};
+static void wm8993_cache_restore(struct snd_soc_codec *codec)
+{
+ u16 *cache = codec->reg_cache;
+ int i;
+
+ if (!codec->cache_sync)
+ return;
+
+ /* Reenable hardware writes */
+ codec->cache_only = 0;
+
+ /* 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]);
+ }
+
+ /* We're in sync again */
+ codec->cache_sync = 0;
+}
+
static int wm8993_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ int ret;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -995,6 +964,18 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0)
+ return ret;
+
+ wm8993_cache_restore(codec);
+
+ /* Tune DC servo configuration */
+ snd_soc_write(codec, 0x44, 3);
+ snd_soc_write(codec, 0x56, 3);
+ snd_soc_write(codec, 0x44, 0);
+
/* Bring up VMID with fast soft start */
snd_soc_update_bits(codec, WM8993_ANTIPOP2,
WM8993_STARTUP_BIAS_ENA |
@@ -1042,6 +1023,18 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
0);
+
+#ifdef CONFIG_REGULATOR
+ /* Post 2.6.34 we will be able to get a callback when
+ * the regulators are disabled which we can use but
+ * for now just assume that the power will be cut if
+ * the regulator API is in use.
+ */
+ codec->cache_sync = 1;
+#endif
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
break;
}
@@ -1054,7 +1047,7 @@ static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case WM8993_SYSCLK_MCLK:
@@ -1074,9 +1067,9 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8993_priv *wm8993 = codec->private_data;
- unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
- unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
+ unsigned int aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
+ unsigned int aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
@@ -1159,8 +1152,8 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
- wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
return 0;
}
@@ -1170,20 +1163,20 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret, i, best, best_val, cur_val;
unsigned int clocking1, clocking3, aif1, aif4;
- clocking1 = wm8993_read(codec, WM8993_CLOCKING_1);
+ clocking1 = snd_soc_read(codec, WM8993_CLOCKING_1);
clocking1 &= ~WM8993_BCLK_DIV_MASK;
- clocking3 = wm8993_read(codec, WM8993_CLOCKING_3);
+ clocking3 = snd_soc_read(codec, WM8993_CLOCKING_3);
clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
- aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+ aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
aif1 &= ~WM8993_AIF_WL_MASK;
- aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+ aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
aif4 &= ~WM8993_LRCLK_RATE_MASK;
/* What BCLK do we need? */
@@ -1276,14 +1269,14 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
aif4 |= wm8993->bclk / wm8993->fs;
- wm8993_write(codec, WM8993_CLOCKING_1, clocking1);
- wm8993_write(codec, WM8993_CLOCKING_3, clocking3);
- wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
- wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+ snd_soc_write(codec, WM8993_CLOCKING_1, clocking1);
+ snd_soc_write(codec, WM8993_CLOCKING_3, clocking3);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
/* ReTune Mobile? */
if (wm8993->pdata.num_retune_configs) {
- u16 eq1 = wm8993_read(codec, WM8993_EQ1);
+ u16 eq1 = snd_soc_read(codec, WM8993_EQ1);
struct wm8993_retune_mobile_setting *s;
best = 0;
@@ -1306,7 +1299,7 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
for (i = 1; i < ARRAY_SIZE(s->config); i++)
- wm8993_write(codec, WM8993_EQ1 + i, s->config[i]);
+ snd_soc_write(codec, WM8993_EQ1 + i, s->config[i]);
snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
}
@@ -1319,14 +1312,14 @@ static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int reg;
- reg = wm8993_read(codec, WM8993_DAC_CTRL);
+ reg = snd_soc_read(codec, WM8993_DAC_CTRL);
if (mute)
reg |= WM8993_DAC_MUTE;
else
reg &= ~WM8993_DAC_MUTE;
- wm8993_write(codec, WM8993_DAC_CTRL, reg);
+ snd_soc_write(codec, WM8993_DAC_CTRL, reg);
return 0;
}
@@ -1335,7 +1328,7 @@ static int wm8993_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 wm8993_priv *wm8993 = codec->private_data;
+ struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int aif1 = 0;
int aif2 = 0;
@@ -1438,7 +1431,7 @@ static int wm8993_probe(struct platform_device *pdev)
socdev->card->codec = wm8993_codec;
codec = wm8993_codec;
- wm8993 = codec->private_data;
+ wm8993 = snd_soc_codec_get_drvdata(codec);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@@ -1480,9 +1473,66 @@ 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 = snd_soc_codec_get_drvdata(codec);
+ 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 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ 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);
@@ -1493,6 +1543,7 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
struct snd_soc_codec *codec;
unsigned int val;
int ret;
+ int i;
if (wm8993_codec) {
dev_err(&i2c->dev, "A WM8993 is already registered\n");
@@ -1513,36 +1564,62 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "WM8993";
- codec->read = wm8993_read;
- codec->write = wm8993_write;
- codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->volatile_register = wm8993_volatile;
codec->reg_cache = wm8993->reg_cache;
codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8993_set_bias_level;
codec->dai = &wm8993_dai;
codec->num_dai = 1;
- codec->private_data = wm8993;
+ snd_soc_codec_set_drvdata(codec, wm8993);
+
+ wm8993->hubs_data.hp_startup_mode = 1;
+ wm8993->hubs_data.dcs_codes = -2;
memcpy(wm8993->reg_cache, wm8993_reg_defaults,
sizeof(wm8993->reg_cache));
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
i2c_set_clientdata(i2c, wm8993);
codec->control_data = i2c;
wm8993_codec = codec;
codec->dev = &i2c->dev;
- val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET);
+ for (i = 0; i < ARRAY_SIZE(wm8993->supplies); i++)
+ wm8993->supplies[i].supply = wm8993_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+ wm8993->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ val = snd_soc_read(codec, WM8993_SOFTWARE_RESET);
if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
dev_err(codec->dev, "Invalid ID register value %x\n", val);
ret = -EINVAL;
- goto err;
+ goto err_enable;
}
- ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
+ ret = snd_soc_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
if (ret != 0)
- goto err;
+ goto err_enable;
+
+ codec->cache_only = 1;
/* By default we're using the output mixers */
wm8993->class_w_users = 2;
@@ -1572,7 +1649,7 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret != 0)
- goto err;
+ goto err_enable;
wm8993_dai.dev = codec->dev;
@@ -1586,6 +1663,10 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
err_bias:
wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
err:
wm8993_codec = NULL;
kfree(wm8993);
@@ -1600,6 +1681,7 @@ static int wm8993_i2c_remove(struct i2c_client *client)
snd_soc_unregister_dai(&wm8993_dai);
wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF);
+ regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
kfree(wm8993);
return 0;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
new file mode 100644
index 000000000000..e84a1177f350
--- /dev/null
+++ b/sound/soc/codecs/wm8994.c
@@ -0,0 +1,4080 @@
+/*
+ * wm8994.c -- WM8994 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 <linux/slab.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 <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+#include "wm_hubs.h"
+
+static struct snd_soc_codec *wm8994_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8994;
+
+struct fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+static int wm8994_drc_base[] = {
+ WM8994_AIF1_DRC1_1,
+ WM8994_AIF1_DRC2_1,
+ WM8994_AIF2_DRC_1,
+};
+
+static int wm8994_retune_mobile_base[] = {
+ WM8994_AIF1_DAC1_EQ_GAINS_1,
+ WM8994_AIF1_DAC2_EQ_GAINS_1,
+ WM8994_AIF2_EQ_GAINS_1,
+};
+
+#define WM8994_REG_CACHE_SIZE 0x621
+
+struct wm8994_micdet {
+ struct snd_soc_jack *jack;
+ int det;
+ int shrt;
+};
+
+/* codec private data */
+struct wm8994_priv {
+ struct wm_hubs_data hubs;
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8994_REG_CACHE_SIZE + 1];
+ int sysclk[2];
+ int sysclk_rate[2];
+ int mclk[2];
+ int aifclk[2];
+ struct fll_config fll[2], fll_suspend[2];
+
+ int dac_rates[2];
+ int lrclk_shared[2];
+
+ /* Platform dependant DRC configuration */
+ const char **drc_texts;
+ int drc_cfg[WM8994_NUM_DRC];
+ struct soc_enum drc_enum;
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[WM8994_NUM_EQ];
+ struct soc_enum retune_mobile_enum;
+
+ struct wm8994_micdet micdet[2];
+
+ struct wm8994_pdata *pdata;
+};
+
+static struct {
+ unsigned short readable; /* Mask of readable bits */
+ unsigned short writable; /* Mask of writable bits */
+ unsigned short vol; /* Mask of volatile bits */
+} access_masks[] = {
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R0 - Software Reset */
+ { 0x3B37, 0x3B37, 0x0000 }, /* R1 - Power Management (1) */
+ { 0x6BF0, 0x6BF0, 0x0000 }, /* R2 - Power Management (2) */
+ { 0x3FF0, 0x3FF0, 0x0000 }, /* R3 - Power Management (3) */
+ { 0x3F3F, 0x3F3F, 0x0000 }, /* R4 - Power Management (4) */
+ { 0x3F0F, 0x3F0F, 0x0000 }, /* R5 - Power Management (5) */
+ { 0x003F, 0x003F, 0x0000 }, /* R6 - Power Management (6) */
+ { 0x0000, 0x0000, 0x0000 }, /* R7 */
+ { 0x0000, 0x0000, 0x0000 }, /* R8 */
+ { 0x0000, 0x0000, 0x0000 }, /* R9 */
+ { 0x0000, 0x0000, 0x0000 }, /* R10 */
+ { 0x0000, 0x0000, 0x0000 }, /* R11 */
+ { 0x0000, 0x0000, 0x0000 }, /* R12 */
+ { 0x0000, 0x0000, 0x0000 }, /* R13 */
+ { 0x0000, 0x0000, 0x0000 }, /* R14 */
+ { 0x0000, 0x0000, 0x0000 }, /* R15 */
+ { 0x0000, 0x0000, 0x0000 }, /* R16 */
+ { 0x0000, 0x0000, 0x0000 }, /* R17 */
+ { 0x0000, 0x0000, 0x0000 }, /* R18 */
+ { 0x0000, 0x0000, 0x0000 }, /* R19 */
+ { 0x0000, 0x0000, 0x0000 }, /* R20 */
+ { 0x01C0, 0x01C0, 0x0000 }, /* R21 - Input Mixer (1) */
+ { 0x0000, 0x0000, 0x0000 }, /* R22 */
+ { 0x0000, 0x0000, 0x0000 }, /* R23 */
+ { 0x00DF, 0x01DF, 0x0000 }, /* R24 - Left Line Input 1&2 Volume */
+ { 0x00DF, 0x01DF, 0x0000 }, /* R25 - Left Line Input 3&4 Volume */
+ { 0x00DF, 0x01DF, 0x0000 }, /* R26 - Right Line Input 1&2 Volume */
+ { 0x00DF, 0x01DF, 0x0000 }, /* R27 - Right Line Input 3&4 Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R28 - Left Output Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R29 - Right Output Volume */
+ { 0x0077, 0x0077, 0x0000 }, /* R30 - Line Outputs Volume */
+ { 0x0030, 0x0030, 0x0000 }, /* R31 - HPOUT2 Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R32 - Left OPGA Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R33 - Right OPGA Volume */
+ { 0x007F, 0x007F, 0x0000 }, /* R34 - SPKMIXL Attenuation */
+ { 0x017F, 0x017F, 0x0000 }, /* R35 - SPKMIXR Attenuation */
+ { 0x003F, 0x003F, 0x0000 }, /* R36 - SPKOUT Mixers */
+ { 0x003F, 0x003F, 0x0000 }, /* R37 - ClassD */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R38 - Speaker Volume Left */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R39 - Speaker Volume Right */
+ { 0x00FF, 0x00FF, 0x0000 }, /* R40 - Input Mixer (2) */
+ { 0x01B7, 0x01B7, 0x0000 }, /* R41 - Input Mixer (3) */
+ { 0x01B7, 0x01B7, 0x0000 }, /* R42 - Input Mixer (4) */
+ { 0x01C7, 0x01C7, 0x0000 }, /* R43 - Input Mixer (5) */
+ { 0x01C7, 0x01C7, 0x0000 }, /* R44 - Input Mixer (6) */
+ { 0x01FF, 0x01FF, 0x0000 }, /* R45 - Output Mixer (1) */
+ { 0x01FF, 0x01FF, 0x0000 }, /* R46 - Output Mixer (2) */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R47 - Output Mixer (3) */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R48 - Output Mixer (4) */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R49 - Output Mixer (5) */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R50 - Output Mixer (6) */
+ { 0x0038, 0x0038, 0x0000 }, /* R51 - HPOUT2 Mixer */
+ { 0x0077, 0x0077, 0x0000 }, /* R52 - Line Mixer (1) */
+ { 0x0077, 0x0077, 0x0000 }, /* R53 - Line Mixer (2) */
+ { 0x03FF, 0x03FF, 0x0000 }, /* R54 - Speaker Mixer */
+ { 0x00C1, 0x00C1, 0x0000 }, /* R55 - Additional Control */
+ { 0x00F0, 0x00F0, 0x0000 }, /* R56 - AntiPOP (1) */
+ { 0x01EF, 0x01EF, 0x0000 }, /* R57 - AntiPOP (2) */
+ { 0x00FF, 0x00FF, 0x0000 }, /* R58 - MICBIAS */
+ { 0x000F, 0x000F, 0x0000 }, /* R59 - LDO 1 */
+ { 0x0007, 0x0007, 0x0000 }, /* R60 - LDO 2 */
+ { 0x0000, 0x0000, 0x0000 }, /* R61 */
+ { 0x0000, 0x0000, 0x0000 }, /* R62 */
+ { 0x0000, 0x0000, 0x0000 }, /* R63 */
+ { 0x0000, 0x0000, 0x0000 }, /* R64 */
+ { 0x0000, 0x0000, 0x0000 }, /* R65 */
+ { 0x0000, 0x0000, 0x0000 }, /* R66 */
+ { 0x0000, 0x0000, 0x0000 }, /* R67 */
+ { 0x0000, 0x0000, 0x0000 }, /* R68 */
+ { 0x0000, 0x0000, 0x0000 }, /* R69 */
+ { 0x0000, 0x0000, 0x0000 }, /* R70 */
+ { 0x0000, 0x0000, 0x0000 }, /* R71 */
+ { 0x0000, 0x0000, 0x0000 }, /* R72 */
+ { 0x0000, 0x0000, 0x0000 }, /* R73 */
+ { 0x0000, 0x0000, 0x0000 }, /* R74 */
+ { 0x0000, 0x0000, 0x0000 }, /* R75 */
+ { 0x8000, 0x8000, 0x0000 }, /* R76 - Charge Pump (1) */
+ { 0x0000, 0x0000, 0x0000 }, /* R77 */
+ { 0x0000, 0x0000, 0x0000 }, /* R78 */
+ { 0x0000, 0x0000, 0x0000 }, /* R79 */
+ { 0x0000, 0x0000, 0x0000 }, /* R80 */
+ { 0x0301, 0x0301, 0x0000 }, /* R81 - Class W (1) */
+ { 0x0000, 0x0000, 0x0000 }, /* R82 */
+ { 0x0000, 0x0000, 0x0000 }, /* R83 */
+ { 0x333F, 0x333F, 0x0000 }, /* R84 - DC Servo (1) */
+ { 0x0FEF, 0x0FEF, 0x0000 }, /* R85 - DC Servo (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R86 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R87 - DC Servo (4) */
+ { 0x0333, 0x0000, 0x0000 }, /* R88 - DC Servo Readback */
+ { 0x0000, 0x0000, 0x0000 }, /* R89 */
+ { 0x0000, 0x0000, 0x0000 }, /* R90 */
+ { 0x0000, 0x0000, 0x0000 }, /* R91 */
+ { 0x0000, 0x0000, 0x0000 }, /* R92 */
+ { 0x0000, 0x0000, 0x0000 }, /* R93 */
+ { 0x0000, 0x0000, 0x0000 }, /* R94 */
+ { 0x0000, 0x0000, 0x0000 }, /* R95 */
+ { 0x00EE, 0x00EE, 0x0000 }, /* R96 - Analogue HP (1) */
+ { 0x0000, 0x0000, 0x0000 }, /* R97 */
+ { 0x0000, 0x0000, 0x0000 }, /* R98 */
+ { 0x0000, 0x0000, 0x0000 }, /* R99 */
+ { 0x0000, 0x0000, 0x0000 }, /* R100 */
+ { 0x0000, 0x0000, 0x0000 }, /* R101 */
+ { 0x0000, 0x0000, 0x0000 }, /* R102 */
+ { 0x0000, 0x0000, 0x0000 }, /* R103 */
+ { 0x0000, 0x0000, 0x0000 }, /* R104 */
+ { 0x0000, 0x0000, 0x0000 }, /* R105 */
+ { 0x0000, 0x0000, 0x0000 }, /* R106 */
+ { 0x0000, 0x0000, 0x0000 }, /* R107 */
+ { 0x0000, 0x0000, 0x0000 }, /* R108 */
+ { 0x0000, 0x0000, 0x0000 }, /* R109 */
+ { 0x0000, 0x0000, 0x0000 }, /* R110 */
+ { 0x0000, 0x0000, 0x0000 }, /* R111 */
+ { 0x0000, 0x0000, 0x0000 }, /* R112 */
+ { 0x0000, 0x0000, 0x0000 }, /* R113 */
+ { 0x0000, 0x0000, 0x0000 }, /* R114 */
+ { 0x0000, 0x0000, 0x0000 }, /* R115 */
+ { 0x0000, 0x0000, 0x0000 }, /* R116 */
+ { 0x0000, 0x0000, 0x0000 }, /* R117 */
+ { 0x0000, 0x0000, 0x0000 }, /* R118 */
+ { 0x0000, 0x0000, 0x0000 }, /* R119 */
+ { 0x0000, 0x0000, 0x0000 }, /* R120 */
+ { 0x0000, 0x0000, 0x0000 }, /* R121 */
+ { 0x0000, 0x0000, 0x0000 }, /* R122 */
+ { 0x0000, 0x0000, 0x0000 }, /* R123 */
+ { 0x0000, 0x0000, 0x0000 }, /* R124 */
+ { 0x0000, 0x0000, 0x0000 }, /* R125 */
+ { 0x0000, 0x0000, 0x0000 }, /* R126 */
+ { 0x0000, 0x0000, 0x0000 }, /* R127 */
+ { 0x0000, 0x0000, 0x0000 }, /* R128 */
+ { 0x0000, 0x0000, 0x0000 }, /* R129 */
+ { 0x0000, 0x0000, 0x0000 }, /* R130 */
+ { 0x0000, 0x0000, 0x0000 }, /* R131 */
+ { 0x0000, 0x0000, 0x0000 }, /* R132 */
+ { 0x0000, 0x0000, 0x0000 }, /* R133 */
+ { 0x0000, 0x0000, 0x0000 }, /* R134 */
+ { 0x0000, 0x0000, 0x0000 }, /* R135 */
+ { 0x0000, 0x0000, 0x0000 }, /* R136 */
+ { 0x0000, 0x0000, 0x0000 }, /* R137 */
+ { 0x0000, 0x0000, 0x0000 }, /* R138 */
+ { 0x0000, 0x0000, 0x0000 }, /* R139 */
+ { 0x0000, 0x0000, 0x0000 }, /* R140 */
+ { 0x0000, 0x0000, 0x0000 }, /* R141 */
+ { 0x0000, 0x0000, 0x0000 }, /* R142 */
+ { 0x0000, 0x0000, 0x0000 }, /* R143 */
+ { 0x0000, 0x0000, 0x0000 }, /* R144 */
+ { 0x0000, 0x0000, 0x0000 }, /* R145 */
+ { 0x0000, 0x0000, 0x0000 }, /* R146 */
+ { 0x0000, 0x0000, 0x0000 }, /* R147 */
+ { 0x0000, 0x0000, 0x0000 }, /* R148 */
+ { 0x0000, 0x0000, 0x0000 }, /* R149 */
+ { 0x0000, 0x0000, 0x0000 }, /* R150 */
+ { 0x0000, 0x0000, 0x0000 }, /* R151 */
+ { 0x0000, 0x0000, 0x0000 }, /* R152 */
+ { 0x0000, 0x0000, 0x0000 }, /* R153 */
+ { 0x0000, 0x0000, 0x0000 }, /* R154 */
+ { 0x0000, 0x0000, 0x0000 }, /* R155 */
+ { 0x0000, 0x0000, 0x0000 }, /* R156 */
+ { 0x0000, 0x0000, 0x0000 }, /* R157 */
+ { 0x0000, 0x0000, 0x0000 }, /* R158 */
+ { 0x0000, 0x0000, 0x0000 }, /* R159 */
+ { 0x0000, 0x0000, 0x0000 }, /* R160 */
+ { 0x0000, 0x0000, 0x0000 }, /* R161 */
+ { 0x0000, 0x0000, 0x0000 }, /* R162 */
+ { 0x0000, 0x0000, 0x0000 }, /* R163 */
+ { 0x0000, 0x0000, 0x0000 }, /* R164 */
+ { 0x0000, 0x0000, 0x0000 }, /* R165 */
+ { 0x0000, 0x0000, 0x0000 }, /* R166 */
+ { 0x0000, 0x0000, 0x0000 }, /* R167 */
+ { 0x0000, 0x0000, 0x0000 }, /* R168 */
+ { 0x0000, 0x0000, 0x0000 }, /* R169 */
+ { 0x0000, 0x0000, 0x0000 }, /* R170 */
+ { 0x0000, 0x0000, 0x0000 }, /* R171 */
+ { 0x0000, 0x0000, 0x0000 }, /* R172 */
+ { 0x0000, 0x0000, 0x0000 }, /* R173 */
+ { 0x0000, 0x0000, 0x0000 }, /* R174 */
+ { 0x0000, 0x0000, 0x0000 }, /* R175 */
+ { 0x0000, 0x0000, 0x0000 }, /* R176 */
+ { 0x0000, 0x0000, 0x0000 }, /* R177 */
+ { 0x0000, 0x0000, 0x0000 }, /* R178 */
+ { 0x0000, 0x0000, 0x0000 }, /* R179 */
+ { 0x0000, 0x0000, 0x0000 }, /* R180 */
+ { 0x0000, 0x0000, 0x0000 }, /* R181 */
+ { 0x0000, 0x0000, 0x0000 }, /* R182 */
+ { 0x0000, 0x0000, 0x0000 }, /* R183 */
+ { 0x0000, 0x0000, 0x0000 }, /* R184 */
+ { 0x0000, 0x0000, 0x0000 }, /* R185 */
+ { 0x0000, 0x0000, 0x0000 }, /* R186 */
+ { 0x0000, 0x0000, 0x0000 }, /* R187 */
+ { 0x0000, 0x0000, 0x0000 }, /* R188 */
+ { 0x0000, 0x0000, 0x0000 }, /* R189 */
+ { 0x0000, 0x0000, 0x0000 }, /* R190 */
+ { 0x0000, 0x0000, 0x0000 }, /* R191 */
+ { 0x0000, 0x0000, 0x0000 }, /* R192 */
+ { 0x0000, 0x0000, 0x0000 }, /* R193 */
+ { 0x0000, 0x0000, 0x0000 }, /* R194 */
+ { 0x0000, 0x0000, 0x0000 }, /* R195 */
+ { 0x0000, 0x0000, 0x0000 }, /* R196 */
+ { 0x0000, 0x0000, 0x0000 }, /* R197 */
+ { 0x0000, 0x0000, 0x0000 }, /* R198 */
+ { 0x0000, 0x0000, 0x0000 }, /* R199 */
+ { 0x0000, 0x0000, 0x0000 }, /* R200 */
+ { 0x0000, 0x0000, 0x0000 }, /* R201 */
+ { 0x0000, 0x0000, 0x0000 }, /* R202 */
+ { 0x0000, 0x0000, 0x0000 }, /* R203 */
+ { 0x0000, 0x0000, 0x0000 }, /* R204 */
+ { 0x0000, 0x0000, 0x0000 }, /* R205 */
+ { 0x0000, 0x0000, 0x0000 }, /* R206 */
+ { 0x0000, 0x0000, 0x0000 }, /* R207 */
+ { 0x0000, 0x0000, 0x0000 }, /* R208 */
+ { 0x0000, 0x0000, 0x0000 }, /* R209 */
+ { 0x0000, 0x0000, 0x0000 }, /* R210 */
+ { 0x0000, 0x0000, 0x0000 }, /* R211 */
+ { 0x0000, 0x0000, 0x0000 }, /* R212 */
+ { 0x0000, 0x0000, 0x0000 }, /* R213 */
+ { 0x0000, 0x0000, 0x0000 }, /* R214 */
+ { 0x0000, 0x0000, 0x0000 }, /* R215 */
+ { 0x0000, 0x0000, 0x0000 }, /* R216 */
+ { 0x0000, 0x0000, 0x0000 }, /* R217 */
+ { 0x0000, 0x0000, 0x0000 }, /* R218 */
+ { 0x0000, 0x0000, 0x0000 }, /* R219 */
+ { 0x0000, 0x0000, 0x0000 }, /* R220 */
+ { 0x0000, 0x0000, 0x0000 }, /* R221 */
+ { 0x0000, 0x0000, 0x0000 }, /* R222 */
+ { 0x0000, 0x0000, 0x0000 }, /* R223 */
+ { 0x0000, 0x0000, 0x0000 }, /* R224 */
+ { 0x0000, 0x0000, 0x0000 }, /* R225 */
+ { 0x0000, 0x0000, 0x0000 }, /* R226 */
+ { 0x0000, 0x0000, 0x0000 }, /* R227 */
+ { 0x0000, 0x0000, 0x0000 }, /* R228 */
+ { 0x0000, 0x0000, 0x0000 }, /* R229 */
+ { 0x0000, 0x0000, 0x0000 }, /* R230 */
+ { 0x0000, 0x0000, 0x0000 }, /* R231 */
+ { 0x0000, 0x0000, 0x0000 }, /* R232 */
+ { 0x0000, 0x0000, 0x0000 }, /* R233 */
+ { 0x0000, 0x0000, 0x0000 }, /* R234 */
+ { 0x0000, 0x0000, 0x0000 }, /* R235 */
+ { 0x0000, 0x0000, 0x0000 }, /* R236 */
+ { 0x0000, 0x0000, 0x0000 }, /* R237 */
+ { 0x0000, 0x0000, 0x0000 }, /* R238 */
+ { 0x0000, 0x0000, 0x0000 }, /* R239 */
+ { 0x0000, 0x0000, 0x0000 }, /* R240 */
+ { 0x0000, 0x0000, 0x0000 }, /* R241 */
+ { 0x0000, 0x0000, 0x0000 }, /* R242 */
+ { 0x0000, 0x0000, 0x0000 }, /* R243 */
+ { 0x0000, 0x0000, 0x0000 }, /* R244 */
+ { 0x0000, 0x0000, 0x0000 }, /* R245 */
+ { 0x0000, 0x0000, 0x0000 }, /* R246 */
+ { 0x0000, 0x0000, 0x0000 }, /* R247 */
+ { 0x0000, 0x0000, 0x0000 }, /* R248 */
+ { 0x0000, 0x0000, 0x0000 }, /* R249 */
+ { 0x0000, 0x0000, 0x0000 }, /* R250 */
+ { 0x0000, 0x0000, 0x0000 }, /* R251 */
+ { 0x0000, 0x0000, 0x0000 }, /* R252 */
+ { 0x0000, 0x0000, 0x0000 }, /* R253 */
+ { 0x0000, 0x0000, 0x0000 }, /* R254 */
+ { 0x0000, 0x0000, 0x0000 }, /* R255 */
+ { 0x000F, 0x0000, 0x0000 }, /* R256 - Chip Revision */
+ { 0x0074, 0x0074, 0x0000 }, /* R257 - Control Interface */
+ { 0x0000, 0x0000, 0x0000 }, /* R258 */
+ { 0x0000, 0x0000, 0x0000 }, /* R259 */
+ { 0x0000, 0x0000, 0x0000 }, /* R260 */
+ { 0x0000, 0x0000, 0x0000 }, /* R261 */
+ { 0x0000, 0x0000, 0x0000 }, /* R262 */
+ { 0x0000, 0x0000, 0x0000 }, /* R263 */
+ { 0x0000, 0x0000, 0x0000 }, /* R264 */
+ { 0x0000, 0x0000, 0x0000 }, /* R265 */
+ { 0x0000, 0x0000, 0x0000 }, /* R266 */
+ { 0x0000, 0x0000, 0x0000 }, /* R267 */
+ { 0x0000, 0x0000, 0x0000 }, /* R268 */
+ { 0x0000, 0x0000, 0x0000 }, /* R269 */
+ { 0x0000, 0x0000, 0x0000 }, /* R270 */
+ { 0x0000, 0x0000, 0x0000 }, /* R271 */
+ { 0x807F, 0x837F, 0x0000 }, /* R272 - Write Sequencer Ctrl (1) */
+ { 0x017F, 0x0000, 0x0000 }, /* R273 - Write Sequencer Ctrl (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R274 */
+ { 0x0000, 0x0000, 0x0000 }, /* R275 */
+ { 0x0000, 0x0000, 0x0000 }, /* R276 */
+ { 0x0000, 0x0000, 0x0000 }, /* R277 */
+ { 0x0000, 0x0000, 0x0000 }, /* R278 */
+ { 0x0000, 0x0000, 0x0000 }, /* R279 */
+ { 0x0000, 0x0000, 0x0000 }, /* R280 */
+ { 0x0000, 0x0000, 0x0000 }, /* R281 */
+ { 0x0000, 0x0000, 0x0000 }, /* R282 */
+ { 0x0000, 0x0000, 0x0000 }, /* R283 */
+ { 0x0000, 0x0000, 0x0000 }, /* R284 */
+ { 0x0000, 0x0000, 0x0000 }, /* R285 */
+ { 0x0000, 0x0000, 0x0000 }, /* R286 */
+ { 0x0000, 0x0000, 0x0000 }, /* R287 */
+ { 0x0000, 0x0000, 0x0000 }, /* R288 */
+ { 0x0000, 0x0000, 0x0000 }, /* R289 */
+ { 0x0000, 0x0000, 0x0000 }, /* R290 */
+ { 0x0000, 0x0000, 0x0000 }, /* R291 */
+ { 0x0000, 0x0000, 0x0000 }, /* R292 */
+ { 0x0000, 0x0000, 0x0000 }, /* R293 */
+ { 0x0000, 0x0000, 0x0000 }, /* R294 */
+ { 0x0000, 0x0000, 0x0000 }, /* R295 */
+ { 0x0000, 0x0000, 0x0000 }, /* R296 */
+ { 0x0000, 0x0000, 0x0000 }, /* R297 */
+ { 0x0000, 0x0000, 0x0000 }, /* R298 */
+ { 0x0000, 0x0000, 0x0000 }, /* R299 */
+ { 0x0000, 0x0000, 0x0000 }, /* R300 */
+ { 0x0000, 0x0000, 0x0000 }, /* R301 */
+ { 0x0000, 0x0000, 0x0000 }, /* R302 */
+ { 0x0000, 0x0000, 0x0000 }, /* R303 */
+ { 0x0000, 0x0000, 0x0000 }, /* R304 */
+ { 0x0000, 0x0000, 0x0000 }, /* R305 */
+ { 0x0000, 0x0000, 0x0000 }, /* R306 */
+ { 0x0000, 0x0000, 0x0000 }, /* R307 */
+ { 0x0000, 0x0000, 0x0000 }, /* R308 */
+ { 0x0000, 0x0000, 0x0000 }, /* R309 */
+ { 0x0000, 0x0000, 0x0000 }, /* R310 */
+ { 0x0000, 0x0000, 0x0000 }, /* R311 */
+ { 0x0000, 0x0000, 0x0000 }, /* R312 */
+ { 0x0000, 0x0000, 0x0000 }, /* R313 */
+ { 0x0000, 0x0000, 0x0000 }, /* R314 */
+ { 0x0000, 0x0000, 0x0000 }, /* R315 */
+ { 0x0000, 0x0000, 0x0000 }, /* R316 */
+ { 0x0000, 0x0000, 0x0000 }, /* R317 */
+ { 0x0000, 0x0000, 0x0000 }, /* R318 */
+ { 0x0000, 0x0000, 0x0000 }, /* R319 */
+ { 0x0000, 0x0000, 0x0000 }, /* R320 */
+ { 0x0000, 0x0000, 0x0000 }, /* R321 */
+ { 0x0000, 0x0000, 0x0000 }, /* R322 */
+ { 0x0000, 0x0000, 0x0000 }, /* R323 */
+ { 0x0000, 0x0000, 0x0000 }, /* R324 */
+ { 0x0000, 0x0000, 0x0000 }, /* R325 */
+ { 0x0000, 0x0000, 0x0000 }, /* R326 */
+ { 0x0000, 0x0000, 0x0000 }, /* R327 */
+ { 0x0000, 0x0000, 0x0000 }, /* R328 */
+ { 0x0000, 0x0000, 0x0000 }, /* R329 */
+ { 0x0000, 0x0000, 0x0000 }, /* R330 */
+ { 0x0000, 0x0000, 0x0000 }, /* R331 */
+ { 0x0000, 0x0000, 0x0000 }, /* R332 */
+ { 0x0000, 0x0000, 0x0000 }, /* R333 */
+ { 0x0000, 0x0000, 0x0000 }, /* R334 */
+ { 0x0000, 0x0000, 0x0000 }, /* R335 */
+ { 0x0000, 0x0000, 0x0000 }, /* R336 */
+ { 0x0000, 0x0000, 0x0000 }, /* R337 */
+ { 0x0000, 0x0000, 0x0000 }, /* R338 */
+ { 0x0000, 0x0000, 0x0000 }, /* R339 */
+ { 0x0000, 0x0000, 0x0000 }, /* R340 */
+ { 0x0000, 0x0000, 0x0000 }, /* R341 */
+ { 0x0000, 0x0000, 0x0000 }, /* R342 */
+ { 0x0000, 0x0000, 0x0000 }, /* R343 */
+ { 0x0000, 0x0000, 0x0000 }, /* R344 */
+ { 0x0000, 0x0000, 0x0000 }, /* R345 */
+ { 0x0000, 0x0000, 0x0000 }, /* R346 */
+ { 0x0000, 0x0000, 0x0000 }, /* R347 */
+ { 0x0000, 0x0000, 0x0000 }, /* R348 */
+ { 0x0000, 0x0000, 0x0000 }, /* R349 */
+ { 0x0000, 0x0000, 0x0000 }, /* R350 */
+ { 0x0000, 0x0000, 0x0000 }, /* R351 */
+ { 0x0000, 0x0000, 0x0000 }, /* R352 */
+ { 0x0000, 0x0000, 0x0000 }, /* R353 */
+ { 0x0000, 0x0000, 0x0000 }, /* R354 */
+ { 0x0000, 0x0000, 0x0000 }, /* R355 */
+ { 0x0000, 0x0000, 0x0000 }, /* R356 */
+ { 0x0000, 0x0000, 0x0000 }, /* R357 */
+ { 0x0000, 0x0000, 0x0000 }, /* R358 */
+ { 0x0000, 0x0000, 0x0000 }, /* R359 */
+ { 0x0000, 0x0000, 0x0000 }, /* R360 */
+ { 0x0000, 0x0000, 0x0000 }, /* R361 */
+ { 0x0000, 0x0000, 0x0000 }, /* R362 */
+ { 0x0000, 0x0000, 0x0000 }, /* R363 */
+ { 0x0000, 0x0000, 0x0000 }, /* R364 */
+ { 0x0000, 0x0000, 0x0000 }, /* R365 */
+ { 0x0000, 0x0000, 0x0000 }, /* R366 */
+ { 0x0000, 0x0000, 0x0000 }, /* R367 */
+ { 0x0000, 0x0000, 0x0000 }, /* R368 */
+ { 0x0000, 0x0000, 0x0000 }, /* R369 */
+ { 0x0000, 0x0000, 0x0000 }, /* R370 */
+ { 0x0000, 0x0000, 0x0000 }, /* R371 */
+ { 0x0000, 0x0000, 0x0000 }, /* R372 */
+ { 0x0000, 0x0000, 0x0000 }, /* R373 */
+ { 0x0000, 0x0000, 0x0000 }, /* R374 */
+ { 0x0000, 0x0000, 0x0000 }, /* R375 */
+ { 0x0000, 0x0000, 0x0000 }, /* R376 */
+ { 0x0000, 0x0000, 0x0000 }, /* R377 */
+ { 0x0000, 0x0000, 0x0000 }, /* R378 */
+ { 0x0000, 0x0000, 0x0000 }, /* R379 */
+ { 0x0000, 0x0000, 0x0000 }, /* R380 */
+ { 0x0000, 0x0000, 0x0000 }, /* R381 */
+ { 0x0000, 0x0000, 0x0000 }, /* R382 */
+ { 0x0000, 0x0000, 0x0000 }, /* R383 */
+ { 0x0000, 0x0000, 0x0000 }, /* R384 */
+ { 0x0000, 0x0000, 0x0000 }, /* R385 */
+ { 0x0000, 0x0000, 0x0000 }, /* R386 */
+ { 0x0000, 0x0000, 0x0000 }, /* R387 */
+ { 0x0000, 0x0000, 0x0000 }, /* R388 */
+ { 0x0000, 0x0000, 0x0000 }, /* R389 */
+ { 0x0000, 0x0000, 0x0000 }, /* R390 */
+ { 0x0000, 0x0000, 0x0000 }, /* R391 */
+ { 0x0000, 0x0000, 0x0000 }, /* R392 */
+ { 0x0000, 0x0000, 0x0000 }, /* R393 */
+ { 0x0000, 0x0000, 0x0000 }, /* R394 */
+ { 0x0000, 0x0000, 0x0000 }, /* R395 */
+ { 0x0000, 0x0000, 0x0000 }, /* R396 */
+ { 0x0000, 0x0000, 0x0000 }, /* R397 */
+ { 0x0000, 0x0000, 0x0000 }, /* R398 */
+ { 0x0000, 0x0000, 0x0000 }, /* R399 */
+ { 0x0000, 0x0000, 0x0000 }, /* R400 */
+ { 0x0000, 0x0000, 0x0000 }, /* R401 */
+ { 0x0000, 0x0000, 0x0000 }, /* R402 */
+ { 0x0000, 0x0000, 0x0000 }, /* R403 */
+ { 0x0000, 0x0000, 0x0000 }, /* R404 */
+ { 0x0000, 0x0000, 0x0000 }, /* R405 */
+ { 0x0000, 0x0000, 0x0000 }, /* R406 */
+ { 0x0000, 0x0000, 0x0000 }, /* R407 */
+ { 0x0000, 0x0000, 0x0000 }, /* R408 */
+ { 0x0000, 0x0000, 0x0000 }, /* R409 */
+ { 0x0000, 0x0000, 0x0000 }, /* R410 */
+ { 0x0000, 0x0000, 0x0000 }, /* R411 */
+ { 0x0000, 0x0000, 0x0000 }, /* R412 */
+ { 0x0000, 0x0000, 0x0000 }, /* R413 */
+ { 0x0000, 0x0000, 0x0000 }, /* R414 */
+ { 0x0000, 0x0000, 0x0000 }, /* R415 */
+ { 0x0000, 0x0000, 0x0000 }, /* R416 */
+ { 0x0000, 0x0000, 0x0000 }, /* R417 */
+ { 0x0000, 0x0000, 0x0000 }, /* R418 */
+ { 0x0000, 0x0000, 0x0000 }, /* R419 */
+ { 0x0000, 0x0000, 0x0000 }, /* R420 */
+ { 0x0000, 0x0000, 0x0000 }, /* R421 */
+ { 0x0000, 0x0000, 0x0000 }, /* R422 */
+ { 0x0000, 0x0000, 0x0000 }, /* R423 */
+ { 0x0000, 0x0000, 0x0000 }, /* R424 */
+ { 0x0000, 0x0000, 0x0000 }, /* R425 */
+ { 0x0000, 0x0000, 0x0000 }, /* R426 */
+ { 0x0000, 0x0000, 0x0000 }, /* R427 */
+ { 0x0000, 0x0000, 0x0000 }, /* R428 */
+ { 0x0000, 0x0000, 0x0000 }, /* R429 */
+ { 0x0000, 0x0000, 0x0000 }, /* R430 */
+ { 0x0000, 0x0000, 0x0000 }, /* R431 */
+ { 0x0000, 0x0000, 0x0000 }, /* R432 */
+ { 0x0000, 0x0000, 0x0000 }, /* R433 */
+ { 0x0000, 0x0000, 0x0000 }, /* R434 */
+ { 0x0000, 0x0000, 0x0000 }, /* R435 */
+ { 0x0000, 0x0000, 0x0000 }, /* R436 */
+ { 0x0000, 0x0000, 0x0000 }, /* R437 */
+ { 0x0000, 0x0000, 0x0000 }, /* R438 */
+ { 0x0000, 0x0000, 0x0000 }, /* R439 */
+ { 0x0000, 0x0000, 0x0000 }, /* R440 */
+ { 0x0000, 0x0000, 0x0000 }, /* R441 */
+ { 0x0000, 0x0000, 0x0000 }, /* R442 */
+ { 0x0000, 0x0000, 0x0000 }, /* R443 */
+ { 0x0000, 0x0000, 0x0000 }, /* R444 */
+ { 0x0000, 0x0000, 0x0000 }, /* R445 */
+ { 0x0000, 0x0000, 0x0000 }, /* R446 */
+ { 0x0000, 0x0000, 0x0000 }, /* R447 */
+ { 0x0000, 0x0000, 0x0000 }, /* R448 */
+ { 0x0000, 0x0000, 0x0000 }, /* R449 */
+ { 0x0000, 0x0000, 0x0000 }, /* R450 */
+ { 0x0000, 0x0000, 0x0000 }, /* R451 */
+ { 0x0000, 0x0000, 0x0000 }, /* R452 */
+ { 0x0000, 0x0000, 0x0000 }, /* R453 */
+ { 0x0000, 0x0000, 0x0000 }, /* R454 */
+ { 0x0000, 0x0000, 0x0000 }, /* R455 */
+ { 0x0000, 0x0000, 0x0000 }, /* R456 */
+ { 0x0000, 0x0000, 0x0000 }, /* R457 */
+ { 0x0000, 0x0000, 0x0000 }, /* R458 */
+ { 0x0000, 0x0000, 0x0000 }, /* R459 */
+ { 0x0000, 0x0000, 0x0000 }, /* R460 */
+ { 0x0000, 0x0000, 0x0000 }, /* R461 */
+ { 0x0000, 0x0000, 0x0000 }, /* R462 */
+ { 0x0000, 0x0000, 0x0000 }, /* R463 */
+ { 0x0000, 0x0000, 0x0000 }, /* R464 */
+ { 0x0000, 0x0000, 0x0000 }, /* R465 */
+ { 0x0000, 0x0000, 0x0000 }, /* R466 */
+ { 0x0000, 0x0000, 0x0000 }, /* R467 */
+ { 0x0000, 0x0000, 0x0000 }, /* R468 */
+ { 0x0000, 0x0000, 0x0000 }, /* R469 */
+ { 0x0000, 0x0000, 0x0000 }, /* R470 */
+ { 0x0000, 0x0000, 0x0000 }, /* R471 */
+ { 0x0000, 0x0000, 0x0000 }, /* R472 */
+ { 0x0000, 0x0000, 0x0000 }, /* R473 */
+ { 0x0000, 0x0000, 0x0000 }, /* R474 */
+ { 0x0000, 0x0000, 0x0000 }, /* R475 */
+ { 0x0000, 0x0000, 0x0000 }, /* R476 */
+ { 0x0000, 0x0000, 0x0000 }, /* R477 */
+ { 0x0000, 0x0000, 0x0000 }, /* R478 */
+ { 0x0000, 0x0000, 0x0000 }, /* R479 */
+ { 0x0000, 0x0000, 0x0000 }, /* R480 */
+ { 0x0000, 0x0000, 0x0000 }, /* R481 */
+ { 0x0000, 0x0000, 0x0000 }, /* R482 */
+ { 0x0000, 0x0000, 0x0000 }, /* R483 */
+ { 0x0000, 0x0000, 0x0000 }, /* R484 */
+ { 0x0000, 0x0000, 0x0000 }, /* R485 */
+ { 0x0000, 0x0000, 0x0000 }, /* R486 */
+ { 0x0000, 0x0000, 0x0000 }, /* R487 */
+ { 0x0000, 0x0000, 0x0000 }, /* R488 */
+ { 0x0000, 0x0000, 0x0000 }, /* R489 */
+ { 0x0000, 0x0000, 0x0000 }, /* R490 */
+ { 0x0000, 0x0000, 0x0000 }, /* R491 */
+ { 0x0000, 0x0000, 0x0000 }, /* R492 */
+ { 0x0000, 0x0000, 0x0000 }, /* R493 */
+ { 0x0000, 0x0000, 0x0000 }, /* R494 */
+ { 0x0000, 0x0000, 0x0000 }, /* R495 */
+ { 0x0000, 0x0000, 0x0000 }, /* R496 */
+ { 0x0000, 0x0000, 0x0000 }, /* R497 */
+ { 0x0000, 0x0000, 0x0000 }, /* R498 */
+ { 0x0000, 0x0000, 0x0000 }, /* R499 */
+ { 0x0000, 0x0000, 0x0000 }, /* R500 */
+ { 0x0000, 0x0000, 0x0000 }, /* R501 */
+ { 0x0000, 0x0000, 0x0000 }, /* R502 */
+ { 0x0000, 0x0000, 0x0000 }, /* R503 */
+ { 0x0000, 0x0000, 0x0000 }, /* R504 */
+ { 0x0000, 0x0000, 0x0000 }, /* R505 */
+ { 0x0000, 0x0000, 0x0000 }, /* R506 */
+ { 0x0000, 0x0000, 0x0000 }, /* R507 */
+ { 0x0000, 0x0000, 0x0000 }, /* R508 */
+ { 0x0000, 0x0000, 0x0000 }, /* R509 */
+ { 0x0000, 0x0000, 0x0000 }, /* R510 */
+ { 0x0000, 0x0000, 0x0000 }, /* R511 */
+ { 0x001F, 0x001F, 0x0000 }, /* R512 - AIF1 Clocking (1) */
+ { 0x003F, 0x003F, 0x0000 }, /* R513 - AIF1 Clocking (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R514 */
+ { 0x0000, 0x0000, 0x0000 }, /* R515 */
+ { 0x001F, 0x001F, 0x0000 }, /* R516 - AIF2 Clocking (1) */
+ { 0x003F, 0x003F, 0x0000 }, /* R517 - AIF2 Clocking (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R518 */
+ { 0x0000, 0x0000, 0x0000 }, /* R519 */
+ { 0x001F, 0x001F, 0x0000 }, /* R520 - Clocking (1) */
+ { 0x0777, 0x0777, 0x0000 }, /* R521 - Clocking (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R522 */
+ { 0x0000, 0x0000, 0x0000 }, /* R523 */
+ { 0x0000, 0x0000, 0x0000 }, /* R524 */
+ { 0x0000, 0x0000, 0x0000 }, /* R525 */
+ { 0x0000, 0x0000, 0x0000 }, /* R526 */
+ { 0x0000, 0x0000, 0x0000 }, /* R527 */
+ { 0x00FF, 0x00FF, 0x0000 }, /* R528 - AIF1 Rate */
+ { 0x00FF, 0x00FF, 0x0000 }, /* R529 - AIF2 Rate */
+ { 0x000F, 0x0000, 0x0000 }, /* R530 - Rate Status */
+ { 0x0000, 0x0000, 0x0000 }, /* R531 */
+ { 0x0000, 0x0000, 0x0000 }, /* R532 */
+ { 0x0000, 0x0000, 0x0000 }, /* R533 */
+ { 0x0000, 0x0000, 0x0000 }, /* R534 */
+ { 0x0000, 0x0000, 0x0000 }, /* R535 */
+ { 0x0000, 0x0000, 0x0000 }, /* R536 */
+ { 0x0000, 0x0000, 0x0000 }, /* R537 */
+ { 0x0000, 0x0000, 0x0000 }, /* R538 */
+ { 0x0000, 0x0000, 0x0000 }, /* R539 */
+ { 0x0000, 0x0000, 0x0000 }, /* R540 */
+ { 0x0000, 0x0000, 0x0000 }, /* R541 */
+ { 0x0000, 0x0000, 0x0000 }, /* R542 */
+ { 0x0000, 0x0000, 0x0000 }, /* R543 */
+ { 0x0007, 0x0007, 0x0000 }, /* R544 - FLL1 Control (1) */
+ { 0x3F77, 0x3F77, 0x0000 }, /* R545 - FLL1 Control (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R546 - FLL1 Control (3) */
+ { 0x7FEF, 0x7FEF, 0x0000 }, /* R547 - FLL1 Control (4) */
+ { 0x1FDB, 0x1FDB, 0x0000 }, /* R548 - FLL1 Control (5) */
+ { 0x0000, 0x0000, 0x0000 }, /* R549 */
+ { 0x0000, 0x0000, 0x0000 }, /* R550 */
+ { 0x0000, 0x0000, 0x0000 }, /* R551 */
+ { 0x0000, 0x0000, 0x0000 }, /* R552 */
+ { 0x0000, 0x0000, 0x0000 }, /* R553 */
+ { 0x0000, 0x0000, 0x0000 }, /* R554 */
+ { 0x0000, 0x0000, 0x0000 }, /* R555 */
+ { 0x0000, 0x0000, 0x0000 }, /* R556 */
+ { 0x0000, 0x0000, 0x0000 }, /* R557 */
+ { 0x0000, 0x0000, 0x0000 }, /* R558 */
+ { 0x0000, 0x0000, 0x0000 }, /* R559 */
+ { 0x0000, 0x0000, 0x0000 }, /* R560 */
+ { 0x0000, 0x0000, 0x0000 }, /* R561 */
+ { 0x0000, 0x0000, 0x0000 }, /* R562 */
+ { 0x0000, 0x0000, 0x0000 }, /* R563 */
+ { 0x0000, 0x0000, 0x0000 }, /* R564 */
+ { 0x0000, 0x0000, 0x0000 }, /* R565 */
+ { 0x0000, 0x0000, 0x0000 }, /* R566 */
+ { 0x0000, 0x0000, 0x0000 }, /* R567 */
+ { 0x0000, 0x0000, 0x0000 }, /* R568 */
+ { 0x0000, 0x0000, 0x0000 }, /* R569 */
+ { 0x0000, 0x0000, 0x0000 }, /* R570 */
+ { 0x0000, 0x0000, 0x0000 }, /* R571 */
+ { 0x0000, 0x0000, 0x0000 }, /* R572 */
+ { 0x0000, 0x0000, 0x0000 }, /* R573 */
+ { 0x0000, 0x0000, 0x0000 }, /* R574 */
+ { 0x0000, 0x0000, 0x0000 }, /* R575 */
+ { 0x0007, 0x0007, 0x0000 }, /* R576 - FLL2 Control (1) */
+ { 0x3F77, 0x3F77, 0x0000 }, /* R577 - FLL2 Control (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R578 - FLL2 Control (3) */
+ { 0x7FEF, 0x7FEF, 0x0000 }, /* R579 - FLL2 Control (4) */
+ { 0x1FDB, 0x1FDB, 0x0000 }, /* R580 - FLL2 Control (5) */
+ { 0x0000, 0x0000, 0x0000 }, /* R581 */
+ { 0x0000, 0x0000, 0x0000 }, /* R582 */
+ { 0x0000, 0x0000, 0x0000 }, /* R583 */
+ { 0x0000, 0x0000, 0x0000 }, /* R584 */
+ { 0x0000, 0x0000, 0x0000 }, /* R585 */
+ { 0x0000, 0x0000, 0x0000 }, /* R586 */
+ { 0x0000, 0x0000, 0x0000 }, /* R587 */
+ { 0x0000, 0x0000, 0x0000 }, /* R588 */
+ { 0x0000, 0x0000, 0x0000 }, /* R589 */
+ { 0x0000, 0x0000, 0x0000 }, /* R590 */
+ { 0x0000, 0x0000, 0x0000 }, /* R591 */
+ { 0x0000, 0x0000, 0x0000 }, /* R592 */
+ { 0x0000, 0x0000, 0x0000 }, /* R593 */
+ { 0x0000, 0x0000, 0x0000 }, /* R594 */
+ { 0x0000, 0x0000, 0x0000 }, /* R595 */
+ { 0x0000, 0x0000, 0x0000 }, /* R596 */
+ { 0x0000, 0x0000, 0x0000 }, /* R597 */
+ { 0x0000, 0x0000, 0x0000 }, /* R598 */
+ { 0x0000, 0x0000, 0x0000 }, /* R599 */
+ { 0x0000, 0x0000, 0x0000 }, /* R600 */
+ { 0x0000, 0x0000, 0x0000 }, /* R601 */
+ { 0x0000, 0x0000, 0x0000 }, /* R602 */
+ { 0x0000, 0x0000, 0x0000 }, /* R603 */
+ { 0x0000, 0x0000, 0x0000 }, /* R604 */
+ { 0x0000, 0x0000, 0x0000 }, /* R605 */
+ { 0x0000, 0x0000, 0x0000 }, /* R606 */
+ { 0x0000, 0x0000, 0x0000 }, /* R607 */
+ { 0x0000, 0x0000, 0x0000 }, /* R608 */
+ { 0x0000, 0x0000, 0x0000 }, /* R609 */
+ { 0x0000, 0x0000, 0x0000 }, /* R610 */
+ { 0x0000, 0x0000, 0x0000 }, /* R611 */
+ { 0x0000, 0x0000, 0x0000 }, /* R612 */
+ { 0x0000, 0x0000, 0x0000 }, /* R613 */
+ { 0x0000, 0x0000, 0x0000 }, /* R614 */
+ { 0x0000, 0x0000, 0x0000 }, /* R615 */
+ { 0x0000, 0x0000, 0x0000 }, /* R616 */
+ { 0x0000, 0x0000, 0x0000 }, /* R617 */
+ { 0x0000, 0x0000, 0x0000 }, /* R618 */
+ { 0x0000, 0x0000, 0x0000 }, /* R619 */
+ { 0x0000, 0x0000, 0x0000 }, /* R620 */
+ { 0x0000, 0x0000, 0x0000 }, /* R621 */
+ { 0x0000, 0x0000, 0x0000 }, /* R622 */
+ { 0x0000, 0x0000, 0x0000 }, /* R623 */
+ { 0x0000, 0x0000, 0x0000 }, /* R624 */
+ { 0x0000, 0x0000, 0x0000 }, /* R625 */
+ { 0x0000, 0x0000, 0x0000 }, /* R626 */
+ { 0x0000, 0x0000, 0x0000 }, /* R627 */
+ { 0x0000, 0x0000, 0x0000 }, /* R628 */
+ { 0x0000, 0x0000, 0x0000 }, /* R629 */
+ { 0x0000, 0x0000, 0x0000 }, /* R630 */
+ { 0x0000, 0x0000, 0x0000 }, /* R631 */
+ { 0x0000, 0x0000, 0x0000 }, /* R632 */
+ { 0x0000, 0x0000, 0x0000 }, /* R633 */
+ { 0x0000, 0x0000, 0x0000 }, /* R634 */
+ { 0x0000, 0x0000, 0x0000 }, /* R635 */
+ { 0x0000, 0x0000, 0x0000 }, /* R636 */
+ { 0x0000, 0x0000, 0x0000 }, /* R637 */
+ { 0x0000, 0x0000, 0x0000 }, /* R638 */
+ { 0x0000, 0x0000, 0x0000 }, /* R639 */
+ { 0x0000, 0x0000, 0x0000 }, /* R640 */
+ { 0x0000, 0x0000, 0x0000 }, /* R641 */
+ { 0x0000, 0x0000, 0x0000 }, /* R642 */
+ { 0x0000, 0x0000, 0x0000 }, /* R643 */
+ { 0x0000, 0x0000, 0x0000 }, /* R644 */
+ { 0x0000, 0x0000, 0x0000 }, /* R645 */
+ { 0x0000, 0x0000, 0x0000 }, /* R646 */
+ { 0x0000, 0x0000, 0x0000 }, /* R647 */
+ { 0x0000, 0x0000, 0x0000 }, /* R648 */
+ { 0x0000, 0x0000, 0x0000 }, /* R649 */
+ { 0x0000, 0x0000, 0x0000 }, /* R650 */
+ { 0x0000, 0x0000, 0x0000 }, /* R651 */
+ { 0x0000, 0x0000, 0x0000 }, /* R652 */
+ { 0x0000, 0x0000, 0x0000 }, /* R653 */
+ { 0x0000, 0x0000, 0x0000 }, /* R654 */
+ { 0x0000, 0x0000, 0x0000 }, /* R655 */
+ { 0x0000, 0x0000, 0x0000 }, /* R656 */
+ { 0x0000, 0x0000, 0x0000 }, /* R657 */
+ { 0x0000, 0x0000, 0x0000 }, /* R658 */
+ { 0x0000, 0x0000, 0x0000 }, /* R659 */
+ { 0x0000, 0x0000, 0x0000 }, /* R660 */
+ { 0x0000, 0x0000, 0x0000 }, /* R661 */
+ { 0x0000, 0x0000, 0x0000 }, /* R662 */
+ { 0x0000, 0x0000, 0x0000 }, /* R663 */
+ { 0x0000, 0x0000, 0x0000 }, /* R664 */
+ { 0x0000, 0x0000, 0x0000 }, /* R665 */
+ { 0x0000, 0x0000, 0x0000 }, /* R666 */
+ { 0x0000, 0x0000, 0x0000 }, /* R667 */
+ { 0x0000, 0x0000, 0x0000 }, /* R668 */
+ { 0x0000, 0x0000, 0x0000 }, /* R669 */
+ { 0x0000, 0x0000, 0x0000 }, /* R670 */
+ { 0x0000, 0x0000, 0x0000 }, /* R671 */
+ { 0x0000, 0x0000, 0x0000 }, /* R672 */
+ { 0x0000, 0x0000, 0x0000 }, /* R673 */
+ { 0x0000, 0x0000, 0x0000 }, /* R674 */
+ { 0x0000, 0x0000, 0x0000 }, /* R675 */
+ { 0x0000, 0x0000, 0x0000 }, /* R676 */
+ { 0x0000, 0x0000, 0x0000 }, /* R677 */
+ { 0x0000, 0x0000, 0x0000 }, /* R678 */
+ { 0x0000, 0x0000, 0x0000 }, /* R679 */
+ { 0x0000, 0x0000, 0x0000 }, /* R680 */
+ { 0x0000, 0x0000, 0x0000 }, /* R681 */
+ { 0x0000, 0x0000, 0x0000 }, /* R682 */
+ { 0x0000, 0x0000, 0x0000 }, /* R683 */
+ { 0x0000, 0x0000, 0x0000 }, /* R684 */
+ { 0x0000, 0x0000, 0x0000 }, /* R685 */
+ { 0x0000, 0x0000, 0x0000 }, /* R686 */
+ { 0x0000, 0x0000, 0x0000 }, /* R687 */
+ { 0x0000, 0x0000, 0x0000 }, /* R688 */
+ { 0x0000, 0x0000, 0x0000 }, /* R689 */
+ { 0x0000, 0x0000, 0x0000 }, /* R690 */
+ { 0x0000, 0x0000, 0x0000 }, /* R691 */
+ { 0x0000, 0x0000, 0x0000 }, /* R692 */
+ { 0x0000, 0x0000, 0x0000 }, /* R693 */
+ { 0x0000, 0x0000, 0x0000 }, /* R694 */
+ { 0x0000, 0x0000, 0x0000 }, /* R695 */
+ { 0x0000, 0x0000, 0x0000 }, /* R696 */
+ { 0x0000, 0x0000, 0x0000 }, /* R697 */
+ { 0x0000, 0x0000, 0x0000 }, /* R698 */
+ { 0x0000, 0x0000, 0x0000 }, /* R699 */
+ { 0x0000, 0x0000, 0x0000 }, /* R700 */
+ { 0x0000, 0x0000, 0x0000 }, /* R701 */
+ { 0x0000, 0x0000, 0x0000 }, /* R702 */
+ { 0x0000, 0x0000, 0x0000 }, /* R703 */
+ { 0x0000, 0x0000, 0x0000 }, /* R704 */
+ { 0x0000, 0x0000, 0x0000 }, /* R705 */
+ { 0x0000, 0x0000, 0x0000 }, /* R706 */
+ { 0x0000, 0x0000, 0x0000 }, /* R707 */
+ { 0x0000, 0x0000, 0x0000 }, /* R708 */
+ { 0x0000, 0x0000, 0x0000 }, /* R709 */
+ { 0x0000, 0x0000, 0x0000 }, /* R710 */
+ { 0x0000, 0x0000, 0x0000 }, /* R711 */
+ { 0x0000, 0x0000, 0x0000 }, /* R712 */
+ { 0x0000, 0x0000, 0x0000 }, /* R713 */
+ { 0x0000, 0x0000, 0x0000 }, /* R714 */
+ { 0x0000, 0x0000, 0x0000 }, /* R715 */
+ { 0x0000, 0x0000, 0x0000 }, /* R716 */
+ { 0x0000, 0x0000, 0x0000 }, /* R717 */
+ { 0x0000, 0x0000, 0x0000 }, /* R718 */
+ { 0x0000, 0x0000, 0x0000 }, /* R719 */
+ { 0x0000, 0x0000, 0x0000 }, /* R720 */
+ { 0x0000, 0x0000, 0x0000 }, /* R721 */
+ { 0x0000, 0x0000, 0x0000 }, /* R722 */
+ { 0x0000, 0x0000, 0x0000 }, /* R723 */
+ { 0x0000, 0x0000, 0x0000 }, /* R724 */
+ { 0x0000, 0x0000, 0x0000 }, /* R725 */
+ { 0x0000, 0x0000, 0x0000 }, /* R726 */
+ { 0x0000, 0x0000, 0x0000 }, /* R727 */
+ { 0x0000, 0x0000, 0x0000 }, /* R728 */
+ { 0x0000, 0x0000, 0x0000 }, /* R729 */
+ { 0x0000, 0x0000, 0x0000 }, /* R730 */
+ { 0x0000, 0x0000, 0x0000 }, /* R731 */
+ { 0x0000, 0x0000, 0x0000 }, /* R732 */
+ { 0x0000, 0x0000, 0x0000 }, /* R733 */
+ { 0x0000, 0x0000, 0x0000 }, /* R734 */
+ { 0x0000, 0x0000, 0x0000 }, /* R735 */
+ { 0x0000, 0x0000, 0x0000 }, /* R736 */
+ { 0x0000, 0x0000, 0x0000 }, /* R737 */
+ { 0x0000, 0x0000, 0x0000 }, /* R738 */
+ { 0x0000, 0x0000, 0x0000 }, /* R739 */
+ { 0x0000, 0x0000, 0x0000 }, /* R740 */
+ { 0x0000, 0x0000, 0x0000 }, /* R741 */
+ { 0x0000, 0x0000, 0x0000 }, /* R742 */
+ { 0x0000, 0x0000, 0x0000 }, /* R743 */
+ { 0x0000, 0x0000, 0x0000 }, /* R744 */
+ { 0x0000, 0x0000, 0x0000 }, /* R745 */
+ { 0x0000, 0x0000, 0x0000 }, /* R746 */
+ { 0x0000, 0x0000, 0x0000 }, /* R747 */
+ { 0x0000, 0x0000, 0x0000 }, /* R748 */
+ { 0x0000, 0x0000, 0x0000 }, /* R749 */
+ { 0x0000, 0x0000, 0x0000 }, /* R750 */
+ { 0x0000, 0x0000, 0x0000 }, /* R751 */
+ { 0x0000, 0x0000, 0x0000 }, /* R752 */
+ { 0x0000, 0x0000, 0x0000 }, /* R753 */
+ { 0x0000, 0x0000, 0x0000 }, /* R754 */
+ { 0x0000, 0x0000, 0x0000 }, /* R755 */
+ { 0x0000, 0x0000, 0x0000 }, /* R756 */
+ { 0x0000, 0x0000, 0x0000 }, /* R757 */
+ { 0x0000, 0x0000, 0x0000 }, /* R758 */
+ { 0x0000, 0x0000, 0x0000 }, /* R759 */
+ { 0x0000, 0x0000, 0x0000 }, /* R760 */
+ { 0x0000, 0x0000, 0x0000 }, /* R761 */
+ { 0x0000, 0x0000, 0x0000 }, /* R762 */
+ { 0x0000, 0x0000, 0x0000 }, /* R763 */
+ { 0x0000, 0x0000, 0x0000 }, /* R764 */
+ { 0x0000, 0x0000, 0x0000 }, /* R765 */
+ { 0x0000, 0x0000, 0x0000 }, /* R766 */
+ { 0x0000, 0x0000, 0x0000 }, /* R767 */
+ { 0xE1F8, 0xE1F8, 0x0000 }, /* R768 - AIF1 Control (1) */
+ { 0xCD1F, 0xCD1F, 0x0000 }, /* R769 - AIF1 Control (2) */
+ { 0xF000, 0xF000, 0x0000 }, /* R770 - AIF1 Master/Slave */
+ { 0x01F0, 0x01F0, 0x0000 }, /* R771 - AIF1 BCLK */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R772 - AIF1ADC LRCLK */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R773 - AIF1DAC LRCLK */
+ { 0x0003, 0x0003, 0x0000 }, /* R774 - AIF1DAC Data */
+ { 0x0003, 0x0003, 0x0000 }, /* R775 - AIF1ADC Data */
+ { 0x0000, 0x0000, 0x0000 }, /* R776 */
+ { 0x0000, 0x0000, 0x0000 }, /* R777 */
+ { 0x0000, 0x0000, 0x0000 }, /* R778 */
+ { 0x0000, 0x0000, 0x0000 }, /* R779 */
+ { 0x0000, 0x0000, 0x0000 }, /* R780 */
+ { 0x0000, 0x0000, 0x0000 }, /* R781 */
+ { 0x0000, 0x0000, 0x0000 }, /* R782 */
+ { 0x0000, 0x0000, 0x0000 }, /* R783 */
+ { 0xF1F8, 0xF1F8, 0x0000 }, /* R784 - AIF2 Control (1) */
+ { 0xFD1F, 0xFD1F, 0x0000 }, /* R785 - AIF2 Control (2) */
+ { 0xF000, 0xF000, 0x0000 }, /* R786 - AIF2 Master/Slave */
+ { 0x01F0, 0x01F0, 0x0000 }, /* R787 - AIF2 BCLK */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R788 - AIF2ADC LRCLK */
+ { 0x0FFF, 0x0FFF, 0x0000 }, /* R789 - AIF2DAC LRCLK */
+ { 0x0003, 0x0003, 0x0000 }, /* R790 - AIF2DAC Data */
+ { 0x0003, 0x0003, 0x0000 }, /* R791 - AIF2ADC Data */
+ { 0x0000, 0x0000, 0x0000 }, /* R792 */
+ { 0x0000, 0x0000, 0x0000 }, /* R793 */
+ { 0x0000, 0x0000, 0x0000 }, /* R794 */
+ { 0x0000, 0x0000, 0x0000 }, /* R795 */
+ { 0x0000, 0x0000, 0x0000 }, /* R796 */
+ { 0x0000, 0x0000, 0x0000 }, /* R797 */
+ { 0x0000, 0x0000, 0x0000 }, /* R798 */
+ { 0x0000, 0x0000, 0x0000 }, /* R799 */
+ { 0x0000, 0x0000, 0x0000 }, /* R800 */
+ { 0x0000, 0x0000, 0x0000 }, /* R801 */
+ { 0x0000, 0x0000, 0x0000 }, /* R802 */
+ { 0x0000, 0x0000, 0x0000 }, /* R803 */
+ { 0x0000, 0x0000, 0x0000 }, /* R804 */
+ { 0x0000, 0x0000, 0x0000 }, /* R805 */
+ { 0x0000, 0x0000, 0x0000 }, /* R806 */
+ { 0x0000, 0x0000, 0x0000 }, /* R807 */
+ { 0x0000, 0x0000, 0x0000 }, /* R808 */
+ { 0x0000, 0x0000, 0x0000 }, /* R809 */
+ { 0x0000, 0x0000, 0x0000 }, /* R810 */
+ { 0x0000, 0x0000, 0x0000 }, /* R811 */
+ { 0x0000, 0x0000, 0x0000 }, /* R812 */
+ { 0x0000, 0x0000, 0x0000 }, /* R813 */
+ { 0x0000, 0x0000, 0x0000 }, /* R814 */
+ { 0x0000, 0x0000, 0x0000 }, /* R815 */
+ { 0x0000, 0x0000, 0x0000 }, /* R816 */
+ { 0x0000, 0x0000, 0x0000 }, /* R817 */
+ { 0x0000, 0x0000, 0x0000 }, /* R818 */
+ { 0x0000, 0x0000, 0x0000 }, /* R819 */
+ { 0x0000, 0x0000, 0x0000 }, /* R820 */
+ { 0x0000, 0x0000, 0x0000 }, /* R821 */
+ { 0x0000, 0x0000, 0x0000 }, /* R822 */
+ { 0x0000, 0x0000, 0x0000 }, /* R823 */
+ { 0x0000, 0x0000, 0x0000 }, /* R824 */
+ { 0x0000, 0x0000, 0x0000 }, /* R825 */
+ { 0x0000, 0x0000, 0x0000 }, /* R826 */
+ { 0x0000, 0x0000, 0x0000 }, /* R827 */
+ { 0x0000, 0x0000, 0x0000 }, /* R828 */
+ { 0x0000, 0x0000, 0x0000 }, /* R829 */
+ { 0x0000, 0x0000, 0x0000 }, /* R830 */
+ { 0x0000, 0x0000, 0x0000 }, /* R831 */
+ { 0x0000, 0x0000, 0x0000 }, /* R832 */
+ { 0x0000, 0x0000, 0x0000 }, /* R833 */
+ { 0x0000, 0x0000, 0x0000 }, /* R834 */
+ { 0x0000, 0x0000, 0x0000 }, /* R835 */
+ { 0x0000, 0x0000, 0x0000 }, /* R836 */
+ { 0x0000, 0x0000, 0x0000 }, /* R837 */
+ { 0x0000, 0x0000, 0x0000 }, /* R838 */
+ { 0x0000, 0x0000, 0x0000 }, /* R839 */
+ { 0x0000, 0x0000, 0x0000 }, /* R840 */
+ { 0x0000, 0x0000, 0x0000 }, /* R841 */
+ { 0x0000, 0x0000, 0x0000 }, /* R842 */
+ { 0x0000, 0x0000, 0x0000 }, /* R843 */
+ { 0x0000, 0x0000, 0x0000 }, /* R844 */
+ { 0x0000, 0x0000, 0x0000 }, /* R845 */
+ { 0x0000, 0x0000, 0x0000 }, /* R846 */
+ { 0x0000, 0x0000, 0x0000 }, /* R847 */
+ { 0x0000, 0x0000, 0x0000 }, /* R848 */
+ { 0x0000, 0x0000, 0x0000 }, /* R849 */
+ { 0x0000, 0x0000, 0x0000 }, /* R850 */
+ { 0x0000, 0x0000, 0x0000 }, /* R851 */
+ { 0x0000, 0x0000, 0x0000 }, /* R852 */
+ { 0x0000, 0x0000, 0x0000 }, /* R853 */
+ { 0x0000, 0x0000, 0x0000 }, /* R854 */
+ { 0x0000, 0x0000, 0x0000 }, /* R855 */
+ { 0x0000, 0x0000, 0x0000 }, /* R856 */
+ { 0x0000, 0x0000, 0x0000 }, /* R857 */
+ { 0x0000, 0x0000, 0x0000 }, /* R858 */
+ { 0x0000, 0x0000, 0x0000 }, /* R859 */
+ { 0x0000, 0x0000, 0x0000 }, /* R860 */
+ { 0x0000, 0x0000, 0x0000 }, /* R861 */
+ { 0x0000, 0x0000, 0x0000 }, /* R862 */
+ { 0x0000, 0x0000, 0x0000 }, /* R863 */
+ { 0x0000, 0x0000, 0x0000 }, /* R864 */
+ { 0x0000, 0x0000, 0x0000 }, /* R865 */
+ { 0x0000, 0x0000, 0x0000 }, /* R866 */
+ { 0x0000, 0x0000, 0x0000 }, /* R867 */
+ { 0x0000, 0x0000, 0x0000 }, /* R868 */
+ { 0x0000, 0x0000, 0x0000 }, /* R869 */
+ { 0x0000, 0x0000, 0x0000 }, /* R870 */
+ { 0x0000, 0x0000, 0x0000 }, /* R871 */
+ { 0x0000, 0x0000, 0x0000 }, /* R872 */
+ { 0x0000, 0x0000, 0x0000 }, /* R873 */
+ { 0x0000, 0x0000, 0x0000 }, /* R874 */
+ { 0x0000, 0x0000, 0x0000 }, /* R875 */
+ { 0x0000, 0x0000, 0x0000 }, /* R876 */
+ { 0x0000, 0x0000, 0x0000 }, /* R877 */
+ { 0x0000, 0x0000, 0x0000 }, /* R878 */
+ { 0x0000, 0x0000, 0x0000 }, /* R879 */
+ { 0x0000, 0x0000, 0x0000 }, /* R880 */
+ { 0x0000, 0x0000, 0x0000 }, /* R881 */
+ { 0x0000, 0x0000, 0x0000 }, /* R882 */
+ { 0x0000, 0x0000, 0x0000 }, /* R883 */
+ { 0x0000, 0x0000, 0x0000 }, /* R884 */
+ { 0x0000, 0x0000, 0x0000 }, /* R885 */
+ { 0x0000, 0x0000, 0x0000 }, /* R886 */
+ { 0x0000, 0x0000, 0x0000 }, /* R887 */
+ { 0x0000, 0x0000, 0x0000 }, /* R888 */
+ { 0x0000, 0x0000, 0x0000 }, /* R889 */
+ { 0x0000, 0x0000, 0x0000 }, /* R890 */
+ { 0x0000, 0x0000, 0x0000 }, /* R891 */
+ { 0x0000, 0x0000, 0x0000 }, /* R892 */
+ { 0x0000, 0x0000, 0x0000 }, /* R893 */
+ { 0x0000, 0x0000, 0x0000 }, /* R894 */
+ { 0x0000, 0x0000, 0x0000 }, /* R895 */
+ { 0x0000, 0x0000, 0x0000 }, /* R896 */
+ { 0x0000, 0x0000, 0x0000 }, /* R897 */
+ { 0x0000, 0x0000, 0x0000 }, /* R898 */
+ { 0x0000, 0x0000, 0x0000 }, /* R899 */
+ { 0x0000, 0x0000, 0x0000 }, /* R900 */
+ { 0x0000, 0x0000, 0x0000 }, /* R901 */
+ { 0x0000, 0x0000, 0x0000 }, /* R902 */
+ { 0x0000, 0x0000, 0x0000 }, /* R903 */
+ { 0x0000, 0x0000, 0x0000 }, /* R904 */
+ { 0x0000, 0x0000, 0x0000 }, /* R905 */
+ { 0x0000, 0x0000, 0x0000 }, /* R906 */
+ { 0x0000, 0x0000, 0x0000 }, /* R907 */
+ { 0x0000, 0x0000, 0x0000 }, /* R908 */
+ { 0x0000, 0x0000, 0x0000 }, /* R909 */
+ { 0x0000, 0x0000, 0x0000 }, /* R910 */
+ { 0x0000, 0x0000, 0x0000 }, /* R911 */
+ { 0x0000, 0x0000, 0x0000 }, /* R912 */
+ { 0x0000, 0x0000, 0x0000 }, /* R913 */
+ { 0x0000, 0x0000, 0x0000 }, /* R914 */
+ { 0x0000, 0x0000, 0x0000 }, /* R915 */
+ { 0x0000, 0x0000, 0x0000 }, /* R916 */
+ { 0x0000, 0x0000, 0x0000 }, /* R917 */
+ { 0x0000, 0x0000, 0x0000 }, /* R918 */
+ { 0x0000, 0x0000, 0x0000 }, /* R919 */
+ { 0x0000, 0x0000, 0x0000 }, /* R920 */
+ { 0x0000, 0x0000, 0x0000 }, /* R921 */
+ { 0x0000, 0x0000, 0x0000 }, /* R922 */
+ { 0x0000, 0x0000, 0x0000 }, /* R923 */
+ { 0x0000, 0x0000, 0x0000 }, /* R924 */
+ { 0x0000, 0x0000, 0x0000 }, /* R925 */
+ { 0x0000, 0x0000, 0x0000 }, /* R926 */
+ { 0x0000, 0x0000, 0x0000 }, /* R927 */
+ { 0x0000, 0x0000, 0x0000 }, /* R928 */
+ { 0x0000, 0x0000, 0x0000 }, /* R929 */
+ { 0x0000, 0x0000, 0x0000 }, /* R930 */
+ { 0x0000, 0x0000, 0x0000 }, /* R931 */
+ { 0x0000, 0x0000, 0x0000 }, /* R932 */
+ { 0x0000, 0x0000, 0x0000 }, /* R933 */
+ { 0x0000, 0x0000, 0x0000 }, /* R934 */
+ { 0x0000, 0x0000, 0x0000 }, /* R935 */
+ { 0x0000, 0x0000, 0x0000 }, /* R936 */
+ { 0x0000, 0x0000, 0x0000 }, /* R937 */
+ { 0x0000, 0x0000, 0x0000 }, /* R938 */
+ { 0x0000, 0x0000, 0x0000 }, /* R939 */
+ { 0x0000, 0x0000, 0x0000 }, /* R940 */
+ { 0x0000, 0x0000, 0x0000 }, /* R941 */
+ { 0x0000, 0x0000, 0x0000 }, /* R942 */
+ { 0x0000, 0x0000, 0x0000 }, /* R943 */
+ { 0x0000, 0x0000, 0x0000 }, /* R944 */
+ { 0x0000, 0x0000, 0x0000 }, /* R945 */
+ { 0x0000, 0x0000, 0x0000 }, /* R946 */
+ { 0x0000, 0x0000, 0x0000 }, /* R947 */
+ { 0x0000, 0x0000, 0x0000 }, /* R948 */
+ { 0x0000, 0x0000, 0x0000 }, /* R949 */
+ { 0x0000, 0x0000, 0x0000 }, /* R950 */
+ { 0x0000, 0x0000, 0x0000 }, /* R951 */
+ { 0x0000, 0x0000, 0x0000 }, /* R952 */
+ { 0x0000, 0x0000, 0x0000 }, /* R953 */
+ { 0x0000, 0x0000, 0x0000 }, /* R954 */
+ { 0x0000, 0x0000, 0x0000 }, /* R955 */
+ { 0x0000, 0x0000, 0x0000 }, /* R956 */
+ { 0x0000, 0x0000, 0x0000 }, /* R957 */
+ { 0x0000, 0x0000, 0x0000 }, /* R958 */
+ { 0x0000, 0x0000, 0x0000 }, /* R959 */
+ { 0x0000, 0x0000, 0x0000 }, /* R960 */
+ { 0x0000, 0x0000, 0x0000 }, /* R961 */
+ { 0x0000, 0x0000, 0x0000 }, /* R962 */
+ { 0x0000, 0x0000, 0x0000 }, /* R963 */
+ { 0x0000, 0x0000, 0x0000 }, /* R964 */
+ { 0x0000, 0x0000, 0x0000 }, /* R965 */
+ { 0x0000, 0x0000, 0x0000 }, /* R966 */
+ { 0x0000, 0x0000, 0x0000 }, /* R967 */
+ { 0x0000, 0x0000, 0x0000 }, /* R968 */
+ { 0x0000, 0x0000, 0x0000 }, /* R969 */
+ { 0x0000, 0x0000, 0x0000 }, /* R970 */
+ { 0x0000, 0x0000, 0x0000 }, /* R971 */
+ { 0x0000, 0x0000, 0x0000 }, /* R972 */
+ { 0x0000, 0x0000, 0x0000 }, /* R973 */
+ { 0x0000, 0x0000, 0x0000 }, /* R974 */
+ { 0x0000, 0x0000, 0x0000 }, /* R975 */
+ { 0x0000, 0x0000, 0x0000 }, /* R976 */
+ { 0x0000, 0x0000, 0x0000 }, /* R977 */
+ { 0x0000, 0x0000, 0x0000 }, /* R978 */
+ { 0x0000, 0x0000, 0x0000 }, /* R979 */
+ { 0x0000, 0x0000, 0x0000 }, /* R980 */
+ { 0x0000, 0x0000, 0x0000 }, /* R981 */
+ { 0x0000, 0x0000, 0x0000 }, /* R982 */
+ { 0x0000, 0x0000, 0x0000 }, /* R983 */
+ { 0x0000, 0x0000, 0x0000 }, /* R984 */
+ { 0x0000, 0x0000, 0x0000 }, /* R985 */
+ { 0x0000, 0x0000, 0x0000 }, /* R986 */
+ { 0x0000, 0x0000, 0x0000 }, /* R987 */
+ { 0x0000, 0x0000, 0x0000 }, /* R988 */
+ { 0x0000, 0x0000, 0x0000 }, /* R989 */
+ { 0x0000, 0x0000, 0x0000 }, /* R990 */
+ { 0x0000, 0x0000, 0x0000 }, /* R991 */
+ { 0x0000, 0x0000, 0x0000 }, /* R992 */
+ { 0x0000, 0x0000, 0x0000 }, /* R993 */
+ { 0x0000, 0x0000, 0x0000 }, /* R994 */
+ { 0x0000, 0x0000, 0x0000 }, /* R995 */
+ { 0x0000, 0x0000, 0x0000 }, /* R996 */
+ { 0x0000, 0x0000, 0x0000 }, /* R997 */
+ { 0x0000, 0x0000, 0x0000 }, /* R998 */
+ { 0x0000, 0x0000, 0x0000 }, /* R999 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1000 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1001 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1002 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1003 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1004 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1005 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1006 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1007 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1008 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1009 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1010 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1011 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1012 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1013 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1014 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1015 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1016 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1017 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1018 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1019 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1020 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1021 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1022 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1023 */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1024 - AIF1 ADC1 Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1025 - AIF1 ADC1 Right Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1026 - AIF1 DAC1 Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1027 - AIF1 DAC1 Right Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1028 - AIF1 ADC2 Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1029 - AIF1 ADC2 Right Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1030 - AIF1 DAC2 Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1031 - AIF1 DAC2 Right Volume */
+ { 0x0000, 0x0000, 0x0000 }, /* R1032 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1033 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1034 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1035 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1036 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1037 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1038 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1039 */
+ { 0xF800, 0xF800, 0x0000 }, /* R1040 - AIF1 ADC1 Filters */
+ { 0x7800, 0x7800, 0x0000 }, /* R1041 - AIF1 ADC2 Filters */
+ { 0x0000, 0x0000, 0x0000 }, /* R1042 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1043 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1044 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1045 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1046 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1047 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1048 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1049 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1050 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1051 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1052 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1053 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1054 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1055 */
+ { 0x02B6, 0x02B6, 0x0000 }, /* R1056 - AIF1 DAC1 Filters (1) */
+ { 0x3F00, 0x3F00, 0x0000 }, /* R1057 - AIF1 DAC1 Filters (2) */
+ { 0x02B6, 0x02B6, 0x0000 }, /* R1058 - AIF1 DAC2 Filters (1) */
+ { 0x3F00, 0x3F00, 0x0000 }, /* R1059 - AIF1 DAC2 Filters (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R1060 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1061 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1062 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1063 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1064 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1065 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1066 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1067 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1068 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1069 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1070 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1071 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1072 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1073 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1074 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1075 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1076 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1077 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1078 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1079 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1080 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1081 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1082 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1083 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1084 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1085 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1086 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1087 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1088 - AIF1 DRC1 (1) */
+ { 0x1FFF, 0x1FFF, 0x0000 }, /* R1089 - AIF1 DRC1 (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1090 - AIF1 DRC1 (3) */
+ { 0x07FF, 0x07FF, 0x0000 }, /* R1091 - AIF1 DRC1 (4) */
+ { 0x03FF, 0x03FF, 0x0000 }, /* R1092 - AIF1 DRC1 (5) */
+ { 0x0000, 0x0000, 0x0000 }, /* R1093 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1094 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1095 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1096 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1097 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1098 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1099 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1100 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1101 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1102 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1103 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1104 - AIF1 DRC2 (1) */
+ { 0x1FFF, 0x1FFF, 0x0000 }, /* R1105 - AIF1 DRC2 (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1106 - AIF1 DRC2 (3) */
+ { 0x07FF, 0x07FF, 0x0000 }, /* R1107 - AIF1 DRC2 (4) */
+ { 0x03FF, 0x03FF, 0x0000 }, /* R1108 - AIF1 DRC2 (5) */
+ { 0x0000, 0x0000, 0x0000 }, /* R1109 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1110 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1111 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1112 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1113 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1114 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1115 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1116 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1117 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1118 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1119 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1120 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1121 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1122 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1123 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1124 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1125 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1126 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1127 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1128 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1129 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1130 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1131 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1132 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1133 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1134 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1135 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1136 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1137 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1138 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1139 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1140 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1141 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1142 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1143 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1144 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1145 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1146 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1147 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1148 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1149 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1150 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1151 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1152 - AIF1 DAC1 EQ Gains (1) */
+ { 0xFFC0, 0xFFC0, 0x0000 }, /* R1153 - AIF1 DAC1 EQ Gains (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1154 - AIF1 DAC1 EQ Band 1 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1155 - AIF1 DAC1 EQ Band 1 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1156 - AIF1 DAC1 EQ Band 1 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1157 - AIF1 DAC1 EQ Band 2 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1158 - AIF1 DAC1 EQ Band 2 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1159 - AIF1 DAC1 EQ Band 2 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1160 - AIF1 DAC1 EQ Band 2 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1161 - AIF1 DAC1 EQ Band 3 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1162 - AIF1 DAC1 EQ Band 3 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1163 - AIF1 DAC1 EQ Band 3 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1164 - AIF1 DAC1 EQ Band 3 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1165 - AIF1 DAC1 EQ Band 4 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1166 - AIF1 DAC1 EQ Band 4 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1167 - AIF1 DAC1 EQ Band 4 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1168 - AIF1 DAC1 EQ Band 4 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1169 - AIF1 DAC1 EQ Band 5 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1170 - AIF1 DAC1 EQ Band 5 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1171 - AIF1 DAC1 EQ Band 5 PG */
+ { 0x0000, 0x0000, 0x0000 }, /* R1172 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1173 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1174 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1175 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1176 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1177 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1178 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1179 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1180 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1181 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1182 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1183 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1184 - AIF1 DAC2 EQ Gains (1) */
+ { 0xFFC0, 0xFFC0, 0x0000 }, /* R1185 - AIF1 DAC2 EQ Gains (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1186 - AIF1 DAC2 EQ Band 1 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1187 - AIF1 DAC2 EQ Band 1 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1188 - AIF1 DAC2 EQ Band 1 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1189 - AIF1 DAC2 EQ Band 2 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1190 - AIF1 DAC2 EQ Band 2 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1191 - AIF1 DAC2 EQ Band 2 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1192 - AIF1 DAC2 EQ Band 2 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1193 - AIF1 DAC2 EQ Band 3 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1194 - AIF1 DAC2 EQ Band 3 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1195 - AIF1 DAC2 EQ Band 3 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1196 - AIF1 DAC2 EQ Band 3 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1197 - AIF1 DAC2 EQ Band 4 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1198 - AIF1 DAC2 EQ Band 4 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1199 - AIF1 DAC2 EQ Band 4 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1200 - AIF1 DAC2 EQ Band 4 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1201 - AIF1 DAC2 EQ Band 5 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1202 - AIF1 DAC2 EQ Band 5 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1203 - AIF1 DAC2 EQ Band 5 PG */
+ { 0x0000, 0x0000, 0x0000 }, /* R1204 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1205 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1206 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1207 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1208 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1209 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1210 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1211 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1212 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1213 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1214 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1215 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1216 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1217 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1218 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1219 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1220 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1221 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1222 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1223 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1224 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1225 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1226 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1227 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1228 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1229 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1230 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1231 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1232 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1233 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1234 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1235 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1236 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1237 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1238 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1239 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1240 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1241 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1242 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1243 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1244 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1245 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1246 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1247 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1248 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1249 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1250 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1251 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1252 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1253 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1254 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1255 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1256 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1257 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1258 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1259 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1260 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1261 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1262 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1263 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1264 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1265 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1266 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1267 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1268 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1269 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1270 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1271 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1272 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1273 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1274 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1275 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1276 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1277 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1278 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1279 */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1280 - AIF2 ADC Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1281 - AIF2 ADC Right Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1282 - AIF2 DAC Left Volume */
+ { 0x00FF, 0x01FF, 0x0000 }, /* R1283 - AIF2 DAC Right Volume */
+ { 0x0000, 0x0000, 0x0000 }, /* R1284 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1285 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1286 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1287 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1288 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1289 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1290 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1291 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1292 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1293 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1294 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1295 */
+ { 0xF800, 0xF800, 0x0000 }, /* R1296 - AIF2 ADC Filters */
+ { 0x0000, 0x0000, 0x0000 }, /* R1297 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1298 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1299 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1300 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1301 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1302 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1303 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1304 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1305 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1306 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1307 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1308 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1309 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1310 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1311 */
+ { 0x02B6, 0x02B6, 0x0000 }, /* R1312 - AIF2 DAC Filters (1) */
+ { 0x3F00, 0x3F00, 0x0000 }, /* R1313 - AIF2 DAC Filters (2) */
+ { 0x0000, 0x0000, 0x0000 }, /* R1314 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1315 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1316 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1317 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1318 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1319 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1320 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1321 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1322 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1323 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1324 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1325 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1326 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1327 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1328 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1329 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1330 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1331 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1332 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1333 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1334 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1335 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1336 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1337 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1338 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1339 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1340 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1341 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1342 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1343 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1344 - AIF2 DRC (1) */
+ { 0x1FFF, 0x1FFF, 0x0000 }, /* R1345 - AIF2 DRC (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1346 - AIF2 DRC (3) */
+ { 0x07FF, 0x07FF, 0x0000 }, /* R1347 - AIF2 DRC (4) */
+ { 0x03FF, 0x03FF, 0x0000 }, /* R1348 - AIF2 DRC (5) */
+ { 0x0000, 0x0000, 0x0000 }, /* R1349 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1350 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1351 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1352 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1353 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1354 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1355 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1356 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1357 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1358 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1359 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1360 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1361 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1362 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1363 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1364 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1365 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1366 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1367 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1368 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1369 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1370 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1371 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1372 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1373 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1374 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1375 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1376 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1377 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1378 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1379 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1380 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1381 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1382 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1383 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1384 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1385 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1386 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1387 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1388 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1389 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1390 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1391 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1392 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1393 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1394 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1395 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1396 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1397 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1398 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1399 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1400 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1401 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1402 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1403 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1404 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1405 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1406 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1407 */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1408 - AIF2 EQ Gains (1) */
+ { 0xFFC0, 0xFFC0, 0x0000 }, /* R1409 - AIF2 EQ Gains (2) */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1410 - AIF2 EQ Band 1 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1411 - AIF2 EQ Band 1 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1412 - AIF2 EQ Band 1 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1413 - AIF2 EQ Band 2 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1414 - AIF2 EQ Band 2 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1415 - AIF2 EQ Band 2 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1416 - AIF2 EQ Band 2 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1417 - AIF2 EQ Band 3 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1418 - AIF2 EQ Band 3 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1419 - AIF2 EQ Band 3 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1420 - AIF2 EQ Band 3 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1421 - AIF2 EQ Band 4 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1422 - AIF2 EQ Band 4 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1423 - AIF2 EQ Band 4 C */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1424 - AIF2 EQ Band 4 PG */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1425 - AIF2 EQ Band 5 A */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1426 - AIF2 EQ Band 5 B */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R1427 - AIF2 EQ Band 5 PG */
+ { 0x0000, 0x0000, 0x0000 }, /* R1428 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1429 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1430 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1431 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1432 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1433 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1434 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1435 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1436 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1437 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1438 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1439 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1440 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1441 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1442 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1443 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1444 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1445 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1446 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1447 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1448 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1449 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1450 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1451 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1452 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1453 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1454 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1455 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1456 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1457 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1458 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1459 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1460 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1461 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1462 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1463 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1464 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1465 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1466 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1467 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1468 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1469 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1470 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1471 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1472 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1473 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1474 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1475 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1476 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1477 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1478 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1479 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1480 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1481 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1482 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1483 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1484 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1485 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1486 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1487 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1488 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1489 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1490 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1491 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1492 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1493 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1494 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1495 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1496 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1497 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1498 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1499 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1500 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1501 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1502 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1503 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1504 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1505 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1506 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1507 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1508 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1509 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1510 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1511 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1512 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1513 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1514 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1515 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1516 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1517 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1518 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1519 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1520 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1521 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1522 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1523 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1524 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1525 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1526 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1527 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1528 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1529 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1530 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1531 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1532 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1533 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1534 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1535 */
+ { 0x01EF, 0x01EF, 0x0000 }, /* R1536 - DAC1 Mixer Volumes */
+ { 0x0037, 0x0037, 0x0000 }, /* R1537 - DAC1 Left Mixer Routing */
+ { 0x0037, 0x0037, 0x0000 }, /* R1538 - DAC1 Right Mixer Routing */
+ { 0x01EF, 0x01EF, 0x0000 }, /* R1539 - DAC2 Mixer Volumes */
+ { 0x0037, 0x0037, 0x0000 }, /* R1540 - DAC2 Left Mixer Routing */
+ { 0x0037, 0x0037, 0x0000 }, /* R1541 - DAC2 Right Mixer Routing */
+ { 0x0003, 0x0003, 0x0000 }, /* R1542 - AIF1 ADC1 Left Mixer Routing */
+ { 0x0003, 0x0003, 0x0000 }, /* R1543 - AIF1 ADC1 Right Mixer Routing */
+ { 0x0003, 0x0003, 0x0000 }, /* R1544 - AIF1 ADC2 Left Mixer Routing */
+ { 0x0003, 0x0003, 0x0000 }, /* R1545 - AIF1 ADC2 Right mixer Routing */
+ { 0x0000, 0x0000, 0x0000 }, /* R1546 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1547 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1548 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1549 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1550 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1551 */
+ { 0x02FF, 0x03FF, 0x0000 }, /* R1552 - DAC1 Left Volume */
+ { 0x02FF, 0x03FF, 0x0000 }, /* R1553 - DAC1 Right Volume */
+ { 0x02FF, 0x03FF, 0x0000 }, /* R1554 - DAC2 Left Volume */
+ { 0x02FF, 0x03FF, 0x0000 }, /* R1555 - DAC2 Right Volume */
+ { 0x0003, 0x0003, 0x0000 }, /* R1556 - DAC Softmute */
+ { 0x0000, 0x0000, 0x0000 }, /* R1557 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1558 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1559 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1560 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1561 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1562 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1563 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1564 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1565 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1566 */
+ { 0x0000, 0x0000, 0x0000 }, /* R1567 */
+ { 0x0003, 0x0003, 0x0000 }, /* R1568 - Oversampling */
+ { 0x03C3, 0x03C3, 0x0000 }, /* R1569 - Sidetone */
+};
+
+static int wm8994_readable(unsigned int reg)
+{
+ if (reg >= ARRAY_SIZE(access_masks))
+ return 0;
+ return access_masks[reg].readable != 0;
+}
+
+static int wm8994_volatile(unsigned int reg)
+{
+ if (reg >= WM8994_REG_CACHE_SIZE)
+ return 1;
+
+ switch (reg) {
+ case WM8994_SOFTWARE_RESET:
+ case WM8994_CHIP_REVISION:
+ case WM8994_DC_SERVO_1:
+ case WM8994_DC_SERVO_READBACK:
+ case WM8994_RATE_STATUS:
+ case WM8994_LDO_1:
+ case WM8994_LDO_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ BUG_ON(reg > WM8994_MAX_REGISTER);
+
+ if (!wm8994_volatile(reg))
+ wm8994->reg_cache[reg] = value;
+
+ dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value);
+
+ return wm8994_reg_write(codec->control_data, reg, value);
+}
+
+static unsigned int wm8994_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *reg_cache = codec->reg_cache;
+
+ BUG_ON(reg > WM8994_MAX_REGISTER);
+
+ if (wm8994_volatile(reg))
+ return wm8994_reg_read(codec->control_data, reg);
+ else
+ return reg_cache[reg];
+}
+
+static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int rate;
+ int reg1 = 0;
+ int offset;
+
+ if (aif)
+ offset = 4;
+ else
+ offset = 0;
+
+ switch (wm8994->sysclk[aif]) {
+ case WM8994_SYSCLK_MCLK1:
+ rate = wm8994->mclk[0];
+ break;
+
+ case WM8994_SYSCLK_MCLK2:
+ reg1 |= 0x8;
+ rate = wm8994->mclk[1];
+ break;
+
+ case WM8994_SYSCLK_FLL1:
+ reg1 |= 0x10;
+ rate = wm8994->fll[0].out;
+ break;
+
+ case WM8994_SYSCLK_FLL2:
+ reg1 |= 0x18;
+ rate = wm8994->fll[1].out;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (rate >= 13500000) {
+ rate /= 2;
+ reg1 |= WM8994_AIF1CLK_DIV;
+
+ dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+ aif + 1, rate);
+ }
+
+ if (rate && rate < 3000000)
+ dev_warn(codec->dev, "AIF%dCLK is %dHz, should be >=3MHz for optimal performance\n",
+ aif + 1, rate);
+
+ wm8994->aifclk[aif] = rate;
+
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset,
+ WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
+ reg1);
+
+ return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int old, new;
+
+ /* Bring up the AIF clocks first */
+ configure_aif_clock(codec, 0);
+ configure_aif_clock(codec, 1);
+
+ /* Then switch CLK_SYS over to the higher of them; a change
+ * can only happen as a result of a clocking change which can
+ * only be made outside of DAPM so we can safely redo the
+ * clocking.
+ */
+
+ /* If they're equal it doesn't matter which is used */
+ if (wm8994->aifclk[0] == wm8994->aifclk[1])
+ return 0;
+
+ if (wm8994->aifclk[0] < wm8994->aifclk[1])
+ new = WM8994_SYSCLK_SRC;
+ else
+ new = 0;
+
+ old = snd_soc_read(codec, WM8994_CLOCKING_1) & WM8994_SYSCLK_SRC;
+
+ /* If there's no change then we're done. */
+ if (old == new)
+ return 0;
+
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new);
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static int check_clk_sys(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ int reg = snd_soc_read(source->codec, WM8994_CLOCKING_1);
+ const char *clk;
+
+ /* Check what we're currently using for CLK_SYS */
+ if (reg & WM8994_SYSCLK_SRC)
+ clk = "AIF2CLK";
+ else
+ clk = "AIF1CLK";
+
+ return strcmp(source->name, clk) == 0;
+}
+
+static const char *sidetone_hpf_text[] = {
+ "2.7kHz", "1.35kHz", "675Hz", "370Hz", "180Hz", "90Hz", "45Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+ SOC_ENUM_SINGLE(WM8994_SIDETONE, 7, 7, sidetone_hpf_text);
+
+static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(wm8994_3d_tlv, -1600, 183, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+#define WM8994_DRC_SWITCH(xname, reg, shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+ .put = wm8994_put_drc_sw, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, 1, 0) }
+
+static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int mask, ret;
+
+ /* Can't enable both ADC and DAC paths simultaneously */
+ if (mc->shift == WM8994_AIF1DAC1_DRC_ENA_SHIFT)
+ mask = WM8994_AIF1ADC1L_DRC_ENA_MASK |
+ WM8994_AIF1ADC1R_DRC_ENA_MASK;
+ else
+ mask = WM8994_AIF1DAC1_DRC_ENA_MASK;
+
+ ret = snd_soc_read(codec, mc->reg);
+ if (ret < 0)
+ return ret;
+ if (ret & mask)
+ return -EINVAL;
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+
+
+static void wm8994_set_drc(struct snd_soc_codec *codec, int drc)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int base = wm8994_drc_base[drc];
+ int cfg = wm8994->drc_cfg[drc];
+ int save, i;
+
+ /* Save any enables; the configuration should clear them. */
+ save = snd_soc_read(codec, base);
+ save &= WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA;
+
+ for (i = 0; i < WM8994_DRC_REGS; i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->drc_cfgs[cfg].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_DRC_ENA |
+ WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_drc(const char *name)
+{
+ if (strcmp(name, "AIF1DRC1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "AIF1DRC2 Mode") == 0)
+ return 1;
+ if (strcmp(name, "AIF2DRC Mode") == 0)
+ return 2;
+ return -EINVAL;
+}
+
+static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int drc = wm8994_get_drc(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (drc < 0)
+ return drc;
+
+ if (value >= pdata->num_drc_cfgs)
+ return -EINVAL;
+
+ wm8994->drc_cfg[drc] = value;
+
+ wm8994_set_drc(codec, drc);
+
+ return 0;
+}
+
+static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int drc = wm8994_get_drc(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8994->drc_cfg[drc];
+
+ return 0;
+}
+
+static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int base = wm8994_retune_mobile_base[block];
+ int iface, best, best_val, save, i, cfg;
+
+ if (!pdata || !wm8994->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ case 1:
+ iface = 0;
+ break;
+ case 2:
+ iface = 1;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8994->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8994->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8994->dac_rates[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8994->dac_rates[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8994->dac_rates[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8994_AIF1DAC1_EQ_ENA;
+
+ for (i = 0; i < WM8994_EQ_REGS; i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "AIF1.1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "AIF1.2 EQ Mode") == 0)
+ return 1;
+ if (strcmp(name, "AIF2 EQ Mode") == 0)
+ return 2;
+ return -EINVAL;
+}
+
+static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8994->retune_mobile_cfg[block] = value;
+
+ wm8994_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
+ WM8994_AIF1_ADC1_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+ WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME,
+ WM8994_AIF2_ADC_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+
+SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
+ WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+ WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME,
+ WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv),
+SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv),
+
+SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2),
+WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1),
+WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0),
+
+SOC_SINGLE_TLV("DAC1 Right Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+ 5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC1 Left Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+ 0, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Right Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+ 5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+ 0, 12, 0, st_tlv),
+SOC_ENUM("Sidetone HPF Mux", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME,
+ WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME,
+ WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME,
+ WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME,
+ WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE_TLV("SPKL DAC2 Volume", WM8994_SPKMIXL_ATTENUATION,
+ 6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL DAC1 Volume", WM8994_SPKMIXL_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC2 Volume", WM8994_SPKMIXR_ATTENUATION,
+ 6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR DAC1 Volume", WM8994_SPKMIXR_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC1 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+ 8, 1, 0),
+SOC_SINGLE_TLV("AIF1DAC2 3D Stereo Volume", WM8994_AIF1_DAC2_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC2 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+ 8, 1, 0),
+SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2,
+ 10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+ 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8994_eq_controls[] = {
+SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ2 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ3 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ4 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ5 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC2 EQ1 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ2 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ3 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ4 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ5 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("AIF2 EQ1 Volume", WM8994_AIF2_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ2 Volume", WM8994_AIF2_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ3 Volume", WM8994_AIF2_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ4 Volume", WM8994_AIF2_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return configure_clock(codec);
+
+ case SND_SOC_DAPM_POST_PMD:
+ configure_clock(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static void wm8994_update_class_w(struct snd_soc_codec *codec)
+{
+ int enable = 1;
+ int source = 0; /* GCC flow analysis can't track enable */
+ int reg, reg_r;
+
+ /* Only support direct DAC->headphone paths */
+ reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
+ if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
+ dev_vdbg(codec->dev, "HPL connected to output mixer\n");
+ enable = 0;
+ }
+
+ reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
+ if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
+ dev_vdbg(codec->dev, "HPR connected to output mixer\n");
+ enable = 0;
+ }
+
+ /* We also need the same setting for L/R and only one path */
+ reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
+ switch (reg) {
+ case WM8994_AIF2DACL_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF2DAC\n");
+ source = 2 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8994_AIF1DAC2L_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF1DAC2\n");
+ source = 1 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ case WM8994_AIF1DAC1L_TO_DAC1L:
+ dev_vdbg(codec->dev, "Class W source AIF1DAC1\n");
+ source = 0 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
+ enable = 0;
+ break;
+ }
+
+ reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
+ if (reg_r != reg) {
+ dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
+ enable = 0;
+ }
+
+ if (enable) {
+ dev_dbg(codec->dev, "Class W enabled\n");
+ snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+ WM8994_CP_DYN_PWR |
+ WM8994_CP_DYN_SRC_SEL_MASK,
+ source | WM8994_CP_DYN_PWR);
+
+ } else {
+ dev_dbg(codec->dev, "Class W disabled\n");
+ snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+ WM8994_CP_DYN_PWR, 0);
+ }
+}
+
+static const char *hp_mux_text[] = {
+ "Mixer",
+ "DAC",
+};
+
+#define WM8994_HP_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = wm8994_put_hp_enum, \
+ .private_value = (unsigned long)&xenum }
+
+static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = w->codec;
+ int ret;
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ wm8994_update_class_w(codec);
+
+ return ret;
+}
+
+static const struct soc_enum hpl_enum =
+ SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+ WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+ SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+ WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
+
+static const char *adc_mux_text[] = {
+ "ADC",
+ "DMIC",
+};
+
+static const struct soc_enum adc_enum =
+ SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text);
+
+static const struct snd_kcontrol_new adcl_mux =
+ SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
+
+static const struct snd_kcontrol_new adcr_mux =
+ SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum);
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 9, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8994_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 8, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8994_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 0, 1, 0),
+};
+
+/* Debugging; dump chip status after DAPM transitions */
+static int post_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ dev_dbg(codec->dev, "SRC status: %x\n",
+ snd_soc_read(codec,
+ WM8994_RATE_STATUS));
+ return 0;
+}
+
+static const struct snd_kcontrol_new aif1adc1l_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc1r_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_dapm_get_volsw, .put = wm8994_put_class_w, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = w->codec;
+ int ret;
+
+ ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ wm8994_update_class_w(codec);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const char *sidetone_text[] = {
+ "ADC/DMIC1", "DMIC2",
+};
+
+static const struct soc_enum sidetone1_enum =
+ SOC_ENUM_SINGLE(WM8994_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone1_mux =
+ SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum);
+
+static const struct soc_enum sidetone2_enum =
+ SOC_ENUM_SINGLE(WM8994_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone2_mux =
+ SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum);
+
+static const char *aif1dac_text[] = {
+ "AIF1DACDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif1dac_enum =
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
+
+static const struct snd_kcontrol_new aif1dac_mux =
+ SOC_DAPM_ENUM("AIF1DAC Mux", aif1dac_enum);
+
+static const char *aif2dac_text[] = {
+ "AIF2DACDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif2dac_enum =
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 1, 2, aif2dac_text);
+
+static const struct snd_kcontrol_new aif2dac_mux =
+ SOC_DAPM_ENUM("AIF2DAC Mux", aif2dac_enum);
+
+static const char *aif2adc_text[] = {
+ "AIF2ADCDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif2adc_enum =
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 2, 2, aif2adc_text);
+
+static const struct snd_kcontrol_new aif2adc_mux =
+ SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum);
+
+static const char *aif3adc_text[] = {
+ "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT",
+};
+
+static const struct soc_enum aif3adc_enum =
+ SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text);
+
+static const struct snd_kcontrol_new aif3adc_mux =
+ SOC_DAPM_ENUM("AIF3ADC Mux", aif3adc_enum);
+
+static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture",
+ 0, WM8994_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture",
+ 0, WM8994_POWER_MANAGEMENT_4, 8, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 8, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture",
+ 0, WM8994_POWER_MANAGEMENT_4, 11, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture",
+ 0, WM8994_POWER_MANAGEMENT_4, 10, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 11, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 10, 0),
+
+SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)),
+SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
+ aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
+
+SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
+SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux),
+
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0,
+ WM8994_POWER_MANAGEMENT_4, 13, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0,
+ WM8994_POWER_MANAGEMENT_4, 12, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACL", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 13, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACR", NULL, 0,
+ WM8994_POWER_MANAGEMENT_5, 12, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux),
+SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux),
+SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux),
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &aif3adc_mux),
+
+SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8994_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
+
+/* Power is done with the muxes since the ADC power also controls the
+ * downsampling chain, the chip will automatically manage the analogue
+ * specific portions.
+ */
+SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
+SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_POST("Debug log", post_ev),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+ { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys },
+ { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys },
+
+ { "DSP1CLK", NULL, "CLK_SYS" },
+ { "DSP2CLK", NULL, "CLK_SYS" },
+ { "DSPINTCLK", NULL, "CLK_SYS" },
+
+ { "AIF1ADC1L", NULL, "AIF1CLK" },
+ { "AIF1ADC1L", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "AIF1CLK" },
+ { "AIF1ADC1R", NULL, "DSP1CLK" },
+ { "AIF1ADC1R", NULL, "DSPINTCLK" },
+
+ { "AIF1DAC1L", NULL, "AIF1CLK" },
+ { "AIF1DAC1L", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "AIF1CLK" },
+ { "AIF1DAC1R", NULL, "DSP1CLK" },
+ { "AIF1DAC1R", NULL, "DSPINTCLK" },
+
+ { "AIF1ADC2L", NULL, "AIF1CLK" },
+ { "AIF1ADC2L", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "AIF1CLK" },
+ { "AIF1ADC2R", NULL, "DSP1CLK" },
+ { "AIF1ADC2R", NULL, "DSPINTCLK" },
+
+ { "AIF1DAC2L", NULL, "AIF1CLK" },
+ { "AIF1DAC2L", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "AIF1CLK" },
+ { "AIF1DAC2R", NULL, "DSP1CLK" },
+ { "AIF1DAC2R", NULL, "DSPINTCLK" },
+
+ { "AIF2ADCL", NULL, "AIF2CLK" },
+ { "AIF2ADCL", NULL, "DSP2CLK" },
+ { "AIF2ADCR", NULL, "AIF2CLK" },
+ { "AIF2ADCR", NULL, "DSP2CLK" },
+ { "AIF2ADCR", NULL, "DSPINTCLK" },
+
+ { "AIF2DACL", NULL, "AIF2CLK" },
+ { "AIF2DACL", NULL, "DSP2CLK" },
+ { "AIF2DACR", NULL, "AIF2CLK" },
+ { "AIF2DACR", NULL, "DSP2CLK" },
+ { "AIF2DACR", NULL, "DSPINTCLK" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1L", NULL, "CLK_SYS" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "CLK_SYS" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2L", NULL, "CLK_SYS" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "CLK_SYS" },
+
+ { "ADCL", NULL, "AIF1CLK" },
+ { "ADCL", NULL, "DSP1CLK" },
+ { "ADCL", NULL, "DSPINTCLK" },
+
+ { "ADCR", NULL, "AIF1CLK" },
+ { "ADCR", NULL, "DSP1CLK" },
+ { "ADCR", NULL, "DSPINTCLK" },
+
+ { "ADCL Mux", "ADC", "ADCL" },
+ { "ADCL Mux", "DMIC", "DMIC1L" },
+ { "ADCR Mux", "ADC", "ADCR" },
+ { "ADCR Mux", "DMIC", "DMIC1R" },
+
+ { "DAC1L", NULL, "AIF1CLK" },
+ { "DAC1L", NULL, "DSP1CLK" },
+ { "DAC1L", NULL, "DSPINTCLK" },
+
+ { "DAC1R", NULL, "AIF1CLK" },
+ { "DAC1R", NULL, "DSP1CLK" },
+ { "DAC1R", NULL, "DSPINTCLK" },
+
+ { "DAC2L", NULL, "AIF2CLK" },
+ { "DAC2L", NULL, "DSP2CLK" },
+ { "DAC2L", NULL, "DSPINTCLK" },
+
+ { "DAC2R", NULL, "AIF2DACR" },
+ { "DAC2R", NULL, "AIF2CLK" },
+ { "DAC2R", NULL, "DSP2CLK" },
+ { "DAC2R", NULL, "DSPINTCLK" },
+
+ { "TOCLK", NULL, "CLK_SYS" },
+
+ /* AIF1 outputs */
+ { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" },
+ { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" },
+ { "AIF1ADC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+
+ { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" },
+ { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
+ { "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+
+ /* Pin level routing for AIF3 */
+ { "AIF1DAC1L", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC1R", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC2L", NULL, "AIF1DAC Mux" },
+ { "AIF1DAC2R", NULL, "AIF1DAC Mux" },
+
+ { "AIF2DACL", NULL, "AIF2DAC Mux" },
+ { "AIF2DACR", NULL, "AIF2DAC Mux" },
+
+ { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
+ { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+ { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
+ { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+ { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
+ { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
+ { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" },
+
+ /* DAC1 inputs */
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+ { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+ { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ /* DAC2/AIF2 outputs */
+ { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" },
+ { "DAC2L", NULL, "AIF2DAC2L Mixer" },
+ { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" },
+ { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+ { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+ { "AIF2DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" },
+ { "DAC2R", NULL, "AIF2DAC2R Mixer" },
+ { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" },
+ { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+ { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+ { "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+ { "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+ { "AIF2ADCDAT", NULL, "AIF2ADC Mux" },
+
+ /* AIF3 output */
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1L" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1R" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2L" },
+ { "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2R" },
+ { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCL" },
+ { "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCR" },
+ { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
+ { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+
+ /* Sidetone */
+ { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
+ { "Left Sidetone", "DMIC2", "DMIC2L" },
+ { "Right Sidetone", "ADC/DMIC1", "ADCR Mux" },
+ { "Right Sidetone", "DMIC2", "DMIC2R" },
+
+ /* Output stages */
+ { "Left Output Mixer", "DAC Switch", "DAC1L" },
+ { "Right Output Mixer", "DAC Switch", "DAC1R" },
+
+ { "SPKL", "DAC1 Switch", "DAC1L" },
+ { "SPKL", "DAC2 Switch", "DAC2L" },
+
+ { "SPKR", "DAC1 Switch", "DAC1R" },
+ { "SPKR", "DAC2 Switch", "DAC2R" },
+
+ { "Left Headphone Mux", "DAC", "DAC1L" },
+ { "Right Headphone Mux", "DAC", "DAC1R" },
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+struct fll_div {
+ u16 outdiv;
+ u16 n;
+ u16 k;
+ u16 clk_ref_div;
+ u16 fll_fratio;
+};
+
+static int wm8994_get_fll_config(struct fll_div *fll,
+ int freq_in, int freq_out)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
+
+ /* Scale the input frequency down to <= 13.5MHz */
+ fll->clk_ref_div = 0;
+ while (freq_in > 13500000) {
+ fll->clk_ref_div++;
+ freq_in /= 2;
+
+ if (fll->clk_ref_div > 3)
+ return -EINVAL;
+ }
+ pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in);
+
+ /* Scale the output to give 90MHz<=Fvco<=100MHz */
+ fll->outdiv = 3;
+ while (freq_out * (fll->outdiv + 1) < 90000000) {
+ fll->outdiv++;
+ if (fll->outdiv > 63)
+ return -EINVAL;
+ }
+ freq_out *= fll->outdiv + 1;
+ pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out);
+
+ if (freq_in > 1000000) {
+ fll->fll_fratio = 0;
+ } else if (freq_in > 256000) {
+ fll->fll_fratio = 1;
+ freq_in *= 2;
+ } else if (freq_in > 128000) {
+ fll->fll_fratio = 2;
+ freq_in *= 4;
+ } else if (freq_in > 64000) {
+ fll->fll_fratio = 3;
+ freq_in *= 8;
+ } else {
+ fll->fll_fratio = 4;
+ freq_in *= 16;
+ }
+ pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in);
+
+ /* Now, calculate N.K */
+ Ndiv = freq_out / freq_in;
+
+ fll->n = Ndiv;
+ Nmod = freq_out % freq_in;
+ 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, freq_in);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll->k = K / 10;
+
+ pr_debug("N=%x K=%x\n", fll->n, fll->k);
+
+ return 0;
+}
+
+static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int reg_offset, ret;
+ struct fll_div fll;
+ u16 reg, aif1, aif2;
+
+ aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ & WM8994_AIF1CLK_ENA;
+
+ aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ & WM8994_AIF2CLK_ENA;
+
+ switch (id) {
+ case WM8994_FLL1:
+ reg_offset = 0;
+ id = 0;
+ break;
+ case WM8994_FLL2:
+ reg_offset = 0x20;
+ id = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (src) {
+ case 0:
+ /* Allow no source specification when stopping */
+ if (freq_out)
+ return -EINVAL;
+ break;
+ case WM8994_FLL_SRC_MCLK1:
+ case WM8994_FLL_SRC_MCLK2:
+ case WM8994_FLL_SRC_LRCLK:
+ case WM8994_FLL_SRC_BCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Are we changing anything? */
+ if (wm8994->fll[id].src == src &&
+ wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out)
+ return 0;
+
+ /* If we're stopping the FLL redo the old config - no
+ * registers will actually be written but we avoid GCC flow
+ * analysis bugs spewing warnings.
+ */
+ if (freq_out)
+ ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
+ else
+ ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
+ wm8994->fll[id].out);
+ if (ret < 0)
+ return ret;
+
+ /* Gate the AIF clocks while we reclock */
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ WM8994_AIF1CLK_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ WM8994_AIF2CLK_ENA, 0);
+
+ /* We always need to disable the FLL while reconfiguring */
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ WM8994_FLL1_ENA, 0);
+
+ reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
+ (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
+ WM8994_FLL1_OUTDIV_MASK |
+ WM8994_FLL1_FRATIO_MASK, reg);
+
+ snd_soc_write(codec, WM8994_FLL1_CONTROL_3 + reg_offset, fll.k);
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
+ WM8994_FLL1_N_MASK,
+ fll.n << WM8994_FLL1_N_SHIFT);
+
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8994_FLL1_REFCLK_DIV_MASK |
+ WM8994_FLL1_REFCLK_SRC_MASK,
+ (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
+ (src - 1));
+
+ /* Enable (with fractional mode if required) */
+ if (freq_out) {
+ if (fll.k)
+ reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
+ else
+ reg = WM8994_FLL1_ENA;
+ snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+ WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
+ reg);
+ }
+
+ wm8994->fll[id].in = freq_in;
+ wm8994->fll[id].out = freq_out;
+ wm8994->fll[id].src = src;
+
+ /* Enable any gated AIF clocks */
+ snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+ WM8994_AIF1CLK_ENA, aif1);
+ snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+ WM8994_AIF2CLK_ENA, aif2);
+
+ configure_clock(codec);
+
+ return 0;
+}
+
+static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ switch (dai->id) {
+ case 1:
+ case 2:
+ break;
+
+ default:
+ /* AIF3 shares clocking with AIF1/2 */
+ return -EINVAL;
+ }
+
+ switch (clk_id) {
+ case WM8994_SYSCLK_MCLK1:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
+ wm8994->mclk[0] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
+ dai->id, freq);
+ break;
+
+ case WM8994_SYSCLK_MCLK2:
+ /* TODO: Set GPIO AF */
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
+ wm8994->mclk[1] = freq;
+ dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
+ dai->id, freq);
+ break;
+
+ case WM8994_SYSCLK_FLL1:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL1;
+ dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id);
+ break;
+
+ case WM8994_SYSCLK_FLL2:
+ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL2;
+ dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ configure_clock(codec);
+
+ return 0;
+}
+
+static int wm8994_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2x40k */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_VMID_SEL_MASK, 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Tweak DC servo configuration for improved
+ * performance. */
+ snd_soc_write(codec, 0x102, 0x3);
+ snd_soc_write(codec, 0x56, 0x3);
+ snd_soc_write(codec, 0x102, 0);
+
+ /* Discharge LINEOUT1 & 2 */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH);
+
+ /* Startup bias, VMID ramp & buffer */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK,
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ (0x11 << WM8994_VMID_RAMP_SHIFT));
+
+ /* Main bias enable, VMID=2x40k */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_BIAS_ENA |
+ WM8994_VMID_SEL_MASK,
+ WM8994_BIAS_ENA | 0x2);
+
+ msleep(20);
+ }
+
+ /* VMID=2x500k */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_VMID_SEL_MASK, 0x4);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ if (codec->bias_level == SND_SOC_BIAS_STANDBY) {
+ /* Switch over to startup biases */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ (1 << WM8994_VMID_RAMP_SHIFT));
+
+ /* Disable main biases */
+ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+ WM8994_BIAS_ENA |
+ WM8994_VMID_SEL_MASK, 0);
+
+ /* Discharge line */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH,
+ WM8994_LINEOUT1_DISCH |
+ WM8994_LINEOUT2_DISCH);
+
+ msleep(5);
+
+ /* Switch off startup biases */
+ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+ WM8994_BIAS_SRC |
+ WM8994_STARTUP_BIAS_ENA |
+ WM8994_VMID_BUF_ENA |
+ WM8994_VMID_RAMP_MASK, 0);
+ }
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int ms_reg;
+ int aif1_reg;
+ int ms = 0;
+ int aif1 = 0;
+
+ switch (dai->id) {
+ case 1:
+ ms_reg = WM8994_AIF1_MASTER_SLAVE;
+ aif1_reg = WM8994_AIF1_CONTROL_1;
+ break;
+ case 2:
+ ms_reg = WM8994_AIF2_MASTER_SLAVE;
+ aif1_reg = WM8994_AIF2_CONTROL_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ms = WM8994_AIF1_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8994_AIF1_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x18;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8994_AIF1_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 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8994_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8994_AIF1_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aif1_reg,
+ WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV |
+ WM8994_AIF1_FMT_MASK,
+ aif1);
+ snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
+ ms);
+
+ return 0;
+}
+
+static struct {
+ int val, rate;
+} srs[] = {
+ { 0, 8000 },
+ { 1, 11025 },
+ { 2, 12000 },
+ { 3, 16000 },
+ { 4, 22050 },
+ { 5, 24000 },
+ { 6, 32000 },
+ { 7, 44100 },
+ { 8, 48000 },
+ { 9, 88200 },
+ { 10, 96000 },
+};
+
+static int fs_ratios[] = {
+ 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536
+};
+
+static int bclk_divs[] = {
+ 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
+ 640, 880, 960, 1280, 1760, 1920
+};
+
+static int wm8994_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 wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int aif1_reg;
+ int bclk_reg;
+ int lrclk_reg;
+ int rate_reg;
+ int aif1 = 0;
+ int bclk = 0;
+ int lrclk = 0;
+ int rate_val = 0;
+ int id = dai->id - 1;
+
+ int i, cur_val, best_val, bclk_rate, best;
+
+ switch (dai->id) {
+ case 1:
+ aif1_reg = WM8994_AIF1_CONTROL_1;
+ bclk_reg = WM8994_AIF1_BCLK;
+ rate_reg = WM8994_AIF1_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ wm8994->lrclk_shared[0])
+ lrclk_reg = WM8994_AIF1DAC_LRCLK;
+ else
+ lrclk_reg = WM8994_AIF1ADC_LRCLK;
+ break;
+ case 2:
+ aif1_reg = WM8994_AIF2_CONTROL_1;
+ bclk_reg = WM8994_AIF2_BCLK;
+ rate_reg = WM8994_AIF2_RATE;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ wm8994->lrclk_shared[1])
+ lrclk_reg = WM8994_AIF2DAC_LRCLK;
+ else
+ lrclk_reg = WM8994_AIF2ADC_LRCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bclk_rate = params_rate(params) * 2;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bclk_rate *= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ bclk_rate *= 20;
+ aif1 |= 0x20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bclk_rate *= 24;
+ aif1 |= 0x40;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bclk_rate *= 32;
+ aif1 |= 0x60;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Try to find an appropriate sample rate; look for an exact match. */
+ for (i = 0; i < ARRAY_SIZE(srs); i++)
+ if (srs[i].rate == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(srs))
+ return -EINVAL;
+ rate_val |= srs[i].val << WM8994_AIF1_SR_SHIFT;
+
+ dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i].rate);
+ dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
+ dai->id, wm8994->aifclk[id], bclk_rate);
+
+ if (wm8994->aifclk[id] == 0) {
+ dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id);
+ return -EINVAL;
+ }
+
+ /* AIFCLK/fs ratio; look for a close match in either direction */
+ best = 0;
+ best_val = abs((fs_ratios[0] * params_rate(params))
+ - wm8994->aifclk[id]);
+ for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) {
+ cur_val = abs((fs_ratios[i] * params_rate(params))
+ - wm8994->aifclk[id]);
+ if (cur_val >= best_val)
+ continue;
+ best = i;
+ best_val = cur_val;
+ }
+ dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n",
+ dai->id, fs_ratios[best]);
+ rate_val |= best;
+
+ /* We may not get quite the right frequency if using
+ * approximate clocks so look for the closest match that is
+ * higher than the target (we need to ensure that there enough
+ * BCLKs to clock out the samples).
+ */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+ bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+ snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, rate_reg, WM8994_AIF1_SR_MASK |
+ WM8994_AIF1CLK_RATE_MASK, rate_val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->id) {
+ case 1:
+ wm8994->dac_rates[0] = params_rate(params);
+ wm8994_set_retune_mobile(codec, 0);
+ wm8994_set_retune_mobile(codec, 1);
+ break;
+ case 2:
+ wm8994->dac_rates[1] = params_rate(params);
+ wm8994_set_retune_mobile(codec, 2);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int mute_reg;
+ int reg;
+
+ switch (codec_dai->id) {
+ case 1:
+ mute_reg = WM8994_AIF1_DAC1_FILTERS_1;
+ break;
+ case 2:
+ mute_reg = WM8994_AIF2_DAC_FILTERS_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mute)
+ reg = WM8994_AIF1DAC1_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, mute_reg, WM8994_AIF1DAC1_MUTE, reg);
+
+ return 0;
+}
+
+static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg, val, mask;
+
+ switch (codec_dai->id) {
+ case 1:
+ reg = WM8994_AIF1_MASTER_SLAVE;
+ mask = WM8994_AIF1_TRI;
+ break;
+ case 2:
+ reg = WM8994_AIF2_MASTER_SLAVE;
+ mask = WM8994_AIF2_TRI;
+ break;
+ case 3:
+ reg = WM8994_POWER_MANAGEMENT_6;
+ mask = WM8994_AIF3_TRI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tristate)
+ val = mask;
+ else
+ val = 0;
+
+ return snd_soc_update_bits(codec, reg, mask, reg);
+}
+
+#define WM8994_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
+ .set_sysclk = wm8994_set_dai_sysclk,
+ .set_fmt = wm8994_set_dai_fmt,
+ .hw_params = wm8994_hw_params,
+ .digital_mute = wm8994_aif_mute,
+ .set_pll = wm8994_set_fll,
+ .set_tristate = wm8994_set_tristate,
+};
+
+static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
+ .set_sysclk = wm8994_set_dai_sysclk,
+ .set_fmt = wm8994_set_dai_fmt,
+ .hw_params = wm8994_hw_params,
+ .digital_mute = wm8994_aif_mute,
+ .set_pll = wm8994_set_fll,
+ .set_tristate = wm8994_set_tristate,
+};
+
+static struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
+ .set_tristate = wm8994_set_tristate,
+};
+
+struct snd_soc_dai wm8994_dai[] = {
+ {
+ .name = "WM8994 AIF1",
+ .id = 1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .ops = &wm8994_aif1_dai_ops,
+ },
+ {
+ .name = "WM8994 AIF2",
+ .id = 2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .ops = &wm8994_aif2_dai_ops,
+ },
+ {
+ .name = "WM8994 AIF3",
+ .id = 3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8994_RATES,
+ .formats = WM8994_FORMATS,
+ },
+ .ops = &wm8994_aif3_dai_ops,
+ }
+};
+EXPORT_SYMBOL_GPL(wm8994_dai);
+
+#ifdef CONFIG_PM
+static int wm8994_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 wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+ memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
+ sizeof(struct fll_config));
+ ret = wm8994_set_fll(&codec->dai[0], i + 1, 0, 0, 0);
+ if (ret < 0)
+ dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
+ i + 1, ret);
+ }
+
+ wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8994_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u16 *reg_cache = codec->reg_cache;
+ int i, ret;
+
+ /* Restore the registers */
+ for (i = 1; i < ARRAY_SIZE(wm8994->reg_cache); i++) {
+ switch (i) {
+ case WM8994_LDO_1:
+ case WM8994_LDO_2:
+ case WM8994_SOFTWARE_RESET:
+ /* Handled by other MFD drivers */
+ continue;
+ default:
+ break;
+ }
+
+ if (!access_masks[i].writable)
+ continue;
+
+ wm8994_reg_write(codec->control_data, i, reg_cache[i]);
+ }
+
+ wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+ if (!wm8994->fll_suspend[i].out)
+ continue;
+
+ ret = wm8994_set_fll(&codec->dai[0], i + 1,
+ wm8994->fll_suspend[i].src,
+ wm8994->fll_suspend[i].in,
+ wm8994->fll_suspend[i].out);
+ if (ret < 0)
+ dev_warn(codec->dev, "Failed to restore FLL%d: %d\n",
+ i + 1, ret);
+ }
+
+ return 0;
+}
+#else
+#define wm8994_suspend NULL
+#define wm8994_resume NULL
+#endif
+
+static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
+{
+ struct snd_soc_codec *codec = &wm8994->codec;
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("AIF1.1 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_put_retune_mobile_enum),
+ SOC_ENUM_EXT("AIF1.2 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_put_retune_mobile_enum),
+ SOC_ENUM_EXT("AIF2 EQ Mode",
+ wm8994->retune_mobile_enum,
+ wm8994_get_retune_mobile_enum,
+ wm8994_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. */
+ wm8994->num_retune_mobile_texts = 0;
+ wm8994->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8994->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8994->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8994->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8994->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8994->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8994->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8994->num_retune_mobile_texts++;
+ wm8994->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8994->num_retune_mobile_texts);
+
+ wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
+ wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
+
+ ret = snd_soc_add_controls(&wm8994->codec, controls,
+ ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(wm8994->codec.dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
+{
+ struct snd_soc_codec *codec = &wm8994->codec;
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int ret, i;
+
+ if (!pdata)
+ return;
+
+ wm_hubs_handle_analogue_pdata(codec, pdata->lineout1_diff,
+ pdata->lineout2_diff,
+ pdata->lineout1fb,
+ pdata->lineout2fb,
+ pdata->jd_scthr,
+ pdata->jd_thr,
+ pdata->micbias1_lvl,
+ pdata->micbias2_lvl);
+
+ dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+ if (pdata->num_drc_cfgs) {
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("AIF1DRC1 Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ SOC_ENUM_EXT("AIF1DRC2 Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ SOC_ENUM_EXT("AIF2DRC Mode", wm8994->drc_enum,
+ wm8994_get_drc_enum, wm8994_put_drc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->drc_texts = kmalloc(sizeof(char *)
+ * pdata->num_drc_cfgs, GFP_KERNEL);
+ if (!wm8994->drc_texts) {
+ dev_err(wm8994->codec.dev,
+ "Failed to allocate %d DRC config texts\n",
+ pdata->num_drc_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_drc_cfgs; i++)
+ wm8994->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+ wm8994->drc_enum.max = pdata->num_drc_cfgs;
+ wm8994->drc_enum.texts = wm8994->drc_texts;
+
+ ret = snd_soc_add_controls(&wm8994->codec, controls,
+ ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(wm8994->codec.dev,
+ "Failed to add DRC mode controls: %d\n", ret);
+
+ for (i = 0; i < WM8994_NUM_DRC; i++)
+ wm8994_set_drc(codec, i);
+ }
+
+ dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+ pdata->num_retune_mobile_cfgs);
+
+ if (pdata->num_retune_mobile_cfgs)
+ wm8994_handle_retune_mobile_pdata(wm8994);
+ else
+ snd_soc_add_controls(&wm8994->codec, wm8994_eq_controls,
+ ARRAY_SIZE(wm8994_eq_controls));
+}
+
+static int wm8994_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8994_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8994_codec;
+ codec = wm8994_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);
+ return ret;
+ }
+
+ wm8994_handle_pdata(snd_soc_codec_get_drvdata(codec));
+
+ wm_hubs_add_analogue_controls(codec);
+ snd_soc_add_controls(codec, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
+ snd_soc_dapm_new_controls(codec, wm8994_dapm_widgets,
+ ARRAY_SIZE(wm8994_dapm_widgets));
+ wm_hubs_add_analogue_routes(codec, 0, 0);
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ return 0;
+}
+
+static int wm8994_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_wm8994 = {
+ .probe = wm8994_probe,
+ .remove = wm8994_remove,
+ .suspend = wm8994_suspend,
+ .resume = wm8994_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8994);
+
+/**
+ * wm8994_mic_detect - Enable microphone detection via the WM8994 IRQ
+ *
+ * @codec: WM8994 codec
+ * @jack: jack to report detection events on
+ * @micbias: microphone bias to detect on
+ * @det: value to report for presence detection
+ * @shrt: value to report for short detection
+ *
+ * Enable microphone detection via IRQ on the WM8994. If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for WM8903 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * Configuration of detection levels is available via the micbias1_lvl
+ * and micbias2_lvl platform data members.
+ */
+int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int micbias, int det, int shrt)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_micdet *micdet;
+ int reg;
+
+ switch (micbias) {
+ case 1:
+ micdet = &wm8994->micdet[0];
+ break;
+ case 2:
+ micdet = &wm8994->micdet[1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "Configuring microphone detection on %d: %x %x\n",
+ micbias, det, shrt);
+
+ /* Store the configuration */
+ micdet->jack = jack;
+ micdet->det = det;
+ micdet->shrt = shrt;
+
+ /* If either of the jacks is set up then enable detection */
+ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
+ reg = WM8994_MICD_ENA;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8994_mic_detect);
+
+static irqreturn_t wm8994_mic_irq(int irq, void *data)
+{
+ struct wm8994_priv *priv = data;
+ struct snd_soc_codec *codec = &priv->codec;
+ int reg;
+ int report;
+
+ reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2);
+ if (reg < 0) {
+ dev_err(codec->dev, "Failed to read microphone status: %d\n",
+ reg);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(codec->dev, "Microphone status: %x\n", reg);
+
+ report = 0;
+ if (reg & WM8994_MIC1_DET_STS)
+ report |= priv->micdet[0].det;
+ if (reg & WM8994_MIC1_SHRT_STS)
+ report |= priv->micdet[0].shrt;
+ snd_soc_jack_report(priv->micdet[0].jack, report,
+ priv->micdet[0].det | priv->micdet[0].shrt);
+
+ report = 0;
+ if (reg & WM8994_MIC2_DET_STS)
+ report |= priv->micdet[1].det;
+ if (reg & WM8994_MIC2_SHRT_STS)
+ report |= priv->micdet[1].shrt;
+ snd_soc_jack_report(priv->micdet[1].jack, report,
+ priv->micdet[1].det | priv->micdet[1].shrt);
+
+ return IRQ_HANDLED;
+}
+
+static int wm8994_codec_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct wm8994_priv *wm8994;
+ struct snd_soc_codec *codec;
+ int i;
+ u16 rev;
+
+ if (wm8994_codec) {
+ dev_err(&pdev->dev, "Another WM8994 is registered\n");
+ return -EINVAL;
+ }
+
+ wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL);
+ if (!wm8994) {
+ dev_err(&pdev->dev, "Failed to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ codec = &wm8994->codec;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ snd_soc_codec_set_drvdata(codec, wm8994);
+ codec->control_data = dev_get_drvdata(pdev->dev.parent);
+ codec->name = "WM8994";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8994_read;
+ codec->write = wm8994_write;
+ codec->readable_register = wm8994_readable;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8994_set_bias_level;
+ codec->dai = &wm8994_dai[0];
+ codec->num_dai = 3;
+ codec->reg_cache_size = WM8994_MAX_REGISTER;
+ codec->reg_cache = &wm8994->reg_cache;
+ codec->dev = &pdev->dev;
+
+ wm8994->pdata = pdev->dev.parent->platform_data;
+
+ /* Fill the cache with physical values we inherited; don't reset */
+ ret = wm8994_bulk_read(codec->control_data, 0,
+ ARRAY_SIZE(wm8994->reg_cache) - 1,
+ codec->reg_cache);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to fill register cache: %d\n",
+ ret);
+ goto err;
+ }
+
+ /* Clear the cached values for unreadable/volatile registers to
+ * avoid potential confusion.
+ */
+ for (i = 0; i < ARRAY_SIZE(wm8994->reg_cache); i++)
+ if (wm8994_volatile(i) || !wm8994_readable(i))
+ wm8994->reg_cache[i] = 0;
+
+ /* Set revision-specific configuration */
+ rev = snd_soc_read(codec, WM8994_CHIP_REVISION);
+ switch (rev) {
+ case 2:
+ case 3:
+ wm8994->hubs.dcs_codes = -5;
+ wm8994->hubs.hp_startup_mode = 1;
+ wm8994->hubs.dcs_readback_mode = 1;
+ break;
+ default:
+ wm8994->hubs.dcs_readback_mode = 1;
+ break;
+ }
+
+ ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_DET,
+ wm8994_mic_irq, "Mic 1 detect", wm8994);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to request Mic1 detect IRQ: %d\n", ret);
+
+ ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT,
+ wm8994_mic_irq, "Mic 1 short", wm8994);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to request Mic1 short IRQ: %d\n", ret);
+
+ ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC2_DET,
+ wm8994_mic_irq, "Mic 2 detect", wm8994);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to request Mic2 detect IRQ: %d\n", ret);
+
+ ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT,
+ wm8994_mic_irq, "Mic 2 short", wm8994);
+ if (ret != 0)
+ dev_warn(&pdev->dev,
+ "Failed to request Mic2 short IRQ: %d\n", ret);
+
+ /* Remember if AIFnLRCLK is configured as a GPIO. This should be
+ * configured on init - if a system wants to do this dynamically
+ * at runtime we can deal with that then.
+ */
+ ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_1);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read GPIO1 state: %d\n", ret);
+ goto err_irq;
+ }
+ if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+ wm8994->lrclk_shared[0] = 1;
+ wm8994_dai[0].symmetric_rates = 1;
+ } else {
+ wm8994->lrclk_shared[0] = 0;
+ }
+
+ ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_6);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read GPIO6 state: %d\n", ret);
+ goto err_irq;
+ }
+ if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+ wm8994->lrclk_shared[1] = 1;
+ wm8994_dai[1].symmetric_rates = 1;
+ } else {
+ wm8994->lrclk_shared[1] = 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8994_dai); i++)
+ wm8994_dai[i].dev = codec->dev;
+
+ wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8994_codec = codec;
+
+ /* Latch volume updates (right only; we always do left then right). */
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_RIGHT_VOLUME,
+ WM8994_AIF1DAC1_VU, WM8994_AIF1DAC1_VU);
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC2_RIGHT_VOLUME,
+ WM8994_AIF1DAC2_VU, WM8994_AIF1DAC2_VU);
+ snd_soc_update_bits(codec, WM8994_AIF2_DAC_RIGHT_VOLUME,
+ WM8994_AIF2DAC_VU, WM8994_AIF2DAC_VU);
+ snd_soc_update_bits(codec, WM8994_AIF1_ADC1_RIGHT_VOLUME,
+ WM8994_AIF1ADC1_VU, WM8994_AIF1ADC1_VU);
+ snd_soc_update_bits(codec, WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ WM8994_AIF1ADC2_VU, WM8994_AIF1ADC2_VU);
+ snd_soc_update_bits(codec, WM8994_AIF2_ADC_RIGHT_VOLUME,
+ WM8994_AIF2ADC_VU, WM8994_AIF1ADC2_VU);
+ snd_soc_update_bits(codec, WM8994_DAC1_RIGHT_VOLUME,
+ WM8994_DAC1_VU, WM8994_DAC1_VU);
+ snd_soc_update_bits(codec, WM8994_DAC2_RIGHT_VOLUME,
+ WM8994_DAC2_VU, WM8994_DAC2_VU);
+
+ /* Set the low bit of the 3D stereo depth so TLV matches */
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_2,
+ 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT);
+ snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_2,
+ 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT);
+ snd_soc_update_bits(codec, WM8994_AIF2_DAC_FILTERS_2,
+ 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
+ 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
+
+ wm8994_update_class_w(codec);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err_irq;
+ }
+
+ ret = snd_soc_register_dais(wm8994_dai, ARRAY_SIZE(wm8994_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_codec;
+ }
+
+ platform_set_drvdata(pdev, wm8994);
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err_irq:
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994);
+err:
+ kfree(wm8994);
+ return ret;
+}
+
+static int __devexit wm8994_codec_remove(struct platform_device *pdev)
+{
+ struct wm8994_priv *wm8994 = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = &wm8994->codec;
+
+ wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dais(wm8994_dai, ARRAY_SIZE(wm8994_dai));
+ snd_soc_unregister_codec(&wm8994->codec);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
+ wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994);
+ kfree(wm8994);
+ wm8994_codec = NULL;
+
+ return 0;
+}
+
+static struct platform_driver wm8994_codec_driver = {
+ .driver = {
+ .name = "wm8994-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8994_codec_probe,
+ .remove = __devexit_p(wm8994_codec_remove),
+};
+
+static __init int wm8994_init(void)
+{
+ return platform_driver_register(&wm8994_codec_driver);
+}
+module_init(wm8994_init);
+
+static __exit void wm8994_exit(void)
+{
+ platform_driver_unregister(&wm8994_codec_driver);
+}
+module_exit(wm8994_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8994 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-codec");
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
new file mode 100644
index 000000000000..7072dc539354
--- /dev/null
+++ b/sound/soc/codecs/wm8994.h
@@ -0,0 +1,34 @@
+/*
+ * wm8994.h -- WM8994 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8994_H
+#define _WM8994_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_codec_device soc_codec_dev_wm8994;
+extern struct snd_soc_dai wm8994_dai[];
+
+/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
+#define WM8994_SYSCLK_MCLK1 1
+#define WM8994_SYSCLK_MCLK2 2
+#define WM8994_SYSCLK_FLL1 3
+#define WM8994_SYSCLK_FLL2 4
+
+#define WM8994_FLL1 1
+#define WM8994_FLL2 2
+
+#define WM8994_FLL_SRC_MCLK1 1
+#define WM8994_FLL_SRC_MCLK2 2
+#define WM8994_FLL_SRC_LRCLK 3
+#define WM8994_FLL_SRC_BCLK 4
+
+int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ int micbias, int det, int shrt);
+
+#endif
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index c468497314ba..13186fb4dcb4 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -520,7 +521,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
unsigned int Fref, unsigned int Fout)
{
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
u16 reg1, reg4, reg5;
struct _fll_div fll_div;
int ret;
@@ -606,7 +607,7 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
static int configure_clock(struct snd_soc_codec *codec)
{
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
int new_sysclk, i, target;
unsigned int reg;
int ret = 0;
@@ -701,7 +702,7 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
/* This should be done on init() for bypass paths */
switch (wm9081->sysclk_source) {
@@ -872,7 +873,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
unsigned int aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
@@ -964,7 +965,7 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
int ret, i, best, best_val, cur_val;
unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
@@ -1138,7 +1139,7 @@ static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case WM9081_SYSCLK_MCLK:
@@ -1158,7 +1159,7 @@ static int wm9081_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 wm9081_priv *wm9081 = codec->private_data;
+ struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
unsigned int aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
@@ -1241,7 +1242,7 @@ static int wm9081_probe(struct platform_device *pdev)
socdev->card->codec = wm9081_codec;
codec = wm9081_codec;
- wm9081 = codec->private_data;
+ wm9081 = snd_soc_codec_get_drvdata(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -1338,7 +1339,7 @@ static int wm9081_register(struct wm9081_priv *wm9081,
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- codec->private_data = wm9081;
+ snd_soc_codec_set_drvdata(codec, wm9081);
codec->name = "WM9081";
codec->owner = THIS_MODULE;
codec->dai = &wm9081_dai;
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
new file mode 100644
index 000000000000..1592250daec0
--- /dev/null
+++ b/sound/soc/codecs/wm9090.c
@@ -0,0 +1,773 @@
+/*
+ * ALSA SoC WM9090 driver
+ *
+ * Copyright 2009, 2010 Wolfson Microelectronics
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/wm9090.h>
+
+#include "wm9090.h"
+
+static struct snd_soc_codec *wm9090_codec;
+
+static const u16 wm9090_reg_defaults[] = {
+ 0x9093, /* R0 - Software Reset */
+ 0x0006, /* R1 - Power Management (1) */
+ 0x6000, /* R2 - Power Management (2) */
+ 0x0000, /* R3 - Power Management (3) */
+ 0x0000, /* R4 */
+ 0x0000, /* R5 */
+ 0x01C0, /* R6 - Clocking 1 */
+ 0x0000, /* R7 */
+ 0x0000, /* R8 */
+ 0x0000, /* R9 */
+ 0x0000, /* R10 */
+ 0x0000, /* R11 */
+ 0x0000, /* R12 */
+ 0x0000, /* R13 */
+ 0x0000, /* R14 */
+ 0x0000, /* R15 */
+ 0x0000, /* R16 */
+ 0x0000, /* R17 */
+ 0x0000, /* R18 */
+ 0x0000, /* R19 */
+ 0x0000, /* R20 */
+ 0x0000, /* R21 */
+ 0x0003, /* R22 - IN1 Line Control */
+ 0x0003, /* R23 - IN2 Line Control */
+ 0x0083, /* R24 - IN1 Line Input A Volume */
+ 0x0083, /* R25 - IN1 Line Input B Volume */
+ 0x0083, /* R26 - IN2 Line Input A Volume */
+ 0x0083, /* R27 - IN2 Line Input B Volume */
+ 0x002D, /* R28 - Left Output Volume */
+ 0x002D, /* R29 - Right Output Volume */
+ 0x0000, /* R30 */
+ 0x0000, /* R31 */
+ 0x0000, /* R32 */
+ 0x0000, /* R33 */
+ 0x0100, /* R34 - SPKMIXL Attenuation */
+ 0x0000, /* R35 */
+ 0x0010, /* R36 - SPKOUT Mixers */
+ 0x0140, /* R37 - ClassD3 */
+ 0x0039, /* R38 - Speaker Volume Left */
+ 0x0000, /* R39 */
+ 0x0000, /* R40 */
+ 0x0000, /* R41 */
+ 0x0000, /* R42 */
+ 0x0000, /* R43 */
+ 0x0000, /* R44 */
+ 0x0000, /* R45 - Output Mixer1 */
+ 0x0000, /* R46 - Output Mixer2 */
+ 0x0100, /* R47 - Output Mixer3 */
+ 0x0100, /* R48 - Output Mixer4 */
+ 0x0000, /* R49 */
+ 0x0000, /* R50 */
+ 0x0000, /* R51 */
+ 0x0000, /* R52 */
+ 0x0000, /* R53 */
+ 0x0000, /* R54 - Speaker Mixer */
+ 0x0000, /* R55 */
+ 0x0000, /* R56 */
+ 0x000D, /* R57 - AntiPOP2 */
+ 0x0000, /* R58 */
+ 0x0000, /* R59 */
+ 0x0000, /* R60 */
+ 0x0000, /* R61 */
+ 0x0000, /* R62 */
+ 0x0000, /* R63 */
+ 0x0000, /* R64 */
+ 0x0000, /* R65 */
+ 0x0000, /* R66 */
+ 0x0000, /* R67 */
+ 0x0000, /* R68 */
+ 0x0000, /* R69 */
+ 0x0000, /* R70 - Write Sequencer 0 */
+ 0x0000, /* R71 - Write Sequencer 1 */
+ 0x0000, /* R72 - Write Sequencer 2 */
+ 0x0000, /* R73 - Write Sequencer 3 */
+ 0x0000, /* R74 - Write Sequencer 4 */
+ 0x0000, /* R75 - Write Sequencer 5 */
+ 0x1F25, /* R76 - Charge Pump 1 */
+ 0x0000, /* R77 */
+ 0x0000, /* R78 */
+ 0x0000, /* R79 */
+ 0x0000, /* R80 */
+ 0x0000, /* R81 */
+ 0x0000, /* R82 */
+ 0x0000, /* R83 */
+ 0x0000, /* R84 - DC Servo 0 */
+ 0x054A, /* R85 - DC Servo 1 */
+ 0x0000, /* R86 */
+ 0x0000, /* R87 - DC Servo 3 */
+ 0x0000, /* R88 - DC Servo Readback 0 */
+ 0x0000, /* R89 - DC Servo Readback 1 */
+ 0x0000, /* R90 - DC Servo Readback 2 */
+ 0x0000, /* R91 */
+ 0x0000, /* R92 */
+ 0x0000, /* R93 */
+ 0x0000, /* R94 */
+ 0x0000, /* R95 */
+ 0x0100, /* R96 - Analogue HP 0 */
+ 0x0000, /* R97 */
+ 0x8640, /* R98 - AGC Control 0 */
+ 0xC000, /* R99 - AGC Control 1 */
+ 0x0200, /* R100 - AGC Control 2 */
+};
+
+/* This struct is used to save the context */
+struct wm9090_priv {
+ /* We're not really registering as a CODEC since ASoC core
+ * does not yet support multiple CODECs but having the CODEC
+ * structure means we can reuse some of the ASoC core
+ * features.
+ */
+ struct snd_soc_codec codec;
+ struct mutex mutex;
+ u16 reg_cache[WM9090_MAX_REGISTER + 1];
+ struct wm9090_platform_data pdata;
+};
+
+static int wm9090_volatile(unsigned int reg)
+{
+ switch (reg) {
+ case WM9090_SOFTWARE_RESET:
+ case WM9090_DC_SERVO_0:
+ case WM9090_DC_SERVO_READBACK_0:
+ case WM9090_DC_SERVO_READBACK_1:
+ case WM9090_DC_SERVO_READBACK_2:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec)
+{
+ unsigned int reg;
+ int count = 0;
+
+ dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ do {
+ count++;
+ msleep(1);
+ reg = snd_soc_read(codec, WM9090_DC_SERVO_READBACK_0);
+ dev_dbg(codec->dev, "DC servo status: %x\n", reg);
+ } while ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
+ != WM9090_DCS_CAL_COMPLETE_MASK && count < 1000);
+
+ if ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
+ != WM9090_DCS_CAL_COMPLETE_MASK)
+ dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+static const unsigned int in_tlv[] = {
+ TLV_DB_RANGE_HEAD(6),
+ 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0),
+};
+static const unsigned int mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm9090_controls[] = {
+SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN1A Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1A ZC Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("IN2A Volume", WM9090_IN2_LINE_INPUT_A_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN2A Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2A ZC Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 6, 1, 0),
+
+SOC_SINGLE("MIXOUTL Switch", WM9090_OUTPUT_MIXER3, 8, 1, 1),
+SOC_SINGLE_TLV("MIXOUTL IN1A Volume", WM9090_OUTPUT_MIXER3, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN2A Volume", WM9090_OUTPUT_MIXER3, 2, 3, 1,
+ mix_tlv),
+
+SOC_SINGLE("MIXOUTR Switch", WM9090_OUTPUT_MIXER4, 8, 1, 1),
+SOC_SINGLE_TLV("MIXOUTR IN1A Volume", WM9090_OUTPUT_MIXER4, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN2A Volume", WM9090_OUTPUT_MIXER4, 2, 3, 1,
+ mix_tlv),
+
+SOC_SINGLE("SPKMIX Switch", WM9090_SPKMIXL_ATTENUATION, 8, 1, 1),
+SOC_SINGLE_TLV("SPKMIX IN1A Volume", WM9090_SPKMIXL_ATTENUATION, 6, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("SPKMIX IN2A Volume", WM9090_SPKMIXL_ATTENUATION, 2, 3, 1,
+ mix_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Switch", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 6, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch", WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("Speaker Volume", WM9090_SPEAKER_VOLUME_LEFT, 0, 63, 0,
+ out_tlv),
+SOC_SINGLE("Speaker Switch", WM9090_SPEAKER_VOLUME_LEFT, 6, 1, 1),
+SOC_SINGLE("Speaker ZC Switch", WM9090_SPEAKER_VOLUME_LEFT, 7, 1, 0),
+SOC_SINGLE_TLV("Speaker Boost Volume", WM9090_CLASSD3, 3, 7, 0, spkboost_tlv),
+};
+
+static const struct snd_kcontrol_new wm9090_in1_se_controls[] = {
+SOC_SINGLE_TLV("IN1B Volume", WM9090_IN1_LINE_INPUT_B_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN1B Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1B ZC Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("SPKMIX IN1B Volume", WM9090_SPKMIXL_ATTENUATION, 4, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN1B Volume", WM9090_OUTPUT_MIXER3, 4, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN1B Volume", WM9090_OUTPUT_MIXER4, 4, 3, 1,
+ mix_tlv),
+};
+
+static const struct snd_kcontrol_new wm9090_in2_se_controls[] = {
+SOC_SINGLE_TLV("IN2B Volume", WM9090_IN2_LINE_INPUT_B_VOLUME, 0, 6, 0,
+ in_tlv),
+SOC_SINGLE("IN2B Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2B ZC Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 6, 1, 0),
+
+SOC_SINGLE_TLV("SPKMIX IN2B Volume", WM9090_SPKMIXL_ATTENUATION, 0, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTL IN2B Volume", WM9090_OUTPUT_MIXER3, 0, 3, 1,
+ mix_tlv),
+SOC_SINGLE_TLV("MIXOUTR IN2B Volume", WM9090_OUTPUT_MIXER4, 0, 3, 1,
+ mix_tlv),
+};
+
+static int hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ unsigned int reg = snd_soc_read(codec, WM9090_ANALOGUE_HP_0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ WM9090_CP_ENA, WM9090_CP_ENA);
+
+ msleep(5);
+
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA);
+
+ reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY;
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+
+ /* Start the DC servo. We don't currently use the
+ * ability to save the state since we don't have full
+ * control of the analogue paths and they can change
+ * DC offsets; see the WM8904 driver for an example of
+ * doing so.
+ */
+ snd_soc_write(codec, WM9090_DC_SERVO_0,
+ WM9090_DCS_ENA_CHAN_0 |
+ WM9090_DCS_ENA_CHAN_1 |
+ WM9090_DCS_TRIG_STARTUP_1 |
+ WM9090_DCS_TRIG_STARTUP_0);
+ wait_for_dc_servo(codec);
+
+ reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT |
+ WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT;
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ reg &= ~(WM9090_HPOUT1L_RMV_SHORT |
+ WM9090_HPOUT1L_DLY |
+ WM9090_HPOUT1L_OUTP |
+ WM9090_HPOUT1R_RMV_SHORT |
+ WM9090_HPOUT1R_DLY |
+ WM9090_HPOUT1R_OUTP);
+
+ snd_soc_write(codec, WM9090_ANALOGUE_HP_0, reg);
+
+ snd_soc_write(codec, WM9090_DC_SERVO_0, 0);
+
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
+ 0);
+
+ snd_soc_update_bits(codec, WM9090_CHARGE_PUMP_1,
+ WM9090_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new spkmix[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new spkout[] = {
+SOC_DAPM_SINGLE("Mixer Switch", WM9090_SPKOUT_MIXERS, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixoutl[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER1, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixoutr[] = {
+SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER2, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm9090_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1+"),
+SND_SOC_DAPM_INPUT("IN1-"),
+SND_SOC_DAPM_INPUT("IN2+"),
+SND_SOC_DAPM_INPUT("IN2-"),
+
+SND_SOC_DAPM_SUPPLY("OSC", WM9090_POWER_MANAGEMENT_1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("IN1A PGA", WM9090_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1B PGA", WM9090_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2A PGA", WM9090_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2B PGA", WM9090_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("SPKMIX", WM9090_POWER_MANAGEMENT_3, 3, 0,
+ spkmix, ARRAY_SIZE(spkmix)),
+SND_SOC_DAPM_MIXER("MIXOUTL", WM9090_POWER_MANAGEMENT_3, 5, 0,
+ mixoutl, ARRAY_SIZE(mixoutl)),
+SND_SOC_DAPM_MIXER("MIXOUTR", WM9090_POWER_MANAGEMENT_3, 4, 0,
+ mixoutr, ARRAY_SIZE(mixoutr)),
+
+SND_SOC_DAPM_PGA_E("HP PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ hp_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA("SPKPGA", WM9090_POWER_MANAGEMENT_3, 8, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("SPKOUT", WM9090_POWER_MANAGEMENT_1, 12, 0,
+ spkout, ARRAY_SIZE(spkout)),
+
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("Speaker"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "IN1A PGA", NULL, "IN1+" },
+ { "IN2A PGA", NULL, "IN2+" },
+
+ { "SPKMIX", "IN1A Switch", "IN1A PGA" },
+ { "SPKMIX", "IN2A Switch", "IN2A PGA" },
+
+ { "MIXOUTL", "IN1A Switch", "IN1A PGA" },
+ { "MIXOUTL", "IN2A Switch", "IN2A PGA" },
+
+ { "MIXOUTR", "IN1A Switch", "IN1A PGA" },
+ { "MIXOUTR", "IN2A Switch", "IN2A PGA" },
+
+ { "HP PGA", NULL, "OSC" },
+ { "HP PGA", NULL, "MIXOUTL" },
+ { "HP PGA", NULL, "MIXOUTR" },
+
+ { "HPL", NULL, "HP PGA" },
+ { "HPR", NULL, "HP PGA" },
+
+ { "SPKPGA", NULL, "OSC" },
+ { "SPKPGA", NULL, "SPKMIX" },
+
+ { "SPKOUT", "Mixer Switch", "SPKPGA" },
+
+ { "Speaker", NULL, "SPKOUT" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in1_se[] = {
+ { "IN1B PGA", NULL, "IN1-" },
+
+ { "SPKMIX", "IN1B Switch", "IN1B PGA" },
+ { "MIXOUTL", "IN1B Switch", "IN1B PGA" },
+ { "MIXOUTR", "IN1B Switch", "IN1B PGA" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in1_diff[] = {
+ { "IN1A PGA", NULL, "IN1-" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in2_se[] = {
+ { "IN2B PGA", NULL, "IN2-" },
+
+ { "SPKMIX", "IN2B Switch", "IN2B PGA" },
+ { "MIXOUTL", "IN2B Switch", "IN2B PGA" },
+ { "MIXOUTR", "IN2B Switch", "IN2B PGA" },
+};
+
+static const struct snd_soc_dapm_route audio_map_in2_diff[] = {
+ { "IN2A PGA", NULL, "IN2-" },
+};
+
+static int wm9090_add_controls(struct snd_soc_codec *codec)
+{
+ struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ snd_soc_dapm_new_controls(codec, wm9090_dapm_widgets,
+ ARRAY_SIZE(wm9090_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_add_controls(codec, wm9090_controls,
+ ARRAY_SIZE(wm9090_controls));
+
+ if (wm9090->pdata.lin1_diff) {
+ snd_soc_dapm_add_routes(codec, audio_map_in1_diff,
+ ARRAY_SIZE(audio_map_in1_diff));
+ } else {
+ snd_soc_dapm_add_routes(codec, audio_map_in1_se,
+ ARRAY_SIZE(audio_map_in1_se));
+ snd_soc_add_controls(codec, wm9090_in1_se_controls,
+ ARRAY_SIZE(wm9090_in1_se_controls));
+ }
+
+ if (wm9090->pdata.lin2_diff) {
+ snd_soc_dapm_add_routes(codec, audio_map_in2_diff,
+ ARRAY_SIZE(audio_map_in2_diff));
+ } else {
+ snd_soc_dapm_add_routes(codec, audio_map_in2_se,
+ ARRAY_SIZE(audio_map_in2_se));
+ snd_soc_add_controls(codec, wm9090_in2_se_controls,
+ ARRAY_SIZE(wm9090_in2_se_controls));
+ }
+
+ if (wm9090->pdata.agc_ena) {
+ for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++)
+ snd_soc_write(codec, WM9090_AGC_CONTROL_0 + i,
+ wm9090->pdata.agc[i]);
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ WM9090_AGC_ENA, WM9090_AGC_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_3,
+ WM9090_AGC_ENA, 0);
+ }
+
+ return 0;
+
+}
+
+/*
+ * The machine driver should call this from their set_bias_level; if there
+ * isn't one then this can just be set as the set_bias_level function.
+ */
+static int wm9090_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 *reg_cache = codec->reg_cache;
+ int i, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, WM9090_ANTIPOP2, WM9090_VMID_ENA,
+ WM9090_VMID_ENA);
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_BIAS_ENA |
+ WM9090_VMID_RES_MASK,
+ WM9090_BIAS_ENA |
+ 1 << WM9090_VMID_RES_SHIFT);
+ msleep(1); /* Probably an overestimate */
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Restore the register cache */
+ for (i = 1; i < codec->reg_cache_size; i++) {
+ if (reg_cache[i] == wm9090_reg_defaults[i])
+ continue;
+ if (wm9090_volatile(i))
+ continue;
+
+ ret = snd_soc_write(codec, i, reg_cache[i]);
+ if (ret != 0)
+ dev_warn(codec->dev,
+ "Failed to restore register %d: %d\n",
+ i, ret);
+ }
+ }
+
+ /* We keep VMID off during standby since the combination of
+ * ground referenced outputs and class D speaker mean that
+ * latency is not an issue.
+ */
+ snd_soc_update_bits(codec, WM9090_POWER_MANAGEMENT_1,
+ WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0);
+ snd_soc_update_bits(codec, WM9090_ANTIPOP2,
+ WM9090_VMID_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static int wm9090_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm9090_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm9090_codec;
+ codec = wm9090_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;
+ }
+
+ wm9090_add_controls(codec);
+
+ return 0;
+
+pcm_err:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int wm9090_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;
+
+ wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm9090_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define wm9090_suspend NULL
+#define wm9090_resume NULL
+#endif
+
+static int wm9090_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_wm9090 = {
+ .probe = wm9090_probe,
+ .remove = wm9090_remove,
+ .suspend = wm9090_suspend,
+ .resume = wm9090_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9090);
+
+static int wm9090_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm9090_priv *wm9090;
+ struct snd_soc_codec *codec;
+ int ret;
+
+ wm9090 = kzalloc(sizeof(*wm9090), GFP_KERNEL);
+ if (wm9090 == NULL) {
+ dev_err(&i2c->dev, "Can not allocate memory\n");
+ return -ENOMEM;
+ }
+ codec = &wm9090->codec;
+
+ if (i2c->dev.platform_data)
+ memcpy(&wm9090->pdata, i2c->dev.platform_data,
+ sizeof(wm9090->pdata));
+
+ wm9090_codec = codec;
+
+ i2c_set_clientdata(i2c, wm9090);
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->control_data = i2c;
+ snd_soc_codec_set_drvdata(codec, wm9090);
+ codec->dev = &i2c->dev;
+ codec->name = "WM9090";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm9090_set_bias_level,
+ codec->reg_cache_size = WM9090_MAX_REGISTER + 1;
+ codec->reg_cache = &wm9090->reg_cache;
+ codec->volatile_register = wm9090_volatile;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ memcpy(&wm9090->reg_cache, wm9090_reg_defaults,
+ sizeof(wm9090->reg_cache));
+
+ ret = snd_soc_read(codec, WM9090_SOFTWARE_RESET);
+ if (ret < 0)
+ goto err;
+ if (ret != wm9090_reg_defaults[WM9090_SOFTWARE_RESET]) {
+ dev_err(&i2c->dev, "Device is not a WM9090, ID=%x\n", ret);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = snd_soc_write(codec, WM9090_SOFTWARE_RESET, 0);
+ if (ret < 0)
+ goto err;
+
+ /* Configure some defaults; they will be written out when we
+ * bring the bias up.
+ */
+ wm9090->reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
+ | WM9090_IN1A_ZC;
+ wm9090->reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
+ | WM9090_IN1B_ZC;
+ wm9090->reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
+ | WM9090_IN2A_ZC;
+ wm9090->reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
+ | WM9090_IN2B_ZC;
+ wm9090->reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
+ WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
+ wm9090->reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
+ WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
+ wm9090->reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
+ WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
+
+ wm9090->reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
+
+ wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+ goto err_bias;
+ }
+
+ return 0;
+
+err_bias:
+ wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err:
+ kfree(wm9090);
+ i2c_set_clientdata(i2c, NULL);
+ wm9090_codec = NULL;
+
+ return ret;
+}
+
+static int wm9090_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm9090_priv *wm9090 = i2c_get_clientdata(i2c);
+ struct snd_soc_codec *codec = &wm9090->codec;
+
+ snd_soc_unregister_codec(codec);
+ wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ kfree(wm9090);
+ wm9090_codec = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id wm9090_id[] = {
+ { "wm9090", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm9090_id);
+
+static struct i2c_driver wm9090_i2c_driver = {
+ .driver = {
+ .name = "wm9090",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm9090_i2c_probe,
+ .remove = __devexit_p(wm9090_i2c_remove),
+ .id_table = wm9090_id,
+};
+
+static int __init wm9090_init(void)
+{
+ return i2c_add_driver(&wm9090_i2c_driver);
+}
+module_init(wm9090_init);
+
+static void __exit wm9090_exit(void)
+{
+ i2c_del_driver(&wm9090_i2c_driver);
+}
+module_exit(wm9090_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM9090 ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9090.h b/sound/soc/codecs/wm9090.h
new file mode 100644
index 000000000000..b08eab932a5b
--- /dev/null
+++ b/sound/soc/codecs/wm9090.h
@@ -0,0 +1,715 @@
+/*
+ * ALSA SoC WM9090 driver
+ *
+ * Copyright 2009 Wolfson Microelectronics
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __WM9090_H
+#define __WM9090_H
+
+extern struct snd_soc_codec_device soc_codec_dev_wm9090;
+
+/*
+ * Register values.
+ */
+#define WM9090_SOFTWARE_RESET 0x00
+#define WM9090_POWER_MANAGEMENT_1 0x01
+#define WM9090_POWER_MANAGEMENT_2 0x02
+#define WM9090_POWER_MANAGEMENT_3 0x03
+#define WM9090_CLOCKING_1 0x06
+#define WM9090_IN1_LINE_CONTROL 0x16
+#define WM9090_IN2_LINE_CONTROL 0x17
+#define WM9090_IN1_LINE_INPUT_A_VOLUME 0x18
+#define WM9090_IN1_LINE_INPUT_B_VOLUME 0x19
+#define WM9090_IN2_LINE_INPUT_A_VOLUME 0x1A
+#define WM9090_IN2_LINE_INPUT_B_VOLUME 0x1B
+#define WM9090_LEFT_OUTPUT_VOLUME 0x1C
+#define WM9090_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM9090_SPKMIXL_ATTENUATION 0x22
+#define WM9090_SPKOUT_MIXERS 0x24
+#define WM9090_CLASSD3 0x25
+#define WM9090_SPEAKER_VOLUME_LEFT 0x26
+#define WM9090_OUTPUT_MIXER1 0x2D
+#define WM9090_OUTPUT_MIXER2 0x2E
+#define WM9090_OUTPUT_MIXER3 0x2F
+#define WM9090_OUTPUT_MIXER4 0x30
+#define WM9090_SPEAKER_MIXER 0x36
+#define WM9090_ANTIPOP2 0x39
+#define WM9090_WRITE_SEQUENCER_0 0x46
+#define WM9090_WRITE_SEQUENCER_1 0x47
+#define WM9090_WRITE_SEQUENCER_2 0x48
+#define WM9090_WRITE_SEQUENCER_3 0x49
+#define WM9090_WRITE_SEQUENCER_4 0x4A
+#define WM9090_WRITE_SEQUENCER_5 0x4B
+#define WM9090_CHARGE_PUMP_1 0x4C
+#define WM9090_DC_SERVO_0 0x54
+#define WM9090_DC_SERVO_1 0x55
+#define WM9090_DC_SERVO_3 0x57
+#define WM9090_DC_SERVO_READBACK_0 0x58
+#define WM9090_DC_SERVO_READBACK_1 0x59
+#define WM9090_DC_SERVO_READBACK_2 0x5A
+#define WM9090_ANALOGUE_HP_0 0x60
+#define WM9090_AGC_CONTROL_0 0x62
+#define WM9090_AGC_CONTROL_1 0x63
+#define WM9090_AGC_CONTROL_2 0x64
+
+#define WM9090_REGISTER_COUNT 40
+#define WM9090_MAX_REGISTER 0x64
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM9090_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM9090_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM9090_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM9090_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */
+#define WM9090_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */
+#define WM9090_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM9090_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */
+#define WM9090_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM9090_OSC_ENA 0x0008 /* OSC_ENA */
+#define WM9090_OSC_ENA_MASK 0x0008 /* OSC_ENA */
+#define WM9090_OSC_ENA_SHIFT 3 /* OSC_ENA */
+#define WM9090_OSC_ENA_WIDTH 1 /* OSC_ENA */
+#define WM9090_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */
+#define WM9090_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */
+#define WM9090_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */
+#define WM9090_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM9090_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM9090_TSHUT 0x8000 /* TSHUT */
+#define WM9090_TSHUT_MASK 0x8000 /* TSHUT */
+#define WM9090_TSHUT_SHIFT 15 /* TSHUT */
+#define WM9090_TSHUT_WIDTH 1 /* TSHUT */
+#define WM9090_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */
+#define WM9090_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */
+#define WM9090_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */
+#define WM9090_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */
+#define WM9090_IN1A_ENA 0x0080 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_MASK 0x0080 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_SHIFT 7 /* IN1A_ENA */
+#define WM9090_IN1A_ENA_WIDTH 1 /* IN1A_ENA */
+#define WM9090_IN1B_ENA 0x0040 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_MASK 0x0040 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_SHIFT 6 /* IN1B_ENA */
+#define WM9090_IN1B_ENA_WIDTH 1 /* IN1B_ENA */
+#define WM9090_IN2A_ENA 0x0020 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_MASK 0x0020 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_SHIFT 5 /* IN2A_ENA */
+#define WM9090_IN2A_ENA_WIDTH 1 /* IN2A_ENA */
+#define WM9090_IN2B_ENA 0x0010 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_MASK 0x0010 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_SHIFT 4 /* IN2B_ENA */
+#define WM9090_IN2B_ENA_WIDTH 1 /* IN2B_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM9090_AGC_ENA 0x4000 /* AGC_ENA */
+#define WM9090_AGC_ENA_MASK 0x4000 /* AGC_ENA */
+#define WM9090_AGC_ENA_SHIFT 14 /* AGC_ENA */
+#define WM9090_AGC_ENA_WIDTH 1 /* AGC_ENA */
+#define WM9090_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */
+#define WM9090_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */
+#define WM9090_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM9090_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */
+#define WM9090_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+#define WM9090_SPKMIX_ENA 0x0008 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_MASK 0x0008 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_SHIFT 3 /* SPKMIX_ENA */
+#define WM9090_SPKMIX_ENA_WIDTH 1 /* SPKMIX_ENA */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM9090_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */
+#define WM9090_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */
+#define WM9090_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */
+#define WM9090_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+
+/*
+ * R22 (0x16) - IN1 Line Control
+ */
+#define WM9090_IN1_DIFF 0x0002 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_MASK 0x0002 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_SHIFT 1 /* IN1_DIFF */
+#define WM9090_IN1_DIFF_WIDTH 1 /* IN1_DIFF */
+#define WM9090_IN1_CLAMP 0x0001 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_MASK 0x0001 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_SHIFT 0 /* IN1_CLAMP */
+#define WM9090_IN1_CLAMP_WIDTH 1 /* IN1_CLAMP */
+
+/*
+ * R23 (0x17) - IN2 Line Control
+ */
+#define WM9090_IN2_DIFF 0x0002 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_MASK 0x0002 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_SHIFT 1 /* IN2_DIFF */
+#define WM9090_IN2_DIFF_WIDTH 1 /* IN2_DIFF */
+#define WM9090_IN2_CLAMP 0x0001 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_MASK 0x0001 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_SHIFT 0 /* IN2_CLAMP */
+#define WM9090_IN2_CLAMP_WIDTH 1 /* IN2_CLAMP */
+
+/*
+ * R24 (0x18) - IN1 Line Input A Volume
+ */
+#define WM9090_IN1_VU 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM9090_IN1A_MUTE 0x0080 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_MASK 0x0080 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_SHIFT 7 /* IN1A_MUTE */
+#define WM9090_IN1A_MUTE_WIDTH 1 /* IN1A_MUTE */
+#define WM9090_IN1A_ZC 0x0040 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_MASK 0x0040 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_SHIFT 6 /* IN1A_ZC */
+#define WM9090_IN1A_ZC_WIDTH 1 /* IN1A_ZC */
+#define WM9090_IN1A_VOL_MASK 0x0007 /* IN1A_VOL - [2:0] */
+#define WM9090_IN1A_VOL_SHIFT 0 /* IN1A_VOL - [2:0] */
+#define WM9090_IN1A_VOL_WIDTH 3 /* IN1A_VOL - [2:0] */
+
+/*
+ * R25 (0x19) - IN1 Line Input B Volume
+ */
+#define WM9090_IN1_VU 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM9090_IN1B_MUTE 0x0080 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_MASK 0x0080 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_SHIFT 7 /* IN1B_MUTE */
+#define WM9090_IN1B_MUTE_WIDTH 1 /* IN1B_MUTE */
+#define WM9090_IN1B_ZC 0x0040 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_MASK 0x0040 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_SHIFT 6 /* IN1B_ZC */
+#define WM9090_IN1B_ZC_WIDTH 1 /* IN1B_ZC */
+#define WM9090_IN1B_VOL_MASK 0x0007 /* IN1B_VOL - [2:0] */
+#define WM9090_IN1B_VOL_SHIFT 0 /* IN1B_VOL - [2:0] */
+#define WM9090_IN1B_VOL_WIDTH 3 /* IN1B_VOL - [2:0] */
+
+/*
+ * R26 (0x1A) - IN2 Line Input A Volume
+ */
+#define WM9090_IN2_VU 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM9090_IN2A_MUTE 0x0080 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_MASK 0x0080 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_SHIFT 7 /* IN2A_MUTE */
+#define WM9090_IN2A_MUTE_WIDTH 1 /* IN2A_MUTE */
+#define WM9090_IN2A_ZC 0x0040 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_MASK 0x0040 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_SHIFT 6 /* IN2A_ZC */
+#define WM9090_IN2A_ZC_WIDTH 1 /* IN2A_ZC */
+#define WM9090_IN2A_VOL_MASK 0x0007 /* IN2A_VOL - [2:0] */
+#define WM9090_IN2A_VOL_SHIFT 0 /* IN2A_VOL - [2:0] */
+#define WM9090_IN2A_VOL_WIDTH 3 /* IN2A_VOL - [2:0] */
+
+/*
+ * R27 (0x1B) - IN2 Line Input B Volume
+ */
+#define WM9090_IN2_VU 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM9090_IN2B_MUTE 0x0080 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_MASK 0x0080 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_SHIFT 7 /* IN2B_MUTE */
+#define WM9090_IN2B_MUTE_WIDTH 1 /* IN2B_MUTE */
+#define WM9090_IN2B_ZC 0x0040 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_MASK 0x0040 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_SHIFT 6 /* IN2B_ZC */
+#define WM9090_IN2B_ZC_WIDTH 1 /* IN2B_ZC */
+#define WM9090_IN2B_VOL_MASK 0x0007 /* IN2B_VOL - [2:0] */
+#define WM9090_IN2B_VOL_SHIFT 0 /* IN2B_VOL - [2:0] */
+#define WM9090_IN2B_VOL_WIDTH 3 /* IN2B_VOL - [2:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM9090_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM9090_HPOUT1L_MUTE 0x0040 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_MASK 0x0040 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_SHIFT 6 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_MUTE_WIDTH 1 /* HPOUT1L_MUTE */
+#define WM9090_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */
+#define WM9090_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */
+#define WM9090_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM9090_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM9090_HPOUT1R_MUTE 0x0040 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_MASK 0x0040 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_SHIFT 6 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_MUTE_WIDTH 1 /* HPOUT1R_MUTE */
+#define WM9090_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */
+#define WM9090_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */
+#define WM9090_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM9090_SPKMIX_MUTE 0x0100 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_MASK 0x0100 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_SHIFT 8 /* SPKMIX_MUTE */
+#define WM9090_SPKMIX_MUTE_WIDTH 1 /* SPKMIX_MUTE */
+#define WM9090_IN1A_SPKMIX_VOL_MASK 0x00C0 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1A_SPKMIX_VOL_SHIFT 6 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1A_SPKMIX_VOL_WIDTH 2 /* IN1A_SPKMIX_VOL - [7:6] */
+#define WM9090_IN1B_SPKMIX_VOL_MASK 0x0030 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN1B_SPKMIX_VOL_SHIFT 4 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN1B_SPKMIX_VOL_WIDTH 2 /* IN1B_SPKMIX_VOL - [5:4] */
+#define WM9090_IN2A_SPKMIX_VOL_MASK 0x000C /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2A_SPKMIX_VOL_SHIFT 2 /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2A_SPKMIX_VOL_WIDTH 2 /* IN2A_SPKMIX_VOL - [3:2] */
+#define WM9090_IN2B_SPKMIX_VOL_MASK 0x0003 /* IN2B_SPKMIX_VOL - [1:0] */
+#define WM9090_IN2B_SPKMIX_VOL_SHIFT 0 /* IN2B_SPKMIX_VOL - [1:0] */
+#define WM9090_IN2B_SPKMIX_VOL_WIDTH 2 /* IN2B_SPKMIX_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM9090_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */
+#define WM9090_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM9090_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */
+#define WM9090_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM9090_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM9090_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM9090_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM9090_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM9090_SPKOUTL_MUTE 0x0040 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_MASK 0x0040 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_SHIFT 6 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_MUTE_WIDTH 1 /* SPKOUTL_MUTE */
+#define WM9090_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */
+#define WM9090_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */
+#define WM9090_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM9090_IN1A_TO_MIXOUTL 0x0040 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_MASK 0x0040 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_SHIFT 6 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN1A_TO_MIXOUTL_WIDTH 1 /* IN1A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL 0x0004 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_MASK 0x0004 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_SHIFT 2 /* IN2A_TO_MIXOUTL */
+#define WM9090_IN2A_TO_MIXOUTL_WIDTH 1 /* IN2A_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM9090_IN1A_TO_MIXOUTR 0x0040 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_MASK 0x0040 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_SHIFT 6 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1A_TO_MIXOUTR_WIDTH 1 /* IN1A_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR 0x0010 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_MASK 0x0010 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_SHIFT 4 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN1B_TO_MIXOUTR_WIDTH 1 /* IN1B_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR 0x0004 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_MASK 0x0004 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_SHIFT 2 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2A_TO_MIXOUTR_WIDTH 1 /* IN2A_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR 0x0001 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_MASK 0x0001 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_SHIFT 0 /* IN2B_TO_MIXOUTR */
+#define WM9090_IN2B_TO_MIXOUTR_WIDTH 1 /* IN2B_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM9090_MIXOUTL_MUTE 0x0100 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_MASK 0x0100 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_SHIFT 8 /* MIXOUTL_MUTE */
+#define WM9090_MIXOUTL_MUTE_WIDTH 1 /* MIXOUTL_MUTE */
+#define WM9090_IN1A_MIXOUTL_VOL_MASK 0x00C0 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTL_VOL_SHIFT 6 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTL_VOL_WIDTH 2 /* IN1A_MIXOUTL_VOL - [7:6] */
+#define WM9090_IN2A_MIXOUTL_VOL_MASK 0x000C /* IN2A_MIXOUTL_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTL_VOL_SHIFT 2 /* IN2A_MIXOUTL_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTL_VOL_WIDTH 2 /* IN2A_MIXOUTL_VOL - [3:2] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM9090_MIXOUTR_MUTE 0x0100 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_MASK 0x0100 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_SHIFT 8 /* MIXOUTR_MUTE */
+#define WM9090_MIXOUTR_MUTE_WIDTH 1 /* MIXOUTR_MUTE */
+#define WM9090_IN1A_MIXOUTR_VOL_MASK 0x00C0 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTR_VOL_SHIFT 6 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1A_MIXOUTR_VOL_WIDTH 2 /* IN1A_MIXOUTR_VOL - [7:6] */
+#define WM9090_IN1B_MIXOUTR_VOL_MASK 0x0030 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN1B_MIXOUTR_VOL_SHIFT 4 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN1B_MIXOUTR_VOL_WIDTH 2 /* IN1B_MIXOUTR_VOL - [5:4] */
+#define WM9090_IN2A_MIXOUTR_VOL_MASK 0x000C /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTR_VOL_SHIFT 2 /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2A_MIXOUTR_VOL_WIDTH 2 /* IN2A_MIXOUTR_VOL - [3:2] */
+#define WM9090_IN2B_MIXOUTR_VOL_MASK 0x0003 /* IN2B_MIXOUTR_VOL - [1:0] */
+#define WM9090_IN2B_MIXOUTR_VOL_SHIFT 0 /* IN2B_MIXOUTR_VOL - [1:0] */
+#define WM9090_IN2B_MIXOUTR_VOL_WIDTH 2 /* IN2B_MIXOUTR_VOL - [1:0] */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM9090_IN1A_TO_SPKMIX 0x0040 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_MASK 0x0040 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_SHIFT 6 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1A_TO_SPKMIX_WIDTH 1 /* IN1A_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX 0x0010 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_MASK 0x0010 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_SHIFT 4 /* IN1B_TO_SPKMIX */
+#define WM9090_IN1B_TO_SPKMIX_WIDTH 1 /* IN1B_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX 0x0004 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_MASK 0x0004 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_SHIFT 2 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2A_TO_SPKMIX_WIDTH 1 /* IN2A_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX 0x0001 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_MASK 0x0001 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_SHIFT 0 /* IN2B_TO_SPKMIX */
+#define WM9090_IN2B_TO_SPKMIX_WIDTH 1 /* IN2B_TO_SPKMIX */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM9090_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */
+#define WM9090_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM9090_VMID_ENA 0x0001 /* VMID_ENA */
+#define WM9090_VMID_ENA_MASK 0x0001 /* VMID_ENA */
+#define WM9090_VMID_ENA_SHIFT 0 /* VMID_ENA */
+#define WM9090_VMID_ENA_WIDTH 1 /* VMID_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM9090_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM9090_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM9090_WSEQ_WRITE_INDEX_MASK 0x000F /* WSEQ_WRITE_INDEX - [3:0] */
+#define WM9090_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [3:0] */
+#define WM9090_WSEQ_WRITE_INDEX_WIDTH 4 /* WSEQ_WRITE_INDEX - [3:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM9090_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM9090_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM9090_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM9090_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM9090_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM9090_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM9090_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM9090_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM9090_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM9090_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM9090_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM9090_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM9090_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM9090_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM9090_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM9090_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM9090_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM9090_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM9090_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM9090_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM9090_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM9090_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM9090_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM9090_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM9090_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM9090_CP_ENA 0x8000 /* CP_ENA */
+#define WM9090_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM9090_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM9090_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM9090_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM9090_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM9090_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM9090_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM9090_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM9090_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM9090_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM9090_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+#define WM9090_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM9090_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM9090_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM9090_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM9090_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM9090_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM9090_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM9090_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM9090_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM9090_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM9090_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM9090_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM9090_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM9090_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_SHIFT 0 /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_1_RD_WIDTH 8 /* DCS_DAC_WR_VAL_1_RD - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_SHIFT 0 /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+#define WM9090_DCS_DAC_WR_VAL_0_RD_WIDTH 8 /* DCS_DAC_WR_VAL_0_RD - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM9090_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM9090_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM9090_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM9090_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM9090_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM9090_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM9090_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - AGC Control 0
+ */
+#define WM9090_AGC_CLIP_ENA 0x8000 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_MASK 0x8000 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_SHIFT 15 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_ENA_WIDTH 1 /* AGC_CLIP_ENA */
+#define WM9090_AGC_CLIP_THR_MASK 0x0F00 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_THR_SHIFT 8 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_THR_WIDTH 4 /* AGC_CLIP_THR - [11:8] */
+#define WM9090_AGC_CLIP_ATK_MASK 0x0070 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_ATK_SHIFT 4 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_ATK_WIDTH 3 /* AGC_CLIP_ATK - [6:4] */
+#define WM9090_AGC_CLIP_DCY_MASK 0x0007 /* AGC_CLIP_DCY - [2:0] */
+#define WM9090_AGC_CLIP_DCY_SHIFT 0 /* AGC_CLIP_DCY - [2:0] */
+#define WM9090_AGC_CLIP_DCY_WIDTH 3 /* AGC_CLIP_DCY - [2:0] */
+
+/*
+ * R99 (0x63) - AGC Control 1
+ */
+#define WM9090_AGC_PWR_ENA 0x8000 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_MASK 0x8000 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_SHIFT 15 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_ENA_WIDTH 1 /* AGC_PWR_ENA */
+#define WM9090_AGC_PWR_AVG 0x1000 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_MASK 0x1000 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_SHIFT 12 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_AVG_WIDTH 1 /* AGC_PWR_AVG */
+#define WM9090_AGC_PWR_THR_MASK 0x0F00 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_THR_SHIFT 8 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_THR_WIDTH 4 /* AGC_PWR_THR - [11:8] */
+#define WM9090_AGC_PWR_ATK_MASK 0x0070 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_ATK_SHIFT 4 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_ATK_WIDTH 3 /* AGC_PWR_ATK - [6:4] */
+#define WM9090_AGC_PWR_DCY_MASK 0x0007 /* AGC_PWR_DCY - [2:0] */
+#define WM9090_AGC_PWR_DCY_SHIFT 0 /* AGC_PWR_DCY - [2:0] */
+#define WM9090_AGC_PWR_DCY_WIDTH 3 /* AGC_PWR_DCY - [2:0] */
+
+/*
+ * R100 (0x64) - AGC Control 2
+ */
+#define WM9090_AGC_RAMP 0x0100 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_MASK 0x0100 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_SHIFT 8 /* AGC_RAMP */
+#define WM9090_AGC_RAMP_WIDTH 1 /* AGC_RAMP */
+#define WM9090_AGC_MINGAIN_MASK 0x003F /* AGC_MINGAIN - [5:0] */
+#define WM9090_AGC_MINGAIN_SHIFT 0 /* AGC_MINGAIN - [5:0] */
+#define WM9090_AGC_MINGAIN_WIDTH 6 /* AGC_MINGAIN - [5:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index ec54c6da9856..8793341849d1 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -10,6 +10,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index e237bf615129..28790a2ffe8d 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -11,6 +11,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -631,9 +632,6 @@ static int wm9712_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
- wm9712_set_bias_level(codec, SND_SOC_BIAS_ON);
-
return ret;
}
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index c58aab375edb..34e0c91092fa 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -16,6 +16,7 @@
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -23,13 +24,12 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
+#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "wm9713.h"
-#define WM9713_VERSION "0.15"
-
struct wm9713_priv {
u32 pll_in; /* PLL input frequency */
};
@@ -115,15 +115,27 @@ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18
SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
};
+static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0);
+static unsigned int mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
-SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv),
SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
-SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+ out_tlv),
SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
-SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
-SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
-SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
-SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv),
+SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv),
+SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv),
SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
@@ -133,7 +145,7 @@ SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
-SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
+SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv),
SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
@@ -154,28 +166,43 @@ SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
-SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
+SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv),
SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
-SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
+SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv),
-SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
+SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv),
SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
-SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
+SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv),
-SOC_SINGLE("Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
-SOC_SINGLE("Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
-SOC_SINGLE("Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
+SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv),
-SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
+SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1,
+ misc_tlv),
SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
+SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1,
+ misc_tlv),
+
+SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1,
+ misc_tlv),
+
+SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1,
+ misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1,
+ misc_tlv),
+
SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
-SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
SOC_ENUM("Bass Control", wm9713_enum[16]),
SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
@@ -737,7 +764,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
static int wm9713_set_pll(struct snd_soc_codec *codec,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
- struct wm9713_priv *wm9713 = codec->private_data;
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
u16 reg, reg2;
struct _pll_div pll_div;
@@ -1148,7 +1175,7 @@ static int wm9713_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- struct wm9713_priv *wm9713 = codec->private_data;
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
@@ -1174,9 +1201,6 @@ static int wm9713_soc_resume(struct platform_device *pdev)
}
}
- if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
- wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
-
return ret;
}
@@ -1186,8 +1210,6 @@ static int wm9713_soc_probe(struct platform_device *pdev)
struct snd_soc_codec *codec;
int ret = 0, reg;
- printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
-
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
GFP_KERNEL);
if (socdev->card->codec == NULL)
@@ -1203,8 +1225,9 @@ static int wm9713_soc_probe(struct platform_device *pdev)
codec->reg_cache_size = sizeof(wm9713_reg);
codec->reg_cache_step = 2;
- codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
- if (codec->private_data == NULL) {
+ snd_soc_codec_set_drvdata(codec, kzalloc(sizeof(struct wm9713_priv),
+ GFP_KERNEL));
+ if (snd_soc_codec_get_drvdata(codec) == NULL) {
ret = -ENOMEM;
goto priv_err;
}
@@ -1255,7 +1278,7 @@ pcm_err:
snd_soc_free_ac97_codec(codec);
codec_err:
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
priv_err:
kfree(codec->reg_cache);
@@ -1277,7 +1300,7 @@ static int wm9713_soc_remove(struct platform_device *pdev)
snd_soc_dapm_free(socdev);
snd_soc_free_pcms(socdev);
snd_soc_free_ac97_codec(codec);
- kfree(codec->private_data);
+ kfree(snd_soc_codec_get_drvdata(codec));
kfree(codec->reg_cache);
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index d73c30536a2c..16f1a57da08a 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -62,36 +62,112 @@ static const char *speaker_mode_text[] = {
static const struct soc_enum speaker_mode =
SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
-static void wait_for_dc_servo(struct snd_soc_codec *codec)
+static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
unsigned int reg;
int count = 0;
+ unsigned int val;
+
+ val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
+
+ /* Trigger the command */
+ snd_soc_write(codec, WM8993_DC_SERVO_0, val);
dev_dbg(codec->dev, "Waiting for DC servo...\n");
+
do {
count++;
msleep(1);
- reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
- dev_dbg(codec->dev, "DC servo status: %x\n", reg);
- } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
- != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
+ dev_dbg(codec->dev, "DC servo: %x\n", reg);
+ } while (reg & op && count < 400);
- if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
- != WM8993_DCS_CAL_COMPLETE_MASK)
+ if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo\n");
}
/*
+ * Startup calibration of the DC servo
+ */
+static void calibrate_dc_servo(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ u16 reg, reg_l, reg_r, dcs_cfg;
+
+ /* Set for 32 series updates */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_SERIES_NO_01_MASK,
+ 32 << WM8993_DCS_SERIES_NO_01_SHIFT);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1);
+
+ /* Apply correction to DC servo result */
+ if (hubs->dcs_codes) {
+ dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
+ hubs->dcs_codes);
+
+ /* Different chips in the family support different
+ * readback methods.
+ */
+ switch (hubs->dcs_readback_mode) {
+ case 0:
+ reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ & WM8993_DCS_INTEG_CHAN_0_MASK;;
+ reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ & WM8993_DCS_INTEG_CHAN_1_MASK;
+ break;
+ case 1:
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
+ reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ break;
+ default:
+ WARN(1, "Unknown DCS readback method");
+ break;
+ }
+
+ dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
+
+ /* HPOUT1L */
+ if (reg_l + hubs->dcs_codes > 0 &&
+ reg_l + hubs->dcs_codes < 0xff)
+ reg_l += hubs->dcs_codes;
+ dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+
+ /* HPOUT1R */
+ if (reg_r + hubs->dcs_codes > 0 &&
+ reg_r + hubs->dcs_codes < 0xff)
+ reg_r += hubs->dcs_codes;
+ dcs_cfg |= reg_r;
+
+ dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);
+
+ /* Do it */
+ snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_DAC_WR_0 |
+ WM8993_DCS_TRIG_DAC_WR_1);
+ }
+}
+
+/*
* Update the DC servo calibration on gain changes
*/
static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+ /* If we're applying an offset correction then updating the
+ * callibration would be likely to introduce further offsets. */
+ if (hubs->dcs_codes)
+ return ret;
+
/* Only need to do this if the outputs are active */
if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
& (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
@@ -251,6 +327,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
line_tlv),
};
+static int hp_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (hubs->hp_startup_mode) {
+ case 0:
+ break;
+ case 1:
+ /* Enable the headphone amp */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA);
+
+ /* Enable the second stage */
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown HP startup mode %d\n",
+ hubs->hp_startup_mode);
+ break;
+ }
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -271,14 +388,11 @@ static int hp_event(struct snd_soc_dapm_widget *w,
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
- /* Start the DC servo */
- snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
- 0xFFFF,
- WM8993_DCS_ENA_CHAN_0 |
- WM8993_DCS_ENA_CHAN_1 |
- WM8993_DCS_TRIG_STARTUP_1 |
- WM8993_DCS_TRIG_STARTUP_0);
- wait_for_dc_servo(codec);
+ /* Smallest supported update interval */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
+
+ calibrate_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
@@ -286,23 +400,19 @@ static int hp_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
- reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
- WM8993_HPOUT1L_DLY |
- WM8993_HPOUT1L_OUTP |
- WM8993_HPOUT1R_RMV_SHORT |
- WM8993_HPOUT1R_DLY |
- WM8993_HPOUT1R_OUTP);
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_OUTP |
+ WM8993_HPOUT1R_OUTP |
+ WM8993_HPOUT1L_RMV_SHORT |
+ WM8993_HPOUT1R_RMV_SHORT, 0);
- snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
- 0xffff, 0);
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY, 0);
- snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);
-
- snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
- WM8993_CP_ENA, 0);
break;
}
@@ -473,6 +583,8 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
NULL, 0,
hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -626,6 +738,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Headphone PGA", NULL, "Left Headphone Mux" },
{ "Headphone PGA", NULL, "Right Headphone Mux" },
{ "Headphone PGA", NULL, "CLK_SYS" },
+ { "Headphone PGA", NULL, "Headphone Supply" },
{ "HPOUT1L", NULL, "Headphone PGA" },
{ "HPOUT1R", NULL, "Headphone PGA" },
@@ -753,6 +866,12 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
WM8993_LINEOUT2_MODE,
WM8993_LINEOUT2_MODE);
+ /* If the line outputs are differential then we aren't presenting
+ * VMID as an output and can disable it.
+ */
+ if (lineout1_diff && lineout2_diff)
+ codec->idle_bias_off = 1;
+
if (lineout1fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 36d3fba1de8b..e51c16683589 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -18,6 +18,13 @@ struct snd_soc_codec;
extern const unsigned int wm_hubs_spkmix_tlv[];
+/* This *must* be the first element of the codec->private_data struct */
+struct wm_hubs_data {
+ int dcs_codes;
+ int dcs_readback_mode;
+ int hp_startup_mode;
+};
+
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,