Merge android12-gs-pixel-5.10-sc-qpr2 into android12-gs-pixel-5.10-sc-v2

Signed-off-by: Robin Peng <robinpeng@google.com>
Change-Id: I31fecdfcd76965cbad32134b43c887bd11129c15
diff --git a/cs35l41/cs35l41.c b/cs35l41/cs35l41.c
index 7b9d400..495d19c 100644
--- a/cs35l41/cs35l41.c
+++ b/cs35l41/cs35l41.c
@@ -2854,13 +2854,33 @@
 	return 0;
 }
 
+static const char * const dapm_names[] = { "SPK", "VP", "VBST", "ISENSE",
+	"VSENSE", "TEMP", "AMP Playback", "AMP Capture" };
+
+static void cs35l41_component_ignore_suspend(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+	char dapm_name[64];
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dapm_names); i++) {
+		if (component->name_prefix)
+			snprintf(dapm_name, sizeof(dapm_name), "%s %s",
+					component->name_prefix, dapm_names[i]);
+		else
+			snprintf(dapm_name, sizeof(dapm_name), "%s", dapm_names[i]);
+
+		pr_debug("snd_soc_dapm_ignore_suspend dapm name[%s]", dapm_name);
+		snd_soc_dapm_ignore_suspend(dapm, dapm_name);
+	}
+	snd_soc_dapm_sync(dapm);
+}
+
 static int cs35l41_component_probe(struct snd_soc_component *component)
 {
 	struct cs35l41_private *cs35l41 =
 		snd_soc_component_get_drvdata(component);
 	struct snd_kcontrol_new *kcontrol;
-	struct snd_soc_dapm_context *dapm =
-			snd_soc_component_get_dapm(component);
 	int ret = 0;
 
 	component->regmap = cs35l41->regmap;
@@ -2892,26 +2912,8 @@
 			       "snd_soc_add_codec_controls failed (%d)\n", ret);
 		kfree(kcontrol);
 	}
-	if (component->name_prefix && !strcmp(component->name_prefix, "R")) {
-		snd_soc_dapm_ignore_suspend(dapm, "R SPK");
-		snd_soc_dapm_ignore_suspend(dapm, "R VP");
-		snd_soc_dapm_ignore_suspend(dapm, "R VBST");
-		snd_soc_dapm_ignore_suspend(dapm, "R ISENSE");
-		snd_soc_dapm_ignore_suspend(dapm, "R VSENSE");
-		snd_soc_dapm_ignore_suspend(dapm, "R TEMP");
-		snd_soc_dapm_ignore_suspend(dapm, "R AMP Playback");
-		snd_soc_dapm_ignore_suspend(dapm, "R AMP Capture");
-	} else {
-		snd_soc_dapm_ignore_suspend(dapm, "AMP Playback");
-		snd_soc_dapm_ignore_suspend(dapm, "VBST");
-		snd_soc_dapm_ignore_suspend(dapm, "SPK");
-		snd_soc_dapm_ignore_suspend(dapm, "VP");
-		snd_soc_dapm_ignore_suspend(dapm, "ISENSE");
-		snd_soc_dapm_ignore_suspend(dapm, "VSENSE");
-		snd_soc_dapm_ignore_suspend(dapm, "TEMP");
-		snd_soc_dapm_ignore_suspend(dapm, "AMP Capture");
-	}
-	snd_soc_dapm_sync(dapm);
+
+	cs35l41_component_ignore_suspend(component);
 exit:
 	return ret;
 }
diff --git a/cs40l26/Makefile b/cs40l26/Makefile
index 8ba7dd4..5642c07 100644
--- a/cs40l26/Makefile
+++ b/cs40l26/Makefile
@@ -16,7 +16,8 @@
 M ?= $(shell pwd)
 
 KBUILD_OPTIONS += CONFIG_INPUT_CS40L26_I2C=m \
-		  CONFIG_CIRRUS_FIRMWARE_CL_DSP=m
+		  CONFIG_CIRRUS_FIRMWARE_CL_DSP=m \
+		  CONFIG_SND_SOC_CS40L26=m
 
 EXTRA_CFLAGS += -DDYNAMIC_DEBUG_MODULE
 EXTRA_CFLAGS += -DCONFIG_INPUT_CS40L26_ATTR_UNDER_BUS
diff --git a/cs40l26/cl_dsp.c b/cs40l26/cl_dsp.c
index 8e02340..12e7a2c 100644
--- a/cs40l26/cl_dsp.c
+++ b/cs40l26/cl_dsp.c
@@ -211,7 +211,12 @@
 		return -EPERM;
 	}
 
-	memcpy(&dsp->wt_desc->owt.raw_data, &bin->data[0], bin->size);
+	dsp->wt_desc->owt.raw_data = devm_kzalloc(dsp->dev, bin->size,
+			GFP_KERNEL);
+	if (!dsp->wt_desc->owt.raw_data)
+		return -ENOMEM;
+
+	memcpy(dsp->wt_desc->owt.raw_data, &bin->data[0], bin->size);
 
 	return 0;
 }
@@ -219,7 +224,7 @@
 static int cl_dsp_read_wt(struct cl_dsp *dsp, int pos, int size)
 {
 	struct cl_dsp_owt_header *entry = dsp->wt_desc->owt.waves;
-	void *buf = (void *)&dsp->wt_desc->owt.raw_data[pos];
+	void *buf = (void *)(dsp->wt_desc->owt.raw_data + pos);
 	struct cl_dsp_memchunk ch = cl_dsp_memchunk_create(buf, size);
 	u32 *wbuf = buf, *max = buf;
 	int i;
@@ -248,6 +253,7 @@
 		}
 	}
 
+	dev_err(dsp->dev, "Maximum number of wavetable entries exceeded\n");
 	return -E2BIG;
 }
 
@@ -266,13 +272,7 @@
 		return -EINVAL;
 	}
 
-	if (CL_DSP_GET_MAJOR(header.fw_revision)
-			!= CL_DSP_GET_MAJOR(dsp->algo_info[0].rev)) {
-		dev_err(dev,
-			"Coeff. revision 0x%06X incompatible with 0x%06X\n",
-			header.fw_revision, dsp->algo_info[0].rev);
-		return -EINVAL;
-	} else if (header.fw_revision != dsp->algo_info[0].rev) {
+	if (header.fw_revision != dsp->algo_info[0].rev) {
 		dev_warn(dev,
 			"Coeff. rev. 0x%06X mistmatches 0x%06X, continuing..\n",
 			header.fw_revision, dsp->algo_info[0].rev);
@@ -327,6 +327,7 @@
 	union cl_dsp_wmdr_header wmdr_header;
 	char wt_date[CL_DSP_WMDR_DATE_LEN];
 	unsigned int reg, wt_reg, algo_rev;
+	u16 algo_id, parent_id;
 	int i;
 
 	if  (!dsp)
@@ -358,11 +359,13 @@
 		memcpy(data_block.payload, &fw->data[pos],
 				data_block.header.data_len);
 
+		algo_id = data_block.header.algo_id & 0xFFFF;
+
 		if (data_block.header.block_type != CL_DSP_WMDR_NAME_TYPE &&
 			data_block.header.block_type != CL_DSP_WMDR_INFO_TYPE) {
 			for (i = 0; i < dsp->num_algos; i++) {
-				if (data_block.header.algo_id
-						== dsp->algo_info[i].id)
+				parent_id = dsp->algo_info[i].id & 0xFFFF;
+				if (algo_id == parent_id)
 					break;
 			}
 
@@ -388,8 +391,7 @@
 				goto err_free;
 			}
 
-			wt_found = ((data_block.header.algo_id & 0xFFFF) ==
-					(dsp->wt_desc->id & 0xFFFF));
+			wt_found = (algo_id == (dsp->wt_desc->id & 0xFFFF));
 		}
 
 		switch (data_block.header.block_type) {
@@ -884,7 +886,6 @@
 		coeff_desc = list_first_entry(&dsp->coeff_desc_head,
 				struct cl_dsp_coeff_desc, list);
 		list_del(&coeff_desc->list);
-		kfree(coeff_desc->parent_name);
 		devm_kfree(dsp->dev, coeff_desc);
 	}
 }
diff --git a/cs40l26/cl_dsp.h b/cs40l26/cl_dsp.h
index fbaf62e..9ec931b 100644
--- a/cs40l26/cl_dsp.h
+++ b/cs40l26/cl_dsp.h
@@ -137,11 +137,9 @@
 #define CL_DSP_ALGO_ENTRY_SIZE			24
 
 /* open wavetable */
-#define CL_DSP_OWT_HEADER_MAX_LEN		128
+#define CL_DSP_OWT_HEADER_MAX_LEN		254
 #define CL_DSP_OWT_HEADER_ENTRY_SIZE		12
 
-#define CL_DSP_MAX_BIN_SIZE			9584
-
 /* macros */
 #define CL_DSP_WORD_ALIGN(n)	(CL_DSP_BYTES_PER_WORD +\
 				(((n) / CL_DSP_BYTES_PER_WORD) *\
@@ -268,7 +266,7 @@
 	struct cl_dsp_owt_header waves[CL_DSP_OWT_HEADER_MAX_LEN];
 	int nwaves;
 	u32 bytes;
-	u8 raw_data[CL_DSP_MAX_BIN_SIZE];
+	u8 *raw_data;
 };
 
 struct cl_dsp_wt_desc {
diff --git a/cs40l26/cs40l26-codec.c b/cs40l26/cs40l26-codec.c
index 9c14b9f..d6def2f 100644
--- a/cs40l26/cs40l26-codec.c
+++ b/cs40l26/cs40l26-codec.c
@@ -7,12 +7,12 @@
 #include "cs40l26.h"
 
 static const struct cs40l26_pll_sysclk_config cs40l26_pll_sysclk[] = {
-	{CS40L26_PLL_CLK_FRQ0, CS40L26_PLL_CLK_CFG0},
-	{CS40L26_PLL_CLK_FRQ1, CS40L26_PLL_CLK_CFG1},
-	{CS40L26_PLL_CLK_FRQ2, CS40L26_PLL_CLK_CFG2},
-	{CS40L26_PLL_CLK_FRQ3, CS40L26_PLL_CLK_CFG3},
-	{CS40L26_PLL_CLK_FRQ4, CS40L26_PLL_CLK_CFG4},
-	{CS40L26_PLL_CLK_FRQ5, CS40L26_PLL_CLK_CFG5},
+	{CS40L26_PLL_CLK_FRQ_32768, CS40L26_PLL_CLK_CFG_32768},
+	{CS40L26_PLL_CLK_FRQ_1536000, CS40L26_PLL_CLK_CFG_1536000},
+	{CS40L26_PLL_CLK_FRQ_3072000, CS40L26_PLL_CLK_CFG_3072000},
+	{CS40L26_PLL_CLK_FRQ_6144000, CS40L26_PLL_CLK_CFG_6144000},
+	{CS40L26_PLL_CLK_FRQ_9600000, CS40L26_PLL_CLK_CFG_9600000},
+	{CS40L26_PLL_CLK_FRQ_12288000, CS40L26_PLL_CLK_CFG_12288000},
 };
 
 static int cs40l26_get_clk_config(u32 freq, u8 *clk_cfg)
@@ -45,7 +45,8 @@
 	case CS40L26_PLL_REFCLK_MCLK:
 		clk_sel = CS40L26_PLL_CLK_SEL_MCLK;
 
-		ret = cs40l26_get_clk_config(CS40L26_PLL_CLK_FRQ0, &clk_cfg);
+		ret = cs40l26_get_clk_config(CS40L26_PLL_CLK_FRQ_32768,
+				&clk_cfg);
 		break;
 	case CS40L26_PLL_REFCLK_FSYNC:
 		ret = -EPERM;
@@ -60,7 +61,7 @@
 	}
 
 	ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT,
-			CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, CS40L26_ENABLE <<
+			CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 1 <<
 			CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT);
 	if (ret) {
 		dev_err(dev, "Failed to set Open-Loop PLL\n");
@@ -77,7 +78,7 @@
 	}
 
 	ret = regmap_update_bits(regmap, CS40L26_REFCLK_INPUT,
-			CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, CS40L26_DISABLE <<
+			CS40L26_PLL_REFCLK_OPEN_LOOP_MASK, 0 <<
 			CS40L26_PLL_REFCLK_OPEN_LOOP_SHIFT);
 	if (ret)
 		dev_err(dev, "Failed to close PLL loop\n");
@@ -94,6 +95,9 @@
 	struct device *dev = cs40l26->dev;
 	int ret;
 
+	dev_info(dev, "%s: %s\n", __func__,
+			event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD");
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		mutex_lock(&cs40l26->lock);
@@ -104,7 +108,6 @@
 		ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_BCLK);
 		if (ret)
 			return ret;
-
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
 		ret = cs40l26_swap_ext_clk(codec, CS40L26_PLL_REFCLK_MCLK);
@@ -135,6 +138,14 @@
 	int ret;
 	u32 reg;
 
+	dev_dbg(dev, "%s: %s\n", __func__,
+			event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD");
+
+	if (codec->bypass_dsp) {
+		dev_err(dev, "Cannot apply A2H if DSP is bypassed\n");
+		return -EPERM;
+	}
+
 	ret = cl_dsp_get_reg(cs40l26->dsp, "A2HEN", CL_DSP_XM_UNPACKED_TYPE,
 			CS40L26_A2H_ALGO_ID, &reg);
 	if (ret)
@@ -163,9 +174,9 @@
 			if (ret)
 				return ret;
 		}
-		return regmap_write(cs40l26->regmap, reg, CS40L26_ENABLE);
+		return regmap_write(cs40l26->regmap, reg, 1);
 	case SND_SOC_DAPM_PRE_PMD:
-		return regmap_write(cs40l26->regmap, reg, CS40L26_DISABLE);
+		return regmap_write(cs40l26->regmap, reg, 0);
 	default:
 		dev_err(dev, "Invalid A2H event: %d\n", event);
 		return -EINVAL;
@@ -181,74 +192,82 @@
 	struct device *dev = cs40l26->dev;
 	u32 asp_en_mask = CS40L26_ASP_TX1_EN_MASK | CS40L26_ASP_TX2_EN_MASK |
 			CS40L26_ASP_RX1_EN_MASK | CS40L26_ASP_RX2_EN_MASK;
-	u32 asp_enables;
+	u32 asp_enables, reg;
+	u8 data_src;
 	int ret;
 
+	dev_info(dev, "%s: %s\n", __func__,
+			event == SND_SOC_DAPM_POST_PMU ? "PMU" : "PMD");
+
+	mutex_lock(&cs40l26->lock);
+
+	data_src = codec->bypass_dsp ? CS40L26_DATA_SRC_ASPRX1 :
+			CS40L26_DATA_SRC_DSP1TX1;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		ret = regmap_update_bits(regmap, CS40L26_DACPCM1_INPUT,
-			CS40L26_DATA_SRC_MASK, CS40L26_DATA_SRC_DSP1TX1);
+			CS40L26_DATA_SRC_MASK, data_src);
 		if (ret) {
 			dev_err(dev, "Failed to set DAC PCM input\n");
-			return ret;
+			goto err_mutex;
 		}
 
 		ret = regmap_update_bits(regmap, CS40L26_ASPTX1_INPUT,
-			CS40L26_DATA_SRC_MASK, CS40L26_DATA_SRC_DSP1TX1);
+			CS40L26_DATA_SRC_MASK, data_src);
 		if (ret) {
 			dev_err(dev, "Failed to set ASPTX1 input\n");
-			return ret;
+			goto err_mutex;
 		}
 
-		asp_enables = CS40L26_ENABLE | (CS40L26_ENABLE <<
-				CS40L26_ASP_TX2_EN_SHIFT) | (CS40L26_ENABLE <<
-				CS40L26_ASP_RX1_EN_SHIFT) | (CS40L26_ENABLE <<
-				CS40L26_ASP_RX2_EN_SHIFT);
+		asp_enables = 1 | (1 << CS40L26_ASP_TX2_EN_SHIFT)
+				| (1 << CS40L26_ASP_RX1_EN_SHIFT)
+				| (1 << CS40L26_ASP_RX2_EN_SHIFT);
 
 		ret = regmap_update_bits(regmap, CS40L26_ASP_ENABLES1,
 				asp_en_mask, asp_enables);
 		if (ret) {
 			dev_err(dev, "Failed to enable ASP channels\n");
-			return ret;
+			goto err_mutex;
 		}
 
-		ret = regmap_update_bits(regmap, CS40L26_VBST_CTL_2,
-				CS40L26_BST_CTL_SEL_MASK,
-				CS40L26_BST_CTL_SEL_CLASSH);
+		ret = cl_dsp_get_reg(cs40l26->dsp, "FLAGS",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+		if (ret)
+			goto err_mutex;
+
+		ret = regmap_write(regmap, reg, codec->svc_for_streaming_data);
 		if (ret) {
-			dev_err(dev, "Failed to select Class H BST CTRL\n");
-			return ret;
+			dev_err(dev, "Failed to specify SVC for streaming\n");
+			goto err_mutex;
 		}
 
-		ret = cs40l26_class_h_set(cs40l26, true);
+		ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
 		if (ret)
 			return ret;
 
-		ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
-				CS40L26_DSP_MBOX_CMD_START_I2S,
-				CS40L26_DSP_MBOX_RESET);
+		ret = regmap_write(regmap, reg, codec->invert_streaming_data);
+		if (ret) {
+			dev_err(dev, "Failed to specify SVC for streaming\n");
+			goto err_mutex;
+		}
+
+		queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work);
+
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
 		ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
 				CS40L26_DSP_MBOX_CMD_STOP_I2S,
 				CS40L26_DSP_MBOX_RESET);
 		if (ret)
-			return ret;
-
-		ret = cs40l26_class_h_set(cs40l26, false);
-		if (ret)
-			return ret;
-
-		asp_enables = CS40L26_DISABLE | (CS40L26_DISABLE <<
-				CS40L26_ASP_TX2_EN_SHIFT) | (CS40L26_DISABLE <<
-				CS40L26_ASP_RX1_EN_SHIFT) | (CS40L26_DISABLE <<
-				CS40L26_ASP_RX2_EN_SHIFT);
+			goto err_mutex;
 
 		ret = regmap_update_bits(regmap, CS40L26_ASP_ENABLES1,
-				asp_en_mask, asp_enables);
+				asp_en_mask, 0);
 		if (ret) {
 			dev_err(dev, "Failed to clear ASPTX1 input\n");
-			return ret;
+			goto err_mutex;
 		}
 
 		ret = regmap_update_bits(regmap, CS40L26_ASPTX1_INPUT,
@@ -261,9 +280,159 @@
 		ret = -EINVAL;
 	}
 
+err_mutex:
+	mutex_unlock(&cs40l26->lock);
+
 	return ret;
 }
 
+static int cs40l26_i2s_vmon_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+	int ret;
+	u32 val;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = regmap_read(cs40l26->regmap, CS40L26_SPKMON_VMON_DEC_OUT_DATA,
+			&val);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to get VMON Data for I2S\n");
+		goto pm_err;
+	}
+
+	if (val & CS40L26_VMON_OVFL_FLAG_MASK) {
+		dev_err(cs40l26->dev, "I2S VMON overflow detected\n");
+		ret = -EOVERFLOW;
+		goto pm_err;
+	}
+
+	ucontrol->value.enumerated.item[0] = val &
+			CS40L26_VMON_DEC_OUT_DATA_MASK;
+
+pm_err:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+
+static int cs40l26_bypass_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (codec->bypass_dsp)
+		ucontrol->value.enumerated.item[0] = 1;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+
+static int cs40l26_bypass_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (ucontrol->value.enumerated.item[0])
+		codec->bypass_dsp = true;
+	else
+		codec->bypass_dsp = false;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+
+static int cs40l26_svc_for_streaming_data_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (codec->svc_for_streaming_data)
+		ucontrol->value.enumerated.item[0] = 1;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+static int cs40l26_svc_for_streaming_data_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (ucontrol->value.enumerated.item[0])
+		codec->svc_for_streaming_data = true;
+	else
+		codec->svc_for_streaming_data = false;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+
+static int cs40l26_invert_streaming_data_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (codec->invert_streaming_data)
+		ucontrol->value.enumerated.item[0] = 1;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+
+static int cs40l26_invert_streaming_data_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec =
+	snd_soc_component_get_drvdata(snd_soc_kcontrol_component(kcontrol));
+	struct cs40l26_private *cs40l26 = codec->core;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (ucontrol->value.enumerated.item[0])
+		codec->invert_streaming_data = true;
+	else
+		codec->invert_streaming_data = false;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return 0;
+}
+
 static int cs40l26_tuning_get(struct snd_kcontrol *kcontrol,
 		struct snd_ctl_elem_value *ucontrol)
 {
@@ -302,7 +471,7 @@
 	return 0;
 }
 
