ASoC: WCD9310: Add mono differential support for line outs

Allows multiple input signals to be mixed within the codec.

Also allows output in inverted phase.

Signed-off-by: Brad Rubin <brubin@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index de1b199..4a527ee 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -92,8 +92,20 @@
 static const struct snd_kcontrol_new tabla_snd_controls[] = {
 	SOC_SINGLE_TLV("LINEOUT1 Volume", TABLA_A_RX_LINE_1_GAIN, 0, 12, 1,
 		line_gain),
+	SOC_SINGLE_TLV("LINEOUT2 Volume", TABLA_A_RX_LINE_2_GAIN, 0, 12, 1,
+		line_gain),
 	SOC_SINGLE_TLV("LINEOUT3 Volume", TABLA_A_RX_LINE_3_GAIN, 0, 12, 1,
 		line_gain),
+	SOC_SINGLE_TLV("LINEOUT4 Volume", TABLA_A_RX_LINE_4_GAIN, 0, 12, 1,
+		line_gain),
+
+	SOC_SINGLE("RX1 CHAIN INVERT Switch", TABLA_A_CDC_RX1_B6_CTL, 4, 1, 0),
+	SOC_SINGLE("RX2 CHAIN INVERT Switch", TABLA_A_CDC_RX2_B6_CTL, 4, 1, 0),
+	SOC_SINGLE("RX3 CHAIN INVERT Switch", TABLA_A_CDC_RX3_B6_CTL, 4, 1, 0),
+	SOC_SINGLE("RX4 CHAIN INVERT Switch", TABLA_A_CDC_RX4_B6_CTL, 4, 1, 0),
+	SOC_SINGLE("RX5 CHAIN INVERT Switch", TABLA_A_CDC_RX5_B6_CTL, 4, 1, 0),
+	SOC_SINGLE("RX6 CHAIN INVERT Switch", TABLA_A_CDC_RX6_B6_CTL, 4, 1, 0),
+
 	SOC_SINGLE_TLV("HPHL Volume", TABLA_A_RX_HPH_L_GAIN, 0, 12, 1,
 		line_gain),
 	SOC_SINGLE_TLV("HPHR Volume", TABLA_A_RX_HPH_R_GAIN, 0, 12, 1,
@@ -171,12 +183,27 @@
 static const struct soc_enum rx3_mix1_inp1_chain_enum =
 	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX3_B1_CTL, 0, 12, rx_mix1_text);
 
+static const struct soc_enum rx3_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX3_B1_CTL, 4, 12, rx_mix1_text);
+
 static const struct soc_enum rx4_mix1_inp1_chain_enum =
 	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX4_B1_CTL, 0, 12, rx_mix1_text);
 
+static const struct soc_enum rx4_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX4_B1_CTL, 4, 12, rx_mix1_text);
+
 static const struct soc_enum rx5_mix1_inp1_chain_enum =
 	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX5_B1_CTL, 0, 12, rx_mix1_text);
 
+static const struct soc_enum rx5_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX5_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX6_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX6_B1_CTL, 4, 12, rx_mix1_text);
+
 static const struct soc_enum sb_tx5_mux_enum =
 	SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B5_CTL, 0, 9, sb_tx5_mux_text);
 
@@ -218,12 +245,27 @@
 static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
 	SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
 
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+
 static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
 	SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
 
+static const struct snd_kcontrol_new rx4_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum);
+
 static const struct snd_kcontrol_new rx5_mix1_inp1_mux =
 	SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum);
 
+static const struct snd_kcontrol_new rx5_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX6 MIX1 INP1 Mux", rx6_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX6 MIX1 INP2 Mux", rx6_mix1_inp2_chain_enum);
+
 static const struct snd_kcontrol_new sb_tx5_mux =
 	SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
 
@@ -266,9 +308,16 @@
 static const struct snd_kcontrol_new lineout1_switch =
 	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_1_DAC_CTL, 6, 1, 0);
 
+static const struct snd_kcontrol_new lineout2_switch =
+	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_2_DAC_CTL, 6, 1, 0);
+
 static const struct snd_kcontrol_new lineout3_switch =
 	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_3_DAC_CTL, 6, 1, 0);
 
+static const struct snd_kcontrol_new lineout4_switch =
+	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+
+
 static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
 	int enable)
 {
@@ -500,81 +549,17 @@
 	return 0;
 }
 
