Merge "dsp: Add support to dynamically load and unload BT modules"
diff --git a/asoc/bengal.c b/asoc/bengal.c
index cbd309e..15c2104 100644
--- a/asoc/bengal.c
+++ b/asoc/bengal.c
@@ -6370,7 +6370,7 @@
 			ret = -EINVAL;
 			goto err;
 		}
-		if (soc_find_component(wsa_of_node, NULL)) {
+		if (soc_find_component_locked(wsa_of_node, NULL)) {
 			/* WSA device registered with ALSA core */
 			wsa881x_dev_info[found].of_node = wsa_of_node;
 			wsa881x_dev_info[found].index = i;
@@ -6467,7 +6467,7 @@
 			ret = -EINVAL;
 			goto err;
 		}
-		if (soc_find_component(aux_codec_of_node, NULL)) {
+		if (soc_find_component_locked(aux_codec_of_node, NULL)) {
 			/* AUX codec registered with ALSA core */
 			aux_cdc_dev_info[codecs_found].of_node =
 						aux_codec_of_node;
diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c
index 79a95c0..3f6292c 100644
--- a/asoc/codecs/wcd938x/wcd938x.c
+++ b/asoc/codecs/wcd938x/wcd938x.c
@@ -3722,7 +3722,7 @@
 }
 
 static const struct of_device_id wcd938x_dt_match[] = {
-	{ .compatible = "qcom,wcd938x-codec" },
+	{ .compatible = "qcom,wcd938x-codec", .data = "wcd938x"},
 	{}
 };
 
diff --git a/asoc/kona-port-config.h b/asoc/kona-port-config.h
index 723c94a..6ab1d29 100644
--- a/asoc/kona-port-config.h
+++ b/asoc/kona-port-config.h
@@ -59,6 +59,14 @@
 	{3,  0,  0,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2},  /* TX4 */
 };
 
+static struct port_params tx_frame_params_wcd937x[SWR_MSTR_PORT_LEN] = {
+	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	{3,  1,  0,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0},
+	{3,  2,  0,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0},
+	{3,  1,  0,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0},
+	{3,  0,  0,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0},
+};
+
 static struct swr_mstr_port_map sm_port_map[] = {
 	{TX_MACRO, SWR_UC0, tx_frame_params_default},
 	{RX_MACRO, SWR_UC0, rx_frame_params_default},
@@ -73,4 +81,11 @@
 	{WSA_MACRO, SWR_UC0, wsa_frame_params_default},
 };
 
+static struct swr_mstr_port_map sm_port_map_wcd937x[] = {
+	{TX_MACRO, SWR_UC0, tx_frame_params_wcd937x},
+	{RX_MACRO, SWR_UC0, rx_frame_params_default},
+	{RX_MACRO, SWR_UC1, rx_frame_params_dsd},
+	{WSA_MACRO, SWR_UC0, wsa_frame_params_default},
+};
+
 #endif /* _KONA_PORT_CONFIG */
diff --git a/asoc/kona.c b/asoc/kona.c
index 33c24f1..02b9e01 100644
--- a/asoc/kona.c
+++ b/asoc/kona.c
@@ -33,6 +33,8 @@
 #include "codecs/wsa881x.h"
 #include "codecs/wsa883x/wsa883x.h"
 #include "codecs/wcd938x/wcd938x.h"
+#include "codecs/wcd937x/wcd937x-mbhc.h"
+#include "codecs/wcd937x/wcd937x.h"
 #include "codecs/bolero/bolero-cdc.h"
 #include <dt-bindings/sound/audio-codec-port-types.h>
 #include "codecs/bolero/wsa-macro.h"
@@ -860,6 +862,23 @@
 static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc85_dma_rx_5_sample_rate,
 				cdc_dma_sample_rate_text);
 
+/* WCD937x */
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_0_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_1_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_2_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_3_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_5_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_0_sample_rate,
+				cdc_dma_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_1_sample_rate,
+				cdc_dma_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_2_sample_rate,
+				cdc_dma_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_3_sample_rate,
+				cdc_dma_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_5_sample_rate,
+				cdc_dma_sample_rate_text);
+
 static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
 static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
 static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
@@ -3685,6 +3704,39 @@
 			cdc_dma_rx_sample_rate_put),
 };
 
+static const struct snd_kcontrol_new msm_int_wcd937x_snd_controls[] = {
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_0 Format", rx_cdc_dma_rx_0_format,
+			cdc_dma_rx_format_get, cdc_dma_rx_format_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_1 Format", rx_cdc_dma_rx_1_format,
+			cdc_dma_rx_format_get, cdc_dma_rx_format_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_2 Format", rx_cdc_dma_rx_2_format,
+			cdc_dma_rx_format_get, cdc_dma_rx_format_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_3 Format", rx_cdc_dma_rx_3_format,
+			cdc_dma_rx_format_get, cdc_dma_rx_format_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_5 Format", rx_cdc_dma_rx_5_format,
+			cdc_dma_rx_format_get, cdc_dma_rx_format_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_0 SampleRate",
+			rx_cdc_dma_rx_0_sample_rate,
+			cdc_dma_rx_sample_rate_get,
+			cdc_dma_rx_sample_rate_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_1 SampleRate",
+			rx_cdc_dma_rx_1_sample_rate,
+			cdc_dma_rx_sample_rate_get,
+			cdc_dma_rx_sample_rate_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_2 SampleRate",
+			rx_cdc_dma_rx_2_sample_rate,
+			cdc_dma_rx_sample_rate_get,
+			cdc_dma_rx_sample_rate_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_3 SampleRate",
+			rx_cdc_dma_rx_3_sample_rate,
+			cdc_dma_rx_sample_rate_get,
+			cdc_dma_rx_sample_rate_put),
+	SOC_ENUM_EXT("RX_CDC_DMA_RX_5 SampleRate",
+			rx_cdc_dma_rx_5_sample_rate,
+			cdc_dma_rx_sample_rate_get,
+			cdc_dma_rx_sample_rate_put),
+};
+
 static const struct snd_kcontrol_new msm_common_snd_controls[] = {
 	SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
 			usb_audio_rx_sample_rate_get,
@@ -5371,6 +5423,9 @@
 	struct snd_card *card;
 	struct snd_info_entry *entry;
 	struct snd_soc_component *aux_comp;
+	struct platform_device *pdev = NULL;
+	int i = 0;
+	char *data = NULL;
 	struct msm_asoc_mach_data *pdata =
 				snd_soc_card_get_drvdata(rtd->card);
 
@@ -5447,18 +5502,52 @@
 						WSA_MACRO_GAIN_OFFSET_M1P5_DB);
 			}
 		}
-		if (pdata->lito_v2_enabled) {
-			/*
-			 * Enable tx data line3 for saipan version v2 amd
-			 * write corresponding lpi register.
-			 */
-			bolero_set_port_map(component, ARRAY_SIZE(sm_port_map_v2),
-					sm_port_map_v2);
-		} else {
-			bolero_set_port_map(component, ARRAY_SIZE(sm_port_map),
-					sm_port_map);
+	}
+
+	for (i = 0; i < rtd->card->num_aux_devs; i++)
+	{
+		if (msm_aux_dev[i].name != NULL ) {
+			if (strstr(msm_aux_dev[i].name, "wsa"))
+				continue;
+		}
+
+		if (msm_aux_dev[i].codec_of_node) {
+			pdev = of_find_device_by_node(
+					msm_aux_dev[i].codec_of_node);
+
+			if (pdev)
+				data = (char*) of_device_get_match_data(
+								&pdev->dev);
+			if (data != NULL) {
+				if (!strncmp(data, "wcd937x",
+						sizeof("wcd937x"))) {
+					bolero_set_port_map(component,
+						ARRAY_SIZE(sm_port_map_wcd937x),
+						sm_port_map_wcd937x);
+					break;
+				} else if (!strncmp( data, "wcd938x",
+							sizeof("wcd938x"))) {
+					if (pdata->lito_v2_enabled) {
+						/*
+						 * Enable tx data line3 for
+						 * saipan version v2 and
+						 * write corresponding
+						 * lpi register.
+						 */
+						bolero_set_port_map(component,
+							ARRAY_SIZE(sm_port_map_v2),
+							sm_port_map_v2);
+					} else {
+						bolero_set_port_map(component,
+							ARRAY_SIZE(sm_port_map),
+							sm_port_map);
+					}
+					break;
+				}
+			}
 		}
 	}
+
 	card = rtd->card->snd_card;
 	if (!pdata->codec_root) {
 		entry = snd_info_create_subdir(card->module, "codecs",
@@ -7659,18 +7748,24 @@
 		}
 		pdata->codec_root = entry;
 	}
-	wcd938x_info_create_codec_entry(pdata->codec_root, component);
-
-	codec_variant = wcd938x_get_codec_variant(component);
-	dev_dbg(component->dev, "%s: variant %d\n", __func__, codec_variant);
-	if (codec_variant == WCD9380)
+	if (!strncmp(component->driver->name, "wcd937x", 7)) {
+		wcd937x_info_create_codec_entry(pdata->codec_root, component);
 		ret = snd_soc_add_component_controls(component,
-					msm_int_wcd9380_snd_controls,
-					ARRAY_SIZE(msm_int_wcd9380_snd_controls));
-	else if (codec_variant == WCD9385)
-		ret = snd_soc_add_component_controls(component,
-					msm_int_wcd9385_snd_controls,
-					ARRAY_SIZE(msm_int_wcd9385_snd_controls));
+					msm_int_wcd937x_snd_controls,
+					ARRAY_SIZE(msm_int_wcd937x_snd_controls));
+	} else {
+		wcd938x_info_create_codec_entry(pdata->codec_root, component);
+		codec_variant = wcd938x_get_codec_variant(component);
+		dev_dbg(component->dev, "%s: variant %d\n", __func__, codec_variant);
+		if (codec_variant == WCD9380)
+			ret = snd_soc_add_component_controls(component,
+						msm_int_wcd9380_snd_controls,
+						ARRAY_SIZE(msm_int_wcd9380_snd_controls));
+		else if (codec_variant == WCD9385)
+			ret = snd_soc_add_component_controls(component,
+						msm_int_wcd9385_snd_controls,
+						ARRAY_SIZE(msm_int_wcd9385_snd_controls));
+	}
 
 	if (ret < 0) {
 		dev_err(component->dev, "%s: add codec specific snd controls failed: %d\n",
@@ -7683,7 +7778,10 @@
 	if (!mbhc_calibration)
 		return -ENOMEM;
 	wcd_mbhc_cfg.calibration = mbhc_calibration;
-	ret = wcd938x_mbhc_hs_detect(component, &wcd_mbhc_cfg);
+	if (!strncmp(component->driver->name, "wcd937x", 7))
+		ret = wcd937x_mbhc_hs_detect(component, &wcd_mbhc_cfg);
+	else
+		ret = wcd938x_mbhc_hs_detect(component, &wcd_mbhc_cfg);
 	if (ret) {
 		dev_err(component->dev, "%s: mbhc hs detect failed, err:%d\n",
 			__func__, ret);
@@ -7798,7 +7896,7 @@
 			ret = -EINVAL;
 			goto err;
 		}
-		if (soc_find_component(wsa_of_node, NULL)) {
+		if (soc_find_component_locked(wsa_of_node, NULL)) {
 			/* WSA device registered with ALSA core */
 			wsa881x_dev_info[found].of_node = wsa_of_node;
 			wsa881x_dev_info[found].index = i;
@@ -7895,7 +7993,7 @@
 			ret = -EINVAL;
 			goto err;
 		}
-		if (soc_find_component(aux_codec_of_node, NULL)) {
+		if (soc_find_component_locked(aux_codec_of_node, NULL)) {
 			/* AUX codec registered with ALSA core */
 			aux_cdc_dev_info[codecs_found].of_node =
 						aux_codec_of_node;
diff --git a/dsp/audio_cal_utils.c b/dsp/audio_cal_utils.c
index fab5186..8e0d7e0 100644
--- a/dsp/audio_cal_utils.c
+++ b/dsp/audio_cal_utils.c
@@ -109,6 +109,9 @@
 		size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg),
 			   sizeof(struct audio_cal_info_sp_ex_vi_param));
 		break;
+	case AFE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_sp_v4_ex_vi_param);
+		break;
 	case AFE_ANC_CAL_TYPE:
 		size = 0;
 		break;
@@ -261,6 +264,9 @@
 		size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg),
 			   sizeof(struct audio_cal_type_sp_ex_vi_param));
 		break;
