ASoC: msm: qdsp6v2: Afe support for speaker protection

For feedback speaker protection feature afe driver
sends the RX and TX speaker calibration or processing
parameters to DSP. Change exposes get/set interface
to exchange speaker protection parameters.

Change-Id: Ie3e17e9e8aec611e7937bc520e5bde4214c24b3c
Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org>
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 93f0aa90..bc68c24 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -6252,6 +6252,93 @@
 	/* Size in bytes of the variable payload in shared memory */
 } __packed;
 
+/* This module represents the Rx processing of Feedback speaker protection.
+ * It contains the excursion control, thermal protection,
+ * analog clip manager features in it.
+ * This module id will support following param ids.
+ * - AFE_PARAM_ID_FBSP_MODE_RX_CFG
+ */
+
+#define AFE_MODULE_FB_SPKR_PROT_RX 0x0001021C
+
+#define AFE_PARAM_ID_FBSP_MODE_RX_CFG 0x0001021D
+
+struct asm_fbsp_mode_rx_cfg {
+	uint32_t minor_version;
+	uint32_t mode;
+} __packed;
+
+/* This module represents the VI processing of feedback speaker protection.
+ * It will receive Vsens and Isens from codec and generates necessary
+ * parameters needed by Rx processing.
+ * This module id will support following param ids.
+ * - AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG
+ * - AFE_PARAM_ID_CALIB_RES_CFG
+ * - AFE_PARAM_ID_FEEDBACK_PATH_CFG
+ */
+
+#define AFE_MODULE_FB_SPKR_PROT_VI_PROC 0x00010226
+
+#define AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG 0x0001022A
+
+struct asm_spkr_calib_vi_proc_cfg {
+	uint32_t minor_version;
+	int32_t	r0_cali_q24;
+	int16_t	t0_cali_q6;
+	int16_t	reserved;
+} __packed;
+
+#define AFE_PARAM_ID_CALIB_RES_CFG 0x0001022B
+
+struct asm_calib_res_cfg {
+	uint32_t minor_version;
+	int32_t	r0_cali_q24;
+	uint32_t th_vi_ca_state;
+} __packed;
+
+#define AFE_PARAM_ID_FEEDBACK_PATH_CFG 0x0001022C
+
+struct asm_feedback_path_cfg {
+	uint32_t minor_version;
+	int32_t	dst_portid;
+	int32_t	num_channels;
+	int32_t	chan_info[4];
+} __packed;
+
+#define AFE_PARAM_ID_MODE_VI_PROC_CFG 0x00010227
+
+struct asm_mode_vi_proc_cfg {
+	uint32_t minor_version;
+	uint32_t cal_mode;
+} __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;
+} __packed;
+
+struct afe_spkr_prot_config_command {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+	union afe_spkr_prot_config prot_config;
+} __packed;
+
+struct afe_spkr_prot_get_vi_calib {
+	struct afe_port_cmd_get_param_v2 get_param;
+	struct afe_port_param_data_v2 pdata;
+	struct asm_calib_res_cfg res_cfg;
+} __packed;
+
+struct afe_spkr_prot_calib_get_resp {
+	uint32_t status;
+	struct afe_port_param_data_v2 pdata;
+	struct asm_calib_res_cfg res_cfg;
+} __packed;
+
+
 /* SRS TRUMEDIA start */
 /* topology */
 #define SRS_TRUMEDIA_TOPOLOGY_ID			0x00010D90
@@ -6355,8 +6442,6 @@
 } __packed;
 /* SRS TruMedia end */
 
-
-
 /* ERROR CODES */
 /* Success. The operation completed with no errors. */
 #define ADSP_EOK          0x00000000
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index 506e877..feac314 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -145,6 +145,9 @@
 int afe_rt_proxy_port_read(u32 buf_addr_p, u32 mem_map_handle, int bytes);
 int afe_port_start(u16 port_id, union afe_port_config *afe_config,
 	u32 rate);