-static int cs40l26_volume_get(struct snd_kcontrol *kcontrol,
+static int cs40l26_a2h_volume_get(struct snd_kcontrol *kcontrol,
 		struct snd_ctl_elem_value *ucontrol)
 {
 	struct cs40l26_codec *codec =
@@ -321,17 +490,9 @@
 	pm_runtime_get_sync(dev);
 
 	ret = regmap_read(regmap, reg, &val);
-	if (ret) {
+	if (ret)
 		dev_err(dev, "Failed to get VOLUMELEVEL\n");
-		goto err_pm;
-	}
 
-	if (val == CS40L26_VOLUME_MAX)
-		val = CS40L26_VOLUME_MAX_STEPS;
-	else
-		val /= CS40L26_VOLUME_STEP_SIZE;
-
-err_pm:
 	pm_runtime_mark_last_busy(dev);
 	pm_runtime_put_autosuspend(dev);
 
@@ -340,7 +501,7 @@
 	return ret;
 }
 
-static int cs40l26_volume_put(struct snd_kcontrol *kcontrol,
+static int cs40l26_a2h_volume_put(struct snd_kcontrol *kcontrol,
 		struct snd_ctl_elem_value *ucontrol)
 {
 	struct cs40l26_codec *codec =
@@ -356,11 +517,12 @@
 	if (ret)
 		return ret;
 
-	val = ucontrol->value.integer.value[0];
-	if (val == CS40L26_VOLUME_MAX_STEPS)
-		val = CS40L26_VOLUME_MAX;
+	if (ucontrol->value.integer.value[0] > CS40L26_A2H_VOLUME_MAX)
+		val = CS40L26_A2H_VOLUME_MAX;
+	else if (ucontrol->value.integer.value[0] < 0)
+		val = 0;
 	else
-		val *= CS40L26_VOLUME_STEP_SIZE;
+		val = ucontrol->value.integer.value[0];
 
 	pm_runtime_get_sync(dev);
 
@@ -374,11 +536,48 @@
 	return ret;
 }
 
+static int cs40l26_slots_get(
+	struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct cs40l26_codec *codec = snd_soc_component_get_drvdata(
+		snd_soc_kcontrol_component(kcontrol));
+
+	ucontrol->value.integer.value[0] = codec->tdm_slot[0];
+	ucontrol->value.integer.value[1] = codec->tdm_slot[1];
+
+	return 0;
+}
+
+static int cs40l26_slots_put(
+	struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct cs40l26_codec *codec = snd_soc_component_get_drvdata(
+		snd_soc_kcontrol_component(kcontrol));
+
+	codec->tdm_slot[0] = ucontrol->value.integer.value[0];
+	codec->tdm_slot[1] = ucontrol->value.integer.value[1];
+
+	return 0;
+}
+
 static const struct snd_kcontrol_new cs40l26_controls[] = {
 	SOC_SINGLE_EXT("A2H Tuning", 0, 0, CS40L26_A2H_MAX_TUNINGS, 0,
 			cs40l26_tuning_get, cs40l26_tuning_put),
-	SOC_SINGLE_EXT("A2H Volume", 0, 0, CS40L26_VOLUME_MAX_STEPS, 0,
-			cs40l26_volume_get, cs40l26_volume_put),
+	SOC_SINGLE_EXT("A2H Volume", 0, 0, CS40L26_A2H_VOLUME_MAX, 0,
+			cs40l26_a2h_volume_get, cs40l26_a2h_volume_put),
+	SOC_SINGLE_EXT("SVC for streaming data", 0, 0, 1, 0,
+			cs40l26_svc_for_streaming_data_get,
+			cs40l26_svc_for_streaming_data_put),
+	SOC_SINGLE_EXT("Invert streaming data", 0, 0, 1, 0,
+			cs40l26_invert_streaming_data_get,
+			cs40l26_invert_streaming_data_put),
+	SOC_SINGLE_EXT("I2S VMON", 0, 0, CS40L26_VMON_DEC_OUT_DATA_MAX, 0,
+			cs40l26_i2s_vmon_get, NULL),
+	SOC_SINGLE_EXT("DSP Bypass", 0, 0, 1, 0, cs40l26_bypass_get,
+			cs40l26_bypass_put),
+	SOC_DOUBLE_EXT("RX Slots", 0, 0, 1, 63, 0, cs40l26_slots_get,
+			cs40l26_slots_put),
 };
 
 static const char * const cs40l26_out_mux_texts[] = { "Off", "PCM", "A2H" };
@@ -495,11 +694,32 @@
 {
 	struct cs40l26_codec *codec =
 			snd_soc_component_get_drvdata(dai->component);
-	u8 asp_rx_wl, asp_rx_width;
-	int ret;
+	u8 asp_rx_wl, asp_rx_width, global_fs;
+	int ret, lrck;
 
 	pm_runtime_get_sync(codec->dev);
 
+	lrck = params_rate(params);
+	switch (lrck) {
+	case 48000:
+		global_fs = CS40L26_GLOBAL_FS_48K;
+		break;
+	case 96000:
+		global_fs = CS40L26_GLOBAL_FS_96K;
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err(codec->dev, "Invalid sample rate: %d Hz\n", lrck);
+		goto err_pm;
+	}
+
+	ret = regmap_update_bits(codec->regmap, CS40L26_GLOBAL_SAMPLE_RATE,
+			CS40L26_GLOBAL_FS_MASK, global_fs);
+	if (ret) {
+		dev_err(codec->dev, "Failed to write global fs\n");
+		goto err_pm;
+	}
+
 	asp_rx_wl = (u8) (params_width(params) & 0xFF);
 	ret = regmap_update_bits(codec->regmap, CS40L26_ASP_DATA_CONTROL5,
 			CS40L26_ASP_RX_WL_MASK, asp_rx_wl);
@@ -545,37 +765,8 @@
 	return ret;
 }
 
-static int cs40l26_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
-		unsigned int rx_mask, int slots, int slot_width)
-{
-	struct cs40l26_codec *codec =
-			snd_soc_component_get_drvdata(dai->component);
-
-	if (dai->id != 0) {
-		dev_err(codec->dev, "Invalid DAI ID: %d\n", dai->id);
-		return -EINVAL;
-	}
-
-	codec->tdm_width = slot_width;
-	codec->tdm_slots = slots;
-
-	/* Reset to slots 0,1 if TDM is being disabled, and catch the case
-	 * where both RX1 and RX2 would be set to slot 0 since that causes
-	 * hardware to flag an error
-	 */
-	if (!slots || rx_mask == 0x1)
-		rx_mask = 0x3;
-
-	codec->tdm_slot[0] = ffs(rx_mask) - 1;
-	rx_mask &= ~(1 << codec->tdm_slot[0]);
-	codec->tdm_slot[1] = ffs(rx_mask) - 1;
-
-	return 0;
-}
-
 static const struct snd_soc_dai_ops cs40l26_dai_ops = {
 	.set_fmt = cs40l26_set_dai_fmt,
-	.set_tdm_slot = cs40l26_set_tdm_slot,
 	.hw_params = cs40l26_pcm_hw_params,
 };
 
@@ -587,7 +778,7 @@
 			.stream_name = "ASP Playback",
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = SNDRV_PCM_RATE_48000,
+			.rates = CS40L26_RATES,
 			.formats = CS40L26_FORMATS,
 		},
 		.ops = &cs40l26_dai_ops,
@@ -607,10 +798,10 @@
 	snprintf(codec->bin_file, PAGE_SIZE, CS40L26_A2H_TUNING_FILE_NAME);
 
 	/* Default audio SCLK frequency */
-	codec->sysclk_rate = CS40L26_PLL_CLK_FRQ1;
+	codec->sysclk_rate = CS40L26_PLL_CLK_FRQ_1536000;
 
-	codec->tdm_slot[0] = 0;
-	codec->tdm_slot[1] = 1;
+	codec->tdm_slot[0] = 2;
+	codec->tdm_slot[1] = 3;
 
 	return 0;
 }
@@ -656,9 +847,11 @@
 
 static int cs40l26_codec_driver_remove(struct platform_device *pdev)
 {
-	pm_runtime_disable(&pdev->dev);
+	struct cs40l26_codec *codec = dev_get_drvdata(&pdev->dev);
 
-	snd_soc_unregister_component(&pdev->dev);
+	pm_runtime_disable(codec->dev);
+
+	snd_soc_unregister_component(codec->dev);
 
 	return 0;
 }
diff --git a/cs40l26/cs40l26-sysfs.c b/cs40l26/cs40l26-sysfs.c
index 47996d0..38dadf6 100644
--- a/cs40l26/cs40l26-sysfs.c
+++ b/cs40l26/cs40l26-sysfs.c
@@ -62,7 +62,7 @@
 	pm_runtime_get_sync(cs40l26->dev);
 
 	ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_HEARTBEAT",
-			CL_DSP_XM_UNPACKED_TYPE, CS40L26_FW_ID, &reg);
+			CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
 	if (ret)
 		return ret;
 
@@ -179,12 +179,282 @@
 }
 static DEVICE_ATTR(vibe_state, 0660, cs40l26_vibe_state_show, NULL);
 
+static ssize_t cs40l26_pseq_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	struct list_head *op_head = &cs40l26->pseq_op_head;
+	u32 base = cs40l26->pseq_base;
+	int i, count = 0;
+	struct cs40l26_pseq_op *pseq_op;
+
+	mutex_lock(&cs40l26->lock);
+
+	list_for_each_entry_reverse(pseq_op, op_head, list) {
+		dev_info(cs40l26->dev, "%d: Address: 0x%08X, Size: %d words\n",
+			count + 1, base + pseq_op->offset, pseq_op->size);
+
+		for (i = 0; i < pseq_op->size; i++)
+			dev_info(cs40l26->dev, "0x%08X\n",
+					*(pseq_op->words + i));
+
+		count++;
+	}
+
+	mutex_unlock(&cs40l26->lock);
+
+	if (count != cs40l26->pseq_num_ops) {
+		dev_err(cs40l26->dev, "Malformed Power on seq.\n");
+		return -EINVAL;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", cs40l26->pseq_num_ops);
+}
+static DEVICE_ATTR(power_on_seq, 0440, cs40l26_pseq_show, NULL);
+
+static ssize_t cs40l26_owt_free_space_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	u32 reg, words;
+	int ret;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_SIZE_XM",
+		CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+	if (ret)
+		goto err_pm;
+
+	ret = regmap_read(cs40l26->regmap, reg, &words);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to get remaining OWT space\n");
+		goto err_pm;
+	}
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", words * 3);
+
+err_pm:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(owt_free_space, 0440, cs40l26_owt_free_space_show, NULL);
+
+static ssize_t cs40l26_die_temp_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	struct regmap *regmap = cs40l26->regmap;
+	u16 die_temp;
+	int ret;
+	u32 val;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = regmap_read(regmap, CS40L26_GLOBAL_ENABLES, &val);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to read GLOBAL_EN status\n");
+		goto err_pm;
+	}
+
+	if (!(val & CS40L26_GLOBAL_EN_MASK)) {
+		dev_err(cs40l26->dev,
+			"Global enable must be set to get die temp.\n");
+		ret = -EPERM;
+		goto err_pm;
+	}
+
+	ret = regmap_read(regmap, CS40L26_ENABLES_AND_CODES_DIG, &val);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to get die temperature\n");
+		goto err_pm;
+	}
+
+	die_temp = (val & CS40L26_TEMP_RESULT_FILT_MASK) >>
+			CS40L26_TEMP_RESULT_FILT_SHIFT;
+
+	ret = snprintf(buf, PAGE_SIZE, "0x%03X\n", die_temp);
+
+err_pm:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(die_temp, 0440, cs40l26_die_temp_show, NULL);
+
+static ssize_t cs40l26_num_waves_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	u32 nwaves;
+	int ret;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+	if (ret)
+		goto err_pm;
+
+	ret = snprintf(buf, PAGE_SIZE, "%u\n", nwaves);
+
+err_pm:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(num_waves, 0440, cs40l26_num_waves_show, NULL);
+
+/* boost_disable_delay is in units of 125us, e.g. 8 ->  1ms */
+static ssize_t cs40l26_boost_disable_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	u32 reg, boost_disable_delay;
+	int ret;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+	if (ret)
+		goto err_pm;
+
+	ret = regmap_read(cs40l26->regmap, reg, &boost_disable_delay);
+	if (ret)
+		goto err_pm;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", boost_disable_delay);
+
+err_pm:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+
+static ssize_t cs40l26_boost_disable_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	u32 reg, boost_disable_delay;
+	int ret;
+
+	dev_dbg(cs40l26->dev, "%s: %s", __func__, buf);
+
+	ret = kstrtou32(buf, 10, &boost_disable_delay);
+
+	if (ret ||
+		boost_disable_delay < CS40L26_BOOST_DISABLE_DELAY_MIN ||
+		boost_disable_delay > CS40L26_BOOST_DISABLE_DELAY_MAX)
+		return -EINVAL;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "BOOST_DISABLE_DELAY",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+	if (ret)
+		goto err_pm;
+
+	ret = regmap_write(cs40l26->regmap, reg, boost_disable_delay);
+
+err_pm:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+static DEVICE_ATTR(boost_disable_delay, 0660, cs40l26_boost_disable_delay_show,
+		cs40l26_boost_disable_delay_store);
+
+static ssize_t cs40l26_f0_offset_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	unsigned int reg, val;
+	int ret;
+
+	pm_runtime_get_sync(cs40l26->dev);
+	mutex_lock(&cs40l26->lock);
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OFFSET",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID,
+			&reg);
+	if (ret)
+		goto err_mutex;
+
+
+	ret = regmap_read(cs40l26->regmap, reg, &val);
+	if (ret)
+		goto err_mutex;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
+
+err_mutex:
+	mutex_unlock(&cs40l26->lock);
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+
+static ssize_t cs40l26_f0_offset_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	unsigned int reg, val;
+	int ret;
+
+	ret = kstrtou32(buf, 10, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val > CS40L26_F0_OFFSET_MAX && val < CS40L26_F0_OFFSET_MIN)
+		return -EINVAL;
+
+	pm_runtime_get_sync(cs40l26->dev);
+	mutex_lock(&cs40l26->lock);
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "F0_OFFSET",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID,
+			&reg);
+	if (ret)
+		goto err_mutex;
+
+	ret = regmap_write(cs40l26->regmap, reg, val);
+	if (ret)
+		goto err_mutex;
+
+	ret = count;
+
+err_mutex:
+	mutex_unlock(&cs40l26->lock);
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(f0_offset, 0660, cs40l26_f0_offset_show,
+		cs40l26_f0_offset_store);
+
 static struct attribute *cs40l26_dev_attrs[] = {
+	&dev_attr_num_waves.attr,
+	&dev_attr_die_temp.attr,
+	&dev_attr_owt_free_space.attr,
+	&dev_attr_power_on_seq.attr,
 	&dev_attr_dsp_state.attr,
 	&dev_attr_halo_heartbeat.attr,
 	&dev_attr_fw_mode.attr,
 	&dev_attr_pm_timeout_ms.attr,
 	&dev_attr_vibe_state.attr,
+	&dev_attr_boost_disable_delay.attr,
+	&dev_attr_f0_offset.attr,
 	NULL,
 };
 
@@ -660,7 +930,7 @@
 
 	ret = cl_dsp_get_reg(cs40l26->dsp, "REDC_PLAYTIME_MS",
 			CL_DSP_XM_UNPACKED_TYPE,
-			CS40L26_FW_ID, &reg);
+			cs40l26->fw.id, &reg);
 	if (ret)
 		goto err_mutex;
 
@@ -867,6 +1137,49 @@
 	return snprintf(buf, PAGE_SIZE, "0x%06X\n", max_vbst);
 }
 
+static ssize_t cs40l26_calib_fw_load_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&cs40l26->lock);
+
+	if (cs40l26->fw.id == CS40L26_FW_ID)
+		ret = snprintf(buf, PAGE_SIZE, "%u\n", 0);
+	else if (cs40l26->fw.id == CS40L26_FW_CALIB_ID)
+		ret = snprintf(buf, PAGE_SIZE, "%u\n", 1);
+	else
+		ret = -EINVAL;
+
+	mutex_unlock(&cs40l26->lock);
+
+	return ret;
+}
+
+static ssize_t cs40l26_calib_fw_load_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct cs40l26_private *cs40l26 = dev_get_drvdata(dev);
+	int ret;
+	unsigned int variant;
+
+	ret = kstrtou32(buf, 10, &variant);
+	if (ret)
+		return ret;
+
+	if (variant == 0)
+		ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_ID);
+	else if (variant == 1)
+		ret = cs40l26_fw_swap(cs40l26, CS40L26_FW_CALIB_ID);
+	else
+		ret = -EINVAL;
+
+	return ret ? ret : count;
+}
+
+static DEVICE_ATTR(calib_fw_load, 0660, cs40l26_calib_fw_load_show,
+		cs40l26_calib_fw_load_store);
 static DEVICE_ATTR(max_vbst, 0440, cs40l26_max_vbst_show, NULL);
 static DEVICE_ATTR(max_bemf, 0440, cs40l26_max_bemf_show, NULL);
 static DEVICE_ATTR(logging_max_reset,
@@ -895,6 +1208,7 @@
 		0440, cs40l26_redc_cal_time_ms_show, NULL);
 
 static struct attribute *cs40l26_dev_attrs_cal[] = {
+	&dev_attr_calib_fw_load.attr,
 	&dev_attr_max_vbst.attr,
 	&dev_attr_max_bemf.attr,
 	&dev_attr_logging_max_reset.attr,
diff --git a/cs40l26/cs40l26-tables.c b/cs40l26/cs40l26-tables.c
index 9bd8056..bf115f4 100644
--- a/cs40l26/cs40l26-tables.c
+++ b/cs40l26/cs40l26-tables.c
@@ -41,29 +41,23 @@
 			cs40l26_sys_resume_noirq)
 };
 
