Merge "asoc: codecs: Add proper null checks and initialize variables"
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index 1a618d0..62c4a37 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -160,6 +160,12 @@
 	{176400, 0xB}, {352800, 0xC},
 };
 
+struct rx_macro_bcl_pmic_params {
+	u8 id;
+	u8 sid;
+	u8 ppid;
+};
+
 static int rx_macro_hw_params(struct snd_pcm_substream *substream,
 			       struct snd_pcm_hw_params *params,
 			       struct snd_soc_dai *dai);
@@ -258,6 +264,9 @@
 	struct platform_device *pdev_child_devices
 			[RX_MACRO_CHILD_DEVICES_MAX];
 	int child_count;
+	int is_softclip_on;
+	int softclip_clk_users;
+	struct rx_macro_bcl_pmic_params bcl_pmic_params;
 };
 
 static struct snd_soc_dai_driver rx_macro_dai[];
@@ -321,6 +330,14 @@
 static const struct soc_enum rx_macro_ear_mode_enum =
 	SOC_ENUM_SINGLE_EXT(2, rx_macro_ear_mode_text);
 
+static const char * const rx_macro_vbat_bcl_gsm_mode_text[] = {"OFF", "ON"};
+static const struct soc_enum rx_macro_vbat_bcl_gsm_mode_enum =
+	SOC_ENUM_SINGLE_EXT(2, rx_macro_vbat_bcl_gsm_mode_text);
+
+static const struct snd_kcontrol_new rx_int2_1_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("RX AUX VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
 RX_MACRO_DAPM_ENUM(rx_int0_2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
 		rx_int_mix_mux_text);
 RX_MACRO_DAPM_ENUM(rx_int1_2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
@@ -1164,6 +1181,52 @@
 	return 0;
 }
 
+static void rx_macro_enable_softclip_clk(struct snd_soc_codec *codec,
+					 struct rx_macro_priv *rx_priv,
+					 bool enable)
+{
+	if (enable) {
+		if (rx_priv->softclip_clk_users == 0)
+			snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_SOFTCLIP_CRC,
+				0x01, 0x01);
+		rx_priv->softclip_clk_users++;
+	} else {
+		rx_priv->softclip_clk_users--;
+		if (rx_priv->softclip_clk_users == 0)
+			snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_SOFTCLIP_CRC,
+				0x01, 0x00);
+	}
+}
+
+static int rx_macro_config_softclip(struct snd_soc_codec *codec,
+				struct rx_macro_priv *rx_priv,
+				int event)
+{
+	dev_dbg(codec->dev, "%s: event %d, enabled %d\n",
+		__func__, event, rx_priv->is_softclip_on);
+
+	if (!rx_priv->is_softclip_on)
+		return 0;
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* Enable Softclip clock */
+		rx_macro_enable_softclip_clk(codec, rx_priv, true);
+		/* Enable Softclip control */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x01, 0x01);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x01, 0x00);
+		rx_macro_enable_softclip_clk(codec, rx_priv, false);
+	}
+
+	return 0;
+}
+
 static inline void
 rx_macro_enable_clsh_block(struct rx_macro_priv *rx_priv, bool enable)
 {
@@ -1396,6 +1459,191 @@
 	return 0;
 }
 