+int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
+	int l_ch, int r_ch);
+int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib);
 int afe_port_stop_nowait(int port_id);
 int afe_apply_gain(u16 port_id, u16 gain);
 int afe_q6_interface_prepare(void);
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
index b71132e..741c646 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.c
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -767,6 +767,13 @@
 done:
 	return;
 }
+void get_spk_protection_cfg(struct acdb_spk_prot_cfg *prot_cfg)
+{
+	/*Disable the processing or calibration mode*/
+	if (prot_cfg) {
+		prot_cfg->mode = -1;
+	}
+}
 
 static int acdb_open(struct inode *inode, struct file *f)
 {
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.h b/sound/soc/msm/qdsp6v2/audio_acdb.h
index 0b6110d..ceb58bd 100644
--- a/sound/soc/msm/qdsp6v2/audio_acdb.h
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.h
@@ -41,6 +41,17 @@
 	atomic_t		cal_paddr;
 };
 
+struct acdb_spk_prot_cfg {
+	int t0;
+	int r0;
+	int mode;
+};
+
+struct acdb_spk_prot_status {
+	int r0;
+	int status;
+};
+
 uint32_t get_voice_rx_topology(void);
 uint32_t get_voice_tx_topology(void);
 uint32_t get_adm_rx_topology(void);
@@ -63,5 +74,6 @@
 void get_vocstrm_cal(struct acdb_cal_block *cal_block);
 void get_vocvol_cal(struct acdb_cal_block *cal_block);
 void get_sidetone_cal(struct sidetone_cal *cal_data);
+void get_spk_protection_cfg(struct acdb_spk_prot_cfg *prot_cfg);
 
 #endif
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 1b5ad17..de0b85c 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -25,7 +25,6 @@
 
 #include "audio_acdb.h"
 
-
 struct afe_ctl {
 	void *apr;
 	atomic_t state;
@@ -44,6 +43,7 @@
 	atomic_t mem_map_cal_handles[MAX_AUDPROC_TYPES];
 	atomic_t mem_map_cal_index;
 	u16 dtmf_gen_rx_portid;
+	struct afe_spkr_prot_calib_get_resp calib_data;
 };
 
 static struct afe_ctl this_afe;
@@ -56,6 +56,10 @@
 
 static int32_t afe_callback(struct apr_client_data *data, void *priv)
 {
+	if (!data) {
+		pr_err("%s: Invalid param data\n", __func__);
+		return -EINVAL;
+	}
 	if (data->opcode == RESET_EVENTS) {
 		pr_debug("q6afe: reset event = %d %d apr[%p]\n",
 			data->reset_event, data->reset_proc, this_afe.apr);
@@ -69,11 +73,30 @@
 			this_afe.task->comm, this_afe.task->pid);
 		return 0;
 	}
-	pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x\n",
-				__func__, data->opcode,
-				((uint32_t *)(data->payload))[0],
-				((uint32_t *)(data->payload))[1]);
-	if (data->payload_size) {
+	pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x size = %d\n",
+			__func__, data->opcode,
+			((uint32_t *)(data->payload))[0],
+			((uint32_t *)(data->payload))[1],
+			 data->payload_size);
+	if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) {
+		u8 *payload = data->payload;
+		if ((data->payload_size < sizeof(this_afe.calib_data))
+			|| !payload || (data->token >= AFE_MAX_PORTS)) {
+			pr_err("%s size %d payload %p token %d\n",
+			__func__, data->payload_size, payload, data->token);
+			return -EINVAL;
+		}
+		memcpy(&this_afe.calib_data, payload,
+			   sizeof(this_afe.calib_data));
+		if (!this_afe.calib_data.status) {
+			atomic_set(&this_afe.state, 0);
+			pr_err("%s rest %d state %x\n" , __func__
+			, this_afe.calib_data.res_cfg.r0_cali_q24,
+			this_afe.calib_data.res_cfg.th_vi_ca_state);
+		} else
+			atomic_set(&this_afe.state, -1);
+		wake_up(&this_afe.wait[data->token]);
+	} else if (data->payload_size) {
 		uint32_t *payload;
 		uint16_t port_id = 0;
 		payload = data->payload;
@@ -346,14 +369,138 @@
 	return;
 }
 