-const char * const cs40l26_ram_coeff_files[3] = {
-	CS40L26_WT_FILE_NAME,
-	CS40L26_SVC_TUNING_FILE_NAME,
-	CS40L26_A2H_TUNING_FILE_NAME,
-};
-
-const u8 cs40l26_pseq_v2_op_sizes[CS40L26_PSEQ_V2_NUM_OPS][2] = {
-	{	CS40L26_PSEQ_V2_OP_WRITE_REG_FULL,
-		CS40L26_PSEQ_V2_OP_WRITE_REG_FULL_WORDS},
-	{	CS40L26_PSEQ_V2_OP_WRITE_FIELD,
-		CS40L26_PSEQ_V2_OP_WRITE_FIELD_WORDS},
-	{	CS40L26_PSEQ_V2_OP_WRITE_REG_ADDR8,
-		CS40L26_PSEQ_V2_OP_WRITE_REG_ADDR8_WORDS},
-	{	CS40L26_PSEQ_V2_OP_WRITE_REG_INCR,
-		CS40L26_PSEQ_V2_OP_WRITE_REG_INCR_WORDS},
-	{	CS40L26_PSEQ_V2_OP_WRITE_REG_L16,
-		CS40L26_PSEQ_V2_OP_WRITE_REG_L16_WORDS},
-	{	CS40L26_PSEQ_V2_OP_WRITE_REG_H16,
-		CS40L26_PSEQ_V2_OP_WRITE_REG_H16_WORDS},
-	{	CS40L26_PSEQ_V2_OP_DELAY,
-		CS40L26_PSEQ_V2_OP_DELAY_WORDS},
-	{	CS40L26_PSEQ_V2_OP_END,
-		CS40L26_PSEQ_V2_OP_END_WORDS},
+const u8 cs40l26_pseq_op_sizes[CS40L26_PSEQ_NUM_OPS][2] = {
+	{	CS40L26_PSEQ_OP_WRITE_REG_FULL,
+		CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS},
+	{	CS40L26_PSEQ_OP_WRITE_FIELD,
+		CS40L26_PSEQ_OP_WRITE_FIELD_WORDS},
+	{	CS40L26_PSEQ_OP_WRITE_REG_ADDR8,
+		CS40L26_PSEQ_OP_WRITE_REG_ADDR8_WORDS},
+	{	CS40L26_PSEQ_OP_WRITE_REG_INCR,
+		CS40L26_PSEQ_OP_WRITE_REG_INCR_WORDS},
+	{	CS40L26_PSEQ_OP_WRITE_REG_L16,
+		CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS},
+	{	CS40L26_PSEQ_OP_WRITE_REG_H16,
+		CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS},
+	{	CS40L26_PSEQ_OP_DELAY,
+		CS40L26_PSEQ_OP_DELAY_WORDS},
+	{	CS40L26_PSEQ_OP_END,
+		CS40L26_PSEQ_OP_END_WORDS},
 };
 
 struct regulator_bulk_data cs40l26_supplies[CS40L26_NUM_SUPPLIES] = {
@@ -72,10 +66,13 @@
 };
 
 const struct mfd_cell cs40l26_devs[CS40L26_NUM_MFD_DEVS] = {
-	{ .name = "cs40l26-codec" },
+	{
+		.name = "cs40l26-codec",
+		.of_compatible = "cs40l26-codec",
+	},
 };
 
-const u32 cs40l26_attn_q21_2_vals[101] = {
+const u32 cs40l26_attn_q21_2_vals[CS40L26_NUM_PCT_MAP_VALUES] = {
 	400, /* MUTE */
 	160, /* 1% */
 	136,
@@ -234,6 +231,8 @@
 	case CS40L26_OSC_TRIM:
 	case CS40L26_ERROR_RELEASE:
 	case CS40L26_PLL_OVERRIDE:
+	case CS40L26_CHIP_STATUS_1:
+	case CS40L26_CHIP_STATUS_2:
 	case CS40L26_BIAS_PTE_MODE_CONTROL:
 	case CS40L26_SCL_PAD_CONTROL:
 	case CS40L26_SDA_PAD_CONTROL:
@@ -338,6 +337,7 @@
 	case CS40L26_SPKMON_RATE_SEL:
 	case CS40L26_MONITOR_FILT:
 	case CS40L26_IMON_COMP:
+	case CS40L26_SPKMON_VMON_DEC_OUT_DATA:
 	case CS40L26_WARN_LIMIT_THRESHOLD:
 	case CS40L26_CONFIGURATION:
 	case CS40L26_STATUS:
@@ -534,6 +534,8 @@
 	case CS40L26_DSP1_AHBM_WINDOW6_CONTROL_1:
 	case CS40L26_DSP1_AHBM_WINDOW7_CONTROL_0:
 	case CS40L26_DSP1_AHBM_WINDOW7_CONTROL_1:
+	case CS40L26_DSP1_AHBM_WINDOW_DEBUG_0:
+	case CS40L26_DSP1_AHBM_WINDOW_DEBUG_1:
 	case CS40L26_DSP1_XMEM_UNPACKED24_0 ...
 CS40L26_DSP1_XMEM_UNPACKED24_8191:
 	case CS40L26_DSP1_XROM_UNPACKED24_0 ...
diff --git a/cs40l26/cs40l26.c b/cs40l26/cs40l26.c
index 4f10c6f..82bf060 100644
--- a/cs40l26/cs40l26.c
+++ b/cs40l26/cs40l26.c
@@ -13,6 +13,12 @@
 
 #include "cs40l26.h"
 
+static inline bool is_owt(unsigned int index)
+{
+	return index >= CS40L26_OWT_INDEX_START &&
+						index <= CS40L26_OWT_INDEX_END;
+}
+
 static int cs40l26_dsp_read(struct cs40l26_private *cs40l26, u32 reg, u32 *val)
 {
 	struct regmap *regmap = cs40l26->regmap;
@@ -81,9 +87,7 @@
 		if (ret)
 			return ret;
 
-		if (val != ack_val)
-			dev_dbg(dev, "Ack'ed value not equal to expected\n");
-		else
+		if (val == ack_val)
 			break;
 
 		usleep_range(CS40L26_DSP_TIMEOUT_US_MIN,
@@ -112,50 +116,19 @@
 }
 EXPORT_SYMBOL(cs40l26_ack_write);
 
-int cs40l26_class_h_set(struct cs40l26_private *cs40l26, bool class_h)
-{
-	int ret;
-
-	ret = regmap_update_bits(cs40l26->regmap, CS40L26_BLOCK_ENABLES2,
-			CS40L26_CLASS_H_EN_MASK, class_h <<
-			CS40L26_CLASS_H_EN_SHIFT);
-	if (ret)
-		dev_err(cs40l26->dev, "Failed to update CLASS H tracking\n");
-
-	return ret;
-}
-EXPORT_SYMBOL(cs40l26_class_h_set);
-
 int cs40l26_dsp_state_get(struct cs40l26_private *cs40l26, u8 *state)
 {
-	u32 algo_id, reg, dsp_state;
-	int ret;
+	u32 reg, dsp_state;
+	int ret = 0;
 
-	if (cs40l26->fw_loaded) {
-		if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-			algo_id = CS40L26_PM_ALGO_ID;
-		else
-			algo_id = CS40L26_PM_ROM_ALGO_ID;
-
+	if (cs40l26->fw_loaded)
 		ret = cl_dsp_get_reg(cs40l26->dsp, "PM_CUR_STATE",
-				CL_DSP_XM_UNPACKED_TYPE, algo_id, &reg);
-		if (ret)
-			return ret;
-	} else {
-		switch (cs40l26->revid) {
-		case CS40L26_REVID_A0:
-			reg = CS40L26_A0_PM_CUR_STATE_STATIC_REG;
-			break;
-		case CS40L26_REVID_A1:
-			reg = CS40L26_A1_PM_CUR_STATE_STATIC_REG;
-			break;
-		default:
-			dev_err(cs40l26->dev,
-					"Revid ID not supported: 0x%02X\n",
-					cs40l26->revid);
-			return -EINVAL;
-		}
-	}
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_PM_ALGO_ID, &reg);
+	else
+		reg = CS40L26_A1_PM_CUR_STATE_STATIC_REG;
+
+	if (ret)
+		return ret;
 
 	ret = cs40l26_dsp_read(cs40l26, reg, &dsp_state);
 	if (ret)
@@ -185,7 +158,7 @@
 {
 	u32 timeout_ticks = timeout_ms * CS40L26_PM_TICKS_MS_DIV;
 	struct regmap *regmap = cs40l26->regmap;
-	u32 lower_val, reg, algo_id;
+	u32 lower_val, reg;
 	u8 upper_val;
 	int ret;
 
@@ -194,13 +167,8 @@
 
 	lower_val = timeout_ticks & CS40L26_PM_TIMEOUT_TICKS_LOWER_MASK;
 
-	if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-		algo_id = CS40L26_PM_ALGO_ID;
-	else
-		algo_id = CS40L26_PM_ROM_ALGO_ID;
-
 	ret = cl_dsp_get_reg(cs40l26->dsp, "PM_TIMER_TIMEOUT_TICKS",
-			CL_DSP_XM_UNPACKED_TYPE, algo_id, &reg);
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_PM_ALGO_ID, &reg);
 	if (ret)
 		return ret;
 
@@ -217,33 +185,17 @@
 int cs40l26_pm_timeout_ms_get(struct cs40l26_private *cs40l26,
 		u32 *timeout_ms)
 {
-	u32 lower_val, upper_val, algo_id, reg;
-	int ret;
+	u32 lower_val, upper_val, reg;
+	int ret = 0;
 
-	if (cs40l26->fw_loaded) {
-		if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-			algo_id = CS40L26_PM_ALGO_ID;
-		else
-			algo_id = CS40L26_PM_ROM_ALGO_ID;
-
+	if (cs40l26->fw_loaded)
 		ret = cl_dsp_get_reg(cs40l26->dsp, "PM_TIMER_TIMEOUT_TICKS",
-				CL_DSP_XM_UNPACKED_TYPE, algo_id, &reg);
-		if (ret)
-			return ret;
-	} else {
-		switch (cs40l26->revid) {
-		case CS40L26_REVID_A0:
-			reg = CS40L26_A0_PM_TIMEOUT_TICKS_STATIC_REG;
-			break;
-		case CS40L26_REVID_A1:
-			reg = CS40L26_A1_PM_TIMEOUT_TICKS_STATIC_REG;
-			break;
-		default:
-			dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-				cs40l26->revid);
-			return -EINVAL;
-		}
-	}
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_PM_ALGO_ID, &reg);
+	else
+		reg = CS40L26_A1_PM_TIMEOUT_TICKS_STATIC_REG;
+
+	if (ret)
+		return ret;
 
 	ret = regmap_read(cs40l26->regmap, reg +
 			CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET, &lower_val);
@@ -405,52 +357,56 @@
 
 static int cs40l26_dsp_pre_config(struct cs40l26_private *cs40l26)
 {
+	u32 halo_state, timeout_ms;
 	u8 dsp_state;
-	u32 halo_state, halo_state_reg;
-	int ret;
+	int ret, i;
 
-	switch (cs40l26->revid) {
-	case CS40L26_REVID_A0:
-		halo_state_reg = CS40L26_A0_DSP_HALO_STATE_REG;
-		break;
-	case CS40L26_REVID_A1:
-		halo_state_reg = CS40L26_A1_DSP_HALO_STATE_REG;
-		break;
-	default:
-		dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-			cs40l26->revid);
-		return -EINVAL;
-	}
-
-	ret = regmap_read(cs40l26->regmap, halo_state_reg,
-			&halo_state);
+	ret = cs40l26_pm_state_transition(cs40l26,
+			CS40L26_PM_STATE_PREVENT_HIBERNATE);
 	if (ret)
 		return ret;
 
+	ret = regmap_read(cs40l26->regmap, CS40L26_A1_DSP_HALO_STATE_REG,
+			&halo_state);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to get HALO state\n");
+		return ret;
+	}
+
 	if (halo_state != CS40L26_DSP_HALO_STATE_RUN) {
 		dev_err(cs40l26->dev, "DSP not Ready: HALO_STATE: %08X\n",
 				halo_state);
 		return -EINVAL;
 	}
 
-	ret = cs40l26_pm_state_transition(cs40l26,
-			CS40L26_PM_STATE_PREVENT_HIBERNATE);
+
+	ret = cs40l26_pm_timeout_ms_get(cs40l26, &timeout_ms);
 	if (ret)
 		return ret;
 
-	ret = cs40l26_dsp_state_get(cs40l26, &dsp_state);
-	if (ret)
-		return ret;
+	for (i = 0; i < 10; i++) {
+		ret = cs40l26_dsp_state_get(cs40l26, &dsp_state);
+		if (ret)
+			return ret;
 
-	if (dsp_state != CS40L26_DSP_STATE_SHUTDOWN &&
-			dsp_state != CS40L26_DSP_STATE_STANDBY) {
-		dev_err(cs40l26->dev, "DSP core not safe to kill\n");
+		if (dsp_state != CS40L26_DSP_STATE_SHUTDOWN &&
+				dsp_state != CS40L26_DSP_STATE_STANDBY)
+			dev_warn(cs40l26->dev, "DSP core not safe to kill\n");
+		else
+			break;
+
+		usleep_range(CS40L26_MS_TO_US(timeout_ms),
+			CS40L26_MS_TO_US(timeout_ms) + 100);
+	}
+
+	if (i == 10) {
+		dev_err(cs40l26->dev, "DSP Core could not be shut down\n");
 		return -EINVAL;
 	}
 
 	/* errata write fixing indeterminent PLL lock time */
 	ret = regmap_update_bits(cs40l26->regmap, CS40L26_PLL_REFCLK_DETECT_0,
-			CS40L26_PLL_REFCLK_DET_EN_MASK, CS40L26_DISABLE);
+			CS40L26_PLL_REFCLK_DET_EN_MASK, 0);
 	if (ret) {
 		dev_err(cs40l26->dev, "Failed to disable PLL refclk detect\n");
 		return ret;
@@ -588,6 +544,12 @@
 				return -EINVAL;
 			}
 			break;
+		case CS40L26_DSP_MBOX_LE_EST_START:
+			dev_dbg(dev, "LE_EST_START\n");
+			break;
+		case CS40L26_DSP_MBOX_LE_EST_DONE:
+			dev_dbg(dev, "LE_EST_DONE\n");
+			break;
 		case CS40L26_DSP_MBOX_SYS_ACK:
 			dev_err(dev, "Mbox buffer value (0x%X) not supported\n",
 					val);
@@ -602,25 +564,24 @@
 	return 0;
 }
 
+void cs40l26_asp_worker(struct work_struct *work)
+{
+	struct cs40l26_private *cs40l26 =
+			container_of(work, struct cs40l26_private, asp_work);
+
+	cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
+			CS40L26_DSP_MBOX_CMD_START_I2S, CS40L26_DSP_MBOX_RESET);
+}
+EXPORT_SYMBOL(cs40l26_asp_worker);
+
 void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26,
 		enum cs40l26_vibe_state new_state)
 {
 	if (cs40l26->vibe_state == new_state)
 		return;
 
-	if (new_state == CS40L26_VIBE_STATE_STOPPED && cs40l26->asp_enable) {
-		/* Re-enable audio stream */
-		cs40l26_class_h_set(cs40l26, true);
-		if (!cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
-				CS40L26_DSP_MBOX_CMD_START_I2S,
-				CS40L26_DSP_MBOX_RESET)) {
-			cs40l26->vibe_state = CS40L26_VIBE_STATE_ASP;
-			return;
-		} else if (new_state == CS40L26_VIBE_STATE_HAPTIC &&
-				cs40l26->vibe_state == CS40L26_VIBE_STATE_ASP) {
-			cs40l26_class_h_set(cs40l26, false);
-		}
-	}
+	if (new_state == CS40L26_VIBE_STATE_STOPPED && cs40l26->asp_enable)
+		queue_work(cs40l26->asp_workqueue, &cs40l26->asp_work);
 
 	cs40l26->vibe_state = new_state;
 #if !IS_ENABLED(CONFIG_INPUT_CS40L26_ATTR_UNDER_BUS)
@@ -855,8 +816,7 @@
 				CS40L26_WKSRC_STS_SHIFT);
 
 		ret = cl_dsp_get_reg(cs40l26->dsp, "LAST_WAKESRC_CTL",
-				CL_DSP_XM_UNPACKED_TYPE,
-				CS40L26_FW_ID, &reg);
+				CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
 		if (ret)
 			goto err;
 
@@ -1120,7 +1080,19 @@
 		return IRQ_NONE;
 	}
 
-	pm_runtime_get_sync(dev);
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0) {
+		pm_runtime_set_active(dev);
+
+		dev_err(dev, "PM Runtime Resume failed, interrupts missed\n");
+
+		cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_1,
+				CS40L26_IRQ_EINT1_ALL_MASK);
+		cs40l26_dsp_write(cs40l26, CS40L26_IRQ1_EINT_2,
+				CS40L26_IRQ_EINT2_ALL_MASK);
+
+		goto err;
+	}
 
 	ret = regmap_read(regmap, CS40L26_IRQ1_EINT_1, &eint);
 	if (ret) {
@@ -1200,173 +1172,50 @@
 	return (irq1_count + irq2_count) ? IRQ_HANDLED : IRQ_NONE;
 }
 
