ASoC: WCD9310: Read microphone voltage on headset removal and button press

Blowing loudly into the microphone can change the voltage
enough to signal button press or headset removal interrupts.
Using a more accurate voltage measurement will verify that
button press interrupts and headset removal interrupts are
signifying the correct events.

Also fixes the issue of removing a mic-less headphone
during audio playback by enabling removal detection through
the microphone for headphones. Also makes sure that the
HPH power amplifier doesn't trigger a headset removal event.

CRs-fixed: 298613
Signed-off-by: Brad Rubin <brubin@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index dc03d2a..3ecbe11 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1562,29 +1562,52 @@
 		.ops = &tabla_dai_ops,
 	},
 };
-
-static short tabla_codec_measure_micbias_voltage(struct snd_soc_codec *codec,
-	int dce)
+static short tabla_codec_read_sta_result(struct snd_soc_codec *codec)
 {
 	u8 bias_msb, bias_lsb;
 	short bias_value;
 
+	bias_msb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B3_STATUS);
+	bias_lsb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B2_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static short tabla_codec_read_dce_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B5_STATUS);
+	bias_lsb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B4_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static short tabla_codec_measure_micbias_voltage(struct snd_soc_codec *codec,
+	int dce)
+{
+	short bias_value;
+
 	if (dce) {
-		bias_msb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B5_STATUS);
-		bias_lsb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B4_STATUS);
+		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
+		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
+		usleep_range(60000, 60000);
+		bias_value = tabla_codec_read_dce_result(codec);
 	} else {
+		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		msleep(100);
+		usleep_range(5000, 5000);
 		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
-		bias_msb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B3_STATUS);
-		bias_lsb = snd_soc_read(codec, TABLA_A_CDC_MBHC_B2_STATUS);
+		usleep_range(50, 50);
+		bias_value = tabla_codec_read_sta_result(codec);
+		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x0);
 	}
 
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x0);
-
-	bias_value = (bias_msb << 8) | bias_lsb;
 	pr_debug("read microphone bias value %x\n", bias_value);
 	return bias_value;
 }
@@ -1645,7 +1668,7 @@
 		pr_err("Error, invalid mic bias line\n");
 		return -EINVAL;
 	}
-	snd_soc_write(codec, micbias_cfilt_ctl_reg, 0x40);
+	snd_soc_write(codec, micbias_cfilt_ctl_reg, 0x00);
 
 	snd_soc_update_bits(codec, micbias_ctl_reg, 0x1F, 0x16);
 	snd_soc_update_bits(codec, micbias_ctl_reg, 0x60,
@@ -1667,13 +1690,14 @@
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x6, 0x6);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 
-	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x00);
+	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x10);
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x01);
 
 	tabla_codec_calibrate_hs_polling(codec);
 
 	bias_value = tabla_codec_measure_micbias_voltage(codec, 0);
-
+	snd_soc_write(codec, micbias_cfilt_ctl_reg, 0x40);
+	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
 	threshold_no_mic = 0xF7F6;
 
 	if (bias_value < threshold_no_mic) {
@@ -1721,6 +1745,10 @@
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0xC,
 		calibration->hph_current << 2);
 
+	/* Turn off HPH PAs during insertion detection to avoid false
+	 * insertion interrupts
+	 */
+	snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30, 0x00);
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x13);
 
 	switch (calibration->bias) {
@@ -1773,6 +1801,7 @@
 		calibration->mic_current << 5);
 	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x80, 0x80);
 	usleep_range(calibration->mic_pid, calibration->mic_pid);
+
 	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x10);
 
 	snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, calibration->bias);
@@ -1804,11 +1833,14 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	short bias_value;
 
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
 
-	pr_debug("%s: Button pressed\n", __func__);
+	bias_value = tabla_codec_read_dce_result(codec);
+	pr_debug("button press interrupt, bias value is %d\n", bias_value);
+
 	if (priv->button_jack)
 		snd_soc_jack_report(priv->button_jack, SND_JACK_BTN_0,
 			SND_JACK_BTN_0);
@@ -1851,7 +1883,6 @@
 
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x0);
 
 	switch (calibration->bias) {
 	case TABLA_MICBIAS1:
@@ -1871,7 +1902,6 @@
 		return;
 	}
 	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x80, 0x00);
-	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x00);
 	usleep_range(calibration->shutdown_plug_removal,
 		calibration->shutdown_plug_removal);
 
@@ -1947,6 +1977,7 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	short bias_value;
 
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
@@ -1954,14 +1985,22 @@
 	usleep_range(priv->calibration->shutdown_plug_removal,
 		priv->calibration->shutdown_plug_removal);
 
-	if (priv->headset_jack) {
-		pr_debug("%s: Reporting removal\n", __func__);
-		snd_soc_jack_report(priv->headset_jack, 0, SND_JACK_HEADSET);
+	bias_value = tabla_codec_measure_micbias_voltage(codec, 1);
+	pr_debug("removal interrupt, bias value is %d\n", bias_value);
+
+	if (bias_value < -90) {
+		pr_debug("False alarm, headset not actually removed\n");
+		tabla_codec_start_hs_polling(codec);
+	} else {
+		if (priv->headset_jack) {
+			pr_debug("%s: Reporting removal\n", __func__);
+			snd_soc_jack_report(priv->headset_jack, 0,
+				SND_JACK_HEADSET);
+		}
+		tabla_codec_shutdown_hs_polling(codec);
+
+		tabla_codec_enable_hs_detect(codec, 1);
 	}
-	tabla_codec_shutdown_hs_polling(codec);
-
-	tabla_codec_enable_hs_detect(codec, 1);
-
 	return IRQ_HANDLED;
 }
 
@@ -2039,11 +2078,6 @@
 	snd_soc_update_bits(codec, TABLA_A_MICB_3_INT_RBIAS, 0x24, 0x24);
 	snd_soc_update_bits(codec, TABLA_A_MICB_4_INT_RBIAS, 0x24, 0x24);
 
-	/* Set headset CFILT to fast mode */
-	snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_1_CTL, 0x00, 0x00);
-	snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_2_CTL, 0x00, 0x00);
-	snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_3_CTL, 0x00, 0x00);
-
 	snd_soc_update_bits(codec, TABLA_A_CDC_CONN_CLSG_CTL, 0x30, 0x10);
 
 	/* Use 16 bit sample size for now */