+	case AFE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_sp_v4_ex_vi_param);
+		break;
 	case AFE_ANC_CAL_TYPE:
 		size = 0;
 		break;
diff --git a/dsp/audio_notifier.c b/dsp/audio_notifier.c
index 56cfa31..7a8249d 100644
--- a/dsp/audio_notifier.c
+++ b/dsp/audio_notifier.c
@@ -606,6 +606,7 @@
 	return 0;
 }
 
+#ifdef CONFIG_MSM_QDSP6_PDR
 static int __init audio_notifier_init(void)
 {
 	int ret;
@@ -624,6 +625,17 @@
 
 	return 0;
 }
+#else
+static int __init audio_notifier_init(void)
+{
+	audio_notifier_subsys_init();
+	audio_notifier_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
+
+	audio_notifier_late_init();
+
+	return 0;
+}
+#endif
 module_init(audio_notifier_init);
 
 static void __exit audio_notifier_exit(void)
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 567f7fe..3d76e48 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -54,6 +54,7 @@
 	AFE_CUST_TOPOLOGY_CAL,
 	AFE_FB_SPKR_PROT_TH_VI_CAL,
 	AFE_FB_SPKR_PROT_EX_VI_CAL,
+	AFE_FB_SPKR_PROT_V4_EX_VI_CAL,
 	MAX_AFE_CAL_TYPES
 };
 
@@ -113,6 +114,11 @@
 	AFE_MATCHED_PORT_ENABLE
 };
 
+enum {
+	AFE_FBSP_V4_EX_VI_MODE_NORMAL = 0,
+	AFE_FBSP_V4_EX_VI_MODE_FTM = 1
+};
+
 struct wlock {
 	struct wakeup_source *ws;
 };
@@ -166,6 +172,10 @@
 	struct afe_sp_th_vi_v_vali_get_param_resp	th_vi_v_vali_resp;
 	struct afe_sp_ex_vi_get_param_resp	ex_vi_resp;
 	struct afe_sp_rx_tmax_xmax_logging_resp	xt_logging_resp;
+	struct afe_sp_v4_th_vi_calib_resp spv4_calib_data;
+	struct afe_sp_v4_param_vi_channel_map_cfg v4_ch_map_cfg;
+	struct afe_sp_v4_gen_get_param_resp *spv4_get_param_resp_ptr;
+	uint32_t spv4_rcvd_param_size;
 	struct afe_av_dev_drift_get_param_resp	av_dev_drift_resp;
 	struct afe_doa_tracking_mon_get_param_resp	doa_tracking_mon_resp;
 	int vi_tx_port;
@@ -578,6 +588,7 @@
 	u32 *data_dest = NULL;
 	u32 *data_start = NULL;
 	size_t expected_size = sizeof(u32);
+	uint32_t num_ch = 0;
 
 	memset(&param_hdr, 0, sizeof(param_hdr));
 
@@ -638,16 +649,79 @@
 				struct afe_sp_rx_tmax_xmax_logging_param);
 		data_dest = (u32 *) &this_afe.xt_logging_resp;
 		break;
+	case AFE_PARAM_ID_SP_V4_CALIB_RES_CFG:
+		expected_size += sizeof(
+				struct afe_sp_v4_param_th_vi_calib_res_cfg);
+		data_dest = (u32 *) &this_afe.spv4_calib_data;
+		break;
+	case AFE_PARAM_ID_SP_V4_TH_VI_FTM_PARAMS:
+		num_ch = data_start[0];
+		this_afe.spv4_rcvd_param_size =
+			sizeof(struct afe_sp_v4_gen_get_param_resp) +
+			sizeof(struct afe_sp_v4_param_th_vi_ftm_params) +
+			(num_ch * sizeof(struct afe_sp_v4_channel_ftm_params));
+		this_afe.spv4_get_param_resp_ptr =
+			 kzalloc(this_afe.spv4_rcvd_param_size, GFP_ATOMIC);
+		data_dest = (u32 *)this_afe.spv4_get_param_resp_ptr;
+		expected_size +=
+			sizeof(struct afe_sp_v4_param_th_vi_ftm_params) +
+			(num_ch * sizeof(struct afe_sp_v4_channel_ftm_params));
+		break;
+	case AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_PARAMS:
+		num_ch = data_start[0];
+		this_afe.spv4_rcvd_param_size =
+			sizeof(struct afe_sp_v4_gen_get_param_resp) +
+			sizeof(struct afe_sp_v4_param_th_vi_v_vali_params) +
+			(num_ch *
+			sizeof(struct afe_sp_v4_channel_v_vali_params));
+		this_afe.spv4_get_param_resp_ptr =
+			 kzalloc(this_afe.spv4_rcvd_param_size, GFP_ATOMIC);
+		data_dest = (u32 *)this_afe.spv4_get_param_resp_ptr;
+		expected_size +=
+			sizeof(struct afe_sp_v4_param_th_vi_v_vali_params) +
+			(num_ch *
+			sizeof(struct afe_sp_v4_channel_v_vali_params));
+		break;
+	case AFE_PARAM_ID_SP_V4_EX_VI_FTM_PARAMS:
+		num_ch = data_start[0];
+		this_afe.spv4_rcvd_param_size =
+		sizeof(struct afe_sp_v4_gen_get_param_resp) +
+		sizeof(struct afe_sp_v4_param_ex_vi_ftm_params) +
+		(num_ch * sizeof(struct afe_sp_v4_channel_ex_vi_ftm_params));
+		this_afe.spv4_get_param_resp_ptr =
+			kzalloc(this_afe.spv4_rcvd_param_size, GFP_ATOMIC);
+		data_dest = (u32 *)this_afe.spv4_get_param_resp_ptr;
+		expected_size +=
+		sizeof(struct afe_sp_v4_param_ex_vi_ftm_params) +
+		(num_ch * sizeof(struct afe_sp_v4_channel_ex_vi_ftm_params));
+		break;
+	case AFE_PARAM_ID_SP_V4_RX_TMAX_XMAX_LOGGING:
+		num_ch = data_start[0];
+		this_afe.spv4_rcvd_param_size =
+		sizeof(struct afe_sp_v4_gen_get_param_resp) +
+		sizeof(struct afe_sp_v4_param_tmax_xmax_logging) +
+		(num_ch * sizeof(struct afe_sp_v4_channel_tmax_xmax_params));
+		this_afe.spv4_get_param_resp_ptr =
+			kzalloc(this_afe.spv4_rcvd_param_size, GFP_ATOMIC);
+		data_dest = (u32 *)this_afe.spv4_get_param_resp_ptr;
+		expected_size +=
+		sizeof(struct afe_sp_v4_param_tmax_xmax_logging) +
+		(num_ch * sizeof(struct afe_sp_v4_channel_tmax_xmax_params));
+		break;
 	default:
 		pr_err("%s: Unrecognized param ID %d\n", __func__,
 		       param_hdr.param_id);
 		return -EINVAL;
 	}
 
+	if (!data_dest)
+		return -ENOMEM;
+
 	if (payload_size < expected_size) {
-		pr_err("%s: Error: received size %d, expected size %zu for param %d\n",
-		       __func__, payload_size, expected_size,
-		       param_hdr.param_id);
+		pr_err(
+		"%s: Error: received size %d, expected size %zu for param %d\n",
+		__func__, payload_size, expected_size,
+		param_hdr.param_id);
 		return -EINVAL;
 	}
 
@@ -1012,6 +1086,22 @@
 					flag_dc_presence[1] == 1) {
 					afe_notify_dc_presence();
 				}
+			} else if ((evt_pl->module_id ==
+					 AFE_MODULE_SPEAKER_PROTECTION_V4_VI) &&
+				(evt_pl->event_id ==
+					 AFE_PORT_SP_DC_DETECTION_EVENT)) {
+				bool dc_detected = false;
+				uint32_t *num_channels =
+				    (uint32_t *)((uint8_t *)payload +
+				    sizeof(struct afe_port_mod_evt_rsp_hdr));
+				uint32_t *dc_presence_flag = num_channels + 1;
+
+				for (i = 0; i < *num_channels; i++) {
+					if (dc_presence_flag[i] == 1)
+						dc_detected = true;
+				}
+				if (dc_detected)
+					afe_notify_dc_presence();
 			} else if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) {
 				if (this_afe.pri_spdif_tx_cb) {
 					this_afe.pri_spdif_tx_cb(data->opcode,
@@ -2056,7 +2146,7 @@
 }
 
 static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id,
-				union afe_spkr_prot_config *prot_config)
+		union afe_spkr_prot_config *prot_config, uint32_t param_size)
 {
 	struct param_hdr_v3 param_info;
 	int ret = -EINVAL;
@@ -2083,6 +2173,9 @@
 	case AFE_PARAM_ID_SP_RX_LIMITER_TH:
 		param_info.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
 		break;
+	case AFE_PARAM_ID_SP_V4_OP_MODE:
+		param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_RX;
+		break;
 	case AFE_PARAM_ID_FEEDBACK_PATH_CFG:
 		this_afe.vi_tx_port = src_port;
 		this_afe.vi_rx_port = dst_port;
@@ -2102,6 +2195,15 @@
 	case AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG:
 		param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
 		break;
+	case AFE_PARAM_ID_SP_V4_VI_CHANNEL_MAP_CFG:
+	case AFE_PARAM_ID_SP_V4_VI_OP_MODE_CFG:
+	case AFE_PARAM_ID_SP_V4_VI_R0T0_CFG:
+	case AFE_PARAM_ID_SP_V4_TH_VI_FTM_CFG:
+	case AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_CFG:
+	case AFE_PARAM_ID_SP_V4_EX_VI_MODE_CFG:
+	case AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG:
+		param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI;
+		break;
 	default:
 		pr_err("%s: default case 0x%x\n", __func__, param_id);
 		goto fail_cmd;
@@ -2109,7 +2211,7 @@
 
 	param_info.instance_id = INSTANCE_ID_0;
 	param_info.param_id = param_id;
-	param_info.param_size = sizeof(union afe_spkr_prot_config);
+	param_info.param_size = param_size;
 
 	ret = q6afe_pack_and_set_param_in_band(src_port,
 					       q6audio_get_port_index(src_port),
@@ -2124,7 +2226,7 @@
 	return ret;
 }
 
-static int afe_spkr_prot_reg_event_cfg(u16 port_id)
+static int afe_spkr_prot_reg_event_cfg(u16 port_id, uint32_t module_id)
 {
 	struct afe_port_cmd_event_cfg *config;
 	struct afe_port_cmd_mod_evt_cfg_payload pl;
@@ -2146,7 +2248,7 @@
 	}
 
 	memset(&pl, 0, sizeof(pl));
-	pl.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+	pl.module_id = module_id;
 	pl.event_id = AFE_PORT_SP_DC_DETECTION_EVENT;
 	pl.reg_flag = AFE_MODULE_REGISTER_EVENT_FLAG;
 
@@ -2170,10 +2272,260 @@
 	return ret;
 }
 