-static bool cs40l26_pseq_v1_addr_exists(struct cs40l26_private *cs40l26,
-							u16 addr, int *index)
-{
-	int i;
-
-	if (cs40l26->pseq_v1_len == 0)
-		return false;
-
-	for (i = 0; i < cs40l26->pseq_v1_len; i++) {
-		if (cs40l26->pseq_v1_table[i].addr == addr) {
-			*index = i;
-			return true;
-		}
-	}
-
-	*index = -1;
-
-	return false;
-}
-
-static int cs40l26_pseq_v1_write(struct cs40l26_private *cs40l26,
-		unsigned int pseq_v1_offset)
-{
-	struct regmap *regmap = cs40l26->regmap;
-	unsigned int len = cs40l26->pseq_v1_len;
-	struct device *dev = cs40l26->dev;
-	u32 val;
-	u16 addr;
-	int ret;
-
-	addr = cs40l26->pseq_v1_table[pseq_v1_offset].addr;
-	val = cs40l26->pseq_v1_table[pseq_v1_offset].val;
-
-	/* the "upper half" first 24-bit word of the sequence pair is written
-	 * to the write sequencer as: [23-16] addr{15-0},
-	 * [15-0] val{31-24} with bits [24-31] acting as a buffer
-	 */
-	ret = regmap_write(regmap, cs40l26->pseq_base +
-			(pseq_v1_offset * CS40L26_PSEQ_V1_STRIDE),
-			(addr << CS40L26_PSEQ_V1_ADDR_SHIFT) |
-			((val & ~CS40L26_PSEQ_V1_VAL_MASK)
-			>> CS40L26_PSEQ_V1_VAL_SHIFT));
-	if (ret) {
-		dev_err(dev, "Failed to write power on seq. (upper half)\n");
-		return ret;
-	}
-
-	/* the "lower half" of the address-value pair is written to the write
-	 * sequencer as: [23-0] data{23-0} with bits [24-31] acting as a buffer
-	 */
-	ret = regmap_write(regmap, cs40l26->pseq_base + CL_DSP_BYTES_PER_WORD
-			+ (pseq_v1_offset * CS40L26_PSEQ_V1_STRIDE),
-			val & CS40L26_PSEQ_V1_VAL_MASK);
-	if (ret) {
-		dev_err(dev, "Failed to write power on seq. (lower half)\n");
-		return ret;
-	}
-
-	/* end of sequence must be marked by list terminator */
-	ret = regmap_write(regmap, cs40l26->pseq_base +
-			(len * CS40L26_PSEQ_V1_STRIDE),
-			CS40L26_PSEQ_V1_LIST_TERM);
-	if (ret)
-		dev_err(dev, "Failed to write power on seq. terminator\n");
-
-	return ret;
-}
-
-static int cs40l26_pseq_v1_add_pair(struct cs40l26_private *cs40l26, u16 addr,
-		u32 val, bool replace)
-{
-	unsigned int len = cs40l26->pseq_v1_len;
-	struct device *dev = cs40l26->dev;
-	unsigned int pseq_v1_offset, prev_val;
-	int ret, index;
-
-	if (len >= CS40L26_PSEQ_V1_MAX_ENTRIES) {
-		dev_err(dev, "Power on seq. exceeded max number of entries\n");
-		return -E2BIG;
-	}
-
-	if (cs40l26_pseq_v1_addr_exists(cs40l26, addr, &index) && replace) {
-		prev_val = cs40l26->pseq_v1_table[index].val;
-		cs40l26->pseq_v1_table[index].val = val;
-		pseq_v1_offset = index;
-	} else {
-		cs40l26->pseq_v1_table[len].addr = addr;
-		cs40l26->pseq_v1_table[len].val = val;
-		cs40l26->pseq_v1_len++;
-		pseq_v1_offset = len;
-	}
-
-	ret = cs40l26_pseq_v1_write(cs40l26, pseq_v1_offset);
-	if (ret) { /* If an error occurs during write, reset the sequence */
-		if (index < 0) { /* No previous value for this address */
-			cs40l26->pseq_v1_table[len].addr = 0;
-			cs40l26->pseq_v1_table[len].val = 0;
-			cs40l26->pseq_v1_len--;
-		} else {
-			cs40l26->pseq_v1_table[index].val = prev_val;
-		}
-	}
-
-	return ret;
-}
-
-int cs40l26_pseq_v1_multi_add_pair(struct cs40l26_private *cs40l26,
-		const struct reg_sequence *reg_seq, int num_regs, bool replace)
-{
-	int ret, i;
-
-	for (i = 0; i < num_regs; i++) {
-		ret = cs40l26_pseq_v1_add_pair(cs40l26, (u16) (reg_seq[i].reg &
-				CS40L26_PSEQ_V1_ADDR_MASK), reg_seq[i].def,
-				replace);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL(cs40l26_pseq_v1_multi_add_pair);
-
-static int cs40l26_pseq_v2_add_op(struct cs40l26_private *cs40l26,
+static int cs40l26_pseq_add_op(struct cs40l26_private *cs40l26,
 		int num_words, u32 *words)
 {
 	struct regmap *regmap = cs40l26->regmap;
 	struct device *dev = cs40l26->dev;
 	u32 offset_for_new_op, *op_words;
 	int ret;
-	struct cs40l26_pseq_v2_op *pseq_v2_op_end, *pseq_v2_op_new, *op;
+	struct cs40l26_pseq_op *pseq_op_end, *pseq_op_new, *op;
 
 	/* get location of the list terminator */
-	list_for_each_entry(pseq_v2_op_end, &cs40l26->pseq_v2_op_head, list) {
-		if (pseq_v2_op_end->operation == CS40L26_PSEQ_V2_OP_END)
+	list_for_each_entry(pseq_op_end, &cs40l26->pseq_op_head, list) {
+		if (pseq_op_end->operation == CS40L26_PSEQ_OP_END)
 			break;
 	}
 
-	if (pseq_v2_op_end->operation != CS40L26_PSEQ_V2_OP_END) {
+	if (pseq_op_end->operation != CS40L26_PSEQ_OP_END) {
 		dev_err(dev, "Failed to find END_OF_SCRIPT\n");
 		return -EINVAL;
 	}
 
-	offset_for_new_op = pseq_v2_op_end->offset;
+	offset_for_new_op = pseq_op_end->offset;
 
 	/* add new op to list */
 	op_words = kzalloc(num_words * CL_DSP_BYTES_PER_WORD, GFP_KERNEL);
 	if (!op_words)
 		return -ENOMEM;
 	memcpy(op_words, words, num_words * CL_DSP_BYTES_PER_WORD);
-	pseq_v2_op_new = devm_kzalloc(dev, sizeof(*pseq_v2_op_new), GFP_KERNEL);
-	if (!pseq_v2_op_new) {
+	pseq_op_new = devm_kzalloc(dev, sizeof(*pseq_op_new), GFP_KERNEL);
+	if (!pseq_op_new) {
 		ret = -ENOMEM;
 		goto err_free;
 	}
 
-	pseq_v2_op_new->size = num_words;
-	pseq_v2_op_new->offset = offset_for_new_op;
-	pseq_v2_op_new->words = op_words;
-	list_add(&pseq_v2_op_new->list, &cs40l26->pseq_v2_op_head);
+	pseq_op_new->size = num_words;
+	pseq_op_new->offset = offset_for_new_op;
+	pseq_op_new->words = op_words;
+	list_add(&pseq_op_new->list, &cs40l26->pseq_op_head);
 
-	cs40l26->pseq_v2_num_ops++;
+	cs40l26->pseq_num_ops++;
 
 	/* bump end of script offset to accomodate new operation */
-	pseq_v2_op_end->offset += num_words * CL_DSP_BYTES_PER_WORD;
+	pseq_op_end->offset += num_words * CL_DSP_BYTES_PER_WORD;
 
-	list_for_each_entry(op, &cs40l26->pseq_v2_op_head, list) {
+	list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
 		if (op->offset >= offset_for_new_op) {
 			ret = regmap_bulk_write(regmap, cs40l26->pseq_base +
 							op->offset,
@@ -1387,22 +1236,22 @@
 	return ret;
 }
 
-static int cs40l26_pseq_v2_add_write_reg_full(struct cs40l26_private *cs40l26,
+static int cs40l26_pseq_add_write_reg_full(struct cs40l26_private *cs40l26,
 		u32 addr, u32 data, bool update_if_op_already_in_seq)
 {
 	int ret;
-	struct cs40l26_pseq_v2_op *op;
-	u32 op_words[CS40L26_PSEQ_V2_OP_WRITE_REG_FULL_WORDS];
+	struct cs40l26_pseq_op *op;
+	u32 op_words[CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS];
 
-	op_words[0] = (CS40L26_PSEQ_V2_OP_WRITE_REG_FULL <<
-						CS40L26_PSEQ_V2_OP_SHIFT);
+	op_words[0] = (CS40L26_PSEQ_OP_WRITE_REG_FULL <<
+						CS40L26_PSEQ_OP_SHIFT);
 	op_words[0] |= addr >> 16;
 	op_words[1] = (addr & 0x0000FFFF) << 8;
 	op_words[1] |= (data & 0xFF000000) >> 24;
 	op_words[2] = (data & 0x00FFFFFF);
 
 	if (update_if_op_already_in_seq) {
-		list_for_each_entry(op, &cs40l26->pseq_v2_op_head, list) {
+		list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
 			/* check if op with same op and addr already exists */
 			if ((op->words[0] == op_words[0]) &&
 				((op->words[1] & 0xFFFFFF00) ==
@@ -1420,21 +1269,21 @@
 	}
 
 	/* if no matching op is found or !update, add the op */
-	ret = cs40l26_pseq_v2_add_op(cs40l26,
-				CS40L26_PSEQ_V2_OP_WRITE_REG_FULL_WORDS,
+	ret = cs40l26_pseq_add_op(cs40l26,
+				CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS,
 				op_words);
 
 	return ret;
 }
 
-int cs40l26_pseq_v2_multi_add_write_reg_full(struct cs40l26_private *cs40l26,
+int cs40l26_pseq_multi_add_write_reg_full(struct cs40l26_private *cs40l26,
 		const struct reg_sequence *reg_seq, int num_regs,
 		bool update_if_op_already_in_seq)
 {
 	int ret, i;
 
 	for (i = 0; i < num_regs; i++) {
-		ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26,
+		ret = cs40l26_pseq_add_write_reg_full(cs40l26,
 						reg_seq[i].reg, reg_seq[i].def,
 						update_if_op_already_in_seq);
 		if (ret)
@@ -1443,94 +1292,77 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(cs40l26_pseq_v2_multi_add_write_reg_full);
+EXPORT_SYMBOL(cs40l26_pseq_multi_add_write_reg_full);
 
-static int cs40l26_pseq_v1_init(struct cs40l26_private *cs40l26)
+/* 24-bit address, 16-bit data */
+static int cs40l26_pseq_add_write_reg_h16(struct cs40l26_private *cs40l26,
+		u32 addr, u16 data, bool update_if_op_already_in_seq)
 {
+	int ret;
 	struct device *dev = cs40l26->dev;
-	int ret, i, index = 0;
-	u8 upper_val = 0;
-	u16 addr = 0;
-	u32 val, word, algo_id;
+	struct cs40l26_pseq_op *op;
+	u32 op_words[CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS];
 
-	if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-		algo_id = CS40L26_PM_ALGO_ID;
-	else
-		algo_id = CS40L26_PM_ROM_ALGO_ID;
+	if (addr & 0xFF000000) {
+		dev_err(dev, "invalid address for pseq write_reg_h16\n");
+		return -EINVAL;
+	}
 
-	ret = cl_dsp_get_reg(cs40l26->dsp, "POWER_ON_SEQUENCE",
-			CL_DSP_XM_UNPACKED_TYPE, algo_id, &cs40l26->pseq_base);
-	if (ret)
-		return ret;
+	op_words[0] = (CS40L26_PSEQ_OP_WRITE_REG_H16 <<
+						CS40L26_PSEQ_OP_SHIFT);
+	op_words[0] |= (addr & 0x00FFFF00) >> 8;
+	op_words[1] = (addr & 0x000000FF) << 16;
+	op_words[1] |= data;
 
-	for (i = 0; i < CS40L26_PSEQ_V1_MAX_WRITES; i++) {
-		ret = regmap_read(cs40l26->regmap,
-				cs40l26->pseq_base +
-				(i * CL_DSP_BYTES_PER_WORD), &word);
-		if (ret) {
-			dev_err(dev, "Failed to read from power on seq.\n");
-			return ret;
-		}
-
-		if ((word & CS40L26_PSEQ_V1_LIST_TERM_MASK) ==
-				CS40L26_PSEQ_V1_LIST_TERM)
-			break;
-
-		if (i % CS40L26_PSEQ_V1_PAIR_NUM_WORDS) { /* lower half */
-			index = i / CS40L26_PSEQ_V1_PAIR_NUM_WORDS;
-			val = (upper_val << CS40L26_PSEQ_V1_VAL_SHIFT) |
-					(word & CS40L26_PSEQ_V1_VAL_MASK);
-
-			cs40l26->pseq_v1_table[index].addr = addr;
-			cs40l26->pseq_v1_table[index].val = val;
-		} else { /* upper half */
-			addr = (word & CS40L26_PSEQ_V1_ADDR_WORD_MASK) >>
-					CS40L26_PSEQ_V1_ADDR_SHIFT;
-
-			upper_val = word & CS40L26_PSEQ_V1_VAL_WORD_UPPER_MASK;
+	if (update_if_op_already_in_seq) {
+		list_for_each_entry(op, &cs40l26->pseq_op_head, list) {
+			/* check if op with same op and addr already exists */
+			if ((op->words[0] == op_words[0]) &&
+				((op->words[1] & 0x00FF0000) ==
+				(op_words[1] & 0x00FF0000))) {
+				/* update data in the existing op and return */
+				ret = regmap_bulk_write(cs40l26->regmap,
+						cs40l26->pseq_base + op->offset,
+						op_words, op->size);
+				if (ret)
+					dev_err(cs40l26->dev,
+						"Failed to update op\n");
+				return ret;
+			}
 		}
 	}
 
-	if (i >= CS40L26_PSEQ_V1_MAX_WRITES) {
-		dev_err(dev, "Original sequence exceeds max # of entries\n");
-		return -E2BIG;
-	}
+	/* if no matching op is found or !update, add the op */
+	ret = cs40l26_pseq_add_op(cs40l26,
+				CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS,
+				op_words);
 
-	ret = regmap_write(cs40l26->regmap, cs40l26->pseq_base +
-			(i * CL_DSP_BYTES_PER_WORD), CS40L26_PSEQ_V1_LIST_TERM);
-	if (ret)
-		return ret;
-
-	cs40l26->pseq_v1_len = index + 1;
-	return 0;
+	return ret;
 }
 
-static int cs40l26_pseq_v2_init(struct cs40l26_private *cs40l26)
+static int cs40l26_pseq_init(struct cs40l26_private *cs40l26)
 {
 	struct device *dev = cs40l26->dev;
+	u32 words[CS40L26_PSEQ_MAX_WORDS], *op_words;
+	struct cs40l26_pseq_op *pseq_op;
 	int ret, i, j, num_words, read_size;
 	u8 operation;
-	u32 words[CS40L26_PSEQ_V2_MAX_WORDS], algo_id, *op_words;
-	struct cs40l26_pseq_v2_op *pseq_v2_op;
 
-	INIT_LIST_HEAD(&cs40l26->pseq_v2_op_head);
-	cs40l26->pseq_v2_num_ops = 0;
-
-	if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-		algo_id = CS40L26_PM_ALGO_ID;
-	else
-		algo_id = CS40L26_PM_ROM_ALGO_ID;
+	INIT_LIST_HEAD(&cs40l26->pseq_op_head);
+	cs40l26->pseq_num_ops = 0;
 
 	ret = cl_dsp_get_reg(cs40l26->dsp, "POWER_ON_SEQUENCE",
-			CL_DSP_XM_UNPACKED_TYPE, algo_id, &cs40l26->pseq_base);
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_PM_ALGO_ID,
+			&cs40l26->pseq_base);
 	if (ret)
 		return ret;
 
 	/* read pseq memory space */
 	i = 0;
-	while (i < CS40L26_PSEQ_V2_MAX_WORDS) {
+	while (i < CS40L26_PSEQ_MAX_WORDS) {
 		read_size = min(CS40L26_MAX_I2C_READ_SIZE_BYTES,
-			CS40L26_PSEQ_V2_MAX_WORDS - i);
+			CS40L26_PSEQ_MAX_WORDS - i);
+
 		ret = regmap_bulk_read(cs40l26->regmap,
 			cs40l26->pseq_base + i * CL_DSP_BYTES_PER_WORD,
 			words + i,
@@ -1543,20 +1375,20 @@
 	}
 
 	i = 0;
-	while (i < CS40L26_PSEQ_V2_MAX_WORDS) {
-		operation = (words[i] & CS40L26_PSEQ_V2_OP_MASK) >>
-			CS40L26_PSEQ_V2_OP_SHIFT;
+	while (i < CS40L26_PSEQ_MAX_WORDS) {
+		operation = (words[i] & CS40L26_PSEQ_OP_MASK) >>
+			CS40L26_PSEQ_OP_SHIFT;
 
 		/* get num words for given operation */
-		for (j = 0; j < CS40L26_PSEQ_V2_NUM_OPS; j++) {
-			if (cs40l26_pseq_v2_op_sizes[j][0] == operation) {
-				num_words = cs40l26_pseq_v2_op_sizes[j][1];
+		for (j = 0; j < CS40L26_PSEQ_NUM_OPS; j++) {
+			if (cs40l26_pseq_op_sizes[j][0] == operation) {
+				num_words = cs40l26_pseq_op_sizes[j][1];
 				break;
 			}
 		}
 
-		if (j == CS40L26_PSEQ_V2_NUM_OPS) {
-			dev_err(dev, "Failed to determine pseq_v2 op size\n");
+		if (j == CS40L26_PSEQ_NUM_OPS) {
+			dev_err(dev, "Failed to determine pseq op size\n");
 			return -EINVAL;
 		}
 
@@ -1566,37 +1398,28 @@
 			return -ENOMEM;
 		memcpy(op_words, &words[i], num_words * CL_DSP_BYTES_PER_WORD);
 
-		pseq_v2_op = devm_kzalloc(dev, sizeof(*pseq_v2_op), GFP_KERNEL);
-		if (!pseq_v2_op) {
+		pseq_op = devm_kzalloc(dev, sizeof(*pseq_op), GFP_KERNEL);
+		if (!pseq_op) {
 			ret = -ENOMEM;
 			goto err_free;
 		}
 
-		pseq_v2_op->size = num_words;
-		pseq_v2_op->offset = i * CL_DSP_BYTES_PER_WORD;
-		pseq_v2_op->operation = operation;
-		pseq_v2_op->words = op_words;
-		list_add(&pseq_v2_op->list, &cs40l26->pseq_v2_op_head);
+		pseq_op->size = num_words;
+		pseq_op->offset = i * CL_DSP_BYTES_PER_WORD;
+		pseq_op->operation = operation;
+		pseq_op->words = op_words;
+		list_add(&pseq_op->list, &cs40l26->pseq_op_head);
 
-		cs40l26->pseq_v2_num_ops++;
+		cs40l26->pseq_num_ops++;
 		i += num_words;
 
-		if (operation == CS40L26_PSEQ_V2_OP_END)
+		if (operation == CS40L26_PSEQ_OP_END)
 			break;
 
 	}
 
-	dev_dbg(dev, "PSEQ_V2 num ops: %d\n", cs40l26->pseq_v2_num_ops);
-	dev_dbg(dev, "offset\tsize\twords\n");
-	list_for_each_entry(pseq_v2_op, &cs40l26->pseq_v2_op_head, list) {
-		dev_dbg(dev, "0x%04X\t%d", pseq_v2_op->offset,
-							pseq_v2_op->size);
-		for (j = 0; j < pseq_v2_op->size; j++)
-			dev_dbg(dev, "0x%08X", *(pseq_v2_op->words + j));
-	}
-
-	if (operation != CS40L26_PSEQ_V2_OP_END) {
-		dev_err(dev, "PSEQ_V2 END_OF_SCRIPT not found\n");
+	if (operation != CS40L26_PSEQ_OP_END) {
+		dev_err(dev, "PSEQ END_OF_SCRIPT not found\n");
 		return -E2BIG;
 	}
 
@@ -1608,80 +1431,154 @@
 	return ret;
 }
 
-static int cs40l26_pseq_init(struct cs40l26_private *cs40l26)
+static int cs40l26_update_reg_defaults_via_pseq(struct cs40l26_private *cs40l26)
 {
+	struct device *dev = cs40l26->dev;
 	int ret;
 
-	switch (cs40l26->revid) {
-	case CS40L26_REVID_A0:
-		ret = cs40l26_pseq_v1_init(cs40l26);
-		break;
-	case CS40L26_REVID_A1:
-		ret = cs40l26_pseq_v2_init(cs40l26);
-		break;
-	default:
-		dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-			cs40l26->revid);
+	/* set SPK_DEFAULT_HIZ to 1 */
+	ret = cs40l26_pseq_add_write_reg_h16(cs40l26,
+			CS40L26_TST_DAC_MSM_CONFIG,
+			CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_H16,
+			true);
+	if (ret)
+		dev_err(dev, "Failed to sequence register default updates\n");
+
+	return ret;
+}
+
+static int cs40l26_buzzgen_set(struct cs40l26_private *cs40l26, u16 freq,
+		u16 level, u16 duration, u8 offset)
+{
+	int ret;
+	unsigned int base_reg, offset_reg;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "BUZZ_EFFECTS1_BUZZ_FREQ",
+		CL_DSP_XM_UNPACKED_TYPE, CS40L26_BUZZGEN_ALGO_ID, &base_reg);
+	if (ret)
+		return ret;
+
+	offset_reg = base_reg + (offset * 12);
+
+	ret = regmap_write(cs40l26->regmap, offset_reg, freq);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to write BUZZGEN frequency\n");
+		return ret;
+	}
+
+	ret = regmap_write(cs40l26->regmap, offset_reg + 4,
+			CS40L26_BUZZGEN_LEVEL_DEFAULT);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to write BUZZGEN level\n");
+		return ret;
+	}
+
+	ret = regmap_write(cs40l26->regmap, offset_reg + 8, duration / 4);
+	if (ret)
+		dev_err(cs40l26->dev, "Failed to write BUZZGEN duration\n");
+
+	return ret;
+}
+
+static int cs40l26_gpio_index_set(struct cs40l26_private *cs40l26,
+		struct ff_effect *effect)
+{
+	u16 button = effect->trigger.button;
+	u8 bank = (button & CS40L26_BTN_BANK_MASK) >> CS40L26_BTN_BANK_SHIFT;
+	u8 edge = (button & CS40L26_BTN_EDGE_MASK) >> CS40L26_BTN_EDGE_SHIFT;
+	u8 gpio = (button & CS40L26_BTN_NUM_MASK) >> CS40L26_BTN_NUM_SHIFT;
+	u8 index = button & CS40L26_BTN_INDEX_MASK;
+	u8 buzz = button & CS40L26_BTN_BUZZ_MASK;
+	u16 write_val, freq;
+	u8 offset;
+	u32 reg;
+	int ret;
+
+	if (gpio != 1) {
+		dev_err(cs40l26->dev, "GPIO%u not supported on 0x%02X\n", gpio,
+				cs40l26->revid);
 		return -EINVAL;
 	}
 
+	if (edge > 1) {
+		dev_err(cs40l26->dev, "Invalid GPI edge %u\n", edge);
+		return -EINVAL;
+	}
+
+	offset = ((edge ^ 1)) * 4;
+	reg = cs40l26->event_map_base + offset;
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	if (buzz) {
+		freq = CS40L26_MS_TO_HZ(effect->u.periodic.period);
+
+		ret = cs40l26_buzzgen_set(cs40l26, freq,
+				CS40L26_BUZZGEN_LEVEL_DEFAULT,
+				effect->replay.length, 0);
+		if (ret)
+			goto pm_err;
+
+		write_val = 1 << CS40L26_BTN_BUZZ_SHIFT;
+	} else {
+		write_val = index | (bank << CS40L26_BTN_BANK_SHIFT);
+	}
+
+	ret = regmap_update_bits(cs40l26->regmap, reg,
+			CS40L26_EVENT_MAP_INDEX_MASK, write_val);
 	if (ret)
-		dev_err(cs40l26->dev, "Failed to init pseq\n");
+		dev_err(cs40l26->dev, "Failed to update event map\n");
+
+pm_err:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
 
 	return ret;
 }
 
+static struct cs40l26_owt *cs40l26_owt_find(struct cs40l26_private *cs40l26,
+		int id)
+{
+	struct cs40l26_owt *owt;
+
+	if (list_empty(&cs40l26->owt_head)) {
+		dev_err(cs40l26->dev, "OWT list is empty\n");
+		return NULL;
+	}
+
+	list_for_each_entry(owt, &cs40l26->owt_head, list) {
+		if (owt->effect_id == id)
+			break;
+	}
+
+	if (owt->effect_id != id) {
+		dev_err(cs40l26->dev, "OWT effect with ID %d not found\n", id);
+		return NULL;
+	}
+
+	return owt;
+}
+
 static void cs40l26_set_gain_worker(struct work_struct *work)
 {
 	struct cs40l26_private *cs40l26 =
 		container_of(work, struct cs40l26_private, set_gain_work);
-	u16 amp_vol_pcm;
-	u32 reg, val;
+	u32 reg;
 	int ret;
 
 	pm_runtime_get_sync(cs40l26->dev);
 	mutex_lock(&cs40l26->lock);
 
-	switch (cs40l26->revid) {
-	case CS40L26_REVID_A0:
-		amp_vol_pcm = CS40L26_AMP_VOL_PCM_MAX & cs40l26->gain_pct;
-
-		ret = regmap_update_bits(cs40l26->regmap, CS40L26_AMP_CTRL,
-				CS40L26_AMP_CTRL_VOL_PCM_MASK, amp_vol_pcm <<
-				CS40L26_AMP_CTRL_VOL_PCM_SHIFT);
-		if (ret) {
-			dev_err(cs40l26->dev, "Failed to update digtal gain\n");
-			goto err_mutex;
-		}
-
-		ret = regmap_read(cs40l26->regmap, CS40L26_AMP_CTRL, &val);
-		if (ret) {
-			dev_err(cs40l26->dev, "Failed to read AMP control\n");
-			goto err_mutex;
-		}
-
-		ret = cs40l26_pseq_v1_add_pair(cs40l26,
-						CS40L26_AMP_CTRL, val, true);
-		if (ret)
-			dev_err(cs40l26->dev, "Failed to set gain in pseq\n");
-		break;
-	case CS40L26_REVID_A1:
-		val = cs40l26_attn_q21_2_vals[cs40l26->gain_pct];
-
-		/* Write Q21.2 value to SOURCE_ATTENUATION */
-		ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_ATTENUATION",
+	/* Write Q21.2 value to SOURCE_ATTENUATION */
+	ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_ATTENUATION",
 			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
-		if (ret)
-			goto err_mutex;
+	if (ret)
+		goto err_mutex;
 
-		ret = regmap_write(cs40l26->regmap, reg, val);
-		if (ret)
-			dev_err(cs40l26->dev, "Failed to set attenuation\n");
-		break;
-	default:
-		dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-			cs40l26->revid);
-	}
+	ret = regmap_write(cs40l26->regmap, reg,
+			cs40l26_attn_q21_2_vals[cs40l26->gain_pct]);
+	if (ret)
+		dev_err(cs40l26->dev, "Failed to set attenuation\n");
 
 err_mutex:
 	mutex_unlock(&cs40l26->lock);
@@ -1694,32 +1591,62 @@
 	struct cs40l26_private *cs40l26 = container_of(work,
 			struct cs40l26_private, vibe_start_work);
 	struct device *dev = cs40l26->dev;
+	u32 index = 0;
 	int ret = 0;
+	struct cs40l26_owt *owt;
 	unsigned int reg, freq;
-	u32 index = 0, buzz_id;
+	bool invert;
 	u16 duration;
 
-	if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM)
-		buzz_id = CS40L26_BUZZGEN_ALGO_ID;
-	else
-		buzz_id = CS40L26_BUZZGEN_ROM_ALGO_ID;
+	dev_dbg(dev, "%s\n", __func__);
 
 	if (cs40l26->effect->u.periodic.waveform == FF_CUSTOM)
 		index = cs40l26->trigger_indices[cs40l26->effect->id];
 
-	if (index >= CS40L26_OWT_INDEX_START && index <= CS40L26_OWT_INDEX_END)
-		duration = CS40L26_SAMPS_TO_MS((cs40l26->owt_wlength &
-				CS40L26_WT_TYPE10_WAVELEN_MAX));
-	else
-		duration = cs40l26->effect->replay.length;
+	if (is_owt(index)) {
+		owt = cs40l26_owt_find(cs40l26, cs40l26->effect->id);
+		if (owt == NULL)
+			return;
+	}
+
+	duration = cs40l26->effect->replay.length;
 
 	pm_runtime_get_sync(dev);
 	mutex_lock(&cs40l26->lock);
 
-	if (duration > 0) /* Effect duration is known */
-		hrtimer_start(&cs40l26->vibe_timer,
-			ktime_set(CS40L26_MS_TO_SECS(duration),
-			CS40L26_MS_TO_NS(duration % 1000)), HRTIMER_MODE_REL);
+	ret = cl_dsp_get_reg(cs40l26->dsp, "TIMEOUT_MS",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+	if (ret)
+		goto err_mutex;
+
+	ret = regmap_write(cs40l26->regmap, reg, duration);
+	if (ret) {
+		dev_err(dev, "Failed to set TIMEOUT_MS\n");
+		goto err_mutex;
+	}
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "SOURCE_INVERT",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EXT_ALGO_ID, &reg);
+	if (ret)
+		goto err_mutex;
+
+	switch (cs40l26->effect->direction) {
+	case 0x0000:
+		invert = false;
+		break;
+	case 0x8000:
+		invert = true;
+		break;
+	default:
+		dev_err(dev, "Invalid ff_effect direction: 0x%X\n",
+			cs40l26->effect->direction);
+		ret = -EINVAL;
+		goto err_mutex;
+	}
+
+	ret = regmap_write(cs40l26->regmap, reg, invert);
+	if (ret)
+		goto err_mutex;
 
 	cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_HAPTIC);
 
@@ -1731,26 +1658,10 @@
 			goto err_mutex;
 		break;
 	case FF_SINE:
-		ret = cl_dsp_get_reg(cs40l26->dsp, "BUZZ_EFFECTS2_BUZZ_FREQ",
-				CL_DSP_XM_UNPACKED_TYPE, buzz_id, &reg);
-		if (ret)
-			goto err_mutex;
-
 		freq = CS40L26_MS_TO_HZ(cs40l26->effect->u.periodic.period);
 
-		ret = regmap_write(cs40l26->regmap, reg, freq);
-		if (ret)
-			goto err_mutex;
-
-		ret = regmap_write(cs40l26->regmap, reg +
-				CS40L26_BUZZGEN_LEVEL_OFFSET,
-				CS40L26_BUZZGEN_LEVEL_DEFAULT);
-		if (ret)
-			goto err_mutex;
-
-		ret = regmap_write(cs40l26->regmap, reg +
-				CS40L26_BUZZGEN_DURATION_OFFSET, duration /
-				CS40L26_BUZZGEN_DURATION_DIV_STEP);
+		ret = cs40l26_buzzgen_set(cs40l26, freq,
+				CS40L26_BUZZGEN_LEVEL_DEFAULT, duration, 1);
 		if (ret)
 			goto err_mutex;
 
@@ -1780,42 +1691,21 @@
 {
 	struct cs40l26_private *cs40l26 = container_of(work,
 			struct cs40l26_private, vibe_stop_work);
-	unsigned int reg;
-	u32 algo_id;
 	int ret;
 
+	dev_dbg(cs40l26->dev, "%s\n", __func__);
+
 	pm_runtime_get_sync(cs40l26->dev);
 	mutex_lock(&cs40l26->lock);
 
-	if (cs40l26->effect->u.periodic.waveform == FF_SINE) {
-		ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
+	if (cs40l26->vibe_state != CS40L26_VIBE_STATE_HAPTIC)
+		goto mutex_exit;
+
+	ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
 				CS40L26_STOP_PLAYBACK, CS40L26_DSP_MBOX_RESET);
-		if (ret) {
-			dev_err(cs40l26->dev, "Failed to stop playback\n");
-			goto mutex_exit;
-		}
-	} else {
-		if (cs40l26->fw_mode == CS40L26_FW_MODE_ROM)
-			algo_id = CS40L26_VIBEGEN_ROM_ALGO_ID;
-		else
-			algo_id = CS40L26_VIBEGEN_ALGO_ID;
-
-		ret = cl_dsp_get_reg(cs40l26->dsp, "END_PLAYBACK",
-				CL_DSP_XM_UNPACKED_TYPE, algo_id, &reg);
-		if (ret)
-			goto mutex_exit;
-
-		ret = regmap_write(cs40l26->regmap, reg, 1);
-		if (ret) {
-			dev_err(cs40l26->dev, "Failed to end VIBE playback\n");
-			goto mutex_exit;
-		}
-
-		ret = regmap_write(cs40l26->regmap, reg, 0);
-		if (ret) {
-			dev_err(cs40l26->dev, "Failed to reset END_PLAYBACK\n");
-			goto mutex_exit;
-		}
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to stop playback\n");
+		goto mutex_exit;
 	}
 
 	cs40l26_vibe_state_set(cs40l26, CS40L26_VIBE_STATE_STOPPED);
@@ -1826,20 +1716,15 @@
 	pm_runtime_put_autosuspend(cs40l26->dev);
 }
 
-static enum hrtimer_restart cs40l26_vibe_timer(struct hrtimer *timer)
-{
-	struct cs40l26_private *cs40l26 =
-		container_of(timer, struct cs40l26_private, vibe_timer);
-
-	queue_work(cs40l26->vibe_workqueue, &cs40l26->vibe_stop_work);
-
-	return HRTIMER_NORESTART;
-}
-
 static void cs40l26_set_gain(struct input_dev *dev, u16 gain)
 {
 	struct cs40l26_private *cs40l26 = input_get_drvdata(dev);
 
+	if (gain < 0 || gain >= CS40L26_NUM_PCT_MAP_VALUES) {
+		dev_err(cs40l26->dev, "Gain value %u out of bounds\n", gain);
+		return;
+	}
+
 	cs40l26->gain_pct = gain;
 
 	queue_work(cs40l26->vibe_workqueue, &cs40l26->set_gain_work);
@@ -1851,6 +1736,9 @@
 	struct cs40l26_private *cs40l26 = input_get_drvdata(dev);
 	struct ff_effect *effect;
 
+	dev_dbg(cs40l26->dev, "%s: effect ID = %d, val = %d\n", __func__,
+			effect_id, val);
+
 	effect = &dev->ff->effects[effect_id];
 	if (!effect) {
 		dev_err(cs40l26->dev, "No such effect to playback\n");
@@ -1859,16 +1747,44 @@
 
 	cs40l26->effect = effect;
 
-	if (val > 0) {
+	if (val > 0)
 		queue_work(cs40l26->vibe_workqueue, &cs40l26->vibe_start_work);
-	} else {
-		hrtimer_cancel(&cs40l26->vibe_timer);
+	else
 		queue_work(cs40l26->vibe_workqueue, &cs40l26->vibe_stop_work);
-	}
 
 	return 0;
 }
 
+int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves)
+{
+	int ret;
+	u32 reg, nwaves, nowt;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "NUM_OF_WAVES",
+			CL_DSP_XM_UNPACKED_TYPE,
+			CS40L26_VIBEGEN_ALGO_ID, &reg);
+	if (ret)
+		return ret;
+
+	ret = cs40l26_dsp_read(cs40l26, reg, &nwaves);
+	if (ret)
+		return ret;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_NUM_OF_WAVES_XM",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+	if (ret)
+		return ret;
+
+	ret = cs40l26_dsp_read(cs40l26, reg, &nowt);
+	if (ret)
+		return ret;
+
+	*num_waves = nwaves + nowt;
+
+	return 0;
+}
+EXPORT_SYMBOL(cs40l26_get_num_waves);
+
 static int cs40l26_owt_get_wlength(struct cs40l26_private *cs40l26, u8 index)
 {
 	struct device *dev = cs40l26->dev;
@@ -1917,8 +1833,10 @@
 }
 
 static int cs40l26_owt_calculate_wlength(struct cs40l26_private *cs40l26,
-		struct cl_dsp_memchunk *ch)
+		s16 *data, u32 data_size, u32 *owt_wlen)
 {
+	u32 data_size_bytes = data_size * 2;
+	struct cl_dsp_memchunk ch;
 	u32 total_len = 0, section_len = 0, loop_len = 0;
 	bool in_loop = false;
 	struct cs40l26_owt_section *sections;
@@ -1926,9 +1844,11 @@
 	u8 nsections, global_rep;
 	u32 dlen, wlen;
 
-	cl_dsp_memchunk_read(ch, 8); /* Skip padding */
-	nsections = cl_dsp_memchunk_read(ch, 8);
-	global_rep = cl_dsp_memchunk_read(ch, 8);
+	ch = cl_dsp_memchunk_create((void *) data, data_size_bytes);
+
+	cl_dsp_memchunk_read(&ch, 8); /* Skip padding */
+	nsections = cl_dsp_memchunk_read(&ch, 8);
+	global_rep = cl_dsp_memchunk_read(&ch, 8);
 
 	if (nsections < 1) {
 		dev_err(cs40l26->dev, "Not enough sections for composite\n");
@@ -1942,7 +1862,7 @@
 		return -ENOMEM;
 	}
 
-	cs40l26_owt_get_section_info(cs40l26, ch, sections, nsections);
+	cs40l26_owt_get_section_info(cs40l26, &ch, sections, nsections);
 
 	for (i = 0; i < nsections; i++) {
 		wlen_whole = cs40l26_owt_get_wlength(cs40l26,
@@ -1995,7 +1915,7 @@
 		section_len = 0;
 	}
 
-	cs40l26->owt_wlength = (total_len * (global_rep + 1)) |
+	*owt_wlen = (total_len * (global_rep + 1)) |
 			CS40L26_WT_TYPE10_WAVELEN_CALCULATED;
 
 err_free:
@@ -2004,65 +1924,70 @@
 	return ret;
 }
 
-static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
-		u32 data_size)
+static int cs40l26_owt_add(struct cs40l26_private *cs40l26, u32 wlen,
+		int effect_id, u32 index)
 {
-	bool pwle = (data[0] == 0x0000) ? false : true;
-	u32 data_size_bytes = data_size * 2;
-	struct device *dev = cs40l26->dev;
-	struct cl_dsp *dsp = cs40l26->dsp;
-	u32 full_data_size, header_size = CL_DSP_OWT_HEADER_ENTRY_SIZE;
-	unsigned int write_reg, reg, wt_offset, wt_size, wt_base;
-	struct cl_dsp_memchunk header_ch, data_ch;
-	u8 *full_data, *header;
-	int ret = 0;
+	struct cs40l26_owt *owt_new;
 
-	data_ch = cl_dsp_memchunk_create((void *) data, data_size_bytes);
-
-	if (pwle) {
-		header_size += CS40L26_WT_TERM_SIZE;
-
-		cs40l26->owt_wlength = cl_dsp_memchunk_read(&data_ch, 24);
-	} else {
-		header_size += CS40L26_WT_WLEN_TERM_SIZE;
-
-		ret = cs40l26_owt_calculate_wlength(cs40l26, &data_ch);
-		if (ret)
-			return ret;
-	}
-	full_data_size = header_size + data_size_bytes;
-
-	header = kcalloc(header_size, sizeof(u8), GFP_KERNEL);
-	if (!header)
+	owt_new = kzalloc(sizeof(*owt_new), GFP_KERNEL);
+	if (!owt_new)
 		return -ENOMEM;
 
-	header_ch = cl_dsp_memchunk_create((void *) header, header_size);
-	/* Header */
-	cl_dsp_memchunk_write(&header_ch, 16,
-			CS40L26_WT_HEADER_DEFAULT_FLAGS);
+	owt_new->effect_id = effect_id;
+	owt_new->wlength = wlen;
+	owt_new->trigger_index = index;
+	list_add(&owt_new->list, &cs40l26->owt_head);
+
+	cs40l26->num_owt_effects++;
+
+	return 0;
+}
+
+static int cs40l26_owt_upload(struct cs40l26_private *cs40l26, s16 *data,
+		u32 data_size, u32 *wlength)
+{
+	bool pwle = (data[0] == 0x0000) ? false : true;
+	u32 data_size_bytes = (data_size * 2) + (pwle ? 0 : 4);
+	struct device *dev = cs40l26->dev;
+	struct cl_dsp *dsp = cs40l26->dsp;
+	unsigned int write_reg, reg, wt_offset, wt_size_words, wt_base;
+	u32 owt_wlength, full_data_size_bytes = 12 + data_size_bytes;
+	struct cl_dsp_memchunk data_ch;
+	u8 *full_data;
+	int ret = 0;
+
+	full_data = kcalloc(full_data_size_bytes, sizeof(u8), GFP_KERNEL);
+	if (!full_data)
+		return -ENOMEM;
+
+	data_ch = cl_dsp_memchunk_create((void *) full_data,
+			full_data_size_bytes);
+	cl_dsp_memchunk_write(&data_ch, 16, CS40L26_WT_HEADER_DEFAULT_FLAGS);
 
 	if (pwle)
-		cl_dsp_memchunk_write(&header_ch, 8, WT_TYPE_V6_PWLE);
+		cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_PWLE);
 	else
-		cl_dsp_memchunk_write(&header_ch, 8, WT_TYPE_V6_COMPOSITE);
+		cl_dsp_memchunk_write(&data_ch, 8, WT_TYPE_V6_COMPOSITE);
 
-	cl_dsp_memchunk_write(&header_ch, 24, CS40L26_WT_HEADER_OFFSET);
-	cl_dsp_memchunk_write(&header_ch, 24, full_data_size /
-					CL_DSP_BYTES_PER_WORD);
+	cl_dsp_memchunk_write(&data_ch, 24, CS40L26_WT_HEADER_OFFSET);
+	cl_dsp_memchunk_write(&data_ch, 24, data_size_bytes /
+							CL_DSP_BYTES_PER_WORD);
 
-	cl_dsp_memchunk_write(&header_ch, 24, CS40L26_WT_HEADER_TERM);
+	if (!pwle) { /* Wlength is included in PWLE raw data */
+		ret = cs40l26_owt_calculate_wlength(cs40l26, data, data_size,
+				&owt_wlength);
+		if (ret)
+			goto err_free;
 
-	if (!pwle) /* Wlength is included in PWLE raw data */
-		cl_dsp_memchunk_write(&header_ch, 24, cs40l26->owt_wlength);
-
-	full_data = kcalloc(full_data_size, sizeof(u8), GFP_KERNEL);
-	if (!full_data) {
-		ret = -ENOMEM;
-		goto err_free;
+		cl_dsp_memchunk_write(&data_ch, 24, owt_wlength);
 	}
 
-	memcpy(full_data, header, header_ch.bytes);
-	memcpy(full_data + header_ch.bytes, data, data_size_bytes);
+	memcpy(full_data + data_ch.bytes, data, data_size_bytes);
+
+	if (pwle)
+		owt_wlength = (full_data[data_ch.bytes + 1] << 16) |
+				(full_data[data_ch.bytes + 2] << 8) |
+				full_data[data_ch.bytes + 3];
 
 	pm_runtime_get_sync(dev);
 
@@ -2082,13 +2007,13 @@
 	if (ret)
 		goto err_pm;
 
-	ret = regmap_read(cs40l26->regmap, reg, &wt_size);
+	ret = regmap_read(cs40l26->regmap, reg, &wt_size_words);
 	if (ret) {
 		dev_err(dev, "Failed to get available WT size\n");
 		goto err_pm;
 	}
 
-	if (wt_size < full_data_size) {
+	if ((wt_size_words * 3) < full_data_size_bytes) {
 		dev_err(dev, "No space for OWT waveform\n");
 		ret = -ENOSPC;
 		goto err_pm;
@@ -2102,7 +2027,7 @@
 	write_reg = wt_base + (wt_offset * 4);
 
 	ret = cl_dsp_raw_write(cs40l26->dsp, write_reg, full_data,
-			full_data_size, CL_DSP_MAX_WLEN);
+			full_data_size_bytes, CL_DSP_MAX_WLEN);
 	if (ret) {
 		dev_err(dev, "Failed to sync OWT\n");
 		goto err_pm;
@@ -2114,14 +2039,15 @@
 		goto err_pm;
 
 	dev_dbg(dev, "Successfully wrote waveform (%u bytes) to 0x%08X\n",
-			full_data_size, write_reg);
+			full_data_size_bytes, write_reg);
+
+	*wlength = owt_wlength;
 
 err_pm:
 	pm_runtime_mark_last_busy(dev);
 	pm_runtime_put_autosuspend(dev);
 
 err_free:
-	kfree(header);
 	kfree(full_data);
 
 	return ret;
@@ -2134,12 +2060,11 @@
 	struct device *cdev = cs40l26->dev;
 	s16 *raw_custom_data = NULL;
 	int ret = 0;
-	u32 trigger_index, min_index, max_index;
+	u32 trigger_index, min_index, max_index, nwaves, owt_wlen;
 	u16 index, bank;
 
 	if (effect->type != FF_PERIODIC) {
-		dev_err(cdev, "Effect type 0x%X not supported\n",
-				effect->type);
+		dev_err(cdev, "Effect type 0x%X not supported\n", effect->type);
 		return -EINVAL;
 	}
 
@@ -2167,15 +2092,8 @@
 		}
 
 		if (effect->u.periodic.custom_len > CS40L26_CUSTOM_DATA_SIZE) {
-			ret = cs40l26_ack_write(cs40l26,
-					CS40L26_DSP_VIRTUAL1_MBOX_1,
-					CS40L26_DSP_MBOX_CMD_OWT_RESET,
-					CS40L26_DSP_MBOX_RESET);
-			if (ret)
-				goto out_free;
-
 			ret = cs40l26_owt_upload(cs40l26, raw_custom_data,
-					effect->u.periodic.custom_len);
+				effect->u.periodic.custom_len, &owt_wlen);
 			if (ret)
 				goto out_free;
 
@@ -2217,6 +2135,15 @@
 			goto out_free;
 		}
 
+		if (is_owt(trigger_index)) {
+			ret = cs40l26_owt_add(cs40l26, owt_wlen, effect->id,
+					trigger_index);
+			if (ret)
+				goto out_free;
+		}
+
+		dev_dbg(cdev, "%s: ID = %u, index = 0x%08X\n",
+				__func__, effect->id, trigger_index);
 		break;
 	case FF_SINE:
 		if (effect->u.periodic.period) {
@@ -2240,6 +2167,19 @@
 		return -EINVAL;
 	}
 
+	if (effect->trigger.button) {
+		ret = cs40l26_gpio_index_set(cs40l26, effect);
+		if (ret)
+			goto out_free;
+	}
+
+	ret = cs40l26_get_num_waves(cs40l26, &nwaves);
+	if (ret)
+		goto out_free;
+
+	dev_dbg(cdev, "Total number of waveforms = %u\n", nwaves);
+
+
 out_free:
 	kfree(raw_custom_data);
 
@@ -2254,6 +2194,50 @@
 };
 #endif
 
+static int cs40l26_erase_effect(struct input_dev *dev, int effect_id)
+{
+	struct cs40l26_private *cs40l26 = input_get_drvdata(dev);
+	u32 index = cs40l26->trigger_indices[effect_id];
+	u32 cmd = CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE;
+	struct cs40l26_owt *owt, *owt_tmp;
+	int ret;
+
+	if (!is_owt(index))
+		return 0;
+
+	/* Update indices for OWT waveforms uploaded after erased effect */
+	list_for_each_entry(owt_tmp, &cs40l26->owt_head, list) {
+		if (owt_tmp->trigger_index > index) {
+			owt_tmp->trigger_index--;
+			cs40l26->trigger_indices[owt_tmp->effect_id]--;
+		}
+	}
+
+	owt = cs40l26_owt_find(cs40l26, effect_id);
+	if (owt == NULL)
+		return -ENOMEM;
+
+	cmd |= (owt->trigger_index & 0xFF);
+
+	list_del(&owt->list);
+	kfree(owt);
+
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1, cmd,
+			CS40L26_DSP_MBOX_RESET);
+	if (ret)
+		goto pm_err;
+
+	cs40l26->num_owt_effects--;
+
+pm_err:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	return ret;
+}
+
 static int cs40l26_input_init(struct cs40l26_private *cs40l26)
 {
 	int ret;
@@ -2288,6 +2272,7 @@
 	cs40l26->input->ff->upload = cs40l26_upload_effect;
 	cs40l26->input->ff->playback = cs40l26_playback_effect;
 	cs40l26->input->ff->set_gain = cs40l26_set_gain;
+	cs40l26->input->ff->erase = cs40l26_erase_effect;
 
 	ret = input_register_device(cs40l26->input);
 	if (ret) {
@@ -2295,9 +2280,6 @@
 		return ret;
 	}
 
-	hrtimer_init(&cs40l26->vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	cs40l26->vibe_timer.function = cs40l26_vibe_timer;
-
 #if !IS_ENABLED(CONFIG_INPUT_CS40L26_ATTR_UNDER_BUS)
 	ret = sysfs_create_group(&cs40l26->input->dev.kobj,
 			&cs40l26_dev_attr_group);
@@ -2353,12 +2335,9 @@
 	}
 
 	val &= CS40L26_REVID_MASK;
-	switch (val) {
-	case CS40L26_REVID_A0:
-	case CS40L26_REVID_A1:
+	if (val == CS40L26_REVID_A1) {
 		cs40l26->revid = val;
-		break;
-	default:
+	} else {
 		dev_err(dev, "Invalid device revision: 0x%02X\n", val);
 		return -EINVAL;
 	}
@@ -2369,35 +2348,60 @@
 	return 0;
 }
 
-static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26)
+static int cs40l26_cl_dsp_init(struct cs40l26_private *cs40l26, u32 id)
 {
-	int ret = 0;
+	int ret = 0, i;
+
+	if (cs40l26->dsp) {
+		ret = cl_dsp_destroy(cs40l26->dsp);
+		if (ret) {
+			dev_err(cs40l26->dev,
+				"Failed to destroy existing DSP structure\n");
+			return ret;
+		}
+		cs40l26->dsp = NULL;
+	}
 
 	cs40l26->dsp = cl_dsp_create(cs40l26->dev, cs40l26->regmap);
-	if (!cs40l26->dsp)
+	if (!cs40l26->dsp) {
+		dev_err(cs40l26->dev, "Failed to allocate space for DSP\n");
 		return -ENOMEM;
+	}
 
 	if (cs40l26->fw_mode == CS40L26_FW_MODE_ROM) {
+		cs40l26->fw.id = CS40L26_FW_ID;
 		cs40l26->fw.min_rev = CS40L26_FW_ROM_MIN_REV;
 		cs40l26->fw.num_coeff_files = 0;
-		cs40l26->fw.coeff_files = NULL;
 	} else {
-		if (cs40l26->revid == CS40L26_REVID_A1)
-			cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
-		else
-			cs40l26->fw.min_rev = CS40L26_FW_A0_RAM_MIN_REV;
+		cs40l26->fw.id = id;
 
-		cs40l26->fw.num_coeff_files =
-				ARRAY_SIZE(cs40l26_ram_coeff_files);
-		cs40l26->fw.coeff_files = cs40l26_ram_coeff_files;
+		if (id == CS40L26_FW_ID)
+			cs40l26->fw.min_rev = CS40L26_FW_A1_RAM_MIN_REV;
+		if (id == CS40L26_FW_CALIB_ID)
+			cs40l26->fw.min_rev = CS40L26_FW_CALIB_MIN_REV;
+
+		cs40l26->fw.num_coeff_files = CS40L26_TUNING_FILES_MAX;
+		cs40l26->fw.coeff_files = devm_kcalloc(cs40l26->dev,
+			CS40L26_TUNING_FILES_MAX, sizeof(char *), GFP_KERNEL);
+
+		for (i = 0; i < CS40L26_TUNING_FILES_MAX; i++)
+			cs40l26->fw.coeff_files[i] = devm_kzalloc(cs40l26->dev,
+				CS40L26_TUNING_FILE_NAME_MAX_LEN, GFP_KERNEL);
+
+		strncpy(cs40l26->fw.coeff_files[0], CS40L26_WT_FILE_NAME,
+				CS40L26_WT_FILE_NAME_LEN);
+		strncpy(cs40l26->fw.coeff_files[1],
+				CS40L26_A2H_TUNING_FILE_NAME,
+				CS40L26_A2H_TUNING_FILE_NAME_LEN);
+		strncpy(cs40l26->fw.coeff_files[2],
+				CS40L26_SVC_TUNING_FILE_NAME,
+				CS40L26_SVC_TUNING_FILE_NAME_LEN);
 
 		ret = cl_dsp_wavetable_create(cs40l26->dsp,
 				CS40L26_VIBEGEN_ALGO_ID, CS40L26_WT_NAME_XM,
-				CS40L26_WT_NAME_YM, "cs40l26.bin");
+				CS40L26_WT_NAME_YM, CS40L26_WT_FILE_NAME);
 	}
 
-	cs40l26->num_owt_effects = 0;
-
 	return ret;
 }
 
@@ -2434,6 +2438,13 @@
 static int cs40l26_gpio_config(struct cs40l26_private *cs40l26)
 {
 	u32 unmask_bits;
+	int ret;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "ENT_MAP_TABLE_EVENT_DATA_PACKED",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_EVENT_HANDLER_ALGO_ID,
+			&cs40l26->event_map_base);
+	if (ret)
+		return ret;
 
 	unmask_bits = BIT(CS40L26_IRQ1_GPIO1_RISE)
 			| BIT(CS40L26_IRQ1_GPIO1_FALL);
@@ -2474,21 +2485,8 @@
 		return ret;
 	}
 
-	switch (cs40l26->revid) {
-	case CS40L26_REVID_A0:
-		ret = cs40l26_pseq_v1_add_pair(cs40l26,
-			CS40L26_BLOCK_ENABLES2, val, CS40L26_PSEQ_V1_REPLACE);
-		break;
-	case CS40L26_REVID_A1:
-		ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26,
-			CS40L26_BLOCK_ENABLES2, val, true);
-		break;
-	default:
-		dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-			cs40l26->revid);
-		return -EINVAL;
-	}
-
+	ret = cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_BLOCK_ENABLES2,
+			val, true);
 	if (ret) {
 		dev_err(dev, "Failed to sequence brownout prevention\n");
 		return ret;
@@ -2592,22 +2590,8 @@
 			return ret;
 		}
 
-		switch (cs40l26->revid) {
-		case CS40L26_REVID_A0:
-			ret = cs40l26_pseq_v1_add_pair(cs40l26,
-				CS40L26_VBBR_CONFIG, val,
-				CS40L26_PSEQ_V1_REPLACE);
-			break;
-		case CS40L26_REVID_A1:
-			ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26,
-				CS40L26_VBBR_CONFIG, val,
-				true);
-			break;
-		default:
-			dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-				cs40l26->revid);
-			return -EINVAL;
-		}
+		ret = cs40l26_pseq_add_write_reg_full(cs40l26,
+				CS40L26_VBBR_CONFIG, val, true);
 		if (ret)
 			return ret;
 	}
@@ -2713,22 +2697,8 @@
 			return ret;
 		}
 
-		switch (cs40l26->revid) {
-		case CS40L26_REVID_A0:
-			ret = cs40l26_pseq_v1_add_pair(cs40l26,
-				CS40L26_VPBR_CONFIG, val,
-				CS40L26_PSEQ_V1_REPLACE);
-			break;
-		case CS40L26_REVID_A1:
-			ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26,
-				CS40L26_VPBR_CONFIG, val,
-				true);
-			break;
-		default:
-			dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-				cs40l26->revid);
-			return -EINVAL;
-		}
+		ret = cs40l26_pseq_add_write_reg_full(cs40l26,
+				CS40L26_VPBR_CONFIG, val, true);
 		if (ret)
 			return ret;
 	}
@@ -2736,21 +2706,6 @@
 	return 0;
 }
 
-static int cs40l26_get_num_waves(struct cs40l26_private *cs40l26,
-		u32 *num_waves)
-{
-	int ret;
-	u32 reg;
-
-	ret = cl_dsp_get_reg(cs40l26->dsp, "NUM_OF_WAVES",
-			CL_DSP_XM_UNPACKED_TYPE,
-			CS40L26_VIBEGEN_ALGO_ID, &reg);
-	if (ret)
-		return ret;
-
-	return regmap_read(cs40l26->regmap, reg, num_waves);
-}
-
 static int cs40l26_verify_fw(struct cs40l26_private *cs40l26)
 {
 	struct cs40l26_fw *fw = &cs40l26->fw;
@@ -2761,7 +2716,7 @@
 	if (ret)
 		return ret;
 
-	if (val != CS40L26_FW_ID) {
+	if (val != cs40l26->fw.id) {
 		dev_err(cs40l26->dev, "Invalid firmware ID: 0x%X\n", val);
 		return -EINVAL;
 	}
@@ -2808,22 +2763,8 @@
 		goto err_free;
 	}
 
-	switch (cs40l26->revid) {
-	case CS40L26_REVID_A0:
-		ret = cs40l26_pseq_v1_multi_add_pair(cs40l26, dsp1rx_config, 2,
-				CS40L26_PSEQ_V1_REPLACE);
-		break;
-	case CS40L26_REVID_A1:
-		ret = cs40l26_pseq_v2_multi_add_write_reg_full(cs40l26,
-				dsp1rx_config, 2, true);
-		break;
-	default:
-		dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-			cs40l26->revid);
-		ret = -EINVAL;
-		goto err_free;
-	}
-
+	ret = cs40l26_pseq_multi_add_write_reg_full(cs40l26, dsp1rx_config, 2,
+			true);
 	if (ret)
 		dev_err(cs40l26->dev, "Failed to add ASP config to pseq\n");
 
@@ -2854,25 +2795,67 @@
 			return ret;
 		}
 
-		switch (cs40l26->revid) {
-		case CS40L26_REVID_A0:
-			ret = cs40l26_pseq_v1_add_pair(cs40l26,
-						CS40L26_BST_DCM_CTL, val, true);
-			break;
-		case CS40L26_REVID_A1:
-			ret = cs40l26_pseq_v2_add_write_reg_full(cs40l26,
-						CS40L26_BST_DCM_CTL, val, true);
-			break;
-		default:
-			dev_err(cs40l26->dev, "Revid ID not supported: %02X\n",
-				cs40l26->revid);
-			ret = -EINVAL;
-		}
+		ret = cs40l26_pseq_add_write_reg_full(cs40l26,
+				CS40L26_BST_DCM_CTL, val, true);
 	}
 
 	return ret;
 }
 
