Merge "ASoC: wcd937x: Fix pop after PDR"
diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild
index 3e5fc12..48e0431 100644
--- a/asoc/codecs/Kbuild
+++ b/asoc/codecs/Kbuild
@@ -98,6 +98,7 @@
 endif
 
 ifdef CONFIG_SND_SOC_WCD9XXX_V2
+	WCD9XXX_OBJS += wcd-clsh.o
 	WCD9XXX_OBJS += wcd9xxx-common-v2.o
 	WCD9XXX_OBJS += wcd9xxx-resmgr-v2.o
 	WCD9XXX_OBJS += wcdcal-hwdep.o
diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c
index b6cb2f6..ba72fd8 100644
--- a/asoc/codecs/audio-ext-clk-up.c
+++ b/asoc/codecs/audio-ext-clk-up.c
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <dt-bindings/clock/qcom,audio-ext-clk.h>
 #include <dsp/q6afe-v2.h>
+#include <dsp/q6core.h>
 #include "audio-ext-clk-up.h"
 
 enum {
@@ -33,6 +34,7 @@
 	AUDIO_EXT_CLK_LPASS5,
 	AUDIO_EXT_CLK_LPASS6,
 	AUDIO_EXT_CLK_LPASS7,
+	AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND,
 	AUDIO_EXT_CLK_LPASS_MAX,
 	AUDIO_EXT_CLK_MAX = AUDIO_EXT_CLK_LPASS_MAX,
 };
@@ -55,6 +57,7 @@
 	struct afe_clk_set clk_cfg;
 	struct audio_ext_clk audio_clk;
 	const char *clk_name;
+	uint32_t npa_rsc_client_handle;
 };
 
 static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw)
@@ -141,12 +144,58 @@
 		return 0;
 }
 
+static int lpass_npa_rsc_prepare(struct clk_hw *hw)
+{
+	struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
+	int ret;
+
+	if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) &&
+		(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX))  {
+		if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+			ret = q6core_request_island_transition(
+				clk_priv->npa_rsc_client_handle, false);
+			if (ret < 0) {
+				pr_err("%s q6core_request_island_transition failed %d\n",
+					__func__, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void lpass_npa_rsc_unprepare(struct clk_hw *hw)
+{
+	struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
+	int ret = 0;
+
+	if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) &&
+		(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX))  {
+		if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+			ret = q6core_request_island_transition(
+					clk_priv->npa_rsc_client_handle, true);
+			if (ret < 0) {
+				pr_err("%s q6core_request_island_transition failed %d\n",
+					__func__, ret);
+			}
+		}
+	}
+}
+
 static const struct clk_ops audio_ext_clk_ops = {
 	.prepare = audio_ext_clk_prepare,
 	.unprepare = audio_ext_clk_unprepare,
 	.get_parent = audio_ext_clk_get_parent,
 };
 
+static const struct clk_ops lpass_npa_rsc_ops = {
+	.prepare = lpass_npa_rsc_prepare,
+	.unprepare = lpass_npa_rsc_unprepare,
+};
+
 static const char * const audio_ext_pmi_div_clk[] = {
 	"qpnp_clkdiv_1",
 	"pms405_div_clk1",
@@ -274,6 +323,15 @@
 			},
 		},
 	},
+	{
+		.pnctrl_info = {NULL},
+		.fact = {
+			.hw.init = &(struct clk_init_data){
+				.name = "lpass_npa_rsc_island_clk",
+				.ops = &lpass_npa_rsc_ops,
+			},
+		},
+	},
 };
 
 static int audio_get_pinctrl(struct platform_device *pdev)
@@ -476,11 +534,34 @@
 		return ret;
 	}
 
+	if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) {
+		if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4) {
+			ret = q6core_create_lpass_npa_client(
+				AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID,
+				"lpass_npa_rsc_mgr",
+				&clk_priv->npa_rsc_client_handle);
+			if (ret) {
+				dev_err(&pdev->dev, "%s: q6core_create_lpass_npa_client is failed %d\n",
+					__func__, ret);
+				audio_put_pinctrl(pdev);
+				return ret;
+			}
+		}
+	}
 	return 0;
 }
 
 static int audio_ref_clk_remove(struct platform_device *pdev)
 {
+	struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev);
+
+	if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_NPA_RSC_ISLAND) {
+		if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V4)
+			q6core_destroy_lpass_npa_client(
+					clk_priv->npa_rsc_client_handle);
+	}
 	audio_put_pinctrl(pdev);
 
 	return 0;
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index dcf688e..bd3c02f 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -18,7 +18,9 @@
 #include <linux/printk.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
+#include <linux/clk.h>
 #include <soc/snd_event.h>
+#include <linux/pm_runtime.h>
 #include "bolero-cdc.h"
 #include "internal.h"
 
@@ -30,6 +32,9 @@
 
 static struct snd_soc_codec_driver bolero;
 
+/* pm runtime auto suspend timer in msecs */
+#define BOLERO_AUTO_SUSPEND_DELAY          1500 /* delay in msec */
+
 /* MCLK_MUX table for all macros */
 static u16 bolero_mclk_mux_tbl[MAX_MACRO][MCLK_MUX_MAX] = {
 	{TX_MACRO, VA_MACRO},
@@ -67,6 +72,8 @@
 			"%s: SSR in progress, exit\n", __func__);
 		goto err;
 	}
+
+	pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
 	current_mclk_mux_macro =
 		priv->current_mclk_mux_macro[macro_id];
 	if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -88,6 +95,8 @@
 	priv->macro_params[current_mclk_mux_macro].mclk_fn(
 			priv->macro_params[current_mclk_mux_macro].dev, false);
 err:
+	pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+	pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
 	mutex_unlock(&priv->clk_lock);
 	return ret;
 }
@@ -104,6 +113,7 @@
 			"%s: SSR in progress, exit\n", __func__);
 		goto err;
 	}
+	ret = pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
 	current_mclk_mux_macro =
 		priv->current_mclk_mux_macro[macro_id];
 	if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -125,6 +135,8 @@
 	priv->macro_params[current_mclk_mux_macro].mclk_fn(
 			priv->macro_params[current_mclk_mux_macro].dev, false);
 err:
+	pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+	pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
 	mutex_unlock(&priv->clk_lock);
 	return ret;
 }
@@ -344,6 +356,9 @@
 	priv->macro_params[macro_id].dev = dev;
 	priv->current_mclk_mux_macro[macro_id] =
 				bolero_mclk_mux_tbl[macro_id][MCLK_MUX0];
+	if (macro_id == TX_MACRO)
+		priv->macro_params[macro_id].reg_wake_irq = ops->reg_wake_irq;
+
 	priv->num_dais += ops->num_dais;
 	priv->num_macros_registered++;
 	priv->macros_supported[macro_id] = true;
@@ -403,6 +418,9 @@
 	priv->macro_params[macro_id].mclk_fn = NULL;
 	priv->macro_params[macro_id].event_handler = NULL;
 	priv->macro_params[macro_id].dev = NULL;
+	if (macro_id == TX_MACRO)
+		priv->macro_params[macro_id].reg_wake_irq = NULL;
+
 	priv->num_dais -= priv->macro_params[macro_id].num_dais;
 	priv->num_macros_registered--;
 
@@ -664,6 +682,37 @@
 }
 EXPORT_SYMBOL(bolero_info_create_codec_entry);
 
+/**
+ * bolero_register_wake_irq - Register wake irq of Tx macro
+ *
+ * @codec: codec ptr.
+ * @ipc_wakeup: bool to identify ipc_wakeup to be used or HW interrupt line.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 ipc_wakeup)
+{
+	struct bolero_priv *priv = NULL;
+
+	if (!codec)
+		return -EINVAL;
+
+	priv = snd_soc_codec_get_drvdata(codec);
+	if (!priv)
+		return -EINVAL;
+
+	if (!bolero_is_valid_codec_dev(priv->dev)) {
+		dev_err(codec->dev, "%s: invalid codec\n", __func__);
+		return -EINVAL;
+	}
+
+	if (priv->macro_params[TX_MACRO].reg_wake_irq)
+		priv->macro_params[TX_MACRO].reg_wake_irq(codec, ipc_wakeup);
+
+	return 0;
+}
+EXPORT_SYMBOL(bolero_register_wake_irq);
+
 static int bolero_soc_codec_probe(struct snd_soc_codec *codec)
 {
 	struct bolero_priv *priv = dev_get_drvdata(codec->dev);
@@ -814,6 +863,7 @@
 	struct bolero_priv *priv;
 	u32 num_macros = 0;
 	int ret;
+	struct clk *lpass_npa_rsc_island = NULL;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(struct bolero_priv),
 			    GFP_KERNEL);
@@ -862,6 +912,17 @@
 		  bolero_add_child_devices);
 	schedule_work(&priv->bolero_add_child_devices_work);
 
+	/* Register LPASS NPA resource */
+	lpass_npa_rsc_island = devm_clk_get(&pdev->dev, "island_lpass_npa_rsc");
+	if (IS_ERR(lpass_npa_rsc_island)) {
+		ret = PTR_ERR(lpass_npa_rsc_island);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "island_lpass_npa_rsc", ret);
+		lpass_npa_rsc_island = NULL;
+		ret = 0;
+	}
+	priv->lpass_npa_rsc_island = lpass_npa_rsc_island;
+
 	return 0;
 }
 
@@ -878,6 +939,41 @@
 	return 0;
 }
 
+int bolero_runtime_resume(struct device *dev)
+{
+	struct bolero_priv *priv = dev_get_drvdata(dev->parent);
+	int ret = 0;
+
+	if (priv->lpass_npa_rsc_island == NULL) {
+		dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+		return 0;
+	}
+
+	ret = clk_prepare_enable(priv->lpass_npa_rsc_island);
+	if (ret < 0)
+		dev_err(dev, "%s:lpass npa rsc island enable failed\n",
+			__func__);
+
+	pm_runtime_set_autosuspend_delay(priv->dev, BOLERO_AUTO_SUSPEND_DELAY);
+	return 0;
+}
+EXPORT_SYMBOL(bolero_runtime_resume);
+
+int bolero_runtime_suspend(struct device *dev)
+{
+	struct bolero_priv *priv = dev_get_drvdata(dev->parent);
+
+	mutex_lock(&priv->clk_lock);
+	if (priv->lpass_npa_rsc_island != NULL)
+		clk_disable_unprepare(priv->lpass_npa_rsc_island);
+	else
+		dev_dbg(dev, "%s: Invalid lpass npa rsc node\n",
+			__func__);
+	mutex_unlock(&priv->clk_lock);
+	return 0;
+}
+EXPORT_SYMBOL(bolero_runtime_suspend);
+
 static const struct of_device_id bolero_dt_match[] = {
 	{.compatible = "qcom,bolero-codec"},
 	{}
diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h
index aa52491..a00c065 100644
--- a/asoc/codecs/bolero/bolero-cdc.h
+++ b/asoc/codecs/bolero/bolero-cdc.h
@@ -45,7 +45,8 @@
 	BOLERO_MACRO_EVT_IMPED_FALSE, /* for imped false */
 	BOLERO_MACRO_EVT_SSR_DOWN,
 	BOLERO_MACRO_EVT_SSR_UP,
-	BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET
+	BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET,
+	BOLERO_MACRO_EVT_REG_WAKE_IRQ
 };
 
 struct macro_ops {
@@ -57,6 +58,7 @@
 	int (*mclk_fn)(struct device *dev, bool enable);
 	int (*event_handler)(struct snd_soc_codec *codec, u16 event,
 			     u32 data);
+	int (*reg_wake_irq)(struct snd_soc_codec *codec, u32 data);
 	char __iomem *io_base;
 };
 
