From: Bard Liao <bardliao@realtek.com>
Subject: [PATCH] ASoC: rt5670: add HS ground control
Date: Thu, 29 Dec 2016 16:23:47 +0800
Message-id: <1482999827-14705-1-git-send-email-bardliao@realtek.com>
Patch-mainline: Submitted, alsa-devel ML
References: bsc#1016250

Some project use external circuit to prevent the headset ground noise.
It will need to use GPIO to control it. Dell Wyse 3040 is the only project
that has the external circuit so far and it uses GPIO2 for that. This patch
provide the support fot it.

Signed-off-by: Bard Liao <bardliao@realtek.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 include/sound/rt5670.h    |    7 ++++
 sound/soc/codecs/rt5670.c |   74 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 1 deletion(-)

--- a/include/sound/rt5670.h
+++ b/include/sound/rt5670.h
@@ -11,12 +11,19 @@
 #ifndef __LINUX_SND_RT5670_H
 #define __LINUX_SND_RT5670_H
 
+enum rt5670_gpios {
+	RT5670_GPIO_NONE = 0,
+	RT5670_GPIO2,
+};
+
+
 struct rt5670_platform_data {
 	int jd_mode;
 	bool in2_diff;
 	bool dev_gpio;
 
 	bool dmic_en;
+	enum rt5670_gpios hs_ground_control_gpio;
 	unsigned int dmic1_data_pin;
 	/* 0 = GPIO6; 1 = IN2P; 3 = GPIO7*/
 	unsigned int dmic2_data_pin;
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -420,13 +420,15 @@ static int rt5670_headset_detect(struct
 	struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
 	if (jack_insert) {
+		if (rt5670->pdata.hs_ground_control_gpio)
+			snd_soc_dapm_force_enable_pin(dapm, "HS GND Control");
+
 		snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
 		snd_soc_dapm_sync(dapm);
 		snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
 		snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
 			RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
 			RT5670_CBJ_MN_JD);
-		snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
 		snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
 			RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
 		snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
@@ -450,6 +452,10 @@ static int rt5670_headset_detect(struct
 			snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
 			snd_soc_dapm_sync(dapm);
 		}
+		if (rt5670->pdata.hs_ground_control_gpio) {
+			snd_soc_dapm_disable_pin(dapm, "HS GND Control");
+			snd_soc_dapm_sync(dapm);
+		}
 	} else {
 		snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
 		snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
@@ -1542,6 +1548,29 @@ static int rt5670_bst2_event(struct snd_
 	return 0;
 }
 
+static int rt5670_hs_ground_control_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, RT5670_GPIO_CTRL2,
+				RT5670_GP2_OUT_MASK, RT5670_GP2_OUT_HI);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, RT5670_GPIO_CTRL2,
+				RT5670_GP2_OUT_MASK, RT5670_GP2_OUT_LO);
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("PLL1", RT5670_PWR_ANLG2,
 			    RT5670_PWR_PLL_BIT, 0, NULL, 0),
@@ -1917,6 +1946,12 @@ static const struct snd_soc_dapm_widget
 	SND_SOC_DAPM_OUTPUT("SPORN"),
 };
 
+static const struct snd_soc_dapm_widget hs_gnd_cnotrol_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("HS GND Control", SND_SOC_NOPM, 0, 0,
+			rt5670_hs_ground_control_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
 static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 	{ "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
 	{ "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
@@ -2314,6 +2349,11 @@ static const struct snd_soc_dapm_route r
 	{ "SPORN", NULL, "SPO Amp" },
 };
 
+static const struct snd_soc_dapm_route hs_gnd_cnotrol_routes[] = {
+	{ "BST1", NULL, "HS GND Control" },
+	{ "HP Amp", NULL, "HS GND Control" },
+};
+
 static int rt5670_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -2679,6 +2719,13 @@ static int rt5670_probe(struct snd_soc_c
 			"The driver is for RT5670 RT5671 or RT5672 only\n");
 		return -ENODEV;
 	}
+	if (rt5670->pdata.hs_ground_control_gpio) {
+		snd_soc_dapm_new_controls(dapm, hs_gnd_cnotrol_widgets,
+			ARRAY_SIZE(hs_gnd_cnotrol_widgets));
+		snd_soc_dapm_add_routes(dapm, hs_gnd_cnotrol_routes,
+			ARRAY_SIZE(hs_gnd_cnotrol_routes));
+	}
+
 	rt5670->codec = codec;
 
 	return 0;
@@ -2825,6 +2872,10 @@ static const struct dmi_system_id dmi_pl
 			DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"),
 		},
 	},
+	{}
+};
+
+static const struct dmi_system_id dmi_platform_dell_wyse[] = {
 	{
 		.ident = "Dell Wyse 3040",
 		.matches = {
@@ -2859,6 +2910,12 @@ static int rt5670_i2c_probe(struct i2c_c
 		rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
 		rt5670->pdata.dev_gpio = true;
 		rt5670->pdata.jd_mode = 1;
+		rt5670->pdata.hs_ground_control_gpio = RT5670_GPIO_NONE;
+	} else if (dmi_check_system(dmi_platform_dell_wyse)) {
+		rt5670->pdata.dmic_en = false;
+		rt5670->pdata.dev_gpio = true;
+		rt5670->pdata.jd_mode = 1;
+		rt5670->pdata.hs_ground_control_gpio = RT5670_GPIO2;
 	}
 
 	rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap);
@@ -2884,6 +2941,21 @@ static int rt5670_i2c_probe(struct i2c_c
 
 	regmap_write(rt5670->regmap, RT5670_RESET, 0);
 
+	switch (rt5670->pdata.hs_ground_control_gpio) {
+	case RT5670_GPIO_NONE:
+		break;
+	case RT5670_GPIO2:
+		regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+			RT5670_GP2_PF_MASK | RT5670_GP2_OUT_MASK,
+			RT5670_GP2_PF_OUT | RT5670_GP2_OUT_LO);
+		break;
+	default:
+		rt5670->pdata.hs_ground_control_gpio =
+			RT5670_GPIO_NONE;
+		dev_warn(&i2c->dev, "Only Support GPIO2 currently\n");
+			break;
+	}
+
 	regmap_read(rt5670->regmap, RT5670_VENDOR_ID, &val);
 	if (val >= 4)
 		regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0980);