+static int rx_macro_vbat_bcl_gsm_mode_func_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ucontrol->value.integer.value[0] =
+		((snd_soc_read(codec, BOLERO_CDC_RX_BCL_VBAT_CFG) & 0x04) ?
+		  1 : 0);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int rx_macro_vbat_bcl_gsm_mode_func_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	/* Set Vbat register configuration for GSM mode bit based on value */
+	if (ucontrol->value.integer.value[0])
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG,
+						0x04, 0x04);
+	else
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG,
+						0x04, 0x00);
+
+	return 0;
+}
+
+static int rx_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct device *rx_dev = NULL;
+	struct rx_macro_priv *rx_priv = NULL;
+
+	if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+		return -EINVAL;
+
+	ucontrol->value.integer.value[0] = rx_priv->is_softclip_on;
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int rx_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct device *rx_dev = NULL;
+	struct rx_macro_priv *rx_priv = NULL;
+
+	if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+		return -EINVAL;
+
+	rx_priv->is_softclip_on =  ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: soft clip enable = %d\n", __func__,
+		rx_priv->is_softclip_on);
+
+	return 0;
+}
+
+static int rx_macro_enable_vbat(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol,
+				 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct device *rx_dev = NULL;
+	struct rx_macro_priv *rx_priv = NULL;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+	if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Enable clock for VBAT block */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_PATH_CTL, 0x10, 0x10);
+		/* Enable VBAT block */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_CFG, 0x01, 0x01);
+		/* Update interpolator with 384K path */
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1,
+					0x80, 0x80);
+		/* Update DSM FS rate */
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_SEC7,
+					0x02, 0x02);
+		/* Use attenuation mode */
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG,
+					0x02, 0x00);
+		/* BCL block needs softclip clock to be enabled */
+		rx_macro_enable_softclip_clk(codec, rx_priv, true);
+		/* Enable VBAT at channel level */
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1,
+					0x02, 0x02);
+		/* Set the ATTK1 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3,
+			0xFF, 0x00);
+		/* Set the ATTK2 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6,
+			0xFF, 0x00);
+		/* Set the ATTK3 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9,
+			0xFF, 0x00);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1,
+					0x80, 0x00);
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_SEC7,
+					0x02, 0x00);
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG,
+					0x02, 0x02);
+		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1,
+					0x02, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8,
+				0xFF, 0x00);
+		snd_soc_update_bits(codec,
+				BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9,
+				0xFF, 0x00);
+		rx_macro_enable_softclip_clk(codec, rx_priv, false);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_CFG, 0x01, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_PATH_CTL, 0x10, 0x00);
+		break;
+	default:
+		dev_err(rx_dev, "%s: Invalid event %d\n", __func__, event);
+		break;
+	}
+	return 0;
+}
+
 static void rx_macro_idle_detect_control(struct snd_soc_codec *codec,
 					 struct rx_macro_priv *rx_priv,
 					 int interp, int event)
@@ -1509,6 +1757,9 @@
 						       event);
 			rx_macro_config_compander(codec, rx_priv,
 						interp_idx, event);
+			if (interp_idx ==  INTERP_AUX)
+				rx_macro_config_softclip(codec, rx_priv,
+							event);
 			rx_macro_config_classh(codec, rx_priv,
 						interp_idx, event);
 		}
@@ -1523,6 +1774,9 @@
 						interp_idx, event);
 			rx_macro_config_compander(codec, rx_priv,
 						interp_idx, event);
+			if (interp_idx ==  INTERP_AUX)
+				rx_macro_config_softclip(codec, rx_priv,
+							event);
 			rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx,
 						       event);
 			rx_macro_hd2_control(codec, interp_idx, event);
@@ -1891,6 +2145,13 @@
 	SOC_ENUM_EXT("RX_EAR Mode", rx_macro_ear_mode_enum,
 		rx_macro_get_ear_mode, rx_macro_put_ear_mode),
 
+	SOC_ENUM_EXT("RX_GSM mode Enable", rx_macro_vbat_bcl_gsm_mode_enum,
+			rx_macro_vbat_bcl_gsm_mode_func_get,
+			rx_macro_vbat_bcl_gsm_mode_func_put),
+	SOC_SINGLE_EXT("RX_Softclip Enable", SND_SOC_NOPM, 0, 1, 0,
+		     rx_macro_soft_clip_enable_get,
+		     rx_macro_soft_clip_enable_put),
+
 	SOC_SINGLE_SX_TLV("IIR0 INP0 Volume",
 		BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40,
 		digital_gain),
@@ -2089,6 +2350,12 @@
 		0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk,
 		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_MIXER_E("RX INT2_1 VBAT", SND_SOC_NOPM,
+		0, 0, rx_int2_1_vbat_mix_switch,
+		ARRAY_SIZE(rx_int2_1_vbat_mix_switch),
+		rx_macro_enable_vbat,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
 	SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -2279,6 +2546,10 @@
 	{"HPHR_OUT", NULL, "RX_MCLK"},
 
 	{"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"},
+
+	{"RX INT2_1 VBAT", "RX AUX VBAT Enable", "RX INT2_1 INTERP"},
+	{"RX INT2 SEC MIX", NULL, "RX INT2_1 VBAT"},
+
 	{"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
 	{"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
 	{"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
@@ -2449,6 +2720,53 @@
 	return ret;
 }
 
+static void rx_macro_init_bcl_pmic_reg(struct snd_soc_codec *codec)
+{
+	struct device *rx_dev = NULL;
+	struct rx_macro_priv *rx_priv = NULL;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer!\n", __func__);
+		return;
+	}
+
+	if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__))
+		return;
+
+	switch (rx_priv->bcl_pmic_params.id) {
+	case 0:
+		/* Enable ID0 to listen to respective PMIC group interrupts */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1, 0x02, 0x02);
+		/* Update MC_SID0 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1, 0x0F,
+			rx_priv->bcl_pmic_params.sid);
+		/* Update MC_PPID0 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG2, 0xFF,
+			rx_priv->bcl_pmic_params.ppid);
+		break;
+	case 1:
+		/* Enable ID1 to listen to respective PMIC group interrupts */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1, 0x01, 0x01);
+		/* Update MC_SID1 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG3, 0x0F,
+			rx_priv->bcl_pmic_params.sid);
+		/* Update MC_PPID1 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1, 0xFF,
+			rx_priv->bcl_pmic_params.ppid);
+		break;
+	default:
+		dev_err(rx_dev, "%s: PMIC ID is invalid\n",
+		       __func__, rx_priv->bcl_pmic_params.id);
+		break;
+	}
+}
+
 static int rx_macro_init(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@@ -2501,6 +2819,7 @@
 	snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG3, 0x03, 0x02);
 	snd_soc_update_bits(codec, BOLERO_CDC_RX_RX1_RX_PATH_CFG3, 0x03, 0x02);
 	snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG3, 0x03, 0x02);
+	rx_macro_init_bcl_pmic_reg(codec);
 
 	rx_priv->codec = codec;
 
@@ -2646,6 +2965,7 @@
 	char __iomem *rx_io_base = NULL, *muxsel_io = NULL;
 	int ret = 0;
 	struct clk *rx_core_clk = NULL, *rx_npl_clk = NULL;
+	u8 bcl_pmic_params[3];
 
 	rx_priv = devm_kzalloc(&pdev->dev, sizeof(struct rx_macro_priv),
 			    GFP_KERNEL);
@@ -2715,6 +3035,19 @@
 		return ret;
 	}
 	rx_priv->rx_npl_clk = rx_npl_clk;
+
+	ret = of_property_read_u8_array(pdev->dev.of_node,
+				"qcom,rx-bcl-pmic-params", bcl_pmic_params,
+				sizeof(bcl_pmic_params));
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n",
+			__func__, "qcom,rx-bcl-pmic-params");
+	} else {
+		rx_priv->bcl_pmic_params.id = bcl_pmic_params[0];
+		rx_priv->bcl_pmic_params.sid = bcl_pmic_params[1];
+		rx_priv->bcl_pmic_params.ppid = bcl_pmic_params[2];
+	}
+
 	dev_set_drvdata(&pdev->dev, rx_priv);
 	mutex_init(&rx_priv->mclk_lock);
 	mutex_init(&rx_priv->swr_clk_lock);
diff --git a/asoc/codecs/bolero/wsa-macro.c b/asoc/codecs/bolero/wsa-macro.c
index 47c5cd4..3fe637e 100644
--- a/asoc/codecs/bolero/wsa-macro.c
+++ b/asoc/codecs/bolero/wsa-macro.c
@@ -50,6 +50,7 @@
 #define WSA_MACRO_MUX_CFG_OFFSET 0x8
 #define WSA_MACRO_MUX_CFG1_OFFSET 0x4
 #define WSA_MACRO_RX_COMP_OFFSET 0x40
+#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40
 #define WSA_MACRO_RX_PATH_OFFSET 0x80
 #define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10
 #define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C
@@ -82,6 +83,12 @@
 	WSA_MACRO_COMP_MAX
 };
 
+enum {
+	WSA_MACRO_SOFTCLIP0, /* RX0 */
+	WSA_MACRO_SOFTCLIP1, /* RX1 */
+	WSA_MACRO_SOFTCLIP_MAX
+};
+
 struct interp_sample_rate {
 	int sample_rate;
 	int rate_val;
@@ -141,6 +148,12 @@
 			  int action);
 };
 