@@ -71,7 +73,10 @@
 int bolero_info_create_codec_entry(
 		struct snd_info_entry *codec_root,
 		struct snd_soc_codec *codec);
+int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 data);
 void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n);
+int bolero_runtime_resume(struct device *dev);
+int bolero_runtime_suspend(struct device *dev);
 #else
 static inline int bolero_register_macro(struct device *dev,
 					u16 macro_id,
@@ -107,5 +112,16 @@
 static inline void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n)
 {
 }
+
+static inline int bolero_register_wake_irq(struct snd_soc_codec *codec,
+					   u32 data)
+static inline int bolero_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+static int bolero_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
 #endif /* CONFIG_SND_SOC_BOLERO */
 #endif /* BOLERO_CDC_H */
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index 89d3cad..71750fb 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -65,6 +65,7 @@
 	u16 current_mclk_mux_macro[MAX_MACRO];
 	struct work_struct bolero_add_child_devices_work;
 	u32 version;
+	struct clk *lpass_npa_rsc_island;
 
 	/* Entry for version info */
 	struct snd_info_entry *entry;
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index f5ad8d2..fcfd951 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -345,6 +345,7 @@
 	bool is_ear_mode_on;
 	bool dev_up;
 	bool hph_pwr_mode;
+	bool hph_hd2_mode;
 	u16 mclk_mux;
 	struct mutex mclk_lock;
 	struct mutex swr_clk_lock;
@@ -433,6 +434,10 @@
 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_hph_hd2_mode_text[] = {"OFF", "ON"};
+static const struct soc_enum rx_macro_hph_hd2_mode_enum =
+	SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_hd2_mode_text);
+
 static const char *const rx_macro_hph_pwr_mode_text[] = {"ULP", "LoHIFI"};
 static const struct soc_enum rx_macro_hph_pwr_mode_enum =
 	SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text);
@@ -1541,12 +1546,12 @@
 
 	switch (interp_idx) {
 	case INTERP_HPHL:
-		hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3;
-		hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0;
+		hd2_scale_reg = BOLERO_CDC_RX_RX0_RX_PATH_SEC3;
+		hd2_enable_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0;
 		break;
 	case INTERP_HPHR:
-		hd2_scale_reg = BOLERO_CDC_RX_RX2_RX_PATH_SEC3;
-		hd2_enable_reg = BOLERO_CDC_RX_RX2_RX_PATH_CFG0;
+		hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3;
+		hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0;
 		break;
 	}
 
@@ -1695,6 +1700,34 @@
 	return 0;
 }
 
+static int rx_macro_get_hph_hd2_mode(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->hph_hd2_mode;
+	return 0;
+}
+
+static int rx_macro_put_hph_hd2_mode(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->hph_hd2_mode = ucontrol->value.integer.value[0];
+	return 0;
+}
+
 static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_value *ucontrol)
 {
@@ -1942,18 +1975,15 @@
 					struct rx_macro_priv *rx_priv,
 					u16 interp_idx, int event)
 {
-	u8 hph_dly_mask = 0;
 	u16 hph_lut_bypass_reg = 0;
 	u16 hph_comp_ctrl7 = 0;
 
 	switch (interp_idx) {
 	case INTERP_HPHL:
-		hph_dly_mask = 1;
 		hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_LUT;
 		hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER0_CTL7;
 		break;
 	case INTERP_HPHR:
-		hph_dly_mask = 2;
 		hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_LUT;
 		hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER1_CTL7;
 		break;
@@ -1962,8 +1992,6 @@
 	}
 
 	if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
-		snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0,
-				    hph_dly_mask, 0x0);
 		if (interp_idx == INTERP_HPHL) {
 			if (rx_priv->is_ear_mode_on)
 				snd_soc_update_bits(codec,
@@ -1983,8 +2011,6 @@
 	}
 
 	if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
-		snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0,
-				    hph_dly_mask, hph_dly_mask);
 		snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG1,
 					0x02, 0x00);
 		snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00);
@@ -2018,7 +2044,8 @@
 			snd_soc_update_bits(codec, main_reg, 0x20, 0x20);
 			rx_macro_idle_detect_control(codec, rx_priv,
 					interp_idx, event);
-			rx_macro_hd2_control(codec, interp_idx, event);
+			if (rx_priv->hph_hd2_mode)
+				rx_macro_hd2_control(codec, interp_idx, event);
 			rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx,
 						       event);
 			rx_macro_config_compander(codec, rx_priv,
@@ -2045,7 +2072,8 @@
 							event);
 			rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx,
 						       event);
-			rx_macro_hd2_control(codec, interp_idx, event);
+			if (rx_priv->hph_hd2_mode)
+				rx_macro_hd2_control(codec, interp_idx, event);
 			rx_macro_idle_detect_control(codec, rx_priv,
 					interp_idx, event);
 			/* Clk Disable */
@@ -2411,6 +2439,9 @@
 	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_HPH HD2 Mode", rx_macro_hph_hd2_mode_enum,
+		rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode),
+
 	SOC_ENUM_EXT("RX_HPH_PWR_MODE", rx_macro_hph_pwr_mode_enum,
 		rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode),
 
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index a16f778..e58cbd5 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -331,6 +331,24 @@
 	return 0;
 }
 
+static int tx_macro_reg_wake_irq(struct snd_soc_codec *codec,
+				 u32 data)
+{
+	struct device *tx_dev = NULL;
+	struct tx_macro_priv *tx_priv = NULL;
+	u32 ipc_wakeup = data;
+	int ret = 0;
+
+	if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	ret = swrm_wcd_notify(
+		tx_priv->swr_ctrl_data[0].tx_swr_pdev,
+		SWR_REGISTER_WAKE_IRQ, &ipc_wakeup);
+
+	return ret;
+}
+
 static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
 {
 	struct delayed_work *hpf_delayed_work = NULL;
@@ -1683,6 +1701,7 @@
 	ops->num_dais = ARRAY_SIZE(tx_macro_dai);
 	ops->mclk_fn = tx_macro_mclk_ctrl;
 	ops->event_handler = tx_macro_event_handler;
+	ops->reg_wake_irq = tx_macro_reg_wake_irq;
 }
 
 static int tx_macro_probe(struct platform_device *pdev)
diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c
index 199f75c..820c4df 100644
--- a/asoc/codecs/bolero/va-macro.c
+++ b/asoc/codecs/bolero/va-macro.c
@@ -20,9 +20,12 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <linux/pm_runtime.h>
 #include "bolero-cdc.h"
 #include "bolero-cdc-registers.h"
 
+/* pm runtime auto suspend timer in msecs */
+#define VA_AUTO_SUSPEND_DELAY          1500 /* delay in msec */
 #define VA_MACRO_MAX_OFFSET 0x1000
 
 #define VA_MACRO_NUM_DECIMATORS 8
@@ -1617,6 +1620,10 @@
 		dev_err(&pdev->dev, "%s: register macro failed\n", __func__);
 		goto reg_macro_fail;
 	}
+	pm_runtime_set_autosuspend_delay(&pdev->dev, VA_AUTO_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
 	return ret;
 
 reg_macro_fail:
@@ -1632,7 +1639,8 @@
 
 	if (!va_priv)
 		return -EINVAL;
-
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
 	bolero_unregister_macro(&pdev->dev, VA_MACRO);
 	mutex_destroy(&va_priv->mclk_lock);
 	return 0;
@@ -1644,10 +1652,19 @@
 	{}
 };
 
