ASoC: WCD9310: Fix fake button press event during headset removal

During slow headset removal, there is a possibility for the ground
on headset connector to touch the mic line on headset plug, causing
the mic voltage drop to ground for a short amount of time. This gets
detected as button 0 press interrupt.

Adds logic to identify such fake button press interrupts during
headset removal and ignore them

Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index b077cf4..5ee8bd5 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -70,6 +70,8 @@
 	u32 anc_slot;
 
 	bool no_mic_headset_override;
+	/* Delayed work to report long button press */
+	struct delayed_work btn0_dwork;
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -2164,6 +2166,28 @@
 	return 0;
 }
 
+static void btn0_lpress_fn(struct work_struct *work)
+{
+	struct delayed_work *delayed_work;
+	struct tabla_priv *tabla;
+
+	pr_debug("%s:\n", __func__);
+
+	delayed_work = to_delayed_work(work);
+	tabla = container_of(delayed_work, struct tabla_priv, btn0_dwork);
+
+	if (tabla) {
+		if (tabla->button_jack) {
+			pr_debug("%s: Reporting long button press event\n",
+					__func__);
+			snd_soc_jack_report(tabla->button_jack, SND_JACK_BTN_0,
+					SND_JACK_BTN_0);
+		}
+	} else {
+		pr_err("%s: Bad tabla private data\n", __func__);
+	}
+
+}
 int tabla_hs_detect(struct snd_soc_codec *codec,
 	struct snd_soc_jack *headset_jack, struct snd_soc_jack *button_jack,
 	struct tabla_mbhc_calibration *calibration)
@@ -2178,37 +2202,35 @@
 	tabla->button_jack = button_jack;
 	tabla->calibration = calibration;
 
+	INIT_DELAYED_WORK(&tabla->btn0_dwork, btn0_lpress_fn);
 	return tabla_codec_enable_hs_detect(codec, 1);
 }
 EXPORT_SYMBOL_GPL(tabla_hs_detect);
 
-#define TABLA_BUTTON_MARGIN_ERROR 4
 static irqreturn_t tabla_dce_handler(int irq, void *data)
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	short bias_value, bias_value2;
+	short bias_value;
 
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
 
 	bias_value = tabla_codec_read_dce_result(codec);
-	pr_debug("button press interrupt, bias value is %d\n", bias_value);
+	pr_debug("%s: button press interrupt, bias value is %d\n",
+			__func__, bias_value);
 
-	/* Do another DCE to make sure button voltage is the same */
-	bias_value2 = tabla_codec_measure_micbias_voltage(codec, 1);
-	pr_debug("button press part 2, bias value is %d\n", bias_value2);
+	/*
+	 * TODO: If button pressed is not button 0,
+	 * report the button press event immediately.
+	 */
+	priv->buttons_pressed |= SND_JACK_BTN_0;
 
-	if (abs(bias_value - bias_value2) < TABLA_BUTTON_MARGIN_ERROR) {
-		if (priv->button_jack)
-			snd_soc_jack_report(priv->button_jack, SND_JACK_BTN_0,
-				SND_JACK_BTN_0);
-
-		priv->buttons_pressed |= SND_JACK_BTN_0;
-	}
 	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0x09);
 	msleep(100);
 
+	schedule_delayed_work(&priv->btn0_dwork, msecs_to_jiffies(400));
+
 	return IRQ_HANDLED;
 }
 
@@ -2216,13 +2238,46 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	int ret, mic_voltage;
+
 	pr_debug("%s\n", __func__);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
+
 	if (priv->buttons_pressed & SND_JACK_BTN_0) {
-		pr_debug("%s: Button released\n", __func__);
-		if (priv->button_jack)
-			snd_soc_jack_report(priv->button_jack, 0,
-				SND_JACK_BTN_0);
+		ret = cancel_delayed_work(&priv->btn0_dwork);
+
+		if (ret == 0) {
+
+			pr_debug("%s: Reporting long button release event\n",
+					__func__);
+			if (priv->button_jack) {
+				snd_soc_jack_report(priv->button_jack, 0,
+					SND_JACK_BTN_0);
+			}
+
+		} else {
+
+			mic_voltage =
+				tabla_codec_measure_micbias_voltage(codec, 0);
+			pr_debug("%s: Microphone Voltage on release = %d\n",
+						__func__, mic_voltage);
+
+			if (mic_voltage < -2000 || mic_voltage > -670) {
+				pr_debug("%s: Fake buttton press interrupt\n",
+						__func__);
+			} else {
+
+				if (priv->button_jack) {
+					pr_debug("%s:reporting short button press and release\n",
+							__func__);
+
+					snd_soc_jack_report(priv->button_jack,
+						SND_JACK_BTN_0, SND_JACK_BTN_0);
+					snd_soc_jack_report(priv->button_jack,
+						0, SND_JACK_BTN_0);
+				}
+			}
+		}
 
 		priv->buttons_pressed &= ~SND_JACK_BTN_0;
 	}