-static int tabla_codec_reset_interpolator_1(struct snd_soc_dapm_widget *w,
+static int tabla_codec_reset_interpolator(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x1,
-			0x1);
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x1,
-			0x0);
-		break;
-	}
-	return 0;
-}
-
-static int tabla_codec_reset_interpolator_2(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-
-	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x2,
-			0x2);
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x2,
-			0x0);
-		break;
-	}
-	return 0;
-}
-
-static int tabla_codec_reset_interpolator_3(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-
-	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x4,
-			0x4);
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x4,
-			0x0);
-		break;
-	}
-	return 0;
-}
-
-static int tabla_codec_reset_interpolator_4(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-
-	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x8,
-			0x8);
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x8,
-			0x0);
-		break;
-	}
-	return 0;
-}
-
-static int tabla_codec_reset_interpolator_5(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-
-	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x10,
-			0x10);
-		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x10,
-			0x0);
+		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL,
+			1 << w->shift, 1 << w->shift);
+		snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL,
+			1 << w->shift, 0x0);
 		break;
 	}
 	return 0;
@@ -596,7 +581,7 @@
 			SND_SOC_DAPM_PRE_PMD),
 	SND_SOC_DAPM_PGA("RX BIAS", TABLA_A_RX_COM_BIAS, 7, 0, NULL, 0),
 	SND_SOC_DAPM_MUX_E("RX1 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 0, 0,
-		&rx_mix1_inp1_mux, tabla_codec_reset_interpolator_1,
+		&rx_mix1_inp1_mux, tabla_codec_reset_interpolator,
 		SND_SOC_DAPM_PRE_PMU),
 	SND_SOC_DAPM_AIF_IN("SLIM RX1", "AIF1 Playback", 0,
 		TABLA_A_CDC_RX1_B6_CTL, 5, 0),
@@ -606,7 +591,7 @@
 		tabla_codec_enable_charge_pump, SND_SOC_DAPM_POST_PMU |
 			SND_SOC_DAPM_PRE_PMD),
 	SND_SOC_DAPM_MUX_E("RX2 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 1, 0,
-		&rx2_mix1_inp1_mux, tabla_codec_reset_interpolator_2,
+		&rx2_mix1_inp1_mux, tabla_codec_reset_interpolator,
 		SND_SOC_DAPM_PRE_PMU),
 	SND_SOC_DAPM_AIF_IN("SLIM RX2", "AIF1 Playback", 0,
 		TABLA_A_CDC_RX2_B6_CTL, 5, 0),
@@ -623,26 +608,54 @@
 
 	/* Speaker */
 	SND_SOC_DAPM_OUTPUT("LINEOUT"),