+struct wsa_macro_bcl_pmic_params {
+	u8 id;
+	u8 sid;
+	u8 ppid;
+};
+
 enum {
 	WSA_MACRO_AIF_INVALID = 0,
 	WSA_MACRO_AIF1_PB,
@@ -205,6 +218,9 @@
 	int ear_spkr_gain;
 	int spkr_gain_offset;
 	int spkr_mode;
+	int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
+	int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
+	struct wsa_macro_bcl_pmic_params bcl_pmic_params;
 };
 
 static int wsa_macro_config_ear_spkr_gain(struct snd_soc_codec *codec,
@@ -242,10 +258,24 @@
 	"NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2"
 };
 
+static const char * const wsa_macro_vbat_bcl_gsm_mode_text[] = {
+	"OFF", "ON"
+};
+
+static const struct snd_kcontrol_new wsa_int0_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("WSA RX0 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new wsa_int1_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("WSA RX1 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
 static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum,
 				wsa_macro_ear_spkr_pa_gain_text);
 static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_spkr_boost_stage_enum,
 			wsa_macro_speaker_boost_stage_text);
+static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_vbat_bcl_gsm_mode_enum,
+			wsa_macro_vbat_bcl_gsm_mode_text);
 
 /* RX INT0 */
 static const struct soc_enum rx0_prim_inp0_chain_enum =
@@ -1095,6 +1125,82 @@
 	return 0;
 }
 
+static void wsa_macro_enable_softclip_clk(struct snd_soc_codec *codec,
+					 struct wsa_macro_priv *wsa_priv,
+					 int path,
+					 bool enable)
+{
+	u16 softclip_clk_reg = BOLERO_CDC_WSA_SOFTCLIP0_CRC +
+			(path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+	u8 softclip_mux_mask = (1 << path);
+	u8 softclip_mux_value = (1 << path);
+
+	dev_dbg(codec->dev, "%s: path %d, enable %d\n",
+		__func__, path, enable);
+	if (enable) {
+		if (wsa_priv->softclip_clk_users[path] == 0) {
+			snd_soc_update_bits(codec,
+				softclip_clk_reg, 0x01, 0x01);
+			snd_soc_update_bits(codec,
+				BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+				softclip_mux_mask, softclip_mux_value);
+		}
+		wsa_priv->softclip_clk_users[path]++;
+	} else {
+		wsa_priv->softclip_clk_users[path]--;
+		if (wsa_priv->softclip_clk_users[path] == 0) {
+			snd_soc_update_bits(codec,
+				softclip_clk_reg, 0x01, 0x00);
+			snd_soc_update_bits(codec,
+				BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0,
+				softclip_mux_mask, 0x00);
+		}
+	}
+}
+
+static int wsa_macro_config_softclip(struct snd_soc_codec *codec,
+				int path, int event)
+{
+	u16 softclip_ctrl_reg = 0;
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+	int softclip_path = 0;
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return -EINVAL;
+
+	if (path == WSA_MACRO_COMP1)
+		softclip_path = WSA_MACRO_SOFTCLIP0;
+	else if (path == WSA_MACRO_COMP2)
+		softclip_path = WSA_MACRO_SOFTCLIP1;
+
+	dev_dbg(codec->dev, "%s: event %d path %d, enabled %d\n",
+		__func__, event, softclip_path,
+		wsa_priv->is_softclip_on[softclip_path]);
+
+	if (!wsa_priv->is_softclip_on[softclip_path])
+		return 0;
+
+	softclip_ctrl_reg = BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL +
+				(softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* Enable Softclip clock and mux */
+		wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path,
+						true);
+		/* Enable Softclip control */
+		snd_soc_update_bits(codec, softclip_ctrl_reg, 0x01, 0x01);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, softclip_ctrl_reg, 0x01, 0x00);
+		wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path,
+						false);
+	}
+
+	return 0;
+}
+
 static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind)
 {
 	u16 prim_int_reg = 0;
@@ -1204,6 +1310,7 @@
 		break;
 	case SND_SOC_DAPM_POST_PMU:
 		wsa_macro_config_compander(codec, w->shift, event);
+		wsa_macro_config_softclip(codec, w->shift, event);
 		/* apply gain after int clk is enabled */
 		if ((wsa_priv->spkr_gain_offset ==
 			WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
@@ -1231,6 +1338,7 @@
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		wsa_macro_config_compander(codec, w->shift, event);
+		wsa_macro_config_softclip(codec, w->shift, event);
 		wsa_macro_enable_prim_interpolator(codec, reg, event);
 		if ((wsa_priv->spkr_gain_offset ==
 			WSA_MACRO_GAIN_OFFSET_M1P5_DB) &&
@@ -1356,6 +1464,127 @@
 	return 0;
 }
 
+
+static int wsa_macro_enable_vbat(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol,
+				 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+	u16 vbat_path_cfg = 0;
+	int softclip_path = 0;
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return -EINVAL;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+	if (!strcmp(w->name, "WSA_RX INT0 VBAT")) {
+		vbat_path_cfg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG1;
+		softclip_path = WSA_MACRO_SOFTCLIP0;
+	} else if (!strcmp(w->name, "WSA_RX INT1 VBAT")) {
+		vbat_path_cfg = BOLERO_CDC_WSA_RX1_RX_PATH_CFG1;
+		softclip_path = WSA_MACRO_SOFTCLIP1;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Enable clock for VBAT block */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x10);
+		/* Enable VBAT block */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x01);
+		/* Update interpolator with 384K path */
+		snd_soc_update_bits(codec, vbat_path_cfg, 0x80, 0x80);
+		/* Use attenuation mode */
+		snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG,
+					0x02, 0x00);
+		/*
+		 * BCL block needs softclip clock and mux config to be enabled
+		 */
+		wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path,
+					      true);
+		/* Enable VBAT at channel level */
+		snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02);
+		/* Set the ATTK1 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3,
+			0xFF, 0x00);
+		/* Set the ATTK2 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6,
+			0xFF, 0x00);
+		/* Set the ATTK3 gain */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7,
+			0xFF, 0xFF);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8,
+			0xFF, 0x03);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9,
+			0xFF, 0x00);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, vbat_path_cfg, 0x80, 0x00);
+		snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG,
+					0x02, 0x02);
+		snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8,
+			0xFF, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9,
+			0xFF, 0x00);
+		wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path,
+					      false);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x00);
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x00);
+		break;
+	default:
+		dev_err(wsa_dev, "%s: Invalid event %d\n", __func__, event);
+		break;
+	}
+	return 0;
+}
+
 static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w,
 				 struct snd_kcontrol *kcontrol,
 				 int event)
