Merge "ASoC: WCD9XXX: Add support for class H PA controller"
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 4ea7013..6c60e04 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -59,12 +59,12 @@
};
static char *taiko_supplies[] = {
- "cdc-vdd-buck", "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
+ WCD9XXX_SUPPLY_BUCK_NAME, "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
"cdc-vdd-a-1p2v", "cdc-vddcx-1", "cdc-vddcx-2",
};
static char *tapan_supplies[] = {
- "cdc-vdd-buck", "cdc-vdd-h", "cdc-vdd-px",
+ WCD9XXX_SUPPLY_BUCK_NAME, "cdc-vdd-h", "cdc-vdd-px",
"cdc-vdd-a-1p2v", "cdc-vdd-cx"
};
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 3ebf091..aed549e 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -29,6 +29,8 @@
(((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0)
#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0)
+#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck"
+
#define SITAR_VERSION_1P0 0
#define SITAR_VERSION_1P1 1
#define SITAR_IS_1P0(ver) \
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index 9c44e8b..412341a 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -196,4 +196,85 @@
#define WCD9XXX_A_MAD_ANA_CTRL (0x150)
#define WCD9XXX_A_MAD_ANA_CTRL__POR (0xF1)
+
+#define WCD9XXX_A_CDC_CLK_OTHR_CTL (0x30C)
+#define WCD9XXX_A_CDC_CLK_OTHR_CTL__POR (0x00)
+
+/* Class H related common registers */
+#define WCD9XXX_A_BUCK_MODE_1 (0x181)
+#define WCD9XXX_A_BUCK_MODE_1__POR (0x21)
+#define WCD9XXX_A_BUCK_MODE_2 (0x182)
+#define WCD9XXX_A_BUCK_MODE_2__POR (0xFF)
+#define WCD9XXX_A_BUCK_MODE_3 (0x183)
+#define WCD9XXX_A_BUCK_MODE_3__POR (0xCC)
+#define WCD9XXX_A_BUCK_MODE_4 (0x184)
+#define WCD9XXX_A_BUCK_MODE_4__POR (0x3A)
+#define WCD9XXX_A_BUCK_MODE_5 (0x185)
+#define WCD9XXX_A_BUCK_MODE_5__POR (0x00)
+#define WCD9XXX_A_BUCK_CTRL_VCL_1 (0x186)
+#define WCD9XXX_A_BUCK_CTRL_VCL_1__POR (0x48)
+#define WCD9XXX_A_BUCK_CTRL_VCL_2 (0x187)
+#define WCD9XXX_A_BUCK_CTRL_VCL_2__POR (0xA3)
+#define WCD9XXX_A_BUCK_CTRL_VCL_3 (0x188)
+#define WCD9XXX_A_BUCK_CTRL_VCL_3__POR (0x82)
+#define WCD9XXX_A_BUCK_CTRL_CCL_1 (0x189)
+#define WCD9XXX_A_BUCK_CTRL_CCL_1__POR (0xAB)
+#define WCD9XXX_A_BUCK_CTRL_CCL_2 (0x18A)
+#define WCD9XXX_A_BUCK_CTRL_CCL_2__POR (0xDC)
+#define WCD9XXX_A_BUCK_CTRL_CCL_3 (0x18B)
+#define WCD9XXX_A_BUCK_CTRL_CCL_3__POR (0x6A)
+#define WCD9XXX_A_BUCK_CTRL_CCL_4 (0x18C)
+#define WCD9XXX_A_BUCK_CTRL_CCL_4__POR (0x58)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1 (0x18D)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2 (0x18E)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3 (0x18F)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77)
+#define WCD9XXX_A_BUCK_TMUX_A_D (0x190)
+#define WCD9XXX_A_BUCK_TMUX_A_D__POR (0x00)
+#define WCD9XXX_A_NCP_EN (0x192)
+#define WCD9XXX_A_NCP_EN__POR (0xFE)
+#define WCD9XXX_A_NCP_STATIC (0x194)
+#define WCD9XXX_A_NCP_STATIC__POR (0x28)
+#define WCD9XXX_A_NCP_BUCKREF (0x191)
+#define WCD9XXX_A_NCP_BUCKREF__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_B1_CTL (0x320)
+#define WCD9XXX_A_CDC_CLSH_B1_CTL__POR (0xE4)
+#define WCD9XXX_A_CDC_CLSH_B2_CTL (0x321)
+#define WCD9XXX_A_CDC_CLSH_B2_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_B3_CTL (0x322)
+#define WCD9XXX_A_CDC_CLSH_B3_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS (0x323)
+#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD (0x324)
+#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12)
+#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD (0x325)
+#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23)
+#define WCD9XXX_A_CDC_CLSH_K_ADDR (0x328)
+#define WCD9XXX_A_CDC_CLSH_K_ADDR__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_K_DATA (0x329)
+#define WCD9XXX_A_CDC_CLSH_K_DATA__POR (0xA4)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR (0x32E)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH (0x32F)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR (0x330)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH (0x331)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00)
+
+
#endif
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0f14dc3..a09dab3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o wcd9xxx-common.o
snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o
snd-soc-msm8x10-wcd-objs := msm8x10-wcd.o msm8x10-wcd-tables.o
snd-soc-wl1273-objs := wl1273.o
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 1746502..6f601c1 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -36,6 +36,7 @@
#include <linux/gpio.h>
#include "wcd9320.h"
#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
static atomic_t kp_taiko_priv;
static int spkr_drv_wrnd_param_set(const char *val,
@@ -69,6 +70,7 @@
#define TAIKO_SLIM_IRQ_PORT_CLOSED (1 << 2)
#define TAIKO_MCLK_CLK_12P288MHZ 12288000
#define TAIKO_MCLK_CLK_9P6HZ 9600000
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -233,6 +235,10 @@
struct wcd9xxx_resmgr resmgr;
/* mbhc module */
struct wcd9xxx_mbhc mbhc;
+
+ /* class h specific data */
+ struct wcd9xxx_clsh_cdc_data clsh_d;
+
};
static const u32 comp_shift[] = {
@@ -354,73 +360,6 @@
return 0;
}
-static int taiko_codec_enable_class_h_clk(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
-
- pr_debug("%s %s %d\n", __func__, w->name, event);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLSH_B1_CTL, 0x01, 0x01);
- break;
- case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x80, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLSH_B1_CTL, 0x01, 0x00);
- break;
- }
- return 0;
-}
-
-static int taiko_codec_enable_class_h(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
-
- pr_debug("%s %s %d\n", __func__, w->name, event);
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x02);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_4, 0xFF, 0xFF);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x04, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x80, 0x80);
- usleep_range(1000, 1000);
- break;
- }
- return 0;
-}
-
-static int taiko_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
-
- pr_debug("%s %s %d\n", __func__, w->name, event);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, w->reg, 0x01, 0x01);
- snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
- break;
-
- case SND_SOC_DAPM_POST_PMU:
- usleep_range(1000, 1000);
- break;
-
- case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, w->reg, 0x01, 0x00);
- snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
- break;
- }
- return 0;
-}
-
-
static int taiko_get_anc_slot(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -905,6 +844,17 @@
static const struct soc_enum cf_rxmix7_enum =
SOC_ENUM_SINGLE(TAIKO_A_CDC_RX7_B4_CTL, 1, 3, cf_text);
+static const char * const class_h_dsm_text[] = {
+ "ZERO", "DSM_HPHL_RX1", "DSM_SPKR_RX7"
+};
+
+static const struct soc_enum class_h_dsm_enum =
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_CONN_CLSH_CTL, 4, 3, class_h_dsm_text);
+
+static const struct snd_kcontrol_new class_h_dsm_mux =
+ SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum);
+
+
static const struct snd_kcontrol_new taiko_snd_controls[] = {
SOC_ENUM_EXT("EAR PA Gain", taiko_ear_pa_gain_enum[0],
@@ -1842,11 +1792,12 @@
if (enable) {
taiko->adc_count++;
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x2, 0x2);
} else {
taiko->adc_count--;
if (!taiko->adc_count)
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_CTL,
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
0x2, 0x0);
}
}
@@ -1934,6 +1885,7 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u16 lineout_gain_reg;
pr_debug("%s %d %s\n", __func__, event, w->name);
@@ -1962,11 +1914,19 @@
snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40);
break;
case SND_SOC_DAPM_POST_PMU:
- pr_debug("%s: sleeping 16 ms after %s PA turn on\n",
+ wcd9xxx_clsh_fsm(codec, &taiko->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ pr_debug("%s: sleeping 3 ms after %s PA turn on\n",
__func__, w->name);
- usleep_range(16000, 16000);
+ usleep_range(3000, 3000);
break;
case SND_SOC_DAPM_POST_PMD:
+ wcd9xxx_clsh_fsm(codec, &taiko->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x00);
break;
}
@@ -2515,18 +2475,52 @@
}
return 0;
}
-static int taiko_hphr_dac_event(struct snd_soc_dapm_widget *w,
+
+static int taiko_hphl_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %s %d\n", __func__, w->name, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
+ 0x02, 0x02);
+ wcd9xxx_clsh_fsm(codec, &taiko_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHL,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
break;
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
+ 0x02, 0x00);
+ }
+ return 0;
+}
+
+static int taiko_hphr_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+ wcd9xxx_clsh_fsm(codec, &taiko_p->clsh_d,
+ WCD9XXX_CLSH_STATE_HPHR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
+ 0x04, 0x00);
snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
break;
}
@@ -2539,14 +2533,17 @@
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ u8 req_clsh_state;
pr_debug("%s: %s event = %d\n", __func__, w->name, event);
if (w->shift == 5) {
e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+ req_clsh_state = WCD9XXX_CLSH_STATE_HPHL;
} else if (w->shift == 4) {
e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+ req_clsh_state = WCD9XXX_CLSH_STATE_HPHR;
} else {
pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
return -EINVAL;
@@ -2559,29 +2556,27 @@
break;
case SND_SOC_DAPM_POST_PMU:
- usleep_range(10000, 10000);
+ wcd9xxx_clsh_fsm(codec, &taiko->clsh_d,
+ req_clsh_state,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x20, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
- usleep_range(10, 10);
+ usleep_range(5000, 5000);
break;
case SND_SOC_DAPM_POST_PMD:
/* Let MBHC module know PA turned off */
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
- /*
- * schedule work is required because at the time HPH PA DAPM
- * event callback is called by DAPM framework, CODEC dapm mutex
- * would have been locked while snd_soc_jack_report also
- * attempts to acquire same lock.
- */
+ wcd9xxx_clsh_fsm(codec, &taiko->clsh_d,
+ req_clsh_state,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+
pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
w->name);
- usleep_range(10000, 10000);
+ usleep_range(5000, 5000);
break;
}
return 0;
@@ -2598,11 +2593,16 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %s %d\n", __func__, w->name, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ wcd9xxx_clsh_fsm(codec, &taiko->clsh_d,
+ WCD9XXX_CLSH_STATE_LO,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
break;
@@ -2757,15 +2757,11 @@
{"SLIM TX10 MUX", "DEC9", "DEC9 MUX"},
{"SLIM TX10 MUX", "DEC10", "DEC10 MUX"},
- /* Change Pump */
- {"CP", NULL, "CLASS_H_CLK"},
-
/* Earpiece (RX MIX1) */
{"EAR", NULL, "EAR PA"},
{"EAR PA", NULL, "EAR_PA_MIXER"},
{"EAR_PA_MIXER", NULL, "DAC1"},
- {"DAC1", NULL, "CLASS_H_EAR"},
- {"CLASS_H_EAR", NULL, "CP"},
+ {"DAC1", NULL, "RX_BIAS"},
{"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"},
{"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"},
@@ -2777,15 +2773,11 @@
{"HPHL", NULL, "HPHL_PA_MIXER"},
{"HPHL_PA_MIXER", NULL, "HPHL DAC"},
+ {"HPHL DAC", NULL, "RX_BIAS"},
{"HPHR", NULL, "HPHR_PA_MIXER"},
{"HPHR_PA_MIXER", NULL, "HPHR DAC"},
-
- {"HPHL DAC", NULL, "CLASS_H_HPH_L"},
- {"CLASS_H_HPH_L", NULL, "CP"},
-
- {"HPHR DAC", NULL, "CLASS_H_HPH_R"},
- {"CLASS_H_HPH_R", NULL, "CP"},
+ {"HPHR DAC", NULL, "RX_BIAS"},
{"ANC", NULL, "ANC1 MUX"},
{"ANC", NULL, "ANC2 MUX"},
@@ -2800,8 +2792,8 @@
{"ANC", NULL, "CDC_CONN"},
- {"DAC1", "Switch", "RX1 CHAIN"},
- {"HPHL DAC", "Switch", "RX1 CHAIN"},
+ {"DAC1", "Switch", "CLASS_H_DSM MUX"},
+ {"HPHL DAC", "Switch", "CLASS_H_DSM MUX"},
{"HPHR DAC", NULL, "RX2 CHAIN"},
{"LINEOUT1", NULL, "LINEOUT1 PA"},
@@ -2810,30 +2802,20 @@
{"LINEOUT4", NULL, "LINEOUT4 PA"},
{"SPK_OUT", NULL, "SPK PA"},
- {"LINEOUT1 PA", NULL, "CP"},
{"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
{"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
- {"LINEOUT2 PA", NULL, "CP"},
{"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
{"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
- {"LINEOUT3 PA", NULL, "CP"},
{"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"},
{"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"},
- {"LINEOUT4 PA", NULL, "CP"},
{"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"},
{"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"},
- {"CP", NULL, "CLASS_H_LINEOUTS_PA"},
- {"CLASS_H_LINEOUTS_PA", NULL, "CLASS_H_CLK"},
-
-
-
{"LINEOUT1 DAC", NULL, "RX3 MIX1"},
-
{"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"},
{"RDAC5 MUX", "DEM4", "RX4 MIX1"},
@@ -2850,12 +2832,13 @@
{"SPK DAC", NULL, "RX7 MIX2"},
{"SPK DAC", NULL, "VDD_SPKDRV"},
+ {"CLASS_H_DSM MUX", "DSM_HPHL_RX1", "RX1 CHAIN"},
+
{"RX1 CHAIN", NULL, "RX1 MIX2"},
{"RX2 CHAIN", NULL, "RX2 MIX2"},
{"RX1 CHAIN", NULL, "ANC"},
{"RX2 CHAIN", NULL, "ANC"},
- {"CLASS_H_CLK", NULL, "RX_BIAS"},
{"LINEOUT1 DAC", NULL, "RX_BIAS"},
{"LINEOUT2 DAC", NULL, "RX_BIAS"},
{"LINEOUT3 DAC", NULL, "RX_BIAS"},
@@ -3986,24 +3969,81 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %s %d\n", __func__, w->name, event);
switch (event) {
- break;
case SND_SOC_DAPM_POST_PMU:
-
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x20, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
+ wcd9xxx_clsh_fsm(codec, &taiko_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
usleep_range(5000, 5000);
break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd9xxx_clsh_fsm(codec, &taiko_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_DISABLE,
+ WCD9XXX_CLSH_EVENT_POST_PA);
+ usleep_range(5000, 5000);
+ }
+ return 0;
+}
+
+static int taiko_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %s %d\n", __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd9xxx_clsh_fsm(codec, &taiko_p->clsh_d,
+ WCD9XXX_CLSH_STATE_EAR,
+ WCD9XXX_CLSH_REQ_ENABLE,
+ WCD9XXX_CLSH_EVENT_PRE_DAC);
+ break;
+ }
+
+ return 0;
+}
+
+static int taiko_codec_dsm_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u8 reg_val, zoh_mux_val = 0x00;
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ reg_val = snd_soc_read(codec, TAIKO_A_CDC_CONN_CLSH_CTL);
+
+ if ((reg_val & 0x30) == 0x10)
+ zoh_mux_val = 0x04;
+ else if ((reg_val & 0x30) == 0x20)
+ zoh_mux_val = 0x08;
+
+ if (zoh_mux_val != 0x00)
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_CONN_CLSH_CTL,
+ 0x0C, zoh_mux_val);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CONN_CLSH_CTL,
+ 0x0C, 0x00);
+ break;
}
return 0;
}
+
/* Todo: Have seperate dapm widgets for I2S and Slimbus.
* Might Need to have callbacks registered only for slimbus
*/
@@ -4012,10 +4052,12 @@
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_PGA_E("EAR PA", TAIKO_A_RX_EAR_EN, 4, 0, NULL, 0,
- taiko_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU),
+ taiko_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MIXER("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
- ARRAY_SIZE(dac1_switch)),
+ SND_SOC_DAPM_MIXER_E("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
+ ARRAY_SIZE(dac1_switch), taiko_codec_ear_dac_event,
+ SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
AIF1_PB, 0, taiko_codec_enable_slimrx,
@@ -4055,8 +4097,9 @@
SND_SOC_DAPM_PGA_E("HPHL", TAIKO_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
taiko_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MIXER("HPHL DAC", TAIKO_A_RX_HPH_L_DAC_CTL, 7, 0,
- hphl_switch, ARRAY_SIZE(hphl_switch)),
+ SND_SOC_DAPM_MIXER_E("HPHL DAC", TAIKO_A_RX_HPH_L_DAC_CTL, 7, 0,
+ hphl_switch, ARRAY_SIZE(hphl_switch), taiko_hphl_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("HPHR", TAIKO_A_RX_HPH_CNP_EN, 4, 0, NULL, 0,
taiko_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
@@ -4192,36 +4235,20 @@
SND_SOC_DAPM_MUX("RDAC7 MUX", SND_SOC_NOPM, 0, 0,
&rx_dac7_mux),
- SND_SOC_DAPM_SUPPLY("CLASS_H_CLK", TAIKO_A_CDC_CLK_OTHR_CTL, 0, 0,
- taiko_codec_enable_class_h_clk, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_PRE_PMD),
-
- SND_SOC_DAPM_SUPPLY("CLASS_H_EAR", TAIKO_A_CDC_CLSH_B1_CTL, 4, 0,
- taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
-
- SND_SOC_DAPM_SUPPLY("CLASS_H_HPH_L", TAIKO_A_CDC_CLSH_B1_CTL, 3, 0,
- taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
-
- SND_SOC_DAPM_SUPPLY("CLASS_H_HPH_R", TAIKO_A_CDC_CLSH_B1_CTL, 2, 0,
- taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
-
- SND_SOC_DAPM_SUPPLY("CLASS_H_LINEOUTS_PA", SND_SOC_NOPM, 0, 0,
- taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
-
- SND_SOC_DAPM_SUPPLY("CP", TAIKO_A_NCP_EN, 0, 0,
- taiko_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("CLASS_H_DSM MUX", SND_SOC_NOPM, 0, 0,
+ &class_h_dsm_mux, taiko_codec_dsm_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
taiko_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("CDC_I2S_RX_CONN", TAIKO_A_CDC_CLK_OTHR_CTL, 5, 0,
+ SND_SOC_DAPM_SUPPLY("CDC_I2S_RX_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 5, 0,
NULL, 0),
/* TX */
- SND_SOC_DAPM_SUPPLY("CDC_CONN", TAIKO_A_CDC_CLK_OTHR_CTL, 2, 0, NULL,
+ SND_SOC_DAPM_SUPPLY("CDC_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 2, 0, NULL,
0),
SND_SOC_DAPM_SUPPLY("LDO_H", TAIKO_A_LDO_H_MODE_1, 7, 0,
@@ -4540,84 +4567,6 @@
return IRQ_HANDLED;
}
-static const struct taiko_reg_mask_val taiko_1_0_class_h_ear[] = {
-
- /* CLASS-H EAR IDLE_THRESHOLD Table */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_IDLE_EAR_THSD, 0x26),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD, 0x2C),
-
- /* CLASS-H EAR I_PA_FACT Table. */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xA9),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U, 0x07),
-
- /* CLASS-H EAR Voltage Headroom , Voltage Min. */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_V_PA_HD_EAR, 0x0D),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_V_PA_MIN_EAR, 0x3A),
-
- /* CLASS-H EAR K values --chnages from load. */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_ADDR, 0x08),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x1B),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x2D),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x36),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x37),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- /** end of Ear PA load 32 */
-};
-
-static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
-
- /* CLASS-H HPH IDLE_THRESHOLD Table */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_IDLE_HPH_THSD, 0x13),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x19),
-
- /* CLASS-H HPH I_PA_FACT Table */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L, 0x9A),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U, 0x06),
-
- /* CLASS-H HPH Voltage Headroom , Voltage Min */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_V_PA_HD_HPH, 0x0D),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_V_PA_MIN_HPH, 0x1D),
-
- /* CLASS-H HPH K values --chnages from load .*/
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_ADDR, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0xAE),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x01),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x1C),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x25),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x27),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_K_DATA, 0x00),
-};
-
-static int taiko_config_ear_class_h(struct snd_soc_codec *codec, u32 ear_load)
-{
- u32 i;
-
- if (ear_load != 32)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(taiko_1_0_class_h_ear); i++)
- snd_soc_write(codec, taiko_1_0_class_h_ear[i].reg,
- taiko_1_0_class_h_ear[i].val);
- return 0;
-}
-
-static int taiko_config_hph_class_h(struct snd_soc_codec *codec, u32 hph_load)
-{
- u32 i;
- if (hph_load != 16)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(taiko_1_0_class_h_hph); i++)
- snd_soc_write(codec, taiko_1_0_class_h_hph[i].reg,
- taiko_1_0_class_h_hph[i].val);
- return 0;
-}
-
static int taiko_handle_pdata(struct taiko_priv *taiko)
{
struct snd_soc_codec *codec = taiko->codec;
@@ -4756,14 +4705,11 @@
0x00 : 0x16);
snd_soc_update_bits(codec, TAIKO_A_MICB_4_CTL, 0x1E, value);
- taiko_config_ear_class_h(codec, 32);
- taiko_config_hph_class_h(codec, 16);
-
done:
return rc;
}
-static const struct taiko_reg_mask_val taiko_reg_defaults[] = {
+static const struct wcd9xxx_reg_mask_val taiko_reg_defaults[] = {
/* set MCLk to 9.6 */
TAIKO_REG_VAL(TAIKO_A_CHIP_CTL, 0x02),
@@ -4772,24 +4718,6 @@
/* EAR PA deafults */
TAIKO_REG_VAL(TAIKO_A_RX_EAR_CMBUFF, 0x05),
- /* BUCK and NCP defaults for EAR and HS */
- TAIKO_REG_VAL(TAIKO_A_BUCK_CTRL_CCL_1, 0x5B),
-
- /* CLASS-H defaults for EAR and HS */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_BUCK_NCP_VARS, 0x00),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_BUCK_NCP_VARS, 0x04),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B2_CTL, 0x01),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B2_CTL, 0x05),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B2_CTL, 0x35),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B3_CTL, 0x30),
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B3_CTL, 0x3B),
-
- /*
- * For CLASS-H, Enable ANC delay buffer,
- * set HPHL and EAR PA ref gain to 0 DB.
- */
- TAIKO_REG_VAL(TAIKO_A_CDC_CLSH_B1_CTL, 0x26),
-
/* RX deafults */
TAIKO_REG_VAL(TAIKO_A_CDC_RX1_B5_CTL, 0x78),
TAIKO_REG_VAL(TAIKO_A_CDC_RX2_B5_CTL, 0x78),
@@ -4811,25 +4739,31 @@
TAIKO_REG_VAL(TAIKO_A_CDC_RX7_B6_CTL, 0x80),
};
-static const struct taiko_reg_mask_val taiko_1_0_reg_defaults[] = {
+static const struct wcd9xxx_reg_mask_val taiko_1_0_reg_defaults[] = {
/*
* The following only need to be written for Taiko 1.0 parts.
* Taiko 2.0 will have appropriate defaults for these registers.
*/
/* BUCK default */
- TAIKO_REG_VAL(TAIKO_A_BUCK_CTRL_CCL_4, 0x50),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_CCL_4, 0x50),
+
+ /* Required defaults for class H operation */
+ TAIKO_REG_VAL(TAIKO_A_RX_HPH_CHOP_CTL, 0xF4),
+ TAIKO_REG_VAL(TAIKO_A_BIAS_CURR_CTL_2, 0x08),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_CCL_1, 0x5B),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_CCL_3, 0x60),
/* Choose max non-overlap time for NCP */
TAIKO_REG_VAL(TAIKO_A_NCP_CLK, 0xFC),
/* Use 25mV/50mV for deltap/m to reduce ripple */
- TAIKO_REG_VAL(TAIKO_A_BUCK_CTRL_VCL_1, 0x08),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_VCL_1, 0x08),
/*
* Set DISABLE_MODE_SEL<1:0> to 0b10 (disable PWM in auto mode).
* Note that the other bits of this register will be changed during
* Rx PA bring up.
*/
- TAIKO_REG_VAL(TAIKO_A_BUCK_MODE_3, 0xCE),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE),
/* Reduce HPH DAC bias to 70% */
TAIKO_REG_VAL(TAIKO_A_RX_HPH_BIAS_PA, 0x7A),
/*Reduce EAR DAC bias to 70% */
@@ -4858,7 +4792,7 @@
* Don't update TAIKO_A_CHIP_CTL, TAIKO_A_BUCK_CTRL_CCL_1 and
* TAIKO_A_RX_EAR_CMBUFF as those are updated in taiko_reg_defaults
*/
-static const struct taiko_reg_mask_val taiko_2_0_reg_defaults[] = {
+static const struct wcd9xxx_reg_mask_val taiko_2_0_reg_defaults[] = {
TAIKO_REG_VAL(TAIKO_A_CDC_TX_1_GAIN, 0x2),
TAIKO_REG_VAL(TAIKO_A_CDC_TX_2_GAIN, 0x2),
TAIKO_REG_VAL(TAIKO_A_CDC_TX_1_2_ADC_IB, 0x44),
@@ -4868,9 +4802,9 @@
TAIKO_REG_VAL(TAIKO_A_CDC_TX_5_GAIN, 0x2),
TAIKO_REG_VAL(TAIKO_A_CDC_TX_6_GAIN, 0x2),
TAIKO_REG_VAL(TAIKO_A_CDC_TX_5_6_ADC_IB, 0x44),
- TAIKO_REG_VAL(TAIKO_A_BUCK_MODE_3, 0xCE),
- TAIKO_REG_VAL(TAIKO_A_BUCK_CTRL_VCL_1, 0x8),
- TAIKO_REG_VAL(TAIKO_A_BUCK_CTRL_CCL_4, 0x51),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_VCL_1, 0x8),
+ TAIKO_REG_VAL(WCD9XXX_A_BUCK_CTRL_CCL_4, 0x51),
TAIKO_REG_VAL(TAIKO_A_NCP_DTEST, 0x10),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_CHOP_CTL, 0xA4),
TAIKO_REG_VAL(TAIKO_A_RX_HPH_BIAS_PA, 0x7A),
@@ -4931,7 +4865,7 @@
}
}
-static const struct taiko_reg_mask_val taiko_codec_reg_init_val[] = {
+static const struct wcd9xxx_reg_mask_val taiko_codec_reg_init_val[] = {
/* Initialize current threshold to 350MA
* number of wait and run cycles to 4096
*/
@@ -4949,9 +4883,6 @@
{TAIKO_A_RX_LINE_4_GAIN, 0x20, 0x20},
{TAIKO_A_SPKR_DRV_GAIN, 0x04, 0x04},
- /* CLASS H config */
- {TAIKO_A_CDC_CONN_CLSH_CTL, 0x3C, 0x14},
-
/* Use 16 bit sample size for TX1 to TX6 */
{TAIKO_A_CDC_CONN_TX_SB_B1_CTL, 0x30, 0x20},
{TAIKO_A_CDC_CONN_TX_SB_B2_CTL, 0x30, 0x20},
@@ -5101,6 +5032,24 @@
return 0;
}
+static int taiko_codec_get_buck_mv(struct snd_soc_codec *codec)
+{
+ int buck_volt = WCD9XXX_CDC_BUCK_UNSUPPORTED;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+ if (!strncmp(pdata->regulator[i].name,
+ WCD9XXX_SUPPLY_BUCK_NAME,
+ sizeof(WCD9XXX_SUPPLY_BUCK_NAME))) {
+ buck_volt = pdata->regulator[i].min_uV;
+ break;
+ }
+ }
+ return buck_volt;
+}
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -5132,8 +5081,10 @@
tx_hpf_corner_freq_callback);
}
+
snd_soc_codec_set_drvdata(codec, taiko);
+
/* codec resmgr module init */
wcd9xxx = codec->control_data;
pdata = dev_get_platdata(codec->dev->parent);
@@ -5144,6 +5095,9 @@
return ret;
}
+ taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
+ wcd9xxx_clsh_init(&taiko->clsh_d);
+
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
if (ret) {
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index aea31db..89a0b9f 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -41,12 +41,6 @@
TAIKO_PID_MIC_20_UA,
};
-struct taiko_reg_mask_val {
- u16 reg;
- u8 mask;
- u8 val;
-};
-
enum taiko_mbhc_analog_pwr_cfg {
TAIKO_ANALOG_PWR_COLLAPSED = 0,
TAIKO_ANALOG_PWR_ON,
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
new file mode 100644
index 0000000..dbf2e39
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -0,0 +1,591 @@
+/* Copyright (c) 2013, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include "wcd9xxx-common.h"
+
+#define CLSH_COMPUTE_EAR 0x01
+#define CLSH_COMPUTE_HPH_L 0x02
+#define CLSH_COMPUTE_HPH_R 0x03
+
+#define BUCK_VREF_2V 0xFF
+#define BUCK_VREF_1P8V 0xE6
+
+#define NCP_FCLK_LEVEL_8 0x08
+#define NCP_FCLK_LEVEL_5 0x05
+
+#define BUCK_SETTLE_TIME_US 50
+#define NCP_SETTLE_TIME_US 50
+
+static inline void wcd9xxx_enable_clsh_block(
+ struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+ 0x01, on ? 0x01 : 0x00);
+}
+
+static inline void wcd9xxx_enable_anc_delay(
+ struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+ 0x02, on ? 0x02 : 0x00);
+}
+
+static inline void wcd9xxx_enable_ncp(
+ struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
+ 0x01, on ? 0x01 : 0x00);
+}
+
+static inline void wcd9xxx_enable_buck(
+ struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x80, on ? 0x80 : 0x00);
+}
+
+static int cdc_lo_count;
+
+static void (*clsh_state_fp[NUM_CLSH_STATES])
+ (struct snd_soc_codec *,
+ struct wcd9xxx_clsh_cdc_data *,
+ u8 req_state, bool req_type);
+
+static const char *state_to_str(u8 state)
+{
+ if (state == WCD9XXX_CLSH_STATE_IDLE)
+ return "STATE_IDLE";
+ else if (state == WCD9XXX_CLSH_STATE_EAR)
+ return "STATE_EAR";
+ else if (state == WCD9XXX_CLSH_STATE_HPHL)
+ return "STATE_HPH_L";
+ else if (state == WCD9XXX_CLSH_STATE_HPHR)
+ return "STATE_HPH_R";
+ else if (state == (WCD9XXX_CLSH_STATE_HPHL
+ | WCD9XXX_CLSH_STATE_HPHR))
+ return "STATE_HPH_L_R";
+ else if (state == WCD9XXX_CLSH_STATE_LO)
+ return "STATE_LO";
+
+ return "UNKNOWN_STATE";
+}
+
+static void wcd9xxx_cfg_clsh_buck(
+ struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_CTRL_CCL_4, 0x0B, 0x00},
+ {WCD9XXX_A_BUCK_CTRL_CCL_1, 0xF0, 0x50},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0x03, 0x00},
+ {WCD9XXX_A_BUCK_CTRL_CCL_3, 0x0B, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Programmed buck parameters", __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_common(
+ struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 0, 0},
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 2, 1 << 2},
+ {WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, (0x1 << 4), 0},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 0), 0x01},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 2), (0x01 << 2)},
+ {WCD9XXX_A_CDC_CLSH_B2_CTL, (0xf << 4), (0x03 << 4)},
+ {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 4), (0x03 << 4)},
+ {WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 0), (0x0B)},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 5), (0x01 << 5)},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 1), (0x01 << 1)},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Programmed class H controller common parameters",
+ __func__);
+}
+
+static void wcd9xxx_chargepump_request(
+ struct snd_soc_codec *codec, bool on)
+{
+ static int cp_count;
+
+ if (on && (++cp_count == 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x01, 0x01);
+ dev_info(codec->dev, "%s: Charge Pump enabled, count = %d\n",
+ __func__, cp_count);
+ }
+
+ else if (!on) {
+ if (--cp_count < 0) {
+ dev_dbg(codec->dev, "%s: Unbalanced disable for charge pump\n",
+ __func__);
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL)
+ & 0x01) {
+ dev_info(codec->dev, "%s: Actual chargepump is ON\n",
+ __func__);
+ }
+ cp_count = 0;
+ WARN_ON(1);
+ }
+
+ if (cp_count == 0) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+ 0x01, 0x00);
+ dev_dbg(codec->dev, "%s: Charge pump disabled, count = %d\n",
+ __func__, cp_count);
+ }
+ }
+}
+
+static inline void wcd9xxx_clsh_computation_request(
+ struct snd_soc_codec *codec, int compute_pa, bool on)
+{
+ u8 reg_val, reg_mask;
+
+ switch (compute_pa) {
+ case CLSH_COMPUTE_EAR:
+ reg_mask = 0x10;
+ reg_val = (on ? 0x10 : 0x00);
+ break;
+ case CLSH_COMPUTE_HPH_L:
+ reg_mask = 0x08;
+ reg_val = (on ? 0x08 : 0x00);
+ break;
+ case CLSH_COMPUTE_HPH_R:
+ reg_mask = 0x04;
+ reg_val = (on ? 0x04 : 0x00);
+ break;
+ default:
+ dev_dbg(codec->dev, "%s: class h computation PA request incorrect\n",
+ __func__);
+ return;
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+ reg_mask, reg_val);
+
+}
+
+static void wcd9xxx_enable_buck_mode(struct snd_soc_codec *codec,
+ u8 buck_vref)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x03},
+ {WCD9XXX_A_BUCK_MODE_4, 0xFF, buck_vref},
+ {WCD9XXX_A_BUCK_MODE_1, 0x04, 0x04},
+ {WCD9XXX_A_BUCK_MODE_1, 0x08, 0x00},
+ {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x00},
+ {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x00},
+ {WCD9XXX_A_BUCK_MODE_1, 0x80, 0x80},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US);
+}
+
+static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00},
+ {WCD9XXX_A_NCP_STATIC, 0x20, 0x00},
+ {WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04},
+ {WCD9XXX_A_BUCK_MODE_3, 0x08, 0x08},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n",
+ __func__);
+
+}
+
+static void wcd9xxx_set_fclk_enable_ncp(struct snd_soc_codec *codec,
+ u8 fclk_level)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_NCP_STATIC, 0x20, 0x20},
+ {WCD9XXX_A_NCP_EN, 0x01, 0x01},
+ };
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x010, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x0F, fclk_level);
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US);
+
+ dev_dbg(codec->dev, "%s: set ncp done\n", __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_ear(struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR, (0x3f << 0), 0x0D},
+ {WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR, (0x3f << 0), 0x3A},
+
+ /* Under assumption that EAR load is 10.7ohm */
+ {WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD, (0x3f << 0), 0x26},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD, (0x3f << 0), 0x2C},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xff, 0xA9},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U, 0xff, 0x07},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0xf << 0), 0x08},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1b},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2d},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x36},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ dev_dbg(codec->dev, "%s: Programmed Class H controller EAR specific params\n",
+ __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_hph(struct snd_soc_codec *codec)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 6), 0},
+ {WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH, 0x3f, 0x0D},
+ {WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH, 0x3f, 0x1D},
+
+ /* Under assumption that HPH load is 16ohm per channel */
+ {WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0x3f, 0x13},
+ {WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x1f, 0x19},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+ {WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+ {WCD9XXX_A_CDC_CLSH_K_ADDR, 0x0f, 0},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+ {WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+ dev_dbg(codec->dev, "%s: Programmed Class H controller HPH specific params\n",
+ __func__);
+}
+
+static void wcd9xxx_clsh_turnoff_postpa
+ (struct snd_soc_codec *codec)
+{
+
+ int i;
+
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_NCP_EN, 0x01, 0x00},
+ {WCD9XXX_A_BUCK_MODE_1, 0x80, 0x00},
+ {WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10, 0x00},
+ };
+
+ wcd9xxx_chargepump_request(codec, false);
+
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(codec, reg_set[i].reg,
+ reg_set[i].mask, reg_set[i].val);
+
+ wcd9xxx_enable_clsh_block(codec, false);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+}
+
+static void wcd9xxx_clsh_state_idle(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+ dev_dbg(codec->dev, "%s: wrong transition, cannot enable IDLE state\n",
+ __func__);
+ } else {
+ if (req_state == WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_clsh_turnoff_postpa(codec);
+ } else if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_L, false);
+ wcd9xxx_clsh_turnoff_postpa(codec);
+ } else if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_R, false);
+ wcd9xxx_clsh_turnoff_postpa(codec);
+ } else if (req_state == WCD9XXX_CLSH_STATE_LO) {
+ wcd9xxx_enable_ncp(codec, false);
+ wcd9xxx_enable_buck(codec, false);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+ wcd9xxx_cfg_clsh_buck(codec);
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_ear(codec);
+ wcd9xxx_enable_clsh_block(codec, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_EAR, true);
+ wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
+ wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Enabled ear mode class h\n", __func__);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to ear\n", __func__);
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_l(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+ wcd9xxx_cfg_clsh_buck(codec);
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_hph(codec);
+ wcd9xxx_enable_clsh_block(codec, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
+ wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ } else {
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR) {
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_R, false);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to hph_l\n",
+ __func__);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_r(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+
+ wcd9xxx_cfg_clsh_buck(codec);
+ wcd9xxx_cfg_clsh_param_common(codec);
+ wcd9xxx_cfg_clsh_param_hph(codec);
+ wcd9xxx_enable_clsh_block(codec, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_R, true);
+ wcd9xxx_enable_buck_mode(codec, BUCK_VREF_2V);
+ wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_8);
+
+ dev_dbg(codec->dev, "%s: Done\n", __func__);
+ } else {
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL) {
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_L, false);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to hph_r\n",
+ __func__);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_st(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_L, true);
+ wcd9xxx_clsh_computation_request(codec,
+ CLSH_COMPUTE_HPH_R, true);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+ }
+}
+
+static void wcd9xxx_clsh_state_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ if (is_enable) {
+ if (++cdc_lo_count > 1)
+ return;
+
+ wcd9xxx_enable_buck_mode(codec, BUCK_VREF_1P8V);
+ wcd9xxx_set_fclk_enable_ncp(codec, NCP_FCLK_LEVEL_5);
+
+ if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, false);
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x01);
+ wcd9xxx_enable_ncp(codec, true);
+ msleep(NCP_SETTLE_TIME_US);
+
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
+ 0x40, 0x00);
+ wcd9xxx_enable_ncp(codec, true);
+ msleep(NCP_SETTLE_TIME_US);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0xFB, (0x02 << 2));
+ }
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ } else {
+ dev_dbg(codec->dev, "%s: stub fallback to lineout\n", __func__);
+ }
+}
+
+static void wcd9xxx_clsh_state_err(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ dev_dbg(codec->dev, "%s Wrong request for class H state machine requested to %s %s"
+ , __func__, is_enable ? "enable" : "disable",
+ state_to_str(req_state));
+ WARN_ON(1);
+}
+
+void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+ u8 req_state, bool req_type, u8 clsh_event)
+{
+ u8 old_state, new_state;
+
+ switch (clsh_event) {
+
+ case WCD9XXX_CLSH_EVENT_PRE_DAC:
+
+ /* PRE_DAC event should be used only for Enable */
+ BUG_ON(req_type != WCD9XXX_CLSH_REQ_ENABLE);
+
+ old_state = cdc_clsh_d->state;
+ new_state = old_state | req_state;
+
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d,
+ req_state, req_type);
+ cdc_clsh_d->state = new_state;
+ dev_info(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state),
+ state_to_str(cdc_clsh_d->state));
+
+ break;
+
+ case WCD9XXX_CLSH_EVENT_POST_PA:
+
+ if (req_type == WCD9XXX_CLSH_REQ_DISABLE) {
+ if (req_state == WCD9XXX_CLSH_STATE_LO
+ && --cdc_lo_count > 0)
+ break;
+
+ old_state = cdc_clsh_d->state;
+ new_state = old_state & (~req_state);
+
+ if (new_state < NUM_CLSH_STATES) {
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d,
+ req_state, req_type);
+ cdc_clsh_d->state = new_state;
+ dev_info(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ __func__, state_to_str(old_state),
+ state_to_str(cdc_clsh_d->state));
+
+ } else {
+ dev_dbg(codec->dev, "%s: wrong new state = %x\n",
+ __func__, new_state);
+ }
+
+
+ } else if (req_state != WCD9XXX_CLSH_STATE_LO) {
+ wcd9xxx_clsh_enable_post_pa(codec);
+ }
+
+ break;
+ }
+
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_clsh_fsm);
+
+void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh)
+{
+ int i;
+ clsh->state = WCD9XXX_CLSH_STATE_IDLE;
+
+ for (i = 0; i < NUM_CLSH_STATES; i++)
+ clsh_state_fp[i] = wcd9xxx_clsh_state_err;
+
+ clsh_state_fp[WCD9XXX_CLSH_STATE_IDLE] = wcd9xxx_clsh_state_idle;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_EAR] = wcd9xxx_clsh_state_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL] =
+ wcd9xxx_clsh_state_hph_l;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR] =
+ wcd9xxx_clsh_state_hph_r;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
+ wcd9xxx_clsh_state_hph_st;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
+
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_clsh_init);
+
+MODULE_DESCRIPTION("WCD9XXX Common");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
new file mode 100644
index 0000000..743ab0c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2013, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD9XXX_CODEC_COMMON
+
+#define WCD9XXX_CODEC_COMMON
+
+#define WCD9XXX_CLSH_REQ_ENABLE true
+#define WCD9XXX_CLSH_REQ_DISABLE false
+
+#define WCD9XXX_CLSH_EVENT_PRE_DAC 0x01
+#define WCD9XXX_CLSH_EVENT_POST_PA 0x02
+
+/* Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ * bit 4: Ultrasound mode
+ */
+#define WCD9XXX_CLSH_STATE_IDLE 0x00
+#define WCD9XXX_CLSH_STATE_EAR (0x01 << 0)
+#define WCD9XXX_CLSH_STATE_HPHL (0x01 << 1)
+#define WCD9XXX_CLSH_STATE_HPHR (0x01 << 2)
+#define WCD9XXX_CLSH_STATE_LO (0x01 << 3)
+#define NUM_CLSH_STATES ((0x01 << 4) - 1)
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_HPHR)
+
+
+struct wcd9xxx_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd9xxx_clsh_cdc_data {
+ u8 state;
+ int buck_mv;
+};
+
+
+enum wcd9xxx_buck_volt {
+ WCD9XXX_CDC_BUCK_UNSUPPORTED = 0,
+ WCD9XXX_CDC_BUCK_MV_1P8 = 1800000,
+ WCD9XXX_CDC_BUCK_MV_2P15 = 2150000,
+};
+
+extern void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+ u8 req_state, bool req_type, u8 clsh_event);
+
+extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh);
+
+#endif