+static void afe_send_cal_spv4_tx(int port_id)
+{
+	union afe_spkr_prot_config afe_spk_config;
+	uint32_t size = 0;
+	void *tmp_ptr = NULL;
+	struct afe_sp_v4_param_th_vi_r0t0_cfg *th_vi_r0t0_cfg;
+	struct afe_sp_v4_channel_r0t0 *ch_r0t0_cfg;
+	struct afe_sp_v4_param_th_vi_ftm_cfg *th_vi_ftm_cfg;
+	struct afe_sp_v4_channel_ftm_cfg *ch_ftm_cfg;
+	struct afe_sp_v4_param_th_vi_v_vali_cfg *th_vi_v_vali_cfg;
+	struct afe_sp_v4_channel_v_vali_cfg *ch_v_vali_cfg;
+	struct afe_sp_v4_param_ex_vi_ftm_cfg *ex_vi_ftm_cfg;
+	struct afe_sp_v4_channel_ex_vi_ftm *ch_ex_vi_ftm_cfg;
+
+	pr_debug("%s: Entry.. port_id %d\n", __func__, port_id);
+
+	if (this_afe.vi_tx_port == port_id) {
+		memcpy(&afe_spk_config.v4_ch_map_cfg, &this_afe.v4_ch_map_cfg,
+			sizeof(struct afe_sp_v4_param_vi_channel_map_cfg));
+		if (afe_spk_prot_prepare(port_id, this_afe.vi_rx_port,
+			AFE_PARAM_ID_SP_V4_VI_CHANNEL_MAP_CFG, &afe_spk_config,
+			sizeof(struct afe_sp_v4_param_vi_channel_map_cfg)))
+			pr_info("%s: SPKR_CALIB_CHANNEL_MAP_CFG failed\n",
+				 __func__);
+	}
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL ||
+	    this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+	    this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL) {
+		pr_info("%s: Returning as no cal data cached\n", __func__);
+		return;
+	}
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	if ((this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED) &&
+		(this_afe.vi_tx_port == port_id) &&
+		(this_afe.prot_cfg.sp_version >= AFE_API_VERSION_V9)) {
+
+		if (this_afe.prot_cfg.mode ==
+			MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
+			afe_spk_config.v4_vi_op_mode.th_operation_mode =
+					Q6AFE_MSM_SPKR_CALIBRATION;
+			afe_spk_config.v4_vi_op_mode.th_quick_calib_flag =
+					this_afe.prot_cfg.quick_calib_flag;
+		} else {
+			afe_spk_config.v4_vi_op_mode.th_operation_mode =
+					Q6AFE_MSM_SPKR_PROCESSING;
+		}
+
+		if (this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE)
+			afe_spk_config.v4_vi_op_mode.th_operation_mode =
+					    Q6AFE_MSM_SPKR_FTM_MODE;
+		else if (this_afe.v_vali_cfg.mode ==
+					MSM_SPKR_PROT_IN_V_VALI_MODE)
+			afe_spk_config.v4_vi_op_mode.th_operation_mode =
+					    Q6AFE_MSM_SPKR_V_VALI_MODE;
+		if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) {
+			struct afe_sp_v4_param_vi_op_mode_cfg *v4_vi_op_mode;
+
+			v4_vi_op_mode = &afe_spk_config.v4_vi_op_mode;
+			v4_vi_op_mode->th_r0t0_selection_flag[SP_V2_SPKR_1] =
+					    USE_CALIBRATED_R0TO;
+			v4_vi_op_mode->th_r0t0_selection_flag[SP_V2_SPKR_2] =
+					    USE_CALIBRATED_R0TO;
+		} else {
+			struct afe_sp_v4_param_vi_op_mode_cfg *v4_vi_op_mode;
+
+			v4_vi_op_mode = &afe_spk_config.v4_vi_op_mode;
+			v4_vi_op_mode->th_r0t0_selection_flag[SP_V2_SPKR_1] =
+							    USE_SAFE_R0TO;
+			v4_vi_op_mode->th_r0t0_selection_flag[SP_V2_SPKR_2] =
+							    USE_SAFE_R0TO;
+		}
+		afe_spk_config.v4_vi_op_mode.num_speakers = SP_V2_NUM_MAX_SPKRS;
+		if (afe_spk_prot_prepare(port_id, 0,
+			AFE_PARAM_ID_SP_V4_VI_OP_MODE_CFG,
+			&afe_spk_config,
+			sizeof(struct afe_sp_v4_param_vi_op_mode_cfg)))
+			pr_info("%s: SPKR_CALIB_VI_PROC_CFG failed\n",
+				__func__);
+
+		size = sizeof(struct afe_sp_v4_param_th_vi_r0t0_cfg) +
+		(SP_V2_NUM_MAX_SPKRS * sizeof(struct afe_sp_v4_channel_r0t0));
+		tmp_ptr = kzalloc(size, GFP_KERNEL);
+		if (!tmp_ptr) {
+			mutex_unlock(
+				&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+			return;
+		}
+		memset(tmp_ptr, 0, size);
+
+		th_vi_r0t0_cfg =
+			(struct afe_sp_v4_param_th_vi_r0t0_cfg *)tmp_ptr;
+		ch_r0t0_cfg =
+			(struct afe_sp_v4_channel_r0t0 *)(th_vi_r0t0_cfg + 1);
+
+		th_vi_r0t0_cfg->num_speakers = SP_V2_NUM_MAX_SPKRS;
+		ch_r0t0_cfg[SP_V2_SPKR_1].r0_cali_q24 =
+			(uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_1];
+		ch_r0t0_cfg[SP_V2_SPKR_2].r0_cali_q24 =
+			(uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_2];
+		ch_r0t0_cfg[SP_V2_SPKR_1].t0_cali_q6 =
+			(uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_1];
+		ch_r0t0_cfg[SP_V2_SPKR_2].t0_cali_q6 =
+			(uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_2];
+		if (afe_spk_prot_prepare(port_id, 0,
+			AFE_PARAM_ID_SP_V4_VI_R0T0_CFG,
+			(union afe_spkr_prot_config *)tmp_ptr, size))
+			pr_info("%s: th vi ftm cfg failed\n", __func__);
+
+		kfree(tmp_ptr);
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+	if ((this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+	    (this_afe.vi_tx_port == port_id) &&
+	    (this_afe.prot_cfg.sp_version >= AFE_API_VERSION_V9)) {
+		size = sizeof(struct afe_sp_v4_param_th_vi_ftm_cfg) +
+		(SP_V2_NUM_MAX_SPKRS*sizeof(struct afe_sp_v4_channel_ftm_cfg));
+		tmp_ptr = kzalloc(size, GFP_KERNEL);
+		if (!tmp_ptr) {
+			mutex_unlock(
+			  &this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+			return;
+		}
+		memset(tmp_ptr, 0, size);
+
+		th_vi_ftm_cfg = (struct afe_sp_v4_param_th_vi_ftm_cfg *)tmp_ptr;
+		ch_ftm_cfg =
+			 (struct afe_sp_v4_channel_ftm_cfg *)(th_vi_ftm_cfg+1);
+
+		th_vi_ftm_cfg->num_ch = SP_V2_NUM_MAX_SPKRS;
+		ch_ftm_cfg[SP_V2_SPKR_1].wait_time_ms =
+			this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_1];
+		ch_ftm_cfg[SP_V2_SPKR_2].wait_time_ms =
+			this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_2];
+		ch_ftm_cfg[SP_V2_SPKR_1].ftm_time_ms =
+			this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+		ch_ftm_cfg[SP_V2_SPKR_2].ftm_time_ms =
+			this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+		if (afe_spk_prot_prepare(port_id, 0,
+				AFE_PARAM_ID_SP_V4_TH_VI_FTM_CFG,
+				(union afe_spkr_prot_config *)tmp_ptr, size))
+			pr_info("%s: th vi ftm cfg failed\n", __func__);
+
+		kfree(tmp_ptr);
+		this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	} else if ((this_afe.v_vali_cfg.mode ==
+			MSM_SPKR_PROT_IN_V_VALI_MODE) &&
+		   (this_afe.vi_tx_port == port_id)) {
+		size = sizeof(struct afe_sp_v4_param_th_vi_v_vali_cfg) +
+			(SP_V2_NUM_MAX_SPKRS *
+			 sizeof(struct afe_sp_v4_channel_v_vali_cfg));
+		tmp_ptr = kzalloc(size, GFP_KERNEL);
+		if (!tmp_ptr) {
+			mutex_unlock(
+			  &this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+			return;
+		}
+		memset(tmp_ptr, 0, size);
+
+		th_vi_v_vali_cfg =
+		 (struct afe_sp_v4_param_th_vi_v_vali_cfg *)tmp_ptr;
+		ch_v_vali_cfg =
+		 (struct afe_sp_v4_channel_v_vali_cfg *)(th_vi_v_vali_cfg + 1);
+
+		th_vi_v_vali_cfg->num_ch = SP_V2_NUM_MAX_SPKRS;
+		ch_v_vali_cfg[SP_V2_SPKR_1].wait_time_ms =
+			this_afe.v_vali_cfg.wait_time[SP_V2_SPKR_1];
+		ch_v_vali_cfg[SP_V2_SPKR_2].wait_time_ms =
+			this_afe.v_vali_cfg.wait_time[SP_V2_SPKR_2];
+		ch_v_vali_cfg[SP_V2_SPKR_1].vali_time_ms =
+			this_afe.v_vali_cfg.vali_time[SP_V2_SPKR_1];
+		ch_v_vali_cfg[SP_V2_SPKR_2].vali_time_ms =
+			this_afe.v_vali_cfg.vali_time[SP_V2_SPKR_2];
+
+		if (afe_spk_prot_prepare(port_id, 0,
+				AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_CFG,
+				(union afe_spkr_prot_config *)tmp_ptr, size))
+			pr_info("%s: th vi v-vali cfg failed\n", __func__);
+
+		kfree(tmp_ptr);
+		this_afe.v_vali_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+	if ((this_afe.ex_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+	    (this_afe.vi_tx_port == port_id) &&
+	    (this_afe.prot_cfg.sp_version >= AFE_API_VERSION_V9)) {
+		size = sizeof(struct afe_sp_v4_param_ex_vi_ftm_cfg) +
+		(SP_V2_NUM_MAX_SPKRS *
+		 sizeof(struct afe_sp_v4_channel_ex_vi_ftm));
+		tmp_ptr = kzalloc(size, GFP_KERNEL);
+		if (!tmp_ptr) {
+			mutex_unlock(
+			  &this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+			return;
+		}
+		memset(tmp_ptr, 0, size);
+
+		ex_vi_ftm_cfg = (struct afe_sp_v4_param_ex_vi_ftm_cfg *)tmp_ptr;
+		ch_ex_vi_ftm_cfg =
+		(struct afe_sp_v4_channel_ex_vi_ftm *)(ex_vi_ftm_cfg + 1);
+
+		afe_spk_config.v4_ex_vi_mode_cfg.operation_mode =
+						AFE_FBSP_V4_EX_VI_MODE_FTM;
+		if (afe_spk_prot_prepare(port_id, 0,
+				 AFE_PARAM_ID_SP_V4_EX_VI_MODE_CFG,
+				 &afe_spk_config,
+				 sizeof(struct afe_sp_v4_param_ex_vi_mode_cfg)))
+			pr_info("%s: ex vi mode cfg failed\n", __func__);
+
+		ex_vi_ftm_cfg->num_ch = SP_V2_NUM_MAX_SPKRS;
+
+		ch_ex_vi_ftm_cfg[SP_V2_SPKR_1].wait_time_ms =
+			this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_1];
+		ch_ex_vi_ftm_cfg[SP_V2_SPKR_2].wait_time_ms =
+			this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_2];
+		ch_ex_vi_ftm_cfg[SP_V2_SPKR_1].ftm_time_ms =
+			this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+		ch_ex_vi_ftm_cfg[SP_V2_SPKR_2].ftm_time_ms =
+			this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+		if (afe_spk_prot_prepare(port_id, 0,
+				 AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG,
+				 (union afe_spkr_prot_config *)tmp_ptr, size))
+			pr_info("%s: ex vi ftm cfg failed\n", __func__);
+		kfree(tmp_ptr);
+		this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+
+	/* Register for DC detection event if speaker protection is enabled */
+	if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED &&
+		(this_afe.vi_tx_port == port_id)) {
+		afe_spkr_prot_reg_event_cfg(port_id,
+					AFE_MODULE_SPEAKER_PROTECTION_V4_VI);
+	}
+
+}
+
 static void afe_send_cal_spkr_prot_tx(int port_id)
 {
 	union afe_spkr_prot_config afe_spk_config;
 
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		afe_send_cal_spv4_tx(port_id);
+		return;
+	}
+
 	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL ||
 	    this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
 	    this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL)
@@ -2228,7 +2580,7 @@
 		}
 		if (afe_spk_prot_prepare(port_id, 0,
 			AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2,
-				&afe_spk_config))
+			&afe_spk_config, sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: SPKR_CALIB_VI_PROC_CFG failed\n",
 				__func__);
 	}