@@ -1637,6 +1866,80 @@
 	return 0;
 }
 
+static int wsa_macro_vbat_bcl_gsm_mode_func_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ucontrol->value.integer.value[0] =
+	    ((snd_soc_read(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG) & 0x04) ?
+	    1 : 0);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int wsa_macro_vbat_bcl_gsm_mode_func_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	/* Set Vbat register configuration for GSM mode bit based on value */
+	if (ucontrol->value.integer.value[0])
+		snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG,
+						0x04, 0x04);
+	else
+		snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG,
+						0x04, 0x00);
+
+	return 0;
+}
+
+static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+	int path = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return -EINVAL;
+
+	ucontrol->value.integer.value[0] = wsa_priv->is_softclip_on[path];
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+	int path = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return -EINVAL;
+
+	wsa_priv->is_softclip_on[path] =  ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: soft clip enable for %d: %d\n", __func__,
+		path, wsa_priv->is_softclip_on[path]);
+
+	return 0;
+}
+
 static const struct snd_kcontrol_new wsa_macro_snd_controls[] = {
 	SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum,
 		     wsa_macro_ear_spkr_pa_gain_get,
@@ -1649,6 +1952,17 @@
 		wsa_macro_spkr_boost_stage_enum,
 		wsa_macro_spkr_right_boost_stage_get,
 		wsa_macro_spkr_right_boost_stage_put),
+	SOC_ENUM_EXT("GSM mode Enable", wsa_macro_vbat_bcl_gsm_mode_enum,
+		     wsa_macro_vbat_bcl_gsm_mode_func_get,
+		     wsa_macro_vbat_bcl_gsm_mode_func_put),
+	SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM,
+			WSA_MACRO_SOFTCLIP0, 1, 0,
+			wsa_macro_soft_clip_enable_get,
+			wsa_macro_soft_clip_enable_put),
+	SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM,
+			WSA_MACRO_SOFTCLIP1, 1, 0,
+			wsa_macro_soft_clip_enable_get,
+			wsa_macro_soft_clip_enable_put),
 	SOC_SINGLE_SX_TLV("WSA_RX0 Digital Volume",
 			  BOLERO_CDC_WSA_RX0_RX_VOL_CTL,
 			  0, -84, 40, digital_gain),
