ASoC: WCD9310: Detect headset button press and release

Detects if inserted headset has a microphone or not, and
if there is a microphone, detects if a button is pressed
which grounds the microphone bias line.

Signed-off-by: Brad Rubin <brubin@codeaurora.org>
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 4a527ee..80b1efb 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -44,10 +44,12 @@
 	bool clock_active;
 	bool config_mode_active;
 	bool mbhc_polling_active;
+	int buttons_pressed;
 
 	struct tabla_mbhc_calibration *calibration;
 
-	struct snd_soc_jack *jack;
+	struct snd_soc_jack *headset_jack;
+	struct snd_soc_jack *button_jack;
 };
 
 static int tabla_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
@@ -1018,6 +1020,43 @@
 	tabla->clock_active = false;
 }
 
+static void tabla_codec_start_hs_polling(struct snd_soc_codec *codec)
+{
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
+	tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+	tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+}
+
+static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
+{
+	/* TODO store register values in calibration */
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B10_CTL, 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B9_CTL, 0x00);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0x08);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0xEE);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL, 0xFC);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL, 0xCE);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 3);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL, 9);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL, 30);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 120);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 0x78, 0x58);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_B2_CTL, 11);
+}
+
+static void tabla_codec_pause_hs_polling(struct snd_soc_codec *codec)
+{
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
+	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+}
+
 static int tabla_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -1042,8 +1081,11 @@
 	}
 
 	if (tabla->mbhc_polling_active && (tabla->ref_cnt == 1)) {
+		tabla_codec_pause_hs_polling(codec);
 		tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
 		tabla_codec_enable_clock_block(codec, 0);
+		tabla_codec_calibrate_hs_polling(codec);
+		tabla_codec_start_hs_polling(codec);
 	}
 
 	return ret;
@@ -1071,11 +1113,14 @@
 
 	if (tabla->mbhc_polling_active) {
 		if (!tabla->ref_cnt) {
+			tabla_codec_pause_hs_polling(codec);
 			tabla_codec_enable_bandgap(codec,
 				TABLA_BANDGAP_MBHC_MODE);
 			snd_soc_update_bits(codec, TABLA_A_RX_COM_BIAS, 0x80,
 				0x80);
 			tabla_codec_enable_clock_block(codec, 1);
+			tabla_codec_calibrate_hs_polling(codec);
+			tabla_codec_start_hs_polling(codec);
 		}
 		snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x01);
 	}
@@ -1157,15 +1202,43 @@
 	},
 };
 
-static void tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
+static short tabla_codec_measure_micbias_voltage(struct snd_soc_codec *codec,
+	int dce)
+{
+	u8 bias_msb, bias_lsb;
+	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);
+	} else {
+		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);
+		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);
+	}
+
+	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;
+}
+
+static int tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 	struct tabla_mbhc_calibration *calibration = tabla->calibration;
-	int micbias_ctl_reg, micbias_cfilt_val_reg, micbias_cfilt_ctl_reg;
+	int micbias_ctl_reg, micbias_cfilt_val_reg, micbias_cfilt_ctl_reg,
+		micbias_mbhc_reg;
+	short bias_value, threshold_no_mic;
 
 	if (!calibration) {
 		pr_err("Error, no tabla calibration\n");
-		return;
+		return -ENODEV;
 	}
 
 	tabla->mbhc_polling_active = true;
@@ -1178,12 +1251,6 @@
 
 	snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x01);
 
-	/* TODO store register values in calibration */
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0x09);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0xEE);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL, 0xFC);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL, 0xCE);
-
 	snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x0F, 0x0D);
 	snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0xE0);
 
@@ -1195,58 +1262,66 @@
 		micbias_ctl_reg = TABLA_A_MICB_1_CTL;
 		micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_1_CTL;
 		micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_1_VAL;
+		micbias_mbhc_reg = TABLA_A_MICB_1_MBHC;
 		break;
 	case TABLA_MICBIAS2:
 		micbias_ctl_reg = TABLA_A_MICB_2_CTL;
 		micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_2_CTL;
 		micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_2_VAL;