@@ -2248,8 +2600,9 @@
 			this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2];
 
 		if (afe_spk_prot_prepare(port_id, 0,
-					 AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG,
-					 &afe_spk_config))
+				 AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG,
+				 &afe_spk_config,
+				 sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: th vi ftm cfg failed\n", __func__);
 		this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
 	} else if ((this_afe.v_vali_cfg.mode ==
@@ -2267,7 +2620,8 @@
 
 		if (afe_spk_prot_prepare(port_id, 0,
 					 AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_CFG,
-					 &afe_spk_config))
+					 &afe_spk_config,
+					 sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: th vi v-vali cfg failed\n", __func__);
 
 		this_afe.v_vali_cfg.mode = MSM_SPKR_PROT_DISABLED;
@@ -2282,7 +2636,8 @@
 						Q6AFE_MSM_SPKR_FTM_MODE;
 		if (afe_spk_prot_prepare(port_id, 0,
 					 AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG,
-					 &afe_spk_config))
+					 &afe_spk_config,
+					 sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: ex vi mode cfg failed\n", __func__);
 
 		afe_spk_config.ex_vi_ftm_cfg.minor_version = 1;
@@ -2297,7 +2652,8 @@
 
 		if (afe_spk_prot_prepare(port_id, 0,
 					 AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG,
-					 &afe_spk_config))
+					 &afe_spk_config,
+					 sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: ex vi ftm cfg failed\n", __func__);
 		this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
 	}
@@ -2306,15 +2662,50 @@
 	/* Register for DC detection event if speaker protection is enabled */
 	if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED &&
 		(this_afe.vi_tx_port == port_id)) {
-		afe_spkr_prot_reg_event_cfg(port_id);
+		afe_spkr_prot_reg_event_cfg(port_id,
+				AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI);
 	}
 }
 
+static void afe_send_cal_spv4_rx(int port_id)
+{
+
+	union afe_spkr_prot_config afe_spk_config;
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+		return;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED &&
+		(this_afe.vi_rx_port == port_id) &&
+		(this_afe.prot_cfg.sp_version >= AFE_API_VERSION_V9)) {
+		if (this_afe.prot_cfg.mode ==
+			MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+			afe_spk_config.v4_op_mode.mode =
+				Q6AFE_MSM_SPKR_CALIBRATION;
+		else
+			afe_spk_config.v4_op_mode.mode =
+				Q6AFE_MSM_SPKR_PROCESSING;
+		if (afe_spk_prot_prepare(port_id, 0,
+			AFE_PARAM_ID_SP_V4_OP_MODE,
+			&afe_spk_config, sizeof(union afe_spkr_prot_config)))
+			pr_info("%s: RX MODE_VI_PROC_CFG failed\n",
+				   __func__);
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+}
+
 static void afe_send_cal_spkr_prot_rx(int port_id)
 {
 	union afe_spkr_prot_config afe_spk_config;
 	union afe_spkr_prot_config afe_spk_limiter_config;
 
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		afe_send_cal_spv4_rx(port_id);
+		return;
+	}
+
 	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
 		goto done;
 
@@ -2332,9 +2723,9 @@
 		afe_spk_config.mode_rx_cfg.minor_version = 1;
 		if (afe_spk_prot_prepare(port_id, 0,
 			AFE_PARAM_ID_FBSP_MODE_RX_CFG,
-			&afe_spk_config))
+			&afe_spk_config, sizeof(union afe_spkr_prot_config)))
 			pr_err("%s: RX MODE_VI_PROC_CFG failed\n",
-				   __func__);
+					   __func__);
 
 		if (afe_spk_config.mode_rx_cfg.mode ==
 			Q6AFE_MSM_SPKR_PROCESSING) {
@@ -2350,7 +2741,8 @@
 				this_afe.prot_cfg.limiter_th[SP_V2_SPKR_2];
 				if (afe_spk_prot_prepare(port_id, 0,
 					AFE_PARAM_ID_SP_RX_LIMITER_TH,
-					&afe_spk_limiter_config))
+					&afe_spk_limiter_config,
+					sizeof(union afe_spkr_prot_config)))
 					pr_err("%s: SP_RX_LIMITER_TH failed.\n",
 						__func__);
 			} else {
@@ -8519,6 +8911,62 @@
 }
 EXPORT_SYMBOL(q6afe_check_osr_clk_freq);
 
+static int afe_get_spv4_th_vi_v_vali_data(void *params, uint32_t size)
+{
+	struct param_hdr_v3 param_hdr;
+	int port = SLIMBUS_4_TX;
+	int ret = -EINVAL;
+	u8 *rcvd_params = NULL;
+	struct afe_sp_v4_channel_v_vali_params *v_vali_params;
+
+	if (!params) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	memset(&param_hdr, 0, sizeof(param_hdr));
+
+	param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_PARAMS;
+	param_hdr.param_size = size;
+
+	ret = q6afe_get_params(port, NULL, &param_hdr);
+	if (ret) {
+		pr_err("%s: Failed to get TH VI V-Vali data\n", __func__);
+		goto get_params_fail;
+	}
+
+	rcvd_params = (u8 *)this_afe.spv4_get_param_resp_ptr +
+				 sizeof(struct afe_sp_v4_gen_get_param_resp);
+
+	memcpy(params, rcvd_params, this_afe.spv4_rcvd_param_size);
+
+	v_vali_params = (struct afe_sp_v4_channel_v_vali_params *)
+		(params + sizeof(struct afe_sp_v4_param_th_vi_v_vali_params));
+	pr_debug("%s:  Vrms %d %d status %d %d\n", __func__,
+		v_vali_params[SP_V2_SPKR_1].vrms_q24,
+		v_vali_params[SP_V2_SPKR_2].vrms_q24,
+		v_vali_params[SP_V2_SPKR_1].status,
+		v_vali_params[SP_V2_SPKR_2].status);
+
+	/*using the non-spv4 status varaible to support v_vali debug app. */
+	this_afe.th_vi_v_vali_resp.param.status[SP_V2_SPKR_1] =
+		 v_vali_params[SP_V2_SPKR_1].status;
+	this_afe.th_vi_v_vali_resp.param.status[SP_V2_SPKR_2] =
+		 v_vali_params[SP_V2_SPKR_2].status;
+
+	ret = 0;
+get_params_fail:
+	kfree(this_afe.spv4_get_param_resp_ptr);
+	mutex_unlock(&this_afe.afe_cmd_lock);
+done:
+	return ret;
+}
+
 static int afe_get_sp_th_vi_v_vali_data(
 		struct afe_sp_th_vi_v_vali_get_param *th_vi_v_vali)
 {
@@ -8562,7 +9010,57 @@
 	return ret;
 }
 
-int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
+static int afe_get_spv4_th_vi_ftm_data(void *params, uint32_t size)
+{
+	struct param_hdr_v3 param_hdr;
+	int port = SLIMBUS_4_TX;
+	int ret = -EINVAL;
+	u8 *rcvd_params = NULL;
+	struct afe_sp_v4_channel_ftm_params *th_vi_params;
+
+	if (!params) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	memset(&param_hdr, 0, sizeof(param_hdr));
+
+	param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_SP_V4_TH_VI_FTM_PARAMS;
+	param_hdr.param_size = size;
+
+	ret = q6afe_get_params(port, NULL, &param_hdr);
+	if (ret) {
+		pr_err("%s: Failed to get TH VI FTM data\n", __func__);
+		goto get_params_fail;
+	}
+
+	rcvd_params = (u8 *)this_afe.spv4_get_param_resp_ptr +
+				 sizeof(struct afe_sp_v4_gen_get_param_resp);
+	memcpy(params, rcvd_params,  this_afe.spv4_rcvd_param_size);
+
+	th_vi_params = (struct afe_sp_v4_channel_ftm_params *)
+		(params + sizeof(struct afe_sp_v4_param_th_vi_ftm_params));
+	pr_debug("%s: DC resistance %d %d temp %d %d status %d %d\n",
+		 __func__, th_vi_params[SP_V2_SPKR_1].dc_res_q24,
+		 th_vi_params[SP_V2_SPKR_2].dc_res_q24,
+		 th_vi_params[SP_V2_SPKR_1].temp_q22,
+		 th_vi_params[SP_V2_SPKR_2].temp_q22,
+		 th_vi_params[SP_V2_SPKR_1].status,
+		 th_vi_params[SP_V2_SPKR_2].status);
+	ret = 0;
+get_params_fail:
+	kfree(this_afe.spv4_get_param_resp_ptr);
+	mutex_unlock(&this_afe.afe_cmd_lock);
+done:
+	return ret;
+}
+
+static int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
 {
 	struct param_hdr_v3 param_hdr;
 	int port = SLIMBUS_4_TX;
@@ -8606,7 +9104,69 @@
 	return ret;
 }
 
-int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi)
+static int afe_get_spv4_ex_vi_ftm_data(void *params, uint32_t size)
+{
+	struct param_hdr_v3 param_hdr;
+	int port = SLIMBUS_4_TX;
+	int ret = -EINVAL;
+	u8 *rcvd_params = NULL;
+	struct afe_sp_v4_channel_ex_vi_ftm_params *ex_vi_ftm_param;
+
+	if (!params) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	memset(&param_hdr, 0, sizeof(param_hdr));
+
+	param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_SP_V4_EX_VI_FTM_PARAMS;
+	param_hdr.param_size = size;
+
+	ret = q6afe_get_params(port, NULL, &param_hdr);
+	if (ret < 0) {
+		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+		       __func__, port, param_hdr.param_id, ret);
+		goto get_params_fail;
+	}
+
+	rcvd_params = (u8 *)this_afe.spv4_get_param_resp_ptr +
+				 sizeof(struct afe_sp_v4_gen_get_param_resp);
+
+	memcpy(params, rcvd_params,  this_afe.spv4_rcvd_param_size);
+
+	ex_vi_ftm_param = (struct afe_sp_v4_channel_ex_vi_ftm_params *)
+		(params + sizeof(struct afe_sp_v4_param_ex_vi_ftm_params));
+
+	pr_debug("%s: resistance %d %d force factor %d %d Damping kg/s %d %d\n"
+		"stiffness N/mm %d %d freq %d %d Qfactor %d %d status %d %d",
+		__func__, ex_vi_ftm_param[SP_V2_SPKR_1].ftm_re_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_re_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_1].ftm_Bl_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_Bl_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_1].ftm_Rms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_Rms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_1].ftm_Kms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_Kms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_1].ftm_Fres_q20,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_Fres_q20,
+		ex_vi_ftm_param[SP_V2_SPKR_1].ftm_Qms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_2].ftm_Qms_q24,
+		ex_vi_ftm_param[SP_V2_SPKR_1].status,
+		ex_vi_ftm_param[SP_V2_SPKR_2].status);
+	ret = 0;
+get_params_fail:
+	kfree(this_afe.spv4_get_param_resp_ptr);
+	mutex_unlock(&this_afe.afe_cmd_lock);
+done:
+	return ret;
+}
+
+static int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi)
 {
 	struct param_hdr_v3 param_hdr;
 	int port = SLIMBUS_4_TX;
@@ -8653,6 +9213,61 @@
 	return ret;
 }
 