@@ -1862,6 +2176,17 @@
 		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
 		SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_MIXER_E("WSA_RX INT0 VBAT", SND_SOC_NOPM,
+		0, 0, wsa_int0_vbat_mix_switch,
+		ARRAY_SIZE(wsa_int0_vbat_mix_switch),
+		wsa_macro_enable_vbat,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("WSA_RX INT1 VBAT", SND_SOC_NOPM,
+		0, 0, wsa_int1_vbat_mix_switch,
+		ARRAY_SIZE(wsa_int1_vbat_mix_switch),
+		wsa_macro_enable_vbat,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
 	SND_SOC_DAPM_INPUT("VIINPUT_WSA"),
 
 	SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"),
@@ -1939,6 +2264,10 @@
 	{"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"},
 	{"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"},
 	{"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"},
+
+	{"WSA_RX INT0 VBAT", "WSA RX0 VBAT Enable", "WSA_RX INT0 INTERP"},
+	{"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 VBAT"},
+
 	{"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"},
 	{"WSA_SPK1 OUT", NULL, "WSA_MCLK"},
 
@@ -1974,6 +2303,10 @@
 
 	{"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"},
 	{"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"},
+
+	{"WSA_RX INT1 VBAT", "WSA RX1 VBAT Enable", "WSA_RX INT1 INTERP"},
+	{"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 VBAT"},
+
 	{"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"},
 	{"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"},
 	{"WSA_SPK2 OUT", NULL, "WSA_MCLK"},
@@ -2006,6 +2339,53 @@
 	{BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x01, 0x01},
 };
 
+static void wsa_macro_init_bcl_pmic_reg(struct snd_soc_codec *codec)
+{
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer!\n", __func__);
+		return;
+	}
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return;
+
+	switch (wsa_priv->bcl_pmic_params.id) {
+	case 0:
+		/* Enable ID0 to listen to respective PMIC group interrupts */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x02, 0x02);
+		/* Update MC_SID0 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1, 0x0F,
+			wsa_priv->bcl_pmic_params.sid);
+		/* Update MC_PPID0 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2, 0xFF,
+			wsa_priv->bcl_pmic_params.ppid);
+		break;
+	case 1:
+		/* Enable ID1 to listen to respective PMIC group interrupts */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x01, 0x01);
+		/* Update MC_SID1 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3, 0x0F,
+			wsa_priv->bcl_pmic_params.sid);
+		/* Update MC_PPID1 */
+		snd_soc_update_bits(codec,
+			BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4, 0xFF,
+			wsa_priv->bcl_pmic_params.ppid);
+		break;
+	default:
+		dev_err(wsa_dev, "%s: PMIC ID is invalid %d\n",
+		       __func__, wsa_priv->bcl_pmic_params.id);
+		break;
+	}
+}
+
 static void wsa_macro_init_reg(struct snd_soc_codec *codec)
 {
 	int i;
@@ -2015,6 +2395,8 @@
 				wsa_macro_reg_init[i].reg,
 				wsa_macro_reg_init[i].mask,
 				wsa_macro_reg_init[i].val);
+
+	wsa_macro_init_bcl_pmic_reg(codec);
 }
 
 static int wsa_swrm_clock(void *handle, bool enable)
@@ -2245,6 +2627,7 @@
 	char __iomem *wsa_io_base;
 	int ret = 0;
 	struct clk *wsa_core_clk, *wsa_npl_clk;
+	u8 bcl_pmic_params[3];
 
 	wsa_priv = devm_kzalloc(&pdev->dev, sizeof(struct wsa_macro_priv),
 				GFP_KERNEL);
@@ -2300,6 +2683,19 @@
 		return ret;
 	}
 	wsa_priv->wsa_npl_clk = wsa_npl_clk;
+
+	ret = of_property_read_u8_array(pdev->dev.of_node,
+				"qcom,wsa-bcl-pmic-params", bcl_pmic_params,
+				sizeof(bcl_pmic_params));
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n",
+			__func__, "qcom,wsa-bcl-pmic-params");
+	} else {
+		wsa_priv->bcl_pmic_params.id = bcl_pmic_params[0];
+		wsa_priv->bcl_pmic_params.sid = bcl_pmic_params[1];
+		wsa_priv->bcl_pmic_params.ppid = bcl_pmic_params[2];
+	}
+
 	dev_set_drvdata(&pdev->dev, wsa_priv);
 	mutex_init(&wsa_priv->mclk_lock);
 	mutex_init(&wsa_priv->swr_clk_lock);
diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c
index 49c17c4..3667676 100644
--- a/asoc/codecs/wcd934x/wcd934x.c
+++ b/asoc/codecs/wcd934x/wcd934x.c
@@ -9977,6 +9977,12 @@
 	int ret;
 
 	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	if (!codec->component.card) {
+		dev_err(codec->dev, "%s: sound card is not enumerated.\n",
+			__func__);
+		return -EINVAL;
+	}
+
 	priv = snd_soc_codec_get_drvdata(codec);
 	for (count = 0; count < NUM_CODEC_DAIS; count++)
 		priv->dai[count].bus_down_in_recovery = true;
@@ -10027,6 +10033,11 @@
 	struct wcd_mbhc *mbhc;
 
 	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	if (!codec->component.card) {
+		dev_err(codec->dev, "%s: sound card is not enumerated.\n",
+			__func__);
+		return -EINVAL;
+	}
 	tavil = snd_soc_codec_get_drvdata(codec);
 	control = dev_get_drvdata(codec->dev->parent);
 
diff --git a/asoc/codecs/wcd937x/internal.h b/asoc/codecs/wcd937x/internal.h
index f883e96..e2f1e03 100644
--- a/asoc/codecs/wcd937x/internal.h
+++ b/asoc/codecs/wcd937x/internal.h
@@ -58,6 +58,8 @@
 	struct wcd937x_mbhc *mbhc;
 
 	u32 hph_mode;
+	bool comp1_enable;
+	bool comp2_enable;
 
 	struct irq_domain *virq;
 	struct wcd_irq_info irq_info;
diff --git a/asoc/codecs/wcd937x/wcd937x-mbhc.c b/asoc/codecs/wcd937x/wcd937x-mbhc.c
index c7be66d..52bc009 100644
--- a/asoc/codecs/wcd937x/wcd937x-mbhc.c
+++ b/asoc/codecs/wcd937x/wcd937x-mbhc.c
@@ -79,6 +79,8 @@
 			  WCD937X_MBHC_NEW_CTL_2, 0x03, 0, 0),
 	WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
 			  WCD937X_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE",
+			  WCD937X_ANA_MBHC_RESULT_3, 0x10, 4, 0),
 	WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
 			  WCD937X_ANA_MBHC_RESULT_3, 0x20, 5, 0),
 	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
@@ -413,8 +415,10 @@
 ramp_down:
 	i = 0;
 	while (x1) {
-		regmap_bulk_read(wcd937x->regmap,
-				 WCD937X_ANA_MBHC_RESULT_1, (u8 *)&val, 2);
+		regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val);
+		regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val1);
+		val = val << 0x8;
+		val |= val1;
 		x1 = WCD937X_MBHC_GET_X1(val);
 		i++;
 		if (i == WCD937X_ZDET_NUM_MEASUREMENTS)