+		micbias_mbhc_reg = TABLA_A_MICB_2_MBHC;
 		break;
 	case TABLA_MICBIAS3:
 		micbias_ctl_reg = TABLA_A_MICB_3_CTL;
 		micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_3_CTL;
 		micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_3_VAL;
+		micbias_mbhc_reg = TABLA_A_MICB_3_MBHC;
 		break;
 	case TABLA_MICBIAS4:
 		pr_err("%s: Error, microphone bias 4 not supported\n",
 			__func__);
-		return;
+		return -EINVAL;
 	default:
 		pr_err("Error, invalid mic bias line\n");
-		return;
+		return -EINVAL;
 	}
 	snd_soc_write(codec, micbias_cfilt_ctl_reg, 0x40);
 
-	snd_soc_write(codec, micbias_ctl_reg, 0x36);
+	snd_soc_update_bits(codec, micbias_ctl_reg, 0x1F, 0x16);
+	snd_soc_update_bits(codec, micbias_ctl_reg, 0x60,
+		calibration->bias << 5);
 
 	snd_soc_write(codec, micbias_cfilt_val_reg, 0x68);
 
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x4);
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
 
 	snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x80);
 	snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x1F, 0x1C);
 	snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
 
 	snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x00);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x80, 0x80);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x80, 0x00);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
 
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 3);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL, 9);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL, 30);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 120);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 0x78, 0x58);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_B2_CTL, 11);
-
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+	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);
 
-	tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
-	tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
-	/* TODO check if we need to maintain the other register bits */
+	snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x00);
+	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);
+
+	threshold_no_mic = 0xF7F6;
+
+	if (bias_value < threshold_no_mic) {
+		pr_debug("headphone detected, micbias %x\n", bias_value);
+		return 0;
+	} else {
+		pr_debug("headset detected, micbias %x\n", bias_value);
+		return 1;
+	}
 }
 
 static int tabla_codec_enable_hs_detect(struct snd_soc_codec *codec,
@@ -1273,13 +1348,13 @@
 		if (!(tabla->clock_active)) {
 			tabla_codec_enable_config_mode(codec, 1);
 			snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
-				0x04, 0);
+				0x06, 0);
 			usleep_range(calibration->shutdown_plug_removal,
 				calibration->shutdown_plug_removal);
 			tabla_codec_enable_config_mode(codec, 0);
 		} else
 			snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
-				0x04, 0);
+				0x06, 0);
 	}
 
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0xC,
@@ -1346,7 +1421,8 @@
 	return 0;
 }
 
-int tabla_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+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)
 {
 	struct tabla_priv *tabla;
@@ -1355,44 +1431,102 @@
 		return -EINVAL;
 	}
 	tabla = snd_soc_codec_get_drvdata(codec);
-	tabla->jack = jack;
+	tabla->headset_jack = headset_jack;
+	tabla->button_jack = button_jack;
 	tabla->calibration = calibration;
 
 	return tabla_codec_enable_hs_detect(codec, 1);
 }
 EXPORT_SYMBOL_GPL(tabla_hs_detect);
 
-static irqreturn_t tabla_dummy_handler(int irq, void *data)
+static irqreturn_t tabla_dce_handler(int irq, void *data)
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+
+	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__);
+	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);
+	usleep_range(100000, 100000);
+
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t tabla_hs_insert_irq(int irq, void *data)
+static irqreturn_t tabla_release_handler(int irq, void *data)
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	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);
 
-	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
-
-	if (priv->jack) {
-		pr_debug("reporting insertion %d\n", SND_JACK_HEADSET);
-		snd_soc_jack_report(priv->jack, SND_JACK_HEADSET,
-			SND_JACK_HEADSET);
+		priv->buttons_pressed &= ~SND_JACK_BTN_0;
+		snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL,
+			0x08);
+		tabla_codec_start_hs_polling(codec);
 	}
-	usleep_range(priv->calibration->setup_plug_removal_delay,
-		priv->calibration->setup_plug_removal_delay);
-
-	tabla_codec_setup_hs_polling(codec);
 
 	return IRQ_HANDLED;
 }
 