+static int cs40l26_bst_ipk_config(struct cs40l26_private *cs40l26)
+{
+	u32 val, bst_ipk_ma = cs40l26->pdata.bst_ipk / 1000;
+	int ret;
+
+	if (bst_ipk_ma < CS40L26_BST_IPK_MILLIAMP_MIN ||
+			bst_ipk_ma > CS40L26_BST_IPK_MILLIAMP_MAX) {
+		val = CS40L26_BST_IPK_DEFAULT;
+		dev_dbg(cs40l26->dev, "Using default BST_IPK\n");
+	} else {
+		val = (bst_ipk_ma / 50) - 16;
+	}
+
+	ret = regmap_write(cs40l26->regmap, CS40L26_BST_IPK_CTL, val);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to update BST peak current\n");
+		return ret;
+	}
+
+	return cs40l26_pseq_add_write_reg_full(cs40l26, CS40L26_BST_IPK_CTL,
+			val, true);
+}
+
+static int cs40l26_owt_setup(struct cs40l26_private *cs40l26)
+{
+	u32 reg, offset, base;
+	int ret;
+
+	INIT_LIST_HEAD(&cs40l26->owt_head);
+	cs40l26->num_owt_effects = 0;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, CS40L26_WT_NAME_XM,
+		CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &base);
+	if (ret)
+		return ret;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "OWT_NEXT_XM",
+			CL_DSP_XM_UNPACKED_TYPE, CS40L26_VIBEGEN_ALGO_ID, &reg);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(cs40l26->regmap, reg, &offset);
+	if (ret) {
+		dev_err(cs40l26->dev, "Failed to get wavetable offset\n");
+		return ret;
+	}
+
+	ret = regmap_write(cs40l26->regmap, reg, 0xFFFFFF);
+	if (ret)
+		dev_err(cs40l26->dev, "Failed to write OWT terminator\n");
+
+	return ret;
+}
+
 static int cs40l26_dsp_config(struct cs40l26_private *cs40l26)
 {
 	struct regmap *regmap = cs40l26->regmap;
@@ -2883,84 +2866,89 @@
 
 	ret = cs40l26_verify_fw(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = regmap_update_bits(regmap, CS40L26_PWRMGT_CTL,
-			CS40L26_MEM_RDY_MASK,
-			CS40L26_ENABLE << CS40L26_MEM_RDY_SHIFT);
+			CS40L26_MEM_RDY_MASK, 1 << CS40L26_MEM_RDY_SHIFT);
 	if (ret) {
 		dev_err(dev, "Failed to set MEM_RDY to initialize RAM\n");
-		goto err_out;
+		return ret;
 	}
 
 	if (cs40l26->fw_mode == CS40L26_FW_MODE_RAM) {
 		ret = cl_dsp_get_reg(cs40l26->dsp, "CALL_RAM_INIT",
-				CL_DSP_XM_UNPACKED_TYPE,
-				CS40L26_FW_ID, &reg);
+				CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
 		if (ret)
-			goto err_out;
+			return ret;
 
-		ret = cs40l26_dsp_write(cs40l26, reg, CS40L26_ENABLE);
+		ret = cs40l26_dsp_write(cs40l26, reg, 1);
 		if (ret)
-			goto err_out;
+			return ret;
 	}
 
 	cs40l26->fw_loaded = true;
 
 	ret = cs40l26_dsp_start(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = cs40l26_pseq_init(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
+
+	ret = cs40l26_update_reg_defaults_via_pseq(cs40l26);
+	if (ret)
+		return ret;
 
 	ret = cs40l26_iseq_init(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = cs40l26_irq_update_mask(cs40l26, CS40L26_IRQ1_MASK_1,
 			BIT(CS40L26_IRQ1_VIRTUAL2_MBOX_WR), CS40L26_IRQ_UNMASK);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = cs40l26_wksrc_config(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = cs40l26_gpio_config(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = cs40l26_bst_dcm_config(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
+
+	ret = cs40l26_bst_ipk_config(cs40l26);
+	if (ret)
+		return ret;
 
 	ret = cs40l26_brownout_prevention_init(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	/* ensure firmware running */
 	ret = cl_dsp_get_reg(cs40l26->dsp, "HALO_STATE",
-			CL_DSP_XM_UNPACKED_TYPE, CS40L26_FW_ID,
-			&reg);
+			CL_DSP_XM_UNPACKED_TYPE, cs40l26->fw.id, &reg);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	ret = regmap_read(regmap, reg, &val);
 	if (ret) {
 		dev_err(dev, "Failed to read HALO_STATE\n");
-		goto err_out;
+		return ret;
 	}
 
 	if (val != CS40L26_DSP_HALO_STATE_RUN) {
 		dev_err(dev, "Firmware in unexpected state: 0x%X\n", val);
-		goto err_out;
+		return ret;
 	}
 
 	ret = cs40l26_pm_runtime_setup(cs40l26);
 	if (ret)
-		goto err_out;
+		return ret;
 
 	pm_runtime_get_sync(dev);
 
@@ -2980,54 +2968,253 @@
 		goto pm_err;
 
 	ret = cs40l26_get_num_waves(cs40l26, &cs40l26->num_waves);
-	if (!ret)
-		dev_info(dev, "%s loaded with %u RAM waveforms\n",
-				CS40L26_DEV_NAME, cs40l26->num_waves);
+	if (ret)
+		goto pm_err;
+
+	dev_info(dev, "%s loaded with %u RAM waveforms\n", CS40L26_DEV_NAME,
+			cs40l26->num_waves);
+
+	ret = cs40l26_owt_setup(cs40l26);
+	if (ret)
+		goto pm_err;
 
 pm_err:
 	pm_runtime_mark_last_busy(dev);
 	pm_runtime_put_autosuspend(dev);
-err_out:
-	enable_irq(cs40l26->irq);
+
 	return ret;
 }
 
-static void cs40l26_firmware_load(const struct firmware *fw, void *context)
+static int cs40l26_tuning_select_from_svc_le(struct cs40l26_private *cs40l26)
 {
-	struct cs40l26_private *cs40l26 = (struct cs40l26_private *)context;
-	struct device *dev = cs40l26->dev;
-	const struct firmware *coeff_fw;
-	int ret, i;
+	unsigned int reg, le = 0;
+	char svc_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN];
+	char wt_bin_file[CS40L26_TUNING_FILE_NAME_MAX_LEN];
+	char n_str[2];
+	int ret, i, j;
 
-	if (!fw) {
-		dev_err(dev, "Failed to request firmware file\n");
-		return;
+	pm_runtime_get_sync(cs40l26->dev);
+
+	ret = cs40l26_ack_write(cs40l26, CS40L26_DSP_VIRTUAL1_MBOX_1,
+			CS40L26_DSP_MBOX_CMD_LE_EST, CS40L26_DSP_MBOX_RESET);
+	if (ret)
+		goto pm_err;
+
+	ret = cl_dsp_get_reg(cs40l26->dsp, "LE_EST_STATUS",
+			CL_DSP_YM_UNPACKED_TYPE, CS40l26_SVC_ALGO_ID, &reg);
+	if (ret)
+		goto pm_err;
+
+	for (i = 0; i < CS40L26_SVC_LE_MAX_ATTEMPTS; i++) {
+		usleep_range(5000, 5100);
+		ret = regmap_read(cs40l26->regmap, reg, &le);
+		if (ret) {
+			dev_err(cs40l26->dev, "Failed to get LE_EST_STATUS\n");
+			goto pm_err;
+		}
+
+		for (j = 0; j < cs40l26->num_svc_le_vals; j++) {
+			if (le >= cs40l26->svc_le_vals[j]->min &&
+					le <= cs40l26->svc_le_vals[j]->max) {
+				strncpy(svc_bin_file,
+					CS40L26_SVC_TUNING_FILE_PREFIX,
+					CS40L26_SVC_TUNING_FILE_PREFIX_LEN);
+
+				strncpy(wt_bin_file, CS40L26_WT_FILE_PREFIX,
+					CS40L26_WT_FILE_PREFIX_LEN);
+
+				snprintf(n_str, 2, "%d",
+						cs40l26->svc_le_vals[j]->n);
+
+				strncat(svc_bin_file, n_str, 2);
+				strncat(wt_bin_file, n_str, 2);
+
+				break;
+			}
+		}
+		if (j < cs40l26->num_svc_le_vals)
+			break;
 	}
 
-	cs40l26->pm_ready = false;
+	if (i == 2) {
+		dev_warn(cs40l26->dev, "Using default tunings\n");
+	} else {
+		strncat(svc_bin_file, CS40L26_TUNING_FILE_SUFFIX,
+				CS40L26_TUNING_FILE_SUFFIX_LEN);
+		strncat(wt_bin_file, CS40L26_TUNING_FILE_SUFFIX,
+				CS40L26_TUNING_FILE_SUFFIX_LEN);
 
-	ret = cl_dsp_firmware_parse(cs40l26->dsp, fw,
-			cs40l26->fw_mode == CS40L26_FW_MODE_RAM);
-	release_firmware(fw);
-	if (ret)
-		return;
+		strncpy(cs40l26->fw.coeff_files[0], wt_bin_file,
+				CS40L26_WT_FILE_CONCAT_NAME_LEN);
+		strncpy(cs40l26->fw.coeff_files[2], svc_bin_file,
+				CS40L26_SVC_TUNING_FILE_NAME_LEN);
+	}
+
+pm_err:
+	pm_runtime_mark_last_busy(cs40l26->dev);
+	pm_runtime_put_autosuspend(cs40l26->dev);
+
+	kfree(cs40l26->svc_le_vals);
+
+	return ret;
+}
+
+static void cs40l26_coeff_load(struct cs40l26_private *cs40l26)
+{
+	struct device *dev = cs40l26->dev;
+	const struct firmware *coeff;
+	int i;
 
 	for (i = 0; i < cs40l26->fw.num_coeff_files; i++) {
-		request_firmware(&coeff_fw, cs40l26->fw.coeff_files[i],
-				dev);
-		if (!coeff_fw) {
+		request_firmware(&coeff, cs40l26->fw.coeff_files[i], dev);
+		if (!coeff) {
 			dev_warn(dev, "Continuing...\n");
 			continue;
 		}
 
-		if (cl_dsp_coeff_file_parse(cs40l26->dsp, coeff_fw))
+		if (cl_dsp_coeff_file_parse(cs40l26->dsp, coeff))
 			dev_warn(dev, "Continuing...\n");
 		else
-			dev_dbg(dev, "%s Loaded Successfully\n",
-				cs40l26->fw.coeff_files[i]);
+			dev_info(dev, "%s Loaded Successfully\n",
+					cs40l26->fw.coeff_files[i]);
+	}
+}
+
+static int cs40l26_firmware_load(struct cs40l26_private *cs40l26, u32 id)
+{
+	struct device *dev = cs40l26->dev;
+	const struct firmware *fw;
+	int ret;
+
+	if (cs40l26->fw.id == CS40L26_FW_ID)
+		ret = request_firmware(&fw, CS40L26_FW_FILE_NAME, dev);
+	else
+		ret = request_firmware(&fw, CS40L26_FW_CALIB_NAME, dev);
+
+	if (ret)
+		return ret;
+
+	ret = cl_dsp_firmware_parse(cs40l26->dsp, fw, true);
+	release_firmware(fw);
+
+	return ret;
+}
+
+int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id)
+{
+	struct device *dev = cs40l26->dev;
+	int ret;
+
+	if (id == cs40l26->fw.id) {
+		dev_warn(dev, "Cannot swap to same ID as running firmware\n");
+		return 0;
 	}
 
-	cs40l26_dsp_config(cs40l26);
+	cs40l26_pm_runtime_teardown(cs40l26);
+
+	cs40l26->fw_loaded = false;
+
+	disable_irq(cs40l26->irq);
+
+	ret = cs40l26_cl_dsp_init(cs40l26, id);
+	if (ret)
+		goto irq_exit;
+
+	ret = cs40l26_dsp_pre_config(cs40l26);
+	if (ret)
+		goto irq_exit;
+
+	ret = cs40l26_firmware_load(cs40l26, id);
+	if (ret)
+		goto irq_exit;
+
+	cs40l26_coeff_load(cs40l26);
+
+	ret = cs40l26_dsp_config(cs40l26);
+
+irq_exit:
+	enable_irq(cs40l26->irq);
+
+	return ret;
+}
+EXPORT_SYMBOL(cs40l26_fw_swap);
+
+static int cs40l26_update_reg_defaults(struct cs40l26_private *cs40l26)
+{
+	int ret;
+
+	ret = regmap_update_bits(cs40l26->regmap, CS40L26_TST_DAC_MSM_CONFIG,
+			CS40L26_SPK_DEFAULT_HIZ_MASK, 1 <<
+			CS40L26_SPK_DEFAULT_HIZ_SHIFT);
+
+	return ret;
+}
+
+static int cs40l26_handle_svc_le_nodes(struct cs40l26_private *cs40l26)
+{
+	struct device *dev = cs40l26->dev;
+	int i, init_count, node_count = 0;
+	struct fwnode_handle *child;
+	unsigned int min, max, index;
+	const char *node_name;
+
+	init_count = device_get_child_node_count(dev);
+	if (!init_count)
+		return 0;
+
+	cs40l26->svc_le_vals = kcalloc(init_count,
+			sizeof(struct cs40l26_svc_le *), GFP_KERNEL);
+
+	device_for_each_child_node(dev, child) {
+		node_name = fwnode_get_name(child);
+
+		if (strncmp(node_name, CS40L26_SVC_DT_PREFIX, 6))
+			continue;
+
+		if (fwnode_property_read_u32(child, "cirrus,min", &min)) {
+			dev_err(dev, "No minimum value for SVC LE node\n");
+			continue;
+		}
+
+		if (fwnode_property_read_u32(child, "cirrus,max", &max)) {
+			dev_err(dev, "No maximum value for SVC LE node\n");
+			continue;
+		}
+
+		if (max <= min) {
+			dev_err(dev, "Max <= Min, SVC LE node malformed\n");
+			continue;
+		}
+
+		if (fwnode_property_read_u32(child, "cirrus,index", &index)) {
+			dev_err(dev, "No index specified for SVC LE node\n");
+			continue;
+		}
+
+		for (i = 0; i < node_count; i++) {
+			if (index == cs40l26->svc_le_vals[i]->n)
+				break;
+		}
+
+		if (i < node_count) {
+			dev_err(dev, "SVC LE nodes must have unique index\n");
+			return -EINVAL;
+		}
+
+		cs40l26->svc_le_vals[node_count] =
+			kzalloc(sizeof(struct cs40l26_svc_le), GFP_KERNEL);
+
+		cs40l26->svc_le_vals[node_count]->min = min;
+		cs40l26->svc_le_vals[node_count]->max = max;
+		cs40l26->svc_le_vals[node_count]->n = index;
+		node_count++;
+	}
+
+	if (node_count != init_count)
+		dev_warn(dev, "%d platform nodes unused for SVC LE\n",
+				init_count - node_count);
+
+	return node_count;
 }
 
 static int cs40l26_handle_platform_data(struct cs40l26_private *cs40l26)
@@ -3035,6 +3222,7 @@
 	struct device *dev = cs40l26->dev;
 	struct device_node *np = dev->of_node;
 	const char *str = NULL;
+	int ret;
 	u32 val;
 
 	if (!np) {
@@ -3113,6 +3301,17 @@
 	else
 		cs40l26->pdata.bst_dcm_en = CS40L26_BST_DCM_EN_DEFAULT;
 
+	if (!of_property_read_u32(np, "cirrus,bst-ipk-microamp", &val))
+		cs40l26->pdata.bst_ipk = val;
+	else
+		cs40l26->pdata.bst_ipk = 0;
+
+	ret = cs40l26_handle_svc_le_nodes(cs40l26);
+	if (ret < 0)
+		cs40l26->num_svc_le_vals = 0;
+	else
+		cs40l26->num_svc_le_vals = ret;
+
 	return 0;
 }
 
@@ -3136,6 +3335,15 @@
 	INIT_WORK(&cs40l26->vibe_stop_work, cs40l26_vibe_stop_worker);
 	INIT_WORK(&cs40l26->set_gain_work, cs40l26_set_gain_worker);
 
+	cs40l26->asp_workqueue = alloc_ordered_workqueue("asp_workqueue",
+			WQ_HIGHPRI);
+	if (!cs40l26->asp_workqueue) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	INIT_WORK(&cs40l26->asp_work, cs40l26_asp_worker);
+
 	ret = devm_regulator_bulk_get(dev, CS40L26_NUM_SUPPLIES,
 			cs40l26_supplies);
 	if (ret) {
@@ -3177,7 +3385,7 @@
 	usleep_range(CS40L26_MIN_RESET_PULSE_WIDTH,
 			CS40L26_MIN_RESET_PULSE_WIDTH + 100);
 
-	gpiod_set_value_cansleep(cs40l26->reset_gpio, CS40L26_ENABLE);
+	gpiod_set_value_cansleep(cs40l26->reset_gpio, 1);
 
 	usleep_range(CS40L26_CONTROL_PORT_READY_DELAY,
 			CS40L26_CONTROL_PORT_READY_DELAY + 100);
@@ -3186,6 +3394,12 @@
 	if (ret)
 		goto err;
 
+	ret = cs40l26_update_reg_defaults(cs40l26);
+	if (ret) {
+		dev_err(dev, "Failed to update reg defaults\n");
+		goto err;
+	}
+
 	ret = devm_request_threaded_irq(dev, cs40l26->irq, NULL, cs40l26_irq,
 			IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
 			"cs40l26", cs40l26);
@@ -3193,23 +3407,54 @@
 		dev_err(dev, "Failed to request threaded IRQ\n");
 		goto err;
 	}
+
 	/* the /ALERT pin may be asserted prior to firmware initialization.
 	 * Disable the interrupt handler until firmware has downloaded
 	 * so erroneous interrupt requests are ignored
 	 */
 	disable_irq(cs40l26->irq);
 
-	ret = cs40l26_cl_dsp_init(cs40l26);
+	cs40l26->pm_ready = false;
+	cs40l26->fw_loaded = false;
+
+	ret = cs40l26_cl_dsp_init(cs40l26, CS40L26_FW_ID);
 	if (ret)
-		goto err;
+		goto irq_err;
 
 	ret = cs40l26_dsp_pre_config(cs40l26);
 	if (ret)
-		goto err;
+		goto irq_err;
 
-	request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
-			CS40L26_FW_FILE_NAME, dev, GFP_KERNEL, cs40l26,
-			cs40l26_firmware_load);
+	ret = cs40l26_firmware_load(cs40l26, cs40l26->fw.id);
+	if (ret)
+		goto irq_err;
+
+	if (cs40l26->num_svc_le_vals) {
+		ret = cs40l26_dsp_config(cs40l26);
+		if (ret)
+			goto irq_err;
+
+		enable_irq(cs40l26->irq);
+
+		ret = cs40l26_tuning_select_from_svc_le(cs40l26);
+		if (ret)
+			goto err;
+
+		disable_irq(cs40l26->irq);
+		cs40l26_pm_runtime_teardown(cs40l26);
+
+		ret = cs40l26_dsp_pre_config(cs40l26);
+		if (ret)
+			goto err;
+	}
+
+	cs40l26_coeff_load(cs40l26);
+
+	ret = cs40l26_dsp_config(cs40l26);
+	if (ret)
+		goto irq_err;
+
+	enable_irq(cs40l26->irq);
 
 	ret = cs40l26_input_init(cs40l26);
 	if (ret)
@@ -3224,6 +3469,8 @@
 
 	return 0;
 
+irq_err:
+	enable_irq(cs40l26->irq);
 err:
 	cs40l26_remove(cs40l26);
 
@@ -3246,10 +3493,15 @@
 		cs40l26_pm_runtime_teardown(cs40l26);
 
 	if (cs40l26->vibe_workqueue) {
-		destroy_workqueue(cs40l26->vibe_workqueue);
 		cancel_work_sync(&cs40l26->vibe_start_work);
 		cancel_work_sync(&cs40l26->vibe_stop_work);
 		cancel_work_sync(&cs40l26->set_gain_work);
+		destroy_workqueue(cs40l26->vibe_workqueue);
+	}
+
+	if (cs40l26->asp_workqueue) {
+		cancel_work_sync(&cs40l26->asp_work);
+		destroy_workqueue(cs40l26->asp_workqueue);
 	}
 
 	if (vp_consumer)
@@ -3258,10 +3510,7 @@
 	if (va_consumer)
 		regulator_disable(va_consumer);
 
-	gpiod_set_value_cansleep(cs40l26->reset_gpio, CS40L26_DISABLE);
-
-	if (cs40l26->vibe_timer.function)
-		hrtimer_cancel(&cs40l26->vibe_timer);
+	gpiod_set_value_cansleep(cs40l26->reset_gpio, 0);
 
 	if (cs40l26->vibe_init_success) {
 #if !IS_ENABLED(CONFIG_INPUT_CS40L26_ATTR_UNDER_BUS)
diff --git a/cs40l26/cs40l26.h b/cs40l26/cs40l26.h
index 7c0bce8..291d458 100644
--- a/cs40l26/cs40l26.h
+++ b/cs40l26/cs40l26.h
@@ -86,6 +86,8 @@
 #define CS40L26_OSC_TRIM				0x2030
 #define CS40L26_ERROR_RELEASE				0x2034
 #define CS40L26_PLL_OVERRIDE				0x2038
+#define CS40L26_CHIP_STATUS_1				0x2040
+#define CS40L26_CHIP_STATUS_2				0x2044
 #define CS40L26_BIAS_PTE_MODE_CONTROL			0x2404
 #define CS40L26_SCL_PAD_CONTROL			0x2408
 #define CS40L26_SDA_PAD_CONTROL			0x240C
@@ -190,6 +192,7 @@
 #define CS40L26_SPKMON_RATE_SEL			0x4004
 #define CS40L26_MONITOR_FILT				0x4008
 #define CS40L26_IMON_COMP				0x4010
+#define CS40L26_SPKMON_VMON_DEC_OUT_DATA		0x41B4
 #define CS40L26_WARN_LIMIT_THRESHOLD			0x4220
 #define CS40L26_CONFIGURATION				0x4224
 #define CS40L26_STATUS					0x4300
@@ -400,6 +403,8 @@
 #define CS40L26_DSP1_AHBM_WINDOW6_CONTROL_1		0x25E2034
 #define CS40L26_DSP1_AHBM_WINDOW7_CONTROL_0		0x25E2038
 #define CS40L26_DSP1_AHBM_WINDOW7_CONTROL_1		0x25E203C
+#define CS40L26_DSP1_AHBM_WINDOW_DEBUG_0		0x25E2040
+#define CS40L26_DSP1_AHBM_WINDOW_DEBUG_1		0x25E2044
 #define CS40L26_DSP1_XMEM_UNPACKED24_0			0x2800000
 #define CS40L26_DSP1_XMEM_UNPACKED24_1			0x2800004
 #define CS40L26_DSP1_XMEM_UNPACKED24_2			0x2800008
@@ -616,9 +621,16 @@
 #define CS40L26_DSP1_PROM_30713			0x3C7DFE4
 #define CS40L26_DSP1_PROM_30714			0x3C7DFE8
 
-/* this is not a CS40L26 restriction and may be able to be removed */
 #define CS40L26_MAX_I2C_READ_SIZE_BYTES	32
 
+/* Register default changes */
+#define CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_FULL 0x11330000
+#define CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_H16 (\
+		CS40L26_TST_DAC_MSM_CONFIG_DEFAULT_CHANGE_VALUE_FULL >> 16)
+#define CS40L26_SPK_DEFAULT_HIZ_MASK BIT(28)
+#define CS40L26_SPK_DEFAULT_HIZ_SHIFT 28
+
+/* Device */
 #define CS40L26_DEV_NAME		"CS40L26"
 #define CS40L26_INPUT_DEV_NAME		"cs40l26_input"
 #define CS40L26_DEVID_A			0x40A260
@@ -626,18 +638,18 @@
 #define CS40L26_DEVID_MASK		GENMASK(23, 0)
 #define CS40L26_NUM_DEVS		2
 
-#define CS40L26_REVID_A0		0xA0
 #define CS40L26_REVID_A1		0xA1
 #define CS40L26_REVID_MASK		GENMASK(7, 0)
 
 #define CS40L26_GLOBAL_EN_MASK		BIT(0)
 
-#define CS40L26_DISABLE			0
-#define CS40L26_ENABLE			1
-
 #define CS40L26_DSP_CCM_CORE_KILL		0x00000080
 #define CS40L26_DSP_CCM_CORE_RESET		0x00000281
 
+#define CS40L26_GLOBAL_FS_MASK			GENMASK(4, 0)
+#define CS40L26_GLOBAL_FS_48K			0x03
+#define CS40L26_GLOBAL_FS_96K			0x04
+
 #define CS40L26_MEM_RDY_MASK			BIT(1)
 #define CS40L26_MEM_RDY_SHIFT			1
 
@@ -645,6 +657,8 @@
 
 #define CS40L26_DSP_HALO_STATE_RUN		2
 
+#define CS40L26_NUM_PCT_MAP_VALUES		101
+
 /* DSP State */
 #define CS40L26_DSP_STATE_HIBERNATE		0
 #define CS40L26_DSP_STATE_SHUTDOWN		1
@@ -655,12 +669,6 @@
 
 #define CS40L26_DSP_STATE_STR_LEN		10
 
-/* ROM Controls A0 */
-#define CS40L26_A0_PM_CUR_STATE_STATIC_REG		0x02800358
-#define CS40L26_A0_PM_TIMEOUT_TICKS_STATIC_REG		0x02800338
-#define CS40L26_A0_DSP_HALO_STATE_REG			0x02806f40
-
-
 /* ROM Controls A1 */
 #define CS40L26_A1_PM_CUR_STATE_STATIC_REG		0x02800370
 #define CS40L26_A1_PM_TIMEOUT_TICKS_STATIC_REG		0x02800350
@@ -682,47 +690,28 @@
 #define CS40L26_LOGGER_ALGO_ID		0x0004013D
 #define CS40L26_EXT_ALGO_ID		0x0004013C
 
-#define CS40L26_VIBEGEN_ROM_ALGO_ID	0x000000BD
-#define CS40L26_BUZZGEN_ROM_ALGO_ID	0x0000F202
-#define CS40L26_PM_ROM_ALGO_ID		0x0000F206
-
 /* power management */
-#define CS40L26_PSEQ_V1_MAX_ENTRIES		32
-#define CS40L26_PSEQ_V1_MAX_WRITES		64
-#define CS40L26_PSEQ_V1_VAL_SHIFT			24
-#define CS40L26_PSEQ_V1_VAL_MASK			GENMASK(23, 0)
-#define CS40L26_PSEQ_V1_ADDR_SHIFT		8
-#define CS40L26_PSEQ_V1_ADDR_MASK			GENMASK(15, 0)
-#define CS40L26_PSEQ_V1_LIST_TERM			0xFFFFFF
-#define CS40L26_PSEQ_V1_LIST_TERM_MASK		GENMASK(31, 0)
-#define CS40L26_PSEQ_V1_STRIDE			8
-#define CS40L26_PSEQ_V1_PAIR_NUM_WORDS		2
-#define CS40L26_PSEQ_V1_ADDR_WORD_MASK		GENMASK(23, 8)
-#define CS40L26_PSEQ_V1_VAL_WORD_UPPER_MASK	GENMASK(8, 0)
-#define CS40L26_PSEQ_V1_DO_NOT_REPLACE		0
-#define CS40L26_PSEQ_V1_REPLACE			1
-
-#define CS40L26_PSEQ_V2_MAX_WORDS_PER_OP CS40L26_PSEQ_V2_OP_WRITE_FIELD_WORDS
-#define CS40L26_PSEQ_V2_MAX_WORDS			129
-#define CS40L26_PSEQ_V2_NUM_OPS				8
-#define CS40L26_PSEQ_V2_OP_MASK				GENMASK(23, 16)
-#define CS40L26_PSEQ_V2_OP_SHIFT			16
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_FULL		0x00
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_FULL_WORDS		3
-#define CS40L26_PSEQ_V2_OP_WRITE_FIELD			0x01
-#define CS40L26_PSEQ_V2_OP_WRITE_FIELD_WORDS		4
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_ADDR8		0x02
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_ADDR8_WORDS	2
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_INCR		0x03
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_INCR_WORDS		2
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_L16		0x04
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_L16_WORDS		2
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_H16		0x05
-#define CS40L26_PSEQ_V2_OP_WRITE_REG_H16_WORDS		2
-#define CS40L26_PSEQ_V2_OP_DELAY			0xFE
-#define CS40L26_PSEQ_V2_OP_DELAY_WORDS			1
-#define CS40L26_PSEQ_V2_OP_END				0xFF
-#define CS40L26_PSEQ_V2_OP_END_WORDS			1
+#define CS40L26_PSEQ_MAX_WORDS_PER_OP CS40L26_PSEQ_OP_WRITE_FIELD_WORDS
+#define CS40L26_PSEQ_MAX_WORDS			129
+#define CS40L26_PSEQ_NUM_OPS				8
+#define CS40L26_PSEQ_OP_MASK				GENMASK(23, 16)
+#define CS40L26_PSEQ_OP_SHIFT			16
+#define CS40L26_PSEQ_OP_WRITE_REG_FULL		0x00
+#define CS40L26_PSEQ_OP_WRITE_REG_FULL_WORDS		3
+#define CS40L26_PSEQ_OP_WRITE_FIELD			0x01
+#define CS40L26_PSEQ_OP_WRITE_FIELD_WORDS		4
+#define CS40L26_PSEQ_OP_WRITE_REG_ADDR8		0x02
+#define CS40L26_PSEQ_OP_WRITE_REG_ADDR8_WORDS	2
+#define CS40L26_PSEQ_OP_WRITE_REG_INCR		0x03
+#define CS40L26_PSEQ_OP_WRITE_REG_INCR_WORDS		2
+#define CS40L26_PSEQ_OP_WRITE_REG_L16		0x04
+#define CS40L26_PSEQ_OP_WRITE_REG_L16_WORDS		2
+#define CS40L26_PSEQ_OP_WRITE_REG_H16		0x05
+#define CS40L26_PSEQ_OP_WRITE_REG_H16_WORDS		2
+#define CS40L26_PSEQ_OP_DELAY			0xFE
+#define CS40L26_PSEQ_OP_DELAY_WORDS			1
+#define CS40L26_PSEQ_OP_END				0xFF
+#define CS40L26_PSEQ_OP_END_WORDS			1
 
 #define CS40L26_PM_STDBY_TIMEOUT_LOWER_OFFSET	16
 #define CS40L26_PM_STDBY_TIMEOUT_UPPER_OFFSET	20
@@ -749,7 +738,7 @@
 /* DSP mailbox controls */
 #define CS40L26_DSP_TIMEOUT_US_MIN		1000
 #define CS40L26_DSP_TIMEOUT_US_MAX		1100
-#define CS40L26_DSP_TIMEOUT_COUNT		50
+#define CS40L26_DSP_TIMEOUT_COUNT		100
 
 #define CS40L26_DSP_MBOX_RESET			0x0
 
@@ -768,6 +757,10 @@
 #define CS40L26_DSP_MBOX_CMD_OWT_PUSH	0x03000008
 #define CS40L26_DSP_MBOX_CMD_OWT_RESET	0x03000009
 
+#define CS40L26_DSP_MBOX_CMD_LE_EST	0x07000004
+
+#define CS40L26_DSP_MBOX_CMD_OWT_DELETE_BASE	0x0D000000
+
 #define CS40L26_DSP_MBOX_CMD_INDEX_MASK	GENMASK(28, 24)
 #define CS40L26_DSP_MBOX_CMD_INDEX_SHIFT	24
 
@@ -783,20 +776,41 @@
 #define CS40L26_DSP_MBOX_F0_EST_DONE		0x07000021
 #define CS40L26_DSP_MBOX_REDC_EST_START		0x07000012
 #define CS40L26_DSP_MBOX_REDC_EST_DONE		0x07000022
+#define CS40L26_DSP_MBOX_LE_EST_START		0x07000014
+#define CS40L26_DSP_MBOX_LE_EST_DONE		0x07000024
 #define CS40L26_DSP_MBOX_SYS_ACK		0x0A000000
 #define CS40L26_DSP_MBOX_PANIC			0x0C000000
 
 /* Firmware Mode */
 #define CS40L26_FW_FILE_NAME		"cs40l26.wmfw"
+#define CS40L26_FW_CALIB_NAME		"cs40l26-calib.wmfw"
 
-#define CS40L26_WT_FILE_NAME		"cs40l26.bin"
-#define CS40L26_SVC_TUNING_FILE_NAME	"cs40l26-svc.bin"
-#define CS40L26_A2H_TUNING_FILE_NAME	"cs40l26-a2h.bin"
+#define CS40L26_TUNING_FILES_MAX	3
+
+#define CS40L26_WT_FILE_NAME			"cs40l26.bin"
+#define CS40L26_WT_FILE_NAME_LEN		12
+#define CS40L26_WT_FILE_PREFIX			"cs40l26-wt"
+#define CS40L26_WT_FILE_PREFIX_LEN		11
+#define CS40L26_WT_FILE_CONCAT_NAME_LEN		16
+#define CS40L26_SVC_TUNING_FILE_PREFIX		"cs40l26-svc"
+#define CS40L26_SVC_TUNING_FILE_PREFIX_LEN	12
+#define CS40L26_SVC_TUNING_FILE_NAME		"cs40l26-svc.bin"
+#define CS40L26_SVC_TUNING_FILE_NAME_LEN	16
+#define CS40L26_A2H_TUNING_FILE_NAME		"cs40l26-a2h.bin"
+#define CS40L26_A2H_TUNING_FILE_NAME_LEN	16
+#define CS40L26_TUNING_FILE_NAME_MAX_LEN	20
+#define CS40L26_TUNING_FILE_SUFFIX		".bin"
+#define CS40L26_TUNING_FILE_SUFFIX_LEN		4
+
+#define CS40L26_SVC_LE_MAX_ATTEMPTS	2
+#define CS40L26_SVC_DT_PREFIX		"svc-le"
 
 #define CS40L26_FW_ID			0x1800D4
 #define CS40L26_FW_ROM_MIN_REV		0x040000
 #define CS40L26_FW_A0_RAM_MIN_REV	0x050004
-#define CS40L26_FW_A1_RAM_MIN_REV	0x070201
+#define CS40L26_FW_A1_RAM_MIN_REV	0x07020A
+#define CS40L26_FW_CALIB_ID		0x1800DA
+#define CS40L26_FW_CALIB_MIN_REV	0x010000
 
 #define CS40L26_CCM_CORE_RESET		0x00000200
 #define CS40L26_CCM_CORE_ENABLE	0x00000281
@@ -833,7 +847,7 @@
 #define CS40L26_ROM_INDEX_END			0x01800026
 
 #define CS40L26_OWT_INDEX_START		0x01400000
-#define CS40L26_OWT_INDEX_END			0x01400005
+#define CS40L26_OWT_INDEX_END			0x01400010
 
 
 #define CS40L26_RAM_BANK_ID			0
@@ -857,6 +871,19 @@
 
 #define CS40L26_AMP_VOL_PCM_MAX		0x07FF
 
+/* GPI Triggering */
+#define CS40L26_EVENT_MAP_INDEX_MASK	GENMASK(8, 0)
+
+#define CS40L26_BTN_INDEX_MASK	GENMASK(6, 0)
+#define CS40L26_BTN_BUZZ_MASK	BIT(7)
+#define CS40L26_BTN_BUZZ_SHIFT	7
+#define CS40L26_BTN_BANK_MASK	BIT(8)
+#define CS40L26_BTN_BANK_SHIFT	8
+#define CS40L26_BTN_NUM_MASK	GENMASK(14, 12)
+#define CS40L26_BTN_NUM_SHIFT	12
+#define CS40L26_BTN_EDGE_MASK	BIT(15)
+#define CS40L26_BTN_EDGE_SHIFT	15
+
 /* Interrupts */
 #define CS40L26_IRQ_STATUS_DEASSERT		0x0
 #define CS40L26_IRQ_STATUS_ASSERT		0x1
@@ -866,21 +893,28 @@
 #define CS40L26_IRQ_UNMASK			0
 #define CS40L26_IRQ_MASK			1
 
-/* output */
-#define CS40L26_GLOBAL_ENABLES2_DEFAULT	0x01000000
-#define CS40L26_BST_CTRL_DEFAULT		0x000000AA
-#define CS40L26_DACPCM1_INPUT_DEFAULT		0x00000032
-#define CS40L26_ASP_ENABLES1_DEFAULT		0x00070003
-#define CS40L26_ASP_CTRL2_DEFAULT		0x20200011
-#define CS40L26_DSP1RX5_INPUT_DEFAULT		0x00000009
+#define CS40L26_IRQ_EINT1_ALL_MASK		0xFFDC7FFF
+#define CS40L26_IRQ_EINT2_ALL_MASK		0x07DE0400
 
-#define CS40L26_NUM_OUTPUT_SETUP_WRITES	3
+/* temp monitoring */
+#define CS40L26_TEMPMON_EN_MASK		BIT(10)
+#define CS40L26_TEMPMON_EN_SHIFT	10
+#define CS40L26_TEMP_RESULT_FILT_MASK	GENMASK(24, 16)
+#define CS40L26_TEMP_RESULT_FILT_SHIFT	16
 
 /* BST */
 #define CS40L26_BST_DCM_EN_DEFAULT		1
 #define CS40L26_BST_DCM_EN_MASK			BIT(0)
 #define CS40L26_BST_DCM_EN_SHIFT		0
 
+#define CS40L26_BST_IPK_MILLIAMP_MAX		4800
+#define CS40L26_BST_IPK_MILLIAMP_MIN		1600
+
+#define CS40L26_BST_IPK_DEFAULT			0x4A
+
+#define CS40L26_BOOST_DISABLE_DELAY_MIN         0
+#define CS40L26_BOOST_DISABLE_DELAY_MAX         8388608
+
 /* brownout prevention */
 #define CS40L26_VXBR_DEFAULT			0xFFFFFFFF
 
@@ -943,19 +977,19 @@
 #define CS40L26_VXBR_REL_RATE_SHIFT		21
 
 /* audio */
-#define CS40L26_PLL_CLK_CFG0			0x00
-#define CS40L26_PLL_CLK_CFG1			0x1B
-#define CS40L26_PLL_CLK_CFG2			0x21
-#define CS40L26_PLL_CLK_CFG3			0x28
-#define CS40L26_PLL_CLK_CFG4			0x30
-#define CS40L26_PLL_CLK_CFG5			0x33
+#define CS40L26_PLL_CLK_CFG_32768		0x00
+#define CS40L26_PLL_CLK_CFG_1536000		0x1B
+#define CS40L26_PLL_CLK_CFG_3072000		0x21
+#define CS40L26_PLL_CLK_CFG_6144000		0x28
+#define CS40L26_PLL_CLK_CFG_9600000		0x30
+#define CS40L26_PLL_CLK_CFG_12288000		0x33
 
-#define CS40L26_PLL_CLK_FRQ0			32768
-#define CS40L26_PLL_CLK_FRQ1			1536000
-#define CS40L26_PLL_CLK_FRQ2			3072000
-#define CS40L26_PLL_CLK_FRQ3			6144000
-#define CS40L26_PLL_CLK_FRQ4			9600000
-#define CS40L26_PLL_CLK_FRQ5			12288000
+#define CS40L26_PLL_CLK_FRQ_32768		32768
+#define CS40L26_PLL_CLK_FRQ_1536000		1536000
+#define CS40L26_PLL_CLK_FRQ_3072000		3072000
+#define CS40L26_PLL_CLK_FRQ_6144000		6144000
+#define CS40L26_PLL_CLK_FRQ_9600000		9600000
+#define CS40L26_PLL_CLK_FRQ_12288000		12288000
 
 #define CS40L26_PLL_CLK_SEL_BCLK		0x0
 #define CS40L26_PLL_CLK_SEL_FSYNC		0x1
@@ -965,6 +999,7 @@
 #define CS40L26_PLL_CLK_CFG_MASK		GENMASK(5, 0)
 
 #define CS40L26_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS40L26_RATES	(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 
 #define CS40L26_ASP_RX_WIDTH_MASK		GENMASK(31, 24)
 #define CS40L26_ASP_RX_WIDTH_SHIFT		24
@@ -1039,19 +1074,13 @@
 #define CS40L26_ASP_RX2_SLOT_MASK	GENMASK(13, 8)
 #define CS40L26_ASP_RX2_SLOT_SHIFT	8
 
-#define CS40L26_CLASS_H_EN_MASK	BIT(4)
-#define CS40L26_CLASS_H_EN_SHIFT	4
-
-#define CS40L26_BST_CTL_SEL_MASK	GENMASK(1, 0)
-#define CS40L26_BST_CTL_SEL_FIXED	0x0
-#define CS40L26_BST_CTL_SEL_CLASSH	0x1
-
 #define CS40L26_A2H_MAX_TUNINGS	5
 
-#define CS40L26_VOLUME_MAX_STEPS	100
-#define CS40L26_VOLUME_MAX		0x7FFFFF
-#define CS40L26_VOLUME_STEP_SIZE	(CS40L26_VOLUME_MAX / \
-					CS40L26_VOLUME_MAX_STEPS)
+#define CS40L26_A2H_VOLUME_MAX		0x7FFFFF
+
+#define CS40L26_VMON_DEC_OUT_DATA_MASK	GENMASK(23, 0)
+#define CS40L26_VMON_OVFL_FLAG_MASK	BIT(31)
+#define CS40L26_VMON_DEC_OUT_DATA_MAX	CS40L26_VMON_DEC_OUT_DATA_MASK
 
 /* OWT */
 #define CS40L26_WT_STR_MAX_LEN			512
@@ -1064,11 +1093,9 @@
 #define CS40L26_WT_INDEF_TIME_VAL		0xFFFF
 #define CS40L26_WT_MAX_TIME_VAL		16383 /* ms */
 
-#define CS40L26_WT_TERM_SIZE			4
-#define CS40L26_WT_WLEN_TERM_SIZE		8
-#define CS40L26_WT_HEADER_TERM			0xFFFFFF
-#define CS40L26_WT_HEADER_OFFSET		4
-#define CS40L26_WT_HEADER_DEFAULT_FLAGS	0x0000
+#define CS40L26_WT_WLEN_SIZE			4
+#define CS40L26_WT_HEADER_OFFSET		3
+#define CS40L26_WT_HEADER_DEFAULT_FLAGS		0x0000
 
 #define CS40L26_WT_TYPE10_COMP_SEG_LEN_MAX	20
 
@@ -1077,6 +1104,10 @@
 #define CS40L26_WT_TYPE10_WAVELEN_CALCULATED	0x800000
 #define CS40L26_WT_TYPE10_COMP_DURATION_FLAG	0x8
 
+/* F0 Offset represented as Q10.14 format */
+#define CS40L26_F0_OFFSET_MAX		0x190000 /* +100 Hz */
+#define CS40L26_F0_OFFSET_MIN		0xE70000 /* -100 Hz */
+
 /* Calibration */
 #define CS40L26_F0_EST_MIN 0xC8000
 #define CS40L26_F0_EST_MAX 0x7FC000
@@ -1114,9 +1145,6 @@
 
 #define CS40L26_SAMPS_TO_MS(n)	((n) / 8)
 
-extern const struct cl_dsp_fw_desc cs40l26_fw;
-extern const struct cl_dsp_fw_desc cs40l26_ram_fw;
-
 /* enums */
 enum cs40l26_vibe_state {
 	CS40L26_VIBE_STATE_STOPPED,
@@ -1225,11 +1253,8 @@
 struct cs40l26_fw {
 	unsigned int id;
 	unsigned int min_rev;
-	unsigned int halo_state_run;
 	unsigned int num_coeff_files;
-	const char * const *coeff_files;
-	const char *fw_file;
-	bool write_fw;
+	char **coeff_files;
 };
 
 struct cs40l26_owt_section {
@@ -1245,12 +1270,7 @@
 	u32 val;
 };
 
-struct cs40l26_pseq_v1_pair {
-	u16 addr;
-	u32 val;
-};
-
-struct cs40l26_pseq_v2_op {
+struct cs40l26_pseq_op {
 	u8 size;
 	u16 offset; /* offset in bytes from pseq_base */
 	u8 operation;
@@ -1258,6 +1278,12 @@
 	struct list_head list;
 };
 
+struct cs40l26_svc_le {
+	u32 min;
+	u32 max;
+	u32 n;
+};
+
 struct cs40l26_platform_data {
 	const char *device_name;
 	bool vbbr_en;
@@ -1275,6 +1301,14 @@
 	u32 vpbr_wait;
 	u32 vpbr_rel_rate;
 	bool bst_dcm_en;
+	u32 bst_ipk;
+};
+
+struct cs40l26_owt {
+	int effect_id;
+	u32 wlength;
+	u32 trigger_index;
+	struct list_head list;
 };
 
 struct cs40l26_private {
@@ -1289,18 +1323,15 @@
 	struct cl_dsp *dsp;
 	unsigned int trigger_indices[FF_MAX_EFFECTS];
 	struct ff_effect *effect;
-	struct hrtimer vibe_timer;
 	struct work_struct vibe_start_work;
 	struct work_struct vibe_stop_work;
 	struct work_struct set_gain_work;
 	struct workqueue_struct *vibe_workqueue;
 	int irq;
 	bool vibe_init_success;
-	unsigned int pseq_v1_len;
-	unsigned int pseq_v2_num_ops;
+	unsigned int pseq_num_ops;
 	u32 pseq_base;
-	struct cs40l26_pseq_v1_pair pseq_v1_table[CS40L26_PSEQ_V1_MAX_ENTRIES];
-	struct list_head pseq_v2_op_head;
+	struct list_head pseq_op_head;
 	enum cs40l26_pm_state pm_state;
 	struct cs40l26_iseq_pair iseq_table[CS40L26_ISEQ_MAX_ENTRIES];
 	enum cs40l26_fw_mode fw_mode;
@@ -1314,10 +1345,15 @@
 	u8 last_wksrc_pol;
 	u8 wksrc_sts;
 	u32 event_count;
-	u32 owt_wlength;
+	struct list_head owt_head;
 	int num_owt_effects;
 	int cal_requested;
 	u16 gain_pct;
+	u32 event_map_base;
+	struct cs40l26_svc_le **svc_le_vals;
+	int num_svc_le_vals;
+	struct workqueue_struct *asp_workqueue;
+	struct work_struct asp_work;
 };
 
 struct cs40l26_codec {
@@ -1332,6 +1368,9 @@
 	int tdm_width;
 	int tdm_slots;
 	int tdm_slot[2];
+	bool svc_for_streaming_data;
+	bool invert_streaming_data;
+	bool bypass_dsp;
 };
 
 struct cs40l26_pll_sysclk_config {
@@ -1340,9 +1379,11 @@
 };
 
 /* exported function prototypes */
+int cs40l26_get_num_waves(struct cs40l26_private *cs40l26, u32 *num_waves);
+void cs40l26_asp_worker(struct work_struct *work);
+int cs40l26_fw_swap(struct cs40l26_private *cs40l26, u32 id);
 void cs40l26_vibe_state_set(struct cs40l26_private *cs40l26,
 		enum cs40l26_vibe_state);
-int cs40l26_class_h_set(struct cs40l26_private *cs40l26, bool class_h);
 int cs40l26_pm_timeout_ms_get(struct cs40l26_private *cs40l26,
 		u32 *timeout_ms);
 int cs40l26_pm_timeout_ms_set(struct cs40l26_private *cs40l26,
@@ -1351,9 +1392,7 @@
 		enum cs40l26_pm_state state);
 int cs40l26_ack_write(struct cs40l26_private *cs40l26, u32 reg, u32 write_val,
 		u32 reset_val);
-int cs40l26_pseq_v1_multi_add_pair(struct cs40l26_private *cs40l26,
-		const struct reg_sequence *reg_seq, int num_regs, bool replace);
-int cs40l26_pseq_v2_multi_add_write_reg_full(struct cs40l26_private *cs40l26,
+int cs40l26_pseq_multi_add_write_reg_full(struct cs40l26_private *cs40l26,
 		const struct reg_sequence *reg_seq, int num_regs,
 		bool update_if_op_already_in_seq);
 int cs40l26_resume(struct device *dev);
@@ -1377,9 +1416,8 @@
 extern const struct dev_pm_ops cs40l26_pm_ops;
 extern const struct regmap_config cs40l26_regmap;
 extern const struct mfd_cell cs40l26_devs[CS40L26_NUM_MFD_DEVS];
-extern const u8 cs40l26_pseq_v2_op_sizes[CS40L26_PSEQ_V2_NUM_OPS][2];
-extern const char * const cs40l26_ram_coeff_files[3];
-extern const u32 cs40l26_attn_q21_2_vals[101];
+extern const u8 cs40l26_pseq_op_sizes[CS40L26_PSEQ_NUM_OPS][2];
+extern const u32 cs40l26_attn_q21_2_vals[CS40L26_NUM_PCT_MAP_VALUES];
 
 
 /* sysfs */