Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
January 2011
- 130 participants
- 347 discussions
17 Jan '11
Signed-off-by: Dimitris Papastamos <dp(a)opensource.wolfsonmicro.com>
---
sound/soc/codecs/wm8995.c | 102 ++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 97 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index f0f678d..7d56341 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -30,6 +31,18 @@
#include "wm8995.h"
+#define WM8995_NUM_SUPPLIES 8
+static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD1",
+ "DBVDD2",
+ "DBVDD3",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "MICVDD"
+};
+
static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
[0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b,
[24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0,
@@ -126,8 +139,37 @@ struct wm8995_priv {
int mclk[2];
int aifclk[2];
struct fll_config fll[2], fll_suspend[2];
+ struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
+ struct snd_soc_codec *codec;
};
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8995_REGULATOR_EVENT(n) \
+static int wm8995_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ wm8995->codec->cache_sync = 1; \
+ } \
+ return 0; \
+}
+
+WM8995_REGULATOR_EVENT(0)
+WM8995_REGULATOR_EVENT(1)
+WM8995_REGULATOR_EVENT(2)
+WM8995_REGULATOR_EVENT(3)
+WM8995_REGULATOR_EVENT(4)
+WM8995_REGULATOR_EVENT(5)
+WM8995_REGULATOR_EVENT(6)
+WM8995_REGULATOR_EVENT(7)
+
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ if (ret)
+ return ret;
+
ret = snd_soc_cache_sync(codec);
if (ret) {
dev_err(codec->dev,
@@ -1492,13 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, WM8995_BG_ENA);
-
}
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, 0);
- codec->cache_sync = 1;
+ regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
break;
}
@@ -1537,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
static int wm8995_probe(struct snd_soc_codec *codec)
{
struct wm8995_priv *wm8995;
+ int i;
int ret;
codec->dapm.idle_bias_off = 1;
wm8995 = snd_soc_codec_get_drvdata(codec);
+ wm8995->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
if (ret < 0) {
@@ -1548,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
return ret;
}
+ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
+ wm8995->supplies[i].supply = wm8995_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
+ wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
+ wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
+ wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
+ wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
+ wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
+ wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
+ wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
+ ret = regulator_register_notifier(wm8995->supplies[i].consumer,
+ &wm8995->disable_nb[i]);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+ wm8995->supplies);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_reg_get;
+ }
+
ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
if (ret < 0) {
dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
- return ret;
+ goto err_reg_enable;
}
if (ret != 0x8995) {
dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
- return -EINVAL;
+ goto err_reg_enable;
}
ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
- return ret;
+ goto err_reg_enable;
}
wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1597,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(wm8995_intercon));
return 0;
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+err_reg_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+ return ret;
}
#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
--
1.7.3.5
3
2
17 Jan '11
The WM8991 is a highly integrated ultra-low power hi-fi CODEC designed for
handsets rich in multimedia features such as GPS, mobile TV, digital audio
playback and gaming.
This driver was originally written by Graeme Gregory and has been maintained
out of tree by Mark Brown and Dimitris Papastamos.
Signed-off-by: Graeme Gregory <gg(a)opensource.wolfsonmicro.com>
Signed-off-by: Dimitris Papastamos <dp(a)opensource.wolfsonmicro.com>
---
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/wm8991.c | 1427 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/wm8991.h | 833 ++++++++++++++++++++++++++
4 files changed, 2266 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/codecs/wm8991.c
create mode 100644 sound/soc/codecs/wm8991.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 61e36ef..a18cff4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -77,6 +77,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
+ select SND_SOC_WM8991 if I2C
select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
@@ -308,6 +309,9 @@ config SND_SOC_WM8988
config SND_SOC_WM8990
tristate
+config SND_SOC_WM8991
+ tristate
+
config SND_SOC_WM8993
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 333910a..68e76af 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -62,6 +62,7 @@ snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
snd-soc-wm8995-objs := wm8995.o
@@ -143,6 +144,7 @@ obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
+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
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
new file mode 100644
index 0000000..28fdfd6
--- /dev/null
+++ b/sound/soc/codecs/wm8991.c
@@ -0,0 +1,1427 @@
+/*
+ * wm8991.c -- WM8991 ALSA Soc Audio driver
+ *
+ * Copyright 2007-2010 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * linux(a)wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8991.h"
+
+struct wm8991_priv {
+ enum snd_soc_control_type control_type;
+ unsigned int pcmclk;
+};
+
+static const u16 wm8991_reg_defs[] = {
+ 0x8991, /* R0 - Reset */
+ 0x0000, /* R1 - Power Management (1) */
+ 0x6000, /* R2 - Power Management (2) */
+ 0x0000, /* R3 - Power Management (3) */
+ 0x4050, /* R4 - Audio Interface (1) */
+ 0x4000, /* R5 - Audio Interface (2) */
+ 0x01C8, /* R6 - Clocking (1) */
+ 0x0000, /* R7 - Clocking (2) */
+ 0x0040, /* R8 - Audio Interface (3) */
+ 0x0040, /* R9 - Audio Interface (4) */
+ 0x0004, /* R10 - DAC CTRL */
+ 0x00C0, /* R11 - Left DAC Digital Volume */
+ 0x00C0, /* R12 - Right DAC Digital Volume */
+ 0x0000, /* R13 - Digital Side Tone */
+ 0x0100, /* R14 - ADC CTRL */
+ 0x00C0, /* R15 - Left ADC Digital Volume */
+ 0x00C0, /* R16 - Right ADC Digital Volume */
+ 0x0000, /* R17 */
+ 0x0000, /* R18 - GPIO CTRL 1 */
+ 0x1000, /* R19 - GPIO1 & GPIO2 */
+ 0x1010, /* R20 - GPIO3 & GPIO4 */
+ 0x1010, /* R21 - GPIO5 & GPIO6 */
+ 0x8000, /* R22 - GPIOCTRL 2 */
+ 0x0800, /* R23 - GPIO_POL */
+ 0x008B, /* R24 - Left Line Input 1&2 Volume */
+ 0x008B, /* R25 - Left Line Input 3&4 Volume */
+ 0x008B, /* R26 - Right Line Input 1&2 Volume */
+ 0x008B, /* R27 - Right Line Input 3&4 Volume */
+ 0x0000, /* R28 - Left Output Volume */
+ 0x0000, /* R29 - Right Output Volume */
+ 0x0066, /* R30 - Line Outputs Volume */
+ 0x0022, /* R31 - Out3/4 Volume */
+ 0x0079, /* R32 - Left OPGA Volume */
+ 0x0079, /* R33 - Right OPGA Volume */
+ 0x0003, /* R34 - Speaker Volume */
+ 0x0003, /* R35 - ClassD1 */
+ 0x0000, /* R36 */
+ 0x0100, /* R37 - ClassD3 */
+ 0x0000, /* R38 */
+ 0x0000, /* R39 - Input Mixer1 */
+ 0x0000, /* R40 - Input Mixer2 */
+ 0x0000, /* R41 - Input Mixer3 */
+ 0x0000, /* R42 - Input Mixer4 */
+ 0x0000, /* R43 - Input Mixer5 */
+ 0x0000, /* R44 - Input Mixer6 */
+ 0x0000, /* R45 - Output Mixer1 */
+ 0x0000, /* R46 - Output Mixer2 */
+ 0x0000, /* R47 - Output Mixer3 */
+ 0x0000, /* R48 - Output Mixer4 */
+ 0x0000, /* R49 - Output Mixer5 */
+ 0x0000, /* R50 - Output Mixer6 */
+ 0x0180, /* R51 - Out3/4 Mixer */
+ 0x0000, /* R52 - Line Mixer1 */
+ 0x0000, /* R53 - Line Mixer2 */
+ 0x0000, /* R54 - Speaker Mixer */
+ 0x0000, /* R55 - Additional Control */
+ 0x0000, /* R56 - AntiPOP1 */
+ 0x0000, /* R57 - AntiPOP2 */
+ 0x0000, /* R58 - MICBIAS */
+ 0x0000, /* R59 */
+ 0x0008, /* R60 - PLL1 */
+ 0x0031, /* R61 - PLL2 */
+ 0x0026, /* R62 - PLL3 */
+};
+
+#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0)
+
+static const unsigned int rec_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
+};
+
+static const unsigned int in_pga_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
+};
+
+static const unsigned int out_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
+};
+
+static const unsigned int out_pga_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
+};
+
+static const unsigned int out_omix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
+};
+
+static const unsigned int out_dac_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
+};
+
+static const unsigned int in_adc_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
+};
+
+static const unsigned int out_sidetone_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
+};
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg = kcontrol->private_value & 0xff;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+static const char *wm8991_digital_sidetone[] =
+{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8991_left_digital_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADC_TO_DACL_SHIFT,
+ WM8991_ADC_TO_DACL_MASK,
+ wm8991_digital_sidetone);
+
+static const struct soc_enum wm8991_right_digital_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADC_TO_DACR_SHIFT,
+ WM8991_ADC_TO_DACR_MASK,
+ wm8991_digital_sidetone);
+
+static const char *wm8991_adcmode[] =
+{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8991_right_adcmode_enum =
+ SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
+ WM8991_ADC_HPF_CUT_SHIFT,
+ WM8991_ADC_HPF_CUT_MASK,
+ wm8991_adcmode);
+
+static const struct snd_kcontrol_new wm8991_snd_controls[] = {
+ /* INMIXL */
+ SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0),
+ SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0),
+ /* INMIXR */
+ SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0),
+ SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0),
+
+ /* LOMIX */
+ SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+ WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+ WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+ /* ROMIX */
+ SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+ WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
+ SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+ WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),
+
+ /* LOUT */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
+ WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0),
+
+ /* ROUT */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
+ WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0),
+
+ /* LOPGA */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
+ WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
+ WM8991_LOPGAZC_BIT, 1, 0),
+
+ /* ROPGA */
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
+ WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
+ SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
+ WM8991_ROPGAZC_BIT, 1, 0),
+
+ SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LONMUTE_BIT, 1, 0),
+ SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LOPMUTE_BIT, 1, 0),
+ SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_LOATTN_BIT, 1, 0),
+ SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_RONMUTE_BIT, 1, 0),
+ SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_ROPMUTE_BIT, 1, 0),
+ SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+ WM8991_ROATTN_BIT, 1, 0),
+
+ SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT3MUTE_BIT, 1, 0),
+ SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT3ATTN_BIT, 1, 0),
+
+ SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT4MUTE_BIT, 1, 0),
+ SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+ WM8991_OUT4ATTN_BIT, 1, 0),
+
+ SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
+ WM8991_CDMODE_BIT, 1, 0),
+
+ SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
+ WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
+ SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
+ WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
+ SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
+ WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8991_LEFT_DAC_DIGITAL_VOLUME,
+ WM8991_DACL_VOL_SHIFT,
+ WM8991_DACL_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8991_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8991_DACR_VOL_SHIFT,
+ WM8991_DACR_VOL_MASK,
+ 0,
+ out_dac_tlv),
+
+ SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
+ SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),
+
+ SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+ SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+ WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
+ out_sidetone_tlv),
+
+ SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
+ WM8991_ADC_HPF_ENA_BIT, 1, 0),
+
+ SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8991_LEFT_ADC_DIGITAL_VOLUME,
+ WM8991_ADCL_VOL_SHIFT,
+ WM8991_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8991_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8991_ADCR_VOL_SHIFT,
+ WM8991_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LIN12VOL_SHIFT,
+ WM8991_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LI12ZC_BIT, 1, 0),
+
+ SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8991_LI12MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LIN34VOL_SHIFT,
+ WM8991_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LI34ZC_BIT, 1, 0),
+
+ SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8991_LI34MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RIN12VOL_SHIFT,
+ WM8991_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RI12ZC_BIT, 1, 0),
+
+ SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8991_RI12MUTE_BIT, 1, 0),
+
+ SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RIN34VOL_SHIFT,
+ WM8991_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+ SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RI34ZC_BIT, 1, 0),
+
+ SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8991_RI34MUTE_BIT, 1, 0),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u16 reg, fakepower;
+
+ reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2);
+ fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS);
+
+ if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) |
+ (1 << WM8991_AINLMUX_PWR_BIT)))
+ reg |= WM8991_AINL_ENA;
+ else
+ reg &= ~WM8991_AINL_ENA;
+
+ if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) |
+ (1 << WM8991_AINRMUX_PWR_BIT)))
+ reg |= WM8991_AINR_ENA;
+ else
+ reg &= ~WM8991_AINL_ENA;
+
+ snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg);
+ return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u32 reg_shift = kcontrol->private_value & 0xfff;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
+ reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1);
+ if (reg & WM8991_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
+ reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2);
+ if (reg & WM8991_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
+ reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+ if (reg & WM8991_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+
+ case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
+ reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+ if (reg & WM8991_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
+ SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
+ SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
+ SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
+ SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
+ WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+ 1, 0),
+ SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
+ WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+ 1, 0),
+ SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8991_ainlmux[] =
+{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8991_ainlmux_enum =
+ SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+ ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
+ SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8991_ainrmux[] =
+{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8991_ainrmux_enum =
+ SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+ ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
+ SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = {
+ SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT,
+ WM8991_LR4BVOL_MASK, 0, in_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT,
+ WM8991_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
+ SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LRBLO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LLBLO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LRI3LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LLI3LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LR12LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LL12LO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
+ WM8991_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
+ SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RLBRO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RRBRO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RLI3RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RRI3RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RL12RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RR12RO_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
+ WM8991_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
+ SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LLOPGALON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LROPGALON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
+ WM8991_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
+ SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
+ WM8991_LR12LOP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
+ WM8991_LL12LOP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+ WM8991_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
+ SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RROPGARON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RLOPGARON_BIT, 1, 0),
+ SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
+ WM8991_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
+ SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
+ WM8991_RL12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
+ WM8991_RR12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+ WM8991_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
+ SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
+ WM8991_LI4O3_BIT, 1, 0),
+ SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
+ WM8991_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
+ SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
+ WM8991_RPGAO4_BIT, 1, 0),
+ SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
+ WM8991_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
+ SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LI2SPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LB2SPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LOPGASPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
+ WM8991_LDSPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RDSPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+ WM8991_ROPGASPK_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RL12ROP_BIT, 1, 0),
+ SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+ WM8991_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
+ /* Input Side */
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+ SND_SOC_DAPM_INPUT("LIN3"),
+ SND_SOC_DAPM_INPUT("LIN4RXN"),
+ SND_SOC_DAPM_INPUT("RIN3"),
+ SND_SOC_DAPM_INPUT("RIN4RXP"),
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+ SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+ /* DACs */
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
+ WM8991_ADCL_ENA_BIT, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2,
+ WM8991_ADCR_ENA_BIT, 0),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
+ 0, &wm8991_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
+ SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
+ 0, &wm8991_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
+ SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
+ 0, &wm8991_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
+ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
+ 0, &wm8991_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
+
+ /* INMIXL */
+ SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0,
+ &wm8991_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8991_dapm_inmixl_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* AINLMUX */
+ SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0,
+ &wm8991_dapm_ainlmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* INMIXR */
+ SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0,
+ &wm8991_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8991_dapm_inmixr_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* AINRMUX */
+ SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0,
+ &wm8991_dapm_ainrmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3,
+ WM8991_DACL_ENA_BIT, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3,
+ WM8991_DACR_ENA_BIT, 0),
+
+ /* LOMIX */
+ SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
+ 0, &wm8991_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+ /* LONMIX */
+ SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
+ &wm8991_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lonmix_controls)),
+
+ /* LOPMIX */
+ SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
+ &wm8991_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_lopmix_controls)),
+
+ /* OUT3MIX */
+ SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
+ &wm8991_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_out3mix_controls)),
+
+ /* SPKMIX */
+ SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
+ &wm8991_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+ /* OUT4MIX */
+ SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
+ &wm8991_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_out4mix_controls)),
+
+ /* ROPMIX */
+ SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
+ &wm8991_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_ropmix_controls)),
+
+ /* RONMIX */
+ SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
+ &wm8991_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_ronmix_controls)),
+
+ /* ROMIX */
+ SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
+ 0, &wm8991_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8991_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+ /* LOUT PGA */
+ SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
+ NULL, 0),
+
+ /* ROUT PGA */
+ SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
+ NULL, 0),
+
+ /* LOPGA */
+ SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
+ NULL, 0),
+
+ /* ROPGA */
+ SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
+ NULL, 0),
+
+ /* MICBIAS */
+ SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1,
+ WM8991_MICBIAS_ENA_BIT, 0),
+
+ SND_SOC_DAPM_OUTPUT("LON"),
+ SND_SOC_DAPM_OUTPUT("LOP"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKN"),
+ SND_SOC_DAPM_OUTPUT("SPKP"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+ SND_SOC_DAPM_OUTPUT("ROP"),
+ SND_SOC_DAPM_OUTPUT("RON"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+
+ SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AINLMUX */
+ {"AINLMUX", "INMIXL Mix", "INMIXL"},
+ {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+ {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+ {"AINLMUX", "RXVOICE Mix", "LIN4RXN"},
+ {"AINLMUX", "RXVOICE Mix", "RIN4RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AINLMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AINRMUX */
+ {"AINRMUX", "INMIXR Mix", "INMIXR"},
+ {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+ {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+ {"AINRMUX", "RXVOICE Mix", "LIN4RXN"},
+ {"AINRMUX", "RXVOICE Mix", "RIN4RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AINRMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+ u32 div2;
+ u32 n;
+ u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+ unsigned int source)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div->div2 = 1;
+ Ndiv = target / source;
+ } else
+ pll_div->div2 = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8991 N value outwith recommended range! N = %d\n", Ndiv);
+
+ pll_div->n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div->k = K;
+}
+
+static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
+{
+ u16 reg;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct _pll_div pll_div;
+
+ if (freq_in && freq_out) {
+ pll_factors(&pll_div, freq_out * 4, freq_in);
+
+ /* Turn on PLL */
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg |= WM8991_PLL_ENA;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+
+ /* sysclk comes from PLL */
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2);
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
+
+ /* set up N , fractional mode and pre-divisor if neccessary */
+ snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+ (pll_div.div2 ? WM8991_PRESCALE : 0));
+ snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
+ } else {
+ /* Turn on PLL */
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ reg &= ~WM8991_PLL_ENA;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+ }
+ return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8991_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8991_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8991_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8991_AIF_TMF_I2S;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8991_AIF_TMF_RIGHTJ;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8991_AIF_TMF_LEFTJ;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8991_AIF_TMF_DSP;
+ audio1 &= ~WM8991_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8991_MCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_MCLK_DIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_DACCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_DAC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_ADCCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+ ~WM8991_ADC_CLKDIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+ break;
+ case WM8991_BCLK_DIV:
+ reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+ ~WM8991_BCLK_DIV_MASK;
+ snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8991_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;
+ u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8991_AIF_WL_MASK;
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ audio1 |= WM8991_AIF_WL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ audio1 |= WM8991_AIF_WL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio1 |= WM8991_AIF_WL_32BITS;
+ break;
+ }
+
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val;
+
+ val = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+ if (mute)
+ snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+ else
+ snd_soc_write(codec, WM8991_DAC_CTRL, val);
+ return 0;
+}
+
+static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 val;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ ~WM8991_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ snd_soc_cache_sync(codec);
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+ WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+ WM8991_DIS_ROUT);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_VMIDTOG);
+
+ /* Delay to allow output caps to discharge */
+ msleep(300);
+
+ /* Disable VMIDTOG */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL);
+
+ /* disable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+
+ /* Enable outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+
+ msleep(50);
+
+ /* Enable VMID at 2x50k */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+
+ msleep(100);
+
+ /* Enable VREF */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+ msleep(600);
+
+ /* Enable BUFIOEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_BUFIOEN);
+
+ /* Disable outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+ }
+
+ /* VMID=2*250k */
+ val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+ ~WM8991_VMID_MODE_MASK;
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_POBCTRL | WM8991_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+ WM8991_BUFDCOPEN | WM8991_POBCTRL |
+ WM8991_BUFIOEN);
+
+ /* mute DAC */
+ val = snd_soc_read(codec, WM8991_DAC_CTRL);
+ snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+ /* Disable VMID */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+
+ msleep(300);
+
+ /* Enable all output discharge bits */
+ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+ WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+ WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+ WM8991_DIS_ROUT);
+
+ /* Disable VREF */
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+ codec->cache_sync = 1;
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8991_resume(struct snd_soc_codec *codec)
+{
+ wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+/* power down chip */
+static int wm8991_remove(struct snd_soc_codec *codec)
+{
+ wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8991_probe(struct snd_soc_codec *codec)
+{
+ struct wm8991_priv *wm8991;
+ int ret;
+ unsigned int reg;
+
+ wm8991 = snd_soc_codec_get_drvdata(codec);
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm8991_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4);
+ snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1);
+
+ reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) &
+ ~WM8991_GPIO1_SEL_MASK;
+ snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1);
+
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1);
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA|
+ WM8991_VMID_MODE_MASK);
+
+ reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+ snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA);
+
+ snd_soc_write(codec, WM8991_DAC_CTRL, 0);
+ snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ snd_soc_add_controls(codec, wm8991_snd_controls,
+ ARRAY_SIZE(wm8991_snd_controls));
+
+ snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets,
+ ARRAY_SIZE(wm8991_dapm_widgets));
+ snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+ ARRAY_SIZE(audio_map));
+ return 0;
+}
+
+#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8991_ops = {
+ .hw_params = wm8991_hw_params,
+ .digital_mute = wm8991_mute,
+ .set_fmt = wm8991_set_dai_fmt,
+ .set_clkdiv = wm8991_set_dai_clkdiv,
+ .set_pll = wm8991_set_dai_pll
+};
+
+/*
+ * The WM8991 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8991_dai = {
+ /* ADC/DAC on primary */
+ .name = "wm8991",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8991_FORMATS
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = WM8991_FORMATS
+ },
+ .ops = &wm8991_ops
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+ .probe = wm8991_probe,
+ .remove = wm8991_remove,
+ .suspend = wm8991_suspend,
+ .resume = wm8991_resume,
+ .set_bias_level = wm8991_set_bias_level,
+ .reg_cache_size = WM8991_MAX_REGISTER + 1,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8991_reg_defs
+};
+
+static __devinit int wm8991_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8991_priv *wm8991;
+ int ret;
+
+ wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL);
+ if (!wm8991)
+ return -ENOMEM;
+
+ wm8991->control_type = SND_SOC_I2C;
+ i2c_set_clientdata(i2c, wm8991);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8991, &wm8991_dai, 1);
+ if (ret < 0)
+ kfree(wm8991);
+ return ret;
+}
+
+static __devexit int wm8991_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id wm8991_i2c_id[] = {
+ { "wm8991", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
+
+static struct i2c_driver wm8991_i2c_driver = {
+ .driver = {
+ .name = "wm8991",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8991_i2c_probe,
+ .remove = __devexit_p(wm8991_i2c_remove),
+ .id_table = wm8991_i2c_id,
+};
+
+static int __init wm8991_modinit(void)
+{
+ int ret;
+ ret = i2c_add_driver(&wm8991_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n",
+ ret);
+ }
+ return 0;
+}
+module_init(wm8991_modinit);
+
+static void __exit wm8991_exit(void)
+{
+ i2c_del_driver(&wm8991_i2c_driver);
+}
+module_exit(wm8991_exit);
+
+MODULE_DESCRIPTION("ASoC WM8991 driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
new file mode 100644
index 0000000..8a942ef
--- /dev/null
+++ b/sound/soc/codecs/wm8991.h
@@ -0,0 +1,833 @@
+/*
+ * wm8991.h -- audio driver for WM8991
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory(a)wolfsonmicro.com or linux(a)wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8991_H
+#define _WM8991_H
+
+/*
+ * Register values.
+ */
+#define WM8991_RESET 0x00
+#define WM8991_POWER_MANAGEMENT_1 0x01
+#define WM8991_POWER_MANAGEMENT_2 0x02
+#define WM8991_POWER_MANAGEMENT_3 0x03
+#define WM8991_AUDIO_INTERFACE_1 0x04
+#define WM8991_AUDIO_INTERFACE_2 0x05
+#define WM8991_CLOCKING_1 0x06
+#define WM8991_CLOCKING_2 0x07
+#define WM8991_AUDIO_INTERFACE_3 0x08
+#define WM8991_AUDIO_INTERFACE_4 0x09
+#define WM8991_DAC_CTRL 0x0A
+#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8991_DIGITAL_SIDE_TONE 0x0D
+#define WM8991_ADC_CTRL 0x0E
+#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8991_GPIO_CTRL_1 0x12
+#define WM8991_GPIO1_GPIO2 0x13
+#define WM8991_GPIO3_GPIO4 0x14
+#define WM8991_GPIO5_GPIO6 0x15
+#define WM8991_GPIOCTRL_2 0x16
+#define WM8991_GPIO_POL 0x17
+#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8991_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8991_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8991_OUT3_4_VOLUME 0x1F
+#define WM8991_LEFT_OPGA_VOLUME 0x20
+#define WM8991_RIGHT_OPGA_VOLUME 0x21
+#define WM8991_SPEAKER_VOLUME 0x22
+#define WM8991_CLASSD1 0x23
+#define WM8991_CLASSD3 0x25
+#define WM8991_INPUT_MIXER1 0x27
+#define WM8991_INPUT_MIXER2 0x28
+#define WM8991_INPUT_MIXER3 0x29
+#define WM8991_INPUT_MIXER4 0x2A
+#define WM8991_INPUT_MIXER5 0x2B
+#define WM8991_INPUT_MIXER6 0x2C
+#define WM8991_OUTPUT_MIXER1 0x2D
+#define WM8991_OUTPUT_MIXER2 0x2E
+#define WM8991_OUTPUT_MIXER3 0x2F
+#define WM8991_OUTPUT_MIXER4 0x30
+#define WM8991_OUTPUT_MIXER5 0x31
+#define WM8991_OUTPUT_MIXER6 0x32
+#define WM8991_OUT3_4_MIXER 0x33
+#define WM8991_LINE_MIXER1 0x34
+#define WM8991_LINE_MIXER2 0x35
+#define WM8991_SPEAKER_MIXER 0x36
+#define WM8991_ADDITIONAL_CONTROL 0x37
+#define WM8991_ANTIPOP1 0x38
+#define WM8991_ANTIPOP2 0x39
+#define WM8991_MICBIAS 0x3A
+#define WM8991_PLL1 0x3C
+#define WM8991_PLL2 0x3D
+#define WM8991_PLL3 0x3E
+#define WM8991_INTDRIVBITS 0x3F
+
+#define WM8991_REGISTER_COUNT 60
+#define WM8991_MAX_REGISTER 0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */
+#define WM8991_SPK_ENA_BIT 12
+#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */
+#define WM8991_OUT3_ENA_BIT 11
+#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */
+#define WM8991_OUT4_ENA_BIT 10
+#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */
+#define WM8991_LOUT_ENA_BIT 9
+#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */
+#define WM8991_ROUT_ENA_BIT 8
+#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
+#define WM8991_MICBIAS_ENA_BIT 4
+#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
+#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */
+#define WM8991_VREF_ENA_BIT 0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */
+#define WM8991_PLL_ENA_BIT 15
+#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8991_TSHUT_ENA_BIT 14
+#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8991_TSHUT_OPDIS_BIT 13
+#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8991_OPCLK_ENA_BIT 11
+#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */
+#define WM8991_AINL_ENA_BIT 9
+#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */
+#define WM8991_AINR_ENA_BIT 8
+#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */
+#define WM8991_LIN34_ENA_BIT 7
+#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */
+#define WM8991_LIN12_ENA_BIT 6
+#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */
+#define WM8991_RIN34_ENA_BIT 5
+#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */
+#define WM8991_RIN12_ENA_BIT 4
+#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8991_ADCL_ENA_BIT 1
+#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8991_ADCR_ENA_BIT 0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8991_LON_ENA 0x2000 /* LON_ENA */
+#define WM8991_LON_ENA_BIT 13
+#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */
+#define WM8991_LOP_ENA_BIT 12
+#define WM8991_RON_ENA 0x0800 /* RON_ENA */
+#define WM8991_RON_ENA_BIT 11
+#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */
+#define WM8991_ROP_ENA_BIT 10
+#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */
+#define WM8991_LOPGA_ENA_BIT 7
+#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */
+#define WM8991_ROPGA_ENA_BIT 6
+#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */
+#define WM8991_LOMIX_ENA_BIT 5
+#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */
+#define WM8991_ROMIX_ENA_BIT 4
+#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8991_DACL_ENA_BIT 1
+#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8991_DACR_ENA_BIT 0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8991_AIF_WL_16BITS (0 << 5)
+#define WM8991_AIF_WL_20BITS (1 << 5)
+#define WM8991_AIF_WL_24BITS (2 << 5)
+#define WM8991_AIF_WL_32BITS (3 << 5)
+#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8991_AIF_TMF_RIGHTJ (0 << 3)
+#define WM8991_AIF_TMF_LEFTJ (1 << 3)
+#define WM8991_AIF_TMF_I2S (2 << 3)
+#define WM8991_AIF_TMF_DSP (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */
+#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */
+#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */
+#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
+#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8991_BCLK_DIV_1 (0x0 << 1)
+#define WM8991_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8991_BCLK_DIV_2 (0x2 << 1)
+#define WM8991_BCLK_DIV_3 (0x3 << 1)
+#define WM8991_BCLK_DIV_4 (0x4 << 1)
+#define WM8991_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8991_BCLK_DIV_6 (0x6 << 1)
+#define WM8991_BCLK_DIV_8 (0x7 << 1)
+#define WM8991_BCLK_DIV_11 (0x8 << 1)
+#define WM8991_BCLK_DIV_12 (0x9 << 1)
+#define WM8991_BCLK_DIV_16 (0xA << 1)
+#define WM8991_BCLK_DIV_22 (0xB << 1)
+#define WM8991_BCLK_DIV_24 (0xC << 1)
+#define WM8991_BCLK_DIV_32 (0xD << 1)
+#define WM8991_BCLK_DIV_44 (0xE << 1)
+#define WM8991_BCLK_DIV_48 (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */
+#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
+#define WM8991_MCLK_DIV_1 (0 << 11)
+#define WM8991_MCLK_DIV_2 ( 2 << 11)
+#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */
+#define WM8991_ADC_CLKDIV_1 (0 << 5)
+#define WM8991_ADC_CLKDIV_1_5 (1 << 5)
+#define WM8991_ADC_CLKDIV_2 (2 << 5)
+#define WM8991_ADC_CLKDIV_3 (3 << 5)
+#define WM8991_ADC_CLKDIV_4 (4 << 5)
+#define WM8991_ADC_CLKDIV_5_5 (5 << 5)
+#define WM8991_ADC_CLKDIV_6 (6 << 5)
+#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
+#define WM8991_DAC_CLKDIV_1 (0 << 2)
+#define WM8991_DAC_CLKDIV_1_5 (1 << 2)
+#define WM8991_DAC_CLKDIV_2 (2 << 2)
+#define WM8991_DAC_CLKDIV_3 (3 << 2)
+#define WM8991_DAC_CLKDIV_4 (4 << 2)
+#define WM8991_DAC_CLKDIV_5_5 (5 << 2)
+#define WM8991_DAC_CLKDIV_6 (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
+#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */
+#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
+#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
+#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
+#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */
+#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
+#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
+#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
+#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8991_DAC_VU 0x0100 /* DAC_VU */
+#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8991_DACL_VOL_SHIFT 0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8991_DAC_VU 0x0100 /* DAC_VU */
+#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8991_DACR_VOL_SHIFT 0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */
+#define WM8991_ADCL_DAC_SVOL_SHIFT 9
+#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */
+#define WM8991_ADCR_DAC_SVOL_SHIFT 5
+#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
+#define WM8991_ADC_TO_DACL_SHIFT 2
+#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
+#define WM8991_ADC_TO_DACR_SHIFT 0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
+#define WM8991_ADC_HPF_ENA_BIT 8
+#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
+#define WM8991_ADC_HPF_CUT_SHIFT 5
+#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8991_ADCL_DATINV_BIT 1
+#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8991_ADCR_DATINV_BIT 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8991_ADC_VU 0x0100 /* ADC_VU */
+#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8991_ADCL_VOL_SHIFT 0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8991_ADC_VU 0x0100 /* ADC_VU */
+#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8991_ADCR_VOL_SHIFT 0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8991_IRQ 0x1000 /* IRQ */
+#define WM8991_TEMPOK 0x0800 /* TEMPOK */
+#define WM8991_MICSHRT 0x0400 /* MICSHRT */
+#define WM8991_MICDET 0x0200 /* MICDET */
+#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */
+#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */
+#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */
+#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
+#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
+#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
+#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
+#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
+#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
+#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
+#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */
+#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */
+#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
+#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
+#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
+#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
+#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
+#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */
+#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */
+#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
+#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
+#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
+#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */
+#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */
+#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
+#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
+#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */
+#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */
+#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
+#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
+#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
+#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */
+#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */
+#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */
+#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */
+#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
+#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
+#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
+#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
+#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
+#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
+#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
+#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
+#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */
+#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */
+#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */
+#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
+#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */
+#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */
+#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */
+#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */
+#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */
+#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */
+#define WM8991_LI12MUTE_BIT 7
+#define WM8991_LI12ZC 0x0040 /* LI12ZC */
+#define WM8991_LI12ZC_BIT 6
+#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
+#define WM8991_LIN12VOL_SHIFT 0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */
+#define WM8991_LI34MUTE_BIT 7
+#define WM8991_LI34ZC 0x0040 /* LI34ZC */
+#define WM8991_LI34ZC_BIT 6
+#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
+#define WM8991_LIN34VOL_SHIFT 0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */
+#define WM8991_RI12MUTE_BIT 7
+#define WM8991_RI12ZC 0x0040 /* RI12ZC */
+#define WM8991_RI12ZC_BIT 6
+#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
+#define WM8991_RIN12VOL_SHIFT 0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8991_IPVU 0x0100 /* IPVU */
+#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */
+#define WM8991_RI34MUTE_BIT 7
+#define WM8991_RI34ZC 0x0040 /* RI34ZC */
+#define WM8991_RI34ZC_BIT 6
+#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
+#define WM8991_RIN34VOL_SHIFT 0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_LOZC 0x0080 /* LOZC */
+#define WM8991_LOZC_BIT 7
+#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
+#define WM8991_LOUTVOL_SHIFT 0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_ROZC 0x0080 /* ROZC */
+#define WM8991_ROZC_BIT 7
+#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
+#define WM8991_ROUTVOL_SHIFT 0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8991_LONMUTE 0x0040 /* LONMUTE */
+#define WM8991_LONMUTE_BIT 6
+#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */
+#define WM8991_LOPMUTE_BIT 5
+#define WM8991_LOATTN 0x0010 /* LOATTN */
+#define WM8991_LOATTN_BIT 4
+#define WM8991_RONMUTE 0x0004 /* RONMUTE */
+#define WM8991_RONMUTE_BIT 2
+#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */
+#define WM8991_ROPMUTE_BIT 1
+#define WM8991_ROATTN 0x0001 /* ROATTN */
+#define WM8991_ROATTN_BIT 0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */
+#define WM8991_OUT3MUTE_BIT 5
+#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */
+#define WM8991_OUT3ATTN_BIT 4
+#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */
+#define WM8991_OUT4MUTE_BIT 1
+#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */
+#define WM8991_OUT4ATTN_BIT 0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */
+#define WM8991_LOPGAZC_BIT 7
+#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
+#define WM8991_LOPGAVOL_SHIFT 0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8991_OPVU 0x0100 /* OPVU */
+#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */
+#define WM8991_ROPGAZC_BIT 7
+#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
+#define WM8991_ROPGAVOL_SHIFT 0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */
+#define WM8991_SPKVOL_SHIFT 0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8991_CDMODE 0x0100 /* CDMODE */
+#define WM8991_CDMODE_BIT 8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
+#define WM8991_DCGAIN_SHIFT 3
+#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
+#define WM8991_ACGAIN_SHIFT 0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
+#define WM8991_AINLMODE_SHIFT 2
+#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
+#define WM8991_AINRMODE_SHIFT 0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8991_LMP4 0x0080 /* LMP4 */
+#define WM8991_LMP4_BIT 7 /* LMP4 */
+#define WM8991_LMN3 0x0040 /* LMN3 */
+#define WM8991_LMN3_BIT 6 /* LMN3 */
+#define WM8991_LMP2 0x0020 /* LMP2 */
+#define WM8991_LMP2_BIT 5 /* LMP2 */
+#define WM8991_LMN1 0x0010 /* LMN1 */
+#define WM8991_LMN1_BIT 4 /* LMN1 */
+#define WM8991_RMP4 0x0008 /* RMP4 */
+#define WM8991_RMP4_BIT 3 /* RMP4 */
+#define WM8991_RMN3 0x0004 /* RMN3 */
+#define WM8991_RMN3_BIT 2 /* RMN3 */
+#define WM8991_RMP2 0x0002 /* RMP2 */
+#define WM8991_RMP2_BIT 1 /* RMP2 */
+#define WM8991_RMN1 0x0001 /* RMN1 */
+#define WM8991_RMN1_BIT 0 /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8991_L34MNB 0x0100 /* L34MNB */
+#define WM8991_L34MNB_BIT 8
+#define WM8991_L34MNBST 0x0080 /* L34MNBST */
+#define WM8991_L34MNBST_BIT 7
+#define WM8991_L12MNB 0x0020 /* L12MNB */
+#define WM8991_L12MNB_BIT 5
+#define WM8991_L12MNBST 0x0010 /* L12MNBST */
+#define WM8991_L12MNBST_BIT 4
+#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
+#define WM8991_LDBVOL_SHIFT 0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8991_R34MNB 0x0100 /* R34MNB */
+#define WM8991_R34MNB_BIT 8
+#define WM8991_R34MNBST 0x0080 /* R34MNBST */
+#define WM8991_R34MNBST_BIT 7
+#define WM8991_R12MNB 0x0020 /* R12MNB */
+#define WM8991_R12MNB_BIT 5
+#define WM8991_R12MNBST 0x0010 /* R12MNBST */
+#define WM8991_R12MNBST_BIT 4
+#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
+#define WM8991_RDBVOL_SHIFT 0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
+#define WM8991_LI2BVOL_SHIFT 6
+#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
+#define WM8991_LR4BVOL_SHIFT 3
+#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
+#define WM8991_LL4BVOL_SHIFT 0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
+#define WM8991_RI2BVOL_SHIFT 6
+#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
+#define WM8991_RL4BVOL_SHIFT 3
+#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
+#define WM8991_RR4BVOL_SHIFT 0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8991_LRBLO 0x0080 /* LRBLO */
+#define WM8991_LRBLO_BIT 7
+#define WM8991_LLBLO 0x0040 /* LLBLO */
+#define WM8991_LLBLO_BIT 6
+#define WM8991_LRI3LO 0x0020 /* LRI3LO */
+#define WM8991_LRI3LO_BIT 5
+#define WM8991_LLI3LO 0x0010 /* LLI3LO */
+#define WM8991_LLI3LO_BIT 4
+#define WM8991_LR12LO 0x0008 /* LR12LO */
+#define WM8991_LR12LO_BIT 3
+#define WM8991_LL12LO 0x0004 /* LL12LO */
+#define WM8991_LL12LO_BIT 2
+#define WM8991_LDLO 0x0001 /* LDLO */
+#define WM8991_LDLO_BIT 0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8991_RLBRO 0x0080 /* RLBRO */
+#define WM8991_RLBRO_BIT 7
+#define WM8991_RRBRO 0x0040 /* RRBRO */
+#define WM8991_RRBRO_BIT 6
+#define WM8991_RLI3RO 0x0020 /* RLI3RO */
+#define WM8991_RLI3RO_BIT 5
+#define WM8991_RRI3RO 0x0010 /* RRI3RO */
+#define WM8991_RRI3RO_BIT 4
+#define WM8991_RL12RO 0x0008 /* RL12RO */
+#define WM8991_RL12RO_BIT 3
+#define WM8991_RR12RO 0x0004 /* RR12RO */
+#define WM8991_RR12RO_BIT 2
+#define WM8991_RDRO 0x0001 /* RDRO */
+#define WM8991_RDRO_BIT 0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
+#define WM8991_LLI3LOVOL_SHIFT 6
+#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
+#define WM8991_LR12LOVOL_SHIFT 3
+#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
+#define WM8991_LL12LOVOL_SHIFT 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
+#define WM8991_RRI3ROVOL_SHIFT 6
+#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
+#define WM8991_RL12ROVOL_SHIFT 3
+#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
+#define WM8991_RR12ROVOL_SHIFT 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
+#define WM8991_LRI3LOVOL_SHIFT 6
+#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
+#define WM8991_LRBLOVOL_SHIFT 3
+#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
+#define WM8991_LLBLOVOL_SHIFT 0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
+#define WM8991_RLI3ROVOL_SHIFT 6
+#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
+#define WM8991_RLBROVOL_SHIFT 3
+#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
+#define WM8991_RRBROVOL_SHIFT 0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */
+#define WM8991_LI4O3 0x0020 /* LI4O3 */
+#define WM8991_LI4O3_BIT 5
+#define WM8991_LPGAO3 0x0010 /* LPGAO3 */
+#define WM8991_LPGAO3_BIT 4
+#define WM8991_RI4O4 0x0002 /* RI4O4 */
+#define WM8991_RI4O4_BIT 1
+#define WM8991_RPGAO4 0x0001 /* RPGAO4 */
+#define WM8991_RPGAO4_BIT 0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */
+#define WM8991_LLOPGALON_BIT 6
+#define WM8991_LROPGALON 0x0020 /* LROPGALON */
+#define WM8991_LROPGALON_BIT 5
+#define WM8991_LOPLON 0x0010 /* LOPLON */
+#define WM8991_LOPLON_BIT 4
+#define WM8991_LR12LOP 0x0004 /* LR12LOP */
+#define WM8991_LR12LOP_BIT 2
+#define WM8991_LL12LOP 0x0002 /* LL12LOP */
+#define WM8991_LL12LOP_BIT 1
+#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */
+#define WM8991_LLOPGALOP_BIT 0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8991_RROPGARON 0x0040 /* RROPGARON */
+#define WM8991_RROPGARON_BIT 6
+#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */
+#define WM8991_RLOPGARON_BIT 5
+#define WM8991_ROPRON 0x0010 /* ROPRON */
+#define WM8991_ROPRON_BIT 4
+#define WM8991_RL12ROP 0x0004 /* RL12ROP */
+#define WM8991_RL12ROP_BIT 2
+#define WM8991_RR12ROP 0x0002 /* RR12ROP */
+#define WM8991_RR12ROP_BIT 1
+#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */
+#define WM8991_RROPGAROP_BIT 0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8991_LB2SPK 0x0080 /* LB2SPK */
+#define WM8991_LB2SPK_BIT 7
+#define WM8991_RB2SPK 0x0040 /* RB2SPK */
+#define WM8991_RB2SPK_BIT 6
+#define WM8991_LI2SPK 0x0020 /* LI2SPK */
+#define WM8991_LI2SPK_BIT 5
+#define WM8991_RI2SPK 0x0010 /* RI2SPK */
+#define WM8991_RI2SPK_BIT 4
+#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */
+#define WM8991_LOPGASPK_BIT 3
+#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */
+#define WM8991_ROPGASPK_BIT 2
+#define WM8991_LDSPK 0x0002 /* LDSPK */
+#define WM8991_LDSPK_BIT 1
+#define WM8991_RDSPK 0x0001 /* RDSPK */
+#define WM8991_RDSPK_BIT 0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8991_VROI 0x0001 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */
+#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */
+#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */
+#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */
+#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */
+#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8991_SOFTST 0x0040 /* SOFTST */
+#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */
+#define WM8991_POBCTRL 0x0002 /* POBCTRL */
+#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
+#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
+#define WM8991_MCD 0x0004 /* MCD */
+#define WM8991_MBSEL 0x0001 /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8991_SDM 0x0080 /* SDM */
+#define WM8991_PRESCALE 0x0040 /* PRESCALE */
+#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8991_INMIXL_PWR_BIT 0
+#define WM8991_AINLMUX_PWR_BIT 1
+#define WM8991_INMIXR_PWR_BIT 2
+#define WM8991_AINRMUX_PWR_BIT 3
+
+#define WM8991_MCLK_DIV 0
+#define WM8991_DACCLK_DIV 1
+#define WM8991_ADCCLK_DIV 2
+#define WM8991_BCLK_DIV 3
+
+#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) }
+
+#endif /* _WM8991_H */
--
1.7.3.5
3
2
[alsa-devel] [PATCH 4/4] ASoC: soc core allow machine driver to register the card
by Koul, Vinod 17 Jan '11
by Koul, Vinod 17 Jan '11
17 Jan '11
From: Vinod Koul <vinod.koul(a)intel.com>
The machine driver can't register the card directly and need to do this thru
soc-audio device creation
This patch allows the register and unregister card to be directly called by
machine drivers
Signed-off-by: Vinod Koul <vinod.koul(a)intel.com>
Signed-off-by: Harsha Priya <priya.harsha(a)intel.com>
---
include/sound/soc.h | 2 ++
sound/soc/soc-core.c | 21 +++++++++++----------
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 541ddfa..9952254 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -258,6 +258,8 @@ enum snd_soc_compress_type {
SND_SOC_RBTREE_COMPRESSION
};
+int snd_soc_register_card(struct snd_soc_card *card);
+int snd_soc_unregister_card(struct snd_soc_card *card);
int snd_soc_register_platform(struct device *dev,
struct snd_soc_platform_driver *platform_drv);
void snd_soc_unregister_platform(struct device *dev);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 8305712..69117b6 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -58,8 +58,6 @@ static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
-static int snd_soc_register_card(struct snd_soc_card *card);
-static int snd_soc_unregister_card(struct snd_soc_card *card);
static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
/*
@@ -1870,6 +1868,13 @@ static int soc_probe(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
int ret = 0;
+ /*
+ * no card, so machine driver should be registering card
+ * we should not be here in that case so ret error
+ */
+ if (!card)
+ return -EINVAL;
+
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
snd_soc_initialize_card_lists(card);
@@ -3105,11 +3110,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
*
* @card: Card to register
*
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
*/
-static int snd_soc_register_card(struct snd_soc_card *card)
+int snd_soc_register_card(struct snd_soc_card *card)
{
int i;
@@ -3141,17 +3143,15 @@ static int snd_soc_register_card(struct snd_soc_card *card)
return 0;
}
+EXPORT_SYMBOL_GPL(snd_soc_register_card);
/**
* snd_soc_unregister_card - Unregister a card with the ASoC core
*
* @card: Card to unregister
*
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
*/
-static int snd_soc_unregister_card(struct snd_soc_card *card)
+int snd_soc_unregister_card(struct snd_soc_card *card)
{
if (card->instantiated)
soc_cleanup_card_resources(card);
@@ -3162,6 +3162,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card)
return 0;
}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_card);
/*
* Simplify DAI link configuration by removing ".-1" from device names
--
1.7.2.3
3
2
[alsa-devel] [PATCH] ASoC: Avoid direct register cache access when setting defaults
by Mark Brown 17 Jan '11
by Mark Brown 17 Jan '11
17 Jan '11
Directly accessing the register cache means that we can't use anything
except a flat register cache so use snd_soc_update_bits().
Signed-off-by: Mark Brown <broonie(a)opensource.wolfsonmicro.com>
---
sound/soc/codecs/wm8523.c | 6 +++---
sound/soc/codecs/wm8741.c | 13 ++++++++-----
sound/soc/codecs/wm8904.c | 41 ++++++++++++++++++++++++++---------------
sound/soc/codecs/wm8955.c | 27 +++++++++++++++++++--------
sound/soc/codecs/wm8962.c | 30 ++++++++++++++++++++----------
sound/soc/codecs/wm8978.c | 2 +-
sound/soc/codecs/wm9090.c | 41 ++++++++++++++++++++++++-----------------
7 files changed, 101 insertions(+), 59 deletions(-)
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 5eb2f50..9b8603f 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -414,7 +414,6 @@ static int wm8523_resume(struct snd_soc_codec *codec)
static int wm8523_probe(struct snd_soc_codec *codec)
{
struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
- u16 *reg_cache = codec->reg_cache;
int ret, i;
codec->hw_write = (hw_write_t)i2c_master_send;
@@ -471,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
- reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
- reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+ snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+ WM8523_DACR_VU, WM8523_DACR_VU);
+ snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 494f2d3..25af901 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -421,7 +421,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
static int wm8741_probe(struct snd_soc_codec *codec)
{
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
- u16 *reg_cache = codec->reg_cache;
int ret = 0;
ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
@@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU */
- reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
- reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
- reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
- reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
+ snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+ WM8741_UPDATELL, WM8741_UPDATELL);
+ snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+ WM8741_UPDATELM, WM8741_UPDATELM);
+ snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+ WM8741_UPDATERL, WM8741_UPDATERL);
+ snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+ WM8741_UPDATERM, WM8741_UPDATERM);
snd_soc_add_controls(codec, wm8741_snd_controls,
ARRAY_SIZE(wm8741_snd_controls));
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 9de44a4..86c1cb8 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
- reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
- reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
- reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
- reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
- reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
- WM8904_HPOUTLZC;
- reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
- WM8904_HPOUTRZC;
- reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
- WM8904_LINEOUTLZC;
- reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
- WM8904_LINEOUTRZC;
- reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+ snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+ WM8904_ADC_VU, WM8904_ADC_VU);
+ snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+ WM8904_ADC_VU, WM8904_ADC_VU);
+ snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+ WM8904_DAC_VU, WM8904_DAC_VU);
+ snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+ WM8904_DAC_VU, WM8904_DAC_VU);
+ snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT,
+ WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+ WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+ snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT,
+ WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+ WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+ snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+ snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+ WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+ snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0,
+ WM8904_SR_MODE, 0);
/* Apply configuration from the platform data. */
if (wm8904->pdata) {
@@ -2469,10 +2478,12 @@ static int wm8904_probe(struct snd_soc_codec *codec)
/* Set Class W by default - this will be managed by the Class
* G widget at runtime where bypass paths are available.
*/
- reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+ snd_soc_update_bits(codec, WM8904_CLASS_W_0,
+ WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
/* Use normal bias source */
- reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+ snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+ WM8904_POBCTRL, 0);
wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 7167dfc..5e0214d 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -934,16 +934,27 @@ static int wm8955_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
- reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
- reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
- reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
- reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
- reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
- reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
- reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+ snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+ WM8955_LDVU, WM8955_LDVU);
+ snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+ WM8955_RDVU, WM8955_RDVU);
+ snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+ WM8955_LO1VU | WM8955_LO1ZC,
+ WM8955_LO1VU | WM8955_LO1ZC);
+ snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+ WM8955_RO1VU | WM8955_RO1ZC,
+ WM8955_RO1VU | WM8955_RO1ZC);
+ snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+ WM8955_LO2VU | WM8955_LO2ZC,
+ WM8955_LO2VU | WM8955_LO2ZC);
+ snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+ WM8955_RO2VU | WM8955_RO2ZC,
+ WM8955_RO2VU | WM8955_RO2ZC);
+ snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+ WM8955_MOZC, WM8955_MOZC);
/* Also enable adaptive bass boost by default */
- reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+ snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
/* Set platform data values */
if (pdata) {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index b9cb1fc..c827057 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3822,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec)
}
/* Latch volume update bits */
- reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU;
- reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU;
- reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU;
- reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU;
- reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU;
- reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU;
- reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU;
- reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU;
- reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU;
- reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU;
+ snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
+ WM8962_IN_VU, WM8962_IN_VU);
+ snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME,
+ WM8962_IN_VU, WM8962_IN_VU);
+ snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME,
+ WM8962_ADC_VU, WM8962_ADC_VU);
+ snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME,
+ WM8962_ADC_VU, WM8962_ADC_VU);
+ snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME,
+ WM8962_DAC_VU, WM8962_DAC_VU);
+ snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME,
+ WM8962_DAC_VU, WM8962_DAC_VU);
+ snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME,
+ WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+ snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME,
+ WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+ snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME,
+ WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+ snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
+ WM8962_HPOUT_VU, WM8962_HPOUT_VU);
wm8962_add_widgets(codec);
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 4bbc344..30fb48e 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -965,7 +965,7 @@ static int wm8978_probe(struct snd_soc_codec *codec)
* written.
*/
for (i = 0; i < ARRAY_SIZE(update_reg); i++)
- ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+ snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
/* Reset the codec */
ret = snd_soc_write(codec, WM8978_RESET, 0);
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index a788c42..3c5d325 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -551,7 +551,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
static int wm9090_probe(struct snd_soc_codec *codec)
{
struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
- u16 *reg_cache = codec->reg_cache;
int ret;
codec->control_data = wm9090->control_data;
@@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec)
/* Configure some defaults; they will be written out when we
* bring the bias up.
*/
- reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
- | WM9090_IN1A_ZC;
- reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
- | WM9090_IN1B_ZC;
- reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
- | WM9090_IN2A_ZC;
- reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
- | WM9090_IN2B_ZC;
- reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
- WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
- reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
- WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
- reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
- WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
-
- reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
+ snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+ WM9090_IN1_VU | WM9090_IN1A_ZC,
+ WM9090_IN1_VU | WM9090_IN1A_ZC);
+ snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+ WM9090_IN1_VU | WM9090_IN1B_ZC,
+ WM9090_IN1_VU | WM9090_IN1B_ZC);
+ snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+ WM9090_IN2_VU | WM9090_IN2A_ZC,
+ WM9090_IN2_VU | WM9090_IN2A_ZC);
+ snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+ WM9090_IN2_VU | WM9090_IN2B_ZC,
+ WM9090_IN2_VU | WM9090_IN2B_ZC);
+ snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+ WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
+ WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
+ snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
+ snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
+ WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
+
+ snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+ WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
--
1.7.2.3
2
1
[alsa-devel] [PATCH] ALSA : HDA - add multi-streaming playback of front panel headphone and rear panel audio jacks at same time for hda ad1988
by Raymond Yau 17 Jan '11
by Raymond Yau 17 Jan '11
17 Jan '11
Attached a patch which add a new model to support multi-streaming playback
for ad1988
playback another stereo stream through the front panel headphone on device 2
while
playback through the speakers connected to rear panel on device 0 at the
same time.
Tested with ad1988a rev2 codec on asus P5B-V motherboard
Signed-off-by : Raymond Yau < superquad.vortex2 <at> gmail.com >
2
1
[alsa-devel] [PATCH] ALSA : au88x0 - fix playback/capture channels of au8830
by Raymond Yau 17 Jan '11
by Raymond Yau 17 Jan '11
17 Jan '11
patch to change supported playback channels of au8830 to 1,2,4 and capture
channels to 1,2
This prevent oops when oss emulation use SNDCTL_DSP_CHANNELS to set 3
channels
2
1
Hello,
I'm currently developing a driver for a card. PCM playback works so far, I just encountered some playback issues when firefox (flash) and another alsa client wanted to play back at the same time. I figured out that the difference between my driver and existing ones is, that my driver only allows one client and others multiple. I get a "Device or resource busy" when I play back two times the same file (also different ones of course) with mplayer f.ex.
I was able to track this down to the fact, that (only) the O_APPEND Flag is missing in file->f_flags (snd_pcm_attach_substream), but was completely unable to find out where this flag comes from in other drivers.
Another point (perhaps not unrelated to this) is, that I get a bug (see below) that seems to tell me, that prepare cannot schedule. Contrary to the documentation that says that prepare is now non-atomic. It is useful to wait in the prepare function since I need to set the sample rate and wait for a reply (USB device). Before I rewrite it, i wanted to ask about atomicity in prepare on this ML.
Greetings,
Torsten
[ 3662.318321] usb 1-2: unlink qh0-00ff/f69a5300 start 0 [2/0 us]
[ 3662.318434] BUG: scheduling while atomic: aplay/17902/0x00000002
[ 3662.318435] Modules linked in: snd_usb_6fire snd_rawmidi snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc nvidia(P) [last unloaded: soundcore]
[ 3662.318443] Pid: 17902, comm: aplay Tainted: P 2.6.36-gentoo-r5 #3
[ 3662.318445] Call Trace:
[ 3662.318448] [<c1025fa0>] __schedule_bug+0x48/0x4d
[ 3662.318451] [<c136f438>] schedule+0x9d/0x6b6
[ 3662.318454] [<c10383f6>] ? __mod_timer+0xf5/0x100
[ 3662.318457] [<c136fc83>] schedule_timeout+0x73/0x90
[ 3662.318460] [<c1037ffe>] ? process_timeout+0x0/0xa
[ 3662.318463] [<fa335622>] pcm_alsa_prepare+0x353/0x3a5 [snd_usb_6fire]
[ 3662.318467] [<c1043121>] ? autoremove_wake_function+0x0/0x2f
[ 3662.318472] [<fa2fb700>] snd_pcm_do_prepare+0xc/0x1c [snd_pcm]
[ 3662.318476] [<fa2fb4d3>] snd_pcm_action_single+0x25/0x4b [snd_pcm]
[ 3662.318481] [<fa2fc4cc>] snd_pcm_action_nonatomic+0x42/0x55 [snd_pcm]
[ 3662.318486] [<fa2fe0c9>] snd_pcm_common_ioctl1+0x379/0xa19 [snd_pcm]
[ 3662.318489] [<c1086437>] ? find_get_page+0x6b/0x74
[ 3662.318492] [<c10259f4>] ? get_parent_ip+0xb/0x31
[ 3662.318497] [<fa2fed24>] snd_pcm_playback_ioctl1+0x2d4/0x2eb [snd_pcm]
[ 3662.318500] [<c1168866>] ? file_has_perm+0x7c/0x85
[ 3662.318505] [<fa2fed5b>] snd_pcm_playback_ioctl+0x20/0x2d [snd_pcm]
[ 3662.318513] [<fa2fed3b>] ? snd_pcm_playback_ioctl+0x0/0x2d [snd_pcm]
[ 3662.318515] [<c10b9e1a>] do_vfs_ioctl+0x43c/0x481
[ 3662.318519] [<c1168a26>] ? selinux_file_ioctl+0x3e/0x41
[ 3662.318522] [<c10b9ea0>] sys_ioctl+0x41/0x61
[ 3662.318525] [<c100278c>] sysenter_do_call+0x12/0x22
3
2
add FreeScale SGTL5000 codec support
Signed-off-by: Zeng Zhaoming <zhaoming.zeng(a)freescale.com>
---
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 1 +
sound/soc/codecs/sgtl5000.c | 1016 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/sgtl5000.h | 403 +++++++++++++++++
4 files changed, 1424 insertions(+), 0 deletions(-)
create mode 100644 sound/soc/codecs/sgtl5000.c
create mode 100644 sound/soc/codecs/sgtl5000.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 883a312..19bbb73 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
+ select SND_SOC_SGTL5000 if I2C
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
@@ -176,6 +177,9 @@ config SND_SOC_MAX98088
config SND_SOC_PCM3008
tristate
+config SND_SOC_SGTL5000
+ tristate
+
config SND_SOC_SPDIF
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 579af9c..78e36cc 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -18,6 +18,7 @@ snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
new file mode 100644
index 0000000..9fe474f
--- /dev/null
+++ b/sound/soc/codecs/sgtl5000.c
@@ -0,0 +1,1016 @@
+/*
+ * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc. 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/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <mach/hardware.h>
+
+#include "sgtl5000.h"
+
+/* default value of all sgtl5000 registers */
+static const u16 sgtl5000_regs[] = {
+ /* 0x0 0x2 0x4 0x6 0x8 0xa 0xc 0xe */
+ 0xa011, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0010, 0x0000,/* 0x */
+ 0x0010, 0x0000, 0x0010, 0x0000, 0x0000, 0x0000, 0x323c, 0x0000,/* 1x */
+ 0x3c3c, 0x0000, 0x3c3c, 0x0000, 0x555f, 0x0000, 0x0000, 0x0000,/* 2x */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x408c, 0x0000, 0x0008, 0x0000,/* 3x */
+ 0x0000, 0x0000, 0x1818, 0x0000, 0x0111, 0x0000, 0x0000, 0x0000,/* 4x */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0404, 0x0000,/* 5x */
+ 0x7060, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x0017, 0x0000,/* 6x */
+ 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,/* 7x */
+};
+
+/* regulator supplies for sgtl5000, VDDD is an option external supply */
+enum sgtl5000_regulator_supplies {
+ VDDA,
+ VDDIO,
+ VDDD,
+ SGTL5000_SUPPLY_NUM
+};
+
+
+static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
+ "VDDA",
+ "VDDIO",
+ "VDDD"
+};
+
+/* sgtl5000 private structure in codec */
+struct sgtl5000_priv {
+ int sysclk; /* sysclk rate */
+ int master; /* i2s master or not? */
+ int fmt; /* i2s data format */
+ int lrclk; /* frame clock rate */
+ int capture_channels; /* the num of channels for capture. */
+ int small_pop; /* hw assistant to eliminate pop */
+ struct regulator *supplies[SGTL5000_SUPPLY_NUM]; /* all regulators. */
+};
+
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ /* change mic bias resistor to 4Kohm */
+ snd_soc_write(w->codec, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_R_4k);
+
+ return 0;
+}
+
+static int dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP|SGTL5000_DAC_POWERUP,
+ SGTL5000_VAG_POWERUP|SGTL5000_DAC_POWERUP);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_DAC_POWERUP, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * if small_pop is enabled, hp_powerup or lineout_powerup
+ * should be set before vag_powerup be cleared, this introduce
+ * 200~400ms latency
+ */
+static int small_pop_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct sgtl5000_priv *priv = snd_soc_codec_get_drvdata(w->codec);
+ int reg;
+
+ if (!priv->small_pop)
+ return 0;
+
+ reg = snd_soc_read(w->codec, SGTL5000_CHIP_ANA_POWER);
+ if (reg & SGTL5000_VAG_POWERUP) {
+ reg &= ~SGTL5000_VAG_POWERUP;
+ snd_soc_write(w->codec, SGTL5000_CHIP_ANA_POWER, reg);
+ msleep(400);
+ }
+
+ return 0;
+}
+
+static int adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_DAC_POWERUP,
+ SGTL5000_DAC_POWERUP);
+
+ return 0;
+}
+
+/* input sources for ADC */
+static const char *adc_mux_text[] = {
+ "MIC_IN", "LINE_IN"
+};
+
+static const struct soc_enum adc_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text);
+
+static const struct snd_kcontrol_new adc_mux =
+SOC_DAPM_ENUM("Capture Mux", adc_enum);
+
+/* input sources for DAC */
+static const char *dac_mux_text[] = {
+ "DAC", "LINE_IN"
+};
+
+static const struct soc_enum dac_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINE_IN"),
+ SND_SOC_DAPM_INPUT("MIC_IN"),
+
+ SND_SOC_DAPM_OUTPUT("HP_OUT"),
+ SND_SOC_DAPM_OUTPUT("LINE_OUT"),
+
+ SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+ mic_bias_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
+ small_pop_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0,
+ small_pop_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
+ SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+
+ /* aif for i2s input */
+ SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
+ 0, SGTL5000_CHIP_DIG_POWER,
+ 0, 0),
+
+ /* aif for i2s output */
+ SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
+ 0, SGTL5000_CHIP_DIG_POWER,
+ 1, 0),
+
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_DIG_POWER, 6, 0,
+ adc_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_DIG_POWER, 5, 0,
+ dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+/* routes for sgtl5000 */
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
+ {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
+
+ {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
+ {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
+
+ {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
+ {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
+ {"LO", NULL, "DAC"}, /* dac --> line_out */
+
+ {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
+ {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
+
+ {"LINE_OUT", NULL, "LO"},
+ {"HP_OUT", NULL, "HP"},
+};
+
+/* custom function to fetch info of PCM playback volume */
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xfc - 0x3c;
+ return 0;
+}
+
+/* custom function to get of PCM playback volume */
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+
+ reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+ l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
+ r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
+ l = l < 0x3c ? 0x3c : l;
+ l = l > 0xfc ? 0xfc : l;
+ r = r < 0x3c ? 0x3c : r;
+ r = r > 0xfc ? 0xfc : r;
+
+ /* revert it */
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ ucontrol->value.integer.value[0] = l;
+ ucontrol->value.integer.value[1] = r;
+
+ return 0;
+}
+
+/* custom put function for PCM playback volume */
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+
+ l = ucontrol->value.integer.value[0];
+ r = ucontrol->value.integer.value[1];
+
+ l = l < 0 ? 0 : l;
+ l = l > 0xfc - 0x3c ? 0xfc - 0x3c : l;
+ r = r < 0 ? 0 : r;
+ r = r > 0xfc - 0x3c ? 0xfc - 0x3c : r;
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
+ r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+ snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+
+ return 0;
+}
+
+/* tlv for mic gain, 0db 20db 30db 40db */
+static const unsigned int mic_gain_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
+};
+
+/* tlv for hp volume, -51.5db to 12.0db, step .5db */
+static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+
+static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
+ SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
+ SOC_SINGLE("Capture Attenuate Switch (-6db)",
+ SGTL5000_CHIP_ANA_ADC_CTRL,
+ 8, 1, 0),
+
+ /* we need SOC_DOUBLE_S8_TLV with invert */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = dac_info_volsw,
+ .get = dac_get_volsw,
+ .put = dac_put_volsw,
+ },
+
+ SOC_DOUBLE_TLV("Headphone Playback Volume",
+ SGTL5000_CHIP_ANA_HP_CTRL,
+ 0, 8,
+ 0x7f, 1,
+ headphone_volume),
+
+ SOC_SINGLE_TLV("Mic Volume",
+ SGTL5000_CHIP_MIC_CTRL,
+ 0, 4, 0, mic_gain_tlv),
+};
+
+/* mutt the codec used by alsa core */
+static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+
+ if (mute)
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ adcdac_ctrl, adcdac_ctrl);
+ else
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ adcdac_ctrl, 0);
+
+ return 0;
+}
+
+/* set codec format */
+static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ u16 i2sctl = 0;
+
+ sgtl5000->master = 0;
+ /*
+ * i2s clock and frame master setting.
+ * ONLY support:
+ * - clock and frame slave,
+ * - clock and frame master
+ */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2sctl |= SGTL5000_I2S_MASTER;
+ sgtl5000->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* setting i2s data format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2sctl |= SGTL5000_I2S_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2sctl |= SGTL5000_I2S_MODE_PCM;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2sctl |= SGTL5000_I2S_MODE_RJ;
+ i2sctl |= SGTL5000_I2S_LRPOL;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ i2sctl |= SGTL5000_I2S_SCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+
+ return 0;
+}
+
+/* set codec sysclk */
+static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case SGTL5000_SYSCLK:
+ sgtl5000->sysclk = freq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ * input: params_rate, params_fmt
+ */
+static int sgtl5000_pcm_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ int channels = params_channels(params);
+ int clk_ctl = 0;
+ int pll_ctl = 0;
+ int i2s_ctl;
+ int div2 = 0;
+ int reg;
+ int sys_fs;
+
+ /* sysclk should already set */
+ if (!sgtl5000->sysclk) {
+ dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+ return -EFAULT;
+ }
+
+ /* power up adc, and set it accroding to stereo or not */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ sgtl5000->capture_channels = channels;
+
+ reg = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+ reg |= SGTL5000_ADC_POWERUP;
+
+ if (sgtl5000->capture_channels == 1)
+ reg &= ~SGTL5000_ADC_STEREO;
+ else
+ reg |= SGTL5000_ADC_STEREO;
+
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ }
+
+ /* get frame clock rate */
+ sgtl5000->lrclk = params_rate(params);
+
+ /*
+ * frame clock is divided from sys fs,
+ * if frame clock lower than 44.1khz, sys fs should set to
+ * 32khz or 44.1khz.
+ */
+ switch (sgtl5000->lrclk) {
+ case 8000:
+ case 16000:
+ sys_fs = 32000;
+ break;
+ case 11025:
+ case 22050:
+ sys_fs = 44100;
+ break;
+ default:
+ sys_fs = sgtl5000->lrclk;
+ break;
+ }
+
+ /* get devided factor of frame clock */
+ switch (sys_fs / sgtl5000->lrclk) {
+ case 4:
+ clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT;
+ break;
+ case 2:
+ clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT;
+ break;
+ default:
+ break;
+ }
+
+ /* set the sys_fs accroding to frame clock */
+ switch (sys_fs) {
+ case 32000:
+ clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 44100:
+ clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 48000:
+ clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 96000:
+ clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ default:
+ dev_err(codec->dev, "sample rate %d not supported\n",
+ sgtl5000->lrclk);
+ return -EINVAL;
+ }
+
+ /* if codec is slave, just set register to match master parameters */
+ if (!sgtl5000->master) {
+ sys_fs = sgtl5000->lrclk;
+ clk_ctl = SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT;
+ if (sys_fs * 256 == sgtl5000->sysclk)
+ clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ else if (sys_fs * 384 == sgtl5000->sysclk && sys_fs != 96000)
+ clk_ctl |= SGTL5000_MCLK_FREQ_384FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ else if (sys_fs * 512 == sgtl5000->sysclk && sys_fs != 96000)
+ clk_ctl |= SGTL5000_MCLK_FREQ_512FS <<
+ SGTL5000_MCLK_FREQ_SHIFT;
+ else {
+ pr_err("%s: PLL not supported in slave mode\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else
+ /* if codec is master, use PLL to generate sys_fs */
+ clk_ctl |= SGTL5000_MCLK_FREQ_PLL << SGTL5000_MCLK_FREQ_SHIFT;
+
+ if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+ u64 out, t;
+ unsigned int in, int_div, frac_div;
+ if (sgtl5000->sysclk > 17000000) {
+ div2 = 1;
+ in = sgtl5000->sysclk / 2;
+ } else {
+ div2 = 0;
+ in = sgtl5000->sysclk;
+ }
+ if (sys_fs == 44100)
+ out = 180633600;
+ else
+ out = 196608000;
+ t = do_div(out, in);
+ int_div = out;
+ t *= 2048;
+ do_div(t, in);
+ frac_div = t;
+ pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
+ frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
+ }
+
+ i2s_ctl = snd_soc_read(codec, SGTL5000_CHIP_I2S_CTRL);
+
+ /* set i2s data format */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "fs=%d,clk_ctl=%d,pll_ctl=%d,i2s_ctl=%d,div2=%d\n",
+ sgtl5000->lrclk, clk_ctl, pll_ctl, i2s_ctl, div2);
+
+ if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+ snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ if (div2)
+ snd_soc_update_bits(codec,
+ SGTL5000_CHIP_CLK_TOP_CTRL,
+ SGTL5000_INPUT_FREQ_DIV2,
+ SGTL5000_INPUT_FREQ_DIV2);
+ else
+ snd_soc_update_bits(codec,
+ SGTL5000_CHIP_CLK_TOP_CTRL,
+ SGTL5000_INPUT_FREQ_DIV2,
+ 0);
+
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+ SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+ }
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl);
+
+ return 0;
+}
+
+/*
+ * set dac bias
+ * common state changes:
+ * startup:
+ * off --> standby --> prepare --> on
+ * standby --> prepare --> on
+ *
+ * stop:
+ * on --> prepare --> standby
+ */
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ codec->bias_level = level;
+ return 0;
+}
+
+#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai_ops sgtl5000_ops = {
+ .hw_params = sgtl5000_pcm_hw_params,
+ .digital_mute = sgtl5000_digital_mute,
+ .set_fmt = sgtl5000_set_dai_fmt,
+ .set_sysclk = sgtl5000_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver sgtl5000_dai = {
+ .name = "sgtl5000",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ /*
+ * only support 8~48K + 96K,
+ * TODO modify hw_param to support more
+ */
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SGTL5000_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SGTL5000_FORMATS,
+ },
+ .ops = &sgtl5000_ops,
+ .symmetric_rates = 1,
+};
+
+static int sgtl5000_volatile_register(unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ return 1;
+ }
+
+ return 0;
+}
+
+static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int sgtl5000_restore_reg(struct snd_soc_codec *codec, unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ return snd_soc_write(codec, reg, cache[reg >> 1]);
+}
+
+static int sgtl5000_resume(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /* Restore refs first in same order as in sgtl5000_probe */
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINREG_CTRL);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_ANA_POWER);
+ msleep(10);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_REF_CTRL);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINE_OUT_CTRL);
+
+ /* Restore everythine else */
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_regs); i++)
+ sgtl5000_restore_reg(codec, i);
+
+ snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static int sgtl5000_probe(struct snd_soc_codec *codec)
+{
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ u16 reg, ana_pwr, lreg_ctrl;
+ int vag;
+ int ret;
+ int vddd, vdda, vddio;
+ int rev;
+ int i;
+
+ /* setup i2c data ops */
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* get and enable all regulators */
+ for (i = 0; i < SGTL5000_SUPPLY_NUM; i++) {
+ struct regulator *reg;
+ reg = regulator_get(codec->dev, supply_names[i]);
+
+ if (IS_ERR(reg))
+ continue;
+
+ regulator_enable(reg);
+ sgtl5000->supplies[i] = reg;
+ }
+
+ /* vdda and vddio regulator must configed */
+ if (!sgtl5000->supplies[VDDA] || !sgtl5000->supplies[VDDIO]) {
+ dev_err(codec->dev,
+ "Not set vdda or vddio regulator correctly\n");
+ /* FIXME, no such platform regulator configed */
+ return -ENODEV;
+ }
+
+ /* 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_out;
+ }
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(codec->dev, "sgtl5000 revision %d\n", rev);
+
+ /* reset value */
+ ana_pwr = SGTL5000_DAC_STEREO |
+ SGTL5000_LINREG_SIMPLE_POWERUP |
+ SGTL5000_STARTUP_POWERUP |
+ SGTL5000_ADC_STEREO | SGTL5000_REFTOP_POWERUP;
+ lreg_ctrl = 0;
+
+ vdda = regulator_get_voltage(sgtl5000->supplies[VDDA]) / 1000;
+ vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO]) / 1000;
+
+ if (regulator_get_voltage(sgtl5000->supplies[VDDD]))
+ vddd = regulator_get_voltage(sgtl5000->supplies[VDDD]) / 1000;
+ else
+ vddd = 0;
+
+ /* workaround for rev 0x11: use vddd linear regulator */
+ if (!vddd || (rev >= 0x11)) {
+ /* set VDDD to 1.2v */
+ lreg_ctrl |= 0x8 << SGTL5000_LINREG_VDDD_SHIFT;
+ /* power internal linear regulator */
+ ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
+ } else {
+ /* turn of startup power */
+ ana_pwr &= ~SGTL5000_STARTUP_POWERUP;
+ ana_pwr &= ~SGTL5000_LINREG_SIMPLE_POWERUP;
+ }
+
+ if (vddio < 3100 && vdda < 3100) {
+ /* Enable VDDC charge pump */
+ ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
+ }
+
+ if (vddio >= 3100 && vdda >= 3100) {
+ /* VDDC use VDDIO rail */
+ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+ lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+ SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ }
+
+ /* If PLL is powered up (such as on power cycle) leave it on. */
+ reg = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr |= reg & (SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+
+ /* set ADC/DAC ref voltage to vdda / 2 */
+ vag = vdda / 2;
+ if (vag <= SGTL5000_ANA_GND_BASE)
+ vag = 0;
+ else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
+ (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT))
+ vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT;
+ else
+ vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
+
+ /* set line out ref voltage to vddio / 2 */
+ vag = vddio / 2;
+ if (vag <= SGTL5000_LINE_OUT_GND_BASE)
+ vag = 0;
+ else if (vag >= SGTL5000_LINE_OUT_GND_BASE + SGTL5000_LINE_OUT_GND_STP *
+ SGTL5000_LINE_OUT_GND_MAX)
+ vag = SGTL5000_LINE_OUT_GND_MAX;
+ else
+ vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+ SGTL5000_LINE_OUT_GND_STP;
+
+ snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ msleep(10);
+
+ /* For rev 0x11, if vddd linear reg has been enabled, we have
+ to disable simple reg to get proper VDDD voltage. */
+ if ((ana_pwr & SGTL5000_LINEREG_D_POWERUP) && (rev >= 0x11)) {
+ ana_pwr &= ~SGTL5000_LINREG_SIMPLE_POWERUP;
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ msleep(10);
+ }
+
+ /* enable small pop, introduce 200~400ms delay in turning on/off */
+ snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
+ vag << SGTL5000_ANA_GND_SHIFT | SGTL5000_SMALL_POP);
+
+ sgtl5000->small_pop = 1;
+
+ snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+ vag << SGTL5000_LINE_OUT_GND_SHIFT |
+ SGTL5000_LINE_OUT_CURRENT_360u <<
+ SGTL5000_LINE_OUT_CURRENT_SHIFT);
+
+ /* enable short cut detect */
+ snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+
+ /*
+ * set sound switch
+ * TODO: add sound switch to control and dapm widge.
+ */
+ snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
+ SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
+ snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER, 0);
+
+ snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ SGTL5000_DAC_VOL_RAMP_EN |
+ SGTL5000_DAC_MUTE_RIGHT |
+ SGTL5000_DAC_MUTE_LEFT);
+
+ snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+
+ reg = snd_soc_read(codec, SGTL5000_CHIP_ANA_ADC_CTRL);
+ reg &= ~SGTL5000_ADC_VOL_M6DB;
+ reg &= ~(SGTL5000_ADC_VOL_LEFT_MASK | SGTL5000_ADC_VOL_RIGHT_MASK);
+ reg |= (0xf << SGTL5000_ADC_VOL_LEFT_SHIFT)
+ | (0xf << SGTL5000_ADC_VOL_RIGHT_SHIFT);
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_ADC_CTRL, reg);
+
+ snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_HP_ZCD_EN |
+ SGTL5000_ADC_ZCD_EN);
+
+ snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+ snd_soc_write(codec, SGTL5000_CHIP_CLK_TOP_CTRL, 0);
+
+ /*
+ * disable DAP
+ * TODO:
+ * Enable DAP in control and dapm.
+ */
+ snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+
+ /* leading to standby state */
+ ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (ret)
+ goto err_out;
+
+ snd_soc_add_controls(codec, sgtl5000_snd_controls,
+ ARRAY_SIZE(sgtl5000_snd_controls));
+
+ snd_soc_dapm_new_controls(codec, sgtl5000_dapm_widgets,
+ ARRAY_SIZE(sgtl5000_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+
+err_out:
+ for (i = 0; i < SGTL5000_SUPPLY_NUM; i++) {
+ if (!sgtl5000->supplies[i])
+ continue;
+
+ regulator_disable(sgtl5000->supplies[i]);
+ regulator_put(sgtl5000->supplies[i]);
+ }
+
+ return ret;
+}
+
+static int sgtl5000_remove(struct snd_soc_codec *codec)
+{
+ struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_dapm_free(codec);
+
+ for (i = 0; i < SGTL5000_SUPPLY_NUM; i++) {
+ if (!sgtl5000->supplies[i])
+ continue;
+
+ regulator_disable(sgtl5000->supplies[i]);
+ regulator_put(sgtl5000->supplies[i]);
+ }
+
+ return 0;
+}
+
+struct snd_soc_codec_driver sgtl5000_driver = {
+ .probe = sgtl5000_probe,
+ .remove = sgtl5000_remove,
+ .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,
+};
+
+static __devinit int sgtl5000_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sgtl5000_priv *sgtl5000;
+ int ret;
+
+ sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL);
+ if (!sgtl5000)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, sgtl5000);
+
+ msleep(1);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &sgtl5000_driver, &sgtl5000_dai, 1);
+ if (ret) {
+ dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+ kfree(sgtl5000);
+ return ret;
+ }
+
+ return 0;
+}
+
+static __devexit int sgtl5000_i2c_remove(struct i2c_client *client)
+{
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ kfree(sgtl5000);
+ return 0;
+}
+
+static const struct i2c_device_id sgtl5000_id[] = {
+ {"sgtl5000-codec", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
+
+static struct i2c_driver sgtl5000_i2c_driver = {
+ .driver = {
+ .name = "sgtl5000-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = sgtl5000_i2c_probe,
+ .remove = __devexit_p(sgtl5000_i2c_remove),
+ .id_table = sgtl5000_id,
+};
+
+static int __init sgtl5000_modinit(void)
+{
+ return i2c_add_driver(&sgtl5000_i2c_driver);
+}
+module_init(sgtl5000_modinit);
+
+static void __exit sgtl5000_exit(void)
+{
+ i2c_del_driver(&sgtl5000_i2c_driver);
+}
+module_exit(sgtl5000_exit);
+
+MODULE_DESCRIPTION("ASoC sgtl5000 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
new file mode 100644
index 0000000..7f24655
--- /dev/null
+++ b/sound/soc/codecs/sgtl5000.h
@@ -0,0 +1,403 @@
+/*
+ * sgtl5000.h - SGTL5000 audio codec interface
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _SGTL5000_H
+#define _SGTL5000_H
+
+#include <linux/i2c.h>
+
+/*
+ * Register values.
+ */
+#define SGTL5000_CHIP_ID 0x0000
+#define SGTL5000_CHIP_DIG_POWER 0x0002
+#define SGTL5000_CHIP_CLK_CTRL 0x0004
+#define SGTL5000_CHIP_I2S_CTRL 0x0006
+#define SGTL5000_CHIP_SSS_CTRL 0x000a
+#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e
+#define SGTL5000_CHIP_DAC_VOL 0x0010
+#define SGTL5000_CHIP_PAD_STRENGTH 0x0014
+#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020
+#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022
+#define SGTL5000_CHIP_ANA_CTRL 0x0024
+#define SGTL5000_CHIP_LINREG_CTRL 0x0026
+#define SGTL5000_CHIP_REF_CTRL 0x0028
+#define SGTL5000_CHIP_MIC_CTRL 0x002a
+#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c
+#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e
+#define SGTL5000_CHIP_ANA_POWER 0x0030
+#define SGTL5000_CHIP_PLL_CTRL 0x0032
+#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034
+#define SGTL5000_CHIP_ANA_STATUS 0x0036
+#define SGTL5000_CHIP_SHORT_CTRL 0x003c
+#define SGTL5000_CHIP_ANA_TEST2 0x003a
+#define SGTL5000_DAP_CTRL 0x0100
+#define SGTL5000_DAP_PEQ 0x0102
+#define SGTL5000_DAP_BASS_ENHANCE 0x0104
+#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106
+#define SGTL5000_DAP_AUDIO_EQ 0x0108
+#define SGTL5000_DAP_SURROUND 0x010a
+#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c
+#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e
+#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110
+#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116
+#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118
+#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a
+#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c
+#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e
+#define SGTL5000_DAP_MAIN_CHAN 0x0120
+#define SGTL5000_DAP_MIX_CHAN 0x0122
+#define SGTL5000_DAP_AVC_CTRL 0x0124
+#define SGTL5000_DAP_AVC_THRESHOLD 0x0126
+#define SGTL5000_DAP_AVC_ATTACK 0x0128
+#define SGTL5000_DAP_AVC_DECAY 0x012a
+#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c
+#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e
+#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130
+#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132
+#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134
+#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136
+#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138
+#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * SGTL5000_CHIP_ID
+ */
+#define SGTL5000_PARTID_MASK 0xff00
+#define SGTL5000_PARTID_SHIFT 8
+#define SGTL5000_PARTID_WIDTH 8
+#define SGTL5000_PARTID_PART_ID 0xa0
+#define SGTL5000_REVID_MASK 0x00ff
+#define SGTL5000_REVID_SHIFT 0
+#define SGTL5000_REVID_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_DIG_POWER
+ */
+#define SGTL5000_ADC_EN 0x0040
+#define SGTL5000_DAC_EN 0x0020
+#define SGTL5000_DAP_POWERUP 0x0010
+#define SGTL5000_I2S_OUT_POWERUP 0x0002
+#define SGTL5000_I2S_IN_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_CLK_CTRL
+ */
+#define SGTL5000_RATE_MODE_MASK 0x0030
+#define SGTL5000_RATE_MODE_SHIFT 4
+#define SGTL5000_RATE_MODE_WIDTH 2
+#define SGTL5000_RATE_MODE_DIV_1 0
+#define SGTL5000_RATE_MODE_DIV_2 1
+#define SGTL5000_RATE_MODE_DIV_4 2
+#define SGTL5000_RATE_MODE_DIV_6 3
+#define SGTL5000_SYS_FS_MASK 0x000c
+#define SGTL5000_SYS_FS_SHIFT 2
+#define SGTL5000_SYS_FS_WIDTH 2
+#define SGTL5000_SYS_FS_32k 0x0
+#define SGTL5000_SYS_FS_44_1k 0x1
+#define SGTL5000_SYS_FS_48k 0x2
+#define SGTL5000_SYS_FS_96k 0x3
+#define SGTL5000_MCLK_FREQ_MASK 0x0003
+#define SGTL5000_MCLK_FREQ_SHIFT 0
+#define SGTL5000_MCLK_FREQ_WIDTH 2
+#define SGTL5000_MCLK_FREQ_256FS 0x0
+#define SGTL5000_MCLK_FREQ_384FS 0x1
+#define SGTL5000_MCLK_FREQ_512FS 0x2
+#define SGTL5000_MCLK_FREQ_PLL 0x3
+
+/*
+ * SGTL5000_CHIP_I2S_CTRL
+ */
+#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100
+#define SGTL5000_I2S_SCLKFREQ_SHIFT 8
+#define SGTL5000_I2S_SCLKFREQ_WIDTH 1
+#define SGTL5000_I2S_SCLKFREQ_64FS 0x0
+#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */
+#define SGTL5000_I2S_MASTER 0x0080
+#define SGTL5000_I2S_SCLK_INV 0x0040
+#define SGTL5000_I2S_DLEN_MASK 0x0030
+#define SGTL5000_I2S_DLEN_SHIFT 4
+#define SGTL5000_I2S_DLEN_WIDTH 2
+#define SGTL5000_I2S_DLEN_32 0x0
+#define SGTL5000_I2S_DLEN_24 0x1
+#define SGTL5000_I2S_DLEN_20 0x2
+#define SGTL5000_I2S_DLEN_16 0x3
+#define SGTL5000_I2S_MODE_MASK 0x000c
+#define SGTL5000_I2S_MODE_SHIFT 2
+#define SGTL5000_I2S_MODE_WIDTH 2
+#define SGTL5000_I2S_MODE_I2S_LJ 0x0
+#define SGTL5000_I2S_MODE_RJ 0x1
+#define SGTL5000_I2S_MODE_PCM 0x2
+#define SGTL5000_I2S_LRALIGN 0x0002
+#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */
+
+/*
+ * SGTL5000_CHIP_SSS_CTRL
+ */
+#define SGTL5000_DAP_MIX_LRSWAP 0x4000
+#define SGTL5000_DAP_LRSWAP 0x2000
+#define SGTL5000_DAC_LRSWAP 0x1000
+#define SGTL5000_I2S_OUT_LRSWAP 0x0400
+#define SGTL5000_DAP_MIX_SEL_MASK 0x0300
+#define SGTL5000_DAP_MIX_SEL_SHIFT 8
+#define SGTL5000_DAP_MIX_SEL_WIDTH 2
+#define SGTL5000_DAP_MIX_SEL_ADC 0x0
+#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1
+#define SGTL5000_DAP_SEL_MASK 0x00c0
+#define SGTL5000_DAP_SEL_SHIFT 6
+#define SGTL5000_DAP_SEL_WIDTH 2
+#define SGTL5000_DAP_SEL_ADC 0x0
+#define SGTL5000_DAP_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_MASK 0x0030
+#define SGTL5000_DAC_SEL_SHIFT 4
+#define SGTL5000_DAC_SEL_WIDTH 2
+#define SGTL5000_DAC_SEL_ADC 0x0
+#define SGTL5000_DAC_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_DAP 0x3
+#define SGTL5000_I2S_OUT_SEL_MASK 0x0003
+#define SGTL5000_I2S_OUT_SEL_SHIFT 0
+#define SGTL5000_I2S_OUT_SEL_WIDTH 2
+#define SGTL5000_I2S_OUT_SEL_ADC 0x0
+#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1
+#define SGTL5000_I2S_OUT_SEL_DAP 0x3
+
+/*
+ * SGTL5000_CHIP_ADCDAC_CTRL
+ */
+#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000
+#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000
+#define SGTL5000_DAC_VOL_RAMP_EN 0x0200
+#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100
+#define SGTL5000_DAC_MUTE_RIGHT 0x0008
+#define SGTL5000_DAC_MUTE_LEFT 0x0004
+#define SGTL5000_ADC_HPF_FREEZE 0x0002
+#define SGTL5000_ADC_HPF_BYPASS 0x0001
+
+/*
+ * SGTL5000_CHIP_DAC_VOL
+ */
+#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00
+#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8
+#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8
+#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff
+#define SGTL5000_DAC_VOL_LEFT_SHIFT 0
+#define SGTL5000_DAC_VOL_LEFT_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_PAD_STRENGTH
+ */
+#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300
+#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8
+#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0
+#define SGTL5000_PAD_I2S_SCLK_SHIFT 6
+#define SGTL5000_PAD_I2S_SCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030
+#define SGTL5000_PAD_I2S_DOUT_SHIFT 4
+#define SGTL5000_PAD_I2S_DOUT_WIDTH 2
+#define SGTL5000_PAD_I2C_SDA_MASK 0x000c
+#define SGTL5000_PAD_I2C_SDA_SHIFT 2
+#define SGTL5000_PAD_I2C_SDA_WIDTH 2
+#define SGTL5000_PAD_I2C_SCL_MASK 0x0003
+#define SGTL5000_PAD_I2C_SCL_SHIFT 0
+#define SGTL5000_PAD_I2C_SCL_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_ANA_ADC_CTRL
+ */
+#define SGTL5000_ADC_VOL_M6DB 0x0100
+#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0
+#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4
+#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4
+#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f
+#define SGTL5000_ADC_VOL_LEFT_SHIFT 0
+#define SGTL5000_ADC_VOL_LEFT_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_ANA_HP_CTRL
+ */
+#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00
+#define SGTL5000_HP_VOL_RIGHT_SHIFT 8
+#define SGTL5000_HP_VOL_RIGHT_WIDTH 7
+#define SGTL5000_HP_VOL_LEFT_MASK 0x007f
+#define SGTL5000_HP_VOL_LEFT_SHIFT 0
+#define SGTL5000_HP_VOL_LEFT_WIDTH 7
+
+/*
+ * SGTL5000_CHIP_ANA_CTRL
+ */
+#define SGTL5000_LINE_OUT_MUTE 0x0100
+#define SGTL5000_HP_SEL_MASK 0x0040
+#define SGTL5000_HP_SEL_SHIFT 6
+#define SGTL5000_HP_SEL_WIDTH 1
+#define SGTL5000_HP_SEL_DAC 0x0
+#define SGTL5000_HP_SEL_LINE_IN 0x1
+#define SGTL5000_HP_ZCD_EN 0x0020
+#define SGTL5000_HP_MUTE 0x0010
+#define SGTL5000_ADC_SEL_MASK 0x0004
+#define SGTL5000_ADC_SEL_SHIFT 2
+#define SGTL5000_ADC_SEL_WIDTH 1
+#define SGTL5000_ADC_SEL_MIC 0x0
+#define SGTL5000_ADC_SEL_LINE_IN 0x1
+#define SGTL5000_ADC_ZCD_EN 0x0002
+#define SGTL5000_ADC_MUTE 0x0001
+
+/*
+ * SGTL5000_CHIP_LINREG_CTRL
+ */
+#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040
+#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6
+#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1
+#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0
+#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1
+#define SGTL5000_VDDC_ASSN_OVRD 0x0020
+#define SGTL5000_LINREG_VDDD_MASK 0x000f
+#define SGTL5000_LINREG_VDDD_SHIFT 0
+#define SGTL5000_LINREG_VDDD_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_REF_CTRL
+ */
+#define SGTL5000_ANA_GND_MASK 0x01f0
+#define SGTL5000_ANA_GND_SHIFT 4
+#define SGTL5000_ANA_GND_WIDTH 5
+#define SGTL5000_ANA_GND_BASE 800 /* mv */
+#define SGTL5000_ANA_GND_STP 25 /*mv */
+#define SGTL5000_BIAS_CTRL_MASK 0x000e
+#define SGTL5000_BIAS_CTRL_SHIFT 1
+#define SGTL5000_BIAS_CTRL_WIDTH 3
+#define SGTL5000_SMALL_POP 0x0001
+
+/*
+ * SGTL5000_CHIP_MIC_CTRL
+ */
+#define SGTL5000_BIAS_R_MASK 0x0200
+#define SGTL5000_BIAS_R_SHIFT 8
+#define SGTL5000_BIAS_R_WIDTH 2
+#define SGTL5000_BIAS_R_off 0x0
+#define SGTL5000_BIAS_R_2K 0x1
+#define SGTL5000_BIAS_R_4k 0x2
+#define SGTL5000_BIAS_R_8k 0x3
+#define SGTL5000_BIAS_VOLT_MASK 0x0070
+#define SGTL5000_BIAS_VOLT_SHIFT 4
+#define SGTL5000_BIAS_VOLT_WIDTH 3
+#define SGTL5000_MIC_GAIN_MASK 0x0003
+#define SGTL5000_MIC_GAIN_SHIFT 0
+#define SGTL5000_MIC_GAIN_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_CTRL
+ */
+#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00
+#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8
+#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4
+#define SGTL5000_LINE_OUT_CURRENT_180u 0x0
+#define SGTL5000_LINE_OUT_CURRENT_270u 0x1
+#define SGTL5000_LINE_OUT_CURRENT_360u 0x3
+#define SGTL5000_LINE_OUT_CURRENT_450u 0x7
+#define SGTL5000_LINE_OUT_CURRENT_540u 0xf
+#define SGTL5000_LINE_OUT_GND_MASK 0x003f
+#define SGTL5000_LINE_OUT_GND_SHIFT 0
+#define SGTL5000_LINE_OUT_GND_WIDTH 6
+#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */
+#define SGTL5000_LINE_OUT_GND_STP 25
+#define SGTL5000_LINE_OUT_GND_MAX 0x23
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_VOL
+ */
+#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00
+#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8
+#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5
+#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f
+#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0
+#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5
+
+/*
+ * SGTL5000_CHIP_ANA_POWER
+ */
+#define SGTL5000_DAC_STEREO 0x4000
+#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
+#define SGTL5000_STARTUP_POWERUP 0x1000
+#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800
+#define SGTL5000_PLL_POWERUP 0x0400
+#define SGTL5000_LINEREG_D_POWERUP 0x0200
+#define SGTL5000_VCOAMP_POWERUP 0x0100
+#define SGTL5000_VAG_POWERUP 0x0080
+#define SGTL5000_ADC_STEREO 0x0040
+#define SGTL5000_REFTOP_POWERUP 0x0020
+#define SGTL5000_HP_POWERUP 0x0010
+#define SGTL5000_DAC_POWERUP 0x0008
+#define SGTL5000_CAPLESS_HP_POWERUP 0x0004
+#define SGTL5000_ADC_POWERUP 0x0002
+#define SGTL5000_LINE_OUT_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_PLL_CTRL
+ */
+#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_SHIFT 0
+#define SGTL5000_PLL_FRAC_DIV_WIDTH 11
+
+/*
+ * SGTL5000_CHIP_CLK_TOP_CTRL
+ */
+#define SGTL5000_INT_OSC_EN 0x0800
+#define SGTL5000_INPUT_FREQ_DIV2 0x0008
+
+/*
+ * SGTL5000_CHIP_ANA_STATUS
+ */
+#define SGTL5000_HP_LRSHORT 0x0200
+#define SGTL5000_CAPLESS_SHORT 0x0100
+#define SGTL5000_PLL_LOCKED 0x0010
+
+/*
+ * SGTL5000_CHIP_SHORT_CTRL
+ */
+#define SGTL5000_LVLADJR_MASK 0x7000
+#define SGTL5000_LVLADJR_SHIFT 12
+#define SGTL5000_LVLADJR_WIDTH 3
+#define SGTL5000_LVLADJL_MASK 0x0700
+#define SGTL5000_LVLADJL_SHIFT 8
+#define SGTL5000_LVLADJL_WIDTH 3
+#define SGTL5000_LVLADJC_MASK 0x0070
+#define SGTL5000_LVLADJC_SHIFT 4
+#define SGTL5000_LVLADJC_WIDTH 3
+#define SGTL5000_LR_SHORT_MOD_MASK 0x000c
+#define SGTL5000_LR_SHORT_MOD_SHIFT 2
+#define SGTL5000_LR_SHORT_MOD_WIDTH 2
+#define SGTL5000_CM_SHORT_MOD_MASK 0x0003
+#define SGTL5000_CM_SHORT_MOD_SHIFT 0
+#define SGTL5000_CM_SHORT_MOD_WIDTH 2
+
+/*
+ *SGTL5000_CHIP_ANA_TEST2
+ */
+#define SGTL5000_MONO_DAC 0x1000
+
+/*
+ * SGTL5000_DAP_CTRL
+ */
+#define SGTL5000_DAP_MIX_EN 0x0010
+#define SGTL5000_DAP_EN 0x0001
+
+#define SGTL5000_SYSCLK 0x00
+#define SGTL5000_LRCLK 0x01
+
+#endif
--
1.7.0.4
--hQiwHBbRI9kgIhsi--
1
0
Hi, I'm not sure if this is the right place to post this, so forgive me if
I'm making a mistake.
I'm trying to find where in the source of alsa kernel modules is the part
that enumerates the jacks (outputs) of the sound card.
This is because I have two headphone jacks and since I upgraded to 2.6.26,
when I plug anything to the 2nd headphone the first gets muted.
I think this is a jack sensing related bug, since the 1st jack is enumerated
as "Front" and the 2nd as "Headphone" when in reality they are both
"Headphone" 1 and 2.
If someone can give me a hint to where the code related with the enumeration
of the jack sensing is located, then I could make some tests myself. I just
want to save some work searching for it.
Regards,
paulo
2
1
At Wed, 12 Jan 2011 14:44:07 +0200,
Alexey Dobriyan wrote:
>
> On Wed, Jan 12, 2011 at 1:03 PM, Hanno Böck <hanno(a)hboeck.de> wrote:
> > struct snd_ac97_build_ops {
> > - int (*build_3d) (struct snd_ac97 *ac97);
> > - int (*build_specific) (struct snd_ac97 *ac97);
> > - int (*build_spdif) (struct snd_ac97 *ac97);
> > - int (*build_post_spdif) (struct snd_ac97 *ac97);
> > + int (* const build_3d) (struct snd_ac97 *ac97);
> > + int (* const build_specific) (struct snd_ac97 *ac97);
> > + int (* const build_spdif) (struct snd_ac97 *ac97);
> > + int (* const build_post_spdif) (struct snd_ac97 *ac97);
> > #ifdef CONFIG_PM
> > - void (*suspend) (struct snd_ac97 *ac97);
> > - void (*resume) (struct snd_ac97 *ac97);
> > + void (* const suspend) (struct snd_ac97 *ac97);
> > + void (* const resume) (struct snd_ac97 *ac97);
> > #endif
> > - void (*update_jacks) (struct snd_ac97 *ac97); /* for jack-sharing */
> > + /* for jack-sharing */
> > + void (* const update_jacks) (struct snd_ac97 *ac97);
>
> Can we please not do this.
>
> You made whole ops structure RO, this is enough.
Yes. Hanno, could you fix and resubmit?
thanks,
Takashi
2
2