Merge "asoc: Register widgets and intercons only when supported"
diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c
index 8a179fd..77b25de 100644
--- a/asoc/msm-lsm-client.c
+++ b/asoc/msm-lsm-client.c
@@ -1700,6 +1700,17 @@
 	u16 stage_idx;
 };
 
+struct lsm_params_get_info_32 {
+	u32 module_id;
+	u16 instance_id;
+	u16 reserved;
+	u32 param_id;
+	u32 param_size;
+	uint32_t param_type;
+	__u16 stage_idx;
+	u8 payload[0];
+} __packed;
+
 struct snd_lsm_module_params_32 {
 	compat_uptr_t params;
 	u32 num_params;
@@ -1717,6 +1728,8 @@
 		_IOW('U', 0x0F, struct snd_lsm_event_status_v3_32),
 	SNDRV_LSM_SET_MODULE_PARAMS_V2_32 =
 		_IOW('U', 0x13, struct snd_lsm_module_params_32),
+	SNDRV_LSM_GET_MODULE_PARAMS_32 =
+		_IOWR('U', 0x14, struct lsm_params_get_info_32),
 };
 
 static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
@@ -2096,6 +2109,95 @@
 		kfree(params32);
 		break;
 	}
+	case SNDRV_LSM_GET_MODULE_PARAMS_32: {
+		struct lsm_params_get_info_32 p_info_32, *param_info_rsp = NULL;
+		struct lsm_params_get_info *p_info = NULL;
+
+		memset(&p_info_32, 0 , sizeof(p_info_32));
+		if (!prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "GET_MODULE_PARAMS_32");
+			err = -EINVAL;
+			goto done;
+		}
+
+		if (copy_from_user(&p_info_32, arg, sizeof(p_info_32))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "GET_MODULE_PARAMS_32",
+				sizeof(p_info_32));
+			err = -EFAULT;
+			goto done;
+		}
+		size = sizeof(p_info_32);
+		p_info = kzalloc(size, GFP_KERNEL);
+
+		if (!p_info) {
+			err = -ENOMEM;
+			goto done;
+		}
+
+		p_info->module_id = p_info_32.module_id;
+		p_info->param_id = p_info_32.param_id;
+		p_info->param_size = p_info_32.param_size;
+		p_info->param_type = p_info_32.param_type;
+		p_info->instance_id = p_info_32.instance_id;
+		p_info->stage_idx = p_info_32.stage_idx;
+
+		prtd->lsm_client->get_param_payload = kzalloc(p_info_32.param_size,
+							      GFP_KERNEL);
+		if (!prtd->lsm_client->get_param_payload) {
+			err = -ENOMEM;
+			kfree(p_info);
+			goto done;
+		}
+		prtd->lsm_client->param_size = p_info_32.param_size;
+
+		err = q6lsm_get_one_param(prtd->lsm_client, p_info,
+					  LSM_GET_CUSTOM_PARAMS);
+		if (err) {
+			dev_err(rtd->dev,
+				"%s: Failed to get custom param, err=%d\n",
+				__func__, err);
+			kfree(p_info);
+			kfree(prtd->lsm_client->get_param_payload);
+			goto done;
+		}
+
+		size = sizeof(p_info_32) + p_info_32.param_size;
+		param_info_rsp = kzalloc(size, GFP_KERNEL);
+
+		if (!param_info_rsp) {
+			err = -ENOMEM;
+			kfree(p_info);
+			kfree(prtd->lsm_client->get_param_payload);
+			goto done;
+		}
+
+		if (!access_ok(VERIFY_WRITE, arg, size)) {
+			dev_err(rtd->dev,
+				"%s: Failed to verify write, size = %d\n",
+				__func__, size);
+			err = -EFAULT;
+			goto free;
+		}
+
+		memcpy(param_info_rsp, &p_info_32, sizeof(p_info_32));
+		memcpy(param_info_rsp->payload, prtd->lsm_client->get_param_payload,
+			p_info_32.param_size);
+
+		if (copy_to_user(arg, param_info_rsp, size)) {
+			dev_err(rtd->dev, "%s: Failed to copy payload to user, size = %d\n",
+				__func__, size);
+			err = -EFAULT;
+		}
+free:
+		kfree(p_info);
+		kfree(param_info_rsp);
+		kfree(prtd->lsm_client->get_param_payload);
+		break;
+	}
 	case SNDRV_LSM_REG_SND_MODEL_V2:
 	case SNDRV_LSM_SET_PARAMS:
 	case SNDRV_LSM_SET_MODULE_PARAMS:
@@ -2303,6 +2405,81 @@
 		break;
 	}
 
+	case SNDRV_LSM_GET_MODULE_PARAMS: {
+		struct lsm_params_get_info temp_p_info, *p_info = NULL;
+
+		memset(&temp_p_info, 0, sizeof(temp_p_info));
+		if (!prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "GET_MODULE_PARAMS_32");
+			err = -EINVAL;
+			goto done;
+		}
+
+		if (copy_from_user(&temp_p_info, arg, sizeof(temp_p_info))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "GET_MODULE_PARAMS_32",
+				sizeof(temp_p_info));
+			err = -EFAULT;
+			goto done;
+		}
+		size = sizeof(temp_p_info) + temp_p_info.param_size;
+		p_info = kzalloc(size, GFP_KERNEL);
+
+		if (!p_info) {
+			err = -ENOMEM;
+			goto done;
+		}
+
+		p_info->module_id = temp_p_info.module_id;
+		p_info->param_id = temp_p_info.param_id;
+		p_info->param_size = temp_p_info.param_size;
+		p_info->param_type = temp_p_info.param_type;
+		p_info->instance_id = temp_p_info.instance_id;
+		p_info->stage_idx = temp_p_info.stage_idx;
+
+		prtd->lsm_client->get_param_payload = kzalloc(temp_p_info.param_size,
+							      GFP_KERNEL);
+		if (!prtd->lsm_client->get_param_payload) {
+			err = -ENOMEM;
+			kfree(p_info);
+			goto done;
+		}
+
+		prtd->lsm_client->param_size =  p_info->param_size;
+		err = q6lsm_get_one_param(prtd->lsm_client, p_info,
+					  LSM_GET_CUSTOM_PARAMS);
+		if (err) {
+			dev_err(rtd->dev,
+				"%s: Failed to get custom param, err=%d\n",
+				__func__, err);
+			goto free;
+		}
+
+		if (!access_ok(VERIFY_WRITE, arg, size)) {
+			dev_err(rtd->dev,
+				"%s: Failed to verify write, size = %d\n",
+				__func__, size);
+			err = -EFAULT;
+			goto free;
+		}
+
+		memcpy(p_info->payload, prtd->lsm_client->get_param_payload,
+			temp_p_info.param_size);
+
+		if (copy_to_user(arg, p_info, sizeof(struct lsm_params_get_info) +
+				 p_info->param_size)) {
+			dev_err(rtd->dev, "%s: Failed to copy payload to user, size = %d\n",
+				__func__, size);
+			err = -EFAULT;
+		}
+free:
+		kfree(p_info);
+		kfree(prtd->lsm_client->get_param_payload);
+		break;
+	}
 	case SNDRV_LSM_EVENT_STATUS:
 	case SNDRV_LSM_GENERIC_DET_EVENT: {
 		struct snd_lsm_event_status *user = NULL;
diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c
index c00bb94..cc57a25 100644
--- a/dsp/q6lsm.c
+++ b/dsp/q6lsm.c
@@ -190,6 +190,58 @@
 					client->priv);
 		spin_unlock_irqrestore(&lsm_session_lock, flags);
 		return 0;