+static const struct dev_pm_ops bolero_dev_pm_ops = {
+	SET_RUNTIME_PM_OPS(
+		bolero_runtime_suspend,
+		bolero_runtime_resume,
+		NULL
+	)
+};
+
 static struct platform_driver va_macro_driver = {
 	.driver = {
 		.name = "va_macro",
 		.owner = THIS_MODULE,
+		.pm = &bolero_dev_pm_ops,
 		.of_match_table = va_macro_dt_match,
 	},
 	.probe = va_macro_probe,
diff --git a/asoc/codecs/wcd-clsh.c b/asoc/codecs/wcd-clsh.c
new file mode 100644
index 0000000..d119b83
--- /dev/null
+++ b/asoc/codecs/wcd-clsh.c
@@ -0,0 +1,575 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <asoc/wcd9xxx_registers.h>
+#include "wcd-clsh.h"
+
+#define WCD_USLEEP_RANGE 50
+
+static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *,
+					      struct wcd_clsh_cdc_info *,
+					      u8 req_state, bool en, int mode);
+
+static const char *mode_to_str(int mode)
+{
+	switch (mode) {
+	case CLS_H_NORMAL:
+		return WCD_CLSH_STRINGIFY(CLS_H_NORMAL);
+	case CLS_H_HIFI:
+		return WCD_CLSH_STRINGIFY(CLS_H_HIFI);
+	case CLS_H_LOHIFI:
+		return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI);
+	case CLS_H_LP:
+		return WCD_CLSH_STRINGIFY(CLS_H_LP);
+	case CLS_H_ULP:
+		return WCD_CLSH_STRINGIFY(CLS_H_ULP);
+	case CLS_AB:
+		return WCD_CLSH_STRINGIFY(CLS_AB);
+	case CLS_AB_HIFI:
+		return WCD_CLSH_STRINGIFY(CLS_AB_HIFI);
+	default:
+		return WCD_CLSH_STRINGIFY(CLS_H_INVALID);
+	};
+}
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+	int i;
+	int cnt = 0;
+	/*
+	 * This array of strings should match with enum wcd_clsh_state_bit.
+	 */
+	static const char *const states[] = {
+		"STATE_EAR",
+		"STATE_HPH_L",
+		"STATE_HPH_R",
+		"STATE_AUX",
+	};
+
+	if (state == WCD_CLSH_STATE_IDLE) {
+		snprintf(buf, buflen, "[STATE_IDLE]");
+		goto done;
+	}
+
+	buf[0] = '\0';
+	for (i = 0; i < ARRAY_SIZE(states); i++) {
+		if (!(state & (1 << i)))
+			continue;
+		cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+			       buf[0] == '\0' ? "[" : "|",
+			       states[i]);
+	}
+	if (cnt > 0)
+		strlcat(buf + cnt, "]", buflen);
+
+done:
+	if (buf[0] == '\0')
+		snprintf(buf, buflen, "[STATE_UNKNOWN]");
+	return buf;
+}
+
+static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d,
+					int clsh_state)
+{
+	int mode;
+
+	if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHL) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHR) &&
+	    (clsh_state != WCD_CLSH_STATE_AUX))
+		mode = CLS_NONE;
+	else
+		mode = clsh_d->interpolator_modes[ffs(clsh_state)];
+
+	return mode;
+}
+
+static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d,
+					int clsh_state, int mode)
+{
+	if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHL) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHR) &&
+	    (clsh_state != WCD_CLSH_STATE_AUX))
+		return;
+
+	clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
+}
+
+static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
+					  int mode)
+{
+	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+	    mode == CLS_AB_HIFI)
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    0x08, 0x08); /* set to HIFI */
+	else
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    0x08, 0x00); /* set to default */
+}
+
+static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
+					     int mode)
+{
+	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+	    mode == CLS_AB_HIFI) {
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    0x04, 0x04);
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+				    0xF0, 0x80);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    0x04, 0x00); /* set to Default */
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+				    0xF0, 0x70);
+	}
+}
+
+static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
+					 int mode, bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+				    0xE0, 0xA0);
+		/* 100usec delay is needed as per HW requirement */
+		usleep_range(100, 110);
+		snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
+				    0x02, 0x02);
+		snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2,
+				    0xFF, 0x1C);
+		if (mode == CLS_H_LOHIFI) {
+			snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+					    0x20, 0x20);
+			snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+					    0xF0, 0xC0);
+			snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+					    0x0E, 0x02);
+		}
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+				    0x20, 0x00);
+		snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+				    0xF0, 0x80);
+		snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+				    0x0E, 0x06);
+	}
+}
+
+static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
+			       struct wcd_clsh_cdc_info *clsh_d,
+			       int mode,
+			       bool enable)
+{
+	/* enable/disable buck */
+	if ((enable && (++clsh_d->buck_users == 1)) ||
+	   (!enable && (--clsh_d->buck_users == 0))) {
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    (1 << 7), (enable << 7));
+		/*
+		 * 500us sleep is required after buck enable/disable
+		 * as per HW requirement
+		 */
+		usleep_range(500, 510);
+		if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
+			mode == CLS_H_HIFI || mode == CLS_H_LP)
+			snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3,
+					    0x02, 0x00);
+
+		snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, 0xFF, 0x3A);
+		/* 500usec delay is needed as per HW requirement */
+		usleep_range(500, 500 + WCD_USLEEP_RANGE);
+	}
+	dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s\n",
+		__func__, clsh_d->buck_users, enable, mode_to_str(mode));
+}
+
+static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_info *clsh_d,
+				  int mode,
+				  bool enable)
+{
+	/* enable/disable flyback */
+	if ((enable && (++clsh_d->flyback_users == 1)) ||
+	   (!enable && (--clsh_d->flyback_users == 0))) {
+		snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+				    (1 << 6), (enable << 6));
+		/*
+		 * 100us sleep is required after flyback enable/disable
+		 * as per HW requirement
+		 */
+		usleep_range(100, 110);
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+			    0xE0, 0xE0);
+		/* 500usec delay is needed as per HW requirement */
+		usleep_range(500, 500 + WCD_USLEEP_RANGE);
+	}
+	dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s\n",
+		__func__, clsh_d->flyback_users, enable, mode_to_str(mode));
+}
+
+static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
+				  int mode)
+{
+	u8 val = 0;
+
+	switch (mode) {
+	case CLS_H_NORMAL:
+	case CLS_H_LOHIFI:
+		val = 0x00;
+		break;
+	case CLS_AB:
+	case CLS_H_ULP:
+		val = 0x0C;
+		break;
+	case CLS_AB_HIFI:
+	case CLS_H_HIFI:
+		val = 0x08;
+		break;
+	case CLS_H_LP:
+		val = 0x04;
+		break;
+	default:
+		dev_err(codec->dev, "%s:Invalid mode %d\n", __func__, mode);
+		return;
+	};
+
+	snd_soc_update_bits(codec, WCD9XXX_ANA_HPH, 0x0C, val);
+}
+
+static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
+{
+
+	snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
+	snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0);
+	/* Sleep needed to avoid click and pop as per HW requirement */
+	usleep_range(100, 110);
+}
+
+static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec,
+					     int mode)
+{
+	snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES,
+			    0x02, 0x00);
+}
+
+static void wcd_clsh_state_ear_aux(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_info *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_aux(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_info *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_info *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_info *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+}
+
+static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
+				 struct wcd_clsh_cdc_info *clsh_d,
+				 u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode == CLS_H_NORMAL) {
+		dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_r\n",
+			__func__);
+		return;
+	}
+
+	if (is_enable) {
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_force_iq_ctl(codec, mode, true);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_hph_mode(codec, mode);
+	} else {
+		wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+		/* buck and flyback set to default mode and disable */
+		wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
+				 struct wcd_clsh_cdc_info *clsh_d,
+				 u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode == CLS_H_NORMAL) {
+		dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_l\n",
+			__func__);
+		return;
+	}
+
+	if (is_enable) {
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_force_iq_ctl(codec, mode, true);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_hph_mode(codec, mode);
+	} else {
+		wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+		/* set buck and flyback to Default Mode */
+		wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_aux(struct snd_soc_codec *codec,
+			      struct wcd_clsh_cdc_info *clsh_d,
+			      u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+	} else {
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_ear(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_info *clsh_d,
+		u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_force_iq_ctl(codec, mode, true);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_hph_mode(codec, mode);
+	} else {
+		wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+		/* set buck and flyback to Default Mode */
+		wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_err(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_info *clsh_d,
+		u8 req_state, bool is_enable, int mode)
+{
+	char msg[128];
+
+	dev_err(codec->dev,
+		"%s Wrong request for class H state machine requested to %s %s\n",
+		__func__, is_enable ? "enable" : "disable",
+		state_to_str(req_state, msg, sizeof(msg)));
+}
+
+/*
+ * Function: wcd_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static bool wcd_clsh_is_state_valid(u8 state)
+{
+	switch (state) {
+	case WCD_CLSH_STATE_IDLE:
+	case WCD_CLSH_STATE_EAR:
+	case WCD_CLSH_STATE_HPHL:
+	case WCD_CLSH_STATE_HPHR:
+	case WCD_CLSH_STATE_HPH_ST:
+	case WCD_CLSH_STATE_AUX:
+	case WCD_CLSH_STATE_HPHL_AUX:
+	case WCD_CLSH_STATE_HPHR_AUX:
+	case WCD_CLSH_STATE_HPH_ST_AUX:
+	case WCD_CLSH_STATE_EAR_AUX:
+		return true;
+	default:
+		return false;
+	};
+}
+
+/*
+ * Function: wcd_cls_h_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_info *cdc_clsh_d,
+		u8 clsh_event, u8 req_state,
+		int int_mode)
+{
+	u8 old_state, new_state;
+	char msg0[128], msg1[128];
+
+	switch (clsh_event) {
+	case WCD_CLSH_EVENT_PRE_DAC:
+		old_state = cdc_clsh_d->state;
+		new_state = old_state | req_state;
+
+		if (!wcd_clsh_is_state_valid(new_state)) {
+			dev_err(codec->dev,
+				"%s: Class-H not a valid new state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		if (new_state == old_state) {
+			dev_err(codec->dev,
+				"%s: Class-H already in requested state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		cdc_clsh_d->state = new_state;
+		wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
+		(*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+					     CLSH_REQ_ENABLE, int_mode);
+		dev_dbg(codec->dev,
+			"%s: ClassH state transition from %s to %s\n",
+			__func__, state_to_str(old_state, msg0, sizeof(msg0)),
+			state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+		break;
+	case WCD_CLSH_EVENT_POST_PA:
+		old_state = cdc_clsh_d->state;
+		new_state = old_state & (~req_state);
+		if (new_state < NUM_CLSH_STATES) {
+			if (!wcd_clsh_is_state_valid(old_state)) {
+				dev_err(codec->dev,
+					"%s:Invalid old state:%s\n",
+					__func__,
+					state_to_str(old_state, msg0,
+						     sizeof(msg0)));
+				return;
+			}
+			if (new_state == old_state) {
+				dev_err(codec->dev,
+					"%s: Class-H already in requested state: %s\n",
+					__func__,
+					state_to_str(new_state, msg0,
+						     sizeof(msg0)));
+				return;
+			}
+			(*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+					req_state, CLSH_REQ_DISABLE,
+					int_mode);
+			cdc_clsh_d->state = new_state;
+			wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
+			dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+				__func__, state_to_str(old_state, msg0,
+						       sizeof(msg0)),
+				state_to_str(cdc_clsh_d->state, msg1,
+					     sizeof(msg1)));
+		}
+		break;
+	};
+}
+EXPORT_SYMBOL(wcd_cls_h_fsm);
+
+/*
+ * wcd_cls_h_init: Called to init clsh info
+ *
+ * @clsh: pointer for clsh state information.
+ */
+void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
+{
+	int i;
+
+	clsh->state = WCD_CLSH_STATE_IDLE;
+
+	for (i = 0; i < NUM_CLSH_STATES; i++)
+		clsh_state_fp[i] = wcd_clsh_state_err;
+
+	clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st;
+	clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] =
+						wcd_clsh_state_hph_aux;
+	clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear;
+	/* Set interpolaotr modes to NONE */
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE);
+	clsh->flyback_users = 0;
+	clsh->buck_users = 0;
+}
+EXPORT_SYMBOL(wcd_cls_h_init);
+
+MODULE_DESCRIPTION("WCD Class-H Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/asoc/codecs/wcd-clsh.h b/asoc/codecs/wcd-clsh.h
new file mode 100644
index 0000000..df305bc
--- /dev/null
+++ b/asoc/codecs/wcd-clsh.h
@@ -0,0 +1,104 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD_CLSH
+#define _WCD_CLSH
+
+#include <linux/stringify.h>
+
+#define WCD_CLSH_STRINGIFY(s)  __stringify(s)
+#define CLSH_REQ_ENABLE true
+#define CLSH_REQ_DISABLE false
+
+#define WCD_CLSH_EVENT_PRE_DAC 0x01
+#define WCD_CLSH_EVENT_POST_PA 0x02
+/*
+ * Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: AUX mode
+ */
+#define	WCD_CLSH_STATE_IDLE 0x00
+#define	WCD_CLSH_STATE_EAR (0x01 << 0)
+#define	WCD_CLSH_STATE_HPHL (0x01 << 1)
+#define	WCD_CLSH_STATE_HPHR (0x01 << 2)
+#define	WCD_CLSH_STATE_AUX (0x01 << 3)
+
+/*
+ * Though number of CLSH states is 4, max state should be 5
+ * because state array index starts from 1.
+ */
+#define WCD_CLSH_STATE_MAX 5
+#define NUM_CLSH_STATES (0x01 << WCD_CLSH_STATE_MAX)
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \
+			       WCD_CLSH_STATE_HPHR)
+
+#define WCD_CLSH_STATE_HPHL_AUX (WCD_CLSH_STATE_HPHL | \
+				    WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPHR_AUX (WCD_CLSH_STATE_HPHR | \
+				    WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPH_ST_AUX (WCD_CLSH_STATE_HPH_ST | \
+				      WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_EAR_AUX (WCD_CLSH_STATE_EAR | \
+				   WCD_CLSH_STATE_AUX)
+#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \
+				     WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \
+				     WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \
+				       WCD_CLSH_STATE_EAR)
+
+enum {
+	CLS_H_NORMAL = 0, /* Class-H Default */
+	CLS_H_HIFI, /* Class-H HiFi */
+	CLS_H_LP, /* Class-H Low Power */
+	CLS_AB, /* Class-AB Low HIFI*/
+	CLS_H_LOHIFI, /* LoHIFI */
+	CLS_H_ULP, /* Ultra Low power */
+	CLS_AB_HIFI, /* Class-AB */
+	CLS_NONE, /* None of the above modes */
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd_clsh_cdc_info {
+	u8 state;
+	int flyback_users;
+	int buck_users;
+	int interpolator_modes[WCD_CLSH_STATE_MAX];
+};
+
+#ifdef CONFIG_SND_SOC_WCD9XXX_V2
+extern void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_info *cdc_clsh_d,
+		u8 clsh_event, u8 req_state,
+		int int_mode);
+
+extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh);
+#else
+extern void wcd_cls_h_fsm(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_info *cdc_clsh_d,
+		u8 clsh_event, u8 req_state,
+		int int_mode)
+{
+}
+
+extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh)
+{
+}
+#endif /* CONFIG_SND_SOC_WCD9XXX_V2 */
+
+#endif
diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c
index 44f741e..0ca7dbb 100644
--- a/asoc/codecs/wcd934x/wcd934x.c
+++ b/asoc/codecs/wcd934x/wcd934x.c
@@ -8939,6 +8939,8 @@
 
 static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil)
 {
+	if (!tavil)
+		return;
 	mutex_lock(&tavil->power_lock);
 	dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n",
 		__func__, tavil->power_active_ref);