+
 	SND_SOC_DAPM_PGA_E("LINEOUT1", TABLA_A_RX_LINE_CNP_EN, 0, 0, NULL, 0,
 		tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_SWITCH("LINEOUT1 DAC", TABLA_A_RX_LINE_1_DAC_CTL, 7, 0,
-		&lineout1_switch),
-	SND_SOC_DAPM_MUX_E("RX3 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 2, 0,
-		&rx3_mix1_inp1_mux, tabla_codec_reset_interpolator_3,
-		SND_SOC_DAPM_PRE_PMU),
-
+	SND_SOC_DAPM_PGA_E("LINEOUT2", TABLA_A_RX_LINE_CNP_EN, 1, 0, NULL, 0,
+		tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_PGA_E("LINEOUT3", TABLA_A_RX_LINE_CNP_EN, 2, 0, NULL, 0,
 		tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT4", TABLA_A_RX_LINE_CNP_EN, 3, 0, NULL, 0,
+		tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SWITCH("LINEOUT1 DAC", TABLA_A_RX_LINE_1_DAC_CTL, 7, 0,
+		&lineout1_switch),
+	SND_SOC_DAPM_SWITCH("LINEOUT2 DAC", TABLA_A_RX_LINE_2_DAC_CTL, 7, 0,
+		&lineout2_switch),
 	SND_SOC_DAPM_SWITCH("LINEOUT3 DAC", TABLA_A_RX_LINE_3_DAC_CTL, 7, 0,
 		&lineout3_switch),
-	SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 3, 0,
-		&rx4_mix1_inp1_mux, tabla_codec_reset_interpolator_4,
-		SND_SOC_DAPM_PRE_PMU),
-	SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 4, 0,
-		&rx5_mix1_inp1_mux, tabla_codec_reset_interpolator_5,
-		SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_SWITCH("LINEOUT4 DAC", TABLA_A_RX_LINE_4_DAC_CTL, 7, 0,
+		&lineout4_switch),
+
+	SND_SOC_DAPM_PGA_E("RX3 MIX1", TABLA_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL, 0,
+		tabla_codec_reset_interpolator, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_PGA_E("RX4 MIX1", TABLA_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL, 0,
+		tabla_codec_reset_interpolator, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_PGA_E("RX5 MIX1", TABLA_A_CDC_CLK_RX_B1_CTL, 4, 0, NULL, 0,
+		tabla_codec_reset_interpolator, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_PGA_E("RX6 MIX1", TABLA_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL, 0,
+		tabla_codec_reset_interpolator, SND_SOC_DAPM_PRE_PMU),
+
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx5_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx5_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX6 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx6_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX6 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx6_mix1_inp2_mux),
 
 	/* TX */
 	SND_SOC_DAPM_INPUT("AMIC1"),
@@ -761,17 +774,46 @@
 	{"RX2 CP", NULL, "RX2 MIX1 INP1"},
 	{"RX2 MIX1 INP1", "RX2", "RX BIAS"},
 
-	/* Speaker (RX MIX3 and RX MIX4) */
 	{"LINEOUT", NULL, "LINEOUT1"},
-	{"LINEOUT1", NULL, "LINEOUT1 DAC"},
-	{"LINEOUT1 DAC", "Switch", "RX3 MIX1 INP1"},
-	{"RX3 MIX1 INP1", "RX1", "RX BIAS"},
-
+	{"LINEOUT", NULL, "LINEOUT2"},
 	{"LINEOUT", NULL, "LINEOUT3"},
+	{"LINEOUT", NULL, "LINEOUT4"},
+
+	{"LINEOUT1", NULL, "LINEOUT1 DAC"},
+	{"LINEOUT2", NULL, "LINEOUT2 DAC"},
 	{"LINEOUT3", NULL, "LINEOUT3 DAC"},
-	{"LINEOUT3 DAC", "Switch", "RX5 MIX1 INP1"},
+	{"LINEOUT4", NULL, "LINEOUT4 DAC"},
+
+	{"LINEOUT1 DAC", "Switch", "RX3 MIX1"},
+	{"LINEOUT2 DAC", "Switch", "RX4 MIX1"},
+	{"LINEOUT3 DAC", "Switch", "RX5 MIX1"},
+	{"LINEOUT4 DAC", "Switch", "RX6 MIX1"},
+
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
+	{"RX5 MIX1", NULL, "RX5 MIX1 INP1"},
+	{"RX5 MIX1", NULL, "RX5 MIX1 INP2"},
+	{"RX6 MIX1", NULL, "RX6 MIX1 INP1"},
+	{"RX6 MIX1", NULL, "RX6 MIX1 INP2"},
+
+	{"RX3 MIX1 INP1", "RX1", "RX BIAS"},
+	{"RX3 MIX1 INP1", "RX2", "RX BIAS"},
+	{"RX3 MIX1 INP2", "RX1", "RX BIAS"},
+	{"RX3 MIX1 INP2", "RX2", "RX BIAS"},
+	{"RX4 MIX1 INP1", "RX1", "RX BIAS"},
 	{"RX4 MIX1 INP1", "RX2", "RX BIAS"},
+	{"RX4 MIX1 INP2", "RX1", "RX BIAS"},
+	{"RX4 MIX1 INP2", "RX2", "RX BIAS"},
+	{"RX5 MIX1 INP1", "RX1", "RX BIAS"},
 	{"RX5 MIX1 INP1", "RX2", "RX BIAS"},
+	{"RX5 MIX1 INP2", "RX1", "RX BIAS"},
+	{"RX5 MIX1 INP2", "RX2", "RX BIAS"},
+	{"RX6 MIX1 INP1", "RX1", "RX BIAS"},
+	{"RX6 MIX1 INP1", "RX2", "RX BIAS"},
+	{"RX6 MIX1 INP2", "RX1", "RX BIAS"},
+	{"RX6 MIX1 INP2", "RX2", "RX BIAS"},
 
 	/* Handset TX */
 	{"DEC5 MUX", "ADC2", "ADC2"},