+int afe_get_sp_v4_rx_tmax_xmax_logging_data(
+		struct afe_sp_rx_tmax_xmax_logging_param *xt_logging,
+		u16 port_id)
+{
+	struct param_hdr_v3 param_hdr;
+	int ret = -EINVAL;
+	struct afe_sp_v4_param_tmax_xmax_logging *tmax_xmax_logging;
+	struct afe_sp_v4_channel_tmax_xmax_params *tx_channel_params;
+	uint32_t i, size = 0;
+
+	if (!xt_logging) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+
+	size = sizeof(struct afe_sp_v4_param_tmax_xmax_logging) +
+		(SP_V2_NUM_MAX_SPKRS *
+		 sizeof(struct afe_sp_v4_channel_tmax_xmax_params));
+	memset(&param_hdr, 0, sizeof(param_hdr));
+
+	param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_RX;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_SP_V4_RX_TMAX_XMAX_LOGGING;
+	param_hdr.param_size = size;
+
+	ret = q6afe_get_params(port_id, NULL, &param_hdr);
+	if (ret) {
+		pr_err("%s: Failed to get Tmax Xmax logging data\n", __func__);
+		goto get_params_fail;
+	}
+
+	tmax_xmax_logging = (struct afe_sp_v4_param_tmax_xmax_logging *)
+				((u8 *)this_afe.spv4_get_param_resp_ptr +
+				sizeof(struct afe_sp_v4_gen_get_param_resp));
+	tx_channel_params = (struct afe_sp_v4_channel_tmax_xmax_params *)
+			((u8 *)tmax_xmax_logging +
+			 sizeof(struct afe_sp_v4_param_tmax_xmax_logging));
+	for (i = 0; i < tmax_xmax_logging->num_ch; i++) {
+		xt_logging->max_excursion[i] =
+			tx_channel_params[i].max_excursion;
+		xt_logging->count_exceeded_excursion[i] =
+			tx_channel_params[i].count_exceeded_excursion;
+		xt_logging->max_temperature[i] =
+			tx_channel_params[i].max_temperature;
+		xt_logging->count_exceeded_temperature[i] =
+			tx_channel_params[i].count_exceeded_temperature;
+	}
+
+	ret = 0;
+get_params_fail:
+	kfree(this_afe.spv4_get_param_resp_ptr);
+done:
+	return ret;
+}
+
 /**
  * afe_get_sp_rx_tmax_xmax_logging_data -
  *       command to get excursion logging data from DSP
@@ -8675,23 +9290,32 @@
 	}
 
 	mutex_lock(&this_afe.afe_cmd_lock);
-	memset(&param_hdr, 0, sizeof(param_hdr));
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		ret = afe_get_sp_v4_rx_tmax_xmax_logging_data(xt_logging,
+								 port_id);
+	} else {
+		memset(&param_hdr, 0, sizeof(param_hdr));
 
-	param_hdr.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
-	param_hdr.instance_id = INSTANCE_ID_0;
-	param_hdr.param_id = AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING;
-	param_hdr.param_size = sizeof(struct afe_sp_rx_tmax_xmax_logging_param);
+		param_hdr.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
+		param_hdr.instance_id = INSTANCE_ID_0;
+		param_hdr.param_id = AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING;
+		param_hdr.param_size =
+			 sizeof(struct afe_sp_rx_tmax_xmax_logging_param);
 
-	ret = q6afe_get_params(port_id, NULL, &param_hdr);
-	if (ret < 0) {
-		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
-		       __func__, port_id, param_hdr.param_id, ret);
-		goto get_params_fail;
+		ret = q6afe_get_params(port_id, NULL, &param_hdr);
+		if (ret < 0) {
+			pr_err(
+			"%s: get param port 0x%x param id[0x%x]failed %d\n",
+			__func__, port_id, param_hdr.param_id, ret);
+			goto get_params_fail;
+		}
+
+		memcpy(xt_logging, &this_afe.xt_logging_resp.param,
+			sizeof(this_afe.xt_logging_resp.param));
 	}
-
-	memcpy(xt_logging, &this_afe.xt_logging_resp.param,
-		sizeof(this_afe.xt_logging_resp.param));
-	pr_debug("%s: max_excursion %d %d count_exceeded_excursion %d %d max_temperature %d %d count_exceeded_temperature %d %d\n",
+	pr_debug("%s: max_excursion %d %d count_exceeded_excursion %d %d"
+		" max_temperature %d %d count_exceeded_temperature %d %d\n",
 		 __func__, xt_logging->max_excursion[SP_V2_SPKR_1],
 		 xt_logging->max_excursion[SP_V2_SPKR_2],
 		 xt_logging->count_exceeded_excursion[SP_V2_SPKR_1],
@@ -8801,6 +9425,46 @@
 }
 EXPORT_SYMBOL(afe_get_doa_tracking_mon);
 
+static int afe_spv4_get_calib_data(
+		struct afe_sp_v4_th_vi_calib_resp *calib_resp)
+{
+	struct param_hdr_v3 param_hdr;
+	int port = SLIMBUS_4_TX;
+	int ret = -EINVAL;
+
+	if (!calib_resp) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto fail_cmd;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	memset(&param_hdr, 0, sizeof(param_hdr));
+	param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_SP_V4_CALIB_RES_CFG;
+	param_hdr.param_size = sizeof(struct afe_sp_v4_th_vi_calib_resp);
+
+	ret = q6afe_get_params(port, NULL, &param_hdr);
+	if (ret < 0) {
+		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+		       __func__, port, param_hdr.param_id, ret);
+		goto get_params_fail;
+	}
+	memcpy(&calib_resp->res_cfg, &this_afe.spv4_calib_data.res_cfg,
+		sizeof(this_afe.calib_data.res_cfg));
+	pr_info("%s: state %s resistance %d %d\n", __func__,
+		fbsp_state[calib_resp->res_cfg.th_vi_ca_state],
+		calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_1],
+		calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_2]);
+	ret = 0;
+get_params_fail:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+fail_cmd:
+	return ret;
+}
+
 int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp)
 {
 	struct param_hdr_v3 param_hdr;
@@ -8878,22 +9542,41 @@
 	}
 	pr_debug("%s: src_port 0x%x  dst_port 0x%x l_ch %d r_ch %d\n",
 		 __func__, src_port, dst_port, l_ch, r_ch);
-	memset(&prot_config, 0, sizeof(prot_config));
-	prot_config.feedback_path_cfg.dst_portid =
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		if (l_ch) {
+			this_afe.v4_ch_map_cfg.chan_info[index++] = 1;
+			this_afe.v4_ch_map_cfg.chan_info[index++] = 2;
+		}
+		if (r_ch) {
+			this_afe.v4_ch_map_cfg.chan_info[index++] = 3;
+			this_afe.v4_ch_map_cfg.chan_info[index++] = 4;
+		}
+		this_afe.v4_ch_map_cfg.num_channels = index;
+		pr_debug("%s no of channels: %d\n", __func__, index);
+		this_afe.vi_tx_port = src_port;
+		this_afe.vi_rx_port = dst_port;
+		ret = 0;
+	} else {
+		memset(&prot_config, 0, sizeof(prot_config));
+		prot_config.feedback_path_cfg.dst_portid =
 		q6audio_get_port_id(dst_port);
-	if (l_ch) {
-		prot_config.feedback_path_cfg.chan_info[index++] = 1;
-		prot_config.feedback_path_cfg.chan_info[index++] = 2;
+		if (l_ch) {
+			prot_config.feedback_path_cfg.chan_info[index++] = 1;
+			prot_config.feedback_path_cfg.chan_info[index++] = 2;
+		}
+		if (r_ch) {
+			prot_config.feedback_path_cfg.chan_info[index++] = 3;
+			prot_config.feedback_path_cfg.chan_info[index++] = 4;
+		}
+		prot_config.feedback_path_cfg.num_channels = index;
+		pr_debug("%s no of channels: %d\n", __func__, index);
+		prot_config.feedback_path_cfg.minor_version = 1;
+		ret = afe_spk_prot_prepare(src_port, dst_port,
+				AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config,
+				 sizeof(union afe_spkr_prot_config));
 	}
-	if (r_ch) {
-		prot_config.feedback_path_cfg.chan_info[index++] = 3;
-		prot_config.feedback_path_cfg.chan_info[index++] = 4;
-	}
-	prot_config.feedback_path_cfg.num_channels = index;
-	pr_debug("%s no of channels: %d\n", __func__, index);
-	prot_config.feedback_path_cfg.minor_version = 1;
-	ret = afe_spk_prot_prepare(src_port, dst_port,
-			AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config);
+
 fail_cmd:
 	return ret;
 }
@@ -9238,6 +9921,9 @@
 	int i, ret = 0;
 	struct audio_cal_type_sp_th_vi_v_vali_param *cal_data = data;
 	struct afe_sp_th_vi_v_vali_get_param th_vi_v_vali;
+	uint32_t size;
+	void *params = NULL;
+	struct afe_sp_v4_channel_v_vali_params *v_vali_params;
 
 	if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
 	    cal_data == NULL ||
@@ -9248,18 +9934,51 @@
 		cal_data->cal_info.status[i] = -EINVAL;
 		cal_data->cal_info.vrms_q24[i] = -1;
 	}
-	if (!afe_get_sp_th_vi_v_vali_data(&th_vi_v_vali)) {
-		for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
-			pr_debug("%s: v-vali param status = %d\n",
-				  __func__, th_vi_v_vali.param.status[i]);
-			if (th_vi_v_vali.param.status[i] ==
-					V_VALI_IN_PROGRESS) {
-				cal_data->cal_info.status[i] = -EAGAIN;
-			} else if (th_vi_v_vali.param.status[i] ==
-					V_VALI_SUCCESS) {
-				cal_data->cal_info.status[i] = V_VALI_SUCCESS;
-				cal_data->cal_info.vrms_q24[i] =
-					th_vi_v_vali.param.vrms_q24[i];
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		size = sizeof(struct afe_sp_v4_param_th_vi_v_vali_params) +
+			(SP_V2_NUM_MAX_SPKRS *
+			 sizeof(struct afe_sp_v4_channel_v_vali_params));
+		params = kzalloc(size, GFP_KERNEL);
+		if (!params)
+			return -ENOMEM;
+		v_vali_params =
+		    (struct afe_sp_v4_channel_v_vali_params *)((u8 *)params +
+		    sizeof(struct afe_sp_v4_param_th_vi_v_vali_params));
+
+		if (!afe_get_spv4_th_vi_v_vali_data(params, size)) {
+			for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+				pr_debug("%s: ftm param status = %d\n",
+					  __func__, v_vali_params[i].status);
+				if (v_vali_params[i].status ==
+							 V_VALI_IN_PROGRESS) {
+					cal_data->cal_info.status[i] = -EAGAIN;
+				} else if (v_vali_params[i].status ==
+							 V_VALI_SUCCESS) {
+					cal_data->cal_info.status[i] =
+						V_VALI_SUCCESS;
+					cal_data->cal_info.vrms_q24[i] =
+						v_vali_params[i].vrms_q24;
+				}
+			}
+		}
+		kfree(params);
+	} else {
+		if (!afe_get_sp_th_vi_v_vali_data(&th_vi_v_vali)) {
+			for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+				pr_debug(
+				"%s: v-vali param status = %d\n",
+				__func__, th_vi_v_vali.param.status[i]);
+				if (th_vi_v_vali.param.status[i] ==
+						V_VALI_IN_PROGRESS) {
+					cal_data->cal_info.status[i] = -EAGAIN;
+				} else if (th_vi_v_vali.param.status[i] ==
+						V_VALI_SUCCESS) {
+					cal_data->cal_info.status[i] =
+					 V_VALI_SUCCESS;
+					cal_data->cal_info.vrms_q24[i] =
+						th_vi_v_vali.param.vrms_q24[i];
+				}
 			}
 		}
 	}
@@ -9274,6 +9993,9 @@
 	int i, ret = 0;
 	struct audio_cal_type_sp_th_vi_param *cal_data = data;
 	struct afe_sp_th_vi_get_param th_vi;
+	uint32_t size;
+	void *params = NULL;
+	struct afe_sp_v4_channel_ftm_params *th_vi_ftm_params;
 
 	if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
 	    cal_data == NULL ||
@@ -9285,18 +10007,52 @@
 		cal_data->cal_info.r_dc_q24[i] = -1;
 		cal_data->cal_info.temp_q22[i] = -1;
 	}
-	if (!afe_get_sp_th_vi_ftm_data(&th_vi)) {
-		for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
-			pr_debug("%s: ftm param status = %d\n",
-				  __func__, th_vi.param.status[i]);
-			if (th_vi.param.status[i] == FBSP_IN_PROGRESS) {
-				cal_data->cal_info.status[i] = -EAGAIN;
-			} else if (th_vi.param.status[i] == FBSP_SUCCESS) {
-				cal_data->cal_info.status[i] = 0;
-				cal_data->cal_info.r_dc_q24[i] =
-					th_vi.param.dc_res_q24[i];
-				cal_data->cal_info.temp_q22[i] =
-					th_vi.param.temp_q22[i];
+	if (q6core_get_avcs_api_version_per_service(
+		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V9) {
+		size = sizeof(struct afe_sp_v4_param_th_vi_ftm_params) +
+			(SP_V2_NUM_MAX_SPKRS *
+			 sizeof(struct afe_sp_v4_channel_ftm_params));
+		params = kzalloc(size, GFP_KERNEL);
+		if (!params)
+			return -ENOMEM;
+		th_vi_ftm_params = (struct afe_sp_v4_channel_ftm_params *)
+		((u8 *)params +
+		sizeof(struct afe_sp_v4_param_th_vi_ftm_params));
+
+		if (!afe_get_spv4_th_vi_ftm_data(params, size)) {
+			for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+				pr_debug("%s: SP V4 ftm param status = %d\n",
+					  __func__, th_vi_ftm_params[i].status);
+				if (th_vi_ftm_params[i].status ==
+							 FBSP_IN_PROGRESS) {
+					cal_data->cal_info.status[i] = -EAGAIN;
+				} else if (th_vi_ftm_params[i].status ==
+							 FBSP_SUCCESS) {
+					cal_data->cal_info.status[i] = 0;
+					cal_data->cal_info.r_dc_q24[i] =
+						th_vi_ftm_params[i].dc_res_q24;
+					cal_data->cal_info.temp_q22[i] =
+						th_vi_ftm_params[i].temp_q22;
+				}
+			}
+		}
+		kfree(params);
+	} else {
+
+		if (!afe_get_sp_th_vi_ftm_data(&th_vi)) {
+			for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+				pr_debug("%s: ftm param status = %d\n",
+					  __func__, th_vi.param.status[i]);
+				if (th_vi.param.status[i] == FBSP_IN_PROGRESS) {
+					cal_data->cal_info.status[i] = -EAGAIN;
+				} else if (th_vi.param.status[i] ==
+								 FBSP_SUCCESS) {
+					cal_data->cal_info.status[i] = 0;
+					cal_data->cal_info.r_dc_q24[i] =
+						th_vi.param.dc_res_q24[i];
+					cal_data->cal_info.temp_q22[i] =
+						th_vi.param.temp_q22[i];
+				}
 			}
 		}
 	}
@@ -9329,6 +10085,75 @@
 	return ret;
 }
 
+static int afe_get_cal_spv4_ex_vi_ftm_param(int32_t cal_type, size_t data_size,
+					  void *data)
+{
+	int i, ret = 0;
+	struct audio_cal_type_sp_v4_ex_vi_param *cal_data = data;
+	uint32_t size;
+	void *params = NULL;
+	struct afe_sp_v4_channel_ex_vi_ftm_params *ex_vi_ftm_param;
+
+	pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_V4_EX_VI_CAL] == NULL ||
+	    cal_data == NULL ||
+	    data_size != sizeof(*cal_data))
+		goto done;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_V4_EX_VI_CAL]->lock);
+	for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+		cal_data->cal_info.status[i] = -EINVAL;
+		cal_data->cal_info.ftm_re_q24[i] = -1;
+		cal_data->cal_info.ftm_re_q24[i] = -1;
+		cal_data->cal_info.ftm_Rms_q24[i] = -1;
+		cal_data->cal_info.ftm_Kms_q24[i] = -1;
+		cal_data->cal_info.ftm_freq_q20[i] = -1;
+		cal_data->cal_info.ftm_Qms_q24[i] = -1;
+	}
+
+	size = sizeof(struct afe_sp_v4_param_ex_vi_ftm_params) +
+		(SP_V2_NUM_MAX_SPKRS *
+		sizeof(struct afe_sp_v4_channel_ex_vi_ftm_params));
+	params = kzalloc(size, GFP_KERNEL);
+	if (!params) {
+		mutex_unlock(
+		  &this_afe.cal_data[AFE_FB_SPKR_PROT_V4_EX_VI_CAL]->lock);
+		return -ENOMEM;
+	}
+	ex_vi_ftm_param = (struct afe_sp_v4_channel_ex_vi_ftm_params *)
+		((u8 *)params +
+		sizeof(struct afe_sp_v4_param_ex_vi_ftm_params));
+
+	if (!afe_get_spv4_ex_vi_ftm_data(params, size)) {
+		for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+			pr_debug("%s: ftm param status = %d\n",
+				  __func__, ex_vi_ftm_param[i].status);
+			if (ex_vi_ftm_param[i].status == FBSP_IN_PROGRESS) {
+				cal_data->cal_info.status[i] = -EAGAIN;
+			} else if (ex_vi_ftm_param[i].status == FBSP_SUCCESS) {
+				cal_data->cal_info.status[i] = 0;
+				cal_data->cal_info.ftm_re_q24[i] =
+					ex_vi_ftm_param[i].ftm_re_q24;
+				cal_data->cal_info.ftm_Bl_q24[i] =
+					ex_vi_ftm_param[i].ftm_Bl_q24;
+				cal_data->cal_info.ftm_Rms_q24[i] =
+					ex_vi_ftm_param[i].ftm_Rms_q24;
+				cal_data->cal_info.ftm_Kms_q24[i] =
+					ex_vi_ftm_param[i].ftm_Kms_q24;
+				cal_data->cal_info.ftm_freq_q20[i] =
+					ex_vi_ftm_param[i].ftm_Fres_q20;
+				cal_data->cal_info.ftm_Qms_q24[i] =
+					ex_vi_ftm_param[i].ftm_Qms_q24;
+			}
+		}
+	}
+	kfree(params);
+
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_V4_EX_VI_CAL]->lock);
+done:
+	return ret;
+}
+
 static int afe_get_cal_sp_ex_vi_ftm_param(int32_t cal_type, size_t data_size,
 					  void *data)
 {
@@ -9377,6 +10202,7 @@
 	int ret = 0;
 	struct audio_cal_type_fb_spk_prot_status *cal_data = data;
 	struct afe_spkr_prot_get_vi_calib calib_resp;
+	struct afe_sp_v4_th_vi_calib_resp spv4_calib_resp;
 
 	pr_debug("%s:\n", __func__);
 
@@ -9400,17 +10226,47 @@
 		cal_data->cal_info.status = -EINVAL;
 		cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
 		cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
-		if (!afe_spk_prot_get_calib_data(&calib_resp)) {
-			if (calib_resp.res_cfg.th_vi_ca_state ==
+		if (this_afe.prot_cfg.sp_version >= AFE_API_VERSION_V9) {
+			if (!(q6core_get_avcs_api_version_per_service(
+				APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >=
+							 AFE_API_VERSION_V9)) {
+				pr_debug(
+				"%s: AFE API version is not supported!\n",
+				__func__);
+				goto done;
+			}
+			if (!afe_spv4_get_calib_data(&spv4_calib_resp)) {
+				if (spv4_calib_resp.res_cfg.th_vi_ca_state ==
 							FBSP_IN_PROGRESS)
-				cal_data->cal_info.status = -EAGAIN;
-			else if (calib_resp.res_cfg.th_vi_ca_state ==
+					cal_data->cal_info.status = -EAGAIN;
+				else if (
+				    spv4_calib_resp.res_cfg.th_vi_ca_state ==
 							FBSP_SUCCESS) {
-				cal_data->cal_info.status = 0;
-				cal_data->cal_info.r0[SP_V2_SPKR_1] =
-				calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_1];
-				cal_data->cal_info.r0[SP_V2_SPKR_2] =
-				calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_2];
+					cal_data->cal_info.status = 0;
+					cal_data->cal_info.r0[SP_V2_SPKR_1] =
+					spv4_calib_resp.res_cfg.r0_cali_q24[
+					    SP_V2_SPKR_1];
+					cal_data->cal_info.r0[SP_V2_SPKR_2] =
+					spv4_calib_resp.res_cfg.r0_cali_q24[
+					    SP_V2_SPKR_2];
+				}
+			}
+		} else {
+
+			if (!afe_spk_prot_get_calib_data(&calib_resp)) {
+				if (calib_resp.res_cfg.th_vi_ca_state ==
+							FBSP_IN_PROGRESS)
+					cal_data->cal_info.status = -EAGAIN;
+				else if (calib_resp.res_cfg.th_vi_ca_state ==
+								FBSP_SUCCESS) {
+					cal_data->cal_info.status = 0;
+					cal_data->cal_info.r0[SP_V2_SPKR_1] =
+					calib_resp.res_cfg.r0_cali_q24[
+					    SP_V2_SPKR_1];
+					cal_data->cal_info.r0[SP_V2_SPKR_2] =
+					calib_resp.res_cfg.r0_cali_q24[
+					    SP_V2_SPKR_2];
+				}
 			}
 		}
 		if (!cal_data->cal_info.status) {
@@ -9595,6 +10451,11 @@
 		{NULL, NULL, NULL, afe_set_cal_sp_ex_vi_ftm_cfg,
 		afe_get_cal_sp_ex_vi_ftm_param, NULL} },
 		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{AFE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE,
+		{NULL, NULL, NULL, NULL,
+		afe_get_cal_spv4_ex_vi_ftm_param, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
 	};
 	pr_debug("%s:\n", __func__);
 
@@ -9741,7 +10602,7 @@
 #else
 	wl.ws = wakeup_source_register("spkr-prot");
 #endif
-/*
+	/*
 	 * Set release function to cleanup memory related to kobject
 	 * before initializing the kobject.
 	 */
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index 1894737..607b910 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -11084,19 +11084,6 @@
 	uint32_t lim_thr_per_calib_q27[SP_V2_NUM_MAX_SPKR];
 } __packed;
 
