aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/88pm860x-codec.c15
-rw-r--r--sound/soc/codecs/Kconfig39
-rw-r--r--sound/soc/codecs/Makefile22
-rw-r--r--sound/soc/codecs/ab8500-codec.c85
-rw-r--r--sound/soc/codecs/ab8500-codec.h42
-rw-r--r--sound/soc/codecs/ac97.c22
-rw-r--r--sound/soc/codecs/ad1980.c55
-rw-r--r--sound/soc/codecs/ad73311.c22
-rw-r--r--sound/soc/codecs/adau1701.c324
-rw-r--r--sound/soc/codecs/adav80x.c13
-rw-r--r--sound/soc/codecs/ads117x.c29
-rw-r--r--sound/soc/codecs/ak4104.c34
-rw-r--r--sound/soc/codecs/ak4554.c106
-rw-r--r--sound/soc/codecs/ak5386.c17
-rw-r--r--sound/soc/codecs/arizona.c76
-rw-r--r--sound/soc/codecs/arizona.h8
-rw-r--r--sound/soc/codecs/bt-sco.c91
-rw-r--r--sound/soc/codecs/cs4270.c20
-rw-r--r--sound/soc/codecs/cs4271.c30
-rw-r--r--sound/soc/codecs/cs42l52.c5
-rw-r--r--sound/soc/codecs/dfbmcs320.c62
-rw-r--r--sound/soc/codecs/dmic.c17
-rw-r--r--sound/soc/codecs/hdmi.c (renamed from sound/soc/codecs/omap-hdmi.c)54
-rw-r--r--sound/soc/codecs/jz4740.c2
-rw-r--r--sound/soc/codecs/lm4857.c107
-rw-r--r--sound/soc/codecs/max9768.c16
-rw-r--r--sound/soc/codecs/max98088.c2
-rw-r--r--sound/soc/codecs/max98090.c34
-rw-r--r--sound/soc/codecs/max9877.c294
-rw-r--r--sound/soc/codecs/mc13783.c5
-rw-r--r--sound/soc/codecs/pcm1681.c339
-rw-r--r--sound/soc/codecs/pcm1792a.c257
-rw-r--r--sound/soc/codecs/pcm1792a.h26
-rw-r--r--sound/soc/codecs/pcm3008.c150
-rw-r--r--sound/soc/codecs/rt5640.c2211
-rw-r--r--sound/soc/codecs/rt5640.h2104
-rw-r--r--sound/soc/codecs/sgtl5000.c298
-rw-r--r--sound/soc/codecs/sgtl5000.h4
-rw-r--r--sound/soc/codecs/si476x.c20
-rw-r--r--sound/soc/codecs/sn95031.c2
-rw-r--r--sound/soc/codecs/spdif_receiver.c27
-rw-r--r--sound/soc/codecs/spdif_transmitter.c (renamed from sound/soc/codecs/spdif_transciever.c)28
-rw-r--r--sound/soc/codecs/ssm2518.c856
-rw-r--r--sound/soc/codecs/ssm2518.h20
-rw-r--r--sound/soc/codecs/ssm2602.c3
-rw-r--r--sound/soc/codecs/sta32x.c10
-rw-r--r--sound/soc/codecs/stac9766.c26
-rw-r--r--sound/soc/codecs/tas5086.c330
-rw-r--r--sound/soc/codecs/tlv320aic26.c51
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c22
-rw-r--r--sound/soc/codecs/tlv320aic3x.c62
-rw-r--r--sound/soc/codecs/twl4030.c2
-rw-r--r--sound/soc/codecs/twl6040.c116
-rw-r--r--sound/soc/codecs/uda134x.c88
-rw-r--r--sound/soc/codecs/wl1273.c17
-rw-r--r--sound/soc/codecs/wm0010.c46
-rw-r--r--sound/soc/codecs/wm5102.c258
-rw-r--r--sound/soc/codecs/wm5110.c227
-rw-r--r--sound/soc/codecs/wm8350.c6
-rw-r--r--sound/soc/codecs/wm8400.c9
-rw-r--r--sound/soc/codecs/wm8727.c17
-rw-r--r--sound/soc/codecs/wm8731.c60
-rw-r--r--sound/soc/codecs/wm8753.c5
-rw-r--r--sound/soc/codecs/wm8782.c17
-rw-r--r--sound/soc/codecs/wm8903.c10
-rw-r--r--sound/soc/codecs/wm8904.c12
-rw-r--r--sound/soc/codecs/wm8960.c10
-rw-r--r--sound/soc/codecs/wm8962.c154
-rw-r--r--sound/soc/codecs/wm8978.c1
-rw-r--r--sound/soc/codecs/wm8990.c11
-rw-r--r--sound/soc/codecs/wm8991.h9
-rw-r--r--sound/soc/codecs/wm8994.c225
-rw-r--r--sound/soc/codecs/wm8994.h3
-rw-r--r--sound/soc/codecs/wm8995.c5
-rw-r--r--sound/soc/codecs/wm8995.h7
-rw-r--r--sound/soc/codecs/wm8997.c1175
-rw-r--r--sound/soc/codecs/wm8997.h23
-rw-r--r--sound/soc/codecs/wm9705.c16
-rw-r--r--sound/soc/codecs/wm9712.c18
-rw-r--r--sound/soc/codecs/wm9713.c18
-rw-r--r--sound/soc/codecs/wm_adsp.c407
-rw-r--r--sound/soc/codecs/wm_adsp.h14
-rw-r--r--sound/soc/codecs/wm_hubs.c14
83 files changed, 10382 insertions, 1112 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 60159c0..8af0434 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,10 +120,8 @@
* before DAC & PGA in DAPM power-off sequence.
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrol_news = NULL, \
- .num_kcontrols = 0, .event = wevent, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
struct pm860x_det {
struct snd_soc_jack *hp_jack;
@@ -1444,7 +1442,7 @@ static int pm860x_codec_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Failed to get IRQ resources\n");
- goto out;
+ return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
@@ -1454,19 +1452,14 @@ static int pm860x_codec_probe(struct platform_device *pdev)
pm860x_dai, ARRAY_SIZE(pm860x_dai));
if (ret) {
dev_err(&pdev->dev, "Failed to register codec\n");
- goto out;
+ return -EINVAL;
}
return ret;
-
-out:
- platform_set_drvdata(pdev, NULL);
- return -EINVAL;
}
static int pm860x_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 2f45f00..b33b45d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -10,6 +10,7 @@ config SND_SOC_I2C_AND_SPI
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
+ depends on COMPILE_TEST
select SND_SOC_88PM860X if MFD_88PM860X
select SND_SOC_L3
select SND_SOC_AB8500_CODEC if ABX500_CORE
@@ -19,7 +20,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
- select SND_SOC_ADAV80X
+ select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
+ select SND_SOC_ADAU1701 if I2C
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -40,7 +42,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA7213 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
- select SND_SOC_DFBMCS320
+ select SND_SOC_BT_SCO
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@@ -53,13 +55,17 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
- select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_PCM1681 if I2C
+ select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_RT5631 if I2C
+ select SND_SOC_RT5640 if I2C
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
+ select SND_SOC_SSM2518 if I2C
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STA32X if I2C
select SND_SOC_STA529 if I2C
@@ -101,7 +107,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
- select SND_SOC_WM8903 if I2C && GENERIC_HARDIRQS
+ select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
@@ -120,6 +126,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8996 if I2C
+ select SND_SOC_WM8997 if MFD_WM8997
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -143,8 +150,10 @@ config SND_SOC_ARIZONA
tristate
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
+ default y if SND_SOC_WM8997=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
+ default m if SND_SOC_WM8997=m
config SND_SOC_WM_HUBS
tristate
@@ -196,6 +205,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4554
+ tristate
+
config SND_SOC_AK4641
tristate
@@ -263,7 +275,7 @@ config SND_SOC_DA732X
config SND_SOC_DA9055
tristate
-config SND_SOC_DFBMCS320
+config SND_SOC_BT_SCO
tristate
config SND_SOC_DMIC
@@ -287,7 +299,13 @@ config SND_SOC_MAX98095
config SND_SOC_MAX9850
tristate
-config SND_SOC_OMAP_HDMI_CODEC
+config SND_SOC_HDMI_CODEC
+ tristate
+
+config SND_SOC_PCM1681
+ tristate
+
+config SND_SOC_PCM1792A
tristate
config SND_SOC_PCM3008
@@ -296,6 +314,9 @@ config SND_SOC_PCM3008
config SND_SOC_RT5631
tristate
+config SND_SOC_RT5640
+ tristate
+
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate
@@ -313,6 +334,9 @@ config SND_SOC_SN95031
config SND_SOC_SPDIF
tristate
+config SND_SOC_SSM2518
+ tristate
+
config SND_SOC_SSM2602
tristate
@@ -492,6 +516,9 @@ config SND_SOC_WM8995
config SND_SOC_WM8996
tristate
+config SND_SOC_WM8997
+ tristate
+
config SND_SOC_WM9081
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b9e41c9..bc12676 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -11,6 +11,7 @@ snd-soc-adav80x-objs := adav80x.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4554-objs := ak4554.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
@@ -27,7 +28,7 @@ snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
-snd-soc-dfbmcs320-objs := dfbmcs320.o
+snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
@@ -41,17 +42,21 @@ snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
-snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
+snd-soc-hdmi-codec-objs := hdmi.o
+snd-soc-pcm1681-objs := pcm1681.o
+snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-rt5631-objs := rt5631.o
+snd-soc-rt5640-objs := rt5640.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-si476x-objs := si476x.o
snd-soc-sn95031-objs := sn95031.o
-snd-soc-spdif-tx-objs := spdif_transciever.o
+snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
+snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta529-objs := sta529.o
@@ -112,6 +117,7 @@ snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
+snd-soc-wm8997-objs := wm8997.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
@@ -136,6 +142,7 @@ obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
@@ -154,7 +161,7 @@ obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
-obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
+obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
@@ -168,14 +175,18 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
-obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
+obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
@@ -235,6 +246,7 @@ obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
+obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index a153b16..b8ba0ad 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1496,6 +1496,12 @@ static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
"AD_OUT7",
"AD_OUT8",
"zeroes",
+ "zeroes",
+ "zeroes",
+ "zeroes",
+ "tristate",
+ "tristate",
+ "tristate",
"tristate"};
static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
@@ -2230,7 +2236,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
- unsigned int val, mask, slots_active;
+ unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
BIT(AB8500_DIGIFCONF2_IF0WL1);
@@ -2286,27 +2292,34 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
+
+ if (tx_mask & ~0xff)
+ return -EINVAL;
+
mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+ tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
+
dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
+
switch (slots_active) {
case 0:
break;
case 1:
- /* Slot 9 -> DA_IN1 & DA_IN3 */
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
+ slot = find_first_bit((unsigned long *)&tx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 2:
- /* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
-
+ slot = find_first_bit((unsigned long *)&tx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ slot = find_next_bit((unsigned long *)&tx_mask, 32, slot + 1);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
+ snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 8:
dev_dbg(dai->codec->dev,
@@ -2321,25 +2334,36 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
}
/* Setup TDM AD according to active RX-slots */
+
+ if (rx_mask & ~0xff)
+ return -EINVAL;
+
+ rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
+
dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
+
switch (slots_active) {
case 0:
break;
case 1:
- /* AD_OUT3 -> slot 0 & 1 */
- snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL,
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD);
+ slot = find_first_bit((unsigned long *)&rx_mask, 32);
+ snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
break;
case 2:
- /* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */
+ slot = find_first_bit((unsigned long *)&rx_mask, 32);
snd_soc_update_bits(codec,
- AB8500_ADSLOTSEL1,
- AB8500_MASK_ALL,
- AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
- AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD);
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
+ slot = find_next_bit((unsigned long *)&rx_mask, 32, slot + 1);
+ snd_soc_update_bits(codec,
+ AB8500_ADSLOTSEL(slot),
+ AB8500_MASK_SLOT(slot),
+ AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
break;
case 8:
dev_dbg(dai->codec->dev,
@@ -2356,6 +2380,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static const struct snd_soc_dai_ops ab8500_codec_ops = {
+ .set_fmt = ab8500_codec_set_dai_fmt,
+ .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+};
+
static struct snd_soc_dai_driver ab8500_codec_dai[] = {
{
.name = "ab8500-codec-dai.0",
@@ -2367,12 +2396,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
- .ops = (struct snd_soc_dai_ops[]) {
- {
- .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
- .set_fmt = ab8500_codec_set_dai_fmt,
- }
- },
+ .ops = &ab8500_codec_ops,
.symmetric_rates = 1
},
{
@@ -2385,12 +2409,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
- .ops = (struct snd_soc_dai_ops[]) {
- {
- .set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
- .set_fmt = ab8500_codec_set_dai_fmt,
- }
- },
+ .ops = &ab8500_codec_ops,
.symmetric_rates = 1
}
};
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
index 306d0bc..e2e5442 100644
--- a/sound/soc/codecs/ab8500-codec.h
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -24,6 +24,13 @@
#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
+/* AB8500 interface slot offset definitions */
+
+#define AB8500_AD_DATA0_OFFSET 0
+#define AB8500_DA_DATA0_OFFSET 8
+#define AB8500_AD_DATA1_OFFSET 16
+#define AB8500_DA_DATA1_OFFSET 24
+
/* AB8500 audio bank (0x0d) register definitions */
#define AB8500_POWERUP 0x00
@@ -73,6 +80,7 @@
#define AB8500_ADSLOTSEL14 0x2C
#define AB8500_ADSLOTSEL15 0x2D
#define AB8500_ADSLOTSEL16 0x2E
+#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
#define AB8500_ADSLOTHIZCTRL1 0x2F
#define AB8500_ADSLOTHIZCTRL2 0x30
#define AB8500_ADSLOTHIZCTRL3 0x31
@@ -144,6 +152,7 @@
#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
#define AB8500_MASK_ALL 0xFF
+#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
#define AB8500_MASK_NONE 0x00
/* AB8500_POWERUP */
@@ -347,28 +356,21 @@
#define AB8500_DIGIFCONF4_IF1WL0 0
/* AB8500_ADSLOTSELX */
-#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD 0x00
-#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD 0x10
-#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD 0x20
-#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD 0x30
-#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD 0x40
-#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD 0x50
-#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD 0x60
-#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD 0x70
-#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD 0x80
-#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD 0xF0
-#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN 0x00
-#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN 0x01
-#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN 0x02
-#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN 0x03
-#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN 0x04
-#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN 0x05
-#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN 0x06
-#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN 0x07
-#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN 0x08
-#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN 0x0F
+#define AB8500_AD_OUT1 0x0
+#define AB8500_AD_OUT2 0x1
+#define AB8500_AD_OUT3 0x2
+#define AB8500_AD_OUT4 0x3
+#define AB8500_AD_OUT5 0x4
+#define AB8500_AD_OUT6 0x5
+#define AB8500_AD_OUT7 0x6
+#define AB8500_AD_OUT8 0x7
+#define AB8500_ZEROES 0x8
+#define AB8500_TRISTATE 0xF
#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
#define AB8500_ADSLOTSELX_ODD_SHIFT 4
+#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
+ ((out) << (((slot) & 1) ? \
+ AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
/* AB8500_ADSLOTHIZCTRL1 */
/* AB8500_ADSLOTHIZCTRL2 */
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index ef2ae32..8d9ba4b 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -23,6 +23,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget ac97_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ac97_routes[] = {
+ { "AC97 Capture", NULL, "RX" },
+ { "TX", NULL, "AC97 Playback" },
+};
+
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -62,13 +72,13 @@ static struct snd_soc_dai_driver ac97_dai = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
return 0;
}
@@ -79,7 +89,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+ ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
+ &ac97_bus);
if (ret < 0)
return ret;
@@ -116,6 +127,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.suspend = ac97_soc_suspend,
.resume = ac97_soc_resume,
+
+ .dapm_widgets = ac97_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+ .dapm_routes = ac97_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac97_routes),
};
static int ac97_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index f385342..7257a88 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -96,6 +96,44 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
};
+static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_INPUT("CD_L"),
+SND_SOC_DAPM_INPUT("CD_R"),
+SND_SOC_DAPM_INPUT("AUX_L"),
+SND_SOC_DAPM_INPUT("AUX_R"),
+SND_SOC_DAPM_INPUT("LINE_IN_L"),
+SND_SOC_DAPM_INPUT("LINE_IN_R"),
+
+SND_SOC_DAPM_OUTPUT("LFE_OUT"),
+SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
+SND_SOC_DAPM_OUTPUT("MONO_OUT"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
+};
+
+static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
+ { "Capture", NULL, "MIC1" },
+ { "Capture", NULL, "MIC2" },
+ { "Capture", NULL, "CD_L" },
+ { "Capture", NULL, "CD_R" },
+ { "Capture", NULL, "AUX_L" },
+ { "Capture", NULL, "AUX_R" },
+ { "Capture", NULL, "LINE_IN_L" },
+ { "Capture", NULL, "LINE_IN_R" },
+
+ { "LFE_OUT", NULL, "Playback" },
+ { "CENTER_OUT", NULL, "Playback" },
+ { "LINE_OUT_L", NULL, "Playback" },
+ { "LINE_OUT_R", NULL, "Playback" },
+ { "MONO_OUT", NULL, "Playback" },
+ { "HP_OUT_L", NULL, "Playback" },
+ { "HP_OUT_R", NULL, "Playback" },
+};
+
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -108,7 +146,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
case AC97_EXTENDED_STATUS:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -124,7 +162,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
@@ -154,13 +192,13 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
u16 retry_cnt = 0;
retry:
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, AC97_RESET) == 0x0090)
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
/* Set bit 16slot in register 74h, then every slot will has only 16
* bits. This command is sent out in 20bit mode, in which case the
* first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
@@ -186,7 +224,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
return ret;
@@ -253,6 +291,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
.reg_cache_step = 2,
.write = ac97_write,
.read = ac97_read,
+
+ .dapm_widgets = ad1980_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+ .dapm_routes = ad1980_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
};
static int ad1980_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index b1f2baf..5fac8ad 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -23,6 +23,21 @@
#include "ad73311.h"
+static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINP"),
+SND_SOC_DAPM_INPUT("VINN"),
+SND_SOC_DAPM_OUTPUT("VOUTN"),
+SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
+ { "Capture", NULL, "VINP" },
+ { "Capture", NULL, "VINN" },
+
+ { "VOUTN", NULL, "Playback" },
+ { "VOUTP", NULL, "Playback" },
+};
+
static struct snd_soc_dai_driver ad73311_dai = {
.name = "ad73311-hifi",
.playback = {
@@ -39,7 +54,12 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static struct snd_soc_codec_driver soc_codec_dev_ad73311;
+static struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
+ .dapm_widgets = ad73311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+ .dapm_routes = ad73311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+};
static int ad73311_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index dafdbe8..ebff112 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -13,6 +13,10 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -21,16 +25,19 @@
#include "sigmadsp.h"
#include "adau1701.h"
-#define ADAU1701_DSPCTRL 0x1c
-#define ADAU1701_SEROCTL 0x1e
-#define ADAU1701_SERICTL 0x1f
+#define ADAU1701_DSPCTRL 0x081c
+#define ADAU1701_SEROCTL 0x081e
+#define ADAU1701_SERICTL 0x081f
-#define ADAU1701_AUXNPOW 0x22
+#define ADAU1701_AUXNPOW 0x0822
+#define ADAU1701_PINCONF_0 0x0820
+#define ADAU1701_PINCONF_1 0x0821
+#define ADAU1701_AUXNPOW 0x0822
-#define ADAU1701_OSCIPOW 0x26
-#define ADAU1701_DACSET 0x27
+#define ADAU1701_OSCIPOW 0x0826
+#define ADAU1701_DACSET 0x0827
-#define ADAU1701_NUM_REGS 0x28
+#define ADAU1701_MAX_REGISTER 0x0828
#define ADAU1701_DSPCTRL_CR (1 << 2)
#define ADAU1701_DSPCTRL_DAM (1 << 3)
@@ -84,10 +91,18 @@
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
+#define ADAU1707_CLKDIV_UNSET (-1U)
+
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
+ int gpio_nreset;
+ int gpio_pll_mode[2];
unsigned int dai_fmt;
+ unsigned int pll_clkdiv;
+ unsigned int sysclk;
+ struct regmap *regmap;
+ u8 pin_config[12];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -119,10 +134,13 @@ static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
{ "ADC", NULL, "IN1" },
};
-static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
+static unsigned int adau1701_register_size(struct device *dev,
unsigned int reg)
{
switch (reg) {
+ case ADAU1701_PINCONF_0:
+ case ADAU1701_PINCONF_1:
+ return 3;
case ADAU1701_DSPCTRL:
case ADAU1701_SEROCTL:
case ADAU1701_AUXNPOW:
@@ -133,33 +151,42 @@ static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
return 1;
}
- dev_err(codec->dev, "Unsupported register address: %d\n", reg);
+ dev_err(dev, "Unsupported register address: %d\n", reg);
return 0;
}
-static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DACSET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+ unsigned int value)
{
+ struct i2c_client *client = context;
unsigned int i;
unsigned int size;
- uint8_t buf[4];
+ uint8_t buf[5];
int ret;
- size = adau1701_register_size(codec, reg);
+ size = adau1701_register_size(&client->dev, reg);
if (size == 0)
return -EINVAL;
- snd_soc_cache_write(codec, reg, value);
-
- buf[0] = 0x08;
- buf[1] = reg;
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
for (i = size + 1; i >= 2; --i) {
buf[i] = value;
value >>= 8;
}
- ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
+ ret = i2c_master_send(client, buf, size + 2);
if (ret == size + 2)
return 0;
else if (ret < 0)
@@ -168,21 +195,107 @@ static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
+static int adau1701_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
{
- unsigned int value;
- unsigned int ret;
+ int ret;
+ unsigned int i;
+ unsigned int size;
+ uint8_t send_buf[2], recv_buf[3];
+ struct i2c_client *client = context;
+ struct i2c_msg msgs[2];
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
- ret = snd_soc_cache_read(codec, reg, &value);
- if (ret)
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
- return value;
+ *value = 0;
+
+ for (i = 0; i < size; i++)
+ *value |= recv_buf[i] << (i * 8);
+
+ return 0;
}
-static int adau1701_load_firmware(struct snd_soc_codec *codec)
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
{
- return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+ int ret;
+
+ if (clkdiv != ADAU1707_CLKDIV_UNSET &&
+ gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
+ gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ switch (clkdiv) {
+ case 64:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 256:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ break;
+ case 384:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 0: /* fallback */
+ case 512:
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ break;
+ }
+ }
+
+ adau1701->pll_clkdiv = clkdiv;
+
+ if (gpio_is_valid(adau1701->gpio_nreset)) {
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ /* minimum reset time is 20ns */
+ udelay(1);
+ gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ /* power-up time may be as long as 85ms */
+ mdelay(85);
+ }
+
+ /*
+ * Postpone the firmware download to a point in time when we
+ * know the correct PLL setup
+ */
+ if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+ ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+ if (ret) {
+ dev_warn(codec->dev, "Failed to load firmware\n");
+ return ret;
+ }
+ }
+
+ regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ regcache_mark_dirty(adau1701->regmap);
+ regcache_sync(adau1701->regmap);
+
+ return 0;
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
@@ -221,7 +334,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
}
- snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
return 0;
}
@@ -249,7 +362,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_SERICTL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
ADAU1701_SERICTL_MODE_MASK, val);
return 0;
@@ -259,8 +372,22 @@ static int adau1701_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 adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int clkdiv = adau1701->sysclk / params_rate(params);
snd_pcm_format_t format;
unsigned int val;
+ int ret;
+
+ /*
+ * If the mclk/lrclk ratio changes, the chip needs updated PLL
+ * mode GPIO settings, and a full reset cycle, including a new
+ * firmware upload.
+ */
+ if (clkdiv != adau1701->pll_clkdiv) {
+ ret = adau1701_reset(codec, clkdiv);
+ if (ret < 0)
+ return ret;
+ }
switch (params_rate(params)) {
case 192000:
@@ -276,7 +403,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
ADAU1701_DSPCTRL_SR_MASK, val);
format = params_format(params);
@@ -352,8 +479,8 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- snd_soc_write(codec, ADAU1701_SERICTL, serictl);
- snd_soc_update_bits(codec, ADAU1701_SEROCTL,
+ regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
return 0;
@@ -363,6 +490,7 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -371,11 +499,13 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* Enable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, 0x00);
break;
case SND_SOC_BIAS_OFF:
/* Disable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, mask);
break;
}
@@ -387,6 +517,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (mute)
@@ -394,7 +525,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
else
val = mask;
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
return 0;
}
@@ -403,6 +534,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -415,7 +547,9 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+ ADAU1701_OSCIPOW_OPD, val);
+ adau1701->sysclk = freq;
return 0;
}
@@ -452,18 +586,45 @@ static struct snd_soc_dai_driver adau1701_dai = {
.symmetric_rates = 1,
};
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+ { .compatible = "adi,adau1701", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
static int adau1701_probe(struct snd_soc_codec *codec)
{
- int ret;
+ int i, ret;
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Let the pll_clkdiv variable default to something that won't happen
+ * at runtime. That way, we can postpone the firmware download from
+ * adau1701_reset() to a point in time when we know the correct PLL
+ * mode parameters.
+ */
+ adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
+
+ /* initalize with pre-configured pll mode settings */
+ ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+ if (ret < 0)
+ return ret;
+
+ /* set up pin config */
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i] << (i * 4);
- codec->control_data = to_i2c_client(codec->dev);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
- ret = adau1701_load_firmware(codec);
- if (ret)
- dev_warn(codec->dev, "Failed to load firmware\n");
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i + 6] << (i * 4);
- snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
- snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
return 0;
}
@@ -473,9 +634,6 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.set_bias_level = adau1701_set_bias_level,
.idle_bias_off = true,
- .reg_cache_size = ADAU1701_NUM_REGS,
- .reg_word_size = sizeof(u16),
-
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
@@ -483,22 +641,86 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
- .write = adau1701_write,
- .read = adau1701_read,
-
.set_sysclk = adau1701_set_sysclk,
};
+static const struct regmap_config adau1701_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = ADAU1701_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = adau1701_volatile_reg,
+ .reg_write = adau1701_reg_write,
+ .reg_read = adau1701_reg_read,
+};
+
static int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
+ struct device *dev = &client->dev;
+ int gpio_nreset = -EINVAL;
+ int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret;
- adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL);
+ adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
+ adau1701->regmap = devm_regmap_init(dev, NULL, client,
+ &adau1701_regmap);
+ if (IS_ERR(adau1701->regmap))
+ return PTR_ERR(adau1701->regmap);
+
+ if (dev->of_node) {
+ gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+ if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
+ return gpio_nreset;
+
+ gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 0);
+ if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
+ return gpio_pll_mode[0];
+
+ gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 1);
+ if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
+ return gpio_pll_mode[1];
+
+ of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+ &adau1701->pll_clkdiv);
+
+ of_property_read_u8_array(dev->of_node, "adi,pin-config",
+ adau1701->pin_config,
+ ARRAY_SIZE(adau1701->pin_config));
+ }
+
+ if (gpio_is_valid(gpio_nreset)) {
+ ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
+ "ADAU1701 Reset");
+ if (ret < 0)
+ return ret;
+ }
+
+ if (gpio_is_valid(gpio_pll_mode[0]) &&
+ gpio_is_valid(gpio_pll_mode[1])) {
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 0");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 1");
+ if (ret < 0)
+ return ret;
+ }
+
+ adau1701->gpio_nreset = gpio_nreset;
+ adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
+ adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
+
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
@@ -512,7 +734,10 @@ static int adau1701_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id adau1701_i2c_id[] = {
+ { "adau1401", 0 },
+ { "adau1401a", 0 },
{ "adau1701", 0 },
+ { "adau1702", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
@@ -521,6 +746,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
.remove = adau1701_i2c_remove,
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 3c839cc..15b012d0 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -868,6 +868,12 @@ static int adav80x_bus_remove(struct device *dev)
}
#if defined(CONFIG_SPI_MASTER)
+static const struct spi_device_id adav80x_spi_id[] = {
+ { "adav801", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
static int adav80x_spi_probe(struct spi_device *spi)
{
return adav80x_bus_probe(&spi->dev, SND_SOC_SPI);
@@ -885,15 +891,16 @@ static struct spi_driver adav80x_spi_driver = {
},
.probe = adav80x_spi_probe,
.remove = adav80x_spi_remove,
+ .id_table = adav80x_spi_id,
};
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static const struct i2c_device_id adav80x_id[] = {
+static const struct i2c_device_id adav80x_i2c_id[] = {
{ "adav803", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, adav80x_id);
+MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id);
static int adav80x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -913,7 +920,7 @@ static struct i2c_driver adav80x_i2c_driver = {
},
.probe = adav80x_i2c_probe,
.remove = adav80x_i2c_remove,
- .id_table = adav80x_id,
+ .id_table = adav80x_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 506d474..8f388ed 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -23,6 +23,28 @@
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Input1"),
+SND_SOC_DAPM_INPUT("Input2"),
+SND_SOC_DAPM_INPUT("Input3"),
+SND_SOC_DAPM_INPUT("Input4"),
+SND_SOC_DAPM_INPUT("Input5"),
+SND_SOC_DAPM_INPUT("Input6"),
+SND_SOC_DAPM_INPUT("Input7"),
+SND_SOC_DAPM_INPUT("Input8"),
+};
+
+static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
+ { "Capture", NULL, "Input1" },
+ { "Capture", NULL, "Input2" },
+ { "Capture", NULL, "Input3" },
+ { "Capture", NULL, "Input4" },
+ { "Capture", NULL, "Input5" },
+ { "Capture", NULL, "Input6" },
+ { "Capture", NULL, "Input7" },
+ { "Capture", NULL, "Input8" },
+};
+
static struct snd_soc_dai_driver ads117x_dai = {
/* ADC */
.name = "ads117x-hifi",
@@ -34,7 +56,12 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static struct snd_soc_codec_driver soc_codec_dev_ads117x;
+static struct snd_soc_codec_driver soc_codec_dev_ads117x = {
+ .dapm_widgets = ads117x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+ .dapm_routes = ads117x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+};
static int ads117x_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index c7cfdf9..71059c0 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -51,6 +51,17 @@ struct ak4104_private {
struct regmap *regmap;
};
+static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
+SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
+ { "TXE", NULL, "Playback" },
+ { "TX", NULL, "TXE" },
+};
+
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
@@ -138,29 +149,11 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- /* enable transmitter */
- ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
- AK4104_TX_TXE, AK4104_TX_TXE);
- if (ret < 0)
- return ret;
-
return 0;
}
-static int ak4104_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
-
- /* disable transmitter */
- return regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
- AK4104_TX_TXE, 0);
-}
-
static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
- .hw_free = ak4104_hw_free,
.set_fmt = ak4104_set_dai_fmt,
};
@@ -214,6 +207,11 @@ static int ak4104_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
.probe = ak4104_probe,
.remove = ak4104_remove,
+
+ .dapm_widgets = ak4104_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+ .dapm_routes = ak4104_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
};
static const struct regmap_config ak4104_regmap = {
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
new file mode 100644
index 0000000..79e9555
--- /dev/null
+++ b/sound/soc/codecs/ak4554.c
@@ -0,0 +1,106 @@
+/*
+ * ak4554.c
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 <sound/soc.h>
+
+/*
+ * ak4554 is very simple DA/AD converter which has no setting register.
+ *
+ * CAUTION
+ *
+ * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
+ * and, capture format is SND_SOC_DAIFMT_LEFT_J
+ * on same bit clock, LR clock.
+ * But, this driver doesn't have snd_soc_dai_ops :: set_fmt
+ *
+ * CPU/Codec DAI image
+ *
+ * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
+ * |
+ * CPU-DAI2 (capture only fmt = LEFT_J) ---+
+ */
+
+static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ak4554_dai = {
+ .name = "ak4554-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .symmetric_rates = 1,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
+ .dapm_widgets = ak4554_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+ .dapm_routes = ak4554_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+};
+
+static int ak4554_soc_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_ak4554,
+ &ak4554_dai, 1);
+}
+
+static int ak4554_soc_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct of_device_id ak4554_of_match[] = {
+ { .compatible = "asahi-kasei,ak4554" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4554_of_match);
+
+static struct platform_driver ak4554_driver = {
+ .driver = {
+ .name = "ak4554-adc-dac",
+ .owner = THIS_MODULE,
+ .of_match_table = ak4554_of_match,
+ },
+ .probe = ak4554_soc_probe,
+ .remove = ak4554_soc_remove,
+};
+module_platform_driver(ak4554_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoC AK4554 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index 1f30398..72e953b 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -22,7 +22,22 @@ struct ak5386_priv {
int reset_gpio;
};
-static struct snd_soc_codec_driver soc_codec_ak5386;
+static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
+static struct snd_soc_codec_driver soc_codec_ak5386 = {
+ .dapm_widgets = ak5386_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+ .dapm_routes = ak5386_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+};
static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 389f232..657808b 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -19,6 +19,7 @@
#include <sound/tlv.h>
#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/gpio.h>
#include <linux/mfd/arizona/registers.h>
#include "arizona.h"
@@ -199,9 +200,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
if (ret != 0)
return ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1);
- if (ret != 0)
- return ret;
+ switch (arizona->type) {
+ case WM8997:
+ break;
+ default:
+ ret = snd_soc_dapm_new_controls(&codec->dapm,
+ &arizona_spkr, 1);
+ if (ret != 0)
+ return ret;
+ break;
+ }
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN,
"Thermal warning", arizona_thermal_warn,
@@ -223,6 +231,41 @@ int arizona_init_spk(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_spk);
+int arizona_init_gpio(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ switch (arizona->type) {
+ case WM5110:
+ snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+ switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
+ case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
+ snd_soc_dapm_enable_pin(&codec->dapm,
+ "DRC1 Signal Activity");
+ break;
+ case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
+ snd_soc_dapm_enable_pin(&codec->dapm,
+ "DRC2 Signal Activity");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_gpio);
+
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
@@ -517,6 +560,26 @@ const struct soc_enum arizona_ng_hold =
4, arizona_ng_hold_text);
EXPORT_SYMBOL_GPL(arizona_ng_hold);
+static const char * const arizona_in_dmic_osr_text[] = {
+ "1.536MHz", "3.072MHz", "6.144MHz",
+};
+
+const struct soc_enum arizona_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT,
+ ARRAY_SIZE(arizona_in_dmic_osr_text),
+ arizona_in_dmic_osr_text),
+};
+EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -1198,6 +1261,13 @@ const struct snd_soc_dai_ops arizona_dai_ops = {
};
EXPORT_SYMBOL_GPL(arizona_dai_ops);
+const struct snd_soc_dai_ops arizona_simple_dai_ops = {
+ .startup = arizona_startup,
+ .hw_params = arizona_hw_params_rate,
+ .set_sysclk = arizona_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(arizona_simple_dai_ops);
+
int arizona_init_dai(struct arizona_priv *priv, int id)
{
struct arizona_dai_priv *dai_priv = &priv->dai[id];
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index af39f10..9e81b63 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 4
+#define ARIZONA_MAX_DAI 6
#define ARIZONA_MAX_ADSP 4
struct arizona;
@@ -150,7 +150,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
-#define ARIZONA_MUX_ROUTES(name) \
+#define ARIZONA_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Input")
#define ARIZONA_MIXER_ROUTES(widget, name) \
@@ -198,6 +199,7 @@ extern const struct soc_enum arizona_lhpf3_mode;
extern const struct soc_enum arizona_lhpf4_mode;
extern const struct soc_enum arizona_ng_hold;
+extern const struct soc_enum arizona_in_dmic_osr[];
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
@@ -213,6 +215,7 @@ extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
+extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
#define ARIZONA_FLL_NAME_LEN 20
@@ -241,6 +244,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
extern int arizona_init_spk(struct snd_soc_codec *codec);
+extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
new file mode 100644
index 0000000..c4cf069
--- /dev/null
+++ b/sound/soc/codecs/bt-sco.c
@@ -0,0 +1,91 @@
+/*
+ * Driver for generic Bluetooth SCO link
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route bt_sco_routes[] = {
+ { "Capture", NULL, "RX" },
+ { "TX", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver bt_sco_dai = {
+ .name = "bt-sco-pcm",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
+ .dapm_widgets = bt_sco_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+ .dapm_routes = bt_sco_routes,
+ .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+};
+
+static int bt_sco_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
+ &bt_sco_dai, 1);
+}
+
+static int bt_sco_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_device_id bt_sco_driver_ids[] = {
+ {
+ .name = "dfbmcs320",
+ },
+ {
+ .name = "bt-sco",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
+
+static struct platform_driver bt_sco_driver = {
+ .driver = {
+ .name = "bt-sco",
+ .owner = THIS_MODULE,
+ },
+ .probe = bt_sco_probe,
+ .remove = bt_sco_remove,
+ .id_table = bt_sco_driver_ids,
+};
+
+module_platform_driver(bt_sco_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ASoC generic bluethooth sco link driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 8e47798..83c835d 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -139,6 +139,22 @@ struct cs4270_private {
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
+static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA", NULL, "Playback" },
+ { "AOUTB", NULL, "Playback" },
+};
+
/**
* struct cs4270_mode_ratios - clock ratio tables
* @ratio: the ratio of MCLK to the sample rate
@@ -612,6 +628,10 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.controls = cs4270_snd_controls,
.num_controls = ARRAY_SIZE(cs4270_snd_controls),
+ .dapm_widgets = cs4270_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
+ .dapm_routes = cs4270_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
};
/*
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 03036b3..a20f1bb 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -173,6 +173,26 @@ struct cs4271_private {
bool enable_soft_reset;
};
+static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINA"),
+SND_SOC_DAPM_INPUT("AINB"),
+
+SND_SOC_DAPM_OUTPUT("AOUTA+"),
+SND_SOC_DAPM_OUTPUT("AOUTA-"),
+SND_SOC_DAPM_OUTPUT("AOUTB+"),
+SND_SOC_DAPM_OUTPUT("AOUTB-"),
+};
+
+static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
+ { "Capture", NULL, "AINA" },
+ { "Capture", NULL, "AINB" },
+
+ { "AOUTA+", NULL, "Playback" },
+ { "AOUTA-", NULL, "Playback" },
+ { "AOUTB+", NULL, "Playback" },
+ { "AOUTB-", NULL, "Playback" },
+};
+
/*
* @freq is the desired MCLK rate
* MCLK rate should (c) be the sample rate, multiplied by one of the
@@ -576,8 +596,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
CS4271_MODE2_MUTECAEQUB,
CS4271_MODE2_MUTECAEQUB);
- return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
- ARRAY_SIZE(cs4271_snd_controls));
+ return 0;
}
static int cs4271_remove(struct snd_soc_codec *codec)
@@ -596,6 +615,13 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
.remove = cs4271_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
+
+ .controls = cs4271_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4271_snd_controls),
+ .dapm_widgets = cs4271_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
+ .dapm_routes = cs4271_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 987f728..be2ba1b 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -195,6 +195,8 @@ static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0);
+static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0);
+
static const unsigned int limiter_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
@@ -451,7 +453,8 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
SOC_ENUM("Beep Pitch", beep_pitch_enum),
SOC_ENUM("Beep on Time", beep_ontime_enum),
SOC_ENUM("Beep off Time", beep_offtime_enum),
- SOC_SINGLE_TLV("Beep Volume", CS42L52_BEEP_VOL, 0, 0x1f, 0x07, hl_tlv),
+ SOC_SINGLE_SX_TLV("Beep Volume", CS42L52_BEEP_VOL,
+ 0, 0x07, 0x1f, beep_tlv),
SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1),
SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c
deleted file mode 100644
index 4f4f7f4..0000000
--- a/sound/soc/codecs/dfbmcs320.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Driver for the DFBM-CS320 bluetooth module
- * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
- *
- * 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/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include <sound/soc.h>
-
-static struct snd_soc_dai_driver dfbmcs320_dai = {
- .name = "dfbmcs320-pcm",
- .playback = {
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-};
-
-static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
-
-static int dfbmcs320_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
- &dfbmcs320_dai, 1);
-}
-
-static int dfbmcs320_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver dfmcs320_driver = {
- .driver = {
- .name = "dfbmcs320",
- .owner = THIS_MODULE,
- },
- .probe = dfbmcs320_probe,
- .remove = dfbmcs320_remove,
-};
-
-module_platform_driver(dfmcs320_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 66967ba..b2090b2 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
-static int dmic_probe(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
- ARRAY_SIZE(dmic_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(dapm);
-
- return 0;
-}
-
static struct snd_soc_codec_driver soc_dmic = {
- .probe = dmic_probe,
+ .dapm_widgets = dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
};
static int dmic_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/hdmi.c
index 529d064..68342b1 100644
--- a/sound/soc/codecs/omap-hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -1,5 +1,5 @@
/*
- * ALSA SoC codec driver for HDMI audio on OMAP processors.
+ * ALSA SoC codec driver for HDMI audio codecs.
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
* Author: Ricardo Neri <ricardo.neri@ti.com>
*
@@ -23,11 +23,20 @@
#define DRV_NAME "hdmi-audio-codec"
-static struct snd_soc_codec_driver omap_hdmi_codec;
+static const struct snd_soc_dapm_widget hdmi_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route hdmi_routes[] = {
+ { "Capture", NULL, "RX" },
+ { "TX", NULL, "Playback" },
+};
-static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
- .name = "omap-hdmi-hifi",
+static struct snd_soc_dai_driver hdmi_codec_dai = {
+ .name = "hdmi-hifi",
.playback = {
+ .stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 |
@@ -37,33 +46,52 @@ static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+
+};
+
+static struct snd_soc_codec_driver hdmi_codec = {
+ .dapm_widgets = hdmi_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+ .dapm_routes = hdmi_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
};
-static int omap_hdmi_codec_probe(struct platform_device *pdev)
+static int hdmi_codec_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec,
- &omap_hdmi_codec_dai, 1);
+ return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
+ &hdmi_codec_dai, 1);
}
-static int omap_hdmi_codec_remove(struct platform_device *pdev)
+static int hdmi_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
-static struct platform_driver omap_hdmi_codec_driver = {
+static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .probe = omap_hdmi_codec_probe,
- .remove = omap_hdmi_codec_remove,
+ .probe = hdmi_codec_probe,
+ .remove = hdmi_codec_remove,
};
-module_platform_driver(omap_hdmi_codec_driver);
+module_platform_driver(hdmi_codec_driver);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver");
+MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 5f607b3..bcebd1a 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -384,8 +384,6 @@ static int jz4740_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index 9f9f595..0e5743e 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -23,12 +24,15 @@
#include <sound/tlv.h>
struct lm4857 {
- struct i2c_client *i2c;
+ struct regmap *regmap;
uint8_t mode;
};
-static const uint8_t lm4857_default_regs[] = {
- 0x00, 0x00, 0x00, 0x00,
+static const struct reg_default lm4857_default_regs[] = {
+ { 0x0, 0x00 },
+ { 0x1, 0x00 },
+ { 0x2, 0x00 },
+ { 0x3, 0x00 },
};
/* The register offsets in the cache array */
@@ -42,39 +46,6 @@ static const uint8_t lm4857_default_regs[] = {
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
-static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- uint8_t data;
- int ret;
-
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return ret;
-
- data = (reg << 6) | value;
- ret = i2c_master_send(codec->control_data, &data, 1);
- if (ret != 1) {
- dev_err(codec->dev, "Failed to write register: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static unsigned int lm4857_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- unsigned int val;
- int ret;
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret)
- return -1;
-
- return val;
-}
-
static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -96,7 +67,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
lm4857->mode = value;
if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
return 1;
}
@@ -108,10 +79,11 @@ static int lm4857_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
+ lm4857->mode + 6);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
+ regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
break;
default:
break;
@@ -171,49 +143,32 @@ static const struct snd_soc_dapm_route lm4857_routes[] = {
{"EP", NULL, "IN"},
};
-static int lm4857_probe(struct snd_soc_codec *codec)
-{
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- int ret;
-
- codec->control_data = lm4857->i2c;
-
- ret = snd_soc_add_codec_controls(codec, lm4857_controls,
- ARRAY_SIZE(lm4857_controls));
- if (ret)
- return ret;
-
- ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
- ARRAY_SIZE(lm4857_dapm_widgets));
- if (ret)
- return ret;
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+ .set_bias_level = lm4857_set_bias_level,
- ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
- ARRAY_SIZE(lm4857_routes));
- if (ret)
- return ret;
+ .controls = lm4857_controls,
+ .num_controls = ARRAY_SIZE(lm4857_controls),
+ .dapm_widgets = lm4857_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
+ .dapm_routes = lm4857_routes,
+ .num_dapm_routes = ARRAY_SIZE(lm4857_routes),
+};
- snd_soc_dapm_new_widgets(dapm);
+static const struct regmap_config lm4857_regmap_config = {
+ .val_bits = 6,
+ .reg_bits = 2,
- return 0;
-}
+ .max_register = LM4857_CTRL,
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
- .write = lm4857_write,
- .read = lm4857_read,
- .probe = lm4857_probe,
- .reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
- .reg_word_size = sizeof(uint8_t),
- .reg_cache_default = lm4857_default_regs,
- .set_bias_level = lm4857_set_bias_level,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = lm4857_default_regs,
+ .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
static int lm4857_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lm4857 *lm4857;
- int ret;
lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
if (!lm4857)
@@ -221,11 +176,11 @@ static int lm4857_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, lm4857);
- lm4857->i2c = i2c;
-
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+ lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+ if (IS_ERR(lm4857->regmap))
+ return PTR_ERR(lm4857->regmap);
- return ret;
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
}
static int lm4857_i2c_remove(struct i2c_client *i2c)
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index a6ac231..31f9156 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -118,6 +118,18 @@ static const struct snd_kcontrol_new max9768_mute[] = {
SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio),
};
+static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN"),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+};
+
+static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
+ { "OUT+", NULL, "IN" },
+ { "OUT-", NULL, "IN" },
+};
+
static int max9768_probe(struct snd_soc_codec *codec)
{
struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
@@ -148,6 +160,10 @@ static struct snd_soc_codec_driver max9768_codec_driver = {
.probe = max9768_probe,
.controls = max9768_volume,
.num_controls = ARRAY_SIZE(max9768_volume),
+ .dapm_widgets = max9768_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets),
+ .dapm_routes = max9768_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes),
};
static const struct regmap_config max9768_i2c_regmap_config = {
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 3eeada5..566a367 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1612,7 +1612,7 @@ static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
static void max98088_sync_cache(struct snd_soc_codec *codec)
{
- u16 *reg_cache = codec->reg_cache;
+ u8 *reg_cache = codec->reg_cache;
int i;
if (!codec->cache_sync)
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 8d14a76..0569a4c 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -857,6 +857,14 @@ static const struct soc_enum mic2_mux_enum =
static const struct snd_kcontrol_new max98090_mic2_mux =
SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
+static const char *dmic_mux_text[] = { "ADC", "DMIC" };
+
+static const struct soc_enum dmic_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text);
+
+static const struct snd_kcontrol_new max98090_dmic_mux =
+ SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum);
+
static const char *max98090_micpre_text[] = { "Off", "On" };
static const struct soc_enum max98090_pa1en_enum =
@@ -1144,6 +1152,9 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
0, 0, &max98090_mic2_mux),
+ SND_SOC_DAPM_VIRT_MUX("DMIC Mux", SND_SOC_NOPM,
+ 0, 0, &max98090_dmic_mux),
+
SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -1336,11 +1347,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"ADCL", NULL, "SHDN"},
{"ADCR", NULL, "SHDN"},
- {"LBENL Mux", "Normal", "ADCL"},
- {"LBENL Mux", "Normal", "DMICL"},
+ {"DMIC Mux", "ADC", "ADCL"},
+ {"DMIC Mux", "ADC", "ADCR"},
+ {"DMIC Mux", "DMIC", "DMICL"},
+ {"DMIC Mux", "DMIC", "DMICR"},
+
+ {"LBENL Mux", "Normal", "DMIC Mux"},
{"LBENL Mux", "Loopback", "LTENL Mux"},
- {"LBENR Mux", "Normal", "ADCR"},
- {"LBENR Mux", "Normal", "DMICR"},
+ {"LBENR Mux", "Normal", "DMIC Mux"},
{"LBENR Mux", "Loopback", "LTENR Mux"},
{"AIFOUTL", NULL, "LBENL Mux"},
@@ -2070,8 +2084,9 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
pm_wakeup_event(codec->dev, 100);
- schedule_delayed_work(&max98090->jack_work,
- msecs_to_jiffies(100));
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
}
if (active & M98090_DRCACT_MASK)
@@ -2118,8 +2133,9 @@ int max98090_mic_detect(struct snd_soc_codec *codec,
snd_soc_jack_report(max98090->jack, 0,
SND_JACK_HEADSET | SND_JACK_BTN_0);
- schedule_delayed_work(&max98090->jack_work,
- msecs_to_jiffies(100));
+ queue_delayed_work(system_power_efficient_wq,
+ &max98090->jack_work,
+ msecs_to_jiffies(100));
return 0;
}
@@ -2336,6 +2352,7 @@ static int max98090_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
static int max98090_runtime_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2355,6 +2372,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
+#endif
static const struct dev_pm_ops max98090_pm = {
SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 6b6c74c..29549cd 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -14,170 +14,21 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max9877.h"
-static struct i2c_client *i2c;
+static struct regmap *regmap;
-static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
-
-static void max9877_write_regs(void)
-{
- unsigned int i;
- u8 data[6];
-
- data[0] = MAX9877_INPUT_MODE;
- for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
- data[i + 1] = max9877_regs[i];
-
- if (i2c_master_send(i2c, data, 6) != 6)
- dev_err(&i2c->dev, "i2c write failed\n");
-}
-
-static int max9877_get_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
-
- if (invert)
- ucontrol->value.integer.value[0] =
- mask - ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static int max9877_set_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int invert = mc->invert;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
-
- if (invert)
- val = mask - val;
-
- if (((max9877_regs[reg] >> shift) & mask) == val)
- return 0;
-
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_write_regs();
-
- return 1;
-}
-
-static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
-
- ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
- ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
-
- return 0;
-}
-
-static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int mask = mc->max;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
- unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
- unsigned int change = 0;
-
- if (((max9877_regs[reg] >> shift) & mask) != val)
- change = 1;
-
- if (((max9877_regs[reg2] >> shift) & mask) != val2)
- change = 1;
-
- if (change) {
- max9877_regs[reg] &= ~(mask << shift);
- max9877_regs[reg] |= val << shift;
- max9877_regs[reg2] &= ~(mask << shift);
- max9877_regs[reg2] |= val2 << shift;
- max9877_write_regs();
- }
-
- return change;
-}
-
-static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
-
- if (value)
- value -= 1;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value += 1;
-
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
-
-static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
-
- value = value >> MAX9877_OSC_OFFSET;
-
- ucontrol->value.integer.value[0] = value;
- return 0;
-}
-
-static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 value = ucontrol->value.integer.value[0];
-
- value = value << MAX9877_OSC_OFFSET;
- if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
- return 0;
-
- max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
- max9877_regs[MAX9877_OUTPUT_MODE] |= value;
- max9877_write_regs();
- return 1;
-}
+static struct reg_default max9877_regs[] = {
+ { 0, 0x40 },
+ { 1, 0x00 },
+ { 2, 0x00 },
+ { 3, 0x00 },
+ { 4, 0x49 },
+};
static const unsigned int max9877_pgain_tlv[] = {
TLV_DB_RANGE_HEAD(2),
@@ -212,65 +63,104 @@ static const char *max9877_osc_mode[] = {
};
static const struct soc_enum max9877_enum[] = {
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
+ max9877_out_mode),
+ SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
+ ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
};
static const struct snd_kcontrol_new max9877_controls[] = {
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
- MAX9877_INPUT_MODE, 0, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
- MAX9877_INPUT_MODE, 2, 2, 0,
- max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
- SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
- MAX9877_SPK_VOLUME, 0, 31, 0,
- max9877_get_reg, max9877_set_reg, max9877_output_tlv),
- SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
- MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
- max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
- SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
- MAX9877_INPUT_MODE, 4, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
- MAX9877_INPUT_MODE, 5, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
- MAX9877_INPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
- MAX9877_OUTPUT_MODE, 6, 1, 0,
- max9877_get_reg, max9877_set_reg),
- SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
- MAX9877_OUTPUT_MODE, 7, 1, 1,
- max9877_get_reg, max9877_set_reg),
- SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
- max9877_get_out_mode, max9877_set_out_mode),
- SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
- max9877_get_osc_mode, max9877_set_osc_mode),
+ SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
+ MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
+ MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
+ SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
+ MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
+ SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
+ MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+ max9877_output_tlv),
+ SOC_SINGLE("MAX9877 INB Stereo Switch",
+ MAX9877_INPUT_MODE, 4, 1, 1),
+ SOC_SINGLE("MAX9877 INA Stereo Switch",
+ MAX9877_INPUT_MODE, 5, 1, 1),
+ SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
+ MAX9877_INPUT_MODE, 6, 1, 0),
+ SOC_SINGLE("MAX9877 Bypass Mode Switch",
+ MAX9877_OUTPUT_MODE, 6, 1, 0),
+ SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
+ SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
};
-/* This function is called from ASoC machine driver */
-int max9877_add_controls(struct snd_soc_codec *codec)
-{
- return snd_soc_add_codec_controls(codec, max9877_controls,
- ARRAY_SIZE(max9877_controls));
-}
-EXPORT_SYMBOL_GPL(max9877_add_controls);
+static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("INA1"),
+SND_SOC_DAPM_INPUT("INA2"),
+SND_SOC_DAPM_INPUT("INB1"),
+SND_SOC_DAPM_INPUT("INB2"),
+SND_SOC_DAPM_INPUT("RXIN+"),
+SND_SOC_DAPM_INPUT("RXIN-"),
+
+SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
+ { "SHDN", NULL, "INA1" },
+ { "SHDN", NULL, "INA2" },
+ { "SHDN", NULL, "INB1" },
+ { "SHDN", NULL, "INB2" },
+
+ { "OUT+", NULL, "RXIN+" },
+ { "OUT+", NULL, "SHDN" },
+
+ { "OUT-", NULL, "SHDN" },
+ { "OUT-", NULL, "RXIN-" },
+
+ { "HPL", NULL, "SHDN" },
+ { "HPR", NULL, "SHDN" },
+};
+
+static const struct snd_soc_codec_driver max9877_codec = {
+ .controls = max9877_controls,
+ .num_controls = ARRAY_SIZE(max9877_controls),
+
+ .dapm_widgets = max9877_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
+ .dapm_routes = max9877_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
+};
+
+static const struct regmap_config max9877_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = max9877_regs,
+ .num_reg_defaults = ARRAY_SIZE(max9877_regs),
+ .cache_type = REGCACHE_RBTREE,
+};
static int max9877_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- i2c = client;
+ int i;
- max9877_write_regs();
+ regmap = devm_regmap_init_i2c(client, &max9877_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ /* Ensure the device is in reset state */
+ for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+ regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
+
+ return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0);
}
static int max9877_i2c_remove(struct i2c_client *client)
{
- i2c = NULL;
+ snd_soc_unregister_codec(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 5402dfb..ea141e1 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -94,7 +94,6 @@
#define AUDIO_DAC_CFS_DLY_B (1 << 10)
struct mc13783_priv {
- struct snd_soc_codec codec;
struct mc13xxx *mc13xxx;
enum mc13783_ssi_port adc_ssi_port;
@@ -126,6 +125,10 @@ static int mc13783_write(struct snd_soc_codec *codec,
ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+ /* include errata fix for spi audio problems */
+ if (reg == MC13783_AUDIO_CODEC || reg == MC13783_AUDIO_DAC)
+ ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+
mc13xxx_unlock(priv->mc13xxx);
return ret;
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
new file mode 100644
index 0000000..651ce09
--- /dev/null
+++ b/sound/soc/codecs/pcm1681.c
@@ -0,0 +1,339 @@
+/*
+ * PCM1681 ASoC codec driver
+ *
+ * Copyright (c) StreamUnlimited GmbH 2013
+ * Marek Belisko <marek.belisko@streamunlimited.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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1681_SOFT_MUTE_ALL 0xff
+#define PCM1681_DEEMPH_RATE_MASK 0x18
+#define PCM1681_DEEMPH_MASK 0x01
+
+#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
+#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */
+#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */
+#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */
+#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */
+#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */
+
+static const struct reg_default pcm1681_reg_defaults[] = {
+ { 0x01, 0xff },
+ { 0x02, 0xff },
+ { 0x03, 0xff },
+ { 0x04, 0xff },
+ { 0x05, 0xff },
+ { 0x06, 0xff },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x06 },
+ { 0x0A, 0x00 },
+ { 0x0B, 0xff },
+ { 0x0C, 0x0f },
+ { 0x0D, 0x00 },
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+};
+
+static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return !((reg == 0x00) || (reg == 0x0f));
+}
+
+static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+{
+ return pcm1681_accessible_reg(dev, reg) &&
+ (reg != PCM1681_ZERO_DETECT_STATUS);
+}
+
+struct pcm1681_private {
+ struct regmap *regmap;
+ unsigned int format;
+ /* Current deemphasis status */
+ unsigned int deemph;
+ /* Current rate for deemphasis control */
+ unsigned int rate;
+};
+
+static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
+
+static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+{
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int i = 0, val = -1, enable = 0;
+
+ if (priv->deemph)
+ for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++)
+ if (pcm1681_deemph[i] == priv->rate)
+ val = i;
+
+ if (val != -1) {
+ regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_RATE_MASK, val);
+ enable = 1;
+ } else
+ enable = 0;
+
+ /* enable/disable deemphasis functionality */
+ return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+ PCM1681_DEEMPH_MASK, enable);
+}
+
+static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = priv->deemph;
+
+ return 0;
+}
+
+static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->deemph = ucontrol->value.enumerated.item[0];
+
+ return pcm1681_set_deemph(codec);
+}
+
+static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* The PCM1681 can only be slave to all clocks */
+ if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_err(codec->dev, "Invalid clocking mode\n");
+ return -EINVAL;
+ }
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val;
+
+ if (mute)
+ val = PCM1681_SOFT_MUTE_ALL;
+ else
+ val = 0;
+
+ return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
+}
+
+static int pcm1681_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 pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val = 0, ret;
+ int pcm_format = params_format(params);
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE)
+ val = 0x00;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x03;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x04;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x05;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
+ if (ret < 0)
+ return ret;
+
+ return pcm1681_set_deemph(codec);
+}
+
+static const struct snd_soc_dai_ops pcm1681_dai_ops = {
+ .set_fmt = pcm1681_set_dai_fmt,
+ .hw_params = pcm1681_hw_params,
+ .digital_mute = pcm1681_digital_mute,
+};
+
+static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUT1"),
+SND_SOC_DAPM_OUTPUT("VOUT2"),
+SND_SOC_DAPM_OUTPUT("VOUT3"),
+SND_SOC_DAPM_OUTPUT("VOUT4"),
+SND_SOC_DAPM_OUTPUT("VOUT5"),
+SND_SOC_DAPM_OUTPUT("VOUT6"),
+SND_SOC_DAPM_OUTPUT("VOUT7"),
+SND_SOC_DAPM_OUTPUT("VOUT8"),
+};
+
+static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
+ { "VOUT1", NULL, "Playback" },
+ { "VOUT2", NULL, "Playback" },
+ { "VOUT3", NULL, "Playback" },
+ { "VOUT4", NULL, "Playback" },
+ { "VOUT5", NULL, "Playback" },
+ { "VOUT6", NULL, "Playback" },
+ { "VOUT7", NULL, "Playback" },
+ { "VOUT8", NULL, "Playback" },
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
+
+static const struct snd_kcontrol_new pcm1681_controls[] = {
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
+ PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
+ 0x7f, 0, pcm1681_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ pcm1681_get_deemph, pcm1681_put_deemph),
+};
+
+static struct snd_soc_dai_driver pcm1681_dai = {
+ .name = "pcm1681-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = PCM1681_PCM_RATES,
+ .formats = PCM1681_PCM_FORMATS,
+ },
+ .ops = &pcm1681_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1681_dt_ids[] = {
+ { .compatible = "ti,pcm1681", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
+#endif
+
+static const struct regmap_config pcm1681_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(pcm1681_reg_defaults) + 1,
+ .reg_defaults = pcm1681_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults),
+ .writeable_reg = pcm1681_writeable_reg,
+ .readable_reg = pcm1681_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
+ .controls = pcm1681_controls,
+ .num_controls = ARRAY_SIZE(pcm1681_controls),
+ .dapm_widgets = pcm1681_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
+ .dapm_routes = pcm1681_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+};
+
+static const struct i2c_device_id pcm1681_i2c_id[] = {
+ {"pcm1681", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
+
+static int pcm1681_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct pcm1681_private *priv;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, priv);
+
+ return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+ &pcm1681_dai, 1);
+}
+
+static int pcm1681_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver pcm1681_i2c_driver = {
+ .driver = {
+ .name = "pcm1681",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcm1681_dt_ids),
+ },
+ .id_table = pcm1681_i2c_id,
+ .probe = pcm1681_i2c_probe,
+ .remove = pcm1681_i2c_remove,
+};
+
+module_i2c_driver(pcm1681_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
new file mode 100644
index 0000000..2a8eccf
--- /dev/null
+++ b/sound/soc/codecs/pcm1792a.c
@@ -0,0 +1,257 @@
+/*
+ * PCM1792A ASoC codec driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spi/spi.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 <linux/of_device.h>
+
+#include "pcm1792a.h"
+
+#define PCM1792A_DAC_VOL_LEFT 0x10
+#define PCM1792A_DAC_VOL_RIGHT 0x11
+#define PCM1792A_FMT_CONTROL 0x12
+#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
+
+#define PCM1792A_FMT_MASK 0x70
+#define PCM1792A_FMT_SHIFT 4
+#define PCM1792A_MUTE_MASK 0x01
+#define PCM1792A_MUTE_SHIFT 0
+#define PCM1792A_ATLD_ENABLE (1 << 7)
+
+static const struct reg_default pcm1792a_reg_defaults[] = {
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x50 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x01 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+};
+
+static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= 0x10 && reg <= 0x17;
+}
+
+static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
+{
+ bool accessible;
+
+ accessible = pcm1792a_accessible_reg(dev, reg);
+
+ return accessible && reg != 0x16 && reg != 0x17;
+}
+
+struct pcm1792a_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+};
+
+static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
+ PCM1792A_MUTE_MASK, !!mute);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcm1792a_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 pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val = 0, ret;
+ int pcm_format = params_format(params);
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+ pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+ val = 0x02;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x00;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+ pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+ val = 0x05;
+ else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+ val = 0x04;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
+
+ ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
+ PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
+ .set_fmt = pcm1792a_set_dai_fmt,
+ .hw_params = pcm1792a_hw_params,
+ .digital_mute = pcm1792a_digital_mute,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm1792a_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
+ PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm1792a_dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("IOUTL+"),
+SND_SOC_DAPM_OUTPUT("IOUTL-"),
+SND_SOC_DAPM_OUTPUT("IOUTR+"),
+SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm1792a_dai = {
+ .name = "pcm1792a-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM1792A_RATES,
+ .formats = PCM1792A_FORMATS, },
+ .ops = &pcm1792a_dai_ops,
+};
+
+static const struct of_device_id pcm1792a_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
+
+static const struct regmap_config pcm1792a_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 24,
+ .reg_defaults = pcm1792a_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults),
+ .writeable_reg = pcm1792a_writeable_reg,
+ .readable_reg = pcm1792a_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
+ .controls = pcm1792a_controls,
+ .num_controls = ARRAY_SIZE(pcm1792a_controls),
+ .dapm_widgets = pcm1792a_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets),
+ .dapm_routes = pcm1792a_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes),
+};
+
+static int pcm1792a_spi_probe(struct spi_device *spi)
+{
+ struct pcm1792a_private *pcm1792a;
+ int ret;
+
+ pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
+ GFP_KERNEL);
+ if (!pcm1792a)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, pcm1792a);
+
+ pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
+ if (IS_ERR(pcm1792a->regmap)) {
+ ret = PTR_ERR(pcm1792a->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
+}
+
+static int pcm1792a_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id pcm1792a_spi_ids[] = {
+ { "pcm1792a", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
+
+static struct spi_driver pcm1792a_codec_driver = {
+ .driver = {
+ .name = "pcm1792a",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcm1792a_of_match),
+ },
+ .id_table = pcm1792a_spi_ids,
+ .probe = pcm1792a_spi_probe,
+ .remove = pcm1792a_spi_remove,
+};
+
+module_spi_driver(pcm1792a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1792A driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h
new file mode 100644
index 0000000..7a83d1f
--- /dev/null
+++ b/sound/soc/codecs/pcm1792a.h
@@ -0,0 +1,26 @@
+/*
+ * definitions for PCM1792A
+ *
+ * Copyright 2013 Amarula Solutions
+ *
+ * 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.
+ */
+
+#ifndef __PCM1792A_H__
+#define __PCM1792A_H__
+
+#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE)
+
+#endif
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index f2a6282..b6618c4 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -28,7 +28,54 @@
#include "pcm3008.h"
-#define PCM3008_VERSION "0.2"
+static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+ gpio_set_value_cansleep(setup->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINL"),
+SND_SOC_DAPM_INPUT("VINR"),
+
+SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = {
+ { "PCM3008 Capture", NULL, "ADC" },
+ { "ADC", NULL, "VINL" },
+ { "ADC", NULL, "VINR" },
+
+ { "DAC", NULL, "PCM3008 Playback" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
@@ -51,20 +98,20 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
-{
- gpio_free(setup->dem0_pin);
- gpio_free(setup->dem1_pin);
- gpio_free(setup->pdad_pin);
- gpio_free(setup->pdda_pin);
-}
+static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
+ .dapm_widgets = pcm3008_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+ .dapm_routes = pcm3008_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+};
-static int pcm3008_soc_probe(struct snd_soc_codec *codec)
+static int pcm3008_codec_probe(struct platform_device *pdev)
{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
- int ret = 0;
+ struct pcm3008_setup_data *setup = pdev->dev.platform_data;
+ int ret;
- printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+ if (!setup)
+ return -EINVAL;
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -74,83 +121,29 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec)
*/
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem0_pin, "codec_dem0");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem0_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
+ GPIOF_OUT_INIT_HIGH, "codec_dem0");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
- ret = gpio_request(setup->dem1_pin, "codec_dem1");
- if (ret == 0)
- ret = gpio_direction_output(setup->dem1_pin, 0);
+ ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
+ GPIOF_OUT_INIT_LOW, "codec_dem1");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDAD GPIO. */
- ret = gpio_request(setup->pdad_pin, "codec_pdad");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdad_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdad");
if (ret != 0)
- goto gpio_err;
+ return ret;
/* Configure PDDA GPIO. */
- ret = gpio_request(setup->pdda_pin, "codec_pdda");
- if (ret == 0)
- ret = gpio_direction_output(setup->pdda_pin, 1);
+ ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
+ GPIOF_OUT_INIT_LOW, "codec_pdda");
if (ret != 0)
- goto gpio_err;
-
- return ret;
-
-gpio_err:
- pcm3008_gpio_free(setup);
+ return ret;
- return ret;
-}
-
-static int pcm3008_soc_remove(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- pcm3008_gpio_free(setup);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int pcm3008_soc_suspend(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 0);
- gpio_set_value(setup->pdda_pin, 0);
-
- return 0;
-}
-
-static int pcm3008_soc_resume(struct snd_soc_codec *codec)
-{
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
- gpio_set_value(setup->pdad_pin, 1);
- gpio_set_value(setup->pdda_pin, 1);
-
- return 0;
-}
-#else
-#define pcm3008_soc_suspend NULL
-#define pcm3008_soc_resume NULL
-#endif
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
- .probe = pcm3008_soc_probe,
- .remove = pcm3008_soc_remove,
- .suspend = pcm3008_soc_suspend,
- .resume = pcm3008_soc_resume,
-};
-
-static int pcm3008_codec_probe(struct platform_device *pdev)
-{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_pcm3008, &pcm3008_dai, 1);
}
@@ -158,6 +151,7 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
static int pcm3008_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
+
return 0;
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
new file mode 100644
index 0000000..c26a8f8
--- /dev/null
+++ b/sound/soc/codecs/rt5640.c
@@ -0,0 +1,2211 @@
+/*
+ * rt5640.c -- RT5640 ALSA SoC audio codec driver
+ *
+ * Copyright 2011 Realtek Semiconductor Corp.
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.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 "rt5640.h"
+
+#define RT5640_DEVICE_ID 0x6231
+
+#define RT5640_PR_RANGE_BASE (0xff + 1)
+#define RT5640_PR_SPACING 0x100
+
+#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING))
+
+static const struct regmap_range_cfg rt5640_ranges[] = {
+ { .name = "PR", .range_min = RT5640_PR_BASE,
+ .range_max = RT5640_PR_BASE + 0xb4,
+ .selector_reg = RT5640_PRIV_INDEX,
+ .selector_mask = 0xff,
+ .selector_shift = 0x0,
+ .window_start = RT5640_PRIV_DATA,
+ .window_len = 0x1, },
+};
+
+static struct reg_default init_list[] = {
+ {RT5640_PR_BASE + 0x3d, 0x3600},
+ {RT5640_PR_BASE + 0x12, 0x0aa8},
+ {RT5640_PR_BASE + 0x14, 0x0aaa},
+ {RT5640_PR_BASE + 0x20, 0x6110},
+ {RT5640_PR_BASE + 0x21, 0xe0e0},
+ {RT5640_PR_BASE + 0x23, 0x1804},
+};
+#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5640_reg[RT5640_VENDOR_ID2 + 1] = {
+ { 0x00, 0x000e },
+ { 0x01, 0xc8c8 },
+ { 0x02, 0xc8c8 },
+ { 0x03, 0xc8c8 },
+ { 0x04, 0x8000 },
+ { 0x0d, 0x0000 },
+ { 0x0e, 0x0000 },
+ { 0x0f, 0x0808 },
+ { 0x19, 0xafaf },
+ { 0x1a, 0xafaf },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x2f2f },
+ { 0x1d, 0x2f2f },
+ { 0x1e, 0x0000 },
+ { 0x27, 0x7060 },
+ { 0x28, 0x7070 },
+ { 0x29, 0x8080 },
+ { 0x2a, 0x5454 },
+ { 0x2b, 0x5454 },
+ { 0x2c, 0xaa00 },
+ { 0x2d, 0x0000 },
+ { 0x2e, 0xa000 },
+ { 0x2f, 0x0000 },
+ { 0x3b, 0x0000 },
+ { 0x3c, 0x007f },
+ { 0x3d, 0x0000 },
+ { 0x3e, 0x007f },
+ { 0x45, 0xe000 },
+ { 0x46, 0x003e },
+ { 0x47, 0x003e },
+ { 0x48, 0xf800 },
+ { 0x49, 0x3800 },
+ { 0x4a, 0x0004 },
+ { 0x4c, 0xfc00 },
+ { 0x4d, 0x0000 },
+ { 0x4f, 0x01ff },
+ { 0x50, 0x0000 },
+ { 0x51, 0x0000 },
+ { 0x52, 0x01ff },
+ { 0x53, 0xf000 },
+ { 0x61, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x63, 0x00c0 },
+ { 0x64, 0x0000 },
+ { 0x65, 0x0000 },
+ { 0x66, 0x0000 },
+ { 0x6a, 0x0000 },
+ { 0x6c, 0x0000 },
+ { 0x70, 0x8000 },
+ { 0x71, 0x8000 },
+ { 0x72, 0x8000 },
+ { 0x73, 0x1114 },
+ { 0x74, 0x0c00 },
+ { 0x75, 0x1d00 },
+ { 0x80, 0x0000 },
+ { 0x81, 0x0000 },
+ { 0x82, 0x0000 },
+ { 0x83, 0x0000 },
+ { 0x84, 0x0000 },
+ { 0x85, 0x0008 },
+ { 0x89, 0x0000 },
+ { 0x8a, 0x0000 },
+ { 0x8b, 0x0600 },
+ { 0x8c, 0x0228 },
+ { 0x8d, 0xa000 },
+ { 0x8e, 0x0004 },
+ { 0x8f, 0x1100 },
+ { 0x90, 0x0646 },
+ { 0x91, 0x0c00 },
+ { 0x92, 0x0000 },
+ { 0x93, 0x3000 },
+ { 0xb0, 0x2080 },
+ { 0xb1, 0x0000 },
+ { 0xb4, 0x2206 },
+ { 0xb5, 0x1f00 },
+ { 0xb6, 0x0000 },
+ { 0xb8, 0x034b },
+ { 0xb9, 0x0066 },
+ { 0xba, 0x000b },
+ { 0xbb, 0x0000 },
+ { 0xbc, 0x0000 },
+ { 0xbd, 0x0000 },
+ { 0xbe, 0x0000 },
+ { 0xbf, 0x0000 },
+ { 0xc0, 0x0400 },
+ { 0xc2, 0x0000 },
+ { 0xc4, 0x0000 },
+ { 0xc5, 0x0000 },
+ { 0xc6, 0x2000 },
+ { 0xc8, 0x0000 },
+ { 0xc9, 0x0000 },
+ { 0xca, 0x0000 },
+ { 0xcb, 0x0000 },
+ { 0xcc, 0x0000 },
+ { 0xcf, 0x0013 },
+ { 0xd0, 0x0680 },
+ { 0xd1, 0x1c17 },
+ { 0xd2, 0x8c00 },
+ { 0xd3, 0xaa20 },
+ { 0xd6, 0x0400 },
+ { 0xd9, 0x0809 },
+ { 0xfe, 0x10ec },
+ { 0xff, 0x6231 },
+};
+
+static int rt5640_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5640_RESET, 0);
+}
+
+static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_ASRC_5:
+ case RT5640_EQ_CTRL1:
+ case RT5640_DRC_AGC_1:
+ case RT5640_ANC_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5640_readable_register(struct device *dev, unsigned int reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++)
+ if ((reg >= rt5640_ranges[i].window_start &&
+ reg <= rt5640_ranges[i].window_start +
+ rt5640_ranges[i].window_len) ||
+ (reg >= rt5640_ranges[i].range_min &&
+ reg <= rt5640_ranges[i].range_max))
+ return true;
+
+ switch (reg) {
+ case RT5640_RESET:
+ case RT5640_SPK_VOL:
+ case RT5640_HP_VOL:
+ case RT5640_OUTPUT:
+ case RT5640_MONO_OUT:
+ case RT5640_IN1_IN2:
+ case RT5640_IN3_IN4:
+ case RT5640_INL_INR_VOL:
+ case RT5640_DAC1_DIG_VOL:
+ case RT5640_DAC2_DIG_VOL:
+ case RT5640_DAC2_CTRL:
+ case RT5640_ADC_DIG_VOL:
+ case RT5640_ADC_DATA:
+ case RT5640_ADC_BST_VOL:
+ case RT5640_STO_ADC_MIXER:
+ case RT5640_MONO_ADC_MIXER:
+ case RT5640_AD_DA_MIXER:
+ case RT5640_STO_DAC_MIXER:
+ case RT5640_MONO_DAC_MIXER:
+ case RT5640_DIG_MIXER:
+ case RT5640_DSP_PATH1:
+ case RT5640_DSP_PATH2:
+ case RT5640_DIG_INF_DATA:
+ case RT5640_REC_L1_MIXER:
+ case RT5640_REC_L2_MIXER:
+ case RT5640_REC_R1_MIXER:
+ case RT5640_REC_R2_MIXER:
+ case RT5640_HPO_MIXER:
+ case RT5640_SPK_L_MIXER:
+ case RT5640_SPK_R_MIXER:
+ case RT5640_SPO_L_MIXER:
+ case RT5640_SPO_R_MIXER:
+ case RT5640_SPO_CLSD_RATIO:
+ case RT5640_MONO_MIXER:
+ case RT5640_OUT_L1_MIXER:
+ case RT5640_OUT_L2_MIXER:
+ case RT5640_OUT_L3_MIXER:
+ case RT5640_OUT_R1_MIXER:
+ case RT5640_OUT_R2_MIXER:
+ case RT5640_OUT_R3_MIXER:
+ case RT5640_LOUT_MIXER:
+ case RT5640_PWR_DIG1:
+ case RT5640_PWR_DIG2:
+ case RT5640_PWR_ANLG1:
+ case RT5640_PWR_ANLG2:
+ case RT5640_PWR_MIXER:
+ case RT5640_PWR_VOL:
+ case RT5640_PRIV_INDEX:
+ case RT5640_PRIV_DATA:
+ case RT5640_I2S1_SDP:
+ case RT5640_I2S2_SDP:
+ case RT5640_ADDA_CLK1:
+ case RT5640_ADDA_CLK2:
+ case RT5640_DMIC:
+ case RT5640_GLB_CLK:
+ case RT5640_PLL_CTRL1:
+ case RT5640_PLL_CTRL2:
+ case RT5640_ASRC_1:
+ case RT5640_ASRC_2:
+ case RT5640_ASRC_3:
+ case RT5640_ASRC_4:
+ case RT5640_ASRC_5:
+ case RT5640_HP_OVCD:
+ case RT5640_CLS_D_OVCD:
+ case RT5640_CLS_D_OUT:
+ case RT5640_DEPOP_M1:
+ case RT5640_DEPOP_M2:
+ case RT5640_DEPOP_M3:
+ case RT5640_CHARGE_PUMP:
+ case RT5640_PV_DET_SPK_G:
+ case RT5640_MICBIAS:
+ case RT5640_EQ_CTRL1:
+ case RT5640_EQ_CTRL2:
+ case RT5640_WIND_FILTER:
+ case RT5640_DRC_AGC_1:
+ case RT5640_DRC_AGC_2:
+ case RT5640_DRC_AGC_3:
+ case RT5640_SVOL_ZC:
+ case RT5640_ANC_CTRL1:
+ case RT5640_ANC_CTRL2:
+ case RT5640_ANC_CTRL3:
+ case RT5640_JD_CTRL:
+ case RT5640_ANC_JD:
+ case RT5640_IRQ_CTRL1:
+ case RT5640_IRQ_CTRL2:
+ case RT5640_INT_IRQ_ST:
+ case RT5640_GPIO_CTRL1:
+ case RT5640_GPIO_CTRL2:
+ case RT5640_GPIO_CTRL3:
+ case RT5640_DSP_CTRL1:
+ case RT5640_DSP_CTRL2:
+ case RT5640_DSP_CTRL3:
+ case RT5640_DSP_CTRL4:
+ case RT5640_PGM_REG_ARR1:
+ case RT5640_PGM_REG_ARR2:
+ case RT5640_PGM_REG_ARR3:
+ case RT5640_PGM_REG_ARR4:
+ case RT5640_PGM_REG_ARR5:
+ case RT5640_SCB_FUNC:
+ case RT5640_SCB_CTRL:
+ case RT5640_BASE_BACK:
+ case RT5640_MP3_PLUS1:
+ case RT5640_MP3_PLUS2:
+ case RT5640_3D_HP:
+ case RT5640_ADJ_HPF:
+ case RT5640_HP_CALIB_AMP_DET:
+ case RT5640_HP_CALIB2:
+ case RT5640_SV_ZCD1:
+ case RT5640_SV_ZCD2:
+ case RT5640_DUMMY1:
+ case RT5640_DUMMY2:
+ case RT5640_DUMMY3:
+ case RT5640_VENDOR_ID:
+ case RT5640_VENDOR_ID1:
+ case RT5640_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static unsigned int bst_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+ 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5640_data_select[] = {
+ "Normal", "left copy to right", "right copy to left", "Swap"};
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
+
+static const SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
+ RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
+
+/* Class D speaker gain ratio */
+static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
+ "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
+ RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
+
+static const struct snd_kcontrol_new rt5640_snd_controls[] = {
+ /* Speaker Output Volume */
+ SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* Headphone Output Volume */
+ SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* OUTPUT Control */
+ SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT,
+ RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
+ SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
+ /* MONO Output Control */
+ SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT,
+ RT5640_L_MUTE_SFT, 1, 1),
+ /* DAC Digital Volume */
+ SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
+ RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
+ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
+ /* IN1/IN2 Control */
+ SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
+ RT5640_BST_SFT1, 8, 0, bst_tlv),
+ SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4,
+ RT5640_BST_SFT2, 8, 0, bst_tlv),
+ /* INL/INR Volume Control */
+ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
+ RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL,
+ RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
+ RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
+ /* ADC Boost Volume Control */
+ SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL,
+ RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
+ /* Class D speaker gain ratio */
+ SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum),
+
+ SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum),
+ SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum),
+ SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum),
+ SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int div[] = {2, 3, 4, 6, 8, 12};
+ int idx = -EINVAL, i;
+ int rate, red, bound, temp;
+
+ rate = rt5640->sysclk;
+ red = 3000000 * 12;
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ bound = div[i] * 3000000;
+ if (rate > bound)
+ continue;
+ temp = bound - rate;
+ if (temp < red) {
+ red = temp;
+ idx = i;
+ }
+ }
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ idx << RT5640_DMIC_CLK_SFT);
+ return idx;
+}
+
+static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ unsigned int val;
+
+ val = snd_soc_read(source->codec, RT5640_GLB_CLK);
+ val &= RT5640_SCLK_SRC_MASK;
+ if (val == RT5640_SCLK_SRC_PLL1 || val == RT5640_SCLK_SRC_PLL1T)
+ return 1;
+ else
+ return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER,
+ RT5640_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER,
+ RT5640_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_ADCMIX_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER,
+ RT5640_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_L2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R1_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_DAC_R2_SFT, 1, 1),
+ SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER,
+ RT5640_M_ANC_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L1_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R1_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_R2_MONO_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER,
+ RT5640_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_L_DAC_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_L2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_dig_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER,
+ RT5640_M_STO_R_DAC_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER,
+ RT5640_M_DAC_R2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
+ SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_HP_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_IN_L_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST4_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_BST1_RM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER,
+ RT5640_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
+ SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_HP_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_IN_R_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST4_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_BST1_RM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER,
+ RT5640_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+static const struct snd_kcontrol_new rt5640_spk_l_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_RM_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_IN_L_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L1_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_DAC_L2_SM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER,
+ RT5640_M_OM_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spk_r_mix[] = {
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_RM_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_IN_R_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R1_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_DAC_R2_SM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER,
+ RT5640_M_OM_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_l_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_SM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_BST1_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_IN_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_RM_L_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_R2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L2_OM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+ RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_out_r_mix[] = {
+ SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_SM_L_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST4_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_BST1_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_IN_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_RM_R_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_L2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R2_OM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+ RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_l_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_R1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_DAC_L1_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_R_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_SV_L_SPM_L_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER,
+ RT5640_M_BST1_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_spo_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_DAC_R1_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_SV_R_SPM_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER,
+ RT5640_M_BST1_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_hpo_mix[] = {
+ SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC2_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+ RT5640_M_DAC1_HM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+ RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_lout_mix[] = {
+ SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_L1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER,
+ RT5640_M_DAC_R1_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_L_LM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER,
+ RT5640_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5640_mono_mix[] = {
+ SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_R2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER,
+ RT5640_M_DAC_L2_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_R_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER,
+ RT5640_M_OV_L_MM_SFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER,
+ RT5640_M_BST1_MM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spk_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
+ RT5640_R_MUTE_SFT, 1, 1);
+
+/* Stereo ADC source */
+static const char * const rt5640_stereo_adc1_src[] = {
+ "DIG MIX", "ADC"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum);
+
+static const char * const rt5640_stereo_adc2_src[] = {
+ "DMIC1", "DMIC2", "DIG MIX"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
+ RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5640_sto_adc_2_mux =
+ SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5640_mono_adc_l1_src[] = {
+ "Mono DAC MIXL", "ADCL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum);
+
+static const char * const rt5640_mono_adc_l2_src[] = {
+ "DMIC L1", "DMIC L2", "Mono DAC MIXL"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum);
+
+static const char * const rt5640_mono_adc_r1_src[] = {
+ "Mono DAC MIXR", "ADCR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux =
+ SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum);
+
+static const char * const rt5640_mono_adc_r2_src[] = {
+ "DMIC R1", "DMIC R2", "Mono DAC MIXR"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
+ RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux =
+ SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum);
+
+/* DAC2 channel source */
+static const char * const rt5640_dac_l2_src[] = {
+ "IF2", "Base L/R"
+};
+
+static int rt5640_dac_l2_values[] = {
+ 0,
+ 3,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dac_l2_enum, RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
+ 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_l2_mux =
+ SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
+
+static const char * const rt5640_dac_r2_src[] = {
+ "IF2",
+};
+
+static int rt5640_dac_r2_values[] = {
+ 0,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dac_r2_enum, RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
+ 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
+
+static const struct snd_kcontrol_new rt5640_dac_r2_mux =
+ SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum);
+
+/* digital interface and iis interface map */
+static const char * const rt5640_dai_iis_map[] = {
+ "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2"
+};
+
+static int rt5640_dai_iis_map_values[] = {
+ 0,
+ 5,
+ 6,
+ 7,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(
+ rt5640_dai_iis_map_enum, RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
+ 0x7, rt5640_dai_iis_map, rt5640_dai_iis_map_values);
+
+static const struct snd_kcontrol_new rt5640_dai_mux =
+ SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum);
+
+/* SDI select */
+static const char * const rt5640_sdi_sel[] = {
+ "IF1", "IF2"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
+ RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
+
+static const struct snd_kcontrol_new rt5640_sdi_mux =
+ SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
+
+static int rt5640_set_dmic1_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:
+ snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK | RT5640_GP3_PIN_MASK,
+ RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP3_PIN_DMIC1_SDA);
+ snd_soc_update_bits(codec, RT5640_DMIC,
+ RT5640_DMIC_1L_LH_MASK | RT5640_DMIC_1R_LH_MASK |
+ RT5640_DMIC_1_DP_MASK,
+ RT5640_DMIC_1L_LH_FALLING | RT5640_DMIC_1R_LH_RISING |
+ RT5640_DMIC_1_DP_IN1P);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dmic2_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:
+ snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
+ RT5640_GP2_PIN_MASK | RT5640_GP4_PIN_MASK,
+ RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP4_PIN_DMIC2_SDA);
+ snd_soc_update_bits(codec, RT5640_DMIC,
+ RT5640_DMIC_2L_LH_MASK | RT5640_DMIC_2R_LH_MASK |
+ RT5640_DMIC_2_DP_MASK,
+ RT5640_DMIC_2L_LH_FALLING | RT5640_DMIC_2R_LH_RISING |
+ RT5640_DMIC_2_DP_IN1N);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+void hp_amp_power_on(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ /* depop parameters */
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK, RT5640_DEPOP_MAN);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK,
+ RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU);
+ regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1,
+ 0x9f00);
+ /* headphone amp power on */
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2, 0);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_HA,
+ RT5640_PWR_HA);
+ usleep_range(10000, 15000);
+ regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2 ,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+}
+
+static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
+ RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
+ RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP,
+ RT5640_PM_HP_MASK, RT5640_PM_HP_HV);
+
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3,
+ RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK,
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) |
+ (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) |
+ (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT));
+
+ regmap_write(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_MAMP_INT_REG2, 0x1c00);
+ regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
+ RT5640_HP_CP_MASK | RT5640_HP_SG_MASK,
+ RT5640_HP_CP_PD | RT5640_HP_SG_EN);
+ regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
+ RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400);
+}
+
+static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt5640_pmu_depop(codec);
+ rt5640->hp_mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ rt5640->hp_mute = 1;
+ usleep_range(70000, 75000);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_power_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_POST_PMU:
+ hp_amp_power_on(codec);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!rt5640->hp_mute)
+ usleep_range(80000, 85000);
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
+ RT5640_PWR_PLL_BIT, 0, NULL, 0),
+ /* Input Side */
+ /* micbias */
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
+ RT5640_PWR_LDO2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2,
+ RT5640_PWR_MB1_BIT, 0, NULL, 0),
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("IN1P"),
+ SND_SOC_DAPM_INPUT("IN1N"),
+ SND_SOC_DAPM_INPUT("IN2P"),
+ SND_SOC_DAPM_INPUT("IN2N"),
+ SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC,
+ RT5640_DMIC_1_EN_SFT, 0, rt5640_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC,
+ RT5640_DMIC_2_EN_SFT, 0, rt5640_set_dmic2_event,
+ SND_SOC_DAPM_PRE_PMU),
+ /* Boost */
+ SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
+ RT5640_PWR_BST4_BIT, 0, NULL, 0),
+ /* Input Volume */
+ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
+ RT5640_PWR_IN_R_BIT, 0, NULL, 0),
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
+ rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
+ SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0,
+ rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)),
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_L_BIT, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_ADC_R_BIT, 0),
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_2_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_sto_adc_1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l2_mux),
+ SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_l1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r1_mux),
+ SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_mono_adc_r2_mux),
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_SF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)),
+ SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2,
+ RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1,
+ RT5640_PWR_I2S2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux),
+ SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux),
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ /* Audio DSP */
+ SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* ANC */
+ SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ /* Output Side */
+ /* DAC mixer before sound effect */
+ SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)),
+ /* DAC2 channel Mux */
+ SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_dac_l2_mux),
+ SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0,
+ &rt5640_dac_r2_mux),
+ /* DAC Mixer */
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)),
+ SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)),
+ SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
+ rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L2_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R1_BIT, 0),
+ SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R2_BIT, 0),
+ /* SPK/OUT Mixer */
+ SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
+ 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
+ SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
+ 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+ 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
+ SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+ 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
+ /* Ouput Volume */
+ SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_SV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_SV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_OV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_OV_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL,
+ RT5640_PWR_HV_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL,
+ RT5640_PWR_HV_R_BIT, 0, NULL, 0),
+ /* SPO/HPO/LOUT/Mono Mixer */
+ SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)),
+ SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0,
+ 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+ rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+ SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0,
+ rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)),
+ SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
+ rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
+ SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
+ RT5640_PWR_MA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
+ 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
+ rt5640_hp_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1,
+ RT5640_PWR_HP_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
+ RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
+
+ /* Output Switch */
+ SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0,
+ &spk_l_enable_control),
+ SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0,
+ &spk_r_enable_control),
+ SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0,
+ &hp_l_enable_control),
+ SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0,
+ &hp_r_enable_control),
+ SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOLP"),
+ SND_SOC_DAPM_OUTPUT("SPOLN"),
+ SND_SOC_DAPM_OUTPUT("SPORP"),
+ SND_SOC_DAPM_OUTPUT("SPORN"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("LOUTL"),
+ SND_SOC_DAPM_OUTPUT("LOUTR"),
+ SND_SOC_DAPM_OUTPUT("MONOP"),
+ SND_SOC_DAPM_OUTPUT("MONON"),
+};
+
+static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+ {"IN1P", NULL, "LDO2"},
+ {"IN2P", NULL, "LDO2"},
+
+ {"DMIC L1", NULL, "DMIC1"},
+ {"DMIC R1", NULL, "DMIC1"},
+ {"DMIC L2", NULL, "DMIC2"},
+ {"DMIC R2", NULL, "DMIC2"},
+
+ {"BST1", NULL, "IN1P"},
+ {"BST1", NULL, "IN1N"},
+ {"BST2", NULL, "IN2P"},
+ {"BST2", NULL, "IN2N"},
+
+ {"INL VOL", NULL, "IN2P"},
+ {"INR VOL", NULL, "IN2N"},
+
+ {"RECMIXL", "HPOL Switch", "HPOL"},
+ {"RECMIXL", "INL Switch", "INL VOL"},
+ {"RECMIXL", "BST2 Switch", "BST2"},
+ {"RECMIXL", "BST1 Switch", "BST1"},
+ {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
+
+ {"RECMIXR", "HPOR Switch", "HPOR"},
+ {"RECMIXR", "INR Switch", "INR VOL"},
+ {"RECMIXR", "BST2 Switch", "BST2"},
+ {"RECMIXR", "BST1 Switch", "BST1"},
+ {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"ADC L", NULL, "RECMIXL"},
+ {"ADC R", NULL, "RECMIXR"},
+
+ {"DMIC L1", NULL, "DMIC CLK"},
+ {"DMIC L1", NULL, "DMIC1 Power"},
+ {"DMIC R1", NULL, "DMIC CLK"},
+ {"DMIC R1", NULL, "DMIC1 Power"},
+ {"DMIC L2", NULL, "DMIC CLK"},
+ {"DMIC L2", NULL, "DMIC2 Power"},
+ {"DMIC R2", NULL, "DMIC CLK"},
+ {"DMIC R2", NULL, "DMIC2 Power"},
+
+ {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"},
+ {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"},
+ {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"},
+ {"Stereo ADC L1 Mux", "ADC", "ADC L"},
+ {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"},
+
+ {"Stereo ADC R1 Mux", "ADC", "ADC R"},
+ {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"},
+ {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"},
+ {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"},
+ {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"},
+
+ {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"},
+ {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"},
+ {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"},
+ {"Mono ADC L1 Mux", "ADCL", "ADC L"},
+
+ {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+ {"Mono ADC R1 Mux", "ADCR", "ADC R"},
+ {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"},
+ {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"},
+ {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"},
+
+ {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
+ {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
+ {"Stereo ADC MIXL", NULL, "Stereo Filter"},
+ {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
+ {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
+ {"Stereo ADC MIXR", NULL, "Stereo Filter"},
+ {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
+ {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
+ {"Mono ADC MIXL", NULL, "Mono Left Filter"},
+ {"Mono Left Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
+ {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
+ {"Mono ADC MIXR", NULL, "Mono Right Filter"},
+ {"Mono Right Filter", NULL, "PLL1", check_sysclk1_source},
+
+ {"IF2 ADC L", NULL, "Mono ADC MIXL"},
+ {"IF2 ADC R", NULL, "Mono ADC MIXR"},
+ {"IF1 ADC L", NULL, "Stereo ADC MIXL"},
+ {"IF1 ADC R", NULL, "Stereo ADC MIXR"},
+
+ {"IF1 ADC", NULL, "I2S1"},
+ {"IF1 ADC", NULL, "IF1 ADC L"},
+ {"IF1 ADC", NULL, "IF1 ADC R"},
+ {"IF2 ADC", NULL, "I2S2"},
+ {"IF2 ADC", NULL, "IF2 ADC L"},
+ {"IF2 ADC", NULL, "IF2 ADC R"},
+
+ {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"},
+ {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"},
+ {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"},
+ {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"},
+ {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"},
+ {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"},
+
+ {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"},
+ {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"},
+ {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"},
+ {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"},
+ {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"},
+ {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"},
+
+ {"AIF1TX", NULL, "DAI1 TX Mux"},
+ {"AIF1TX", NULL, "SDI1 TX Mux"},
+ {"AIF2TX", NULL, "DAI2 TX Mux"},
+ {"AIF2TX", NULL, "SDI2 TX Mux"},
+
+ {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"},
+ {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"},
+ {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"},
+ {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"},
+ {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"},
+
+ {"IF1 DAC", NULL, "I2S1"},
+ {"IF1 DAC", NULL, "DAI1 RX Mux"},
+ {"IF2 DAC", NULL, "I2S2"},
+ {"IF2 DAC", NULL, "DAI2 RX Mux"},
+
+ {"IF1 DAC L", NULL, "IF1 DAC"},
+ {"IF1 DAC R", NULL, "IF1 DAC"},
+ {"IF2 DAC L", NULL, "IF2 DAC"},
+ {"IF2 DAC R", NULL, "IF2 DAC"},
+
+ {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
+ {"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
+ {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+
+ {"ANC", NULL, "Stereo ADC MIXL"},
+ {"ANC", NULL, "Stereo ADC MIXR"},
+
+ {"Audio DSP", NULL, "DAC MIXL"},
+ {"Audio DSP", NULL, "DAC MIXR"},
+
+ {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+ {"DAC L2 Mux", "Base L/R", "Audio DSP"},
+
+ {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+
+ {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Stereo DAC MIXL", "ANC Switch", "ANC"},
+ {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Stereo DAC MIXR", "ANC Switch", "ANC"},
+
+ {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+ {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+
+ {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"},
+ {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+ {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
+ {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+
+ {"DAC L1", NULL, "Stereo DAC MIXL"},
+ {"DAC L1", NULL, "PLL1", check_sysclk1_source},
+ {"DAC R1", NULL, "Stereo DAC MIXR"},
+ {"DAC R1", NULL, "PLL1", check_sysclk1_source},
+ {"DAC L2", NULL, "Mono DAC MIXL"},
+ {"DAC L2", NULL, "PLL1", check_sysclk1_source},
+ {"DAC R2", NULL, "Mono DAC MIXR"},
+ {"DAC R2", NULL, "PLL1", check_sysclk1_source},
+
+ {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"SPK MIXL", "INL Switch", "INL VOL"},
+ {"SPK MIXL", "DAC L1 Switch", "DAC L1"},
+ {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
+ {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"},
+ {"SPK MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"SPK MIXR", "INR Switch", "INR VOL"},
+ {"SPK MIXR", "DAC R1 Switch", "DAC R1"},
+ {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
+ {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"},
+
+ {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
+ {"OUT MIXL", "BST1 Switch", "BST1"},
+ {"OUT MIXL", "INL Switch", "INL VOL"},
+ {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+ {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+ {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
+ {"OUT MIXR", "BST2 Switch", "BST2"},
+ {"OUT MIXR", "BST1 Switch", "BST1"},
+ {"OUT MIXR", "INR Switch", "INR VOL"},
+ {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+ {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
+ {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+ {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+ {"SPKVOL L", NULL, "SPK MIXL"},
+ {"SPKVOL R", NULL, "SPK MIXR"},
+ {"HPOVOL L", NULL, "OUT MIXL"},
+ {"HPOVOL R", NULL, "OUT MIXR"},
+ {"OUTVOL L", NULL, "OUT MIXL"},
+ {"OUTVOL R", NULL, "OUT MIXR"},
+
+ {"SPOL MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOL MIX", "DAC L1 Switch", "DAC L1"},
+ {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"},
+ {"SPOL MIX", "BST1 Switch", "BST1"},
+ {"SPOR MIX", "DAC R1 Switch", "DAC R1"},
+ {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"},
+ {"SPOR MIX", "BST1 Switch", "BST1"},
+
+ {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
+ {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
+ {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
+ {"HPO MIX L", NULL, "HP L Amp"},
+ {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
+ {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
+ {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
+ {"HPO MIX R", NULL, "HP R Amp"},
+
+ {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+ {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+ {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+ {"Mono MIX", "DAC R2 Switch", "DAC R2"},
+ {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+ {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
+ {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
+ {"Mono MIX", "BST1 Switch", "BST1"},
+
+ {"HP Amp", NULL, "HPO MIX L"},
+ {"HP Amp", NULL, "HPO MIX R"},
+
+ {"Speaker L Playback", "Switch", "SPOL MIX"},
+ {"Speaker R Playback", "Switch", "SPOR MIX"},
+ {"SPOLP", NULL, "Speaker L Playback"},
+ {"SPOLN", NULL, "Speaker L Playback"},
+ {"SPORP", NULL, "Speaker R Playback"},
+ {"SPORN", NULL, "Speaker R Playback"},
+
+ {"SPOLP", NULL, "Improve SPK Amp Drv"},
+ {"SPOLN", NULL, "Improve SPK Amp Drv"},
+ {"SPORP", NULL, "Improve SPK Amp Drv"},
+ {"SPORN", NULL, "Improve SPK Amp Drv"},
+
+ {"HPOL", NULL, "Improve HP Amp Drv"},
+ {"HPOR", NULL, "Improve HP Amp Drv"},
+
+ {"HP L Playback", "Switch", "HP Amp"},
+ {"HP R Playback", "Switch", "HP Amp"},
+ {"HPOL", NULL, "HP L Playback"},
+ {"HPOR", NULL, "HP R Playback"},
+ {"LOUTL", NULL, "LOUT MIX"},
+ {"LOUTR", NULL, "LOUT MIX"},
+ {"MONOP", NULL, "Mono MIX"},
+ {"MONON", NULL, "Mono MIX"},
+ {"MONOP", NULL, "Improve MONO Amp Drv"},
+};
+
+static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+{
+ int ret = 0, val;
+
+ if (codec == NULL)
+ return -EINVAL;
+
+ val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT;
+ switch (dai_id) {
+ case RT5640_AIF1:
+ switch (val) {
+ case RT5640_IF_123:
+ case RT5640_IF_132:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_113:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_312:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ case RT5640_AIF2:
+ switch (val) {
+ case RT5640_IF_231:
+ case RT5640_IF_213:
+ ret |= RT5640_U_IF1;
+ break;
+ case RT5640_IF_223:
+ ret |= RT5640_U_IF1;
+ case RT5640_IF_123:
+ case RT5640_IF_321:
+ ret |= RT5640_U_IF2;
+ break;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt5640_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_codec *codec = rtd->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0, val_clk, mask_clk, dai_sel;
+ int pre_div, bclk_ms, frame_size;
+
+ rt5640->lrck[dai->id] = params_rate(params);
+ pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
+ if (pre_div < 0) {
+ dev_err(codec->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ return frame_size;
+ }
+ if (frame_size > 32)
+ bclk_ms = 1;
+ else
+ bclk_ms = 0;
+ rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5640->bclk[dai->id], rt5640->lrck[dai->id]);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len |= RT5640_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len |= RT5640_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len |= RT5640_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT |
+ pre_div << RT5640_I2S_PD1_SFT;
+ snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK;
+ val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT |
+ pre_div << RT5640_I2S_PD2_SFT;
+ snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ RT5640_I2S_DL_MASK, val_len);
+ snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0, dai_sel;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5640->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5640_I2S_MS_S;
+ rt5640->master[dai->id] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5640_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5640_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5640_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5640_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ snd_soc_update_bits(codec, RT5640_I2S2_SDP,
+ RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
+ RT5640_I2S_DF_MASK, reg_val);
+ }
+
+ return 0;
+}
+
+static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5640_SCLK_S_MCLK:
+ reg_val |= RT5640_SCLK_SRC_MCLK;
+ break;
+ case RT5640_SCLK_S_PLL1:
+ reg_val |= RT5640_SCLK_SRC_PLL1;
+ break;
+ case RT5640_SCLK_S_PLL1_TK:
+ reg_val |= RT5640_SCLK_SRC_PLL1T;
+ break;
+ case RT5640_SCLK_S_RCCLK:
+ reg_val |= RT5640_SCLK_SRC_RCCLK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, reg_val);
+ rt5640->sysclk = freq;
+ rt5640->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ return 0;
+}
+
+/**
+ * rt5640_pll_calc - Calculate PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K and bypass flag.
+ *
+ * Calculate M/N/K code to configure PLL for codec. And K is assigned to 2
+ * which make calculation more efficiently.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5640_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt5640_pll_code *pll_code)
+{
+ int max_n = RT5640_PLL_N_MAX, max_m = RT5640_PLL_M_MAX;
+ int n = 0, m = 0, red, n_t, m_t, in_t, out_t;
+ int red_t = abs(freq_out - freq_in);
+ bool bypass = false;
+
+ if (RT5640_PLL_INP_MAX < freq_in || RT5640_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = (freq_in >> 1) + (freq_in >> 2) * n_t;
+ if (in_t < 0)
+ continue;
+ if (in_t == freq_out) {
+ bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / (m_t + 2);
+ red = abs(out_t - freq_out);
+ if (red < red_t) {
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+ pll_code->m_bp = bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = 2;
+ return 0;
+}
+
+static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_pll_code *pll_code = &rt5640->pll_code;
+ int ret, dai_sel;
+
+ if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
+ freq_out == rt5640->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5640->pll_in = 0;
+ rt5640->pll_out = 0;
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5640_PLL1_S_MCLK:
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
+ break;
+ case RT5640_PLL1_S_BCLK1:
+ case RT5640_PLL1_S_BCLK2:
+ dai_sel = get_sdp_info(codec, dai->id);
+ if (dai_sel < 0) {
+ dev_err(codec->dev,
+ "Failed to get sdp info: %d\n", dai_sel);
+ return -EINVAL;
+ }
+ if (dai_sel & RT5640_U_IF1) {
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+ }
+ if (dai_sel & RT5640_U_IF2) {
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
+ }
+ break;
+ default:
+ dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt5640_pll_calc(freq_in, freq_out, pll_code);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp,
+ (pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code);
+
+ snd_soc_write(codec, RT5640_PLL_CTRL1,
+ pll_code->n_code << RT5640_PLL_N_SFT | pll_code->k_code);
+ snd_soc_write(codec, RT5640_PLL_CTRL2,
+ (pll_code->m_bp ? 0 : pll_code->m_code) << RT5640_PLL_M_SFT |
+ pll_code->m_bp << RT5640_PLL_M_BP_SFT);
+
+ rt5640->pll_in = freq_in;
+ rt5640->pll_out = freq_out;
+ rt5640->pll_src = source;
+
+ return 0;
+}
+
+static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+ regcache_cache_only(rt5640->regmap, false);
+ snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2,
+ RT5640_PWR_VREF1 | RT5640_PWR_MB |
+ RT5640_PWR_BG | RT5640_PWR_VREF2);
+ usleep_range(10000, 15000);
+ snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2,
+ RT5640_PWR_FV1 | RT5640_PWR_FV2);
+ regcache_sync(rt5640->regmap);
+ snd_soc_update_bits(codec, RT5640_DUMMY1,
+ 0x0301, 0x0301);
+ snd_soc_update_bits(codec, RT5640_MICBIAS,
+ 0x0030, 0x0030);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004);
+ snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100);
+ snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0);
+ snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_VOL, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000);
+ snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int rt5640_probe(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ rt5640->codec = codec;
+ codec->control_data = rt5640->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ codec->dapm.idle_bias_off = 1;
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
+ snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
+ snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+
+ return 0;
+}
+
+static int rt5640_remove(struct snd_soc_codec *codec)
+{
+ rt5640_reset(codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5640_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ rt5640_reset(codec);
+ regcache_cache_only(rt5640->regmap, true);
+ regcache_mark_dirty(rt5640->regmap);
+
+ return 0;
+}
+
+static int rt5640_resume(struct snd_soc_codec *codec)
+{
+ rt5640_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define rt5640_suspend NULL
+#define rt5640_resume NULL
+#endif
+
+#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5640_aif_dai_ops = {
+ .hw_params = rt5640_hw_params,
+ .set_fmt = rt5640_set_dai_fmt,
+ .set_sysclk = rt5640_set_dai_sysclk,
+ .set_pll = rt5640_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5640_dai[] = {
+ {
+ .name = "rt5640-aif1",
+ .id = RT5640_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+ {
+ .name = "rt5640-aif2",
+ .id = RT5640_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT5640_STEREO_RATES,
+ .formats = RT5640_FORMATS,
+ },
+ .ops = &rt5640_aif_dai_ops,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
+ .probe = rt5640_probe,
+ .remove = rt5640_remove,
+ .suspend = rt5640_suspend,
+ .resume = rt5640_resume,
+ .set_bias_level = rt5640_set_bias_level,
+ .controls = rt5640_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5640_snd_controls),
+ .dapm_widgets = rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
+ .dapm_routes = rt5640_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+};
+
+static const struct regmap_config rt5640_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) *
+ RT5640_PR_SPACING),
+ .volatile_reg = rt5640_volatile_register,
+ .readable_reg = rt5640_readable_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5640_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5640_reg),
+ .ranges = rt5640_ranges,
+ .num_ranges = ARRAY_SIZE(rt5640_ranges),
+};
+
+static const struct i2c_device_id rt5640_i2c_id[] = {
+ { "rt5640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
+
+static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
+{
+ rt5640->pdata.in1_diff = of_property_read_bool(np,
+ "realtek,in1-differential");
+ rt5640->pdata.in2_diff = of_property_read_bool(np,
+ "realtek,in2-differential");
+
+ rt5640->pdata.ldo1_en = of_get_named_gpio(np,
+ "realtek,ldo1-en-gpios", 0);
+ /*
+ * LDO1_EN is optional (it may be statically tied on the board).
+ * -ENOENT means that the property doesn't exist, i.e. there is no
+ * GPIO, so is not an error. Any other error code means the property
+ * exists, but could not be parsed.
+ */
+ if (!gpio_is_valid(rt5640->pdata.ldo1_en) &&
+ (rt5640->pdata.ldo1_en != -ENOENT))
+ return rt5640->pdata.ldo1_en;
+
+ return 0;
+}
+
+static int rt5640_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5640_priv *rt5640;
+ int ret;
+ unsigned int val;
+
+ rt5640 = devm_kzalloc(&i2c->dev,
+ sizeof(struct rt5640_priv),
+ GFP_KERNEL);
+ if (NULL == rt5640)
+ return -ENOMEM;
+ i2c_set_clientdata(i2c, rt5640);
+
+ if (pdata) {
+ rt5640->pdata = *pdata;
+ /*
+ * Translate zero'd out (default) pdata value to an invalid
+ * GPIO ID. This makes the pdata and DT paths consistent in
+ * terms of the value left in this field when no GPIO is
+ * specified, but means we can't actually use GPIO 0.
+ */
+ if (!rt5640->pdata.ldo1_en)
+ rt5640->pdata.ldo1_en = -EINVAL;
+ } else if (i2c->dev.of_node) {
+ ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
+ if (ret)
+ return ret;
+ } else
+ rt5640->pdata.ldo1_en = -EINVAL;
+
+ rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
+ if (IS_ERR(rt5640->regmap)) {
+ ret = PTR_ERR(rt5640->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
+ ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en,
+ GPIOF_OUT_INIT_HIGH,
+ "RT5640 LDO1_EN");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
+ rt5640->pdata.ldo1_en, ret);
+ return ret;
+ }
+ msleep(400);
+ }
+
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if ((val != RT5640_DEVICE_ID)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5640/39\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5640->regmap, RT5640_RESET, 0);
+
+ ret = regmap_register_patch(rt5640->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5640->pdata.in1_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+ RT5640_IN_DF1, RT5640_IN_DF1);
+
+ if (rt5640->pdata.in2_diff)
+ regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
+ RT5640_IN_DF2, RT5640_IN_DF2);
+
+ rt5640->hp_mute = 1;
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
+ rt5640_dai, ARRAY_SIZE(rt5640_dai));
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static int rt5640_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static struct i2c_driver rt5640_i2c_driver = {
+ .driver = {
+ .name = "rt5640",
+ .owner = THIS_MODULE,
+ },
+ .probe = rt5640_i2c_probe,
+ .remove = rt5640_i2c_remove,
+ .id_table = rt5640_i2c_id,
+};
+module_i2c_driver(rt5640_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5640 driver");
+MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
new file mode 100644
index 0000000..5e8df25a
--- /dev/null
+++ b/sound/soc/codecs/rt5640.h
@@ -0,0 +1,2104 @@
+/*
+ * rt5640.h -- RT5640 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.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 _RT5640_H
+#define _RT5640_H
+
+#include <sound/rt5640.h>
+
+/* Info */
+#define RT5640_RESET 0x00
+#define RT5640_VENDOR_ID 0xfd
+#define RT5640_VENDOR_ID1 0xfe
+#define RT5640_VENDOR_ID2 0xff
+/* I/O - Output */
+#define RT5640_SPK_VOL 0x01
+#define RT5640_HP_VOL 0x02
+#define RT5640_OUTPUT 0x03
+#define RT5640_MONO_OUT 0x04
+/* I/O - Input */
+#define RT5640_IN1_IN2 0x0d
+#define RT5640_IN3_IN4 0x0e
+#define RT5640_INL_INR_VOL 0x0f
+/* I/O - ADC/DAC/DMIC */
+#define RT5640_DAC1_DIG_VOL 0x19
+#define RT5640_DAC2_DIG_VOL 0x1a
+#define RT5640_DAC2_CTRL 0x1b
+#define RT5640_ADC_DIG_VOL 0x1c
+#define RT5640_ADC_DATA 0x1d
+#define RT5640_ADC_BST_VOL 0x1e
+/* Mixer - D-D */
+#define RT5640_STO_ADC_MIXER 0x27
+#define RT5640_MONO_ADC_MIXER 0x28
+#define RT5640_AD_DA_MIXER 0x29
+#define RT5640_STO_DAC_MIXER 0x2a
+#define RT5640_MONO_DAC_MIXER 0x2b
+#define RT5640_DIG_MIXER 0x2c
+#define RT5640_DSP_PATH1 0x2d
+#define RT5640_DSP_PATH2 0x2e
+#define RT5640_DIG_INF_DATA 0x2f
+/* Mixer - ADC */
+#define RT5640_REC_L1_MIXER 0x3b
+#define RT5640_REC_L2_MIXER 0x3c
+#define RT5640_REC_R1_MIXER 0x3d
+#define RT5640_REC_R2_MIXER 0x3e
+/* Mixer - DAC */
+#define RT5640_HPO_MIXER 0x45
+#define RT5640_SPK_L_MIXER 0x46
+#define RT5640_SPK_R_MIXER 0x47
+#define RT5640_SPO_L_MIXER 0x48
+#define RT5640_SPO_R_MIXER 0x49
+#define RT5640_SPO_CLSD_RATIO 0x4a
+#define RT5640_MONO_MIXER 0x4c
+#define RT5640_OUT_L1_MIXER 0x4d
+#define RT5640_OUT_L2_MIXER 0x4e
+#define RT5640_OUT_L3_MIXER 0x4f
+#define RT5640_OUT_R1_MIXER 0x50
+#define RT5640_OUT_R2_MIXER 0x51
+#define RT5640_OUT_R3_MIXER 0x52
+#define RT5640_LOUT_MIXER 0x53
+/* Power */
+#define RT5640_PWR_DIG1 0x61
+#define RT5640_PWR_DIG2 0x62
+#define RT5640_PWR_ANLG1 0x63
+#define RT5640_PWR_ANLG2 0x64
+#define RT5640_PWR_MIXER 0x65
+#define RT5640_PWR_VOL 0x66
+/* Private Register Control */
+#define RT5640_PRIV_INDEX 0x6a
+#define RT5640_PRIV_DATA 0x6c
+/* Format - ADC/DAC */
+#define RT5640_I2S1_SDP 0x70
+#define RT5640_I2S2_SDP 0x71
+#define RT5640_ADDA_CLK1 0x73
+#define RT5640_ADDA_CLK2 0x74
+#define RT5640_DMIC 0x75
+/* Function - Analog */
+#define RT5640_GLB_CLK 0x80
+#define RT5640_PLL_CTRL1 0x81
+#define RT5640_PLL_CTRL2 0x82
+#define RT5640_ASRC_1 0x83
+#define RT5640_ASRC_2 0x84
+#define RT5640_ASRC_3 0x85
+#define RT5640_ASRC_4 0x89
+#define RT5640_ASRC_5 0x8a
+#define RT5640_HP_OVCD 0x8b
+#define RT5640_CLS_D_OVCD 0x8c
+#define RT5640_CLS_D_OUT 0x8d
+#define RT5640_DEPOP_M1 0x8e
+#define RT5640_DEPOP_M2 0x8f
+#define RT5640_DEPOP_M3 0x90
+#define RT5640_CHARGE_PUMP 0x91
+#define RT5640_PV_DET_SPK_G 0x92
+#define RT5640_MICBIAS 0x93
+/* Function - Digital */
+#define RT5640_EQ_CTRL1 0xb0
+#define RT5640_EQ_CTRL2 0xb1
+#define RT5640_WIND_FILTER 0xb2
+#define RT5640_DRC_AGC_1 0xb4
+#define RT5640_DRC_AGC_2 0xb5
+#define RT5640_DRC_AGC_3 0xb6
+#define RT5640_SVOL_ZC 0xb7
+#define RT5640_ANC_CTRL1 0xb8
+#define RT5640_ANC_CTRL2 0xb9
+#define RT5640_ANC_CTRL3 0xba
+#define RT5640_JD_CTRL 0xbb
+#define RT5640_ANC_JD 0xbc
+#define RT5640_IRQ_CTRL1 0xbd
+#define RT5640_IRQ_CTRL2 0xbe
+#define RT5640_INT_IRQ_ST 0xbf
+#define RT5640_GPIO_CTRL1 0xc0
+#define RT5640_GPIO_CTRL2 0xc1
+#define RT5640_GPIO_CTRL3 0xc2
+#define RT5640_DSP_CTRL1 0xc4
+#define RT5640_DSP_CTRL2 0xc5
+#define RT5640_DSP_CTRL3 0xc6
+#define RT5640_DSP_CTRL4 0xc7
+#define RT5640_PGM_REG_ARR1 0xc8
+#define RT5640_PGM_REG_ARR2 0xc9
+#define RT5640_PGM_REG_ARR3 0xca
+#define RT5640_PGM_REG_ARR4 0xcb
+#define RT5640_PGM_REG_ARR5 0xcc
+#define RT5640_SCB_FUNC 0xcd
+#define RT5640_SCB_CTRL 0xce
+#define RT5640_BASE_BACK 0xcf
+#define RT5640_MP3_PLUS1 0xd0
+#define RT5640_MP3_PLUS2 0xd1
+#define RT5640_3D_HP 0xd2
+#define RT5640_ADJ_HPF 0xd3
+#define RT5640_HP_CALIB_AMP_DET 0xd6
+#define RT5640_HP_CALIB2 0xd7
+#define RT5640_SV_ZCD1 0xd9
+#define RT5640_SV_ZCD2 0xda
+/* Dummy Register */
+#define RT5640_DUMMY1 0xfa
+#define RT5640_DUMMY2 0xfb
+#define RT5640_DUMMY3 0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5640_CHPUMP_INT_REG1 0x24
+#define RT5640_MAMP_INT_REG2 0x37
+#define RT5640_3D_SPK 0x63
+#define RT5640_WND_1 0x6c
+#define RT5640_WND_2 0x6d
+#define RT5640_WND_3 0x6e
+#define RT5640_WND_4 0x6f
+#define RT5640_WND_5 0x70
+#define RT5640_WND_8 0x73
+#define RT5640_DIP_SPK_INF 0x75
+#define RT5640_HP_DCC_INT1 0x77
+#define RT5640_EQ_BW_LOP 0xa0
+#define RT5640_EQ_GN_LOP 0xa1
+#define RT5640_EQ_FC_BP1 0xa2
+#define RT5640_EQ_BW_BP1 0xa3
+#define RT5640_EQ_GN_BP1 0xa4
+#define RT5640_EQ_FC_BP2 0xa5
+#define RT5640_EQ_BW_BP2 0xa6
+#define RT5640_EQ_GN_BP2 0xa7
+#define RT5640_EQ_FC_BP3 0xa8
+#define RT5640_EQ_BW_BP3 0xa9
+#define RT5640_EQ_GN_BP3 0xaa
+#define RT5640_EQ_FC_BP4 0xab
+#define RT5640_EQ_BW_BP4 0xac
+#define RT5640_EQ_GN_BP4 0xad
+#define RT5640_EQ_FC_HIP1 0xae
+#define RT5640_EQ_GN_HIP1 0xaf
+#define RT5640_EQ_FC_HIP2 0xb0
+#define RT5640_EQ_BW_HIP2 0xb1
+#define RT5640_EQ_GN_HIP2 0xb2
+#define RT5640_EQ_PRE_VOL 0xb3
+#define RT5640_EQ_PST_VOL 0xb4
+
+/* global definition */
+#define RT5640_L_MUTE (0x1 << 15)
+#define RT5640_L_MUTE_SFT 15
+#define RT5640_VOL_L_MUTE (0x1 << 14)
+#define RT5640_VOL_L_SFT 14
+#define RT5640_R_MUTE (0x1 << 7)
+#define RT5640_R_MUTE_SFT 7
+#define RT5640_VOL_R_MUTE (0x1 << 6)
+#define RT5640_VOL_R_SFT 6
+#define RT5640_L_VOL_MASK (0x3f << 8)
+#define RT5640_L_VOL_SFT 8
+#define RT5640_R_VOL_MASK (0x3f)
+#define RT5640_R_VOL_SFT 0
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5640_BST_SFT1 12
+#define RT5640_BST_SFT2 8
+#define RT5640_IN_DF1 (0x1 << 7)
+#define RT5640_IN_SFT1 7
+#define RT5640_IN_DF2 (0x1 << 6)
+#define RT5640_IN_SFT2 6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5640_INL_SEL_MASK (0x1 << 15)
+#define RT5640_INL_SEL_SFT 15
+#define RT5640_INL_SEL_IN4P (0x0 << 15)
+#define RT5640_INL_SEL_MONOP (0x1 << 15)
+#define RT5640_INL_VOL_MASK (0x1f << 8)
+#define RT5640_INL_VOL_SFT 8
+#define RT5640_INR_SEL_MASK (0x1 << 7)
+#define RT5640_INR_SEL_SFT 7
+#define RT5640_INR_SEL_IN4N (0x0 << 7)
+#define RT5640_INR_SEL_MONON (0x1 << 7)
+#define RT5640_INR_VOL_MASK (0x1f)
+#define RT5640_INR_VOL_SFT 0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5640_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L1_VOL_SFT 8
+#define RT5640_DAC_R1_VOL_MASK (0xff)
+#define RT5640_DAC_R1_VOL_SFT 0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5640_DAC_L2_VOL_MASK (0xff << 8)
+#define RT5640_DAC_L2_VOL_SFT 8
+#define RT5640_DAC_R2_VOL_MASK (0xff)
+#define RT5640_DAC_R2_VOL_SFT 0
+
+/* DAC2 Control (0x1b) */
+#define RT5640_M_DAC_L2_VOL (0x1 << 13)
+#define RT5640_M_DAC_L2_VOL_SFT 13
+#define RT5640_M_DAC_R2_VOL (0x1 << 12)
+#define RT5640_M_DAC_R2_VOL_SFT 12
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5640_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_ADC_L_VOL_SFT 8
+#define RT5640_ADC_R_VOL_MASK (0x7f)
+#define RT5640_ADC_R_VOL_SFT 0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5640_MONO_ADC_L_VOL_SFT 8
+#define RT5640_MONO_ADC_R_VOL_MASK (0x7f)
+#define RT5640_MONO_ADC_R_VOL_SFT 0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5640_ADC_L_BST_MASK (0x3 << 14)
+#define RT5640_ADC_L_BST_SFT 14
+#define RT5640_ADC_R_BST_MASK (0x3 << 12)
+#define RT5640_ADC_R_BST_SFT 12
+#define RT5640_ADC_COMP_MASK (0x3 << 10)
+#define RT5640_ADC_COMP_SFT 10
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5640_M_ADC_L1 (0x1 << 14)
+#define RT5640_M_ADC_L1_SFT 14
+#define RT5640_M_ADC_L2 (0x1 << 13)
+#define RT5640_M_ADC_L2_SFT 13
+#define RT5640_ADC_1_SRC_MASK (0x1 << 12)
+#define RT5640_ADC_1_SRC_SFT 12
+#define RT5640_ADC_1_SRC_ADC (0x1 << 12)
+#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12)
+#define RT5640_ADC_2_SRC_MASK (0x3 << 10)
+#define RT5640_ADC_2_SRC_SFT 10
+#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10)
+#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10)
+#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10)
+#define RT5640_M_ADC_R1 (0x1 << 6)
+#define RT5640_M_ADC_R1_SFT 6
+#define RT5640_M_ADC_R2 (0x1 << 5)
+#define RT5640_M_ADC_R2_SFT 5
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5640_M_MONO_ADC_L1 (0x1 << 14)
+#define RT5640_M_MONO_ADC_L1_SFT 14
+#define RT5640_M_MONO_ADC_L2 (0x1 << 13)
+#define RT5640_M_MONO_ADC_L2_SFT 13
+#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12)
+#define RT5640_MONO_ADC_L1_SRC_SFT 12
+#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12)
+#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12)
+#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10)
+#define RT5640_MONO_ADC_L2_SRC_SFT 10
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10)
+#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10)
+#define RT5640_M_MONO_ADC_R1 (0x1 << 6)
+#define RT5640_M_MONO_ADC_R1_SFT 6
+#define RT5640_M_MONO_ADC_R2 (0x1 << 5)
+#define RT5640_M_MONO_ADC_R2_SFT 5
+#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_SFT 4
+#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4)
+#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4)
+#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2)
+#define RT5640_MONO_ADC_R2_SRC_SFT 2
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2)
+#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5640_M_ADCMIX_L (0x1 << 15)
+#define RT5640_M_ADCMIX_L_SFT 15
+#define RT5640_M_IF1_DAC_L (0x1 << 14)
+#define RT5640_M_IF1_DAC_L_SFT 14
+#define RT5640_M_ADCMIX_R (0x1 << 7)
+#define RT5640_M_ADCMIX_R_SFT 7
+#define RT5640_M_IF1_DAC_R (0x1 << 6)
+#define RT5640_M_IF1_DAC_R_SFT 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5640_M_DAC_L1 (0x1 << 14)
+#define RT5640_M_DAC_L1_SFT 14
+#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_STO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2 (0x1 << 12)
+#define RT5640_M_DAC_L2_SFT 12
+#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_STO_L_VOL_SFT 11
+#define RT5640_M_ANC_DAC_L (0x1 << 10)
+#define RT5640_M_ANC_DAC_L_SFT 10
+#define RT5640_M_DAC_R1 (0x1 << 6)
+#define RT5640_M_DAC_R1_SFT 6
+#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_STO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2 (0x1 << 4)
+#define RT5640_M_DAC_R2_SFT 4
+#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_STO_R_VOL_SFT 3
+#define RT5640_M_ANC_DAC_R (0x1 << 2)
+#define RT5640_M_ANC_DAC_R_SFT 2
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5640_M_DAC_L1_MONO_L (0x1 << 14)
+#define RT5640_M_DAC_L1_MONO_L_SFT 14
+#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13)
+#define RT5640_DAC_L1_MONO_L_VOL_SFT 13
+#define RT5640_M_DAC_L2_MONO_L (0x1 << 12)
+#define RT5640_M_DAC_L2_MONO_L_SFT 12
+#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11)
+#define RT5640_DAC_L2_MONO_L_VOL_SFT 11
+#define RT5640_M_DAC_R2_MONO_L (0x1 << 10)
+#define RT5640_M_DAC_R2_MONO_L_SFT 10
+#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9)
+#define RT5640_DAC_R2_MONO_L_VOL_SFT 9
+#define RT5640_M_DAC_R1_MONO_R (0x1 << 6)
+#define RT5640_M_DAC_R1_MONO_R_SFT 6
+#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5)
+#define RT5640_DAC_R1_MONO_R_VOL_SFT 5
+#define RT5640_M_DAC_R2_MONO_R (0x1 << 4)
+#define RT5640_M_DAC_R2_MONO_R_SFT 4
+#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3)
+#define RT5640_DAC_R2_MONO_R_VOL_SFT 3
+#define RT5640_M_DAC_L2_MONO_R (0x1 << 2)
+#define RT5640_M_DAC_L2_MONO_R_SFT 2
+#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1)
+#define RT5640_DAC_L2_MONO_R_VOL_SFT 1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5640_M_STO_L_DAC_L (0x1 << 15)
+#define RT5640_M_STO_L_DAC_L_SFT 15
+#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14)
+#define RT5640_STO_L_DAC_L_VOL_SFT 14
+#define RT5640_M_DAC_L2_DAC_L (0x1 << 13)
+#define RT5640_M_DAC_L2_DAC_L_SFT 13
+#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12)
+#define RT5640_DAC_L2_DAC_L_VOL_SFT 12
+#define RT5640_M_STO_R_DAC_R (0x1 << 11)
+#define RT5640_M_STO_R_DAC_R_SFT 11
+#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10)
+#define RT5640_STO_R_DAC_R_VOL_SFT 10
+#define RT5640_M_DAC_R2_DAC_R (0x1 << 9)
+#define RT5640_M_DAC_R2_DAC_R_SFT 9
+#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8)
+#define RT5640_DAC_R2_DAC_R_VOL_SFT 8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5640_RXDP_SRC_MASK (0x1 << 15)
+#define RT5640_RXDP_SRC_SFT 15
+#define RT5640_RXDP_SRC_NOR (0x0 << 15)
+#define RT5640_RXDP_SRC_DIV3 (0x1 << 15)
+#define RT5640_TXDP_SRC_MASK (0x1 << 14)
+#define RT5640_TXDP_SRC_SFT 14
+#define RT5640_TXDP_SRC_NOR (0x0 << 14)
+#define RT5640_TXDP_SRC_DIV3 (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5640_DAC_L2_SEL_MASK (0x3 << 14)
+#define RT5640_DAC_L2_SEL_SFT 14
+#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14)
+#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14)
+#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14)
+#define RT5640_DAC_L2_SEL_BASS (0x3 << 14)
+#define RT5640_DAC_R2_SEL_MASK (0x3 << 12)
+#define RT5640_DAC_R2_SEL_SFT 12
+#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12)
+#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12)
+#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12)
+#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11)
+#define RT5640_IF2_ADC_L_SEL_SFT 11
+#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11)
+#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11)
+#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10)
+#define RT5640_IF2_ADC_R_SEL_SFT 10
+#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10)
+#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10)
+#define RT5640_RXDC_SEL_MASK (0x3 << 8)
+#define RT5640_RXDC_SEL_SFT 8
+#define RT5640_RXDC_SEL_NOR (0x0 << 8)
+#define RT5640_RXDC_SEL_L2R (0x1 << 8)
+#define RT5640_RXDC_SEL_R2L (0x2 << 8)
+#define RT5640_RXDC_SEL_SWAP (0x3 << 8)
+#define RT5640_RXDP_SEL_MASK (0x3 << 6)
+#define RT5640_RXDP_SEL_SFT 6
+#define RT5640_RXDP_SEL_NOR (0x0 << 6)
+#define RT5640_RXDP_SEL_L2R (0x1 << 6)
+#define RT5640_RXDP_SEL_R2L (0x2 << 6)
+#define RT5640_RXDP_SEL_SWAP (0x3 << 6)
+#define RT5640_TXDC_SEL_MASK (0x3 << 4)
+#define RT5640_TXDC_SEL_SFT 4
+#define RT5640_TXDC_SEL_NOR (0x0 << 4)
+#define RT5640_TXDC_SEL_L2R (0x1 << 4)
+#define RT5640_TXDC_SEL_R2L (0x2 << 4)
+#define RT5640_TXDC_SEL_SWAP (0x3 << 4)
+#define RT5640_TXDP_SEL_MASK (0x3 << 2)
+#define RT5640_TXDP_SEL_SFT 2
+#define RT5640_TXDP_SEL_NOR (0x0 << 2)
+#define RT5640_TXDP_SEL_L2R (0x1 << 2)
+#define RT5640_TXDP_SEL_R2L (0x2 << 2)
+#define RT5640_TRXDP_SEL_SWAP (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SFT 14
+#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14)
+#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12)
+#define RT5640_IF1_ADC_SEL_SFT 12
+#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12)
+#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12)
+#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12)
+#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12)
+#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10)
+#define RT5640_IF2_DAC_SEL_SFT 10
+#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10)
+#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10)
+#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10)
+#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10)
+#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8)
+#define RT5640_IF2_ADC_SEL_SFT 8
+#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8)
+#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8)
+#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8)
+#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8)
+#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6)
+#define RT5640_IF3_DAC_SEL_SFT 6
+#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6)
+#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6)
+#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6)
+#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6)
+#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4)
+#define RT5640_IF3_ADC_SEL_SFT 4
+#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4)
+#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4)
+#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4)
+#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
+#define RT5640_G_HP_L_RM_L_SFT 13
+#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_IN_L_RM_L_SFT 10
+#define RT5640_G_BST4_RM_L_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_L_SFT 7
+#define RT5640_G_BST3_RM_L_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_L_SFT 4
+#define RT5640_G_BST2_RM_L_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_L_SFT 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5640_G_BST1_RM_L_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_L_SFT 13
+#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10)
+#define RT5640_G_OM_L_RM_L_SFT 10
+#define RT5640_M_HP_L_RM_L (0x1 << 6)
+#define RT5640_M_HP_L_RM_L_SFT 6
+#define RT5640_M_IN_L_RM_L (0x1 << 5)
+#define RT5640_M_IN_L_RM_L_SFT 5
+#define RT5640_M_BST4_RM_L (0x1 << 4)
+#define RT5640_M_BST4_RM_L_SFT 4
+#define RT5640_M_BST3_RM_L (0x1 << 3)
+#define RT5640_M_BST3_RM_L_SFT 3
+#define RT5640_M_BST2_RM_L (0x1 << 2)
+#define RT5640_M_BST2_RM_L_SFT 2
+#define RT5640_M_BST1_RM_L (0x1 << 1)
+#define RT5640_M_BST1_RM_L_SFT 1
+#define RT5640_M_OM_L_RM_L (0x1)
+#define RT5640_M_OM_L_RM_L_SFT 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13)
+#define RT5640_G_HP_R_RM_R_SFT 13
+#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_IN_R_RM_R_SFT 10
+#define RT5640_G_BST4_RM_R_MASK (0x7 << 7)
+#define RT5640_G_BST4_RM_R_SFT 7
+#define RT5640_G_BST3_RM_R_MASK (0x7 << 4)
+#define RT5640_G_BST3_RM_R_SFT 4
+#define RT5640_G_BST2_RM_R_MASK (0x7 << 1)
+#define RT5640_G_BST2_RM_R_SFT 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5640_G_BST1_RM_R_MASK (0x7 << 13)
+#define RT5640_G_BST1_RM_R_SFT 13
+#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10)
+#define RT5640_G_OM_R_RM_R_SFT 10
+#define RT5640_M_HP_R_RM_R (0x1 << 6)
+#define RT5640_M_HP_R_RM_R_SFT 6
+#define RT5640_M_IN_R_RM_R (0x1 << 5)
+#define RT5640_M_IN_R_RM_R_SFT 5
+#define RT5640_M_BST4_RM_R (0x1 << 4)
+#define RT5640_M_BST4_RM_R_SFT 4
+#define RT5640_M_BST3_RM_R (0x1 << 3)
+#define RT5640_M_BST3_RM_R_SFT 3
+#define RT5640_M_BST2_RM_R (0x1 << 2)
+#define RT5640_M_BST2_RM_R_SFT 2
+#define RT5640_M_BST1_RM_R (0x1 << 1)
+#define RT5640_M_BST1_RM_R_SFT 1
+#define RT5640_M_OM_R_RM_R (0x1)
+#define RT5640_M_OM_R_RM_R_SFT 0
+
+/* HPMIX Control (0x45) */
+#define RT5640_M_DAC2_HM (0x1 << 15)
+#define RT5640_M_DAC2_HM_SFT 15
+#define RT5640_M_DAC1_HM (0x1 << 14)
+#define RT5640_M_DAC1_HM_SFT 14
+#define RT5640_M_HPVOL_HM (0x1 << 13)
+#define RT5640_M_HPVOL_HM_SFT 13
+#define RT5640_G_HPOMIX_MASK (0x1 << 12)
+#define RT5640_G_HPOMIX_SFT 12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14)
+#define RT5640_G_RM_L_SM_L_SFT 14
+#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12)
+#define RT5640_G_IN_L_SM_L_SFT 12
+#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10)
+#define RT5640_G_DAC_L1_SM_L_SFT 10
+#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8)
+#define RT5640_G_DAC_L2_SM_L_SFT 8
+#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6)
+#define RT5640_G_OM_L_SM_L_SFT 6
+#define RT5640_M_RM_L_SM_L (0x1 << 5)
+#define RT5640_M_RM_L_SM_L_SFT 5
+#define RT5640_M_IN_L_SM_L (0x1 << 4)
+#define RT5640_M_IN_L_SM_L_SFT 4
+#define RT5640_M_DAC_L1_SM_L (0x1 << 3)
+#define RT5640_M_DAC_L1_SM_L_SFT 3
+#define RT5640_M_DAC_L2_SM_L (0x1 << 2)
+#define RT5640_M_DAC_L2_SM_L_SFT 2
+#define RT5640_M_OM_L_SM_L (0x1 << 1)
+#define RT5640_M_OM_L_SM_L_SFT 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14)
+#define RT5640_G_RM_R_SM_R_SFT 14
+#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12)
+#define RT5640_G_IN_R_SM_R_SFT 12
+#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10)
+#define RT5640_G_DAC_R1_SM_R_SFT 10
+#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8)
+#define RT5640_G_DAC_R2_SM_R_SFT 8
+#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6)
+#define RT5640_G_OM_R_SM_R_SFT 6
+#define RT5640_M_RM_R_SM_R (0x1 << 5)
+#define RT5640_M_RM_R_SM_R_SFT 5
+#define RT5640_M_IN_R_SM_R (0x1 << 4)
+#define RT5640_M_IN_R_SM_R_SFT 4
+#define RT5640_M_DAC_R1_SM_R (0x1 << 3)
+#define RT5640_M_DAC_R1_SM_R_SFT 3
+#define RT5640_M_DAC_R2_SM_R (0x1 << 2)
+#define RT5640_M_DAC_R2_SM_R_SFT 2
+#define RT5640_M_OM_R_SM_R (0x1 << 1)
+#define RT5640_M_OM_R_SM_R_SFT 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5640_M_DAC_R1_SPM_L (0x1 << 15)
+#define RT5640_M_DAC_R1_SPM_L_SFT 15
+#define RT5640_M_DAC_L1_SPM_L (0x1 << 14)
+#define RT5640_M_DAC_L1_SPM_L_SFT 14
+#define RT5640_M_SV_R_SPM_L (0x1 << 13)
+#define RT5640_M_SV_R_SPM_L_SFT 13
+#define RT5640_M_SV_L_SPM_L (0x1 << 12)
+#define RT5640_M_SV_L_SPM_L_SFT 12
+#define RT5640_M_BST1_SPM_L (0x1 << 11)
+#define RT5640_M_BST1_SPM_L_SFT 11
+
+/* SPORMIX Control (0x49) */
+#define RT5640_M_DAC_R1_SPM_R (0x1 << 13)
+#define RT5640_M_DAC_R1_SPM_R_SFT 13
+#define RT5640_M_SV_R_SPM_R (0x1 << 12)
+#define RT5640_M_SV_R_SPM_R_SFT 12
+#define RT5640_M_BST1_SPM_R (0x1 << 11)
+#define RT5640_M_BST1_SPM_R_SFT 11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5640_SPO_CLSD_RATIO_MASK (0x7)
+#define RT5640_SPO_CLSD_RATIO_SFT 0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5640_M_DAC_R2_MM (0x1 << 15)
+#define RT5640_M_DAC_R2_MM_SFT 15
+#define RT5640_M_DAC_L2_MM (0x1 << 14)
+#define RT5640_M_DAC_L2_MM_SFT 14
+#define RT5640_M_OV_R_MM (0x1 << 13)
+#define RT5640_M_OV_R_MM_SFT 13
+#define RT5640_M_OV_L_MM (0x1 << 12)
+#define RT5640_M_OV_L_MM_SFT 12
+#define RT5640_M_BST1_MM (0x1 << 11)
+#define RT5640_M_BST1_MM_SFT 11
+#define RT5640_G_MONOMIX_MASK (0x1 << 10)
+#define RT5640_G_MONOMIX_SFT 10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5640_G_BST3_OM_L_MASK (0x7 << 13)
+#define RT5640_G_BST3_OM_L_SFT 13
+#define RT5640_G_BST2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_L_SFT 10
+#define RT5640_G_BST1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_L_SFT 7
+#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4)
+#define RT5640_G_IN_L_OM_L_SFT 4
+#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1)
+#define RT5640_G_RM_L_OM_L_SFT 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13)
+#define RT5640_G_DAC_R2_OM_L_SFT 13
+#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10)
+#define RT5640_G_DAC_L2_OM_L_SFT 10
+#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7)
+#define RT5640_G_DAC_L1_OM_L_SFT 7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5640_M_SM_L_OM_L (0x1 << 8)
+#define RT5640_M_SM_L_OM_L_SFT 8
+#define RT5640_M_BST3_OM_L (0x1 << 7)
+#define RT5640_M_BST3_OM_L_SFT 7
+#define RT5640_M_BST2_OM_L (0x1 << 6)
+#define RT5640_M_BST2_OM_L_SFT 6
+#define RT5640_M_BST1_OM_L (0x1 << 5)
+#define RT5640_M_BST1_OM_L_SFT 5
+#define RT5640_M_IN_L_OM_L (0x1 << 4)
+#define RT5640_M_IN_L_OM_L_SFT 4
+#define RT5640_M_RM_L_OM_L (0x1 << 3)
+#define RT5640_M_RM_L_OM_L_SFT 3
+#define RT5640_M_DAC_R2_OM_L (0x1 << 2)
+#define RT5640_M_DAC_R2_OM_L_SFT 2
+#define RT5640_M_DAC_L2_OM_L (0x1 << 1)
+#define RT5640_M_DAC_L2_OM_L_SFT 1
+#define RT5640_M_DAC_L1_OM_L (0x1)
+#define RT5640_M_DAC_L1_OM_L_SFT 0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5640_G_BST4_OM_R_MASK (0x7 << 13)
+#define RT5640_G_BST4_OM_R_SFT 13
+#define RT5640_G_BST2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_BST2_OM_R_SFT 10
+#define RT5640_G_BST1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_BST1_OM_R_SFT 7
+#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4)
+#define RT5640_G_IN_R_OM_R_SFT 4
+#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1)
+#define RT5640_G_RM_R_OM_R_SFT 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13)
+#define RT5640_G_DAC_L2_OM_R_SFT 13
+#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10)
+#define RT5640_G_DAC_R2_OM_R_SFT 10
+#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7)
+#define RT5640_G_DAC_R1_OM_R_SFT 7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5640_M_SM_L_OM_R (0x1 << 8)
+#define RT5640_M_SM_L_OM_R_SFT 8
+#define RT5640_M_BST4_OM_R (0x1 << 7)
+#define RT5640_M_BST4_OM_R_SFT 7
+#define RT5640_M_BST2_OM_R (0x1 << 6)
+#define RT5640_M_BST2_OM_R_SFT 6
+#define RT5640_M_BST1_OM_R (0x1 << 5)
+#define RT5640_M_BST1_OM_R_SFT 5
+#define RT5640_M_IN_R_OM_R (0x1 << 4)
+#define RT5640_M_IN_R_OM_R_SFT 4
+#define RT5640_M_RM_R_OM_R (0x1 << 3)
+#define RT5640_M_RM_R_OM_R_SFT 3
+#define RT5640_M_DAC_L2_OM_R (0x1 << 2)
+#define RT5640_M_DAC_L2_OM_R_SFT 2
+#define RT5640_M_DAC_R2_OM_R (0x1 << 1)
+#define RT5640_M_DAC_R2_OM_R_SFT 1
+#define RT5640_M_DAC_R1_OM_R (0x1)
+#define RT5640_M_DAC_R1_OM_R_SFT 0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5640_M_DAC_L1_LM (0x1 << 15)
+#define RT5640_M_DAC_L1_LM_SFT 15
+#define RT5640_M_DAC_R1_LM (0x1 << 14)
+#define RT5640_M_DAC_R1_LM_SFT 14
+#define RT5640_M_OV_L_LM (0x1 << 13)
+#define RT5640_M_OV_L_LM_SFT 13
+#define RT5640_M_OV_R_LM (0x1 << 12)
+#define RT5640_M_OV_R_LM_SFT 12
+#define RT5640_G_LOUTMIX_MASK (0x1 << 11)
+#define RT5640_G_LOUTMIX_SFT 11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5640_PWR_I2S1 (0x1 << 15)
+#define RT5640_PWR_I2S1_BIT 15
+#define RT5640_PWR_I2S2 (0x1 << 14)
+#define RT5640_PWR_I2S2_BIT 14
+#define RT5640_PWR_DAC_L1 (0x1 << 12)
+#define RT5640_PWR_DAC_L1_BIT 12
+#define RT5640_PWR_DAC_R1 (0x1 << 11)
+#define RT5640_PWR_DAC_R1_BIT 11
+#define RT5640_PWR_DAC_L2 (0x1 << 7)
+#define RT5640_PWR_DAC_L2_BIT 7
+#define RT5640_PWR_DAC_R2 (0x1 << 6)
+#define RT5640_PWR_DAC_R2_BIT 6
+#define RT5640_PWR_ADC_L (0x1 << 2)
+#define RT5640_PWR_ADC_L_BIT 2
+#define RT5640_PWR_ADC_R (0x1 << 1)
+#define RT5640_PWR_ADC_R_BIT 1
+#define RT5640_PWR_CLS_D (0x1)
+#define RT5640_PWR_CLS_D_BIT 0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5640_PWR_ADC_SF (0x1 << 15)
+#define RT5640_PWR_ADC_SF_BIT 15
+#define RT5640_PWR_ADC_MF_L (0x1 << 14)
+#define RT5640_PWR_ADC_MF_L_BIT 14
+#define RT5640_PWR_ADC_MF_R (0x1 << 13)
+#define RT5640_PWR_ADC_MF_R_BIT 13
+#define RT5640_PWR_I2S_DSP (0x1 << 12)
+#define RT5640_PWR_I2S_DSP_BIT 12
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5640_PWR_VREF1 (0x1 << 15)
+#define RT5640_PWR_VREF1_BIT 15
+#define RT5640_PWR_FV1 (0x1 << 14)
+#define RT5640_PWR_FV1_BIT 14
+#define RT5640_PWR_MB (0x1 << 13)
+#define RT5640_PWR_MB_BIT 13
+#define RT5640_PWR_LM (0x1 << 12)
+#define RT5640_PWR_LM_BIT 12
+#define RT5640_PWR_BG (0x1 << 11)
+#define RT5640_PWR_BG_BIT 11
+#define RT5640_PWR_MM (0x1 << 10)
+#define RT5640_PWR_MM_BIT 10
+#define RT5640_PWR_MA (0x1 << 8)
+#define RT5640_PWR_MA_BIT 8
+#define RT5640_PWR_HP_L (0x1 << 7)
+#define RT5640_PWR_HP_L_BIT 7
+#define RT5640_PWR_HP_R (0x1 << 6)
+#define RT5640_PWR_HP_R_BIT 6
+#define RT5640_PWR_HA (0x1 << 5)
+#define RT5640_PWR_HA_BIT 5
+#define RT5640_PWR_VREF2 (0x1 << 4)
+#define RT5640_PWR_VREF2_BIT 4
+#define RT5640_PWR_FV2 (0x1 << 3)
+#define RT5640_PWR_FV2_BIT 3
+#define RT5640_PWR_LDO2 (0x1 << 2)
+#define RT5640_PWR_LDO2_BIT 2
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5640_PWR_BST1 (0x1 << 15)
+#define RT5640_PWR_BST1_BIT 15
+#define RT5640_PWR_BST2 (0x1 << 14)
+#define RT5640_PWR_BST2_BIT 14
+#define RT5640_PWR_BST3 (0x1 << 13)
+#define RT5640_PWR_BST3_BIT 13
+#define RT5640_PWR_BST4 (0x1 << 12)
+#define RT5640_PWR_BST4_BIT 12
+#define RT5640_PWR_MB1 (0x1 << 11)
+#define RT5640_PWR_MB1_BIT 11
+#define RT5640_PWR_PLL (0x1 << 9)
+#define RT5640_PWR_PLL_BIT 9
+
+/* Power Management for Mixer (0x65) */
+#define RT5640_PWR_OM_L (0x1 << 15)
+#define RT5640_PWR_OM_L_BIT 15
+#define RT5640_PWR_OM_R (0x1 << 14)
+#define RT5640_PWR_OM_R_BIT 14
+#define RT5640_PWR_SM_L (0x1 << 13)
+#define RT5640_PWR_SM_L_BIT 13
+#define RT5640_PWR_SM_R (0x1 << 12)
+#define RT5640_PWR_SM_R_BIT 12
+#define RT5640_PWR_RM_L (0x1 << 11)
+#define RT5640_PWR_RM_L_BIT 11
+#define RT5640_PWR_RM_R (0x1 << 10)
+#define RT5640_PWR_RM_R_BIT 10
+
+/* Power Management for Volume (0x66) */
+#define RT5640_PWR_SV_L (0x1 << 15)
+#define RT5640_PWR_SV_L_BIT 15
+#define RT5640_PWR_SV_R (0x1 << 14)
+#define RT5640_PWR_SV_R_BIT 14
+#define RT5640_PWR_OV_L (0x1 << 13)
+#define RT5640_PWR_OV_L_BIT 13
+#define RT5640_PWR_OV_R (0x1 << 12)
+#define RT5640_PWR_OV_R_BIT 12
+#define RT5640_PWR_HV_L (0x1 << 11)
+#define RT5640_PWR_HV_L_BIT 11
+#define RT5640_PWR_HV_R (0x1 << 10)
+#define RT5640_PWR_HV_R_BIT 10
+#define RT5640_PWR_IN_L (0x1 << 9)
+#define RT5640_PWR_IN_L_BIT 9
+#define RT5640_PWR_IN_R (0x1 << 8)
+#define RT5640_PWR_IN_R_BIT 8
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */
+#define RT5640_I2S_MS_MASK (0x1 << 15)
+#define RT5640_I2S_MS_SFT 15
+#define RT5640_I2S_MS_M (0x0 << 15)
+#define RT5640_I2S_MS_S (0x1 << 15)
+#define RT5640_I2S_IF_MASK (0x7 << 12)
+#define RT5640_I2S_IF_SFT 12
+#define RT5640_I2S_O_CP_MASK (0x3 << 10)
+#define RT5640_I2S_O_CP_SFT 10
+#define RT5640_I2S_O_CP_OFF (0x0 << 10)
+#define RT5640_I2S_O_CP_U_LAW (0x1 << 10)
+#define RT5640_I2S_O_CP_A_LAW (0x2 << 10)
+#define RT5640_I2S_I_CP_MASK (0x3 << 8)
+#define RT5640_I2S_I_CP_SFT 8
+#define RT5640_I2S_I_CP_OFF (0x0 << 8)
+#define RT5640_I2S_I_CP_U_LAW (0x1 << 8)
+#define RT5640_I2S_I_CP_A_LAW (0x2 << 8)
+#define RT5640_I2S_BP_MASK (0x1 << 7)
+#define RT5640_I2S_BP_SFT 7
+#define RT5640_I2S_BP_NOR (0x0 << 7)
+#define RT5640_I2S_BP_INV (0x1 << 7)
+#define RT5640_I2S_DL_MASK (0x3 << 2)
+#define RT5640_I2S_DL_SFT 2
+#define RT5640_I2S_DL_16 (0x0 << 2)
+#define RT5640_I2S_DL_20 (0x1 << 2)
+#define RT5640_I2S_DL_24 (0x2 << 2)
+#define RT5640_I2S_DL_8 (0x3 << 2)
+#define RT5640_I2S_DF_MASK (0x3)
+#define RT5640_I2S_DF_SFT 0
+#define RT5640_I2S_DF_I2S (0x0)
+#define RT5640_I2S_DF_LEFT (0x1)
+#define RT5640_I2S_DF_PCM_A (0x2)
+#define RT5640_I2S_DF_PCM_B (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5640_I2S2_SDI_MASK (0x1 << 6)
+#define RT5640_I2S2_SDI_SFT 6
+#define RT5640_I2S2_SDI_I2S1 (0x0 << 6)
+#define RT5640_I2S2_SDI_I2S2 (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15)
+#define RT5640_I2S_BCLK_MS1_SFT 15
+#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15)
+#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15)
+#define RT5640_I2S_PD1_MASK (0x7 << 12)
+#define RT5640_I2S_PD1_SFT 12
+#define RT5640_I2S_PD1_1 (0x0 << 12)
+#define RT5640_I2S_PD1_2 (0x1 << 12)
+#define RT5640_I2S_PD1_3 (0x2 << 12)
+#define RT5640_I2S_PD1_4 (0x3 << 12)
+#define RT5640_I2S_PD1_6 (0x4 << 12)
+#define RT5640_I2S_PD1_8 (0x5 << 12)
+#define RT5640_I2S_PD1_12 (0x6 << 12)
+#define RT5640_I2S_PD1_16 (0x7 << 12)
+#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11)
+#define RT5640_I2S_BCLK_MS2_SFT 11
+#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11)
+#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11)
+#define RT5640_I2S_PD2_MASK (0x7 << 8)
+#define RT5640_I2S_PD2_SFT 8
+#define RT5640_I2S_PD2_1 (0x0 << 8)
+#define RT5640_I2S_PD2_2 (0x1 << 8)
+#define RT5640_I2S_PD2_3 (0x2 << 8)
+#define RT5640_I2S_PD2_4 (0x3 << 8)
+#define RT5640_I2S_PD2_6 (0x4 << 8)
+#define RT5640_I2S_PD2_8 (0x5 << 8)
+#define RT5640_I2S_PD2_12 (0x6 << 8)
+#define RT5640_I2S_PD2_16 (0x7 << 8)
+#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7)
+#define RT5640_I2S_BCLK_MS3_SFT 7
+#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7)
+#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7)
+#define RT5640_I2S_PD3_MASK (0x7 << 4)
+#define RT5640_I2S_PD3_SFT 4
+#define RT5640_I2S_PD3_1 (0x0 << 4)
+#define RT5640_I2S_PD3_2 (0x1 << 4)
+#define RT5640_I2S_PD3_3 (0x2 << 4)
+#define RT5640_I2S_PD3_4 (0x3 << 4)
+#define RT5640_I2S_PD3_6 (0x4 << 4)
+#define RT5640_I2S_PD3_8 (0x5 << 4)
+#define RT5640_I2S_PD3_12 (0x6 << 4)
+#define RT5640_I2S_PD3_16 (0x7 << 4)
+#define RT5640_DAC_OSR_MASK (0x3 << 2)
+#define RT5640_DAC_OSR_SFT 2
+#define RT5640_DAC_OSR_128 (0x0 << 2)
+#define RT5640_DAC_OSR_64 (0x1 << 2)
+#define RT5640_DAC_OSR_32 (0x2 << 2)
+#define RT5640_DAC_OSR_16 (0x3 << 2)
+#define RT5640_ADC_OSR_MASK (0x3)
+#define RT5640_ADC_OSR_SFT 0
+#define RT5640_ADC_OSR_128 (0x0)
+#define RT5640_ADC_OSR_64 (0x1)
+#define RT5640_ADC_OSR_32 (0x2)
+#define RT5640_ADC_OSR_16 (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5640_DAC_L_OSR_MASK (0x3 << 14)
+#define RT5640_DAC_L_OSR_SFT 14
+#define RT5640_DAC_L_OSR_128 (0x0 << 14)
+#define RT5640_DAC_L_OSR_64 (0x1 << 14)
+#define RT5640_DAC_L_OSR_32 (0x2 << 14)
+#define RT5640_DAC_L_OSR_16 (0x3 << 14)
+#define RT5640_ADC_R_OSR_MASK (0x3 << 12)
+#define RT5640_ADC_R_OSR_SFT 12
+#define RT5640_ADC_R_OSR_128 (0x0 << 12)
+#define RT5640_ADC_R_OSR_64 (0x1 << 12)
+#define RT5640_ADC_R_OSR_32 (0x2 << 12)
+#define RT5640_ADC_R_OSR_16 (0x3 << 12)
+#define RT5640_DAHPF_EN (0x1 << 11)
+#define RT5640_DAHPF_EN_SFT 11
+#define RT5640_ADHPF_EN (0x1 << 10)
+#define RT5640_ADHPF_EN_SFT 10
+
+/* Digital Microphone Control (0x75) */
+#define RT5640_DMIC_1_EN_MASK (0x1 << 15)
+#define RT5640_DMIC_1_EN_SFT 15
+#define RT5640_DMIC_1_DIS (0x0 << 15)
+#define RT5640_DMIC_1_EN (0x1 << 15)
+#define RT5640_DMIC_2_EN_MASK (0x1 << 14)
+#define RT5640_DMIC_2_EN_SFT 14
+#define RT5640_DMIC_2_DIS (0x0 << 14)
+#define RT5640_DMIC_2_EN (0x1 << 14)
+#define RT5640_DMIC_1L_LH_MASK (0x1 << 13)
+#define RT5640_DMIC_1L_LH_SFT 13
+#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13)
+#define RT5640_DMIC_1L_LH_RISING (0x1 << 13)
+#define RT5640_DMIC_1R_LH_MASK (0x1 << 12)
+#define RT5640_DMIC_1R_LH_SFT 12
+#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12)
+#define RT5640_DMIC_1R_LH_RISING (0x1 << 12)
+#define RT5640_DMIC_1_DP_MASK (0x1 << 11)
+#define RT5640_DMIC_1_DP_SFT 11
+#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11)
+#define RT5640_DMIC_1_DP_IN1P (0x1 << 11)
+#define RT5640_DMIC_2_DP_MASK (0x1 << 10)
+#define RT5640_DMIC_2_DP_SFT 10
+#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10)
+#define RT5640_DMIC_2_DP_IN1N (0x1 << 10)
+#define RT5640_DMIC_2L_LH_MASK (0x1 << 9)
+#define RT5640_DMIC_2L_LH_SFT 9
+#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9)
+#define RT5640_DMIC_2L_LH_RISING (0x1 << 9)
+#define RT5640_DMIC_2R_LH_MASK (0x1 << 8)
+#define RT5640_DMIC_2R_LH_SFT 8
+#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8)
+#define RT5640_DMIC_2R_LH_RISING (0x1 << 8)
+#define RT5640_DMIC_CLK_MASK (0x7 << 5)
+#define RT5640_DMIC_CLK_SFT 5
+
+/* Global Clock Control (0x80) */
+#define RT5640_SCLK_SRC_MASK (0x3 << 14)
+#define RT5640_SCLK_SRC_SFT 14
+#define RT5640_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5640_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5640_SCLK_SRC_PLL1T (0x2 << 14)
+#define RT5640_SCLK_SRC_RCCLK (0x3 << 14) /* 15MHz */
+#define RT5640_PLL1_SRC_MASK (0x3 << 12)
+#define RT5640_PLL1_SRC_SFT 12
+#define RT5640_PLL1_SRC_MCLK (0x0 << 12)
+#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12)
+#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12)
+#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5640_PLL1_PD_MASK (0x1 << 3)
+#define RT5640_PLL1_PD_SFT 3
+#define RT5640_PLL1_PD_1 (0x0 << 3)
+#define RT5640_PLL1_PD_2 (0x1 << 3)
+
+#define RT5640_PLL_INP_MAX 40000000
+#define RT5640_PLL_INP_MIN 256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5640_PLL_N_MAX 0x1ff
+#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7)
+#define RT5640_PLL_N_SFT 7
+#define RT5640_PLL_K_MAX 0x1f
+#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX)
+#define RT5640_PLL_K_SFT 0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5640_PLL_M_MAX 0xf
+#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12)
+#define RT5640_PLL_M_SFT 12
+#define RT5640_PLL_M_BP (0x1 << 11)
+#define RT5640_PLL_M_BP_SFT 11
+
+/* ASRC Control 1 (0x83) */
+#define RT5640_STO_T_MASK (0x1 << 15)
+#define RT5640_STO_T_SFT 15
+#define RT5640_STO_T_SCLK (0x0 << 15)
+#define RT5640_STO_T_LRCK1 (0x1 << 15)
+#define RT5640_M1_T_MASK (0x1 << 14)
+#define RT5640_M1_T_SFT 14
+#define RT5640_M1_T_I2S2 (0x0 << 14)
+#define RT5640_M1_T_I2S2_D3 (0x1 << 14)
+#define RT5640_I2S2_F_MASK (0x1 << 12)
+#define RT5640_I2S2_F_SFT 12
+#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12)
+#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12)
+#define RT5640_DMIC_1_M_MASK (0x1 << 9)
+#define RT5640_DMIC_1_M_SFT 9
+#define RT5640_DMIC_1_M_NOR (0x0 << 9)
+#define RT5640_DMIC_1_M_ASYN (0x1 << 9)
+#define RT5640_DMIC_2_M_MASK (0x1 << 8)
+#define RT5640_DMIC_2_M_SFT 8
+#define RT5640_DMIC_2_M_NOR (0x0 << 8)
+#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
+
+/* ASRC Control 2 (0x84) */
+#define RT5640_MDA_L_M_MASK (0x1 << 15)
+#define RT5640_MDA_L_M_SFT 15
+#define RT5640_MDA_L_M_NOR (0x0 << 15)
+#define RT5640_MDA_L_M_ASYN (0x1 << 15)
+#define RT5640_MDA_R_M_MASK (0x1 << 14)
+#define RT5640_MDA_R_M_SFT 14
+#define RT5640_MDA_R_M_NOR (0x0 << 14)
+#define RT5640_MDA_R_M_ASYN (0x1 << 14)
+#define RT5640_MAD_L_M_MASK (0x1 << 13)
+#define RT5640_MAD_L_M_SFT 13
+#define RT5640_MAD_L_M_NOR (0x0 << 13)
+#define RT5640_MAD_L_M_ASYN (0x1 << 13)
+#define RT5640_MAD_R_M_MASK (0x1 << 12)
+#define RT5640_MAD_R_M_SFT 12
+#define RT5640_MAD_R_M_NOR (0x0 << 12)
+#define RT5640_MAD_R_M_ASYN (0x1 << 12)
+#define RT5640_ADC_M_MASK (0x1 << 11)
+#define RT5640_ADC_M_SFT 11
+#define RT5640_ADC_M_NOR (0x0 << 11)
+#define RT5640_ADC_M_ASYN (0x1 << 11)
+#define RT5640_STO_DAC_M_MASK (0x1 << 5)
+#define RT5640_STO_DAC_M_SFT 5
+#define RT5640_STO_DAC_M_NOR (0x0 << 5)
+#define RT5640_STO_DAC_M_ASYN (0x1 << 5)
+#define RT5640_I2S1_R_D_MASK (0x1 << 4)
+#define RT5640_I2S1_R_D_SFT 4
+#define RT5640_I2S1_R_D_DIS (0x0 << 4)
+#define RT5640_I2S1_R_D_EN (0x1 << 4)
+#define RT5640_I2S2_R_D_MASK (0x1 << 3)
+#define RT5640_I2S2_R_D_SFT 3
+#define RT5640_I2S2_R_D_DIS (0x0 << 3)
+#define RT5640_I2S2_R_D_EN (0x1 << 3)
+#define RT5640_PRE_SCLK_MASK (0x3)
+#define RT5640_PRE_SCLK_SFT 0
+#define RT5640_PRE_SCLK_512 (0x0)
+#define RT5640_PRE_SCLK_1024 (0x1)
+#define RT5640_PRE_SCLK_2048 (0x2)
+
+/* ASRC Control 3 (0x85) */
+#define RT5640_I2S1_RATE_MASK (0xf << 12)
+#define RT5640_I2S1_RATE_SFT 12
+#define RT5640_I2S2_RATE_MASK (0xf << 8)
+#define RT5640_I2S2_RATE_SFT 8
+
+/* ASRC Control 4 (0x89) */
+#define RT5640_I2S1_PD_MASK (0x7 << 12)
+#define RT5640_I2S1_PD_SFT 12
+#define RT5640_I2S2_PD_MASK (0x7 << 8)
+#define RT5640_I2S2_PD_SFT 8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5640_HP_OVCD_MASK (0x1 << 10)
+#define RT5640_HP_OVCD_SFT 10
+#define RT5640_HP_OVCD_DIS (0x0 << 10)
+#define RT5640_HP_OVCD_EN (0x1 << 10)
+#define RT5640_HP_OC_TH_MASK (0x3 << 8)
+#define RT5640_HP_OC_TH_SFT 8
+#define RT5640_HP_OC_TH_90 (0x0 << 8)
+#define RT5640_HP_OC_TH_105 (0x1 << 8)
+#define RT5640_HP_OC_TH_120 (0x2 << 8)
+#define RT5640_HP_OC_TH_135 (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5640_CLSD_OC_MASK (0x1 << 9)
+#define RT5640_CLSD_OC_SFT 9
+#define RT5640_CLSD_OC_PU (0x0 << 9)
+#define RT5640_CLSD_OC_PD (0x1 << 9)
+#define RT5640_AUTO_PD_MASK (0x1 << 8)
+#define RT5640_AUTO_PD_SFT 8
+#define RT5640_AUTO_PD_DIS (0x0 << 8)
+#define RT5640_AUTO_PD_EN (0x1 << 8)
+#define RT5640_CLSD_OC_TH_MASK (0x3f)
+#define RT5640_CLSD_OC_TH_SFT 0
+
+/* Class D Output Control (0x8d) */
+#define RT5640_CLSD_RATIO_MASK (0xf << 12)
+#define RT5640_CLSD_RATIO_SFT 12
+#define RT5640_CLSD_OM_MASK (0x1 << 11)
+#define RT5640_CLSD_OM_SFT 11
+#define RT5640_CLSD_OM_MONO (0x0 << 11)
+#define RT5640_CLSD_OM_STO (0x1 << 11)
+#define RT5640_CLSD_SCH_MASK (0x1 << 10)
+#define RT5640_CLSD_SCH_SFT 10
+#define RT5640_CLSD_SCH_L (0x0 << 10)
+#define RT5640_CLSD_SCH_S (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5640_SMT_TRIG_MASK (0x1 << 15)
+#define RT5640_SMT_TRIG_SFT 15
+#define RT5640_SMT_TRIG_DIS (0x0 << 15)
+#define RT5640_SMT_TRIG_EN (0x1 << 15)
+#define RT5640_HP_L_SMT_MASK (0x1 << 9)
+#define RT5640_HP_L_SMT_SFT 9
+#define RT5640_HP_L_SMT_DIS (0x0 << 9)
+#define RT5640_HP_L_SMT_EN (0x1 << 9)
+#define RT5640_HP_R_SMT_MASK (0x1 << 8)
+#define RT5640_HP_R_SMT_SFT 8
+#define RT5640_HP_R_SMT_DIS (0x0 << 8)
+#define RT5640_HP_R_SMT_EN (0x1 << 8)
+#define RT5640_HP_CD_PD_MASK (0x1 << 7)
+#define RT5640_HP_CD_PD_SFT 7
+#define RT5640_HP_CD_PD_DIS (0x0 << 7)
+#define RT5640_HP_CD_PD_EN (0x1 << 7)
+#define RT5640_RSTN_MASK (0x1 << 6)
+#define RT5640_RSTN_SFT 6
+#define RT5640_RSTN_DIS (0x0 << 6)
+#define RT5640_RSTN_EN (0x1 << 6)
+#define RT5640_RSTP_MASK (0x1 << 5)
+#define RT5640_RSTP_SFT 5
+#define RT5640_RSTP_DIS (0x0 << 5)
+#define RT5640_RSTP_EN (0x1 << 5)
+#define RT5640_HP_CO_MASK (0x1 << 4)
+#define RT5640_HP_CO_SFT 4
+#define RT5640_HP_CO_DIS (0x0 << 4)
+#define RT5640_HP_CO_EN (0x1 << 4)
+#define RT5640_HP_CP_MASK (0x1 << 3)
+#define RT5640_HP_CP_SFT 3
+#define RT5640_HP_CP_PD (0x0 << 3)
+#define RT5640_HP_CP_PU (0x1 << 3)
+#define RT5640_HP_SG_MASK (0x1 << 2)
+#define RT5640_HP_SG_SFT 2
+#define RT5640_HP_SG_DIS (0x0 << 2)
+#define RT5640_HP_SG_EN (0x1 << 2)
+#define RT5640_HP_DP_MASK (0x1 << 1)
+#define RT5640_HP_DP_SFT 1
+#define RT5640_HP_DP_PD (0x0 << 1)
+#define RT5640_HP_DP_PU (0x1 << 1)
+#define RT5640_HP_CB_MASK (0x1)
+#define RT5640_HP_CB_SFT 0
+#define RT5640_HP_CB_PD (0x0)
+#define RT5640_HP_CB_PU (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5640_DEPOP_MASK (0x1 << 13)
+#define RT5640_DEPOP_SFT 13
+#define RT5640_DEPOP_AUTO (0x0 << 13)
+#define RT5640_DEPOP_MAN (0x1 << 13)
+#define RT5640_RAMP_MASK (0x1 << 12)
+#define RT5640_RAMP_SFT 12
+#define RT5640_RAMP_DIS (0x0 << 12)
+#define RT5640_RAMP_EN (0x1 << 12)
+#define RT5640_BPS_MASK (0x1 << 11)
+#define RT5640_BPS_SFT 11
+#define RT5640_BPS_DIS (0x0 << 11)
+#define RT5640_BPS_EN (0x1 << 11)
+#define RT5640_FAST_UPDN_MASK (0x1 << 10)
+#define RT5640_FAST_UPDN_SFT 10
+#define RT5640_FAST_UPDN_DIS (0x0 << 10)
+#define RT5640_FAST_UPDN_EN (0x1 << 10)
+#define RT5640_MRES_MASK (0x3 << 8)
+#define RT5640_MRES_SFT 8
+#define RT5640_MRES_15MO (0x0 << 8)
+#define RT5640_MRES_25MO (0x1 << 8)
+#define RT5640_MRES_35MO (0x2 << 8)
+#define RT5640_MRES_45MO (0x3 << 8)
+#define RT5640_VLO_MASK (0x1 << 7)
+#define RT5640_VLO_SFT 7
+#define RT5640_VLO_3V (0x0 << 7)
+#define RT5640_VLO_32V (0x1 << 7)
+#define RT5640_DIG_DP_MASK (0x1 << 6)
+#define RT5640_DIG_DP_SFT 6
+#define RT5640_DIG_DP_DIS (0x0 << 6)
+#define RT5640_DIG_DP_EN (0x1 << 6)
+#define RT5640_DP_TH_MASK (0x3 << 4)
+#define RT5640_DP_TH_SFT 4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5640_CP_SYS_MASK (0x7 << 12)
+#define RT5640_CP_SYS_SFT 12
+#define RT5640_CP_FQ1_MASK (0x7 << 8)
+#define RT5640_CP_FQ1_SFT 8
+#define RT5640_CP_FQ2_MASK (0x7 << 4)
+#define RT5640_CP_FQ2_SFT 4
+#define RT5640_CP_FQ3_MASK (0x7)
+#define RT5640_CP_FQ3_SFT 0
+#define RT5640_CP_FQ_1_5_KHZ 0
+#define RT5640_CP_FQ_3_KHZ 1
+#define RT5640_CP_FQ_6_KHZ 2
+#define RT5640_CP_FQ_12_KHZ 3
+#define RT5640_CP_FQ_24_KHZ 4
+#define RT5640_CP_FQ_48_KHZ 5
+#define RT5640_CP_FQ_96_KHZ 6
+#define RT5640_CP_FQ_192_KHZ 7
+
+/* HPOUT charge pump (0x91) */
+#define RT5640_OSW_L_MASK (0x1 << 11)
+#define RT5640_OSW_L_SFT 11
+#define RT5640_OSW_L_DIS (0x0 << 11)
+#define RT5640_OSW_L_EN (0x1 << 11)
+#define RT5640_OSW_R_MASK (0x1 << 10)
+#define RT5640_OSW_R_SFT 10
+#define RT5640_OSW_R_DIS (0x0 << 10)
+#define RT5640_OSW_R_EN (0x1 << 10)
+#define RT5640_PM_HP_MASK (0x3 << 8)
+#define RT5640_PM_HP_SFT 8
+#define RT5640_PM_HP_LV (0x0 << 8)
+#define RT5640_PM_HP_MV (0x1 << 8)
+#define RT5640_PM_HP_HV (0x2 << 8)
+#define RT5640_IB_HP_MASK (0x3 << 6)
+#define RT5640_IB_HP_SFT 6
+#define RT5640_IB_HP_125IL (0x0 << 6)
+#define RT5640_IB_HP_25IL (0x1 << 6)
+#define RT5640_IB_HP_5IL (0x2 << 6)
+#define RT5640_IB_HP_1IL (0x3 << 6)
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5640_PVDD_DET_MASK (0x1 << 15)
+#define RT5640_PVDD_DET_SFT 15
+#define RT5640_PVDD_DET_DIS (0x0 << 15)
+#define RT5640_PVDD_DET_EN (0x1 << 15)
+#define RT5640_SPK_AG_MASK (0x1 << 14)
+#define RT5640_SPK_AG_SFT 14
+#define RT5640_SPK_AG_DIS (0x0 << 14)
+#define RT5640_SPK_AG_EN (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5640_MIC1_BS_MASK (0x1 << 15)
+#define RT5640_MIC1_BS_SFT 15
+#define RT5640_MIC1_BS_9AV (0x0 << 15)
+#define RT5640_MIC1_BS_75AV (0x1 << 15)
+#define RT5640_MIC2_BS_MASK (0x1 << 14)
+#define RT5640_MIC2_BS_SFT 14
+#define RT5640_MIC2_BS_9AV (0x0 << 14)
+#define RT5640_MIC2_BS_75AV (0x1 << 14)
+#define RT5640_MIC1_CLK_MASK (0x1 << 13)
+#define RT5640_MIC1_CLK_SFT 13
+#define RT5640_MIC1_CLK_DIS (0x0 << 13)
+#define RT5640_MIC1_CLK_EN (0x1 << 13)
+#define RT5640_MIC2_CLK_MASK (0x1 << 12)
+#define RT5640_MIC2_CLK_SFT 12
+#define RT5640_MIC2_CLK_DIS (0x0 << 12)
+#define RT5640_MIC2_CLK_EN (0x1 << 12)
+#define RT5640_MIC1_OVCD_MASK (0x1 << 11)
+#define RT5640_MIC1_OVCD_SFT 11
+#define RT5640_MIC1_OVCD_DIS (0x0 << 11)
+#define RT5640_MIC1_OVCD_EN (0x1 << 11)
+#define RT5640_MIC1_OVTH_MASK (0x3 << 9)
+#define RT5640_MIC1_OVTH_SFT 9
+#define RT5640_MIC1_OVTH_600UA (0x0 << 9)
+#define RT5640_MIC1_OVTH_1500UA (0x1 << 9)
+#define RT5640_MIC1_OVTH_2000UA (0x2 << 9)
+#define RT5640_MIC2_OVCD_MASK (0x1 << 8)
+#define RT5640_MIC2_OVCD_SFT 8
+#define RT5640_MIC2_OVCD_DIS (0x0 << 8)
+#define RT5640_MIC2_OVCD_EN (0x1 << 8)
+#define RT5640_MIC2_OVTH_MASK (0x3 << 6)
+#define RT5640_MIC2_OVTH_SFT 6
+#define RT5640_MIC2_OVTH_600UA (0x0 << 6)
+#define RT5640_MIC2_OVTH_1500UA (0x1 << 6)
+#define RT5640_MIC2_OVTH_2000UA (0x2 << 6)
+#define RT5640_PWR_MB_MASK (0x1 << 5)
+#define RT5640_PWR_MB_SFT 5
+#define RT5640_PWR_MB_PD (0x0 << 5)
+#define RT5640_PWR_MB_PU (0x1 << 5)
+#define RT5640_PWR_CLK25M_MASK (0x1 << 4)
+#define RT5640_PWR_CLK25M_SFT 4
+#define RT5640_PWR_CLK25M_PD (0x0 << 4)
+#define RT5640_PWR_CLK25M_PU (0x1 << 4)
+
+/* EQ Control 1 (0xb0) */
+#define RT5640_EQ_SRC_MASK (0x1 << 15)
+#define RT5640_EQ_SRC_SFT 15
+#define RT5640_EQ_SRC_DAC (0x0 << 15)
+#define RT5640_EQ_SRC_ADC (0x1 << 15)
+#define RT5640_EQ_UPD (0x1 << 14)
+#define RT5640_EQ_UPD_BIT 14
+#define RT5640_EQ_CD_MASK (0x1 << 13)
+#define RT5640_EQ_CD_SFT 13
+#define RT5640_EQ_CD_DIS (0x0 << 13)
+#define RT5640_EQ_CD_EN (0x1 << 13)
+#define RT5640_EQ_DITH_MASK (0x3 << 8)
+#define RT5640_EQ_DITH_SFT 8
+#define RT5640_EQ_DITH_NOR (0x0 << 8)
+#define RT5640_EQ_DITH_LSB (0x1 << 8)
+#define RT5640_EQ_DITH_LSB_1 (0x2 << 8)
+#define RT5640_EQ_DITH_LSB_2 (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5640_EQ_HPF1_M_MASK (0x1 << 8)
+#define RT5640_EQ_HPF1_M_SFT 8
+#define RT5640_EQ_HPF1_M_HI (0x0 << 8)
+#define RT5640_EQ_HPF1_M_1ST (0x1 << 8)
+#define RT5640_EQ_LPF1_M_MASK (0x1 << 7)
+#define RT5640_EQ_LPF1_M_SFT 7
+#define RT5640_EQ_LPF1_M_LO (0x0 << 7)
+#define RT5640_EQ_LPF1_M_1ST (0x1 << 7)
+#define RT5640_EQ_HPF2_MASK (0x1 << 6)
+#define RT5640_EQ_HPF2_SFT 6
+#define RT5640_EQ_HPF2_DIS (0x0 << 6)
+#define RT5640_EQ_HPF2_EN (0x1 << 6)
+#define RT5640_EQ_HPF1_MASK (0x1 << 5)
+#define RT5640_EQ_HPF1_SFT 5
+#define RT5640_EQ_HPF1_DIS (0x0 << 5)
+#define RT5640_EQ_HPF1_EN (0x1 << 5)
+#define RT5640_EQ_BPF4_MASK (0x1 << 4)
+#define RT5640_EQ_BPF4_SFT 4
+#define RT5640_EQ_BPF4_DIS (0x0 << 4)
+#define RT5640_EQ_BPF4_EN (0x1 << 4)
+#define RT5640_EQ_BPF3_MASK (0x1 << 3)
+#define RT5640_EQ_BPF3_SFT 3
+#define RT5640_EQ_BPF3_DIS (0x0 << 3)
+#define RT5640_EQ_BPF3_EN (0x1 << 3)
+#define RT5640_EQ_BPF2_MASK (0x1 << 2)
+#define RT5640_EQ_BPF2_SFT 2
+#define RT5640_EQ_BPF2_DIS (0x0 << 2)
+#define RT5640_EQ_BPF2_EN (0x1 << 2)
+#define RT5640_EQ_BPF1_MASK (0x1 << 1)
+#define RT5640_EQ_BPF1_SFT 1
+#define RT5640_EQ_BPF1_DIS (0x0 << 1)
+#define RT5640_EQ_BPF1_EN (0x1 << 1)
+#define RT5640_EQ_LPF_MASK (0x1)
+#define RT5640_EQ_LPF_SFT 0
+#define RT5640_EQ_LPF_DIS (0x0)
+#define RT5640_EQ_LPF_EN (0x1)
+
+/* Memory Test (0xb2) */
+#define RT5640_MT_MASK (0x1 << 15)
+#define RT5640_MT_SFT 15
+#define RT5640_MT_DIS (0x0 << 15)
+#define RT5640_MT_EN (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5640_DRC_AGC_P_MASK (0x1 << 15)
+#define RT5640_DRC_AGC_P_SFT 15
+#define RT5640_DRC_AGC_P_DAC (0x0 << 15)
+#define RT5640_DRC_AGC_P_ADC (0x1 << 15)
+#define RT5640_DRC_AGC_MASK (0x1 << 14)
+#define RT5640_DRC_AGC_SFT 14
+#define RT5640_DRC_AGC_DIS (0x0 << 14)
+#define RT5640_DRC_AGC_EN (0x1 << 14)
+#define RT5640_DRC_AGC_UPD (0x1 << 13)
+#define RT5640_DRC_AGC_UPD_BIT 13
+#define RT5640_DRC_AGC_AR_MASK (0x1f << 8)
+#define RT5640_DRC_AGC_AR_SFT 8
+#define RT5640_DRC_AGC_R_MASK (0x7 << 5)
+#define RT5640_DRC_AGC_R_SFT 5
+#define RT5640_DRC_AGC_R_48K (0x1 << 5)
+#define RT5640_DRC_AGC_R_96K (0x2 << 5)
+#define RT5640_DRC_AGC_R_192K (0x3 << 5)
+#define RT5640_DRC_AGC_R_441K (0x5 << 5)
+#define RT5640_DRC_AGC_R_882K (0x6 << 5)
+#define RT5640_DRC_AGC_R_1764K (0x7 << 5)
+#define RT5640_DRC_AGC_RC_MASK (0x1f)
+#define RT5640_DRC_AGC_RC_SFT 0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5640_DRC_AGC_POB_MASK (0x3f << 8)
+#define RT5640_DRC_AGC_POB_SFT 8
+#define RT5640_DRC_AGC_CP_MASK (0x1 << 7)
+#define RT5640_DRC_AGC_CP_SFT 7
+#define RT5640_DRC_AGC_CP_DIS (0x0 << 7)
+#define RT5640_DRC_AGC_CP_EN (0x1 << 7)
+#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5)
+#define RT5640_DRC_AGC_CPR_SFT 5
+#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5)
+#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5)
+#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5)
+#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5)
+#define RT5640_DRC_AGC_PRB_MASK (0x1f)
+#define RT5640_DRC_AGC_PRB_SFT 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5640_DRC_AGC_NGB_MASK (0xf << 12)
+#define RT5640_DRC_AGC_NGB_SFT 12
+#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7)
+#define RT5640_DRC_AGC_TAR_SFT 7
+#define RT5640_DRC_AGC_NG_MASK (0x1 << 6)
+#define RT5640_DRC_AGC_NG_SFT 6
+#define RT5640_DRC_AGC_NG_DIS (0x0 << 6)
+#define RT5640_DRC_AGC_NG_EN (0x1 << 6)
+#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5)
+#define RT5640_DRC_AGC_NGH_SFT 5
+#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5)
+#define RT5640_DRC_AGC_NGH_EN (0x1 << 5)
+#define RT5640_DRC_AGC_NGT_MASK (0x1f)
+#define RT5640_DRC_AGC_NGT_SFT 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5640_ANC_M_MASK (0x1 << 15)
+#define RT5640_ANC_M_SFT 15
+#define RT5640_ANC_M_NOR (0x0 << 15)
+#define RT5640_ANC_M_REV (0x1 << 15)
+#define RT5640_ANC_MASK (0x1 << 14)
+#define RT5640_ANC_SFT 14
+#define RT5640_ANC_DIS (0x0 << 14)
+#define RT5640_ANC_EN (0x1 << 14)
+#define RT5640_ANC_MD_MASK (0x3 << 12)
+#define RT5640_ANC_MD_SFT 12
+#define RT5640_ANC_MD_DIS (0x0 << 12)
+#define RT5640_ANC_MD_67MS (0x1 << 12)
+#define RT5640_ANC_MD_267MS (0x2 << 12)
+#define RT5640_ANC_MD_1067MS (0x3 << 12)
+#define RT5640_ANC_SN_MASK (0x1 << 11)
+#define RT5640_ANC_SN_SFT 11
+#define RT5640_ANC_SN_DIS (0x0 << 11)
+#define RT5640_ANC_SN_EN (0x1 << 11)
+#define RT5640_ANC_CLK_MASK (0x1 << 10)
+#define RT5640_ANC_CLK_SFT 10
+#define RT5640_ANC_CLK_ANC (0x0 << 10)
+#define RT5640_ANC_CLK_REG (0x1 << 10)
+#define RT5640_ANC_ZCD_MASK (0x3 << 8)
+#define RT5640_ANC_ZCD_SFT 8
+#define RT5640_ANC_ZCD_DIS (0x0 << 8)
+#define RT5640_ANC_ZCD_T1 (0x1 << 8)
+#define RT5640_ANC_ZCD_T2 (0x2 << 8)
+#define RT5640_ANC_ZCD_WT (0x3 << 8)
+#define RT5640_ANC_CS_MASK (0x1 << 7)
+#define RT5640_ANC_CS_SFT 7
+#define RT5640_ANC_CS_DIS (0x0 << 7)
+#define RT5640_ANC_CS_EN (0x1 << 7)
+#define RT5640_ANC_SW_MASK (0x1 << 6)
+#define RT5640_ANC_SW_SFT 6
+#define RT5640_ANC_SW_NOR (0x0 << 6)
+#define RT5640_ANC_SW_AUTO (0x1 << 6)
+#define RT5640_ANC_CO_L_MASK (0x3f)
+#define RT5640_ANC_CO_L_SFT 0
+
+/* ANC Control 2 (0xb6) */
+#define RT5640_ANC_FG_R_MASK (0xf << 12)
+#define RT5640_ANC_FG_R_SFT 12
+#define RT5640_ANC_FG_L_MASK (0xf << 8)
+#define RT5640_ANC_FG_L_SFT 8
+#define RT5640_ANC_CG_R_MASK (0xf << 4)
+#define RT5640_ANC_CG_R_SFT 4
+#define RT5640_ANC_CG_L_MASK (0xf)
+#define RT5640_ANC_CG_L_SFT 0
+
+/* ANC Control 3 (0xb6) */
+#define RT5640_ANC_CD_MASK (0x1 << 6)
+#define RT5640_ANC_CD_SFT 6
+#define RT5640_ANC_CD_BOTH (0x0 << 6)
+#define RT5640_ANC_CD_IND (0x1 << 6)
+#define RT5640_ANC_CO_R_MASK (0x3f)
+#define RT5640_ANC_CO_R_SFT 0
+
+/* Jack Detect Control (0xbb) */
+#define RT5640_JD_MASK (0x7 << 13)
+#define RT5640_JD_SFT 13
+#define RT5640_JD_DIS (0x0 << 13)
+#define RT5640_JD_GPIO1 (0x1 << 13)
+#define RT5640_JD_JD1_IN4P (0x2 << 13)
+#define RT5640_JD_JD2_IN4N (0x3 << 13)
+#define RT5640_JD_GPIO2 (0x4 << 13)
+#define RT5640_JD_GPIO3 (0x5 << 13)
+#define RT5640_JD_GPIO4 (0x6 << 13)
+#define RT5640_JD_HP_MASK (0x1 << 11)
+#define RT5640_JD_HP_SFT 11
+#define RT5640_JD_HP_DIS (0x0 << 11)
+#define RT5640_JD_HP_EN (0x1 << 11)
+#define RT5640_JD_HP_TRG_MASK (0x1 << 10)
+#define RT5640_JD_HP_TRG_SFT 10
+#define RT5640_JD_HP_TRG_LO (0x0 << 10)
+#define RT5640_JD_HP_TRG_HI (0x1 << 10)
+#define RT5640_JD_SPL_MASK (0x1 << 9)
+#define RT5640_JD_SPL_SFT 9
+#define RT5640_JD_SPL_DIS (0x0 << 9)
+#define RT5640_JD_SPL_EN (0x1 << 9)
+#define RT5640_JD_SPL_TRG_MASK (0x1 << 8)
+#define RT5640_JD_SPL_TRG_SFT 8
+#define RT5640_JD_SPL_TRG_LO (0x0 << 8)
+#define RT5640_JD_SPL_TRG_HI (0x1 << 8)
+#define RT5640_JD_SPR_MASK (0x1 << 7)
+#define RT5640_JD_SPR_SFT 7
+#define RT5640_JD_SPR_DIS (0x0 << 7)
+#define RT5640_JD_SPR_EN (0x1 << 7)
+#define RT5640_JD_SPR_TRG_MASK (0x1 << 6)
+#define RT5640_JD_SPR_TRG_SFT 6
+#define RT5640_JD_SPR_TRG_LO (0x0 << 6)
+#define RT5640_JD_SPR_TRG_HI (0x1 << 6)
+#define RT5640_JD_MO_MASK (0x1 << 5)
+#define RT5640_JD_MO_SFT 5
+#define RT5640_JD_MO_DIS (0x0 << 5)
+#define RT5640_JD_MO_EN (0x1 << 5)
+#define RT5640_JD_MO_TRG_MASK (0x1 << 4)
+#define RT5640_JD_MO_TRG_SFT 4
+#define RT5640_JD_MO_TRG_LO (0x0 << 4)
+#define RT5640_JD_MO_TRG_HI (0x1 << 4)
+#define RT5640_JD_LO_MASK (0x1 << 3)
+#define RT5640_JD_LO_SFT 3
+#define RT5640_JD_LO_DIS (0x0 << 3)
+#define RT5640_JD_LO_EN (0x1 << 3)
+#define RT5640_JD_LO_TRG_MASK (0x1 << 2)
+#define RT5640_JD_LO_TRG_SFT 2
+#define RT5640_JD_LO_TRG_LO (0x0 << 2)
+#define RT5640_JD_LO_TRG_HI (0x1 << 2)
+#define RT5640_JD1_IN4P_MASK (0x1 << 1)
+#define RT5640_JD1_IN4P_SFT 1
+#define RT5640_JD1_IN4P_DIS (0x0 << 1)
+#define RT5640_JD1_IN4P_EN (0x1 << 1)
+#define RT5640_JD2_IN4N_MASK (0x1)
+#define RT5640_JD2_IN4N_SFT 0
+#define RT5640_JD2_IN4N_DIS (0x0)
+#define RT5640_JD2_IN4N_EN (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5640_ANC_DET_MASK (0x3 << 4)
+#define RT5640_ANC_DET_SFT 4
+#define RT5640_ANC_DET_DIS (0x0 << 4)
+#define RT5640_ANC_DET_MB1 (0x1 << 4)
+#define RT5640_ANC_DET_MB2 (0x2 << 4)
+#define RT5640_ANC_DET_JD (0x3 << 4)
+#define RT5640_AD_TRG_MASK (0x1 << 3)
+#define RT5640_AD_TRG_SFT 3
+#define RT5640_AD_TRG_LO (0x0 << 3)
+#define RT5640_AD_TRG_HI (0x1 << 3)
+#define RT5640_ANCM_DET_MASK (0x3 << 4)
+#define RT5640_ANCM_DET_SFT 4
+#define RT5640_ANCM_DET_DIS (0x0 << 4)
+#define RT5640_ANCM_DET_MB1 (0x1 << 4)
+#define RT5640_ANCM_DET_MB2 (0x2 << 4)
+#define RT5640_ANCM_DET_JD (0x3 << 4)
+#define RT5640_AMD_TRG_MASK (0x1 << 3)
+#define RT5640_AMD_TRG_SFT 3
+#define RT5640_AMD_TRG_LO (0x0 << 3)
+#define RT5640_AMD_TRG_HI (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5640_IRQ_JD_MASK (0x1 << 15)
+#define RT5640_IRQ_JD_SFT 15
+#define RT5640_IRQ_JD_BP (0x0 << 15)
+#define RT5640_IRQ_JD_NOR (0x1 << 15)
+#define RT5640_IRQ_OT_MASK (0x1 << 14)
+#define RT5640_IRQ_OT_SFT 14
+#define RT5640_IRQ_OT_BP (0x0 << 14)
+#define RT5640_IRQ_OT_NOR (0x1 << 14)
+#define RT5640_JD_STKY_MASK (0x1 << 13)
+#define RT5640_JD_STKY_SFT 13
+#define RT5640_JD_STKY_DIS (0x0 << 13)
+#define RT5640_JD_STKY_EN (0x1 << 13)
+#define RT5640_OT_STKY_MASK (0x1 << 12)
+#define RT5640_OT_STKY_SFT 12
+#define RT5640_OT_STKY_DIS (0x0 << 12)
+#define RT5640_OT_STKY_EN (0x1 << 12)
+#define RT5640_JD_P_MASK (0x1 << 11)
+#define RT5640_JD_P_SFT 11
+#define RT5640_JD_P_NOR (0x0 << 11)
+#define RT5640_JD_P_INV (0x1 << 11)
+#define RT5640_OT_P_MASK (0x1 << 10)
+#define RT5640_OT_P_SFT 10
+#define RT5640_OT_P_NOR (0x0 << 10)
+#define RT5640_OT_P_INV (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15)
+#define RT5640_IRQ_MB1_OC_SFT 15
+#define RT5640_IRQ_MB1_OC_BP (0x0 << 15)
+#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15)
+#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14)
+#define RT5640_IRQ_MB2_OC_SFT 14
+#define RT5640_IRQ_MB2_OC_BP (0x0 << 14)
+#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14)
+#define RT5640_MB1_OC_STKY_MASK (0x1 << 11)
+#define RT5640_MB1_OC_STKY_SFT 11
+#define RT5640_MB1_OC_STKY_DIS (0x0 << 11)
+#define RT5640_MB1_OC_STKY_EN (0x1 << 11)
+#define RT5640_MB2_OC_STKY_MASK (0x1 << 10)
+#define RT5640_MB2_OC_STKY_SFT 10
+#define RT5640_MB2_OC_STKY_DIS (0x0 << 10)
+#define RT5640_MB2_OC_STKY_EN (0x1 << 10)
+#define RT5640_MB1_OC_P_MASK (0x1 << 7)
+#define RT5640_MB1_OC_P_SFT 7
+#define RT5640_MB1_OC_P_NOR (0x0 << 7)
+#define RT5640_MB1_OC_P_INV (0x1 << 7)
+#define RT5640_MB2_OC_P_MASK (0x1 << 6)
+#define RT5640_MB2_OC_P_SFT 6
+#define RT5640_MB2_OC_P_NOR (0x0 << 6)
+#define RT5640_MB2_OC_P_INV (0x1 << 6)
+#define RT5640_MB1_OC_CLR (0x1 << 3)
+#define RT5640_MB1_OC_CLR_SFT 3
+#define RT5640_MB2_OC_CLR (0x1 << 2)
+#define RT5640_MB2_OC_CLR_SFT 2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5640_GP1_PIN_MASK (0x1 << 15)
+#define RT5640_GP1_PIN_SFT 15
+#define RT5640_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5640_GP1_PIN_IRQ (0x1 << 15)
+#define RT5640_GP2_PIN_MASK (0x1 << 14)
+#define RT5640_GP2_PIN_SFT 14
+#define RT5640_GP2_PIN_GPIO2 (0x0 << 14)
+#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14)
+#define RT5640_GP3_PIN_MASK (0x3 << 12)
+#define RT5640_GP3_PIN_SFT 12
+#define RT5640_GP3_PIN_GPIO3 (0x0 << 12)
+#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12)
+#define RT5640_GP3_PIN_IRQ (0x2 << 12)
+#define RT5640_GP4_PIN_MASK (0x1 << 11)
+#define RT5640_GP4_PIN_SFT 11
+#define RT5640_GP4_PIN_GPIO4 (0x0 << 11)
+#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11)
+#define RT5640_DP_SIG_MASK (0x1 << 10)
+#define RT5640_DP_SIG_SFT 10
+#define RT5640_DP_SIG_TEST (0x0 << 10)
+#define RT5640_DP_SIG_AP (0x1 << 10)
+#define RT5640_GPIO_M_MASK (0x1 << 9)
+#define RT5640_GPIO_M_SFT 9
+#define RT5640_GPIO_M_FLT (0x0 << 9)
+#define RT5640_GPIO_M_PH (0x1 << 9)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5640_GP4_PF_MASK (0x1 << 11)
+#define RT5640_GP4_PF_SFT 11
+#define RT5640_GP4_PF_IN (0x0 << 11)
+#define RT5640_GP4_PF_OUT (0x1 << 11)
+#define RT5640_GP4_OUT_MASK (0x1 << 10)
+#define RT5640_GP4_OUT_SFT 10
+#define RT5640_GP4_OUT_LO (0x0 << 10)
+#define RT5640_GP4_OUT_HI (0x1 << 10)
+#define RT5640_GP4_P_MASK (0x1 << 9)
+#define RT5640_GP4_P_SFT 9
+#define RT5640_GP4_P_NOR (0x0 << 9)
+#define RT5640_GP4_P_INV (0x1 << 9)
+#define RT5640_GP3_PF_MASK (0x1 << 8)
+#define RT5640_GP3_PF_SFT 8
+#define RT5640_GP3_PF_IN (0x0 << 8)
+#define RT5640_GP3_PF_OUT (0x1 << 8)
+#define RT5640_GP3_OUT_MASK (0x1 << 7)
+#define RT5640_GP3_OUT_SFT 7
+#define RT5640_GP3_OUT_LO (0x0 << 7)
+#define RT5640_GP3_OUT_HI (0x1 << 7)
+#define RT5640_GP3_P_MASK (0x1 << 6)
+#define RT5640_GP3_P_SFT 6
+#define RT5640_GP3_P_NOR (0x0 << 6)
+#define RT5640_GP3_P_INV (0x1 << 6)
+#define RT5640_GP2_PF_MASK (0x1 << 5)
+#define RT5640_GP2_PF_SFT 5
+#define RT5640_GP2_PF_IN (0x0 << 5)
+#define RT5640_GP2_PF_OUT (0x1 << 5)
+#define RT5640_GP2_OUT_MASK (0x1 << 4)
+#define RT5640_GP2_OUT_SFT 4
+#define RT5640_GP2_OUT_LO (0x0 << 4)
+#define RT5640_GP2_OUT_HI (0x1 << 4)
+#define RT5640_GP2_P_MASK (0x1 << 3)
+#define RT5640_GP2_P_SFT 3
+#define RT5640_GP2_P_NOR (0x0 << 3)
+#define RT5640_GP2_P_INV (0x1 << 3)
+#define RT5640_GP1_PF_MASK (0x1 << 2)
+#define RT5640_GP1_PF_SFT 2
+#define RT5640_GP1_PF_IN (0x0 << 2)
+#define RT5640_GP1_PF_OUT (0x1 << 2)
+#define RT5640_GP1_OUT_MASK (0x1 << 1)
+#define RT5640_GP1_OUT_SFT 1
+#define RT5640_GP1_OUT_LO (0x0 << 1)
+#define RT5640_GP1_OUT_HI (0x1 << 1)
+#define RT5640_GP1_P_MASK (0x1)
+#define RT5640_GP1_P_SFT 0
+#define RT5640_GP1_P_NOR (0x0)
+#define RT5640_GP1_P_INV (0x1)
+
+/* FM34-500 Register Control 1 (0xc4) */
+#define RT5640_DSP_ADD_SFT 0
+
+/* FM34-500 Register Control 2 (0xc5) */
+#define RT5640_DSP_DAT_SFT 0
+
+/* FM34-500 Register Control 3 (0xc6) */
+#define RT5640_DSP_BUSY_MASK (0x1 << 15)
+#define RT5640_DSP_BUSY_BIT 15
+#define RT5640_DSP_DS_MASK (0x1 << 14)
+#define RT5640_DSP_DS_SFT 14
+#define RT5640_DSP_DS_FM3010 (0x1 << 14)
+#define RT5640_DSP_DS_TEMP (0x1 << 14)
+#define RT5640_DSP_CLK_MASK (0x3 << 12)
+#define RT5640_DSP_CLK_SFT 12
+#define RT5640_DSP_CLK_384K (0x0 << 12)
+#define RT5640_DSP_CLK_192K (0x1 << 12)
+#define RT5640_DSP_CLK_96K (0x2 << 12)
+#define RT5640_DSP_CLK_64K (0x3 << 12)
+#define RT5640_DSP_PD_PIN_MASK (0x1 << 11)
+#define RT5640_DSP_PD_PIN_SFT 11
+#define RT5640_DSP_PD_PIN_LO (0x0 << 11)
+#define RT5640_DSP_PD_PIN_HI (0x1 << 11)
+#define RT5640_DSP_RST_PIN_MASK (0x1 << 10)
+#define RT5640_DSP_RST_PIN_SFT 10
+#define RT5640_DSP_RST_PIN_LO (0x0 << 10)
+#define RT5640_DSP_RST_PIN_HI (0x1 << 10)
+#define RT5640_DSP_R_EN (0x1 << 9)
+#define RT5640_DSP_R_EN_BIT 9
+#define RT5640_DSP_W_EN (0x1 << 8)
+#define RT5640_DSP_W_EN_BIT 8
+#define RT5640_DSP_CMD_MASK (0xff)
+#define RT5640_DSP_CMD_SFT 0
+#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */
+#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */
+#define RT5640_DSP_CMD_RR (0x60) /* Register Read */
+#define RT5640_DSP_CMD_RW (0x68) /* Register Write */
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5640_REG_SEQ_MASK (0xf << 12)
+#define RT5640_REG_SEQ_SFT 12
+#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/
+#define RT5640_SEQ1_ST_SFT 11
+#define RT5640_SEQ1_ST_RUN (0x0 << 11)
+#define RT5640_SEQ1_ST_FIN (0x1 << 11)
+#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/
+#define RT5640_SEQ2_ST_SFT 10
+#define RT5640_SEQ2_ST_RUN (0x0 << 10)
+#define RT5640_SEQ2_ST_FIN (0x1 << 10)
+#define RT5640_REG_LV_MASK (0x1 << 9)
+#define RT5640_REG_LV_SFT 9
+#define RT5640_REG_LV_MX (0x0 << 9)
+#define RT5640_REG_LV_PR (0x1 << 9)
+#define RT5640_SEQ_2_PT_MASK (0x1 << 8)
+#define RT5640_SEQ_2_PT_BIT 8
+#define RT5640_REG_IDX_MASK (0xff)
+#define RT5640_REG_IDX_SFT 0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5640_REG_DAT_MASK (0xffff)
+#define RT5640_REG_DAT_SFT 0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5640_SEQ_DLY_MASK (0xff << 8)
+#define RT5640_SEQ_DLY_SFT 8
+#define RT5640_PROG_MASK (0x1 << 7)
+#define RT5640_PROG_SFT 7
+#define RT5640_PROG_DIS (0x0 << 7)
+#define RT5640_PROG_EN (0x1 << 7)
+#define RT5640_SEQ1_PT_RUN (0x1 << 6)
+#define RT5640_SEQ1_PT_RUN_BIT 6
+#define RT5640_SEQ2_PT_RUN (0x1 << 5)
+#define RT5640_SEQ2_PT_RUN_BIT 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5640_SEQ1_START_MASK (0xf << 8)
+#define RT5640_SEQ1_START_SFT 8
+#define RT5640_SEQ1_END_MASK (0xf)
+#define RT5640_SEQ1_END_SFT 0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5640_SEQ2_START_MASK (0xf << 8)
+#define RT5640_SEQ2_START_SFT 8
+#define RT5640_SEQ2_END_MASK (0xf)
+#define RT5640_SEQ2_END_SFT 0
+
+/* Scramble Function (0xcd) */
+#define RT5640_SCB_KEY_MASK (0xff)
+#define RT5640_SCB_KEY_SFT 0
+
+/* Scramble Control (0xce) */
+#define RT5640_SCB_SWAP_MASK (0x1 << 15)
+#define RT5640_SCB_SWAP_SFT 15
+#define RT5640_SCB_SWAP_DIS (0x0 << 15)
+#define RT5640_SCB_SWAP_EN (0x1 << 15)
+#define RT5640_SCB_MASK (0x1 << 14)
+#define RT5640_SCB_SFT 14
+#define RT5640_SCB_DIS (0x0 << 14)
+#define RT5640_SCB_EN (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5640_BB_MASK (0x1 << 15)
+#define RT5640_BB_SFT 15
+#define RT5640_BB_DIS (0x0 << 15)
+#define RT5640_BB_EN (0x1 << 15)
+#define RT5640_BB_CT_MASK (0x7 << 12)
+#define RT5640_BB_CT_SFT 12
+#define RT5640_BB_CT_A (0x0 << 12)
+#define RT5640_BB_CT_B (0x1 << 12)
+#define RT5640_BB_CT_C (0x2 << 12)
+#define RT5640_BB_CT_D (0x3 << 12)
+#define RT5640_M_BB_L_MASK (0x1 << 9)
+#define RT5640_M_BB_L_SFT 9
+#define RT5640_M_BB_R_MASK (0x1 << 8)
+#define RT5640_M_BB_R_SFT 8
+#define RT5640_M_BB_HPF_L_MASK (0x1 << 7)
+#define RT5640_M_BB_HPF_L_SFT 7
+#define RT5640_M_BB_HPF_R_MASK (0x1 << 6)
+#define RT5640_M_BB_HPF_R_SFT 6
+#define RT5640_G_BB_BST_MASK (0x3f)
+#define RT5640_G_BB_BST_SFT 0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5640_M_MP3_L_MASK (0x1 << 15)
+#define RT5640_M_MP3_L_SFT 15
+#define RT5640_M_MP3_R_MASK (0x1 << 14)
+#define RT5640_M_MP3_R_SFT 14
+#define RT5640_M_MP3_MASK (0x1 << 13)
+#define RT5640_M_MP3_SFT 13
+#define RT5640_M_MP3_DIS (0x0 << 13)
+#define RT5640_M_MP3_EN (0x1 << 13)
+#define RT5640_EG_MP3_MASK (0x1f << 8)
+#define RT5640_EG_MP3_SFT 8
+#define RT5640_MP3_HLP_MASK (0x1 << 7)
+#define RT5640_MP3_HLP_SFT 7
+#define RT5640_MP3_HLP_DIS (0x0 << 7)
+#define RT5640_MP3_HLP_EN (0x1 << 7)
+#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6)
+#define RT5640_M_MP3_ORG_L_SFT 6
+#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5)
+#define RT5640_M_MP3_ORG_R_SFT 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5640_MP3_WT_MASK (0x1 << 13)
+#define RT5640_MP3_WT_SFT 13
+#define RT5640_MP3_WT_1_4 (0x0 << 13)
+#define RT5640_MP3_WT_1_2 (0x1 << 13)
+#define RT5640_OG_MP3_MASK (0x1f << 8)
+#define RT5640_OG_MP3_SFT 8
+#define RT5640_HG_MP3_MASK (0x3f)
+#define RT5640_HG_MP3_SFT 0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5640_3D_CF_MASK (0x1 << 15)
+#define RT5640_3D_CF_SFT 15
+#define RT5640_3D_CF_DIS (0x0 << 15)
+#define RT5640_3D_CF_EN (0x1 << 15)
+#define RT5640_3D_HP_MASK (0x1 << 14)
+#define RT5640_3D_HP_SFT 14
+#define RT5640_3D_HP_DIS (0x0 << 14)
+#define RT5640_3D_HP_EN (0x1 << 14)
+#define RT5640_3D_BT_MASK (0x1 << 13)
+#define RT5640_3D_BT_SFT 13
+#define RT5640_3D_BT_DIS (0x0 << 13)
+#define RT5640_3D_BT_EN (0x1 << 13)
+#define RT5640_3D_1F_MIX_MASK (0x3 << 11)
+#define RT5640_3D_1F_MIX_SFT 11
+#define RT5640_3D_HP_M_MASK (0x1 << 10)
+#define RT5640_3D_HP_M_SFT 10
+#define RT5640_3D_HP_M_SUR (0x0 << 10)
+#define RT5640_3D_HP_M_FRO (0x1 << 10)
+#define RT5640_M_3D_HRTF_MASK (0x1 << 9)
+#define RT5640_M_3D_HRTF_SFT 9
+#define RT5640_M_3D_D2H_MASK (0x1 << 8)
+#define RT5640_M_3D_D2H_SFT 8
+#define RT5640_M_3D_D2R_MASK (0x1 << 7)
+#define RT5640_M_3D_D2R_SFT 7
+#define RT5640_M_3D_REVB_MASK (0x1 << 6)
+#define RT5640_M_3D_REVB_SFT 6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5640_2ND_HPF_MASK (0x1 << 15)
+#define RT5640_2ND_HPF_SFT 15
+#define RT5640_2ND_HPF_DIS (0x0 << 15)
+#define RT5640_2ND_HPF_EN (0x1 << 15)
+#define RT5640_HPF_CF_L_MASK (0x7 << 12)
+#define RT5640_HPF_CF_L_SFT 12
+#define RT5640_1ST_HPF_MASK (0x1 << 11)
+#define RT5640_1ST_HPF_SFT 11
+#define RT5640_1ST_HPF_DIS (0x0 << 11)
+#define RT5640_1ST_HPF_EN (0x1 << 11)
+#define RT5640_HPF_CF_R_MASK (0x7 << 8)
+#define RT5640_HPF_CF_R_SFT 8
+#define RT5640_ZD_T_MASK (0x3 << 6)
+#define RT5640_ZD_T_SFT 6
+#define RT5640_ZD_F_MASK (0x3 << 4)
+#define RT5640_ZD_F_SFT 4
+#define RT5640_ZD_F_IM (0x0 << 4)
+#define RT5640_ZD_F_ZC_IM (0x1 << 4)
+#define RT5640_ZD_F_ZC_IOD (0x2 << 4)
+#define RT5640_ZD_F_UN (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5640_SI_DAC_MASK (0x1 << 11)
+#define RT5640_SI_DAC_SFT 11
+#define RT5640_SI_DAC_AUTO (0x0 << 11)
+#define RT5640_SI_DAC_TEST (0x1 << 11)
+#define RT5640_DC_CAL_M_MASK (0x1 << 10)
+#define RT5640_DC_CAL_M_SFT 10
+#define RT5640_DC_CAL_M_CAL (0x0 << 10)
+#define RT5640_DC_CAL_M_NOR (0x1 << 10)
+#define RT5640_DC_CAL_MASK (0x1 << 9)
+#define RT5640_DC_CAL_SFT 9
+#define RT5640_DC_CAL_DIS (0x0 << 9)
+#define RT5640_DC_CAL_EN (0x1 << 9)
+#define RT5640_HPD_RCV_MASK (0x7 << 6)
+#define RT5640_HPD_RCV_SFT 6
+#define RT5640_HPD_PS_MASK (0x1 << 5)
+#define RT5640_HPD_PS_SFT 5
+#define RT5640_HPD_PS_DIS (0x0 << 5)
+#define RT5640_HPD_PS_EN (0x1 << 5)
+#define RT5640_CAL_M_MASK (0x1 << 4)
+#define RT5640_CAL_M_SFT 4
+#define RT5640_CAL_M_DEP (0x0 << 4)
+#define RT5640_CAL_M_CAL (0x1 << 4)
+#define RT5640_CAL_MASK (0x1 << 3)
+#define RT5640_CAL_SFT 3
+#define RT5640_CAL_DIS (0x0 << 3)
+#define RT5640_CAL_EN (0x1 << 3)
+#define RT5640_CAL_TEST_MASK (0x1 << 2)
+#define RT5640_CAL_TEST_SFT 2
+#define RT5640_CAL_TEST_DIS (0x0 << 2)
+#define RT5640_CAL_TEST_EN (0x1 << 2)
+#define RT5640_CAL_P_MASK (0x3)
+#define RT5640_CAL_P_SFT 0
+#define RT5640_CAL_P_NONE (0x0)
+#define RT5640_CAL_P_CAL (0x1)
+#define RT5640_CAL_P_DAC_CAL (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5640_SV_MASK (0x1 << 15)
+#define RT5640_SV_SFT 15
+#define RT5640_SV_DIS (0x0 << 15)
+#define RT5640_SV_EN (0x1 << 15)
+#define RT5640_SPO_SV_MASK (0x1 << 14)
+#define RT5640_SPO_SV_SFT 14
+#define RT5640_SPO_SV_DIS (0x0 << 14)
+#define RT5640_SPO_SV_EN (0x1 << 14)
+#define RT5640_OUT_SV_MASK (0x1 << 13)
+#define RT5640_OUT_SV_SFT 13
+#define RT5640_OUT_SV_DIS (0x0 << 13)
+#define RT5640_OUT_SV_EN (0x1 << 13)
+#define RT5640_HP_SV_MASK (0x1 << 12)
+#define RT5640_HP_SV_SFT 12
+#define RT5640_HP_SV_DIS (0x0 << 12)
+#define RT5640_HP_SV_EN (0x1 << 12)
+#define RT5640_ZCD_DIG_MASK (0x1 << 11)
+#define RT5640_ZCD_DIG_SFT 11
+#define RT5640_ZCD_DIG_DIS (0x0 << 11)
+#define RT5640_ZCD_DIG_EN (0x1 << 11)
+#define RT5640_ZCD_MASK (0x1 << 10)
+#define RT5640_ZCD_SFT 10
+#define RT5640_ZCD_PD (0x0 << 10)
+#define RT5640_ZCD_PU (0x1 << 10)
+#define RT5640_M_ZCD_MASK (0x3f << 4)
+#define RT5640_M_ZCD_SFT 4
+#define RT5640_M_ZCD_RM_L (0x1 << 9)
+#define RT5640_M_ZCD_RM_R (0x1 << 8)
+#define RT5640_M_ZCD_SM_L (0x1 << 7)
+#define RT5640_M_ZCD_SM_R (0x1 << 6)
+#define RT5640_M_ZCD_OM_L (0x1 << 5)
+#define RT5640_M_ZCD_OM_R (0x1 << 4)
+#define RT5640_SV_DLY_MASK (0xf)
+#define RT5640_SV_DLY_SFT 0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5640_ZCD_HP_MASK (0x1 << 15)
+#define RT5640_ZCD_HP_SFT 15
+#define RT5640_ZCD_HP_DIS (0x0 << 15)
+#define RT5640_ZCD_HP_EN (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5640_3D_SPK_MASK (0x1 << 15)
+#define RT5640_3D_SPK_SFT 15
+#define RT5640_3D_SPK_DIS (0x0 << 15)
+#define RT5640_3D_SPK_EN (0x1 << 15)
+#define RT5640_3D_SPK_M_MASK (0x3 << 13)
+#define RT5640_3D_SPK_M_SFT 13
+#define RT5640_3D_SPK_CG_MASK (0x1f << 8)
+#define RT5640_3D_SPK_CG_SFT 8
+#define RT5640_3D_SPK_SG_MASK (0x1f)
+#define RT5640_3D_SPK_SG_SFT 0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5640_WND_MASK (0x1 << 15)
+#define RT5640_WND_SFT 15
+#define RT5640_WND_DIS (0x0 << 15)
+#define RT5640_WND_EN (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5640_WND_FC_NW_MASK (0x3f << 10)
+#define RT5640_WND_FC_NW_SFT 10
+#define RT5640_WND_FC_WK_MASK (0x3f << 4)
+#define RT5640_WND_FC_WK_SFT 4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5640_HPF_FC_MASK (0x3f << 6)
+#define RT5640_HPF_FC_SFT 6
+#define RT5640_WND_FC_ST_MASK (0x3f)
+#define RT5640_WND_FC_ST_SFT 0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5640_WND_TH_LO_MASK (0x3ff)
+#define RT5640_WND_TH_LO_SFT 0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5640_WND_TH_HI_MASK (0x3ff)
+#define RT5640_WND_TH_HI_SFT 0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */
+#define RT5640_WND_WIND_SFT 13
+#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */
+#define RT5640_WND_STRONG_SFT 12
+enum {
+ RT5640_NO_WIND,
+ RT5640_BREEZE,
+ RT5640_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5640_DP_ATT_MASK (0x3 << 14)
+#define RT5640_DP_ATT_SFT 14
+#define RT5640_DP_SPK_MASK (0x1 << 10)
+#define RT5640_DP_SPK_SFT 10
+#define RT5640_DP_SPK_DIS (0x0 << 10)
+#define RT5640_DP_SPK_EN (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5640_EQ_PRE_VOL_MASK (0xffff)
+#define RT5640_EQ_PRE_VOL_SFT 0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5640_EQ_PST_VOL_MASK (0xffff)
+#define RT5640_EQ_PST_VOL_SFT 0
+
+#define RT5640_NO_JACK BIT(0)
+#define RT5640_HEADSET_DET BIT(1)
+#define RT5640_HEADPHO_DET BIT(2)
+
+/* System Clock Source */
+#define RT5640_SCLK_S_MCLK 0
+#define RT5640_SCLK_S_PLL1 1
+#define RT5640_SCLK_S_PLL1_TK 2
+#define RT5640_SCLK_S_RCCLK 3
+
+/* PLL1 Source */
+#define RT5640_PLL1_S_MCLK 0
+#define RT5640_PLL1_S_BCLK1 1
+#define RT5640_PLL1_S_BCLK2 2
+#define RT5640_PLL1_S_BCLK3 3
+
+
+enum {
+ RT5640_AIF1,
+ RT5640_AIF2,
+ RT5640_AIF3,
+ RT5640_AIFS,
+};
+
+enum {
+ RT5640_U_IF1 = 0x1,
+ RT5640_U_IF2 = 0x2,
+ RT5640_U_IF3 = 0x4,
+};
+
+enum {
+ RT5640_IF_123,
+ RT5640_IF_132,
+ RT5640_IF_312,
+ RT5640_IF_321,
+ RT5640_IF_231,
+ RT5640_IF_213,
+ RT5640_IF_113,
+ RT5640_IF_223,
+ RT5640_IF_ALL,
+};
+
+enum {
+ RT5640_DMIC_DIS,
+ RT5640_DMIC1,
+ RT5640_DMIC2,
+};
+
+struct rt5640_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+struct rt5640_priv {
+ struct snd_soc_codec *codec;
+ struct rt5640_platform_data pdata;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck[RT5640_AIFS];
+ int bclk[RT5640_AIFS];
+ int master[RT5640_AIFS];
+
+ struct rt5640_pll_code pll_code;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int dmic_en;
+ bool hp_mute;
+};
+
+#endif
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 92bbfec..1f4093f 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
+#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
@@ -34,30 +35,30 @@
#define SGTL5000_MAX_REG_OFFSET 0x013A
/* default value of sgtl5000 registers */
-static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = {
- [SGTL5000_CHIP_CLK_CTRL] = 0x0008,
- [SGTL5000_CHIP_I2S_CTRL] = 0x0010,
- [SGTL5000_CHIP_SSS_CTRL] = 0x0008,
- [SGTL5000_CHIP_DAC_VOL] = 0x3c3c,
- [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f,
- [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818,
- [SGTL5000_CHIP_ANA_CTRL] = 0x0111,
- [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404,
- [SGTL5000_CHIP_ANA_POWER] = 0x7060,
- [SGTL5000_CHIP_PLL_CTRL] = 0x5000,
- [SGTL5000_DAP_BASS_ENHANCE] = 0x0040,
- [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f,
- [SGTL5000_DAP_SURROUND] = 0x0040,
- [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f,
- [SGTL5000_DAP_MAIN_CHAN] = 0x8000,
- [SGTL5000_DAP_AVC_CTRL] = 0x0510,
- [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473,
- [SGTL5000_DAP_AVC_ATTACK] = 0x0028,
- [SGTL5000_DAP_AVC_DECAY] = 0x0050,
+static const struct reg_default sgtl5000_reg_defaults[] = {
+ { SGTL5000_CHIP_CLK_CTRL, 0x0008 },
+ { SGTL5000_CHIP_I2S_CTRL, 0x0010 },
+ { SGTL5000_CHIP_SSS_CTRL, 0x0010 },
+ { SGTL5000_CHIP_DAC_VOL, 0x3c3c },
+ { SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
+ { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
+ { SGTL5000_CHIP_ANA_CTRL, 0x0111 },
+ { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
+ { SGTL5000_CHIP_ANA_POWER, 0x7060 },
+ { SGTL5000_CHIP_PLL_CTRL, 0x5000 },
+ { SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
+ { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
+ { SGTL5000_DAP_SURROUND, 0x0040 },
+ { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
+ { SGTL5000_DAP_MAIN_CHAN, 0x8000 },
+ { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
+ { SGTL5000_DAP_AVC_ATTACK, 0x0028 },
+ { SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -112,6 +113,8 @@ struct sgtl5000_priv {
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
struct ldo_regulator *ldo;
+ struct regmap *regmap;
+ struct clk *mclk;
};
/*
@@ -150,16 +153,26 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
static int power_vag_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
+ const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP;
+
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
break;
- case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, 0);
- msleep(400);
+ case SND_SOC_DAPM_PRE_PMD:
+ /*
+ * Don't clear VAG_POWERUP, when both DAC and ADC are
+ * operational to prevent inadvertently starving the
+ * other one of them.
+ */
+ if ((snd_soc_read(w->codec, SGTL5000_CHIP_ANA_POWER) &
+ mask) != mask) {
+ snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP, 0);
+ msleep(400);
+ }
break;
default:
break;
@@ -217,12 +230,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0,
- power_vag_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
+
+ SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
+ SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
};
/* routes for sgtl5000 */
@@ -230,16 +242,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
{"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
- {"ADC", NULL, "VAG_POWER"},
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "VAG_POWER"},
{"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
- {"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@@ -389,7 +398,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)",
SGTL5000_CHIP_ANA_ADC_CTRL,
- 8, 2, 0, capture_6db_attenuate),
+ 8, 1, 0, capture_6db_attenuate),
SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
SOC_DOUBLE_TLV("Headphone Playback Volume",
@@ -645,16 +654,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+
+ /* if using pll, clk_ctrl must be set after pll power up */
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
+ /* otherwise, clk_ctrl must be set before pll power down */
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
/* power down pll */
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
0);
}
- /* if using pll, clk_ctrl must be set after pll power up */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
-
return 0;
}
@@ -909,10 +921,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
if (ret)
return ret;
udelay(10);
+
+ regcache_cache_only(sgtl5000->regmap, false);
+
+ ret = regcache_sync(sgtl5000->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to restore cache: %d\n", ret);
+
+ regcache_cache_only(sgtl5000->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
+
+ return ret;
+ }
}
break;
case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl5000->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
break;
@@ -958,17 +985,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.symmetric_rates = 1,
};
-static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
- return 1;
+ return true;
}
- return 0;
+ return false;
+}
+
+static bool sgtl5000_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_DIG_POWER:
+ case SGTL5000_CHIP_CLK_CTRL:
+ case SGTL5000_CHIP_I2S_CTRL:
+ case SGTL5000_CHIP_SSS_CTRL:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_DAC_VOL:
+ case SGTL5000_CHIP_PAD_STRENGTH:
+ case SGTL5000_CHIP_ANA_ADC_CTRL:
+ case SGTL5000_CHIP_ANA_HP_CTRL:
+ case SGTL5000_CHIP_ANA_CTRL:
+ case SGTL5000_CHIP_LINREG_CTRL:
+ case SGTL5000_CHIP_REF_CTRL:
+ case SGTL5000_CHIP_MIC_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_VOL:
+ case SGTL5000_CHIP_ANA_POWER:
+ case SGTL5000_CHIP_PLL_CTRL:
+ case SGTL5000_CHIP_CLK_TOP_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ case SGTL5000_CHIP_SHORT_CTRL:
+ case SGTL5000_CHIP_ANA_TEST2:
+ case SGTL5000_DAP_CTRL:
+ case SGTL5000_DAP_PEQ:
+ case SGTL5000_DAP_BASS_ENHANCE:
+ case SGTL5000_DAP_BASS_ENHANCE_CTRL:
+ case SGTL5000_DAP_AUDIO_EQ:
+ case SGTL5000_DAP_SURROUND:
+ case SGTL5000_DAP_FLT_COEF_ACCESS:
+ case SGTL5000_DAP_COEF_WR_B0_MSB:
+ case SGTL5000_DAP_COEF_WR_B0_LSB:
+ case SGTL5000_DAP_EQ_BASS_BAND0:
+ case SGTL5000_DAP_EQ_BASS_BAND1:
+ case SGTL5000_DAP_EQ_BASS_BAND2:
+ case SGTL5000_DAP_EQ_BASS_BAND3:
+ case SGTL5000_DAP_EQ_BASS_BAND4:
+ case SGTL5000_DAP_MAIN_CHAN:
+ case SGTL5000_DAP_MIX_CHAN:
+ case SGTL5000_DAP_AVC_CTRL:
+ case SGTL5000_DAP_AVC_THRESHOLD:
+ case SGTL5000_DAP_AVC_ATTACK:
+ case SGTL5000_DAP_AVC_DECAY:
+ case SGTL5000_DAP_COEF_WR_B1_MSB:
+ case SGTL5000_DAP_COEF_WR_B1_LSB:
+ case SGTL5000_DAP_COEF_WR_B2_MSB:
+ case SGTL5000_DAP_COEF_WR_B2_LSB:
+ case SGTL5000_DAP_COEF_WR_A1_MSB:
+ case SGTL5000_DAP_COEF_WR_A1_LSB:
+ case SGTL5000_DAP_COEF_WR_A2_MSB:
+ case SGTL5000_DAP_COEF_WR_A2_LSB:
+ return true;
+
+ default:
+ return false;
+ }
}
#ifdef CONFIG_SUSPEND
@@ -1214,7 +1300,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
{
- u16 reg;
+ int reg;
int ret;
int rev;
int i;
@@ -1242,23 +1328,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
/* wait for all power rails bring up */
udelay(10);
- /* read chip information */
- reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
- if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
- SGTL5000_PARTID_PART_ID) {
- dev_err(codec->dev,
- "Device with ID register %x is not a sgtl5000\n", reg);
- ret = -ENODEV;
- goto err_regulator_disable;
- }
-
- rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
- dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev);
-
/*
* workaround for revision 0x11 and later,
* roll back to use internal LDO
*/
+
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto err_regulator_disable;
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+
if (external_vddd && rev >= 0x11) {
/* disable all regulator first */
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
@@ -1300,7 +1380,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
/* setup i2c data ops */
- ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ codec->control_data = sgtl5000->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -1391,11 +1472,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.suspend = sgtl5000_suspend,
.resume = sgtl5000_resume,
.set_bias_level = sgtl5000_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
- .reg_word_size = sizeof(u16),
- .reg_cache_step = 2,
- .reg_cache_default = sgtl5000_regs,
- .volatile_register = sgtl5000_volatile_register,
.controls = sgtl5000_snd_controls,
.num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
.dapm_widgets = sgtl5000_dapm_widgets,
@@ -1404,28 +1480,118 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
};
+static const struct regmap_config sgtl5000_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+
+ .max_register = SGTL5000_MAX_REG_OFFSET,
+ .volatile_reg = sgtl5000_volatile,
+ .readable_reg = sgtl5000_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = sgtl5000_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults),
+};
+
+/*
+ * Write all the default values from sgtl5000_reg_defaults[] array into the
+ * sgtl5000 registers, to make sure we always start with the sane registers
+ * values as stated in the datasheet.
+ *
+ * Since sgtl5000 does not have a reset line, nor a reset command in software,
+ * we follow this approach to guarantee we always start from the default values
+ * and avoid problems like, not being able to probe after an audio playback
+ * followed by a system reset or a 'reboot' command in Linux
+ */
+static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+{
+ int i, ret, val, index;
+
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
+ val = sgtl5000_reg_defaults[i].def;
+ index = sgtl5000_reg_defaults[i].reg;
+ ret = regmap_write(sgtl5000->regmap, index, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sgtl5000_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sgtl5000_priv *sgtl5000;
- int ret;
+ int ret, reg, rev;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
+ sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
+ if (IS_ERR(sgtl5000->regmap)) {
+ ret = PTR_ERR(sgtl5000->regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(sgtl5000->mclk)) {
+ ret = PTR_ERR(sgtl5000->mclk);
+ dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
+ /* Defer the probe to see if the clk will be provided later */
+ if (ret == -ENOENT)
+ return -EPROBE_DEFER;
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sgtl5000->mclk);
+ if (ret)
+ return ret;
+
+ /* read chip information */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto disable_clk;
+
+ if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+ SGTL5000_PARTID_PART_ID) {
+ dev_err(&client->dev,
+ "Device with ID register %x is not a sgtl5000\n", reg);
+ ret = -ENODEV;
+ goto disable_clk;
+ }
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
+
i2c_set_clientdata(client, sgtl5000);
+ /* Ensure sgtl5000 will start with sane register values */
+ ret = sgtl5000_fill_defaults(sgtl5000);
+ if (ret)
+ goto disable_clk;
+
ret = snd_soc_register_codec(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(sgtl5000->mclk);
return ret;
}
static int sgtl5000_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+ snd_soc_unregister_codec(&client->dev);
+ clk_disable_unprepare(sgtl5000->mclk);
return 0;
}
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 8a9f435..2f8c889 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -12,7 +12,7 @@
#define _SGTL5000_H
/*
- * Register values.
+ * Registers addresses
*/
#define SGTL5000_CHIP_ID 0x0000
#define SGTL5000_CHIP_DIG_POWER 0x0002
@@ -347,7 +347,7 @@
#define SGTL5000_PLL_INT_DIV_MASK 0xf800
#define SGTL5000_PLL_INT_DIV_SHIFT 11
#define SGTL5000_PLL_INT_DIV_WIDTH 5
-#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700
+#define SGTL5000_PLL_FRAC_DIV_MASK 0x07ff
#define SGTL5000_PLL_FRAC_DIV_SHIFT 0
#define SGTL5000_PLL_FRAC_DIV_WIDTH 11
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index 721587c..38f3b10 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -38,9 +38,9 @@ enum si476x_digital_io_output_format {
SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8,
};
-#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
- (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
-#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0b1111110)
+#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
+ (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
+#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e)
enum si476x_daudio_formats {
SI476X_DAUDIO_MODE_I2S = (0x0 << 1),
@@ -102,6 +102,16 @@ static int si476x_codec_write(struct snd_soc_codec *codec,
return err;
}
+static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
+ { "Capture", NULL, "LOUT" },
+ { "Capture", NULL, "ROUT" },
+};
+
static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
@@ -260,6 +270,10 @@ static struct snd_soc_codec_driver soc_codec_dev_si476x = {
.probe = si476x_codec_probe,
.read = si476x_codec_read,
.write = si476x_codec_write,
+ .dapm_widgets = si476x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+ .dapm_routes = si476x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
};
static int si476x_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index d1ae869d..dba26e63 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -883,7 +883,7 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec)
return 0;
}
-struct snd_soc_codec_driver sn95031_codec = {
+static struct snd_soc_codec_driver sn95031_codec = {
.probe = sn95031_codec_probe,
.remove = sn95031_codec_remove,
.read = sn95031_read,
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index dd8d856..e3501f4 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -21,12 +21,28 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
+#include <linux/of.h>
+
+static const struct snd_soc_dapm_widget dir_widgets[] = {
+ SND_SOC_DAPM_INPUT("spdif-in"),
+};
+
+static const struct snd_soc_dapm_route dir_routes[] = {
+ { "Capture", NULL, "spdif-in" },
+};
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static struct snd_soc_codec_driver soc_codec_spdif_dir;
+static struct snd_soc_codec_driver soc_codec_spdif_dir = {
+ .dapm_widgets = dir_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+ .dapm_routes = dir_routes,
+ .num_dapm_routes = ARRAY_SIZE(dir_routes),
+};
static struct snd_soc_dai_driver dir_stub_dai = {
.name = "dir-hifi",
@@ -51,12 +67,21 @@ static int spdif_dir_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id spdif_dir_dt_ids[] = {
+ { .compatible = "linux,spdif-dir", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
+#endif
+
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
.remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spdif_dir_dt_ids),
},
};
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transmitter.c
index 112a49d..a078aa3 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -20,14 +20,29 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
+#include <linux/of.h>
#define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
-#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+ { "spdif-out", NULL, "Playback" },
+};
-static struct snd_soc_codec_driver soc_codec_spdif_dit;
+static struct snd_soc_codec_driver soc_codec_spdif_dit = {
+ .dapm_widgets = dit_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+ .dapm_routes = dit_routes,
+ .num_dapm_routes = ARRAY_SIZE(dit_routes),
+};
static struct snd_soc_dai_driver dit_stub_dai = {
.name = "dit-hifi",
@@ -52,12 +67,21 @@ static int spdif_dit_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id spdif_dit_dt_ids[] = {
+ { .compatible = "linux,spdif-dit", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
+#endif
+
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spdif_dit_dt_ids),
},
};
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
new file mode 100644
index 0000000..95aed55
--- /dev/null
+++ b/sound/soc/codecs/ssm2518.c
@@ -0,0 +1,856 @@
+/*
+ * SSM2518 amplifier audio driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/ssm2518.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ssm2518.h"
+
+#define SSM2518_REG_POWER1 0x00
+#define SSM2518_REG_CLOCK 0x01
+#define SSM2518_REG_SAI_CTRL1 0x02
+#define SSM2518_REG_SAI_CTRL2 0x03
+#define SSM2518_REG_CHAN_MAP 0x04
+#define SSM2518_REG_LEFT_VOL 0x05
+#define SSM2518_REG_RIGHT_VOL 0x06
+#define SSM2518_REG_MUTE_CTRL 0x07
+#define SSM2518_REG_FAULT_CTRL 0x08
+#define SSM2518_REG_POWER2 0x09
+#define SSM2518_REG_DRC_1 0x0a
+#define SSM2518_REG_DRC_2 0x0b
+#define SSM2518_REG_DRC_3 0x0c
+#define SSM2518_REG_DRC_4 0x0d
+#define SSM2518_REG_DRC_5 0x0e
+#define SSM2518_REG_DRC_6 0x0f
+#define SSM2518_REG_DRC_7 0x10
+#define SSM2518_REG_DRC_8 0x11
+#define SSM2518_REG_DRC_9 0x12
+
+#define SSM2518_POWER1_RESET BIT(7)
+#define SSM2518_POWER1_NO_BCLK BIT(5)
+#define SSM2518_POWER1_MCS_MASK (0xf << 1)
+#define SSM2518_POWER1_MCS_64FS (0x0 << 1)
+#define SSM2518_POWER1_MCS_128FS (0x1 << 1)
+#define SSM2518_POWER1_MCS_256FS (0x2 << 1)
+#define SSM2518_POWER1_MCS_384FS (0x3 << 1)
+#define SSM2518_POWER1_MCS_512FS (0x4 << 1)
+#define SSM2518_POWER1_MCS_768FS (0x5 << 1)
+#define SSM2518_POWER1_MCS_100FS (0x6 << 1)
+#define SSM2518_POWER1_MCS_200FS (0x7 << 1)
+#define SSM2518_POWER1_MCS_400FS (0x8 << 1)
+#define SSM2518_POWER1_SPWDN BIT(0)
+
+#define SSM2518_CLOCK_ASR BIT(0)
+
+#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5)
+#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5)
+#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5)
+#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5)
+
+#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2)
+#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2)
+#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2)
+#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2)
+
+#define SSM2518_SAI_CTRL1_FS_MASK (0x3)
+#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0)
+#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1)
+#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2)
+#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3)
+
+#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7)
+#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6)
+#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5)
+#define SSM2518_SAI_CTRL2_MSB BIT(4)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2)
+#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2)
+#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1)
+
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
+#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
+#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f
+
+#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5)
+#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0)
+
+#define SSM2518_POWER2_APWDN BIT(0)
+
+#define SSM2518_DAC_MUTE BIT(6)
+#define SSM2518_DAC_FS_MASK 0x07
+#define SSM2518_DAC_FS_8000 0x00
+#define SSM2518_DAC_FS_16000 0x01
+#define SSM2518_DAC_FS_32000 0x02
+#define SSM2518_DAC_FS_64000 0x03
+#define SSM2518_DAC_FS_128000 0x04
+
+struct ssm2518 {
+ struct regmap *regmap;
+ bool right_j;
+
+ unsigned int sysclk;
+ const struct snd_pcm_hw_constraint_list *constraints;
+
+ int enable_gpio;
+};
+
+static const struct reg_default ssm2518_reg_defaults[] = {
+ { 0x00, 0x05 },
+ { 0x01, 0x00 },
+ { 0x02, 0x02 },
+ { 0x03, 0x00 },
+ { 0x04, 0x10 },
+ { 0x05, 0x40 },
+ { 0x06, 0x40 },
+ { 0x07, 0x81 },
+ { 0x08, 0x0c },
+ { 0x09, 0x99 },
+ { 0x0a, 0x7c },
+ { 0x0b, 0x5b },
+ { 0x0c, 0x57 },
+ { 0x0d, 0x89 },
+ { 0x0e, 0x8c },
+ { 0x0f, 0x77 },
+ { 0x10, 0x26 },
+ { 0x11, 0x1c },
+ { 0x12, 0x97 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400);
+static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0);
+
+static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+ 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0),
+);
+
+static const char * const ssm2518_drc_peak_detector_attack_time_text[] = {
+ "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms",
+ "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms",
+ "768 ms", "1536 ms",
+};
+
+static const char * const ssm2518_drc_peak_detector_release_time_text[] = {
+ "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms",
+ "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms",
+ "12288 ms", "24576 ms"
+};
+
+static const char * const ssm2518_drc_hold_time_text[] = {
+ "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms",
+ "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms",
+ "682.24 ms", "1364 ms",
+};
+
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
+ SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
+ SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
+ SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
+ SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
+ SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
+ SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text);
+static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
+ SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text);
+
+static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
+ SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL,
+ 4, 1, 0),
+ SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL,
+ SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv),
+ SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1),
+
+ SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0),
+ SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0),
+
+ SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0),
+ SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0),
+ SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0),
+ SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0),
+ SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0),
+
+ SOC_SINGLE_TLV("DRC Limiter Threshold Volume",
+ SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume",
+ SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv),
+ SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4,
+ 4, 15, 1, ssm2518_expander_tlv),
+ SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume",
+ SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Upper Output Threshold Volume",
+ SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv),
+ SOC_SINGLE_TLV("DRC Lower Output Threshold Volume",
+ SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv),
+ SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8,
+ 2, 15, 1, ssm2518_post_drc_tlv),
+
+ SOC_ENUM("DRC Peak Detector Attack Time",
+ ssm2518_drc_peak_detector_attack_time_enum),
+ SOC_ENUM("DRC Peak Detector Release Time",
+ ssm2518_drc_peak_detector_release_time_enum),
+ SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum),
+ SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum),
+ SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum),
+ SOC_ENUM("DRC Noise Gate Hold Time",
+ ssm2518_drc_noise_gate_hold_time_enum),
+ SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum),
+};
+
+static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1),
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route ssm2518_routes[] = {
+ { "OUTL", NULL, "DACL" },
+ { "OUTR", NULL, "DACR" },
+};
+
+struct ssm2518_mcs_lut {
+ unsigned int rate;
+ const unsigned int *sysclks;
+};
+
+static const unsigned int ssm2518_sysclks_2048000[] = {
+ 2048000, 4096000, 8192000, 12288000, 16384000, 24576000,
+ 3200000, 6400000, 12800000, 0
+};
+
+static const unsigned int ssm2518_sysclks_2822000[] = {
+ 2822000, 5644800, 11289600, 16934400, 22579200, 33868800,
+ 4410000, 8820000, 17640000, 0
+};
+
+static const unsigned int ssm2518_sysclks_3072000[] = {
+ 3072000, 6144000, 12288000, 16384000, 24576000, 38864000,
+ 4800000, 9600000, 19200000, 0
+};
+
+static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = {
+ { 8000, ssm2518_sysclks_2048000, },
+ { 11025, ssm2518_sysclks_2822000, },
+ { 12000, ssm2518_sysclks_3072000, },
+ { 16000, ssm2518_sysclks_2048000, },
+ { 24000, ssm2518_sysclks_3072000, },
+ { 22050, ssm2518_sysclks_2822000, },
+ { 32000, ssm2518_sysclks_2048000, },
+ { 44100, ssm2518_sysclks_2822000, },
+ { 48000, ssm2518_sysclks_3072000, },
+ { 96000, ssm2518_sysclks_3072000, },
+};
+
+static const unsigned int ssm2518_rates_2048000[] = {
+ 8000, 16000, 32000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = {
+ .list = ssm2518_rates_2048000,
+ .count = ARRAY_SIZE(ssm2518_rates_2048000),
+};
+
+static const unsigned int ssm2518_rates_2822000[] = {
+ 11025, 22050, 44100,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = {
+ .list = ssm2518_rates_2822000,
+ .count = ARRAY_SIZE(ssm2518_rates_2822000),
+};
+
+static const unsigned int ssm2518_rates_3072000[] = {
+ 12000, 24000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = {
+ .list = ssm2518_rates_3072000,
+ .count = ARRAY_SIZE(ssm2518_rates_3072000),
+};
+
+static const unsigned int ssm2518_rates_12288000[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
+ .list = ssm2518_rates_12288000,
+ .count = ARRAY_SIZE(ssm2518_rates_12288000),
+};
+
+static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
+ unsigned int rate)
+{
+ const unsigned int *sysclks = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) {
+ if (ssm2518_mcs_lut[i].rate == rate) {
+ sysclks = ssm2518_mcs_lut[i].sysclks;
+ break;
+ }
+ }
+
+ if (!sysclks)
+ return -EINVAL;
+
+ for (i = 0; sysclks[i]; i++) {
+ if (sysclks[i] == ssm2518->sysclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ssm2518_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 ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+ unsigned int ctrl1, ctrl1_mask;
+ int mcs;
+ int ret;
+
+ mcs = ssm2518_lookup_mcs(ssm2518, rate);
+ if (mcs < 0)
+ return mcs;
+
+ ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK;
+
+ if (rate >= 8000 && rate <= 12000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000;
+ else if (rate >= 16000 && rate <= 24000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000;
+ else if (rate >= 32000 && rate <= 48000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000;
+ else if (rate >= 64000 && rate <= 96000)
+ ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000;
+ else
+ return -EINVAL;
+
+ if (ssm2518->right_j) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK;
+ }
+
+ /* Disable auto samplerate detection */
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK,
+ SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ ctrl1_mask, ctrl1);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_MCS_MASK, mcs << 1);
+}
+
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int val;
+
+ if (mute)
+ val = SSM2518_MUTE_CTRL_MUTE_MASTER;
+ else
+ val = 0;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL,
+ SSM2518_MUTE_CTRL_MUTE_MASTER, val);
+}
+
+static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ctrl1 = 0, ctrl2 = 0;
+ bool invert_fclk;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_fclk = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
+ invert_fclk = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->right_j = false;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
+ ssm2518->right_j = true;
+ invert_fclk = !invert_fclk;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
+ invert_fclk = false;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
+ ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
+ invert_fclk = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (invert_fclk)
+ ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT;
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2);
+}
+
+static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
+{
+ int ret = 0;
+
+ if (!enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN);
+ regcache_mark_dirty(ssm2518->regmap);
+ }
+
+ if (gpio_is_valid(ssm2518->enable_gpio))
+ gpio_set_value(ssm2518->enable_gpio, enable);
+
+ regcache_cache_only(ssm2518->regmap, !enable);
+
+ if (enable) {
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00);
+ regcache_sync(ssm2518->regmap);
+ }
+
+ return ret;
+}
+
+static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ ret = ssm2518_set_power(ssm2518, true);
+ break;
+ case SND_SOC_BIAS_OFF:
+ ret = ssm2518_set_power(ssm2518, false);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int ctrl1, ctrl2;
+ int left_slot, right_slot;
+ int ret;
+
+ if (slots == 0)
+ return regmap_update_bits(ssm2518->regmap,
+ SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK,
+ SSM2518_SAI_CTRL1_SAI_I2S);
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ /* We assume the left channel < right channel */
+ left_slot = ffs(tx_mask);
+ tx_mask &= ~(1 << tx_mask);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = ffs(tx_mask);
+ tx_mask &= ~(1 << tx_mask);
+ }
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ switch (width) {
+ case 16:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16;
+ break;
+ case 24:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24;
+ break;
+ case 32:
+ ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 1:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO;
+ break;
+ case 2:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2;
+ break;
+ case 4:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4;
+ break;
+ case 8:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8;
+ break;
+ case 16:
+ ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP,
+ (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) |
+ (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
+ SSM2518_SAI_CTRL1_SAI_MASK, ctrl1);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2,
+ SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2);
+}
+
+static int ssm2518_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (ssm2518->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints);
+
+ return 0;
+}
+
+#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+
+static const struct snd_soc_dai_ops ssm2518_dai_ops = {
+ .startup = ssm2518_startup,
+ .hw_params = ssm2518_hw_params,
+ .digital_mute = ssm2518_mute,
+ .set_fmt = ssm2518_set_dai_fmt,
+ .set_tdm_slot = ssm2518_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ssm2518_dai = {
+ .name = "ssm2518-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SSM2518_FORMATS,
+ },
+ .ops = &ssm2518_dai_ops,
+};
+
+static int ssm2518_probe(struct snd_soc_codec *codec)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = ssm2518->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int ssm2518_remove(struct snd_soc_codec *codec)
+{
+ ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ if (clk_id != SSM2518_SYSCLK)
+ return -EINVAL;
+
+ switch (source) {
+ case SSM2518_SYSCLK_SRC_MCLK:
+ val = 0;
+ break;
+ case SSM2518_SYSCLK_SRC_BCLK:
+ /* In this case the bitclock is used as the system clock, and
+ * the bitclock signal needs to be connected to the MCLK pin and
+ * the BCLK pin is left unconnected */
+ val = SSM2518_POWER1_NO_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 0:
+ ssm2518->constraints = NULL;
+ break;
+ case 2048000:
+ case 4096000:
+ case 8192000:
+ case 3200000:
+ case 6400000:
+ case 12800000:
+ ssm2518->constraints = &ssm2518_constraints_2048000;
+ break;
+ case 2822000:
+ case 5644800:
+ case 11289600:
+ case 16934400:
+ case 22579200:
+ case 33868800:
+ case 4410000:
+ case 8820000:
+ case 17640000:
+ ssm2518->constraints = &ssm2518_constraints_2822000;
+ break;
+ case 3072000:
+ case 6144000:
+ case 38864000:
+ case 4800000:
+ case 9600000:
+ case 19200000:
+ ssm2518->constraints = &ssm2518_constraints_3072000;
+ break;
+ case 12288000:
+ case 16384000:
+ case 24576000:
+ ssm2518->constraints = &ssm2518_constraints_12288000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssm2518->sysclk = freq;
+
+ return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_NO_BCLK, val);
+}
+
+static struct snd_soc_codec_driver ssm2518_codec_driver = {
+ .probe = ssm2518_probe,
+ .remove = ssm2518_remove,
+ .set_bias_level = ssm2518_set_bias_level,
+ .set_sysclk = ssm2518_set_sysclk,
+ .idle_bias_off = true,
+
+ .controls = ssm2518_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm2518_snd_controls),
+ .dapm_widgets = ssm2518_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
+ .dapm_routes = ssm2518_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
+};
+
+static bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct regmap_config ssm2518_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+
+ .max_register = SSM2518_REG_DRC_9,
+ .volatile_reg = ssm2518_register_volatile,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ssm2518_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
+};
+
+static int ssm2518_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
+ struct ssm2518 *ssm2518;
+ int ret;
+
+ ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL);
+ if (ssm2518 == NULL)
+ return -ENOMEM;
+
+ if (pdata) {
+ ssm2518->enable_gpio = pdata->enable_gpio;
+ } else if (i2c->dev.of_node) {
+ ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
+ if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
+ return ssm2518->enable_gpio;
+ } else {
+ ssm2518->enable_gpio = -1;
+ }
+
+ if (gpio_is_valid(ssm2518->enable_gpio)) {
+ ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
+ GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
+ if (ret)
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, ssm2518);
+
+ ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config);
+ if (IS_ERR(ssm2518->regmap))
+ return PTR_ERR(ssm2518->regmap);
+
+ /*
+ * The reset bit is obviously volatile, but we need to be able to cache
+ * the other bits in the register, so we can't just mark the whole
+ * register as volatile. Since this is the only place where we'll ever
+ * touch the reset bit just bypass the cache for this operation.
+ */
+ regcache_cache_bypass(ssm2518->regmap, true);
+ ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1,
+ SSM2518_POWER1_RESET);
+ regcache_cache_bypass(ssm2518->regmap, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2,
+ SSM2518_POWER2_APWDN, 0x00);
+ if (ret)
+ return ret;
+
+ ret = ssm2518_set_power(ssm2518, false);
+ if (ret)
+ return ret;
+
+ return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ &ssm2518_dai, 1);
+}
+
+static int ssm2518_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id ssm2518_i2c_ids[] = {
+ { "ssm2518", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
+
+static struct i2c_driver ssm2518_driver = {
+ .driver = {
+ .name = "ssm2518",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2518_i2c_probe,
+ .remove = ssm2518_i2c_remove,
+ .id_table = ssm2518_i2c_ids,
+};
+module_i2c_driver(ssm2518_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2518 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h
new file mode 100644
index 0000000..62511d8
--- /dev/null
+++ b/sound/soc/codecs/ssm2518.h
@@ -0,0 +1,20 @@
+/*
+ * SSM2518 amplifier audio driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __SND_SOC_CODECS_SSM2518_H__
+#define __SND_SOC_CODECS_SSM2518_H__
+
+#define SSM2518_SYSCLK 0
+
+enum ssm2518_sysclk_src {
+ SSM2518_SYSCLK_SRC_MCLK = 0,
+ SSM2518_SYSCLK_SRC_BCLK = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index f8d30e5..492644e 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -561,8 +561,9 @@ static int ssm2602_suspend(struct snd_soc_codec *codec)
static int ssm2602_resume(struct snd_soc_codec *codec)
{
- snd_soc_cache_sync(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ regcache_sync(ssm2602->regmap);
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index cfb55fe..06edb39 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -363,16 +363,18 @@ static void sta32x_watchdog(struct work_struct *work)
}
if (!sta32x->shutdown)
- schedule_delayed_work(&sta32x->watchdog_work,
- round_jiffies_relative(HZ));
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
}
static void sta32x_watchdog_start(struct sta32x_priv *sta32x)
{
if (sta32x->pdata->needs_esd_watchdog) {
sta32x->shutdown = 0;
- schedule_delayed_work(&sta32x->watchdog_work,
- round_jiffies_relative(HZ));
+ queue_delayed_work(system_power_efficient_wq,
+ &sta32x->watchdog_work,
+ round_jiffies_relative(HZ));
}
}
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 2eda85ba..a5455c1 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -28,8 +28,6 @@
#include "stac9766.h"
-#define STAC9766_VERSION "0.10"
-
/*
* STAC9766 register cache
*/
@@ -145,14 +143,14 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
cache[reg / 2] = val;
return 0;
}
@@ -164,7 +162,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
+ val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
@@ -175,7 +173,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
reg == AC97_VENDOR_ID2) {
- val = soc_ac97_ops.read(codec->ac97, reg);
+ val = soc_ac97_ops->read(codec->ac97, reg);
return val;
}
return cache[reg / 2];
@@ -242,15 +240,15 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
return -EIO;
return 0;
@@ -274,7 +272,7 @@ reset:
return -EIO;
}
codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+ id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
if (id != 0x4c13) {
stac9766_reset(codec, 0);
reset++;
@@ -338,9 +336,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d447c4a..6d31d88 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -83,6 +83,14 @@
#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */
#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */
#define TAS5086_BKNDERR 0x1c
+#define TAS5086_INPUT_MUX 0x20
+#define TAS5086_PWM_OUTPUT_MUX 0x25
+
+#define TAS5086_MAX_REGISTER TAS5086_PWM_OUTPUT_MUX
+
+#define TAS5086_PWM_START_MIDZ_FOR_START_1 (1 << 7)
+#define TAS5086_PWM_START_MIDZ_FOR_START_2 (1 << 6)
+#define TAS5086_PWM_START_CHANNEL_MASK (0x3f)
/*
* Default TAS5086 power-up configuration
@@ -119,9 +127,30 @@ static const struct reg_default tas5086_reg_defaults[] = {
{ 0x1c, 0x05 },
};
+static int tas5086_register_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
+ return 1;
+ case TAS5086_INPUT_MUX:
+ case TAS5086_PWM_OUTPUT_MUX:
+ return 4;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
{
- return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17));
+ switch (reg) {
+ case 0x0f:
+ case 0x11 ... 0x17:
+ case 0x1d ... 0x1f:
+ return false;
+ default:
+ return true;
+ }
}
static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
@@ -140,6 +169,76 @@ static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
}
+static int tas5086_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i, size;
+ uint8_t buf[5];
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg;
+
+ for (i = size; i >= 1; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 1);
+ if (ret == size + 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int tas5086_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ uint8_t send_buf, recv_buf[4];
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ unsigned int i;
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
struct tas5086_private {
struct regmap *regmap;
unsigned int mclk, sclk;
@@ -376,6 +475,202 @@ static const struct snd_kcontrol_new tas5086_controls[] = {
tas5086_get_deemph, tas5086_put_deemph),
};
+/* Input mux controls */
+static const char *tas5086_dapm_sdin_texts[] =
+{
+ "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R",
+ "SDIN3-L", "SDIN3-R", "Ground (0)", "nc"
+};
+
+static const struct soc_enum tas5086_dapm_input_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0, 8, tas5086_dapm_sdin_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = {
+ SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]),
+ SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]),
+ SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]),
+ SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]),
+ SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]),
+ SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]),
+};
+
+/* Output mux controls */
+static const char *tas5086_dapm_channel_texts[] =
+ { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux",
+ "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" };
+
+static const struct soc_enum tas5086_dapm_output_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0, 6, tas5086_dapm_channel_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = {
+ SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]),
+ SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]),
+ SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]),
+ SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]),
+ SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]),
+ SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]),
+};
+
+static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("SDIN1-L"),
+ SND_SOC_DAPM_INPUT("SDIN1-R"),
+ SND_SOC_DAPM_INPUT("SDIN2-L"),
+ SND_SOC_DAPM_INPUT("SDIN2-R"),
+ SND_SOC_DAPM_INPUT("SDIN3-L"),
+ SND_SOC_DAPM_INPUT("SDIN3-R"),
+ SND_SOC_DAPM_INPUT("SDIN4-L"),
+ SND_SOC_DAPM_INPUT("SDIN4-R"),
+
+ SND_SOC_DAPM_OUTPUT("PWM1"),
+ SND_SOC_DAPM_OUTPUT("PWM2"),
+ SND_SOC_DAPM_OUTPUT("PWM3"),
+ SND_SOC_DAPM_OUTPUT("PWM4"),
+ SND_SOC_DAPM_OUTPUT("PWM5"),
+ SND_SOC_DAPM_OUTPUT("PWM6"),
+
+ SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[0]),
+ SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[1]),
+ SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[2]),
+ SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[3]),
+ SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[4]),
+ SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[5]),
+
+ SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[0]),
+ SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[1]),
+ SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[2]),
+ SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[3]),
+ SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[4]),
+ SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[5]),
+};
+
+static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
+ /* SDIN inputs -> channel muxes */
+ { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" },
+
+ /* Channel muxes -> PWM muxes */
+ { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+
+ { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+
+ { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+
+ { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+
+ { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+
+ { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+
+ /* The PWM muxes are directly connected to the PWM outputs */
+ { "PWM1", NULL, "PWM1 Mux" },
+ { "PWM2", NULL, "PWM2 Mux" },
+ { "PWM3", NULL, "PWM3 Mux" },
+ { "PWM4", NULL, "PWM4 Mux" },
+ { "PWM5", NULL, "PWM5 Mux" },
+ { "PWM6", NULL, "PWM6 Mux" },
+
+};
+
static const struct snd_soc_dai_ops tas5086_dai_ops = {
.hw_params = tas5086_hw_params,
.set_sysclk = tas5086_set_dai_sysclk,
@@ -426,13 +721,34 @@ static int tas5086_probe(struct snd_soc_codec *codec)
{
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int charge_period = 1300000; /* hardware default is 1300 ms */
+ u8 pwm_start_mid_z = 0;
int i, ret;
if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
struct device_node *of_node = codec->dev->of_node;
of_property_read_u32(of_node, "ti,charge-period", &charge_period);
+
+ for (i = 0; i < 6; i++) {
+ char name[25];
+
+ snprintf(name, sizeof(name),
+ "ti,mid-z-channel-%d", i + 1);
+
+ if (of_get_property(of_node, name, NULL) != NULL)
+ pwm_start_mid_z |= 1 << i;
+ }
}
+ /*
+ * If any of the channels is configured to start in Mid-Z mode,
+ * configure 'part 1' of the PWM starts to use Mid-Z, and tell
+ * all configured mid-z channels to start start under 'part 1'.
+ */
+ if (pwm_start_mid_z)
+ regmap_write(priv->regmap, TAS5086_PWM_START,
+ TAS5086_PWM_START_MIDZ_FOR_START_1 |
+ pwm_start_mid_z);
+
/* lookup and set split-capacitor charge period */
if (charge_period == 0) {
regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
@@ -490,6 +806,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.resume = tas5086_soc_resume,
.controls = tas5086_controls,
.num_controls = ARRAY_SIZE(tas5086_controls),
+ .dapm_widgets = tas5086_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
+ .dapm_routes = tas5086_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
};
static const struct i2c_device_id tas5086_i2c_id[] = {
@@ -500,14 +820,16 @@ MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
static const struct regmap_config tas5086_regmap = {
.reg_bits = 8,
- .val_bits = 8,
- .max_register = ARRAY_SIZE(tas5086_reg_defaults),
+ .val_bits = 32,
+ .max_register = TAS5086_MAX_REGISTER,
.reg_defaults = tas5086_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = tas5086_volatile_reg,
.writeable_reg = tas5086_writeable_reg,
.readable_reg = tas5086_accessible_reg,
+ .reg_read = tas5086_reg_read,
+ .reg_write = tas5086_reg_write,
};
static int tas5086_i2c_probe(struct i2c_client *i2c,
@@ -522,7 +844,7 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
- priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap);
+ priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index b1f6982..7b8f3d9 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");
/* AIC26 driver private data */
struct aic26 {
struct spi_device *spi;
- struct snd_soc_codec codec;
+ struct snd_soc_codec *codec;
int master;
int datfm;
int mclk;
@@ -119,6 +119,22 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
+static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = {
+ { "Capture", NULL, "MICIN" },
+ { "Capture", NULL, "AUX" },
+
+ { "HPL", NULL, "Playback" },
+ { "HPR", NULL, "Playback" },
+};
+
/* ---------------------------------------------------------------------
* Digital Audio Interface Operations
*/
@@ -174,9 +190,9 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval);
qval = 0;
reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
- aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
+ snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg);
reg = dval << 2;
- aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+ snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg);
/* Audio Control 3 (master mode, fsref rate) */
reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
@@ -185,13 +201,13 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
reg |= 0x0800;
if (fsref == 48000)
reg |= 0x2000;
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
/* Audio Control 1 (FSref divisor) */
reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
reg &= ~0x0fff;
reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
return 0;
}
@@ -212,7 +228,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute)
reg |= 0x8080;
else
reg &= ~0x8080;
- aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+ snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg);
return 0;
}
@@ -330,7 +346,7 @@ static ssize_t aic26_keyclick_show(struct device *dev,
struct aic26 *aic26 = dev_get_drvdata(dev);
int val, amp, freq, len;
- val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
amp = (val >> 12) & 0x7;
freq = (125 << ((val >> 8) & 0x7)) >> 1;
len = 2 * (1 + ((val >> 4) & 0xf));
@@ -346,9 +362,9 @@ static ssize_t aic26_keyclick_set(struct device *dev,
struct aic26 *aic26 = dev_get_drvdata(dev);
int val;
- val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
val |= 0x8000;
- aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+ snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
return count;
}
@@ -360,25 +376,26 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
*/
static int aic26_probe(struct snd_soc_codec *codec)
{
+ struct aic26 *aic26 = dev_get_drvdata(codec->dev);
int ret, err, i, reg;
- dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n");
+ aic26->codec = codec;
/* Reset the codec to power on defaults */
- aic26_reg_write(codec, AIC26_REG_RESET, 0xBB00);
+ snd_soc_write(codec, AIC26_REG_RESET, 0xBB00);
/* Power up CODEC */
- aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
+ snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0);
/* Audio Control 3 (master mode, fsref rate) */
- reg = aic26_reg_read(codec, AIC26_REG_AUDIO_CTRL3);
+ reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3);
reg &= ~0xf800;
reg |= 0x0800; /* set master mode */
- aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+ snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
/* Fill register cache */
for (i = 0; i < codec->driver->reg_cache_size; i++)
- aic26_reg_read(codec, i);
+ snd_soc_read(codec, i);
/* Register the sysfs files for debugging */
/* Create SysFS files */
@@ -401,6 +418,10 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = {
.write = aic26_reg_write,
.reg_cache_size = AIC26_NUM_REGS,
.reg_word_size = sizeof(u16),
+ .dapm_widgets = tlv320aic26_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+ .dapm_routes = tlv320aic26_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
};
/* ---------------------------------------------------------------------
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 17df4e3..2ed57d4 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate)
return -EINVAL;
}
-static int aic32x4_add_widgets(struct snd_soc_codec *codec)
-{
- snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
- ARRAY_SIZE(aic32x4_dapm_widgets));
-
- snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
- ARRAY_SIZE(aic32x4_dapm_routes));
-
- snd_soc_dapm_new_widgets(&codec->dapm);
- return 0;
-}
-
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
}
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_add_codec_controls(codec, aic32x4_snd_controls,
- ARRAY_SIZE(aic32x4_snd_controls));
- aic32x4_add_widgets(codec);
/*
* Workaround: for an unknown reason, the ADC needs to be powered up
@@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.suspend = aic32x4_suspend,
.resume = aic32x4_resume,
.set_bias_level = aic32x4_set_bias_level,
+
+ .controls = aic32x4_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+ .dapm_widgets = aic32x4_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+ .dapm_routes = aic32x4_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
};
static int aic32x4_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 1514bf8..6e3f269 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -128,10 +128,8 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
};
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
+ snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)
/*
* All input lines are connected when !0xf and disconnected with 0xf bit field,
@@ -140,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -149,10 +146,9 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned short val, val_mask;
- int ret;
- struct snd_soc_dapm_path *path;
- int found = 0;
+ unsigned short val;
+ struct snd_soc_dapm_update update;
+ int connect, change;
val = (ucontrol->value.integer.value[0] & mask);
@@ -160,42 +156,26 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
if (val)
val = mask;
+ connect = !!val;
+
if (invert)
val = mask - val;
- val_mask = mask << shift;
- val = val << shift;
-
- mutex_lock(&widget->codec->mutex);
- if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
- /* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
- if (path->kcontrol != kcontrol)
- continue;
+ mask <<= shift;
+ val <<= shift;
- /* found, now check type */
- found = 1;
- if (val)
- /* new connection */
- path->connect = invert ? 0 : 1;
- else
- /* old connection must be powered down */
- path->connect = invert ? 1 : 0;
+ change = snd_soc_test_bits(codec, val, mask, reg);
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
- dapm_mark_dirty(path->source, "tlv320aic3x source");
- dapm_mark_dirty(path->sink, "tlv320aic3x sink");
-
- break;
- }
+ snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+ &update);
}
- mutex_unlock(&widget->codec->mutex);
-
- if (found)
- snd_soc_dapm_sync(widget->dapm);
-
- ret = snd_soc_update_bits_locked(widget->codec, reg, val_mask, val);
- return ret;
+ return change;
}
/*
@@ -1494,6 +1474,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = {
{ "tlv320aic3x", AIC3X_MODEL_3X },
{ "tlv320aic33", AIC3X_MODEL_33 },
{ "tlv320aic3007", AIC3X_MODEL_3007 },
+ { "tlv320aic3106", AIC3X_MODEL_3X },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
@@ -1584,6 +1565,9 @@ static int aic3x_i2c_remove(struct i2c_client *client)
#if defined(CONFIG_OF)
static const struct of_device_id tlv320aic3x_of_match[] = {
{ .compatible = "ti,tlv320aic3x", },
+ { .compatible = "ti,tlv320aic33" },
+ { .compatible = "ti,tlv320aic3007" },
+ { .compatible = "ti,tlv320aic3106" },
{},
};
MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 8e6e5b0..1e3884d 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -137,8 +137,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */
struct twl4030_priv {
- struct snd_soc_codec codec;
-
unsigned int codec_powered;
/* reference counts of AIF/APLL users */
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 9b9a6e5..3c79dbb 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -38,6 +38,14 @@
#include "twl6040.h"
+enum twl6040_dai_id {
+ TWL6040_DAI_LEGACY = 0,
+ TWL6040_DAI_UL,
+ TWL6040_DAI_DL1,
+ TWL6040_DAI_DL2,
+ TWL6040_DAI_VIB,
+};
+
#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
@@ -67,6 +75,8 @@ struct twl6040_data {
int pll_power_mode;
int hs_power_mode;
int hs_power_mode_locked;
+ bool dl1_unmuted;
+ bool dl2_unmuted;
unsigned int clk_in;
unsigned int sysclk;
struct twl6040_jack_data hs_jack;
@@ -220,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
return value;
}
+static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ /* DL1 path */
+ return priv->dl1_unmuted;
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ return priv->dl2_unmuted;
+ default:
+ return 1;
+ };
+}
+
/*
* write to the twl6040 register space
*/
@@ -232,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
return -EIO;
twl6040_write_reg_cache(codec, reg, value);
- if (likely(reg < TWL6040_REG_SW_SHADOW))
+ if (likely(reg < TWL6040_REG_SW_SHADOW) &&
+ twl6040_is_path_unmuted(codec, reg))
return twl6040_reg_write(twl6040, reg, value);
else
return 0;
@@ -399,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
struct snd_soc_codec *codec = data;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hs_jack.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
@@ -407,9 +438,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
@@ -1026,16 +1055,84 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
+ int mute)
+{
+ struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int hslctl, hsrctl, earctl;
+ int hflctl, hfrctl;
+
+ switch (id) {
+ case TWL6040_DAI_DL1:
+ hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
+ earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ earctl &= ~0x01;
+ hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+ hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
+
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
+ priv->dl1_unmuted = !mute;
+ break;
+ case TWL6040_DAI_DL2:
+ hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
+ hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);
+
+ if (mute) {
+ /* Power down drivers and DACs */
+ hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
+ TWL6040_HFDRVENA);
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
+ twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
+ priv->dl2_unmuted = !mute;
+ break;
+ default:
+ break;
+ };
+}
+
+static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ switch (dai->id) {
+ case TWL6040_DAI_LEGACY:
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
+ twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
+ break;
+ case TWL6040_DAI_DL1:
+ case TWL6040_DAI_DL2:
+ twl6040_mute_path(dai->codec, dai->id, mute);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops twl6040_dai_ops = {
.startup = twl6040_startup,
.hw_params = twl6040_hw_params,
.prepare = twl6040_prepare,
.set_sysclk = twl6040_set_dai_sysclk,
+ .digital_mute = twl6040_digital_mute,
};
static struct snd_soc_dai_driver twl6040_dai[] = {
{
.name = "twl6040-legacy",
+ .id = TWL6040_DAI_LEGACY,
.playback = {
.stream_name = "Legacy Playback",
.channels_min = 1,
@@ -1054,6 +1151,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-ul",
+ .id = TWL6040_DAI_UL,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -1065,6 +1163,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-dl1",
+ .id = TWL6040_DAI_DL1,
.playback = {
.stream_name = "Headset Playback",
.channels_min = 1,
@@ -1076,6 +1175,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-dl2",
+ .id = TWL6040_DAI_DL2,
.playback = {
.stream_name = "Handsfree Playback",
.channels_min = 1,
@@ -1087,6 +1187,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
{
.name = "twl6040-vib",
+ .id = TWL6040_DAI_VIB,
.playback = {
.stream_name = "Vibra Playback",
.channels_min = 1,
@@ -1143,7 +1244,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
mutex_init(&priv->mutex);
- ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL,
+ ret = request_threaded_irq(priv->plug_irq, NULL,
twl6040_audio_handler, IRQF_NO_SUSPEND,
"twl6040_irq_plug", codec);
if (ret) {
@@ -1159,6 +1260,9 @@ static int twl6040_probe(struct snd_soc_codec *codec)
static int twl6040_remove(struct snd_soc_codec *codec)
{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ free_irq(priv->plug_irq, codec);
twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 6d0aa44..c94d4c1 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -325,7 +325,6 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u8 reg;
struct uda134x_platform_data *pd = codec->control_data;
int i;
u8 *cache = codec->reg_cache;
@@ -334,23 +333,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
- /* ADC, DAC on */
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- case UDA134X_UDA1345:
- reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
- uda134x_write(codec, UDA134X_DATA011, reg | 0x03);
- break;
- case UDA134X_UDA1341:
- reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
- uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
- break;
- default:
- printk(KERN_ERR "UDA134X SoC codec: "
- "unsupported model %d\n", pd->model);
- return -EINVAL;
- }
break;
case SND_SOC_BIAS_PREPARE:
/* power on */
@@ -362,23 +344,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_STANDBY:
- /* ADC, DAC power off */
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- case UDA134X_UDA1345:
- reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
- uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03));
- break;
- case UDA134X_UDA1341:
- reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
- uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
- break;
- default:
- printk(KERN_ERR "UDA134X SoC codec: "
- "unsupported model %d\n", pd->model);
- return -EINVAL;
- }
break;
case SND_SOC_BIAS_OFF:
/* power off */
@@ -450,6 +415,37 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
+/* UDA1341 has the DAC/ADC power down in STATUS1 */
+static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
+};
+
+/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
+static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
+ { "ADC", NULL, "VINL1" },
+ { "ADC", NULL, "VINR1" },
+ { "ADC", NULL, "VINL2" },
+ { "ADC", NULL, "VINR2" },
+ { "VOUTL", NULL, "DAC" },
+ { "VOUTR", NULL, "DAC" },
+};
+
static const struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
@@ -485,6 +481,8 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
{
struct uda134x_priv *uda134x;
struct uda134x_platform_data *pd = codec->card->dev->platform_data;
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned num_widgets;
int ret;
@@ -526,6 +524,22 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
else
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (pd->model == UDA134X_UDA1341) {
+ widgets = uda1341_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
+ } else {
+ widgets = uda1340_dapm_widgets;
+ num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
+ }
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+ if (ret) {
+ printk(KERN_ERR "%s failed to register dapm controls: %d",
+ __func__, ret);
+ kfree(uda134x);
+ return ret;
+ }
+
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
@@ -599,6 +613,10 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.read = uda134x_read_reg_cache,
.write = uda134x_write,
.set_bias_level = uda134x_set_bias_level,
+ .dapm_widgets = uda134x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
+ .dapm_routes = uda134x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
};
static int uda134x_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 54cd3da..b7ab2ef 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -290,6 +290,18 @@ static const struct snd_kcontrol_new wl1273_controls[] = {
snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
};
+static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("RX"),
+
+ SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
+ { "Capture", NULL, "RX" },
+
+ { "TX", NULL, "Playback" },
+};
+
static int wl1273_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -483,6 +495,11 @@ static int wl1273_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
.probe = wl1273_probe,
.remove = wl1273_remove,
+
+ .dapm_widgets = wl1273_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
+ .dapm_routes = wl1273_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
};
static int wl1273_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 370af0c..d5ebcb0 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
@@ -409,39 +410,39 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
rec->command, rec->length);
len = rec->length + 8;
- out = kzalloc(len, GFP_KERNEL);
+ xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
+ if (!xfer) {
+ dev_err(codec->dev, "Failed to allocate xfer\n");
+ ret = -ENOMEM;
+ goto abort;
+ }
+
+ xfer->codec = codec;
+ list_add_tail(&xfer->list, &xfer_list);
+
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev,
"Failed to allocate RX buffer\n");
ret = -ENOMEM;
goto abort1;
}
+ xfer->t.rx_buf = out;
- img = kzalloc(len, GFP_KERNEL);
+ img = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img) {
dev_err(codec->dev,
"Failed to allocate image buffer\n");
ret = -ENOMEM;
goto abort1;
}
+ xfer->t.tx_buf = img;
byte_swap_64((u64 *)&rec->command, img, len);
- xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
- if (!xfer) {
- dev_err(codec->dev, "Failed to allocate xfer\n");
- ret = -ENOMEM;
- goto abort1;
- }
-
- xfer->codec = codec;
- list_add_tail(&xfer->list, &xfer_list);
-
spi_message_init(&xfer->m);
xfer->m.complete = wm0010_boot_xfer_complete;
xfer->m.context = xfer;
- xfer->t.tx_buf = img;
- xfer->t.rx_buf = out;
xfer->t.len = len;
xfer->t.bits_per_word = 8;
@@ -522,14 +523,14 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
/* Copy to local buffer first as vmalloc causes problems for dma */
- img = kzalloc(fw->size, GFP_KERNEL);
+ img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
if (!img) {
dev_err(codec->dev, "Failed to allocate image buffer\n");
ret = -ENOMEM;
goto abort2;
}
- out = kzalloc(fw->size, GFP_KERNEL);
+ out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev, "Failed to allocate output buffer\n");
ret = -ENOMEM;
@@ -669,14 +670,14 @@ static int wm0010_boot(struct snd_soc_codec *codec)
ret = -ENOMEM;
len = pll_rec.length + 8;
- out = kzalloc(len, GFP_KERNEL);
+ out = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!out) {
dev_err(codec->dev,
"Failed to allocate RX buffer\n");
goto abort;
}
- img_swap = kzalloc(len, GFP_KERNEL);
+ img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
if (!img_swap) {
dev_err(codec->dev,
"Failed to allocate image buffer\n");
@@ -972,6 +973,13 @@ static int wm0010_spi_probe(struct spi_device *spi)
}
wm0010->irq = irq;
+ ret = irq_set_irq_wake(irq, 1);
+ if (ret) {
+ dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n",
+ irq, ret);
+ return ret;
+ }
+
if (spi->max_speed_hz)
wm0010->board_max_spi_speed = spi->max_speed_hz;
else
@@ -995,6 +1003,8 @@ static int wm0010_spi_remove(struct spi_device *spi)
gpio_set_value_cansleep(wm0010->gpio_reset,
wm0010->gpio_reset_value);
+ irq_set_irq_wake(wm0010->irq, 0);
+
if (wm0010->irq)
free_irq(wm0010->irq, wm0010);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 100fdad..8bbddc1 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -814,7 +814,20 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
-SOC_VALUE_ENUM("HPOUT3 OSR", wm5102_hpout_osr[2]),
+SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
+
+SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
+SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
+SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE,
+ ARIZONA_DRE3L_ENA_SHIFT, 1, 0),
+
+SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2,
+ ARIZONA_DRE_T_LOW_SHIFT, 63, 0),
+
+SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3,
+ ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -852,6 +865,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -898,6 +920,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@@ -967,6 +998,8 @@ SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1117,6 +1150,56 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
@@ -1189,6 +1272,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@@ -1249,6 +1341,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@@ -1304,17 +1404,25 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "OUT5L", NULL, "SYSCLK" },
{ "OUT5R", NULL, "SYSCLK" },
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
- { "Mic Mute Mixer", NULL, "Noise Mixer" },
- { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
{ "AIF1 Capture", NULL, "AIF1TX1" },
{ "AIF1 Capture", NULL, "AIF1TX2" },
{ "AIF1 Capture", NULL, "AIF1TX3" },
@@ -1345,13 +1453,41 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1362,23 +1498,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "IN3L PGA", NULL, "IN3L" },
{ "IN3R PGA", NULL, "IN3R" },
- { "ASRC1L", NULL, "ASRC1L Input" },
- { "ASRC1R", NULL, "ASRC1R Input" },
- { "ASRC2L", NULL, "ASRC2L Input" },
- { "ASRC2R", NULL, "ASRC2R Input" },
-
- { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
- { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
-
- { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
- { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
-
- { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
- { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
-
- { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
- { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
-
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1408,6 +1527,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@@ -1421,22 +1549,25 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MUX_ROUTES("ASRC1L"),
- ARIZONA_MUX_ROUTES("ASRC1R"),
- ARIZONA_MUX_ROUTES("ASRC2L"),
- ARIZONA_MUX_ROUTES("ASRC2R"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
- ARIZONA_MUX_ROUTES("ISRC1INT1"),
- ARIZONA_MUX_ROUTES("ISRC1INT2"),
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
- ARIZONA_MUX_ROUTES("ISRC1DEC1"),
- ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
- ARIZONA_MUX_ROUTES("ISRC2INT1"),
- ARIZONA_MUX_ROUTES("ISRC2INT2"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
- ARIZONA_MUX_ROUTES("ISRC2DEC1"),
- ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
ARIZONA_DSP_ROUTES("DSP1"),
@@ -1468,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "SPKDAT1R", NULL, "OUT5R" },
{ "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
};
static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1560,6 +1694,63 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
+ {
+ .name = "wm5102-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5102-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
};
static int wm5102_codec_probe(struct snd_soc_codec *codec)
@@ -1578,6 +1769,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_spk(codec);
+ arizona_init_gpio(codec);
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 88ad7db..bbd6438 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -58,14 +58,10 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
-SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
- ARIZONA_IN4_OSR_SHIFT, 1, 0),
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
+SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
@@ -309,6 +305,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@@ -360,6 +365,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@@ -414,6 +428,9 @@ SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"),
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -550,6 +567,56 @@ SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
@@ -640,6 +707,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@@ -690,6 +766,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@@ -736,17 +820,27 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT6L", NULL, "SYSCLK" },
{ "OUT6R", NULL, "SYSCLK" },
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+ { "IN3L", NULL, "SYSCLK" },
+ { "IN3R", NULL, "SYSCLK" },
+ { "IN4L", NULL, "SYSCLK" },
+ { "IN4R", NULL, "SYSCLK" },
+
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
- { "Mic Mute Mixer", NULL, "Noise Mixer" },
- { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
{ "AIF1 Capture", NULL, "AIF1TX1" },
{ "AIF1 Capture", NULL, "AIF1TX2" },
{ "AIF1 Capture", NULL, "AIF1TX3" },
@@ -777,13 +871,41 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -829,6 +951,15 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@@ -844,10 +975,13 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MUX_ROUTES("ASRC1L"),
- ARIZONA_MUX_ROUTES("ASRC1R"),
- ARIZONA_MUX_ROUTES("ASRC2L"),
- ARIZONA_MUX_ROUTES("ASRC2R"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
{ "HPOUT1L", NULL, "OUT1L" },
{ "HPOUT1R", NULL, "OUT1R" },
@@ -871,6 +1005,11 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "SPKDAT2R", NULL, "OUT6R" },
{ "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1L" },
+ { "DRC1 Signal Activity", NULL, "DRC1R" },
+ { "DRC2 Signal Activity", NULL, "DRC2L" },
+ { "DRC2 Signal Activity", NULL, "DRC2R" },
};
static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -963,6 +1102,63 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
+ {
+ .name = "wm5110-slim1",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim2",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm5110-slim3",
+ .id = 6,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
};
static int wm5110_codec_probe(struct snd_soc_codec *codec)
@@ -978,6 +1174,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
return ret;
arizona_init_spk(codec);
+ arizona_init_gpio(codec);
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 0e8b3aa..af1318d 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1301,7 +1301,8 @@ static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
if (device_may_wakeup(wm8350->dev))
pm_wakeup_event(wm8350->dev, 250);
- schedule_delayed_work(&priv->hpl.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpl.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
@@ -1318,7 +1319,8 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
if (device_may_wakeup(wm8350->dev))
pm_wakeup_event(wm8350->dev, 250);
- schedule_delayed_work(&priv->hpr.work, msecs_to_jiffies(200));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->hpr.work, msecs_to_jiffies(200));
return IRQ_HANDLED;
}
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index af6d227..d2a0928 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -143,13 +143,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array)
static const char *wm8400_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index 462f5e4..7b1a6d5 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -23,6 +23,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8727_dapm_routes[] = {
+ { "VOUTL", NULL, "Playback" },
+ { "VOUTR", NULL, "Playback" },
+};
+
/*
* Note this is a simple chip with no configuration interface, sample rate is
* determined automatically by examining the Master clock and Bit clock ratios
@@ -43,7 +53,12 @@ static struct snd_soc_dai_driver wm8727_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8727;
+static struct snd_soc_codec_driver soc_codec_dev_wm8727 = {
+ .dapm_widgets = wm8727_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+ .dapm_routes = wm8727_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+};
static int wm8727_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 5276062..456bb8c 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -45,6 +45,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
struct wm8731_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+ const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk;
int sysclk_type;
int playback_fs;
@@ -290,6 +291,36 @@ static const struct _coeff_div coeff_div[] = {
{12000000, 88200, 136, 0xf, 0x1, 0x1},
};
+/* rates constraints */
+static const unsigned int wm8731_rates_12000000[] = {
+ 8000, 32000, 44100, 48000, 96000, 88200,
+};
+
+static const unsigned int wm8731_rates_12288000_18432000[] = {
+ 8000, 32000, 48000, 96000,
+};
+
+static const unsigned int wm8731_rates_11289600_16934400[] = {
+ 8000, 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = {
+ .list = wm8731_rates_12000000,
+ .count = ARRAY_SIZE(wm8731_rates_12000000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = {
+ .list = wm8731_rates_12288000_18432000,
+ .count = ARRAY_SIZE(wm8731_rates_12288000_18432000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = {
+ .list = wm8731_rates_11289600_16934400,
+ .count = ARRAY_SIZE(wm8731_rates_11289600_16934400),
+};
+
static inline int get_coeff(int mclk, int rate)
{
int i;
@@ -362,17 +393,26 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
switch (freq) {
- case 11289600:
+ case 0:
+ wm8731->constraints = NULL;
+ break;
case 12000000:
+ wm8731->constraints = &wm8731_constraints_12000000;
+ break;
case 12288000:
- case 16934400:
case 18432000:
- wm8731->sysclk = freq;
+ wm8731->constraints = &wm8731_constraints_12288000_18432000;
+ break;
+ case 16934400:
+ case 11289600:
+ wm8731->constraints = &wm8731_constraints_11289600_16934400;
break;
default:
return -EINVAL;
}
+ wm8731->sysclk = freq;
+
snd_soc_dapm_sync(&codec->dapm);
return 0;
@@ -475,12 +515,26 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
+static int wm8731_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (wm8731->constraints)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ wm8731->constraints);
+
+ return 0;
+}
+
#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)
static const struct snd_soc_dai_ops wm8731_dai_ops = {
+ .startup = wm8731_startup,
.hw_params = wm8731_hw_params,
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 0a4ab4c..d96ebf5 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1456,8 +1456,9 @@ static int wm8753_resume(struct snd_soc_codec *codec)
if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
codec->dapm.bias_level = SND_SOC_BIAS_ON;
- schedule_delayed_work(&codec->dapm.delayed_work,
- msecs_to_jiffies(caps_charge));
+ queue_delayed_work(system_power_efficient_wq,
+ &codec->dapm.delayed_work,
+ msecs_to_jiffies(caps_charge));
}
return 0;
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
index f1fdbf6..8092495 100644
--- a/sound/soc/codecs/wm8782.c
+++ b/sound/soc/codecs/wm8782.c
@@ -26,6 +26,16 @@
#include <sound/initval.h>
#include <sound/soc.h>
+static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route wm8782_dapm_routes[] = {
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
+};
+
static struct snd_soc_dai_driver wm8782_dai = {
.name = "wm8782",
.capture = {
@@ -40,7 +50,12 @@ static struct snd_soc_dai_driver wm8782_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8782;
+static struct snd_soc_codec_driver soc_codec_dev_wm8782 = {
+ .dapm_widgets = wm8782_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+ .dapm_routes = wm8782_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+};
static int wm8782_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 9d88437..eebcb1d 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg;
int ret;
@@ -403,10 +401,8 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
}
#define SOC_DAPM_SINGLE_W(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 = wm8903_class_w_put, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8903_class_w_put)
static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 3ff195c..4dfa8dc 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -603,13 +603,8 @@ SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
SOC_ENUM("High Pass Filter Mode", hpf_mode),
-
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "ADC 128x OSR Switch",
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,
- .put = wm8904_adc_osr_put,
- .private_value = SOC_SINGLE_VALUE(WM8904_ANALOGUE_ADC_0, 0, 1, 0),
-},
+SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0,
+ snd_soc_get_volsw, wm8904_adc_osr_put),
};
static const char *drc_path_text[] = {
@@ -1017,7 +1012,7 @@ 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);
+ SOC_DAPM_ENUM("LINER Mux", liner_enum);
static const char *sidetone_text[] = {
"None", "Left", "Right"
@@ -1207,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
break;
}
- snd_soc_dapm_new_widgets(dapm);
return 0;
}
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 0a4ffdd..f156010 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -263,8 +263,8 @@ SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
-SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
- 0, 127, 0),
+SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC,
+ 0, 255, 0, adc_tlv),
SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
@@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (pll_div.k) {
reg |= 0x20;
- snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
- snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
- snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff);
}
snd_soc_write(codec, WM8960_PLL1, reg);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e971028..11d80f3 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -51,6 +51,7 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = {
/* codec private data */
struct wm8962_priv {
+ struct wm8962_pdata pdata;
struct regmap *regmap;
struct snd_soc_codec *codec;
@@ -1600,7 +1601,6 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- u16 *reg_cache = codec->reg_cache;
int ret;
/* Apply the update (if any) */
@@ -1609,16 +1609,19 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
return 0;
/* If the left PGA is enabled hit that VU bit... */
- if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTL_PGA_ENA)
- return snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
- reg_cache[WM8962_HPOUTL_VOLUME]);
+ ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
+ if (ret & WM8962_HPOUTL_PGA_ENA) {
+ snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTL_VOLUME));
+ return 1;
+ }
/* ...otherwise the right. The VU is stereo. */
- if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTR_PGA_ENA)
- return snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
- reg_cache[WM8962_HPOUTR_VOLUME]);
+ if (ret & WM8962_HPOUTR_PGA_ENA)
+ snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
+ snd_soc_read(codec, WM8962_HPOUTR_VOLUME));
- return 0;
+ return 1;
}
/* The VU bits for the speakers are in a different register to the mute
@@ -2345,12 +2348,13 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
static int wm8962_add_widgets(struct snd_soc_codec *codec)
{
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_add_codec_controls(codec, wm8962_snd_controls,
ARRAY_SIZE(wm8962_snd_controls));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls,
ARRAY_SIZE(wm8962_spk_mono_controls));
else
@@ -2360,7 +2364,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets,
ARRAY_SIZE(wm8962_dapm_widgets));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets,
ARRAY_SIZE(wm8962_dapm_spk_mono_widgets));
else
@@ -2369,7 +2373,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, wm8962_intercon,
ARRAY_SIZE(wm8962_intercon));
- if (pdata && pdata->spk_mono)
+ if (pdata->spk_mono)
snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon,
ARRAY_SIZE(wm8962_spk_mono_intercon));
else
@@ -2617,8 +2621,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
wm8962->sysclk_rate = freq;
- wm8962_configure_bclk(codec);
-
return 0;
}
@@ -3042,8 +3044,9 @@ static irqreturn_t wm8962_irq(int irq, void *data)
pm_wakeup_event(dev, 300);
- schedule_delayed_work(&wm8962->mic_work,
- msecs_to_jiffies(250));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8962->mic_work,
+ msecs_to_jiffies(250));
}
return IRQ_HANDLED;
@@ -3171,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
long int time;
int ret;
- ret = strict_strtol(buf, 10, &time);
+ ret = kstrtol(buf, 10, &time);
if (ret != 0)
return ret;
@@ -3333,14 +3336,14 @@ static struct gpio_chip wm8962_template_chip = {
static void wm8962_init_gpio(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
int ret;
wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
wm8962->gpio_chip.dev = codec->dev;
- if (pdata && pdata->gpio_base)
+ if (pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
else
wm8962->gpio_chip.base = -1;
@@ -3373,8 +3376,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
{
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
- u16 *reg_cache = codec->reg_cache;
+ struct wm8962_pdata *pdata = &wm8962->pdata;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@@ -3421,30 +3423,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
0);
- if (pdata) {
- /* Apply static configuration for GPIOs */
- for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
- if (pdata->gpio_init[i]) {
- wm8962_set_gpio_mode(codec, i + 1);
- snd_soc_write(codec, 0x200 + i,
- pdata->gpio_init[i] & 0xffff);
- }
+ /* Apply static configuration for GPIOs */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
+ if (pdata->gpio_init[i]) {
+ wm8962_set_gpio_mode(codec, i + 1);
+ snd_soc_write(codec, 0x200 + i,
+ pdata->gpio_init[i] & 0xffff);
+ }
- /* Put the speakers into mono mode? */
- if (pdata->spk_mono)
- reg_cache[WM8962_CLASS_D_CONTROL_2]
- |= WM8962_SPK_MONO;
- /* Micbias setup, detection enable and detection
- * threasholds. */
- if (pdata->mic_cfg)
- snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
- WM8962_MICDET_ENA |
- WM8962_MICDET_THR_MASK |
- WM8962_MICSHORT_THR_MASK |
- WM8962_MICBIAS_LVL,
- pdata->mic_cfg);
- }
+ /* Put the speakers into mono mode? */
+ if (pdata->spk_mono)
+ snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_2,
+ WM8962_SPK_MONO_MASK, WM8962_SPK_MONO);
+
+ /* Micbias setup, detection enable and detection
+ * threasholds. */
+ if (pdata->mic_cfg)
+ snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
+ WM8962_MICDET_ENA |
+ WM8962_MICDET_THR_MASK |
+ WM8962_MICSHORT_THR_MASK |
+ WM8962_MICBIAS_LVL,
+ pdata->mic_cfg);
/* Latch volume update bits */
snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
@@ -3506,7 +3507,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_gpio(codec);
if (wm8962->irq) {
- if (pdata && pdata->irq_active_low) {
+ if (pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
} else {
@@ -3584,6 +3585,34 @@ static const struct regmap_config wm8962_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
+ struct wm8962_pdata *pdata)
+{
+ const struct device_node *np = i2c->dev.of_node;
+ u32 val32;
+ int i;
+
+ if (of_property_read_bool(np, "spk-mono"))
+ pdata->spk_mono = true;
+
+ if (of_property_read_u32(np, "mic-cfg", &val32) >= 0)
+ pdata->mic_cfg = val32;
+
+ if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init,
+ ARRAY_SIZE(pdata->gpio_init)) >= 0)
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) {
+ /*
+ * The range of GPIO register value is [0x0, 0xffff]
+ * While the default value of each register is 0x0
+ * Any other value will be regarded as default value
+ */
+ if (pdata->gpio_init[i] > 0xffff)
+ pdata->gpio_init[i] = 0x0;
+ }
+
+ return 0;
+}
+
static int wm8962_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -3603,6 +3632,15 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
init_completion(&wm8962->fll_lock);
wm8962->irq = i2c->irq;
+ /* If platform data was supplied, update the default data in priv */
+ if (pdata) {
+ memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata));
+ } else if (i2c->dev.of_node) {
+ ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata);
+ if (ret != 0)
+ return ret;
+ }
+
for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
wm8962->supplies[i].supply = wm8962_supply_names[i];
@@ -3666,7 +3704,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
- if (pdata && pdata->in4_dc_measure) {
+ if (wm8962->pdata.in4_dc_measure) {
ret = regmap_register_patch(wm8962->regmap,
wm8962_dc_measure,
ARRAY_SIZE(wm8962_dc_measure));
@@ -3719,8 +3757,34 @@ static int wm8962_runtime_resume(struct device *dev)
wm8962_reset(wm8962);
+ /* SYSCLK defaults to on; make sure it is off so we can safely
+ * write to registers if the device is declocked.
+ */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA, 0);
+
+ /* Ensure we have soft control over all registers */
+ regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
+ WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
+
+ /* Ensure that the oscillator and PLLs are disabled */
+ regmap_update_bits(wm8962->regmap, WM8962_PLL2,
+ WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
+ 0);
+
regcache_sync(wm8962->regmap);
+ regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
+ WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
+
+ /* Bias enable at 2*5k (fast start-up) */
+ regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
+ WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK,
+ WM8962_BIAS_ENA | 0x180);
+
+ msleep(5);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 029f31c..d8fc531 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -921,6 +921,7 @@ static struct snd_soc_dai_driver wm8978_dai = {
.formats = WM8978_FORMATS,
},
.ops = &wm8978_dai_ops,
+ .symmetric_rates = 1,
};
static int wm8978_suspend(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 837978e..253c88b 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -151,14 +151,9 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
- tlv_array) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
static const char *wm8990_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 8a942ef..07707d8 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -822,12 +822,7 @@
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
#endif /* _WM8991_H */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 29e95f9..86426a1 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -289,10 +290,8 @@ static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 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) }
+ SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \
+ snd_soc_get_volsw, wm8994_put_drc_sw)
static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -820,8 +819,9 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
* don't want false reports.
*/
if (wm8994->jackdet && !wm8994->clk_has_run) {
- schedule_delayed_work(&wm8994->jackdet_bootstrap,
- msecs_to_jiffies(1000));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->jackdet_bootstrap,
+ msecs_to_jiffies(1000));
wm8994->clk_has_run = true;
}
break;
@@ -1432,17 +1432,13 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
};
#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) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8994_put_class_w)
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -1498,6 +1494,24 @@ static const char *aif1dac_text[] = {
"AIF1DACDAT", "AIF3DACDAT",
};
+static const char *loopback_text[] = {
+ "None", "ADCDAT",
+};
+
+static const struct soc_enum aif1_loopback_enum =
+ SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif1_loopback =
+ SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum);
+
+static const struct soc_enum aif2_loopback_enum =
+ SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2,
+ loopback_text);
+
+static const struct snd_kcontrol_new aif2_loopback =
+ SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum);
+
static const struct soc_enum aif1dac_enum =
SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
@@ -1744,6 +1758,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
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("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback),
+SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback),
+
SND_SOC_DAPM_POST("Debug log", post_ev),
};
@@ -1875,9 +1892,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
- { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
+ { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" },
{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
- { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
+ { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" },
{ "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
@@ -1928,6 +1945,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+ /* Loopback */
+ { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
+ { "AIF1 Loopback", "None", "AIF1DACDAT" },
+ { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" },
+ { "AIF2 Loopback", "None", "AIF2DACDAT" },
+
/* Sidetone */
{ "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
{ "Left Sidetone", "DMIC2", "DMIC2L" },
@@ -2010,15 +2033,16 @@ struct fll_div {
u16 outdiv;
u16 n;
u16 k;
+ u16 lambda;
u16 clk_ref_div;
u16 fll_fratio;
};
-static int wm8994_get_fll_config(struct fll_div *fll,
+static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
int freq_in, int freq_out)
{
u64 Kpart;
- unsigned int K, Ndiv, Nmod;
+ unsigned int K, Ndiv, Nmod, gcd_fll;
pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
@@ -2067,20 +2091,32 @@ static int wm8994_get_fll_config(struct fll_div *fll,
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;
+ switch (control->type) {
+ case WM8994:
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, freq_in);
+
+ K = Kpart & 0xFFFFFFFF;
- do_div(Kpart, freq_in);
+ if ((K % 10) >= 5)
+ K += 5;
- K = Kpart & 0xFFFFFFFF;
+ /* Move down to proper range now rounding is done */
+ fll->k = K / 10;
+ fll->lambda = 0;
- if ((K % 10) >= 5)
- K += 5;
+ pr_debug("N=%x K=%x\n", fll->n, fll->k);
+ break;
- /* Move down to proper range now rounding is done */
- fll->k = K / 10;
+ default:
+ gcd_fll = gcd(freq_out, freq_in);
- pr_debug("N=%x K=%x\n", fll->n, fll->k);
+ fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
+ fll->lambda = freq_in / gcd_fll;
+
+ }
return 0;
}
@@ -2144,9 +2180,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
* analysis bugs spewing warnings.
*/
if (freq_out)
- ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
+ ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
else
- ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
+ ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
wm8994->fll[id].out);
if (ret < 0)
return ret;
@@ -2191,6 +2227,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_N_MASK,
fll.n << WM8994_FLL1_N_SHIFT);
+ if (fll.lambda) {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
+ WM8958_FLL1_LAMBDA_MASK,
+ fll.lambda);
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
+ } else {
+ snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
+ WM8958_FLL1_EFS_ENA, 0);
+ }
+
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
@@ -2555,17 +2602,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct wm8994 *control = wm8994->wm8994;
int ms_reg;
int aif1_reg;
+ int dac_reg;
+ int adc_reg;
int ms = 0;
int aif1 = 0;
+ int lrclk = 0;
switch (dai->id) {
case 1:
ms_reg = WM8994_AIF1_MASTER_SLAVE;
aif1_reg = WM8994_AIF1_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
break;
case 2:
ms_reg = WM8994_AIF2_MASTER_SLAVE;
aif1_reg = WM8994_AIF2_CONTROL_1;
+ dac_reg = WM8994_AIF1DAC_LRCLK;
+ adc_reg = WM8994_AIF1ADC_LRCLK;
break;
default:
return -EINVAL;
@@ -2584,6 +2638,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif1 |= WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x18;
break;
@@ -2622,12 +2677,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_IB_IF:
aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
+ lrclk |= WM8958_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;
+ lrclk |= WM8958_AIF1_LRCLK_INV;
break;
default:
return -EINVAL;
@@ -2658,6 +2715,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
aif1);
snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
ms);
+ snd_soc_update_bits(codec, dac_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
+ snd_soc_update_bits(codec, adc_reg,
+ WM8958_AIF1_LRCLK_INV, lrclk);
return 0;
}
@@ -3096,24 +3157,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
static int wm8994_codec_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994 *control = wm8994->wm8994;
int i, ret;
- unsigned int val, mask;
-
- if (control->revision < 4) {
- /* force a HW read */
- ret = regmap_read(control->regmap,
- WM8994_POWER_MANAGEMENT_5, &val);
-
- /* modify the cache only */
- codec->cache_only = 1;
- mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA |
- WM8994_DAC2R_ENA | WM8994_DAC2L_ENA;
- val &= mask;
- snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
- mask, val);
- codec->cache_only = 0;
- }
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
if (!wm8994->fll_suspend[i].out)
@@ -3442,7 +3486,8 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
pm_wakeup_event(codec->dev, 300);
- schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
+ queue_delayed_work(system_power_efficient_wq,
+ &priv->mic_work, msecs_to_jiffies(250));
return IRQ_HANDLED;
}
@@ -3495,6 +3540,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
wm8994->btn_mask);
}
+static void wm8958_open_circuit_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ open_circuit_work.work);
+ struct device *dev = wm8994->wm8994->dev;
+
+ wm1811_micd_stop(wm8994->hubs.codec);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ dev_dbg(dev, "Reporting open circuit\n");
+
+ wm8994->jack_mic = false;
+ wm8994->mic_detecting = true;
+
+ wm8958_micd_set_rate(wm8994->hubs.codec);
+
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+ wm8994->btn_mask |
+ SND_JACK_HEADSET);
+
+ mutex_unlock(&wm8994->accdet_lock);
+}
+
static void wm8958_mic_id(void *data, u16 status)
{
struct snd_soc_codec *codec = data;
@@ -3504,16 +3574,10 @@ static void wm8958_mic_id(void *data, u16 status)
if (!(status & WM8958_MICD_STS)) {
/* If nothing present then clear our statuses */
dev_dbg(codec->dev, "Detected open circuit\n");
- wm8994->jack_mic = false;
- wm8994->mic_detecting = true;
-
- wm1811_micd_stop(codec);
-
- wm8958_micd_set_rate(codec);
- snd_soc_jack_report(wm8994->micdet[0].jack, 0,
- wm8994->btn_mask |
- SND_JACK_HEADSET);
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->open_circuit_work,
+ msecs_to_jiffies(2500));
return;
}
@@ -3598,6 +3662,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
pm_runtime_get_sync(codec->dev);
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+
mutex_lock(&wm8994->accdet_lock);
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
@@ -3625,8 +3691,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
WM1811_JACKDET_DB, 0);
delay = control->pdata.micdet_delay;
- schedule_delayed_work(&wm8994->mic_work,
- msecs_to_jiffies(delay));
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_work,
+ msecs_to_jiffies(delay));
} else {
dev_dbg(codec->dev, "Jack not detected\n");
@@ -3780,11 +3847,29 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
EXPORT_SYMBOL_GPL(wm8958_mic_detect);
+static void wm8958_mic_work(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ mic_complete_work.work);
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
+
+ pm_runtime_get_sync(codec->dev);
+
+ mutex_lock(&wm8994->accdet_lock);
+
+ wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status);
+
+ mutex_unlock(&wm8994->accdet_lock);
+
+ pm_runtime_put(codec->dev);
+}
+
static irqreturn_t wm8958_mic_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
struct snd_soc_codec *codec = wm8994->hubs.codec;
- int reg, count, ret;
+ int reg, count, ret, id_delay;
/*
* Jack detection may have detected a removal simulataneously
@@ -3794,6 +3879,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
+
pm_runtime_get_sync(codec->dev);
/* We may occasionally read a detection without an impedence
@@ -3846,8 +3934,13 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
goto out;
}
+ wm8994->mic_status = reg;
+ id_delay = wm8994->wm8994->pdata.mic_id_delay;
+
if (wm8994->mic_detecting)
- wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg);
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8994->mic_complete_work,
+ msecs_to_jiffies(id_delay));
else
wm8958_button_det(codec, reg);
@@ -3899,6 +3992,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
mutex_init(&wm8994->accdet_lock);
INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
wm1811_jackdet_bootstrap);
+ INIT_DELAYED_WORK(&wm8994->open_circuit_work,
+ wm8958_open_circuit_work);
switch (control->type) {
case WM8994:
@@ -3911,14 +4006,13 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
+ INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
+
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);
wm8994->micdet_irq = control->pdata.micdet_irq;
- pm_runtime_enable(codec->dev);
- pm_runtime_idle(codec->dev);
-
/* By default use idle_bias_off, will override for WM8994 */
codec->dapm.idle_bias_off = 1;
@@ -4291,8 +4385,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
- pm_runtime_disable(codec->dev);
-
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
&wm8994->fll_locked[i]);
@@ -4351,6 +4443,9 @@ static int wm8994_probe(struct platform_device *pdev)
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
wm8994_dai, ARRAY_SIZE(wm8994_dai));
}
@@ -4358,6 +4453,8 @@ static int wm8994_probe(struct platform_device *pdev)
static int wm8994_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 55ddf4d..6536f8d 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -134,6 +134,9 @@ struct wm8994_priv {
struct mutex accdet_lock;
struct wm8994_micdet micdet[2];
struct delayed_work mic_work;
+ struct delayed_work open_circuit_work;
+ struct delayed_work mic_complete_work;
+ u16 mic_status;
bool mic_detecting;
bool jack_mic;
int btn_mask;
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 90a65c4..da2899e6 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct snd_soc_codec *codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
- codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec);
return ret;
diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h
index 5642121..508ad27 100644
--- a/sound/soc/codecs/wm8995.h
+++ b/sound/soc/codecs/wm8995.h
@@ -4237,11 +4237,8 @@
#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */
#define WM8995_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 = wm8995_put_class_w, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) \
-}
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8995_put_class_w)
struct wm8995_reg_access {
u16 read;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
new file mode 100644
index 0000000..6ec3de3
--- /dev/null
+++ b/sound/soc/codecs/wm8997.c
@@ -0,0 +1,1175 @@
+/*
+ * wm8997.c -- WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm8997.h"
+
+struct wm8997_priv {
+ struct arizona_priv core;
+ struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+static const struct reg_default wm8997_sysclk_reva_patch[] = {
+ { 0x301D, 0x7B15 },
+ { 0x301B, 0x0050 },
+ { 0x305D, 0x7B17 },
+ { 0x305B, 0x0050 },
+ { 0x3001, 0x08FE },
+ { 0x3003, 0x00F4 },
+ { 0x3041, 0x08FF },
+ { 0x3043, 0x0005 },
+ { 0x3020, 0x0225 },
+ { 0x3021, 0x0A00 },
+ { 0x3022, 0xE24D },
+ { 0x3023, 0x0800 },
+ { 0x3024, 0xE24D },
+ { 0x3025, 0xF000 },
+ { 0x3060, 0x0226 },
+ { 0x3061, 0x0A00 },
+ { 0x3062, 0xE252 },
+ { 0x3063, 0x0800 },
+ { 0x3064, 0xE252 },
+ { 0x3065, 0xF000 },
+ { 0x3116, 0x022B },
+ { 0x3117, 0xFA00 },
+ { 0x3110, 0x246C },
+ { 0x3111, 0x0A03 },
+ { 0x3112, 0x246E },
+ { 0x3113, 0x0A03 },
+ { 0x3114, 0x2470 },
+ { 0x3115, 0x0A03 },
+ { 0x3126, 0x246C },
+ { 0x3127, 0x0A02 },
+ { 0x3128, 0x246E },
+ { 0x3129, 0x0A02 },
+ { 0x312A, 0x2470 },
+ { 0x312B, 0xFA02 },
+ { 0x3125, 0x0800 },
+};
+
+static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct regmap *regmap = codec->control_data;
+ const struct reg_default *patch = NULL;
+ int i, patch_size;
+
+ switch (arizona->rev) {
+ case 0:
+ patch = wm8997_sysclk_reva_patch;
+ patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch);
+ break;
+ default:
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (patch)
+ for (i = 0; i < patch_size; i++)
+ regmap_write(regmap, patch[i].reg,
+ patch[i].def);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char *wm8997_osr_text[] = {
+ "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm8997_osr_val[] = {
+ 0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm8997_hpout_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+ wm8997_osr_text, wm8997_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+ wm8997_osr_text, wm8997_osr_val),
+};
+
+#define WM8997_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new wm8997_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2_OSR_SHIFT, 1, 0),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+ ARIZONA_EQ1_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+ ARIZONA_EQ2_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+ ARIZONA_EQ3_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+ ARIZONA_EQ4_ENA_MASK),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+ 24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+ ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+ ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
+
+SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
+SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+ ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char *wm8997_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm8997_aec_loopback_values[] = {
+ 0, 1, 4, 6, 8, 9,
+};
+
+static const struct soc_enum wm8997_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+ ARRAY_SIZE(wm8997_aec_loopback_texts),
+ wm8997_aec_loopback_texts,
+ wm8997_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
+ SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+ 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+ 0, NULL, 0, arizona_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+ ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+ ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+ ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+ ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+ 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+ 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+ ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+ ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+ ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+ ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+ ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+ ARIZONA_SLIMTX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+ ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+ ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+ &wm8997_aec_loopback_mux),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name) \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
+ { name, "AEC", "AEC Loopback" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+ { name, "AIF1RX1", "AIF1RX1" }, \
+ { name, "AIF1RX2", "AIF1RX2" }, \
+ { name, "AIF1RX3", "AIF1RX3" }, \
+ { name, "AIF1RX4", "AIF1RX4" }, \
+ { name, "AIF1RX5", "AIF1RX5" }, \
+ { name, "AIF1RX6", "AIF1RX6" }, \
+ { name, "AIF1RX7", "AIF1RX7" }, \
+ { name, "AIF1RX8", "AIF1RX8" }, \
+ { name, "AIF2RX1", "AIF2RX1" }, \
+ { name, "AIF2RX2", "AIF2RX2" }, \
+ { name, "SLIMRX1", "SLIMRX1" }, \
+ { name, "SLIMRX2", "SLIMRX2" }, \
+ { name, "SLIMRX3", "SLIMRX3" }, \
+ { name, "SLIMRX4", "SLIMRX4" }, \
+ { name, "SLIMRX5", "SLIMRX5" }, \
+ { name, "SLIMRX6", "SLIMRX6" }, \
+ { name, "SLIMRX7", "SLIMRX7" }, \
+ { name, "SLIMRX8", "SLIMRX8" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
+ { "AIF2 Capture", NULL, "DBVDD2" },
+ { "AIF2 Playback", NULL, "DBVDD2" },
+
+ { "OUT1L", NULL, "CPVDD" },
+ { "OUT1R", NULL, "CPVDD" },
+ { "OUT3L", NULL, "CPVDD" },
+
+ { "OUT4L", NULL, "SPKVDD" },
+
+ { "OUT1L", NULL, "SYSCLK" },
+ { "OUT1R", NULL, "SYSCLK" },
+ { "OUT3L", NULL, "SYSCLK" },
+ { "OUT4L", NULL, "SYSCLK" },
+
+ { "IN1L", NULL, "SYSCLK" },
+ { "IN1R", NULL, "SYSCLK" },
+ { "IN2L", NULL, "SYSCLK" },
+ { "IN2R", NULL, "SYSCLK" },
+
+ { "MICBIAS1", NULL, "MICVDD" },
+ { "MICBIAS2", NULL, "MICVDD" },
+ { "MICBIAS3", NULL, "MICVDD" },
+
+ { "Noise Generator", NULL, "SYSCLK" },
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+
+ { "Noise Generator", NULL, "NOISE" },
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+
+ { "AIF1 Capture", NULL, "AIF1TX1" },
+ { "AIF1 Capture", NULL, "AIF1TX2" },
+ { "AIF1 Capture", NULL, "AIF1TX3" },
+ { "AIF1 Capture", NULL, "AIF1TX4" },
+ { "AIF1 Capture", NULL, "AIF1TX5" },
+ { "AIF1 Capture", NULL, "AIF1TX6" },
+ { "AIF1 Capture", NULL, "AIF1TX7" },
+ { "AIF1 Capture", NULL, "AIF1TX8" },
+
+ { "AIF1RX1", NULL, "AIF1 Playback" },
+ { "AIF1RX2", NULL, "AIF1 Playback" },
+ { "AIF1RX3", NULL, "AIF1 Playback" },
+ { "AIF1RX4", NULL, "AIF1 Playback" },
+ { "AIF1RX5", NULL, "AIF1 Playback" },
+ { "AIF1RX6", NULL, "AIF1 Playback" },
+ { "AIF1RX7", NULL, "AIF1 Playback" },
+ { "AIF1RX8", NULL, "AIF1 Playback" },
+
+ { "AIF2 Capture", NULL, "AIF2TX1" },
+ { "AIF2 Capture", NULL, "AIF2TX2" },
+
+ { "AIF2RX1", NULL, "AIF2 Playback" },
+ { "AIF2RX2", NULL, "AIF2 Playback" },
+
+ { "Slim1 Capture", NULL, "SLIMTX1" },
+ { "Slim1 Capture", NULL, "SLIMTX2" },
+ { "Slim1 Capture", NULL, "SLIMTX3" },
+ { "Slim1 Capture", NULL, "SLIMTX4" },
+
+ { "SLIMRX1", NULL, "Slim1 Playback" },
+ { "SLIMRX2", NULL, "Slim1 Playback" },
+ { "SLIMRX3", NULL, "Slim1 Playback" },
+ { "SLIMRX4", NULL, "Slim1 Playback" },
+
+ { "Slim2 Capture", NULL, "SLIMTX5" },
+ { "Slim2 Capture", NULL, "SLIMTX6" },
+
+ { "SLIMRX5", NULL, "Slim2 Playback" },
+ { "SLIMRX6", NULL, "Slim2 Playback" },
+
+ { "Slim3 Capture", NULL, "SLIMTX7" },
+ { "Slim3 Capture", NULL, "SLIMTX8" },
+
+ { "SLIMRX7", NULL, "Slim3 Playback" },
+ { "SLIMRX8", NULL, "Slim3 Playback" },
+
+ { "AIF1 Playback", NULL, "SYSCLK" },
+ { "AIF2 Playback", NULL, "SYSCLK" },
+ { "Slim1 Playback", NULL, "SYSCLK" },
+ { "Slim2 Playback", NULL, "SYSCLK" },
+ { "Slim3 Playback", NULL, "SYSCLK" },
+
+ { "AIF1 Capture", NULL, "SYSCLK" },
+ { "AIF2 Capture", NULL, "SYSCLK" },
+ { "Slim1 Capture", NULL, "SYSCLK" },
+ { "Slim2 Capture", NULL, "SYSCLK" },
+ { "Slim3 Capture", NULL, "SYSCLK" },
+
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+ ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+ ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+ ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+ ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+ ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+ ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+ ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+ ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+ ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+ ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+ ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+ ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+ ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+ ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+ ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+ ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+ ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+ ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+ ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+ ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+ ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+ ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+ ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+ ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+ ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+ ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+ ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+ ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+ ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+ ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+ ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+ ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+ ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+ ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+ ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+ ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
+ { "HPOUT1L", NULL, "OUT1L" },
+ { "HPOUT1R", NULL, "OUT1R" },
+
+ { "AEC Loopback", "EPOUT", "OUT3L" },
+ { "EPOUTN", NULL, "OUT3L" },
+ { "EPOUTP", NULL, "OUT3L" },
+
+ { "AEC Loopback", "SPKOUT", "OUT4L" },
+ { "SPKOUTN", NULL, "OUT4L" },
+ { "SPKOUTP", NULL, "OUT4L" },
+
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+ { "SPKDAT1L", NULL, "OUT5L" },
+ { "SPKDAT1R", NULL, "OUT5R" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+};
+
+static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fll_id) {
+ case WM8997_FLL1:
+ return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout);
+ case WM8997_FLL2:
+ return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout);
+ case WM8997_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref,
+ Fout);
+ case WM8997_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref,
+ Fout);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8997_dai[] = {
+ {
+ .name = "wm8997-aif1",
+ .id = 1,
+ .base = ARIZONA_AIF1_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "wm8997-aif2",
+ .id = 2,
+ .base = ARIZONA_AIF2_BCLK_CTRL,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "wm8997-slim1",
+ .id = 3,
+ .playback = {
+ .stream_name = "Slim1 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim2",
+ .id = 4,
+ .playback = {
+ .stream_name = "Slim2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+ {
+ .name = "wm8997-slim3",
+ .id = 5,
+ .playback = {
+ .stream_name = "Slim3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Slim3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8997_RATES,
+ .formats = WM8997_FORMATS,
+ },
+ .ops = &arizona_simple_dai_ops,
+ },
+};
+
+static int wm8997_codec_probe(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = priv->core.arizona->regmap;
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ arizona_init_spk(codec);
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm8997_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
+}
+
+#define WM8997_DIG_VU 0x0200
+
+static unsigned int wm8997_digital_vu[] = {
+ ARIZONA_DAC_DIGITAL_VOLUME_1L,
+ ARIZONA_DAC_DIGITAL_VOLUME_1R,
+ ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_4L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5L,
+ ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
+ .probe = wm8997_codec_probe,
+ .remove = wm8997_codec_remove,
+
+ .idle_bias_off = true,
+
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8997_set_fll,
+
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+ .dapm_routes = wm8997_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+};
+
+static int wm8997_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct wm8997_priv *wm8997;
+ int i;
+
+ wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
+ GFP_KERNEL);
+ if (wm8997 == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, wm8997);
+
+ wm8997->core.arizona = arizona;
+ wm8997->core.num_inputs = 4;
+
+ for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
+ wm8997->fll[i].vco_mult = 1;
+
+ arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+ &wm8997->fll[0]);
+ arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+ ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+ &wm8997->fll[1]);
+
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+ for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++)
+ arizona_init_dai(&wm8997->core, i);
+
+ /* Latch volume update bits */
+ for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++)
+ regmap_update_bits(arizona->regmap, wm8997_digital_vu[i],
+ WM8997_DIG_VU, WM8997_DIG_VU);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_idle(&pdev->dev);
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+ wm8997_dai, ARRAY_SIZE(wm8997_dai));
+}
+
+static int wm8997_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver wm8997_codec_driver = {
+ .driver = {
+ .name = "wm8997-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8997_probe,
+ .remove = wm8997_remove,
+};
+
+module_platform_driver(wm8997_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8997 driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8997-codec");
diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h
new file mode 100644
index 0000000..5e91c6a
--- /dev/null
+++ b/sound/soc/codecs/wm8997.h
@@ -0,0 +1,23 @@
+/*
+ * wm8997.h -- WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 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 _WM8997_H
+#define _WM8997_H
+
+#include "arizona.h"
+
+#define WM8997_FLL1 1
+#define WM8997_FLL2 2
+#define WM8997_FLL1_REFCLK 3
+#define WM8997_FLL2_REFCLK 4
+
+#endif
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 05b1f34..70ce6793 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -209,7 +209,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -225,7 +225,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
@@ -294,8 +294,8 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
static int wm9705_reset(struct snd_soc_codec *codec)
{
- if (soc_ac97_ops.reset) {
- soc_ac97_ops.reset(codec->ac97);
+ if (soc_ac97_ops->reset) {
+ soc_ac97_ops->reset(codec->ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
@@ -306,7 +306,7 @@ static int wm9705_reset(struct snd_soc_codec *codec)
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
- soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
+ soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
@@ -323,7 +323,7 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
}
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
return 0;
@@ -337,9 +337,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "WM9705 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 8e9a6a3..c5eb746 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -455,7 +455,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -472,7 +472,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
@@ -581,15 +581,15 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -624,7 +624,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
(i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -635,7 +635,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index f7afa68..a53e175 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -652,7 +652,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -668,7 +668,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
@@ -1095,15 +1095,15 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
@@ -1180,7 +1180,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -1197,7 +1197,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3470b64..b38f350 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -215,6 +216,29 @@ static struct {
[WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
+struct wm_coeff_ctl_ops {
+ int (*xget)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xput)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xinfo)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+};
+
+struct wm_coeff_ctl {
+ const char *name;
+ struct wm_adsp_alg_region region;
+ struct wm_coeff_ctl_ops ops;
+ struct wm_adsp *adsp;
+ void *private;
+ unsigned int enabled:1;
+ struct list_head list;
+ void *cache;
+ size_t len;
+ unsigned int set:1;
+ struct snd_kcontrol *kcontrol;
+};
+
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -279,7 +303,7 @@ static const struct soc_enum wm_adsp2_rate_enum[] = {
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
- SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
@@ -334,6 +358,163 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
}
}
+static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ return 0;
+}
+
+static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+ const void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(adsp->regmap, reg, scratch,
+ ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to write %zu bytes to %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(ctl->cache, p, ctl->len);
+
+ if (!ctl->enabled) {
+ ctl->set = 1;
+ return 0;
+ }
+
+ return wm_coeff_write_control(kcontrol, p, ctl->len);
+}
+
+static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+ void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to read %zu bytes from %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ memcpy(buf, scratch, ctl->len);
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(p, ctl->cache, ctl->len);
+ return 0;
+}
+
+struct wmfw_ctl_work {
+ struct wm_adsp *adsp;
+ struct wm_coeff_ctl *ctl;
+ struct work_struct work;
+};
+
+static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
+{
+ struct snd_kcontrol_new *kcontrol;
+ int ret;
+
+ if (!ctl || !ctl->name)
+ return -EINVAL;
+
+ kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+
+ kcontrol->name = ctl->name;
+ kcontrol->info = wm_coeff_info;
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
+ kcontrol->private_value = (unsigned long)ctl;
+
+ ret = snd_soc_add_card_controls(adsp->card,
+ kcontrol, 1);
+ if (ret < 0)
+ goto err_kcontrol;
+
+ kfree(kcontrol);
+
+ ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+ ctl->name);
+
+ list_add(&ctl->list, &adsp->ctl_list);
+ return 0;
+
+err_kcontrol:
+ kfree(kcontrol);
+ return ret;
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -547,6 +728,152 @@ out:
return ret;
}
+static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
+ if (!ctl->enabled || ctl->set)
+ continue;
+ ret = wm_coeff_read_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_adsp *adsp)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &adsp->ctl_list, list) {
+ if (!ctl->enabled)
+ continue;
+ if (ctl->set) {
+ ret = wm_coeff_write_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+ struct wmfw_ctl_work *ctl_work = container_of(work,
+ struct wmfw_ctl_work,
+ work);
+
+ wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
+ kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct wm_adsp *dsp,
+ const struct wm_adsp_alg_region *region)
+
+{
+ struct wm_coeff_ctl *ctl;
+ struct wmfw_ctl_work *ctl_work;
+ char *name;
+ char *region_name;
+ int ret;
+
+ name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_name;
+ }
+
+ snprintf(name, PAGE_SIZE, "DSP%d %s %x",
+ dsp->num, region_name, region->alg);
+
+ list_for_each_entry(ctl, &dsp->ctl_list,
+ list) {
+ if (!strcmp(ctl->name, name)) {
+ if (!ctl->enabled)
+ ctl->enabled = 1;
+ goto found;
+ }
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl) {
+ ret = -ENOMEM;
+ goto err_name;
+ }
+ ctl->region = *region;
+ ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+ if (!ctl->name) {
+ ret = -ENOMEM;
+ goto err_ctl;
+ }
+ ctl->enabled = 1;
+ ctl->set = 0;
+ ctl->ops.xget = wm_coeff_get;
+ ctl->ops.xput = wm_coeff_put;
+ ctl->adsp = dsp;
+
+ ctl->len = region->len;
+ ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ if (!ctl->cache) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+
+ ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+ if (!ctl_work) {
+ ret = -ENOMEM;
+ goto err_ctl_cache;
+ }
+
+ ctl_work->adsp = dsp;
+ ctl_work->ctl = ctl;
+ INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+ schedule_work(&ctl_work->work);
+
+found:
+ kfree(name);
+
+ return 0;
+
+err_ctl_cache:
+ kfree(ctl->cache);
+err_ctl_name:
+ kfree(ctl->name);
+err_ctl:
+ kfree(ctl);
+err_name:
+ kfree(name);
+ return ret;
+}
+
static int wm_adsp_setup_algs(struct wm_adsp *dsp)
{
struct regmap *regmap = dsp->regmap;
@@ -730,7 +1057,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].dm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
+ region->len -= be32_to_cpu(adsp1_alg[i].dm);
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -738,7 +1074,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp1_alg[i].zm);
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
break;
case WMFW_ADSP2:
@@ -758,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].xm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
+ region->len -= be32_to_cpu(adsp2_alg[i].xm);
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -766,7 +1120,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].ym);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
+ region->len -= be32_to_cpu(adsp2_alg[i].ym);
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -774,7 +1137,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp2_alg[i].zm);
+ wm_adsp_create_control(dsp, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
break;
}
}
@@ -986,9 +1358,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = w->codec;
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
+ struct wm_coeff_ctl *ctl;
int ret;
int val;
+ dsp->card = codec->card;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -1031,6 +1406,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp);
+ if (ret != 0)
+ goto err;
+
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START,
@@ -1047,6 +1432,9 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
+ ctl->enabled = 0;
break;
default:
@@ -1099,9 +1487,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
+ struct wm_coeff_ctl *ctl;
unsigned int val;
int ret;
+ dsp->card = codec->card;
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/*
@@ -1172,6 +1563,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp);
+ if (ret != 0)
+ goto err;
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -1209,6 +1610,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret);
}
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
+ ctl->enabled = 0;
+
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
@@ -1246,6 +1650,7 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
}
INIT_LIST_HEAD(&adsp->alg_regions);
+ INIT_LIST_HEAD(&adsp->ctl_list);
if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index fea5146..d018dea 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
unsigned int alg;
int type;
unsigned int base;
+ size_t len;
};
struct wm_adsp {
@@ -38,6 +39,7 @@ struct wm_adsp {
int type;
struct device *dev;
struct regmap *regmap;
+ struct snd_soc_card *card;
int base;
int sysclk_reg;
@@ -55,17 +57,17 @@ struct wm_adsp {
bool running;
struct regulator *dvfs;
+
+ struct list_head ctl_list;
};
#define WM_ADSP1(wname, num) \
- { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = num, .event = wm_adsp1_event, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
#define WM_ADSP2(wname, num) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = num, .event = wm_adsp2_event, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f5d81b9..8b50e59 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -693,17 +693,13 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
#define WM_HUBS_SINGLE_W(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 = class_w_put_volsw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, class_w_put_volsw)
static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -723,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
static int class_w_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct snd_soc_codec *codec = widget->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
int ret;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);