@@ -10298,6 +10300,8 @@
 	snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
 	snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
 	snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+	snd_soc_dapm_ignore_suspend(dapm, "WDMA3_OUT");
+
 	if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
 		snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
 		snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
diff --git a/asoc/codecs/wcd937x/internal.h b/asoc/codecs/wcd937x/internal.h
index c0f96f1..d712129 100644
--- a/asoc/codecs/wcd937x/internal.h
+++ b/asoc/codecs/wcd937x/internal.h
@@ -13,6 +13,7 @@
 #ifndef _WCD937X_INTERNAL_H
 #define _WCD937X_INTERNAL_H
 
+#include "../wcd-clsh.h"
 #include "../wcd-mbhc-v2.h"
 #include "asoc/wcd-irq.h"
 #include "wcd937x-mbhc.h"
@@ -54,6 +55,8 @@
 	s32 dmic_0_1_clk_cnt;
 	s32 dmic_2_3_clk_cnt;
 	s32 dmic_4_5_clk_cnt;
+	/* class h specific info */
+	struct wcd_clsh_cdc_info clsh_info;
 	/* mbhc module */
 	struct wcd937x_mbhc *mbhc;
 
@@ -136,8 +139,8 @@
 
 enum {
 	/* INTR_CTRL_INT_MASK_0 */
-	WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET = 0,
-	WCD937X_IRQ_MBHC_BUTTON_PRESS_DET,
+	WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+	WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET,
 	WCD937X_IRQ_MBHC_ELECT_INS_REM_DET,
 	WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
 	WCD937X_IRQ_MBHC_SW_DET,
diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c
index 34e4b1b..0d88e67 100644
--- a/asoc/codecs/wcd937x/wcd937x.c
+++ b/asoc/codecs/wcd937x/wcd937x.c
@@ -118,6 +118,10 @@
 	snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x40);
 	usleep_range(10000, 10010);
 	snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x00);
+	snd_soc_update_bits(codec, WCD937X_HPH_OCP_CTL, 0xFF, 0x3A);
+	snd_soc_update_bits(codec, WCD937X_RX_OCP_CTL, 0x0F, 0x02);
+	snd_soc_update_bits(codec, WCD937X_HPH_R_TEST, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD937X_HPH_L_TEST, 0x01, 0x01);
 
 	return 0;
 }
@@ -297,7 +301,6 @@
 
 static int wcd937x_rx_clk_enable(struct snd_soc_codec *codec)
 {
-
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 
 	if (wcd937x->rx_clk_cnt == 0) {
@@ -324,10 +327,12 @@
 {
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 
+	if (wcd937x->rx_clk_cnt == 0) {
+		dev_dbg(wcd937x->dev, "%s:clk already disabled\n", __func__);
+		return 0;
+	}
 	wcd937x->rx_clk_cnt--;
 	if (wcd937x->rx_clk_cnt == 0) {
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x40, 0x00);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x80, 0x00);
 		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x01, 0x00);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL,
 				    0x02, 0x00);
@@ -368,6 +373,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -384,8 +390,14 @@
 				    0x80, 0x00);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
-				    0x0F, 0x02);
+		if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+				0x0F, 0x02);
+		else if (hph_mode == CLS_H_LOHIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+				0x0F, 0x06);
 		if (wcd937x->comp1_enable) {
 			snd_soc_update_bits(codec,
 					WCD937X_DIGITAL_CDC_COMP_CTL_0,
@@ -402,8 +414,15 @@
 		usleep_range(5000, 5010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x00);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHL,
+			     hph_mode);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec,
+			WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+			0x0F, 0x01);
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
 		    wcd937x->rx_swr_dev->dev_num,
 		    false);
@@ -419,6 +438,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 
@@ -436,8 +456,14 @@
 				    0x80, 0x00);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
-				    0x0F, 0x02);
+		if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+				0x0F, 0x02);
+		else if (hph_mode == CLS_H_LOHIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+				0x0F, 0x06);
 		if (wcd937x->comp2_enable) {
 			snd_soc_update_bits(codec,
 					WCD937X_DIGITAL_CDC_COMP_CTL_0,
@@ -454,8 +480,15 @@
 		usleep_range(5000, 5010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x00);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHR,
+			     hph_mode);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec,
+			WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R,
+			0x0F, 0x01);
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
 		    wcd937x->rx_swr_dev->dev_num,
 		    false);
@@ -471,6 +504,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -483,11 +517,29 @@
 				    0x04, 0x04);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
 				    0x01, 0x01);
+		if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+				0x0F, 0x02);
+		else if (hph_mode == CLS_H_LOHIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+				0x0F, 0x06);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0,
 				    0x02, 0x02);
 		usleep_range(5000, 5010);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_EAR,
+			     hph_mode);
+
 		break;
 	case SND_SOC_DAPM_POST_PMD:
+		if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI ||
+		    hph_mode == CLS_H_HIFI)
+			snd_soc_update_bits(codec,
+				WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L,
+				0x0F, 0x01);
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
 		    wcd937x->rx_swr_dev->dev_num,
 		    false);
@@ -503,6 +555,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -517,6 +570,11 @@
 				    0x04, 0x04);
 		snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_AUX_GAIN_CTL,
 				    0x01, 0x01);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_AUX,
+			     hph_mode);
+
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
@@ -538,6 +596,7 @@
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
+	int hph_mode = wcd937x->hph_mode;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
 		w->name, event);
@@ -554,8 +613,9 @@
 		usleep_range(7000, 7010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x02);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x02, 0x02);
+		if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+			snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
 		if (wcd937x->update_wcd_event)
 			wcd937x->update_wcd_event(wcd937x->handle,
 						WCD_BOLERO_EVT_RX_MUTE,
@@ -576,6 +636,10 @@
 					     WCD_EVENT_POST_HPHR_PA_OFF,
 					     &wcd937x->mbhc->wcd_mbhc);
 		snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x10, 0x00);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHR,
+			     hph_mode);
 		break;
 	};
 	return ret;
@@ -588,6 +652,7 @@
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
+	int hph_mode = wcd937x->hph_mode;
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
@@ -601,8 +666,9 @@
 		usleep_range(7000, 7010);
 		snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1,
 				    0x02, 0x02);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x02, 0x02);
+		if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+			snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
 		if (wcd937x->update_wcd_event)
 			wcd937x->update_wcd_event(wcd937x->handle,
 						WCD_BOLERO_EVT_RX_MUTE,
@@ -623,6 +689,10 @@
 					     WCD_EVENT_POST_HPHL_PA_OFF,
 					     &wcd937x->mbhc->wcd_mbhc);
 		snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x20, 0x00);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHL,
+			     hph_mode);
 		break;
 	};
 	return ret;
@@ -634,6 +704,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -641,19 +712,15 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x80, 0x80);
-		usleep_range(500, 510);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
-		usleep_range(500, 510);
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
 			    wcd937x->rx_swr_dev->dev_num,
 			    true);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
 		usleep_range(1000, 1010);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x20, 0x20);
+		if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+			snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
 		if (wcd937x->update_wcd_event)
 			wcd937x->update_wcd_event(wcd937x->handle,
 						WCD_BOLERO_EVT_RX_MUTE,
@@ -668,6 +735,10 @@
 	case SND_SOC_DAPM_POST_PMD:
 		usleep_range(1000, 1010);
 		usleep_range(1000, 1010);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_AUX,
+			     hph_mode);
 		break;
 	};
 	return ret;
@@ -679,6 +750,7 @@
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = wcd937x->hph_mode;
 	int ret = 0;
 
 	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
@@ -686,19 +758,15 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x08, 0x08);
-		usleep_range(500, 510);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
-		usleep_range(500, 510);
 		ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev,
 			    wcd937x->rx_swr_dev->dev_num,
 			    true);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
 		usleep_range(6000, 6010);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x02, 0x02);
+		if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI)
+			snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
 		if (wcd937x->update_wcd_event)
 			wcd937x->update_wcd_event(wcd937x->handle,
 						WCD_BOLERO_EVT_RX_MUTE,
@@ -712,16 +780,44 @@
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		usleep_range(7000, 7010);
+		wcd_cls_h_fsm(codec, &wcd937x->clsh_info,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_EAR,
+			     hph_mode);
 		break;
 	};
 	return ret;
 }
 
+static int wcd937x_enable_clsh(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 wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
+	int mode = wcd937x->hph_mode;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP ||
+		mode == CLS_H_HIFI || mode == CLS_H_LP) {
+		wcd937x_rx_connect_port(codec, CLSH,
+				SND_SOC_DAPM_EVENT_ON(event));
+		if (SND_SOC_DAPM_EVENT_OFF(event))
+			ret = swr_slvdev_datapath_control(
+					wcd937x->rx_swr_dev,
+					wcd937x->rx_swr_dev->dev_num,
+					false);
+	}
+	return ret;
+}
+
 static int wcd937x_enable_rx1(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 wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec);
 
@@ -730,26 +826,6 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4,
-				    0xF0, 0x80);
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
-				    0xE0, 0xA0);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3,
-				    0x02, 0x02);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2,
-				    0xFF, 0x1C);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x40, 0x40);
-		usleep_range(100, 110);
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
-				    0xE0, 0xE0);
-		usleep_range(100, 110);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x80, 0x80);
-		usleep_range(500, 510);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
-		usleep_range(500, 510);
-
 		wcd937x_rx_connect_port(codec, HPH_L, true);
 		if (wcd937x->comp1_enable)
 			wcd937x_rx_connect_port(codec, COMP_L, true);
@@ -765,6 +841,7 @@
 	};
 	return 0;
 }
+
 static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w,
 			      struct snd_kcontrol *kcontrol, int event)
 {
@@ -776,24 +853,6 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_4,
-				    0xF0, 0x80);
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
-				    0xE0, 0xA0);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x40, 0x40);
-		usleep_range(100, 110);
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEGDAC_CTRL_2,
-				    0xE0, 0xE0);
-		usleep_range(100, 110);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x80, 0x80);
-		usleep_range(500, 510);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x3A);
-		usleep_range(500, 510);
-
 		wcd937x_rx_connect_port(codec, HPH_R, true);
 		if (wcd937x->comp2_enable)
 			wcd937x_rx_connect_port(codec, COMP_R, true);
@@ -823,16 +882,6 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2,
-				    0xE0, 0xA0);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_3, 0x02, 0x02);
-		snd_soc_update_bits(codec, WCD937X_CLASSH_MODE_2, 0xFF, 0x1C);
-		snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES,
-				    0x40, 0x40);
-		usleep_range(100, 110);
-		snd_soc_update_bits(codec, WCD937X_FLYBACK_VNEG_CTRL_2,
-				    0xE0, 0xE0);
-		usleep_range(100, 110);
 		wcd937x_rx_connect_port(codec, LO, true);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
