From 011de1bd8337ed045f685c8fb77b03353e25db5d Mon Sep 17 00:00:00 2001
From: Minecrell <minecrell@minecrell.net>
Date: Thu, 4 Jul 2019 00:39:27 +0200
Subject: [PATCH 31/78] ASoC: msm8916-wcd-analog: Add jack detection using GPIO

Samsung decided to implement entirely custom jack detection using
an extra IC for some reason. Because of that, the jack detection
in PM8916 does not work as-is. (It always detects that headphones
are plugged in...)

However, the headset/button detection is still working fine through
PM8916. To avoid implementing custom Samsung stuff, override the
jack detection to use the provided GPIO, but keep everything else.
---
 sound/soc/codecs/msm8916-wcd-analog.c | 96 ++++++++++++++++++++-------
 1 file changed, 71 insertions(+), 25 deletions(-)

diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 3fe5be2d3..9bd2d1bdd 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
 #include <sound/soc.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -298,6 +299,7 @@ struct pm8916_wcd_analog_priv {
 	struct snd_soc_component *component;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 	struct snd_soc_jack *jack;
+	struct snd_soc_jack_gpio jack_gpio;
 	bool hphl_jack_type_normally_open;
 	bool gnd_jack_type_normally_open;
 	/* Voltage threshold when internal current source of 100uA is used */
@@ -484,7 +486,7 @@ static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
 	struct snd_soc_component *component = wcd->component;
 	bool micbias_enabled = false;
 	u32 plug_type = 0;
-	u32 int_en_mask;
+	u32 int_en_mask = 0;
 
 	snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_1,
 		      CDC_A_MBHC_DET_CTL_L_DET_EN |
@@ -519,7 +521,8 @@ static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
 
 	pm8916_mbhc_configure_bias(wcd, micbias_enabled);
 
-	int_en_mask = MBHC_SWITCH_INT;
+	if (!wcd->jack_gpio.report)
+		int_en_mask |= MBHC_SWITCH_INT;
 	if (wcd->mbhc_btn_enabled)
 		int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET;
 
@@ -995,6 +998,22 @@ static int pm8916_wcd_analog_set_jack(struct snd_soc_component *component,
 {
 	struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
 
+	if (wcd->jack_gpio.report && wcd->jack != jack) {
+		int ret;
+
+		if (wcd->jack)
+			snd_soc_jack_free_gpios(wcd->jack, 1, &wcd->jack_gpio);
+
+		if (jack) {
+			ret = snd_soc_jack_add_gpiods(component->dev, jack,
+						      1, &wcd->jack_gpio);
+			if (ret)
+				dev_err(component->dev,
+					"Failed to add GPIO to jack: %d\n",
+					ret);
+		}
+	}
+
 	wcd->jack = jack;
 
 	return 0;
@@ -1056,15 +1075,10 @@ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+static int pm8916_wcd_analog_switch_check(struct pm8916_wcd_analog_priv *priv, bool ins)
 {
-	struct pm8916_wcd_analog_priv *priv = arg;
 	struct snd_soc_component *component = priv->component;
-	bool ins = false;
-
-	if (snd_soc_component_read(component, CDC_A_MBHC_DET_CTL_1) &
-				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
-		ins = true;
+	int report;
 
 	/* Set the detection type appropriately */
 	snd_soc_component_update_bits(component, CDC_A_MBHC_DET_CTL_1,
@@ -1088,23 +1102,46 @@ static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
 		 * a headset.
 		 */
 		if (priv->mbhc_btn0_released)
-			snd_soc_jack_report(priv->jack,
-					    SND_JACK_HEADSET, hs_jack_mask);
+			report = SND_JACK_HEADSET;
 		else
-			snd_soc_jack_report(priv->jack,
-					    SND_JACK_HEADPHONE, hs_jack_mask);
+			report = SND_JACK_HEADPHONE;
 
 		priv->detect_accessory_type = false;
 
 	} else { /* removal */
-		snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+		report = 0;
 		priv->detect_accessory_type = true;
 		priv->mbhc_btn0_released = false;
 	}
 
+	return report;
+}
+
+static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+	struct snd_soc_component *component = priv->component;
+	bool ins = false;
+	int report;
+
+	if (snd_soc_component_read(component, CDC_A_MBHC_DET_CTL_1) &
+				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+		ins = true;
+
+	report = pm8916_wcd_analog_switch_check(priv, ins);
+	snd_soc_jack_report(priv->jack, report, hs_jack_mask);
+
 	return IRQ_HANDLED;
 }
 
+static int pm8916_wcd_analog_jack_status_check(void *data)
+{
+	struct pm8916_wcd_analog_priv *wcd = data;
+	bool ins = gpiod_get_value_cansleep(wcd->jack_gpio.desc);
+
+	return pm8916_wcd_analog_switch_check(wcd, ins);
+}
+
 static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 	[0] = {
 	       .name = "pm8916_wcd_analog_pdm_rx",
@@ -1236,17 +1273,26 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
-	if (irq < 0)
-		return irq;
-
-	ret = devm_request_threaded_irq(dev, irq, NULL,
-			       pm8916_mbhc_switch_irq_handler,
-			       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
-			       IRQF_ONESHOT,
-			       "mbhc switch irq", priv);
-	if (ret)
-		dev_err(dev, "cannot request mbhc switch irq\n");
+	if (gpiod_count(dev, "jack") > 0) {
+		priv->jack_gpio.name = "jack";
+		priv->jack_gpio.report = hs_jack_mask;
+		priv->jack_gpio.debounce_time = 100;
+		priv->jack_gpio.jack_status_check =
+			pm8916_wcd_analog_jack_status_check;
+		priv->jack_gpio.data = priv;
+	} else {
+		irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+		if (irq < 0)
+			return irq;
+
+		ret = devm_request_threaded_irq(dev, irq, NULL,
+				       pm8916_mbhc_switch_irq_handler,
+				       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+				       IRQF_ONESHOT,
+				       "mbhc switch irq", priv);
+		if (ret)
+			dev_err(dev, "cannot request mbhc switch irq\n");
+	}
 
 	if (priv->mbhc_btn_enabled) {
 		irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
-- 
2.31.1