+static void tabla_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
+{
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+	struct tabla_mbhc_calibration *calibration = tabla->calibration;
+	int micbias_mbhc_reg;
+
+	if (!tabla->ref_cnt && !tabla->mbhc_polling_active)
+		tabla_codec_enable_config_mode(codec, 1);
+
+	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:
+		micbias_mbhc_reg = TABLA_A_MICB_1_MBHC;
+		break;
+	case TABLA_MICBIAS2:
+		micbias_mbhc_reg = TABLA_A_MICB_2_MBHC;
+		break;
+	case TABLA_MICBIAS3:
+		micbias_mbhc_reg = TABLA_A_MICB_3_MBHC;
+		break;
+	case TABLA_MICBIAS4:
+		micbias_mbhc_reg = TABLA_A_MICB_4_MBHC;
+		break;
+	default:
+		pr_err("Error, invalid mic bias line\n");
+		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);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+	if (!tabla->ref_cnt && !tabla->mbhc_polling_active)
+		tabla_codec_enable_config_mode(codec, 0);
+
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
 static void tabla_codec_shutdown_hs_polling(struct snd_soc_codec *codec)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+	tabla_codec_shutdown_hs_removal_detect(codec);
+
 	if (!tabla->ref_cnt) {
 		snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0x00);
 		tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
@@ -1402,6 +1536,52 @@
 	tabla->mbhc_polling_active = false;
 }
 
+static irqreturn_t tabla_hs_insert_irq(int irq, void *data)
+{
+	struct tabla_priv *priv = data;
+	struct snd_soc_codec *codec = priv->codec;
+	int microphone_present;
+
+	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+
+	usleep_range(priv->calibration->setup_plug_removal_delay,
+		priv->calibration->setup_plug_removal_delay);
+
+	if (snd_soc_read(codec, TABLA_A_CDC_MBHC_INT_CTL) & 0x02) {
+		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_removal_detect(codec);
+		tabla_codec_enable_hs_detect(codec, 1);
+		return IRQ_HANDLED;
+	}
+
+	microphone_present = tabla_codec_setup_hs_polling(codec);
+
+	if (microphone_present == 0) {
+		if (priv->headset_jack) {
+			pr_debug("%s: Reporting insertion %d\n", __func__,
+				SND_JACK_HEADPHONE);
+			snd_soc_jack_report(priv->headset_jack,
+				SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+		}
+		tabla_codec_shutdown_hs_polling(codec);
+		tabla_codec_enable_hs_detect(codec, 0);
+	} else if (microphone_present == 1) {
+		if (priv->headset_jack) {
+			pr_debug("%s: Reporting insertion %d\n", __func__,
+				SND_JACK_HEADSET);
+			snd_soc_jack_report(priv->headset_jack,
+				SND_JACK_HEADSET, SND_JACK_HEADSET);
+		}
+		tabla_codec_start_hs_polling(codec);
+	}
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
 {
 	struct tabla_priv *priv = data;
@@ -1413,9 +1593,9 @@
 	usleep_range(priv->calibration->shutdown_plug_removal,
 		priv->calibration->shutdown_plug_removal);
 
-	if (priv->jack) {
-		pr_debug("reporting removal\n");
-		snd_soc_jack_report(priv->jack, 0, SND_JACK_HEADSET);
+	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);
 
@@ -1546,7 +1726,7 @@
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
 
 	ret = tabla_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
-		tabla_dummy_handler, "DC Estimation detect", tabla);
+		tabla_dce_handler, "DC Estimation detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
 			TABLA_IRQ_MBHC_POTENTIAL);
@@ -1554,6 +1734,14 @@
 	}
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
 
+	ret = tabla_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
+		tabla_release_handler, "Button Release detect", tabla);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			TABLA_IRQ_MBHC_RELEASE);
+		goto err_release_irq;
+	}
+
 	ret = tabla_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
 		tabla_slimbus_irq, "SLIMBUS Slave", tabla);
 	if (ret) {
@@ -1569,6 +1757,8 @@
 	return ret;
 
 err_slimbus_irq:
+	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
+err_release_irq:
 	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
 err_potential_irq:
 	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
@@ -1582,6 +1772,7 @@
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 	tabla_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
 	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
 	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
 	tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);