+static int afe_spk_prot_prepare(int port, int param_id,
+		union afe_spkr_prot_config *prot_config)
+{
+	int ret = -EINVAL;
+	int index = 0;
+	struct afe_spkr_prot_config_command config;
+
+	if (!prot_config) {
+		pr_err("%s Invalid params\n", __func__);
+		goto fail_cmd;
+	}
+	if ((q6audio_validate_port(port) < 0)) {
+		pr_err("%s invalid port %d", __func__, port);
+		goto fail_cmd;
+	}
+	memset(&config, 0 , sizeof(config));
+	index = q6audio_get_port_index(port);
+	switch (param_id) {
+	case AFE_PARAM_ID_FBSP_MODE_RX_CFG:
+		config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_RX;
+		break;
+	case AFE_PARAM_ID_FEEDBACK_PATH_CFG:
+	case AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG:
+	case AFE_PARAM_ID_MODE_VI_PROC_CFG:
+		config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC;
+		break;
+	default:
+		goto fail_cmd;
+		break;
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port);
+	config.param.payload_size = sizeof(config) - sizeof(config.hdr)
+		- sizeof(config.param);
+	config.pdata.param_id = param_id;
+	config.pdata.param_size = sizeof(config.prot_config);
+	config.prot_config = *prot_config;
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+	if (ret < 0) {
+		pr_err("%s: Setting param for port %d param[0x%x]failed\n",
+		 __func__, port, param_id);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) != 0) {
+		pr_err("%s: config cmd failed\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = 0;
+fail_cmd:
+	pr_err("%s config.pdata.param_id %x status %d\n",
+	__func__, config.pdata.param_id, ret);
+	return ret;
+}
+
+static void afe_send_cal_spkr_prot_tx(int port_id)
+{
+	struct acdb_spk_prot_cfg prot_cfg;
+	union afe_spkr_prot_config afe_spk_config;
+
+	/*Get spkr protection cfg data*/
+	get_spk_protection_cfg(&prot_cfg);
+
+	if (!prot_cfg.mode || prot_cfg.mode == 1) {
+		afe_spk_config.mode_rx_cfg.minor_version = 1;
+		afe_spk_config.mode_rx_cfg.mode =
+		(uint32_t)prot_cfg.mode;
+		if (afe_spk_prot_prepare(port_id,
+			AFE_PARAM_ID_MODE_VI_PROC_CFG,
+			&afe_spk_config))
+			pr_err("%s TX VI_PROC_CFG failed\n", __func__);
+		afe_spk_config.vi_proc_cfg.minor_version = 1;
+		afe_spk_config.vi_proc_cfg.r0_cali_q24 =
+		(uint32_t) prot_cfg.r0;
+		afe_spk_config.vi_proc_cfg.t0_cali_q6 =
+		(uint32_t) prot_cfg.t0;
+		if (afe_spk_prot_prepare(port_id,
+			AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG,
+			&afe_spk_config))
+			pr_err("%s SPKR_CALIB_VI_PROC_CFG failed\n",
+				__func__);
+	}
+}
+
+static void afe_send_cal_spkr_prot_rx(int port_id)
+{
+	struct acdb_spk_prot_cfg prot_cfg;
+	union afe_spkr_prot_config afe_spk_config;
+
+	/*Get spkr protection cfg data*/
+	get_spk_protection_cfg(&prot_cfg);
+
+	if (!prot_cfg.mode || prot_cfg.mode == 1) {
+		afe_spk_config.mode_rx_cfg.mode =
+		(uint32_t)prot_cfg.mode;
+		afe_spk_config.mode_rx_cfg.minor_version = 1;
+		if (afe_spk_prot_prepare(port_id,
+			AFE_PARAM_ID_FBSP_MODE_RX_CFG,
+			&afe_spk_config))
+			pr_err("%s RX MODE_VI_PROC_CFG failed\n",
+				   __func__);
+	}
+}
+
 void afe_send_cal(u16 port_id)
 {
 	pr_debug("%s\n", __func__);
 
-	if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)
+	if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) {
+		afe_send_cal_spkr_prot_tx(port_id);
 		afe_send_cal_block(TX_CAL, port_id);
-	else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX)
+	} else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) {
+		afe_send_cal_spkr_prot_rx(port_id);
 		afe_send_cal_block(RX_CAL, port_id);
+	}
 }
 
 int afe_port_start(u16 port_id, union afe_port_config *afe_config,
@@ -2299,6 +2446,107 @@
 	return ret;
 }
 