+	} else if (data->opcode == LSM_SESSION_CMDRSP_GET_PARAMS_V3 ||
+		data->opcode == LSM_SESSION_CMDRSP_GET_PARAMS_V2) {
+
+		uint32_t payload_min_size_expected = 0;
+		uint32_t param_size = 0, ret = 0;
+		/*
+		 * sizeof(uint32_t) is added to accomodate the status field
+		 * in adsp response payload
+		 */
+
+		if (data->opcode == LSM_SESSION_CMDRSP_GET_PARAMS_V3)
+			payload_min_size_expected  =  sizeof(uint32_t) +
+						sizeof(struct param_hdr_v3);
+		else
+			payload_min_size_expected  =  sizeof(uint32_t) +
+						sizeof(struct param_hdr_v2);
+
+		if (data->payload_size < payload_min_size_expected) {
+			pr_err("%s: invalid payload size %d expected size %d\n",
+				__func__, data->payload_size,
+				payload_min_size_expected);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		if (data->opcode == LSM_SESSION_CMDRSP_GET_PARAMS_V3)
+			param_size = payload[4];
+		else
+			param_size = payload[3];
+
+		if (data->payload_size != payload_min_size_expected + param_size) {
+			pr_err("%s: cmdrsp_get_params error payload size %d expected size %d\n",
+				__func__, data->payload_size,
+				payload_min_size_expected + param_size);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		if (client->param_size != param_size) {
+			pr_err("%s: response payload size %d mismatched with user requested %d\n",
+			    __func__, param_size, client->param_size);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		memcpy((u8 *)client->get_param_payload,
+			(u8 *)payload + payload_min_size_expected, param_size);
+done:
+		spin_unlock_irqrestore(&lsm_session_lock, flags);
+		atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+		wake_up(&client->cmd_wait);
+		return ret;
 	} else if (data->opcode == APR_BASIC_RSP_RESULT) {
 		token = data->token;
 		switch (payload[0]) {
@@ -208,6 +260,8 @@
 		case LSM_CMD_ADD_TOPOLOGIES:
 		case LSM_SESSION_CMD_SET_PARAMS_V2:
 		case LSM_SESSION_CMD_SET_PARAMS_V3:
+		case LSM_SESSION_CMD_GET_PARAMS_V2:
+		case LSM_SESSION_CMD_GET_PARAMS_V3:
 			if (token != client->session &&
 			    payload[0] !=
 				LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) {
@@ -623,6 +677,48 @@
 	return ret;
 }
 
+static int q6lsm_get_params_v2(struct lsm_client *client,
+				struct mem_mapping_hdr *mem_hdr,
+				struct param_hdr_v2 *param_hdr)
+{
+	struct lsm_session_cmd_get_params_v2 lsm_get_param;
+	uint16_t pkt_size = sizeof(lsm_get_param);
+
+	memset(&lsm_get_param, 0, pkt_size);
+	q6lsm_add_hdr(client, &lsm_get_param.apr_hdr, pkt_size, true);
+	lsm_get_param.apr_hdr.opcode = LSM_SESSION_CMD_GET_PARAMS_V2;
+
+	if (mem_hdr != NULL)
+		lsm_get_param.mem_hdr = *mem_hdr;
+
+	memcpy(&lsm_get_param.param_info, param_hdr,
+		sizeof(struct param_hdr_v2));
+
+	return q6lsm_apr_send_pkt(client, client->apr, &lsm_get_param, true,
+				 NULL);
+}
+
+static int q6lsm_get_params_v3(struct lsm_client *client,
+				struct mem_mapping_hdr *mem_hdr,
+				struct param_hdr_v3 *param_hdr)
+{
+	struct lsm_session_cmd_get_params_v3 lsm_get_param;
+	uint16_t pkt_size = sizeof(lsm_get_param);
+
+	memset(&lsm_get_param, 0, pkt_size);
+	q6lsm_add_hdr(client, &lsm_get_param.apr_hdr, pkt_size, true);
+	lsm_get_param.apr_hdr.opcode = LSM_SESSION_CMD_GET_PARAMS_V3;
+
+	if (mem_hdr != NULL)
+		lsm_get_param.mem_hdr = *mem_hdr;
+
+	memcpy(&lsm_get_param.param_info, param_hdr,
+		sizeof(struct param_hdr_v3));
+
+	return q6lsm_apr_send_pkt(client, client->apr, &lsm_get_param, true,
+				 NULL);
+}
+
 static int q6lsm_set_params(struct lsm_client *client,
 			    struct mem_mapping_hdr *mem_hdr,
 			    uint8_t *param_data, uint32_t param_size,
@@ -665,6 +761,27 @@
 	return ret;
 }
 
+static int q6lsm_get_params(struct lsm_client *client,
+				struct mem_mapping_hdr *mem_hdr,
+				struct param_hdr_v3 *param_info)
+
+{
+	struct param_hdr_v2 param_info_v2;
+	int ret = 0;
+	bool iid_supported = q6common_is_instance_id_supported();
+	memset(&param_info_v2, 0, sizeof(struct param_hdr_v2));
+
+	if (iid_supported)
+		ret = q6lsm_get_params_v3(client, mem_hdr, param_info);
+	else {
+		param_info_v2.module_id = param_info->module_id;
+		param_info_v2.param_id = param_info->param_id;
+		param_info_v2.param_size = param_info->param_size;
+		ret = q6lsm_get_params_v2(client, mem_hdr, &param_info_v2);
+	}
+	return ret;
+}
+
 static int q6lsm_send_custom_topologies(struct lsm_client *client)
 {
 	int rc;
@@ -2313,6 +2430,36 @@
 }
 EXPORT_SYMBOL(q6lsm_set_one_param);
 
+int q6lsm_get_one_param(struct lsm_client *client,
+		struct lsm_params_get_info *p_info,
+		uint32_t param_type)
+{
+	struct param_hdr_v3 param_info;
+	int rc = 0;
+
+	memset(&param_info, 0, sizeof(param_info));
+
+	switch (param_type) {
+	case LSM_GET_CUSTOM_PARAMS: {
+		param_info.module_id = p_info->module_id;
+		param_info.instance_id = p_info->instance_id;
+		param_info.param_id = p_info->param_id;
+		param_info.param_size = p_info->param_size + sizeof(param_info);
+		rc = q6lsm_get_params(client, NULL, &param_info);
+		if (rc) {
+			pr_err("%s: LSM_GET_CUSTOM_PARAMS failed, rc %d\n",
+				__func__, rc);
+		}
+		break;
+
+	}
+	default:
+		pr_err("%s: wrong param_type 0x%x\n",
+			__func__, p_info->param_type);
+	}
+	return rc;
+}
+EXPORT_SYMBOL(q6lsm_get_one_param);
 
 /**
  * q6lsm_start -
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index 607b910..eed5652 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -11694,6 +11694,10 @@
 #define LSM_SESSION_CMD_SET_PARAMS			(0x00012A83)
 #define LSM_SESSION_CMD_SET_PARAMS_V2			(0x00012A8F)
 #define LSM_SESSION_CMD_SET_PARAMS_V3			(0x00012A92)
+#define LSM_SESSION_CMD_GET_PARAMS_V2			(0x00012A90)
+#define LSM_SESSION_CMDRSP_GET_PARAMS_V2		(0x00012A91)
+#define LSM_SESSION_CMD_GET_PARAMS_V3			(0x00012A93)
+#define LSM_SESSION_CMDRSP_GET_PARAMS_V3		(0x00012A94)
 #define LSM_SESSION_CMD_REGISTER_SOUND_MODEL		(0x00012A84)
 #define LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL		(0x00012A85)
 #define LSM_SESSION_CMD_START				(0x00012A86)
diff --git a/include/dsp/q6lsm.h b/include/dsp/q6lsm.h
index 02897d9..d5d6ad6 100644
--- a/include/dsp/q6lsm.h
+++ b/include/dsp/q6lsm.h
@@ -106,6 +106,8 @@
 	struct lsm_stage_config	stage_cfg[LSM_MAX_STAGES_PER_SESSION];
 	uint64_t	fe_id;
 	uint16_t	unprocessed_data;
+	void		*get_param_payload;
+	size_t		param_size;
 };
 
 struct lsm_stream_cmd_open_tx {
@@ -153,6 +155,18 @@
 	u32 param_data[0];
 } __packed;
 
+struct lsm_session_cmd_get_params_v2 {
+	struct apr_hdr apr_hdr;
+	struct mem_mapping_hdr mem_hdr;
+	struct param_hdr_v2 param_info;
+} __packed;
+
+struct lsm_session_cmd_get_params_v3 {
+	struct apr_hdr apr_hdr;
+	struct mem_mapping_hdr mem_hdr;
+	struct param_hdr_v3 param_info;
+} __packed;
+
 struct lsm_param_op_mode {
 	uint32_t	minor_version;
 	uint16_t	mode;
@@ -289,6 +303,9 @@
 int q6lsm_set_one_param(struct lsm_client *client,
 			struct lsm_params_info_v2 *p_info, void *data,
 			uint32_t param_type);
+int q6lsm_get_one_param(struct lsm_client *client,
+			struct lsm_params_get_info *p_info,
+			uint32_t param_type);
 void q6lsm_sm_set_param_data(struct lsm_client *client,
 		struct lsm_params_info_v2 *p_info,
 		size_t *offset);
diff --git a/include/uapi/sound/lsm_params.h b/include/uapi/sound/lsm_params.h
index 57dc3be..8e1a3d6 100644
--- a/include/uapi/sound/lsm_params.h
+++ b/include/uapi/sound/lsm_params.h
@@ -34,7 +34,8 @@
 #define LSM_POLLING_ENABLE (7)
 #define LSM_DET_EVENT_TYPE (8)
 #define LSM_LAB_CONTROL (9)
-#define LSM_PARAMS_MAX (LSM_LAB_CONTROL + 1)
+#define LSM_GET_CUSTOM_PARAMS (10)
+#define LSM_PARAMS_MAX (LSM_GET_CUSTOM_PARAMS + 1)
 
 #define LSM_EVENT_NON_TIME_STAMP_MODE (0)
 #define LSM_EVENT_TIME_STAMP_MODE (1)
@@ -284,6 +285,29 @@
 	__u16 num_channels;
 } __packed;
 
+/*
+ * Param get info  for each parameter type
+ * add "for SNDRV_LSM_GET_MODULE_PARAMS ioctl"
+ * Existing member variables:
+ * @module_id: Module to which parameter is to be set
+ * @instance_id: instance id of the param to which parameter is to be set
+ * @param_id: Parameter that is to be set
+ * @param_size: size of requested param
+ * @param_type: Parameter type as defined in values upto LSM_PARAMS_MAX
+ * @stage_idx: detection stage for which the param is applicable
+ * @payload: memory where requested param info will be populated
+ */
+struct lsm_params_get_info {
+	__u32 module_id;
+	__u16 instance_id;
+	__u16 reserved;
+	__u32 param_id;
+	__u32 param_size;
+	uint32_t param_type;
+	__u16 stage_idx;
+	__u8 payload[0];
+} __packed;
+
 #define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int)
 #define SNDRV_LSM_EVENT_STATUS	_IOW('U', 0x02, struct snd_lsm_event_status)
 #define SNDRV_LSM_ABORT_EVENT	_IOW('U', 0x03, int)
@@ -311,5 +335,6 @@
 					struct snd_lsm_session_data_v2)
 #define SNDRV_LSM_SET_MODULE_PARAMS_V2 _IOW('U', 0x13, \
 					struct snd_lsm_module_params)
-
+#define SNDRV_LSM_GET_MODULE_PARAMS _IOWR('U', 0x14, \
+					struct lsm_params_get_info)
 #endif