@@ -972,7 +976,7 @@
 
 	wcd_mbhc_deinit(wcd_mbhc);
 	ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids,
-			    wcd_mbhc_registers, false);
+			    wcd_mbhc_registers, WCD937X_ZDET_SUPPORTED);
 	if (ret) {
 		dev_err(codec->dev, "%s: mbhc initialization failed\n",
 			__func__);
@@ -1024,7 +1028,7 @@
 
 	ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb,
 				&intr_ids, wcd_mbhc_registers,
-				false);
+				WCD937X_ZDET_SUPPORTED);
 	if (ret) {
 		dev_err(codec->dev, "%s: mbhc initialization failed\n",
 			__func__);
diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c
index f4c45bb..2d7f40a 100644
--- a/asoc/codecs/wcd937x/wcd937x.c
+++ b/asoc/codecs/wcd937x/wcd937x.c
@@ -398,8 +398,19 @@
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
 				    0x0F, 0x02);
-		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0,
-				    0x02, 0x02);
+		if (wcd937x->comp1_enable) {
+			snd_soc_update_bits(codec,
+					WCD937X_DIGITAL_CDC_COMP_CTL_0,
+					0x02, 0x02);
+			snd_soc_update_bits(codec,
+					WCD937X_HPH_L_EN, 0x20, 0x00);
+		} else {
+			snd_soc_update_bits(codec,
+					WCD937X_DIGITAL_CDC_COMP_CTL_0,
+					0x02, 0x00);
+			snd_soc_update_bits(codec,
+					WCD937X_HPH_L_EN, 0x20, 0x20);
+		}
 		usleep_range(5000, 5010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x00);
@@ -439,8 +450,19 @@
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
 				    0x0F, 0x02);
-		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0,
-				    0x01, 0x01);
+		if (wcd937x->comp2_enable) {
+			snd_soc_update_bits(codec,
+					WCD937X_DIGITAL_CDC_COMP_CTL_0,
+					0x01, 0x01);
+			snd_soc_update_bits(codec,
+					WCD937X_HPH_R_EN, 0x20, 0x00);
+		} else {
+			snd_soc_update_bits(codec,
+					WCD937X_DIGITAL_CDC_COMP_CTL_0,
+					0x01, 0x00);
+			snd_soc_update_bits(codec,
+					WCD937X_HPH_R_EN, 0x20, 0x20);
+		}
 		usleep_range(5000, 5010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x00);
@@ -677,6 +699,7 @@
 {
 
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
 		w->name, event);
@@ -704,11 +727,13 @@
 		usleep_range(500, 510);
 
 		wcd937x_rx_connect_port(codec, HPH_L, true);
-		wcd937x_rx_connect_port(codec, COMP_L, true);
+		if (wcd937x->comp1_enable)
+			wcd937x_rx_connect_port(codec, COMP_L, true);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		wcd937x_rx_connect_port(codec, HPH_L, false);
-		wcd937x_rx_connect_port(codec, COMP_L, false);
+		if (wcd937x->comp1_enable)
+			wcd937x_rx_connect_port(codec, COMP_L, false);
 		wcd937x_rx_clk_disable(codec);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
 				    0x01, 0x00);
@@ -720,6 +745,7 @@
 			      struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
 		w->name, event);
@@ -745,11 +771,13 @@
 		usleep_range(500, 510);
 
 		wcd937x_rx_connect_port(codec, HPH_R, true);
-		wcd937x_rx_connect_port(codec, COMP_R, true);
+		if (wcd937x->comp2_enable)
+			wcd937x_rx_connect_port(codec, COMP_R, true);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		wcd937x_rx_connect_port(codec, HPH_R, false);
-		wcd937x_rx_connect_port(codec, COMP_R, false);
+		if (wcd937x->comp2_enable)
+			wcd937x_rx_connect_port(codec, COMP_R, false);
 		wcd937x_rx_clk_disable(codec);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
 				    0x02, 0x00);
@@ -1223,6 +1251,42 @@
 	return 0;
 }
 
+static int wcd937x_get_compander(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+
+	ucontrol->value.integer.value[0] = hphr ? wcd937x->comp2_enable :
+						wcd937x->comp1_enable;
+	return 0;
+}
+
+static int wcd937x_set_compander(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+	if (hphr)
+		wcd937x->comp2_enable = value;
+	else
+		wcd937x->comp1_enable = value;
+
+	return 0;
+}
+
 static const char * const rx_hph_mode_mux_text[] = {
 	"CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
 	"CLS_H_ULP", "CLS_AB_HIFI",
@@ -1235,6 +1299,10 @@
 static const struct snd_kcontrol_new wcd937x_snd_controls[] = {
 	SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
 		wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put),
+	SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0,
+		wcd937x_get_compander, wcd937x_set_compander),
+	SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0,
+		wcd937x_get_compander, wcd937x_set_compander),
 
 	SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain),
 	SOC_SINGLE_TLV("HPHR Volume", WCD937X_HPH_R_EN, 0, 20, 1, line_gain),
diff --git a/dsp/codecs/audio_aac.c b/dsp/codecs/audio_aac.c
index c742f72..44444ef 100644
--- a/dsp/codecs/audio_aac.c
+++ b/dsp/codecs/audio_aac.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -230,7 +230,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
+			pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n",
 				__func__, audio, rc);
 	}
 	}
@@ -339,7 +339,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_compat_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
+			pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n",
 				__func__, audio, rc);
 	}
 	}
