ASoC: Fix audio distortion issue during headset record

Audio is distorted during first 3 secs on headset record
while doing device switch from dmic to headset mic.
Disable BCS before slow insertion detection and enable it
afterwards to resolve the issue.

Change-Id: Ie5bc4b5292e5f69066760cab44d78989a74f13f4
Signed-off-by: Vatsal Bucha <vbucha@codeaurora.org>
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index feb0082..e56127c 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -214,6 +214,12 @@
 				priv->component,
 				BOLERO_MACRO_EVT_RX_COMPANDER_SOFT_RST, data);
 		break;
+	case WCD_BOLERO_EVT_BCS_CLK_OFF:
+		if (priv->macro_params[TX_MACRO].event_handler)
+			priv->macro_params[TX_MACRO].event_handler(
+				priv->component,
+				BOLERO_MACRO_EVT_BCS_CLK_OFF, data);
+		break;
 	default:
 		dev_err(priv->dev, "%s: Invalid event %d trigger from wcd\n",
 			__func__, event);
diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h
index f104f88..fe02ad0 100644
--- a/asoc/codecs/bolero/bolero-cdc.h
+++ b/asoc/codecs/bolero/bolero-cdc.h
@@ -40,7 +40,8 @@
 	BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET,
 	BOLERO_MACRO_EVT_CLK_RESET,
 	BOLERO_MACRO_EVT_REG_WAKE_IRQ,
-	BOLERO_MACRO_EVT_RX_COMPANDER_SOFT_RST
+	BOLERO_MACRO_EVT_RX_COMPANDER_SOFT_RST,
+	BOLERO_MACRO_EVT_BCS_CLK_OFF
 };
 
 struct macro_ops {
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index d3f6894..ba89832 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -31,6 +31,7 @@
 	WCD_BOLERO_EVT_IMPED_TRUE,   /* for imped true */
 	WCD_BOLERO_EVT_IMPED_FALSE,  /* for imped false */
 	WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST,
+	WCD_BOLERO_EVT_BCS_CLK_OFF,
 };
 
 struct wcd_ctrl_platform_data {
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index ca88bfa..73ce9d2 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -172,6 +172,8 @@
 	int tx_clk_status;
 	bool bcs_enable;
 	int dec_mode[NUM_DECIMATORS];
+	bool bcs_clk_en;
+	bool hs_slow_insert_complete;
 };
 
 static bool tx_macro_get_data(struct snd_soc_component *component,
@@ -387,6 +389,15 @@
 	case BOLERO_MACRO_EVT_CLK_RESET:
 		bolero_rsc_clk_reset(tx_dev, TX_CORE_CLK);
 		break;
+	case BOLERO_MACRO_EVT_BCS_CLK_OFF:
+		if (tx_priv->bcs_clk_en)
+			snd_soc_component_update_bits(component,
+				BOLERO_CDC_TX0_TX_PATH_SEC7, 0x40, data << 6);
+		if (data)
+			tx_priv->hs_slow_insert_complete = true;
+		else
+			tx_priv->hs_slow_insert_complete = false;
+		break;
 	}
 	return 0;
 }
@@ -902,8 +913,11 @@
 		if (tx_priv->bcs_enable) {
 			snd_soc_component_update_bits(component, dec_cfg_reg,
 					0x01, 0x01);
-			snd_soc_component_update_bits(component,
-				BOLERO_CDC_TX0_TX_PATH_SEC7, 0x40, 0x40);
+			tx_priv->bcs_clk_en = true;
+			if (tx_priv->hs_slow_insert_complete)
+				snd_soc_component_update_bits(component,
+					BOLERO_CDC_TX0_TX_PATH_SEC7, 0x40,
+					0x40);
 		}
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
@@ -946,6 +960,7 @@
 					0x01, 0x00);
 			snd_soc_component_update_bits(component,
 				BOLERO_CDC_TX0_TX_PATH_SEC7, 0x40, 0x00);
+			tx_priv->bcs_clk_en = false;
 		}
 		break;
 	}
diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c
index 4de19de..9652d11 100644
--- a/asoc/codecs/wcd-mbhc-adc.c
+++ b/asoc/codecs/wcd-mbhc-adc.c
@@ -689,6 +689,13 @@
 	}
 
 correct_plug_type:
+	/*
+	 * Callback to disable BCS slow insertion detection
+	 */
+	if (plug_type == MBHC_PLUG_TYPE_HEADSET ||
+	    plug_type == MBHC_PLUG_TYPE_HEADPHONE)
+		mbhc->mbhc_cb->bcs_enable(mbhc, false);
+
 	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
 	while (!time_after(jiffies, timeout)) {
 		if (mbhc->hs_detect_work_stop) {
@@ -833,6 +840,10 @@
 			wrk_complete = false;
 		}
 	}