@@ -1594,6 +1643,10 @@
 			     wcd937x_codec_enable_vdd_buck,
 			     SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_SUPPLY("CLS_H_PORT", SND_SOC_NOPM, 0, 0,
+			     wcd937x_enable_clsh,
+			     SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
 	/*rx widgets*/
 	SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0,
 				wcd937x_codec_enable_ear_pa,
@@ -1781,6 +1834,11 @@
 	{"HPHR", NULL, "VDD_BUCK"},
 	{"HPHL", NULL, "VDD_BUCK"},
 	{"AUX", NULL, "VDD_BUCK"},
+
+	{"EAR", NULL, "CLS_H_PORT"},
+	{"HPHR", NULL, "CLS_H_PORT"},
+	{"HPHL", NULL, "CLS_H_PORT"},
+	{"AUX", NULL, "CLS_H_PORT"},
 };
 
 static const struct snd_soc_dapm_route wcd9375_audio_map[] = {
@@ -1953,6 +2011,7 @@
 	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
 	snd_soc_dapm_sync(dapm);
 
+	wcd_cls_h_init(&wcd937x->clsh_info);
 	wcd937x_init_reg(codec);
 
 	if (wcd937x->variant == WCD9375_VARIANT) {
diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c
index 171337a..9516867 100644
--- a/asoc/msm-dai-q6-v2.c
+++ b/asoc/msm-dai-q6-v2.c
@@ -3231,8 +3231,9 @@
 			sizeof(struct asm_aac_dec_cfg_v2_t));
 		break;
 	case DEC_FMT_SBC:
-	case DEC_FMT_MP3:
-		/* No decoder specific data available */
+		memcpy(&dai_data->dec_config.data,
+			ucontrol->value.bytes.data + format_size,
+			sizeof(struct asm_sbc_dec_cfg_t));
 		break;
 	default:
 		pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n",
diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c
index 1dccecd..5da9795 100644
--- a/asoc/msm-pcm-q6-v2.c
+++ b/asoc/msm-pcm-q6-v2.c
@@ -686,6 +686,7 @@
 	if (!prtd->audio_client) {
 		pr_info("%s: Could not allocate memory\n", __func__);
 		kfree(prtd);
+		prtd = NULL;
 		return -ENOMEM;
 	}
 
@@ -1174,6 +1175,12 @@
 	}
 
 	prtd = substream->runtime->private_data;
+	if (prtd == NULL) {
+		pr_err("%s prtd is null.\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
 	if (prtd->audio_client == NULL) {
 		pr_err("%s prtd is null.\n", __func__);
 		ret = -EINVAL;
diff --git a/asoc/sdm845.c b/asoc/sdm845.c
index 392ac85..3fe4023 100644
--- a/asoc/sdm845.c
+++ b/asoc/sdm845.c
@@ -116,19 +116,6 @@
 	AUX_PCM_MAX,
 };
 
-enum {
-	PCM_I2S_SEL_PRIM = 0,
-	PCM_I2S_SEL_SEC,
-	PCM_I2S_SEL_TERT,
-	PCM_I2S_SEL_QUAT,
-	PCM_I2S_SEL_MAX,
-};
-
-struct mi2s_aux_pcm_common_conf {
-	struct mutex lock;
-	void *pcm_i2s_sel_vt_addr;
-};
-
 struct mi2s_conf {
 	struct mutex lock;
 	u32 ref_cnt;
@@ -142,11 +129,6 @@
 	Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT
 };
 
-struct auxpcm_conf {
-	struct mutex lock;
-	u32 ref_cnt;
-};
-
 struct dev_config {
 	u32 sample_rate;
 	u32 bit_format;
@@ -584,9 +566,7 @@
 	}
 };
 
-static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
 static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
-static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
 
 static int slim_get_sample_rate_val(int sample_rate)
 {
@@ -4116,83 +4096,6 @@
 	return ret;
 }
 
-static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
-{
-	int ret = 0;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	int index = cpu_dai->id - 1;
-
-	dev_dbg(rtd->card->dev,
-		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
-		__func__, substream->name, substream->stream,
-		cpu_dai->name, cpu_dai->id);
-
-	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
-		ret = -EINVAL;
-		dev_err(rtd->card->dev,
-			"%s: CPU DAI id (%d) out of range\n",
-			__func__, cpu_dai->id);
-		goto err;
-	}
-
-	mutex_lock(&auxpcm_intf_conf[index].lock);
-	if (++auxpcm_intf_conf[index].ref_cnt == 1) {
-		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
-			mutex_lock(&mi2s_auxpcm_conf[index].lock);
-			iowrite32(1,
-				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
-			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
-		} else {
-			dev_err(rtd->card->dev,
-				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
-				__func__);
-			ret = -EINVAL;
-		}
-	}
-	if (ret < 0)
-		auxpcm_intf_conf[index].ref_cnt--;
-
-	mutex_unlock(&auxpcm_intf_conf[index].lock);
-
-err:
-	return ret;
-}
-
-static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	int index = rtd->cpu_dai->id - 1;
-
-	dev_dbg(rtd->card->dev,
-		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
-		__func__,
-		substream->name, substream->stream,
-		rtd->cpu_dai->name, rtd->cpu_dai->id);
-
-	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
-		dev_err(rtd->card->dev,
-			"%s: CPU DAI id (%d) out of range\n",
-			__func__, rtd->cpu_dai->id);
-		return;
-	}
-
-	mutex_lock(&auxpcm_intf_conf[index].lock);
-	if (--auxpcm_intf_conf[index].ref_cnt == 0) {
-		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
-			mutex_lock(&mi2s_auxpcm_conf[index].lock);
-			iowrite32(0,
-				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
-			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
-		} else {
-			dev_err(rtd->card->dev,
-				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
-				__func__);
-		}
-	}
-	mutex_unlock(&auxpcm_intf_conf[index].lock);
-}
-
 static int msm_get_port_id(int be_id)
 {
 	int afe_port_id;
@@ -4723,18 +4626,7 @@
 				__func__, ret);
 			goto clean_up;
 		}
-		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
-			mutex_lock(&mi2s_auxpcm_conf[index].lock);
-			iowrite32(0,
-				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
-			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
-		} else {
-			dev_err(rtd->card->dev,
-				"%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
-				__func__, index);
-			ret = -EINVAL;
-			goto clk_off;
-		}
+
 		ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
 		if (ret < 0) {
 			pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
@@ -4792,11 +4684,6 @@
 	.shutdown = msm_mi2s_snd_shutdown,
 };
 
-static struct snd_soc_ops msm_aux_pcm_be_ops = {
-	.startup = msm_aux_pcm_snd_startup,
-	.shutdown = msm_aux_pcm_snd_shutdown,
-};
-
 static struct snd_soc_ops msm_be_ops = {
 	.hw_params = msm_snd_hw_params,
 };
@@ -5412,6 +5299,7 @@
 		.codec_name = "tavil_codec",
 		.codec_dai_name = "tavil_rx2",
 		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
 		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
 		.ops = &msm_slimbus_2_be_ops,
 	},