diff --git a/dsp/codecs/audio_alac.c b/dsp/codecs/audio_alac.c
index cfb3087..975a1e9 100644
--- a/dsp/codecs/audio_alac.c
+++ b/dsp/codecs/audio_alac.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -151,7 +151,7 @@
 	default: {
 		rc = audio->codec_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
@@ -253,7 +253,7 @@
 	default: {
 		rc = audio->codec_compat_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
diff --git a/dsp/codecs/audio_ape.c b/dsp/codecs/audio_ape.c
index 3df6347..f2a6bf7 100644
--- a/dsp/codecs/audio_ape.c
+++ b/dsp/codecs/audio_ape.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -137,7 +137,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
@@ -235,7 +235,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_compat_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
diff --git a/dsp/codecs/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c
index db07d3f..298a77d 100644
--- a/dsp/codecs/audio_utils_aio.c
+++ b/dsp/codecs/audio_utils_aio.c
@@ -201,17 +201,17 @@
 	if (audio->enabled) {
 		rc = q6asm_cmd(audio->ac, CMD_PAUSE);
 		if (rc < 0)
-			pr_err("%s[%pK]: pause cmd failed rc=%d\n",
+			pr_err_ratelimited("%s[%pK]: pause cmd failed rc=%d\n",
 				__func__, audio, rc);
 
 		if (rc == 0) {
 			/* Send suspend only if pause was successful */
 			rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
 			if (rc < 0)
-				pr_err("%s[%pK]: suspend cmd failed rc=%d\n",
+				pr_err_ratelimited("%s[%pK]: suspend cmd failed rc=%d\n",
 					__func__, audio, rc);
 		} else
-			pr_err("%s[%pK]: not sending suspend since pause failed\n",
+			pr_err_ratelimited("%s[%pK]: not sending suspend since pause failed\n",
 				__func__, audio);
 
 	} else
@@ -230,7 +230,7 @@
 		if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
 			rc = audio_aio_pause(audio);
 			if (rc < 0)
-				pr_err("%s[%pK}: pause cmd failed rc=%d\n",
+				pr_err_ratelimited("%s[%pK}: pause cmd failed rc=%d\n",
 					__func__, audio,
 					rc);
 			else
@@ -238,13 +238,13 @@
 		}
 		rc = q6asm_cmd(audio->ac, CMD_FLUSH);
 		if (rc < 0)
-			pr_err("%s[%pK]: flush cmd failed rc=%d\n",
+			pr_err_ratelimited("%s[%pK]: flush cmd failed rc=%d\n",
 				__func__, audio, rc);
 		/* Not in stop state, reenable the stream */
 		if (audio->stopped == 0) {
 			rc = audio_aio_enable(audio);
 			if (rc)
-				pr_err("%s[%pK]:audio re-enable failed\n",
+				pr_err_ratelimited("%s[%pK]:audio re-enable failed\n",
 					__func__, audio);
 			else {
 				audio->enabled = 1;
@@ -268,7 +268,7 @@
 
 	rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
 	if (rc < 0)
-		pr_err("%s[%pK}: output port flush cmd failed rc=%d\n",
+		pr_err_ratelimited("%s[%pK}: output port flush cmd failed rc=%d\n",
 			__func__, audio, rc);
 	return rc;
 }
@@ -402,7 +402,7 @@
 		/* Close the session */
 		rc = q6asm_cmd(audio->ac, CMD_CLOSE);
 		if (rc < 0)
-			pr_err("%s[%pK]:Failed to close the session rc=%d\n",
+			pr_err_ratelimited("%s[%pK]:Failed to close the session rc=%d\n",
 				__func__, audio, rc);
 		audio->stopped = 1;
 		wake_up(&audio->write_wait);
@@ -676,7 +676,7 @@
 	pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio);
 
 	if (rc < 0)
-		pr_err("%s[%pK]: q6asm_cmd failed, rc = %d",
+		pr_err_ratelimited("%s[%pK]: q6asm_cmd failed, rc = %d",
 			__func__, audio, rc);
 
 	pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n"
@@ -1409,7 +1409,7 @@
 		mutex_lock(&audio->read_lock);
 		rc = audio_aio_outport_flush(audio);
 		if (rc < 0) {
-			pr_err("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n",
+			pr_err_ratelimited("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n",
 				__func__, audio);
 			rc = -EINTR;
 		}
@@ -1423,7 +1423,7 @@
 		audio->stopped = 1;
 		rc = audio_aio_flush(audio);
 		if (rc < 0) {
-			pr_err("%s[%pK]:Audio Stop procedure failed rc=%d\n",
+			pr_err_ratelimited("%s[%pK]:Audio Stop procedure failed rc=%d\n",
 				__func__, audio, rc);
 			mutex_unlock(&audio->lock);
 			break;
@@ -1444,7 +1444,7 @@
 		if (arg == 1) {
 			rc = audio_aio_pause(audio);
 			if (rc < 0) {
-				pr_err("%s[%pK]: pause FAILED rc=%d\n",
+				pr_err_ratelimited("%s[%pK]: pause FAILED rc=%d\n",
 					__func__, audio, rc);
 				mutex_unlock(&audio->lock);
 				break;
@@ -1454,7 +1454,7 @@
 			if (audio->drv_status & ADRV_STATUS_PAUSE) {
 				rc = audio_aio_enable(audio);
 				if (rc)
-					pr_err("%s[%pK]: audio enable failed\n",
+					pr_err_ratelimited("%s[%pK]: audio enable failed\n",
 					__func__, audio);
 				else {
 					audio->drv_status &= ~ADRV_STATUS_PAUSE;
@@ -1481,7 +1481,7 @@
 		/* Flush input / Output buffer in software*/
 		audio_aio_ioport_reset(audio);
 		if (rc < 0) {
-			pr_err("%s[%pK]:AUDIO_FLUSH interrupted\n",
+			pr_err_ratelimited("%s[%pK]:AUDIO_FLUSH interrupted\n",
 				__func__, audio);
 			rc = -EINTR;
 		} else {
@@ -1501,7 +1501,7 @@
 		mutex_lock(&audio->lock);
 		if (copy_to_user((void *)arg, &audio->ac->session,
 			sizeof(u16))) {
-			pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
+			pr_err_ratelimited("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
 				__func__);
 			rc = -EFAULT;
 		}
@@ -1511,7 +1511,7 @@
 	case AUDIO_PM_AWAKE: {
 		if ((audio->audio_ws_mgr ==  NULL) ||
 				(audio->miscdevice == NULL)) {
-			pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
+			pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice",
 					__func__, audio);
 			rc = -EACCES;
 			break;
@@ -1531,7 +1531,7 @@
 	case AUDIO_PM_RELAX: {
 		if ((audio->audio_ws_mgr ==  NULL) ||
 				(audio->miscdevice == NULL)) {
-			pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
+			pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice",
 					__func__, audio);
 			rc = -EACCES;
 			break;
diff --git a/dsp/codecs/audio_wma.c b/dsp/codecs/audio_wma.c
index ef9c8c6..0e8341e 100644
--- a/dsp/codecs/audio_wma.c
+++ b/dsp/codecs/audio_wma.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -126,7 +126,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
@@ -215,7 +215,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_compat_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
diff --git a/dsp/codecs/audio_wmapro.c b/dsp/codecs/audio_wmapro.c
index 2182618..6b1e46f 100644
--- a/dsp/codecs/audio_wmapro.c
+++ b/dsp/codecs/audio_wmapro.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -177,7 +177,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
@@ -287,7 +287,7 @@
 		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
 		rc = audio->codec_compat_ioctl(file, cmd, arg);
 		if (rc)
-			pr_err("Failed in utils_ioctl: %d\n", rc);
+			pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc);
 		break;
 	}
 	}
diff --git a/dsp/q6asm.c b/dsp/q6asm.c
index b7d92ad..74fd1cf 100644
--- a/dsp/q6asm.c
+++ b/dsp/q6asm.c
@@ -9444,11 +9444,11 @@
 	int cnt = 0;
 
 	if (!ac) {
-		pr_err("%s: APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	if (ac->apr == NULL) {
-		pr_err("%s: AC APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: AC APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
@@ -9608,11 +9608,11 @@
 	int rc;
 
 	if (!ac) {
-		pr_err("%s: APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	if (ac->apr == NULL) {
-		pr_err("%s: AC APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: AC APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
@@ -9697,11 +9697,11 @@
 	int rc = 0;
 
 	if (!ac) {
-		pr_err("%s: APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	if (ac->apr == NULL) {
-		pr_err("%s: AC APR handle NULL\n", __func__);
+		pr_err_ratelimited("%s: AC APR handle NULL\n", __func__);
 		return -EINVAL;
 	}
 	pr_debug("%s: session[%d]\n", __func__, ac->session);
@@ -10136,9 +10136,15 @@
 	memset(&mem_hdr, 0, sizeof(mem_hdr));
 	mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock);
 	cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]);
-	if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) {
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL\n",
+			__func__);
+		goto unlock;
+	}
+
+	if (cal_utils_is_cal_stale(cal_block)) {
 		rc = 0; /* not error case */
-		pr_err("%s: cal_block is NULL or stale\n",
+		pr_debug("%s: cal_block is stale\n",
 			__func__);
 		goto unlock;
 	}
diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c
index f34187d..46e2013 100644
--- a/dsp/q6lsm.c
+++ b/dsp/q6lsm.c
@@ -75,6 +75,8 @@
 };
 
 static struct lsm_common lsm_common;
+static DEFINE_MUTEX(session_lock);
+
 /*
  * mmap_handle_p can point either client->sound_model.mem_map_handle or
  * lsm_common.mmap_handle_for_cal.
@@ -95,6 +97,24 @@
 static int q6lsm_memory_unmap_regions(struct lsm_client *client,
 				      uint32_t handle);
 
+static int q6lsm_get_session_id_from_lsm_client(struct lsm_client *client)
+{
+	int n;
+
+	for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) {
+		if (lsm_session[n] == client)
+			return n;
+	}
+	pr_err("%s: cannot find matching lsm client. client = %pa\n",
+		__func__, client);
+	return LSM_INVALID_SESSION_ID;
+}
+
+static bool q6lsm_is_valid_lsm_client(struct lsm_client *client)
+{
+	return q6lsm_get_session_id_from_lsm_client(client) ? 1 : 0;
+}
+
 static int q6lsm_callback(struct apr_client_data *data, void *priv)
 {
 	struct lsm_client *client = (struct lsm_client *)priv;
@@ -113,6 +133,13 @@
 			 __func__, data->opcode, data->reset_event,
 			 data->reset_proc);
 
+		mutex_lock(&session_lock);
+		if (!client || !q6lsm_is_valid_lsm_client(client)) {
+			pr_err("%s: client already freed/invalid, return\n",
+				__func__);
+			mutex_unlock(&session_lock);
+			return 0;
+		}
 		apr_reset(client->apr);
 		client->apr = NULL;
 		atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
@@ -122,6 +149,7 @@
 		mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
 		lsm_common.set_custom_topology = 1;
 		mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+		mutex_unlock(&session_lock);
 		return 0;
 	}
 
@@ -334,6 +362,7 @@
 		pr_err("%s: Invalid Session %d\n", __func__, client->session);
 		return;
 	}
+	mutex_lock(&session_lock);
 	apr_deregister(client->apr);
 	client->mmap_apr = NULL;
 	q6lsm_session_free(client);
@@ -341,6 +370,7 @@
 	mutex_destroy(&client->cmd_lock);
 	kfree(client);
 	client = NULL;
+	mutex_unlock(&session_lock);
 }
 EXPORT_SYMBOL(q6lsm_client_free);