+int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp)
+{
+	int ret = -EINVAL;
+	int index = 0, port = SLIMBUS_4_TX;
+
+	if (!calib_resp) {
+		pr_err("%s Invalid params\n", __func__);
+		goto fail_cmd;
+	}
+	if ((q6audio_validate_port(port) < 0)) {
+		pr_err("%s invalid port %d\n", __func__, port);
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(port);
+	calib_resp->get_param.hdr.hdr_field =
+	APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+	APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	calib_resp->get_param.hdr.pkt_size = sizeof(*calib_resp);
+	calib_resp->get_param.hdr.src_port = 0;
+	calib_resp->get_param.hdr.dest_port = 0;
+	calib_resp->get_param.hdr.token = index;
+	calib_resp->get_param.hdr.opcode =  AFE_PORT_CMD_GET_PARAM_V2;
+	calib_resp->get_param.mem_map_handle = 0;
+	calib_resp->get_param.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC;
+	calib_resp->get_param.param_id = AFE_PARAM_ID_CALIB_RES_CFG;
+	calib_resp->get_param.payload_address_lsw = 0;
+	calib_resp->get_param.payload_address_msw = 0;
+	calib_resp->get_param.payload_size = sizeof(*calib_resp)
+		- sizeof(calib_resp->get_param);
+	calib_resp->get_param.port_id = q6audio_get_port_id(port);
+	calib_resp->pdata.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC;
+	calib_resp->pdata.param_id = AFE_PARAM_ID_CALIB_RES_CFG;
+	calib_resp->pdata.param_size = sizeof(calib_resp->res_cfg);
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *)calib_resp);
+	if (ret < 0) {
+		pr_err("%s: get param port %d param id[0x%x]failed\n",
+			   __func__, port, calib_resp->get_param.param_id);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) != 0) {
+		pr_err("%s: config cmd failed\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	memcpy(&calib_resp->res_cfg , &this_afe.calib_data.res_cfg,
+		sizeof(this_afe.calib_data.res_cfg));
+	pr_debug("%s state %d resistance %d\n", __func__,
+			 calib_resp->res_cfg.th_vi_ca_state,
+			 calib_resp->res_cfg.r0_cali_q24);
+	ret = 0;
+fail_cmd:
+	return ret;
+}
+
+int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
+	int l_ch, int r_ch)
+{
+	int ret = -EINVAL;
+	union afe_spkr_prot_config prot_config;
+	int index = 0;
+
+	if ((q6audio_validate_port(src_port) < 0) ||
+		(q6audio_validate_port(dst_port) < 0)) {
+		pr_err("%s invalid ports src %d dst %d",
+			__func__, src_port, dst_port);
+		goto fail_cmd;
+	}
+	if (!l_ch && !r_ch) {
+		pr_err("%s error ch values zero\n", __func__);
+		goto fail_cmd;
+	}
+	pr_debug("%s src_port %x  dst_port %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 =
+		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 (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;
+	prot_config.feedback_path_cfg.minor_version = 1;
+	ret = afe_spk_prot_prepare(src_port,
+			AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config);
+fail_cmd:
+	return ret;
+}
+
 static int __init afe_init(void)
 {
 	int i = 0;