@@ -6084,7 +5972,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	{
 		.name = LPASS_BE_AUXPCM_TX,
@@ -6099,7 +5986,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	/* Secondary AUX PCM Backend DAI Links */
 	{
@@ -6115,7 +6001,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	{
 		.name = LPASS_BE_SEC_AUXPCM_TX,
@@ -6130,7 +6015,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_suspend = 1,
 		.ignore_pmdown_time = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	/* Tertiary AUX PCM Backend DAI Links */
 	{
@@ -6146,7 +6030,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	{
 		.name = LPASS_BE_TERT_AUXPCM_TX,
@@ -6161,7 +6044,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_suspend = 1,
 		.ignore_pmdown_time = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	/* Quaternary AUX PCM Backend DAI Links */
 	{
@@ -6177,7 +6059,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 	{
 		.name = LPASS_BE_QUAT_AUXPCM_TX,
@@ -6192,7 +6073,6 @@
 		.be_hw_params_fixup = msm_be_hw_params_fixup,
 		.ignore_suspend = 1,
 		.ignore_pmdown_time = 1,
-		.ops = &msm_aux_pcm_be_ops,
 	},
 };
 
@@ -6822,41 +6702,15 @@
 
 static void msm_i2s_auxpcm_init(struct platform_device *pdev)
 {
-	struct resource *muxsel;
 	int count;
 	u32 mi2s_master_slave[MI2S_MAX];
 	int ret;
-	char *str[PCM_I2S_SEL_MAX] = {
-		"lpaif_pri_mode_muxsel",
-		"lpaif_sec_mode_muxsel",
-		"lpaif_tert_mode_muxsel",
-		"lpaif_quat_mode_muxsel"
-	};
 
 	for (count = 0; count < MI2S_MAX; count++) {
 		mutex_init(&mi2s_intf_conf[count].lock);
 		mi2s_intf_conf[count].ref_cnt = 0;
 	}
 
-	for (count = 0; count < AUX_PCM_MAX; count++) {
-		mutex_init(&auxpcm_intf_conf[count].lock);
-		auxpcm_intf_conf[count].ref_cnt = 0;
-	}
-
-	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
-		mutex_init(&mi2s_auxpcm_conf[count].lock);
-		mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
-	}
-
-	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
-		muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						      str[count]);
-		if (muxsel) {
-			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
-				= ioremap(muxsel->start, resource_size(muxsel));
-		}
-	}
-
 	ret = of_property_read_u32_array(pdev->dev.of_node,
 			"qcom,msm-mi2s-master",
 			mi2s_master_slave, MI2S_MAX);
@@ -6875,21 +6729,6 @@
 {
 	int count;
 
-	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
-		if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
-		    NULL) {
-			iounmap(
-			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
-			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
-		}
-		mutex_destroy(&mi2s_auxpcm_conf[count].lock);
-	}
-
-	for (count = 0; count < AUX_PCM_MAX; count++) {
-		mutex_destroy(&auxpcm_intf_conf[count].lock);
-		auxpcm_intf_conf[count].ref_cnt = 0;
-	}
-
 	for (count = 0; count < MI2S_MAX; count++) {
 		mutex_destroy(&mi2s_intf_conf[count].lock);
 		mi2s_intf_conf[count].ref_cnt = 0;
diff --git a/asoc/sm6150.c b/asoc/sm6150.c
index 307ef0c..65aaa9b 100644
--- a/asoc/sm6150.c
+++ b/asoc/sm6150.c
@@ -30,6 +30,7 @@
 #include <sound/pcm_params.h>
 #include <sound/info.h>
 #include <soc/snd_event.h>
+#include <soc/qcom/socinfo.h>
 #include <dsp/q6afe-v2.h>
 #include <dsp/q6core.h>
 #include "device_event.h"
@@ -79,6 +80,9 @@
 #define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */
 #define MSM_HIFI_ON 1
 
+#define SM6150_SOC_VERSION_1_0 0x00010000
+#define SM6150_SOC_MSM_ID 0x163
+
 enum {
 	SLIM_RX_0 = 0,
 	SLIM_RX_1,
@@ -4925,6 +4929,19 @@
 		pdata->codec_root = entry;
 	}
 	bolero_info_create_codec_entry(pdata->codec_root, codec);
+	/*
+	 * SM6150 MSM 1.0 doesn't have hardware wake up interrupt line
+	 * from AOSS to APSS. So, it uses SW workaround and listens to
+	 * interrupt from AFE over IPC.
+	 * Check for MSM version and MSM ID and register wake irq
+	 * accordingly to provide compatibility to all chipsets.
+	 */
+	if (socinfo_get_id() == SM6150_SOC_MSM_ID &&
+	    socinfo_get_version() == SM6150_SOC_VERSION_1_0)
+		bolero_register_wake_irq(codec, true);
+	else
+		bolero_register_wake_irq(codec, false);
+
 	codec_reg_done = true;
 	return 0;
 err:
diff --git a/dsp/q6adm.c b/dsp/q6adm.c
index b9c11ac..c664b59 100644
--- a/dsp/q6adm.c
+++ b/dsp/q6adm.c
@@ -644,7 +644,7 @@
 	index = index + ch_mixer->input_channels[channel_index];
 	ret = adm_populate_channel_weight(&adm_pspd_params[index],
 					ch_mixer, channel_index);
-	if (!ret) {
+	if (ret) {
 		pr_err("%s: fail to get channel weight with error %d\n",
 			__func__, ret);
 		goto fail_cmd;
@@ -1389,7 +1389,7 @@
 	 */
 	if ((payload_size >= struct_size + data_size) &&
 	    (ARRAY_SIZE(adm_get_parameters) > idx) &&
-	    (ARRAY_SIZE(adm_get_parameters) >= idx + 1 + data_size)) {
+	    (ARRAY_SIZE(adm_get_parameters) > idx + 1 + data_size)) {
 		pr_debug("%s: Received parameter data in band\n",
 					__func__);
 		/*
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 0d5721e..2810beb 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -3541,6 +3541,10 @@
 		media_type.sample_rate =
 			cfg->data.aac_config.sample_rate;
 		break;
+	case ASM_MEDIA_FMT_SBC:
+		media_type.sample_rate =
+			cfg->data.sbc_config.sample_rate;
+		break;
 	default:
 		media_type.sample_rate =
 			afe_config.slim_sch.sample_rate;
diff --git a/dsp/q6core.c b/dsp/q6core.c
index f66fc84..d4ea984 100644
--- a/dsp/q6core.c
+++ b/dsp/q6core.c
@@ -32,7 +32,7 @@
 
 #define TIMEOUT_MS 1000
 /*
- * AVS bring up in the modem is optimitized for the new
+ * AVS bring up in the modem is optimized for the new
  * Sub System Restart design and 100 milliseconds timeout
  * is sufficient to make sure the Q6 will be ready.
  */
@@ -67,6 +67,8 @@
 	wait_queue_head_t mdf_map_resp_wait;
 	wait_queue_head_t cmd_req_wait;
 	wait_queue_head_t avcs_fwk_ver_req_wait;
+	wait_queue_head_t lpass_npa_rsc_wait;
+	u32 lpass_npa_rsc_rsp_rcvd;
 	u32 bus_bw_resp_received;
 	u32 mdf_map_resp_received;
 	enum cmd_flags {
@@ -84,6 +86,7 @@
 	struct cal_type_data *cal_data[CORE_MAX_CAL];
 	uint32_t mem_map_cal_handle;
 	uint32_t mdf_mem_map_cal_handle;
+	uint32_t npa_client_handle;
 	int32_t adsp_status;
 	int32_t avs_state;
 	struct q6core_avcs_ver_info q6core_avcs_ver_info;
@@ -321,6 +324,15 @@
 				"AVCS_CMD_UNLOAD_TOPO_MODULES",
 				adsp_err_get_err_str(payload1[1]));
 			break;
+		case AVCS_CMD_DESTROY_LPASS_NPA_CLIENT:
+		case AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES:
+			pr_debug("%s: Cmd = AVCS_CMD_CREATE_LPASS_NPA_CLIENT/AVCS_CMD_DESTROY_LPASS_NPA_CLIENT status[%s]\n",
+				__func__, adsp_err_get_err_str(payload1[1]));
+			/* ADSP status to match Linux error standard */
+			q6core_lcl.adsp_status = -payload1[1];
+			q6core_lcl.lpass_npa_rsc_rsp_rcvd = 1;
+			wake_up(&q6core_lcl.lpass_npa_rsc_wait);
+			break;
 		default:
 			pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
 					__func__,
@@ -355,6 +367,15 @@
 			wake_up(&q6core_lcl.bus_bw_req_wait);
 		}
 		break;
+	case AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT:
+		payload1 = data->payload;
+		pr_debug("%s: AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT handle %d\n",
+			__func__, payload1[1]);
+		q6core_lcl.adsp_status = payload1[0];
+		q6core_lcl.npa_client_handle = payload1[1];
+		q6core_lcl.lpass_npa_rsc_rsp_rcvd = 1;
+		wake_up(&q6core_lcl.lpass_npa_rsc_wait);
+		break;
 	case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
 		payload1 = data->payload;
 		q6core_lcl.param = payload1[0];
@@ -933,6 +954,201 @@
 }
 EXPORT_SYMBOL(q6core_is_adsp_ready);
 
+int q6core_create_lpass_npa_client(uint32_t node_id, char *client_name,
+				   uint32_t *client_handle)
+{
+	struct avcs_cmd_create_lpass_npa_client_t create_lpass_npa_client;
+	struct avcs_cmd_create_lpass_npa_client_t *cmd_ptr =
+						&create_lpass_npa_client;
+	int ret = 0;
+
+	if (!client_name) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+
+	memset(cmd_ptr, 0, sizeof(create_lpass_npa_client));
+
+	cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+	cmd_ptr->hdr.pkt_size = sizeof(create_lpass_npa_client);
+	cmd_ptr->hdr.src_port = 0;
+	cmd_ptr->hdr.dest_port = 0;
+	cmd_ptr->hdr.token = 0;
+	cmd_ptr->hdr.opcode = AVCS_CMD_CREATE_LPASS_NPA_CLIENT;
+	cmd_ptr->node_id = AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID;
+	strlcpy(cmd_ptr->client_name, client_name,
+			sizeof(cmd_ptr->client_name));
+
+	pr_debug("%s: create lpass npa client opcode[0x%x] node id[0x%x]\n",
+		__func__, cmd_ptr->hdr.opcode, cmd_ptr->node_id);
+
+	*client_handle = 0;
+	q6core_lcl.adsp_status = 0;
+	q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+	if (ret < 0) {
+		pr_err("%s: create lpass npa client failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+				(q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: timeout. waited for create lpass npa rsc client\n",
+			__func__);
+		ret = -ETIMEDOUT;
+		goto done;
+	} else {
+		/* set ret to 0 as no timeout happened */
+		ret = 0;
+	}
+
+	if (q6core_lcl.adsp_status < 0) {
+		pr_err("%s: DSP returned error %d\n",
+			__func__, q6core_lcl.adsp_status);
+		ret = q6core_lcl.adsp_status;
+		goto done;
+	}
+
+	*client_handle = q6core_lcl.npa_client_handle;
+	pr_debug("%s: q6core_lcl.npa_client_handle %d\n", __func__,
+		q6core_lcl.npa_client_handle);
+done:
+	mutex_unlock(&q6core_lcl.cmd_lock);
+	return ret;
+}
+EXPORT_SYMBOL(q6core_create_lpass_npa_client);
+
+int q6core_destroy_lpass_npa_client(uint32_t client_handle)
+{
+	struct avcs_cmd_destroy_lpass_npa_client_t destroy_lpass_npa_client;
+	struct avcs_cmd_destroy_lpass_npa_client_t *cmd_ptr =
+						&destroy_lpass_npa_client;
+	int ret = 0;
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+
+	memset(cmd_ptr, 0, sizeof(destroy_lpass_npa_client));
+
+	cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+	cmd_ptr->hdr.pkt_size = sizeof(destroy_lpass_npa_client);
+	cmd_ptr->hdr.src_port = 0;
+	cmd_ptr->hdr.dest_port = 0;
+	cmd_ptr->hdr.token = 0;
+	cmd_ptr->hdr.opcode = AVCS_CMD_DESTROY_LPASS_NPA_CLIENT;
+	cmd_ptr->client_handle = client_handle;
+
+	pr_debug("%s: dstry lpass npa client opcode[0x%x] client hdl[0x%x]\n",
+		__func__, cmd_ptr->hdr.opcode, cmd_ptr->client_handle);
+
+	q6core_lcl.adsp_status = 0;
+	q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+	if (ret < 0) {
+		pr_err("%s: destroy lpass npa client failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+				(q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: timeout. waited for destroy lpass npa rsc client\n",
+			__func__);
+		ret = -ETIMEDOUT;
+		goto done;
+	} else {
+		/* set ret to 0 as no timeout happened */
+		ret = 0;
+	}
+
+	if (q6core_lcl.adsp_status < 0) {
+		pr_err("%s: DSP returned error %d\n",
+			__func__, q6core_lcl.adsp_status);
+		ret = q6core_lcl.adsp_status;
+	}
+done:
+	mutex_unlock(&q6core_lcl.cmd_lock);
+	return ret;
+}
+EXPORT_SYMBOL(q6core_destroy_lpass_npa_client);
+
+int q6core_request_island_transition(uint32_t client_handle,
+				     uint32_t island_allow_mode)
+{
+	struct avcs_sleep_node_island_transition_config_t island_tsn_cfg;
+	struct avcs_sleep_node_island_transition_config_t *cmd_ptr =
+						&island_tsn_cfg;
+	int ret = 0;
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+
+	memset(cmd_ptr, 0, sizeof(island_tsn_cfg));
+
+	cmd_ptr->req_lpass_npa_rsc.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+	cmd_ptr->req_lpass_npa_rsc.hdr.pkt_size = sizeof(island_tsn_cfg);
+	cmd_ptr->req_lpass_npa_rsc.hdr.src_port = 0;
+	cmd_ptr->req_lpass_npa_rsc.hdr.dest_port = 0;
+	cmd_ptr->req_lpass_npa_rsc.hdr.token = 0;
+	cmd_ptr->req_lpass_npa_rsc.hdr.opcode =
+					AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES;
+	cmd_ptr->req_lpass_npa_rsc.client_handle = client_handle;
+	cmd_ptr->req_lpass_npa_rsc.resource_id =
+				AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID;
+	cmd_ptr->island_allow_mode = island_allow_mode;
+
+	pr_debug("%s: req islnd tnsn opcode[0x%x] island_allow_mode[0x%x]\n",
+		__func__, cmd_ptr->req_lpass_npa_rsc.hdr.opcode,
+		cmd_ptr->island_allow_mode);
+
+	q6core_lcl.adsp_status = 0;
+	q6core_lcl.lpass_npa_rsc_rsp_rcvd = 0;
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) cmd_ptr);
+	if (ret < 0) {
+		pr_err("%s: island tnsn cmd send failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.lpass_npa_rsc_wait,
+				(q6core_lcl.lpass_npa_rsc_rsp_rcvd == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: timeout. waited for island lpass npa rsc req\n",
+			__func__);
+		ret = -ETIMEDOUT;
+		goto done;
+	} else {
+		/* set ret to 0 as no timeout happened */
+		ret = 0;
+	}
+
+	if (q6core_lcl.adsp_status < 0) {
+		pr_err("%s: DSP returned error %d\n",
+			__func__, q6core_lcl.adsp_status);
+		ret = q6core_lcl.adsp_status;
+	}
+done:
+	mutex_unlock(&q6core_lcl.cmd_lock);
+	return ret;
+}
+EXPORT_SYMBOL(q6core_request_island_transition);
+
 int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
 			uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle)
 {
@@ -1671,6 +1887,7 @@
 	init_waitqueue_head(&q6core_lcl.cmd_req_wait);
 	init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait);
 	init_waitqueue_head(&q6core_lcl.mdf_map_resp_wait);
+	init_waitqueue_head(&q6core_lcl.lpass_npa_rsc_wait);
 	q6core_lcl.cmd_resp_received_flag = FLAG_NONE;
 	mutex_init(&q6core_lcl.cmd_lock);
 	mutex_init(&q6core_lcl.ver_lock);
diff --git a/include/asoc/wcd9xxx_registers.h b/include/asoc/wcd9xxx_registers.h
new file mode 100644
index 0000000..928dfcc
--- /dev/null
+++ b/include/asoc/wcd9xxx_registers.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9XXX_REGISTERS_H
+#define _WCD9XXX_REGISTERS_H
+
+#define WCD9XXX_BASE_ADDRESS 0x3000
+
+#define WCD9XXX_ANA_RX_SUPPLIES                     (WCD9XXX_BASE_ADDRESS+0x008)
+#define WCD9XXX_ANA_HPH                             (WCD9XXX_BASE_ADDRESS+0x009)
+#define WCD9XXX_CLASSH_MODE_2                       (WCD9XXX_BASE_ADDRESS+0x098)
+#define WCD9XXX_CLASSH_MODE_3                       (WCD9XXX_BASE_ADDRESS+0x099)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_4                 (WCD9XXX_BASE_ADDRESS+0x0A8)
+#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2              (WCD9XXX_BASE_ADDRESS+0x0AF)
+#define WCD9XXX_RX_BIAS_HPH_LOWPOWER                (WCD9XXX_BASE_ADDRESS+0x0BF)
+#define WCD9XXX_RX_BIAS_FLYB_BUFF                   (WCD9XXX_BASE_ADDRESS+0x0C7)
+#define WCD9XXX_HPH_PA_CTL1                         (WCD9XXX_BASE_ADDRESS+0x0D1)
+#define WCD9XXX_HPH_NEW_INT_PA_MISC2                (WCD9XXX_BASE_ADDRESS+0x138)
+
+#endif
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index eb529dc..9ac7281 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -4311,9 +4311,23 @@
  * #ASM_MEDIA_FMT_SBC media format.
  */
 struct asm_sbc_dec_cfg_t {
-	/* All configuration is extracted from the stream */
-} __packed;
+	uint16_t          channels;
+	/*
+	 * Number of channels present in the SBC stream.
+	 *
+	 * @values
+	 * - 1 -- Mono
+	 * - 2 -- Stereo
+	 */
 
+	uint32_t          sample_rate;
+	/*
+	 * Number of samples per second.
+	 *
+	 * @values 8000, 11025, 12000, 16000, 22050, 24000, 32000,
+	 *         44100, 48000, 64000, 88200, 96000 Hz
+	 */
+} __packed;
 /*
  * Payload of the MP3 decoder configuration parameters in the
  * #ASM_MEDIA_FMT_MP3 media format.
diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h
index 1fa0d1e..e1f8b97 100644
--- a/include/dsp/q6core.h
+++ b/include/dsp/q6core.h
@@ -19,6 +19,8 @@
 
 #define AVCS_CMD_ADSP_EVENT_GET_STATE		0x0001290C
 #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE	0x0001290D
+#define AVCS_API_VERSION_V4		4
+#define APRV2_IDS_SERVICE_ID_ADSP_CORE_V (0x3)
 
 bool q6core_is_adsp_ready(void);
 
@@ -150,7 +152,7 @@
 } __packed;
 
 
-#define AVCS_CMD_DEREGISTER_TOPOLOGIES                                0x0001292a
+#define AVCS_CMD_DEREGISTER_TOPOLOGIES                             0x0001292a
 
 /* The payload for the AVCS_CMD_DEREGISTER_TOPOLOGIES command */
 struct avcs_cmd_deregister_topologies {
@@ -195,6 +197,99 @@
 	uint32_t topology_id;
 } __packed;
 
+/* This command allows a remote client(HLOS) creates a client to LPASS NPA
+ * resource node. Currently, this command supports only the NPA Sleep resource
+ * "/island/core/drivers" node ID.
+ */
+#define AVCS_CMD_CREATE_LPASS_NPA_CLIENT    0x00012985
+
+#define AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID    0x00000001
+
+struct avcs_cmd_create_lpass_npa_client_t {
+	struct apr_hdr hdr;
+	uint32_t  node_id;
+	/* Unique ID of the NPA node.
+	 * @values
+	 *   - #AVCS_SLEEP_ISLAND_CORE_DRIVER_NODE_ID
+	 */
+
+	char client_name[16];
+	/* Client name, up to a maximum of sixteen characters.*/
+};
+
+/* In response to the #AVCS_CMD_CREATE_LPASS_NPA_CLIENT command, the AVCS
+ * returns the handle to remote HLOS client.
+ */
+#define AVCS_CMDRSP_CREATE_LPASS_NPA_CLIENT    0x00012986
+
+struct avcs_cmdrsp_create_lpass_npa_client_t {
+	uint32_t status;
+	/* Status message (error code).
+	 * @values
+	 *   - ADSP_EOK -- Create was successful
+	 *   - ADSP_EFAILED -- Create failed
+	 */
+
+	uint32_t  client_handle;
+	/* Handle of the client.*/
+};
+
+/* The remote HLOS client use this command to issue the request to the npa
+ * resource. Remote client(HLOS) must send the valid npa client handle and
+ * resource id info.
+ */
+#define AVCS_CMD_REQUEST_LPASS_NPA_RESOURCES    0x00012987
+
+#define AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID    0x00000001
+
+#define SLEEP_RESTRICT_ISLAND                0x0
+#define SLEEP_ALLOW_ISLAND                   0x1
+
+/* Immediately following this structure is the resource request configuration
+ * data payload. Payload varies depend on the resource_id requested.
+ * Currently supported only island transition payload.
+ */
+struct avcs_cmd_request_lpass_npa_resources_t {
+	struct apr_hdr hdr;
+	uint32_t  client_handle;
+	/* Handle of the client.
+	 * @values
+	 * - Valid uint32 number
+	 */
+
+	uint32_t  resource_id;
+	/* Unique ID of the NPA resource ID.
+	 * @values
+	 * - #AVCS_SLEEP_NODE_ISLAND_TRANSITION_RESOURCE_ID
+	 */
+};
+
+/* This structure contains the sleep node resource payload data.
+ */
+struct avcs_sleep_node_island_transition_config_t {
+	struct avcs_cmd_request_lpass_npa_resources_t req_lpass_npa_rsc;
+	uint32_t  island_allow_mode;
+	/* Specifies the island state.
+	 * @values
+	 * - #SLEEP_RESTRICT_ISLAND
+	 * - #SLEEP_ALLOW_ISLAND
+	 */
+};
+
+/* This command allows remote client(HLOS) to destroy the npa node client
+ * handle, which is created using the #AVCS_CMD_CREATE_LPASS_NPA_CLIENT command.
+ * Remote client(HLOS) must send the valid npa client handle.
+ */
+#define AVCS_CMD_DESTROY_LPASS_NPA_CLIENT    0x00012988
+
+struct avcs_cmd_destroy_lpass_npa_client_t {
+	struct apr_hdr hdr;
+	uint32_t  client_handle;
+	/* Handle of the client.
+	 * @values
+	 * - Valid uint32 number
+	 */
+};
 
 int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
 			uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle);
@@ -212,6 +307,12 @@
 int32_t q6core_load_unload_topo_modules(uint32_t topology_id,
 			bool preload_type);
 
+int q6core_create_lpass_npa_client(uint32_t node_id, char *client_name,
+				   uint32_t *client_handle);
+int q6core_destroy_lpass_npa_client(uint32_t client_handle);
+int q6core_request_island_transition(uint32_t client_handle,
+				     uint32_t island_allow_mode);
+
 #if IS_ENABLED(CONFIG_USE_Q6_32CH_SUPPORT)
 static inline bool q6core_use_Q6_32ch_support(void)
 {
diff --git a/include/soc/swr-wcd.h b/include/soc/swr-wcd.h
index 8ea8616..7eaad27 100644
--- a/include/soc/swr-wcd.h
+++ b/include/soc/swr-wcd.h
@@ -26,6 +26,7 @@
 	SWR_CLK_FREQ,
 	SWR_DEVICE_SSR_DOWN,
 	SWR_DEVICE_SSR_UP,
+	SWR_REGISTER_WAKE_IRQ,
 };
 
 struct swr_mstr_port {
diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c
index 21d51fb..f6628bb 100644
--- a/soc/pinctrl-lpi.c
+++ b/soc/pinctrl-lpi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
@@ -21,12 +21,16 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/clk.h>
 #include <soc/snd_event.h>
+#include <linux/pm_runtime.h>
 #include <dsp/audio_notifier.h>
 
 #include "core.h"
 #include "pinctrl-utils.h"
 
+#define LPI_AUTO_SUSPEND_DELAY          1500 /* delay in msec */
+
 #define LPI_ADDRESS_SIZE			0x20000
 
 #define LPI_GPIO_REG_VAL_CTL			0x00
@@ -97,6 +101,7 @@
 	struct pinctrl_dev *ctrl;
 	struct gpio_chip chip;
 	char __iomem	*base;
+	struct clk *lpass_npa_rsc_island;
 };
 
 static const char *const lpi_gpio_groups[] = {
@@ -128,11 +133,14 @@
 				   __func__);
 		return 0;
 	}