-union afe_spkr_prot_config {
-	struct asm_fbsp_mode_rx_cfg mode_rx_cfg;
-	struct asm_spkr_calib_vi_proc_cfg vi_proc_cfg;
-	struct asm_feedback_path_cfg feedback_path_cfg;
-	struct asm_mode_vi_proc_cfg mode_vi_proc_cfg;
-	struct afe_sp_th_vi_mode_cfg th_vi_mode_cfg;
-	struct afe_sp_th_vi_ftm_cfg th_vi_ftm_cfg;
-	struct afe_sp_th_vi_v_vali_cfg th_vi_v_vali_cfg;
-	struct afe_sp_ex_vi_mode_cfg ex_vi_mode_cfg;
-	struct afe_sp_ex_vi_ftm_cfg ex_vi_ftm_cfg;
-	struct afe_sp_rx_limiter_th_param limiter_th_cfg;
-} __packed;
-
 struct afe_spkr_prot_get_vi_calib {
 	struct apr_hdr hdr;
 	struct mem_mapping_hdr mem_hdr;
@@ -11111,6 +11098,366 @@
 } __packed;
 
 
+#define AFE_MODULE_SPEAKER_PROTECTION_V4_RX       0x000102C7
+#define AFE_PARAM_ID_SP_V4_OP_MODE                0x000102C9
+#define AFE_PARAM_ID_SP_V4_RX_TMAX_XMAX_LOGGING   0x000102D2
+
+struct afe_sp_v4_param_op_mode {
+	uint32_t mode;
+} __packed;
+
+struct afe_sp_v4_channel_tmax_xmax_params {
+	/*
+	 * Maximum excursion since the last grasp of xmax in mm.
+	 */
+	int32_t max_excursion;
+	/*
+	 * Number of periods when the monitored excursion exceeds to and
+	 * stays at Xmax during logging_count_period.
+	 */
+	uint32_t count_exceeded_excursion;
+	/*
+	 * Maximum temperature since the last grasp of tmax in C.
+	 */
+	int32_t max_temperature;
+	/*
+	 * Number of periods when the monitored temperature exceeds to and
+	 * stays at Tmax during logging_count_period
+	 */
+	uint32_t count_exceeded_temperature;
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures of
+ * type afe_sp_v4_channel_tmax_xmax_params.
+ */
+struct afe_sp_v4_param_tmax_xmax_logging {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_tmax_xmax_params ch_tmax_xmax[0];
+} __packed;
+
+#define AFE_MODULE_SPEAKER_PROTECTION_V4_VI     0x000102D3
+#define AFE_PARAM_ID_SP_V4_VI_OP_MODE_CFG       0x000102D4
+#define AFE_PARAM_ID_SP_V4_VI_R0T0_CFG          0x000102D5
+#define AFE_PARAM_ID_SP_V4_CALIB_RES_CFG        0x000102D8
+#define AFE_PARAM_ID_SP_V4_TH_VI_FTM_CFG        0x000102D9
+#define AFE_PARAM_ID_SP_V4_TH_VI_FTM_PARAMS     0x000102DA
+#define AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_CFG     0x000102DB
+#define AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_PARAMS  0x000102DC
+#define AFE_PARAM_ID_SP_V4_EX_VI_MODE_CFG       0x000102DF
+#define AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG        0x000102E0
+#define AFE_PARAM_ID_SP_V4_EX_VI_FTM_PARAMS     0x000102E1
+#define AFE_PARAM_ID_SP_V4_VI_CHANNEL_MAP_CFG   0x000102E5
+
+struct afe_sp_v4_param_vi_op_mode_cfg {
+	uint32_t num_speakers;
+	/* Number of channels for Rx signal.
+	 */
+	uint32_t th_operation_mode;
+	/*
+	 * Operation mode of thermal VI module.
+	 *   0 -- Normal Running mode
+	 *   1 -- Calibration mode
+	 *   2 -- FTM mode
+	 *   3 -- V-Validation mode
+	 */
+	uint32_t th_quick_calib_flag;
+	/*
+	 * Indicates whether calibration is to be done in quick mode or not.
+	 * This field is valid only in Calibration mode (operation_mode = 1).
+	 * 0 -- Disabled
+	 * 1 -- Enabled
+	 */
+	uint32_t th_r0t0_selection_flag[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Specifies which set of R0, T0 values the algorithm will use.
+	 * This field is valid only in Normal mode (operation_mode = 0).
+	 * 0 -- Use calibrated R0, T0 value
+	 * 1 -- Use safe R0, T0 value
+	 */
+} __packed;
+
+struct afe_sp_v4_channel_r0t0 {
+	int32_t r0_cali_q24;
+	/*
+	 * Calibration point resistance per device. This field is valid
+	 * only in Normal mode (operation_mode = 0).
+	 * values 33554432 to 1073741824 Ohms (in Q24 format)
+	 */
+	int16_t t0_cali_q6;
+	/*
+	 * Calibration point temperature per device. This field is valid
+	 * in both Normal mode and Calibration mode.
+	 * values -1920 to 5120 degrees C (in Q6 format)
+	 */
+	uint16_t reserved;
+
+} __packed;
+
+/* Followed by this structure are 'num_speaakers' number of structures
+ *  of type afe_sp_v4_channel_r0t0.
+ */
+struct afe_sp_v4_param_th_vi_r0t0_cfg {
+	uint32_t num_speakers;
+	/* Number of channels for Rx signal.
+	 */
+
+	struct afe_sp_v4_channel_r0t0 ch_r0t0[0];
+} __packed;
+
+struct afe_sp_v4_param_th_vi_calib_res_cfg {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+	uint32_t th_vi_ca_state;
+	/*
+	 * Represents the calibration state for both speakers.
+	 * 0 -- Incorrect operation mode.
+	 * 1 -- Inactive mode.
+	 * 2 -- Wait state.
+	 * 3 -- Calibration in progress state.
+	 * 4 -- Calibration success.
+	 * 5 -- Calibration failed.
+	 */
+	int32_t	r0_cali_q24[SP_V2_NUM_MAX_SPKR];
+	/* Calibration resistance per device.
+	 */
+} __packed;
+
+struct afe_sp_v4_th_vi_calib_resp {
+	uint32_t status;
+	struct param_hdr_v3 pdata;
+	struct afe_sp_v4_param_th_vi_calib_res_cfg res_cfg;
+} __packed;
+
+struct afe_sp_v4_channel_ftm_cfg {
+	uint32_t wait_time_ms;
+	/*
+	 * Wait time to heat up speaker before collecting statistics
+	 * for ftm mode in ms.
+	 * values 0 to 4294967295 ms
+	 */
+	uint32_t ftm_time_ms;
+	/*
+	 * duration for which FTM statistics are collected in ms.
+	 * values 0 to 2000 ms
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures
+ * of type afe_sp_v4_channel_ftm_cfg.
+ */
+struct afe_sp_v4_param_th_vi_ftm_cfg {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_ftm_cfg ch_ftm_cfg[0];
+} __packed;
+
+struct afe_sp_v4_channel_ftm_params {
+	int32_t dc_res_q24;
+	/*
+	 * DC resistance value in q24 format
+	 * values 0 to 2147483647 Ohms (in Q24 format)
+	 */
+	int32_t temp_q22;
+	/*
+	 * temperature value in q22 format
+	 * values -125829120 to 2147483647 degC (in Q22 format)
+	 */
+	uint32_t status;
+	/*
+	 * FTM packet status
+	 * 0 - Incorrect operation mode.This status is returned
+	 *     when GET_PARAM is called in non FTM Mode
+	 * 1 - Inactive mode -- Port is not yet started.
+	 * 2 - Wait state. wait_time_ms has not yet elapsed
+	 * 3 - In progress state. ftm_time_ms has not yet elapsed.
+	 * 4 - Success.
+	 * 5 - Failed.
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures
+ * of type afe_sp_v4_channel_ftm_params.
+ */
+struct afe_sp_v4_param_th_vi_ftm_params {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_ftm_params ch_ftm_params[0];
+} __packed;
+
+struct afe_sp_v4_gen_get_param_resp {
+	uint32_t status;
+	struct param_hdr_v3 pdata;
+} __packed;
+
+struct afe_sp_v4_channel_v_vali_cfg {
+	uint32_t wait_time_ms;
+	/*
+	 * Wait time to heat up speaker before collecting statistics
+	 * for V validation mode in ms.
+	 * values 100 to 1000 ms
+	 */
+	uint32_t vali_time_ms;
+	/*
+	 * duration for which V VALIDATION statistics are collected in ms.
+	 * values 1000 to 3000 ms
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures
+ * of type afe_sp_v4_channel_v_vali_cfg.
+ */
+struct afe_sp_v4_param_th_vi_v_vali_cfg {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_v_vali_cfg ch_v_vali_cfg[0];
+} __packed;
+
+struct afe_sp_v4_channel_v_vali_params {
+	uint32_t vrms_q24;
+	/*
+	 * Vrms value in q24 format
+	 * values [0 33554432] Q24 (0 - 2Vrms)
+	 */
+	uint32_t status;
+	/*
+	 * v-vali packet status
+	 * 0 - Failed.
+	 * 1 - Success.
+	 * 2 - Incorrect operation mode.This status is returned
+	 *     when GET_PARAM is called in non v-vali Mode
+	 * 3 - Inactive mode -- Port is not yet started.
+	 * 4 - Wait state. wait_time_ms has not yet elapsed
+	 * 5 - In progress state. ftm_time_ms has not yet elapsed.
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures
+ * of type afe_sp_v4_channel_v_vali_params.
+ */
+struct afe_sp_v4_param_th_vi_v_vali_params {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_v_vali_params ch_v_vali_params[0];
+} __packed;
+
+struct afe_sp_v4_param_ex_vi_mode_cfg {
+	uint32_t operation_mode;
+	/*
+	 * Operation mode of Excursion VI module.
+	 * 0 - Normal Running mode
+	 * 2 - FTM mode
+	 */
+} __packed;
+
+struct afe_sp_v4_channel_ex_vi_ftm {
+	uint32_t wait_time_ms;
+	/*
+	 * Wait time to heat up speaker before collecting statistics
+	 * for ftm mode in ms.
+	 * values 0 to 4294967295 ms
+	 */
+	uint32_t ftm_time_ms;
+	/*
+	 * duration for which FTM statistics are collected in ms.
+	 * values 0 to 2000 ms
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures
+ * of type afe_sp_v4_channel_ex_vi_ftm.
+ */
+struct afe_sp_v4_param_ex_vi_ftm_cfg {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_ex_vi_ftm ch_ex_vi_ftm[0];
+} __packed;
+
+struct afe_sp_v4_channel_ex_vi_ftm_params {
+	int32_t ftm_re_q24;
+	/*
+	 * DC resistance of voice coil at room temperature
+	 * or small signal level in Ohm.
+	 */
+	int32_t ftm_Bl_q24;
+	/* Force factor.
+	 */
+	int32_t ftm_Rms_q24;
+	/* Mechanical damping or resistance of loudspeaker in Kg/sec.
+	 */
+	int32_t ftm_Kms_q24;
+	/* Mechanical stiffness of driver suspension in N/mm.
+	 */
+	int32_t ftm_Fres_q20;
+	/* Resonance frequency in Hz.
+	 */
+	int32_t ftm_Qms_q24;
+	/* Mechanical Q-factor.
+	 */
+	uint32_t status;
+	/*
+	 * FTM packet status
+	 * 0 - Incorrect operation mode.This status is returned
+	 *      when GET_PARAM is called in non FTM Mode.
+	 * 1 - Inactive mode -- Port is not yet started.
+	 * 2 - Wait state. wait_time_ms has not yet elapsed
+	 * 3 - In progress state. ftm_time_ms has not yet elapsed.
+	 * 4 - Success.
+	 * 5 - Failed.
+	 */
+} __packed;
+
+/* This structure is followed by 'num_ch' number of structures of
+ * type afe_sp_v4_channel_ex_vi_ftm_params.
+ */
+struct afe_sp_v4_param_ex_vi_ftm_params {
+	uint32_t num_ch;
+	/* Number of channels for Rx signal.
+	 */
+
+	 struct afe_sp_v4_channel_ex_vi_ftm_params ch_ex_vi_ftm_params[0];
+} __packed;
+
+struct afe_sp_v4_param_vi_channel_map_cfg {
+	int32_t	num_channels;
+	int32_t	chan_info[4];
+} __packed;
+
+union afe_spkr_prot_config {
+	struct asm_fbsp_mode_rx_cfg mode_rx_cfg;
+	struct asm_spkr_calib_vi_proc_cfg vi_proc_cfg;
+	struct asm_feedback_path_cfg feedback_path_cfg;
+	struct asm_mode_vi_proc_cfg mode_vi_proc_cfg;
+	struct afe_sp_th_vi_mode_cfg th_vi_mode_cfg;
+	struct afe_sp_th_vi_ftm_cfg th_vi_ftm_cfg;
+	struct afe_sp_th_vi_v_vali_cfg th_vi_v_vali_cfg;
+	struct afe_sp_ex_vi_mode_cfg ex_vi_mode_cfg;
+	struct afe_sp_ex_vi_ftm_cfg ex_vi_ftm_cfg;
+	struct afe_sp_rx_limiter_th_param limiter_th_cfg;
+	struct afe_sp_v4_param_op_mode v4_op_mode;
+	struct afe_sp_v4_param_vi_op_mode_cfg v4_vi_op_mode;
+	struct afe_sp_v4_param_th_vi_r0t0_cfg v4_r0t0_cfg;
+	struct afe_sp_v4_param_th_vi_ftm_cfg v4_th_vi_ftm_cfg;
+	struct afe_sp_v4_param_th_vi_v_vali_cfg v4_v_vali_cfg;
+	struct afe_sp_v4_param_ex_vi_mode_cfg v4_ex_vi_mode_cfg;
+	struct afe_sp_v4_param_ex_vi_ftm_cfg v4_ex_vi_ftm_cfg;
+	struct afe_sp_v4_param_vi_channel_map_cfg v4_ch_map_cfg;
+} __packed;
+
+
 /* SRS TRUMEDIA start */
 /* topology */
 #define SRS_TRUMEDIA_TOPOLOGY_ID			0x00010D90
@@ -11782,7 +12129,7 @@
 	 * for enable and disable clock.
 	 *	"clk_freq_in_hz", "clk_attri", and "clk_root"
 	 *	are ignored in disable clock case.
-	 *	@values 
+	 *	@values
 	 *	- 0 -- Disabled
 	 *	- 1 -- Enabled  @tablebulletend
 	 */
diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h
index 55ae6be..8b82069 100644
--- a/include/dsp/q6afe-v2.h
+++ b/include/dsp/q6afe-v2.h
@@ -45,7 +45,8 @@
 #define AFE_API_VERSION_V4		4
 /* for VAD enable */
 #define AFE_API_VERSION_V6		6
-
+/* for Speaker Protection V4 */
+#define AFE_API_VERSION_V9		9
 
 typedef int (*routing_cb)(int port);
 
diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h
index e8ba1b3..5562692 100644
--- a/include/uapi/linux/msm_audio_calibration.h
+++ b/include/uapi/linux/msm_audio_calibration.h
@@ -105,11 +105,13 @@
 	ADM_LSM_AUDPROC_CAL_TYPE,
 	ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE,
 	ADM_AUDPROC_PERSISTENT_CAL_TYPE,
+	AFE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE,
 	MAX_CAL_TYPES,
 };
 
 #define AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE
 #define AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE
+#define FE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE AFE_FB_SPKR_PROT_V4_EX_VI_CAL_TYPE
 
 #define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_CAL_TYPE
 
@@ -123,6 +125,7 @@
 
 #define TOPOLOGY_SPECIFIC_CHANNEL_INFO
 #define MSM_SPKR_PROT_SPV3
+#define MSM_SPKR_PROT_SPV4
 
 enum {
 	VERSION_0_0,
@@ -375,6 +378,16 @@
 	int32_t		status[SP_V2_NUM_MAX_SPKRS];
 };
 
+struct audio_cal_info_sp_v4_ex_vi_param {
+	int32_t		ftm_re_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		ftm_Bl_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		ftm_Rms_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		ftm_Kms_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		ftm_freq_q20[SP_V2_NUM_MAX_SPKRS];
+	int32_t		ftm_Qms_q24[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	status[SP_V2_NUM_MAX_SPKRS];
+};
+
 struct audio_cal_info_sp_th_vi_param {
 	/*
 	 * mode should be first param, add new params later to this.
@@ -797,6 +810,7 @@
 	struct audio_cal_header				hdr;
 	struct audio_cal_type_sp_th_vi_v_vali_param	cal_type;
 };
+
 struct audio_cal_type_sp_ex_vi_param {
 	struct audio_cal_type_header			cal_hdr;
 	struct audio_cal_data				cal_data;
@@ -807,4 +821,16 @@
 	struct audio_cal_header				hdr;
 	struct audio_cal_type_sp_ex_vi_param		cal_type;
 };
+
+struct audio_cal_type_sp_v4_ex_vi_param {
+	struct audio_cal_type_header			cal_hdr;
+	struct audio_cal_data				cal_data;
+	struct audio_cal_info_sp_v4_ex_vi_param		cal_info;
+};
+
+struct audio_cal_sp_v4_ex_vi_param {
+	struct audio_cal_header				hdr;
+	struct audio_cal_type_sp_v4_ex_vi_param		cal_type;
+};
+
 #endif /* _UAPI_MSM_AUDIO_CALIBRATION_H */
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index 981060c..2e7779a 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -96,7 +96,7 @@
 static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm);
 static u32 swr_master_read(struct swr_mstr_ctrl *swrm, unsigned int reg_addr);
 static void swr_master_write(struct swr_mstr_ctrl *swrm, u16 reg_addr, u32 val);
-
+static int swrm_runtime_resume(struct device *dev);
 
 static u8 swrm_get_clk_div(int mclk_freq, int bus_clk_freq)
 {
@@ -853,6 +853,8 @@
 	mutex_unlock(&swrm->devlock);
 
 	pm_runtime_get_sync(swrm->dev);
+	if (swrm->req_clk_switch)
+		swrm_runtime_resume(swrm->dev);
 	ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, len);
 
 	if (!ret)
@@ -886,6 +888,8 @@
 	mutex_unlock(&swrm->devlock);
 
 	pm_runtime_get_sync(swrm->dev);
+	if (swrm->req_clk_switch)
+		swrm_runtime_resume(swrm->dev);
 	ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
 
 	pm_runtime_put_autosuspend(swrm->dev);
@@ -2032,16 +2036,22 @@
 				swrm->intr_mask);
 			break;
 		case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW:
-			dev_dbg(swrm->dev, "%s: SWR read FIFO overflow\n",
-				__func__);
+			value = swr_master_read(swrm, SWRM_CMD_FIFO_STATUS);
+			dev_err(swrm->dev,
+				"%s: SWR read FIFO overflow fifo status 0x%x\n",
+				__func__, value);
 			break;
 		case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW:
-			dev_dbg(swrm->dev, "%s: SWR read FIFO underflow\n",
-				__func__);
+			value = swr_master_read(swrm, SWRM_CMD_FIFO_STATUS);
+			dev_err(swrm->dev,
+				"%s: SWR read FIFO underflow fifo status 0x%x\n",
+				__func__, value);
 			break;
 		case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW:
-			dev_dbg(swrm->dev, "%s: SWR write FIFO overflow\n",
-				__func__);
+			value = swr_master_read(swrm, SWRM_CMD_FIFO_STATUS);
+			dev_err(swrm->dev,
+				"%s: SWR write FIFO overflow fifo status %x\n",
+				__func__, value);
 			swr_master_write(swrm, SWRM_CMD_FIFO_CMD, 0x1);
 			break;
 		case SWRM_INTERRUPT_STATUS_CMD_ERROR:
@@ -2337,7 +2347,7 @@
 
 static int swrm_master_init(struct swr_mstr_ctrl *swrm)
 {
-	int ret = 0;
+	int ret = 0, i = 0;
 	u32 val;
 	u8 row_ctrl = SWR_ROW_50;
 	u8 col_ctrl = SWR_MIN_COL;
@@ -2348,6 +2358,18 @@
 	u32 temp = 0;
 	int len = 0;
 
+	/* SW workaround to gate hw_ctl for SWR version >=1.6 */
+	if (swrm->version >= SWRM_VERSION_1_6) {
+		if (swrm->swrm_hctl_reg) {
+			temp = ioread32(swrm->swrm_hctl_reg);
+			temp &= 0xFFFFFFFD;
+			iowrite32(temp, swrm->swrm_hctl_reg);
+			usleep_range(500, 505);
+			temp = ioread32(swrm->swrm_hctl_reg);
+			dev_dbg(swrm->dev, "%s: hctl_reg val: 0x%x\n",
+				__func__, temp);
+		}
+	}
 	ssp_period = swrm_get_ssp_period(swrm, SWRM_ROW_50,
 					SWRM_COL_02, SWRM_FRAME_SYNC_SEL);
 	dev_dbg(swrm->dev, "%s: ssp_period: %d\n", __func__, ssp_period);
@@ -2403,6 +2425,13 @@
 		dev_err(swrm->dev,
 			"%s: swr link failed to connect\n",
 			__func__);
+		for (i = 0; i < len; i++) {
+			usleep_range(50, 55);
+			dev_err(swrm->dev,
+				"%s:reg:0x%x val:0x%x\n",
+				__func__,
+				reg[i], swr_master_read(swrm, reg[i]));
+		}
 		return -EINVAL;
 	}
 	/*
@@ -2415,14 +2444,6 @@
 				(swr_master_read(swrm,
 					SWRM_CMD_FIFO_CFG_ADDR) | 0x80000000));
 
-	/* SW workaround to gate hw_ctl for SWR version >=1.6 */
-	if (swrm->version >= SWRM_VERSION_1_6) {
-		if (swrm->swrm_hctl_reg) {
-			temp = ioread32(swrm->swrm_hctl_reg);
-			temp &= 0xFFFFFFFD;
-			iowrite32(temp, swrm->swrm_hctl_reg);
-		}
-	}
 	return ret;
 }
 
@@ -2755,6 +2776,7 @@
 			"%s: Error in master Initialization , err %d\n",
 			__func__, ret);
 		mutex_unlock(&swrm->mlock);
+		ret = -EPROBE_DEFER;
 		goto err_mstr_init_fail;
 	}
 
@@ -2803,11 +2825,17 @@
 err_mstr_init_fail:
 	swr_unregister_master(&swrm->master);
 err_mstr_fail:
-	if (swrm->reg_irq)
+	if (swrm->reg_irq) {
 		swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
 				swrm, SWR_IRQ_FREE);
-	else if (swrm->irq)
+	} else if (swrm->irq) {
 		free_irq(swrm->irq, swrm);
+		irqd_set_trigger_type(
+			irq_get_irq_data(swrm->irq),
+			IRQ_TYPE_NONE);
+	}
+	if (swrm->swr_irq_wakeup_capable)
+		irq_set_irq_wake(swrm->irq, 0);
 err_irq_fail:
 	mutex_destroy(&swrm->irq_lock);
 	mutex_destroy(&swrm->mlock);
@@ -2827,13 +2855,17 @@
 {
 	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
 
-	if (swrm->reg_irq)
+	if (swrm->reg_irq) {
 		swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
 				swrm, SWR_IRQ_FREE);
-	else if (swrm->irq)
+	} else if (swrm->irq) {
 		free_irq(swrm->irq, swrm);
-	else if (swrm->wake_irq > 0)
+		irqd_set_trigger_type(
+			irq_get_irq_data(swrm->irq),
+			IRQ_TYPE_NONE);
+	} else if (swrm->wake_irq > 0) {
 		free_irq(swrm->wake_irq, swrm);
+	}
 	if (swrm->swr_irq_wakeup_capable)
 		irq_set_irq_wake(swrm->irq, 0);
 	cancel_work_sync(&swrm->wakeup_work);
@@ -2991,6 +3023,8 @@
 	else
 		pm_runtime_set_autosuspend_delay(&pdev->dev,
 				auto_suspend_timer);
+	if (swrm->req_clk_switch)
+		swrm->req_clk_switch = false;
 	mutex_unlock(&swrm->reslock);
 
 	trace_printk("%s: pm_runtime: resume done, state:%d\n",
@@ -3296,8 +3330,12 @@
 		}
 		mutex_lock(&swrm->mlock);
 		if (swrm->clk_src != *(int *)data) {
-			if (swrm->state == SWR_MSTR_UP)
+			if (swrm->state == SWR_MSTR_UP) {
+				swrm->req_clk_switch = true;
 				swrm_device_suspend(&pdev->dev);
+				if (swrm->state == SWR_MSTR_UP)
+					swrm->req_clk_switch = false;
+			}
 			swrm->clk_src = *(int *)data;
 		}
 		mutex_unlock(&swrm->mlock);
diff --git a/soc/swr-mstr-ctrl.h b/soc/swr-mstr-ctrl.h
index 5439ce6..c7d42b0 100644
--- a/soc/swr-mstr-ctrl.h
+++ b/soc/swr-mstr-ctrl.h
@@ -171,6 +171,7 @@
 	bool dev_up;
 	bool ipc_wakeup_triggered;
 	bool aud_core_err;
+	bool req_clk_switch;
 	struct pm_qos_request pm_qos_req;
 	enum swrm_pm_state pm_state;
 	wait_queue_head_t pm_wq;