+	if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
+	    plug_type == MBHC_PLUG_TYPE_HEADPHONE))
+		mbhc->mbhc_cb->bcs_enable(mbhc, true);
+
 	if (!wrk_complete) {
 		/*
 		 * If plug_tye is headset, we might have already reported either
diff --git a/asoc/codecs/wcd938x/internal.h b/asoc/codecs/wcd938x/internal.h
index 098a60d..46d61a0 100644
--- a/asoc/codecs/wcd938x/internal.h
+++ b/asoc/codecs/wcd938x/internal.h
@@ -149,6 +149,7 @@
 	WCD_BOLERO_EVT_IMPED_TRUE,	/* for imped true */
 	WCD_BOLERO_EVT_IMPED_FALSE,	/* for imped false */
 	WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST,
+	WCD_BOLERO_EVT_BCS_CLK_OFF,
 };
 
 enum {
@@ -182,6 +183,9 @@
 
 extern struct wcd938x_mbhc *wcd938x_soc_get_mbhc(
 				struct snd_soc_component *component);
+extern void wcd938x_disable_bcs_before_slow_insert(
+				struct snd_soc_component *component,
+				bool bcs_disable);
 extern int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
 					int volt, int micb_num);
 extern int wcd938x_get_micb_vout_ctl_val(u32 micb_mv);
diff --git a/asoc/codecs/wcd938x/wcd938x-mbhc.c b/asoc/codecs/wcd938x/wcd938x-mbhc.c
index 27566ee..0687de6 100644
--- a/asoc/codecs/wcd938x/wcd938x-mbhc.c
+++ b/asoc/codecs/wcd938x/wcd938x-mbhc.c
@@ -803,6 +803,15 @@
 			0x04, (enable << 2));
 }
 
+static void wcd938x_mbhc_bcs_enable(struct wcd_mbhc *mbhc,
+						  bool bcs_enable)
+{
+	if (bcs_enable)
+		wcd938x_disable_bcs_before_slow_insert(mbhc->component, false);
+	else
+		wcd938x_disable_bcs_before_slow_insert(mbhc->component, true);
+}
+
 static const struct wcd_mbhc_cb mbhc_cb = {
 	.request_irq = wcd938x_mbhc_request_irq,
 	.irq_control = wcd938x_mbhc_irq_control,
@@ -827,6 +836,7 @@
 	.mbhc_get_moisture_status = wcd938x_mbhc_get_moisture_status,
 	.mbhc_moisture_polling_ctrl = wcd938x_mbhc_moisture_polling_ctrl,
 	.mbhc_moisture_detect_en = wcd938x_mbhc_moisture_detect_en,
+	.bcs_enable = wcd938x_mbhc_bcs_enable,
 };
 
 static int wcd938x_get_hph_type(struct snd_kcontrol *kcontrol,
diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c
index 04954d9..0355ea6 100644
--- a/asoc/codecs/wcd938x/wcd938x.c
+++ b/asoc/codecs/wcd938x/wcd938x.c
@@ -1523,6 +1523,21 @@
 	return 0;
 }
 
+void wcd938x_disable_bcs_before_slow_insert(struct snd_soc_component *component,
+					    bool bcs_disable)
+{
+	struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+	if (wcd938x->update_wcd_event) {
+		if (bcs_disable)
+			wcd938x->update_wcd_event(wcd938x->handle,
+						WCD_BOLERO_EVT_BCS_CLK_OFF, 0);
+		else
+			wcd938x->update_wcd_event(wcd938x->handle,
+						WCD_BOLERO_EVT_BCS_CLK_OFF, 1);
+	}
+}
+
 int wcd938x_tx_channel_config(struct snd_soc_component *component,
 			      int channel, int mode)
 {
diff --git a/include/asoc/wcd-mbhc-v2.h b/include/asoc/wcd-mbhc-v2.h
index 1f9d64e..f3ee728 100644
--- a/include/asoc/wcd-mbhc-v2.h
+++ b/include/asoc/wcd-mbhc-v2.h
@@ -451,6 +451,8 @@
 };
 
 struct wcd_mbhc_cb {
+	void (*bcs_enable)
+		(struct wcd_mbhc *mbhc, bool bcs_enable);
 	int (*enable_mb_source)(struct wcd_mbhc *mbhc, bool turn_on);
 	void (*trim_btn_reg)(struct snd_soc_component *component);
 	void (*compute_impedance)(struct wcd_mbhc *mbhc,