+	pm_runtime_get_sync(lpi_dev);
 
 	ret = ioread32(pad->base + pad->offset + addr);
 	if (ret < 0)
 		pr_err("%s: read 0x%x failed\n", __func__, addr);
 
+	pm_runtime_mark_last_busy(lpi_dev);
+	pm_runtime_put_autosuspend(lpi_dev);
 	return ret;
 }
 
@@ -144,8 +152,12 @@
 				   __func__);
 		return 0;
 	}
+	pm_runtime_get_sync(lpi_dev);
 
 	iowrite32(val, pad->base + pad->offset + addr);
+
+	pm_runtime_mark_last_busy(lpi_dev);
+	pm_runtime_put_autosuspend(lpi_dev);
 	return 0;
 }
 
@@ -499,6 +511,7 @@
 	int ret, npins, i;
 	char __iomem *lpi_base;
 	u32 reg;
+	struct clk *lpass_npa_rsc_island = NULL;
 
 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
 	if (ret < 0) {
@@ -608,6 +621,22 @@
 		goto err_snd_evt;
 	}
 
+	/* Register LPASS NPA resource */
+	lpass_npa_rsc_island = devm_clk_get(&pdev->dev, "island_lpass_npa_rsc");
+	if (IS_ERR(lpass_npa_rsc_island)) {
+		ret = PTR_ERR(lpass_npa_rsc_island);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "island_lpass_npa_rsc", ret);
+		lpass_npa_rsc_island = NULL;
+		ret = 0;
+	}
+	state->lpass_npa_rsc_island = lpass_npa_rsc_island;
+
+	pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	return 0;
 
 err_snd_evt:
@@ -621,6 +650,8 @@
 static int lpi_pinctrl_remove(struct platform_device *pdev)
 {
 	struct lpi_gpio_state *state = platform_get_drvdata(pdev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
 
 	snd_event_client_deregister(&pdev->dev);
 	audio_notifier_deregister("lpi_tlmm");
@@ -635,9 +666,49 @@
 
 MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
 
+int lpi_pinctrl_runtime_resume(struct device *dev)
+{
+	struct lpi_gpio_state *state = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (state->lpass_npa_rsc_island == NULL) {
+		dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+		return 0;
+	}
+
+	ret = clk_prepare_enable(state->lpass_npa_rsc_island);
+	if (ret < 0) {
+		dev_err(dev, "%s:lpass npa rsc island enable failed\n",
+			__func__);
+	}
+	pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY);
+	return 0;
+}
+
+int lpi_pinctrl_runtime_suspend(struct device *dev)
+{
+	struct lpi_gpio_state *state = dev_get_drvdata(dev);
+
+	if (state->lpass_npa_rsc_island == NULL) {
+		dev_dbg(dev, "%s: Invalid lpass npa rsc node\n", __func__);
+		return 0;
+	}
+	clk_disable_unprepare(state->lpass_npa_rsc_island);
+	return 0;
+}
+
+static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = {
+	SET_RUNTIME_PM_OPS(
+		lpi_pinctrl_runtime_suspend,
+		lpi_pinctrl_runtime_resume,
+		NULL
+	)
+};
+
 static struct platform_driver lpi_pinctrl_driver = {
 	.driver = {
 		   .name = "qcom-lpi-pinctrl",
+		   .pm = &lpi_pinctrl_dev_pm_ops,
 		   .of_match_table = lpi_pinctrl_of_match,
 	},
 	.probe = lpi_pinctrl_probe,
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index df373ab..efb20f7 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -41,6 +41,8 @@
 			((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))
 
 #define SWR_INVALID_PARAM 0xFF
+#define SWR_HSTOP_MAX_VAL 0xF
+#define SWR_HSTART_MIN_VAL 0x0
 
 #define SWRM_INTERRUPT_STATUS_MASK 0x1FDFD
 /* pm runtime auto suspend timer in msecs */
@@ -534,6 +536,10 @@
 		dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
 		return -EINVAL;
 	}
+	if (!dev_num) {
+		dev_err(&master->dev, "%s: invalid slave dev num\n", __func__);
+		return -EINVAL;
+	}
 	mutex_lock(&swrm->devlock);
 	if (!swrm->dev_up) {
 		mutex_unlock(&swrm->devlock);
@@ -542,11 +548,7 @@
 	mutex_unlock(&swrm->devlock);
 
 	pm_runtime_get_sync(swrm->dev);
-	if (dev_num)
-		ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr,
-					   len);
-	else
-		val = swr_master_read(swrm, reg_addr);
+	ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, len);
 
 	if (!ret)
 		*reg_val = (u8)val;
@@ -567,6 +569,10 @@
 		dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
 		return -EINVAL;
 	}
+	if (!dev_num) {
+		dev_err(&master->dev, "%s: invalid slave dev num\n", __func__);
+		return -EINVAL;
+	}
 	mutex_lock(&swrm->devlock);
 	if (!swrm->dev_up) {
 		mutex_unlock(&swrm->devlock);
@@ -575,10 +581,7 @@
 	mutex_unlock(&swrm->devlock);
 
 	pm_runtime_get_sync(swrm->dev);
-	if (dev_num)
-		ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
-	else
-		swr_master_write(swrm, reg_addr, reg_val);
+	ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
 
 	pm_runtime_put_autosuspend(swrm->dev);
 	pm_runtime_mark_last_busy(swrm->dev);
@@ -926,6 +929,10 @@
 			reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank);
 			hparams = (mport->hstop << 4) | mport->hstart;
 			val[len++] = hparams;
+		} else {
+			reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank);
+			hparams = (SWR_HSTOP_MAX_VAL << 4) | SWR_HSTART_MIN_VAL;
+			val[len++] = hparams;
 		}
 		if (mport->blk_pack_mode != SWR_INVALID_PARAM) {
 			reg[len] = SWRM_DP_BLOCK_CTRL3_BANK(i + 1, bank);
@@ -1414,6 +1421,32 @@
 	return ret;
 }
 
+static irqreturn_t swrm_wakeup_interrupt(int irq, void *dev)
+{
+	struct swr_mstr_ctrl *swrm = dev;
+	int ret = IRQ_HANDLED;
+
+	if (!swrm || !(swrm->dev)) {
+		pr_err("%s: swrm or dev is null\n", __func__);
+		return IRQ_NONE;
+	}
+	mutex_lock(&swrm->devlock);
+	if (!swrm->dev_up) {
+		if (swrm->wake_irq > 0)
+			disable_irq_nosync(swrm->wake_irq);
+		mutex_unlock(&swrm->devlock);
+		return ret;
+	}
+	mutex_unlock(&swrm->devlock);
+	if (swrm->wake_irq > 0)
+		disable_irq_nosync(swrm->wake_irq);
+	pm_runtime_get_sync(swrm->dev);
+	pm_runtime_mark_last_busy(swrm->dev);
+	pm_runtime_put_autosuspend(swrm->dev);
+
+	return ret;
+}
+
 static void swrm_wakeup_work(struct work_struct *work)
 {
 	struct swr_mstr_ctrl *swrm;
@@ -1585,8 +1618,8 @@
 		schedule_work(&(swrm->dc_presence_work));
 		break;
 	case SWR_WAKE_IRQ_EVENT:
-		if (swrm->wakeup_req && !swrm->wakeup_triggered) {
-			swrm->wakeup_triggered = true;
+		if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) {
+			swrm->ipc_wakeup_triggered = true;
 			schedule_work(&swrm->wakeup_work);
 		}
 		break;
@@ -1762,6 +1795,8 @@
 	swrm->mclk_freq = MCLK_FREQ;
 	swrm->dev_up = true;
 	swrm->state = SWR_MSTR_UP;
+	swrm->ipc_wakeup = false;
+	swrm->ipc_wakeup_triggered = false;
 	init_completion(&swrm->reset);
 	init_completion(&swrm->broadcast);
 	init_completion(&swrm->clk_off_complete);
@@ -1789,11 +1824,6 @@
 		}
 	}
 
-	if (of_property_read_u32(swrm->dev->of_node,
-			"qcom,swr-wakeup-required", &swrm->wakeup_req)) {
-		swrm->wakeup_req = false;
-	}
-
 	if (swrm->reg_irq) {
 		ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
 			    SWR_IRQ_REGISTER);
@@ -1903,6 +1933,9 @@
 				swrm, SWR_IRQ_FREE);
 	else if (swrm->irq)
 		free_irq(swrm->irq, swrm);
+	else if (swrm->wake_irq > 0)
+		free_irq(swrm->wake_irq, swrm);
+
 	pm_runtime_disable(&pdev->dev);
 	pm_runtime_set_suspended(&pdev->dev);
 	swr_unregister_master(&swrm->master);
@@ -1944,9 +1977,10 @@
 
 	if ((swrm->state == SWR_MSTR_DOWN) ||
 	    (swrm->state == SWR_MSTR_SSR && swrm->dev_up)) {
-		if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
-			msm_aud_evt_blocking_notifier_call_chain(
-				SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
+		if (swrm->clk_stop_mode0_supp) {
+			if (swrm->ipc_wakeup)
+				msm_aud_evt_blocking_notifier_call_chain(
+					SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
 		}
 
 		if (swrm_clk_request(swrm, true))
@@ -2025,10 +2059,14 @@
 		}
 		swrm_clk_request(swrm, false);
 
-		if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
-			msm_aud_evt_blocking_notifier_call_chain(
-				SWR_WAKE_IRQ_REGISTER, (void *)swrm);
-			swrm->wakeup_triggered = false;
+		if (swrm->clk_stop_mode0_supp) {
+			if (swrm->wake_irq > 0) {
+				enable_irq(swrm->wake_irq);
+			} else if (swrm->ipc_wakeup) {
+				msm_aud_evt_blocking_notifier_call_chain(
+					SWR_WAKE_IRQ_REGISTER, (void *)swrm);
+				swrm->ipc_wakeup_triggered = false;
+			}
 		}
 
 	}
@@ -2064,6 +2102,35 @@
 	return 0;
 }
 
+int swrm_register_wake_irq(struct swr_mstr_ctrl *swrm)
+{
+	int ret = 0;
+
+	if (!swrm->ipc_wakeup) {
+		swrm->wake_irq = platform_get_irq_byname(swrm->pdev,
+					"swr_wake_irq");
+		if (swrm->wake_irq < 0) {
+			dev_err(swrm->dev,
+				"%s() error getting wake irq handle: %d\n",
+				__func__, swrm->wake_irq);
+			return -EINVAL;
+		}
+
+		ret = request_threaded_irq(swrm->wake_irq, NULL,
+					   swrm_wakeup_interrupt,
+					   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+					   "swr_wake_irq", swrm);
+		if (ret) {
+			dev_err(swrm->dev, "%s: Failed to request irq %d\n",
+				__func__, ret);
+			return -EINVAL;
+		}
+		/* Disable wake irq - enable it after clock stop */
+		disable_irq(swrm->wake_irq);
+	}
+	return ret;
+}
+
 /**
  * swrm_wcd_notify - parent device can notify to soundwire master through
  * this function
@@ -2110,9 +2177,10 @@
 		break;
 	case SWR_DEVICE_SSR_UP:
 		/* wait for clk voting to be zero */
+		reinit_completion(&swrm->clk_off_complete);
 		if (swrm->clk_ref_count &&
 			 !wait_for_completion_timeout(&swrm->clk_off_complete,
-						   (1 * HZ/100)))
+						   msecs_to_jiffies(200)))
 			dev_err(swrm->dev, "%s: clock voting not zero\n",
 				__func__);
 
@@ -2132,6 +2200,13 @@
 		break;
 	case SWR_DEVICE_UP:
 		dev_dbg(swrm->dev, "%s: swr master up called\n", __func__);
+		mutex_lock(&swrm->devlock);
+		if (!swrm->dev_up) {
+			dev_dbg(swrm->dev, "SSR not complete yet\n");
+			mutex_unlock(&swrm->devlock);
+			return -EBUSY;
+		}
+		mutex_unlock(&swrm->devlock);
 		mutex_lock(&swrm->mlock);
 		pm_runtime_mark_last_busy(&pdev->dev);
 		pm_runtime_get_sync(&pdev->dev);
@@ -2181,6 +2256,21 @@
 			mutex_unlock(&swrm->mlock);
 		}
 		break;
+	case SWR_REGISTER_WAKE_IRQ:
+		if (!data) {
+			dev_err(swrm->dev, "%s: reg wake irq data is NULL\n",
+				__func__);
+			ret = -EINVAL;
+		} else {
+			mutex_lock(&swrm->mlock);
+			swrm->ipc_wakeup = *(u32 *)data;
+			ret = swrm_register_wake_irq(swrm);
+			if (ret)
+				dev_err(swrm->dev, "%s: register wake_irq failed\n",
+					__func__);
+			mutex_unlock(&swrm->mlock);
+		}
+		break;
 	default:
 		dev_err(swrm->dev, "%s: swr master unknown id %d\n",
 			__func__, id);
diff --git a/soc/swr-mstr-ctrl.h b/soc/swr-mstr-ctrl.h
index 4a40fa3..b3e358a 100644
--- a/soc/swr-mstr-ctrl.h
+++ b/soc/swr-mstr-ctrl.h
@@ -131,6 +131,7 @@
 	int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
 			void *data), void *swr_handle, int type);
 	int irq;
+	int wake_irq;
 	int version;
 	int mclk_freq;
 	u32 num_dev;
@@ -152,9 +153,9 @@
 	int swr_irq;
 	u32 clk_stop_mode0_supp;
 	struct work_struct wakeup_work;
-	u32 wakeup_req;
+	u32 ipc_wakeup;
 	bool dev_up;
-	bool wakeup_triggered;
+	bool ipc_wakeup_triggered;
 };
 
 #endif /* _SWR_WCD_CTRL_H */