Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65fa58ef authored by Jitendra Singh Naruka's avatar Jitendra Singh Naruka Committed by Stephen Boyd
Browse files

ASoC: msm: Add support for customized stereo mixing in DSP



For the use case of playback through tunnel mode, customized
mixing for stereo content is required for hearing impaired
Add support to enable customized stereo mixing in DSP.
The customized mixing supported is the average of left and
right channel and further duplicating the averaged output to
both left and right channels.

Change-Id: I8639d1e298a24a76a32fffdc2c0e372a48aeeed0
Signed-off-by: default avatarJitendra Singh Naruka <jnaruk@codeaurora.org>
parent b5039861
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -289,6 +289,28 @@ struct adm_param_data_v5 {
	 */
} __packed;

/* set customized mixing on matrix mixer */
#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5                        0x00010344
struct adm_cmd_set_pspd_mtmx_strtr_params_v5 {
	struct apr_hdr hdr;
	/* LSW of parameter data payload address.*/
	u32		payload_addr_lsw;
	/* MSW of parameter data payload address.*/
	u32		payload_addr_msw;
	/* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */
	/* command. If mem_map_handle is zero implies the message is in */
	/* the payload */
	u32		mem_map_handle;
	/* Size in bytes of the variable payload accompanying this */
	/* message or in shared memory. This is used for parsing the */
	/* parameter payload. */
	u32		payload_size;
	u16		direction;
	u16		sessionid;
	u16		deviceid;
	u16		reserved;
} __packed;

/* Defined specifically for in-band use, includes params */
struct adm_cmd_set_pp_params_inband_v5 {
	struct apr_hdr hdr;
@@ -308,7 +330,6 @@ struct adm_cmd_set_pp_params_inband_v5 {
	struct adm_param_data_v5        params;
} __packed;


/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command.
 */
#define ADM_CMDRSP_DEVICE_OPEN_V5                      0x00010329
@@ -6929,6 +6950,17 @@ struct afe_port_cmd_set_aanc_acdb_table {
#define RMS_PARAM_FIRST_SAMPLE 0x10009012
#define RMS_PAYLOAD_LEN 4

/* Customized mixing in matix mixer */
#define MTMX_MODULE_ID_DEFAULT_CHMIXER  0x00010341
#define DEFAULT_CHMIXER_PARAM_ID_COEFF  0x00010342
#define CUSTOM_STEREO_PAYLOAD_SIZE	9
#define CUSTOM_STEREO_CMD_PARAM_SIZE	24
#define CUSTOM_STEREO_NUM_OUT_CH	0x0002
#define CUSTOM_STEREO_NUM_IN_CH		0x0002
#define CUSTOM_STEREO_INDEX_PARAM	0x0002
#define Q14_GAIN_ZERO_POINT_FIVE	0x2000
#define Q14_GAIN_UNITY			0x4000

struct afe_svc_cmd_set_clip_bank_selection {
	struct apr_hdr hdr;
	struct afe_svc_cmd_set_param param;
+3 −0
Original line number Diff line number Diff line
@@ -62,4 +62,7 @@ void adm_set_multi_ch_map(char *channel_map);

void adm_get_multi_ch_map(char *channel_map);

int adm_set_stereo_to_custom_stereo(int port_id, unsigned int session_id,
				    char *params, uint32_t params_length);

#endif /* __Q6_ADM_V2_H__ */
+161 −4
Original line number Diff line number Diff line
@@ -148,6 +148,7 @@ union srs_trumedia_params_u {
};
static union srs_trumedia_params_u msm_srs_trumedia_params[2];
static int srs_port_id = -1;
static bool is_custom_stereo_on; /* set to false by default */

static void srs_send_params(int port_id, unsigned int techs,
		int param_block_idx)
@@ -265,6 +266,69 @@ static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
	{INVALID_SESSION, INVALID_SESSION},
};

static int send_stereo_to_custom_stereo_cmd(int port_id,
					    unsigned int session_id,
					    uint16_t op_FL_ip_FL_weight,
					    uint16_t op_FL_ip_FR_weight,
					    uint16_t op_FR_ip_FL_weight,
					    uint16_t op_FR_ip_FR_weight)
{
	char *params_value;
	int *update_params_value32, rc = 0;
	int16_t *update_params_value16 = 0;
	uint32_t params_length = CUSTOM_STEREO_PAYLOAD_SIZE * sizeof(uint32_t);
	pr_debug("%s\n", __func__);
	params_value = kzalloc(params_length, GFP_KERNEL);
	if (!params_value) {
		pr_err("%s, params memory alloc failed\n", __func__);
		return -ENOMEM;
	}
	update_params_value32 = (int *)params_value;
	*update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER;
	*update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF;

	update_params_value16 = (int16_t *)update_params_value32;
	*update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE;
	/*for alignment only*/
	*update_params_value16++ = 0;
	/*index is 32-bit param in little endian*/
	*update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM;
	*update_params_value16++ = 0;
	/*for stereo mixing num out ch*/
	*update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH;
	/*for stereo mixing num in ch*/
	*update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH;

	/* Out ch map FL/FR*/
	*update_params_value16++ = PCM_CHANNEL_FL;
	*update_params_value16++ = PCM_CHANNEL_FR;

	/* In ch map FL/FR*/
	*update_params_value16++ = PCM_CHANNEL_FL;
	*update_params_value16++ = PCM_CHANNEL_FR;

	/* weighting coefficients as name suggests,
	mixing will be done according to these coefficients*/
	*update_params_value16++ = op_FL_ip_FL_weight;
	*update_params_value16++ = op_FL_ip_FR_weight;
	*update_params_value16++ = op_FR_ip_FL_weight;
	*update_params_value16++ = op_FR_ip_FR_weight;

	if (params_length) {
		rc = adm_set_stereo_to_custom_stereo(port_id,
						     session_id,
						     params_value,
						     params_length);
		if (rc) {
			pr_err("%s: send params failed\n", __func__);
			kfree(params_value);
			return -EINVAL;
		}
	}
	kfree(params_value);
	return 0;
}

static uint8_t is_be_dai_extproc(int be_dai)
{
	if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX ||
@@ -278,7 +342,7 @@ static uint8_t is_be_dai_extproc(int be_dai)
static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
	int path_type, bool perf_mode)
{
	int i, port_type;
	int i, port_type, itr = 0;
	struct route_payload payload;

	payload.num_copps = 0;
@@ -294,9 +358,21 @@ static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
					msm_bedais[i].port_id;
	}

	if (payload.num_copps)
	if (payload.num_copps) {
		adm_matrix_map(dspst_id, path_type,
			payload.num_copps, payload.copp_ids, 0, perf_mode);
		if ((path_type == ADM_PATH_PLAYBACK) && !perf_mode &&
		     is_custom_stereo_on) {
			for (itr = 0; itr < payload.num_copps; itr++)
				send_stereo_to_custom_stereo_cmd(
						payload.copp_ids[itr],
						dspst_id,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE);
		}
	}
}

void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
@@ -342,6 +418,7 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode,
					int dspst_id, int stream_type)
{
	int i, session_type, path_type, port_type, port_id, topology;
	int itr = 0;
	struct route_payload payload;
	u32 channels;
	uint16_t bits_per_sample = 16;
@@ -419,10 +496,21 @@ void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode,
						__func__);
		}
	}
	if (payload.num_copps)
	if (payload.num_copps) {
		adm_matrix_map(dspst_id, path_type,
			payload.num_copps, payload.copp_ids, 0, perf_mode);

		if ((path_type == ADM_PATH_PLAYBACK) && !perf_mode &&
		     is_custom_stereo_on) {
			for (itr = 0; itr < payload.num_copps; itr++)
				send_stereo_to_custom_stereo_cmd(
						payload.copp_ids[itr],
						dspst_id,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE);
		}
	}
	mutex_unlock(&routing_lock);
}

@@ -2590,6 +2678,72 @@ static const struct snd_kcontrol_new dolby_dap_param_end_point_controls[] = {
	msm_routing_put_dolby_dap_endpoint_control),
};

static int msm_routing_get_stereo_to_custom_stereo_control(
					struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	/* not used */
	return 0;
}

static int msm_routing_put_stereo_to_custom_stereo_control(
					struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	int flag = 0, rc = 0, i = 0;
	int be_index = 0, port_id;
	unsigned int session_id = 0;
	flag = ucontrol->value.integer.value[0];
	pr_debug("%s E flag %d\n", __func__, flag);

	if ((is_custom_stereo_on && flag) || (!is_custom_stereo_on && !flag))
		return 0;
	is_custom_stereo_on = flag ? true : false;
	for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
		port_id = msm_bedais[be_index].port_id;
		if (((port_id == SLIMBUS_0_RX) ||
		     (port_id == RT_PROXY_PORT_001_RX)) &&
		    msm_bedais[be_index].active) {
			for_each_set_bit(i,
				&msm_bedais[be_index].fe_sessions,
				MSM_FRONTEND_DAI_MM_SIZE) {
				if (test_bit(i,
				    &(msm_bedais[be_index].perf_mode)))
					goto skip_send_custom_stereo;
				session_id = fe_dai_map[i][SESSION_TYPE_RX];
				if (is_custom_stereo_on) {
					rc = send_stereo_to_custom_stereo_cmd(
						msm_bedais[be_index].port_id,
						session_id,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE,
						Q14_GAIN_ZERO_POINT_FIVE);
				} else {
					rc = send_stereo_to_custom_stereo_cmd(
						msm_bedais[be_index].port_id,
						session_id,
						Q14_GAIN_UNITY,
						0,
						0,
						Q14_GAIN_UNITY);
				}
skip_send_custom_stereo:
				if (rc)
					pr_err("%s custom stero mixing set failed\n",
						__func__);
			}
		}
	}
	return 0;
}

static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = {
	SOC_SINGLE_EXT("Set Custom Stereo OnOff", SND_SOC_NOPM, 0,
	1, 0, msm_routing_get_stereo_to_custom_stereo_control,
	msm_routing_put_stereo_to_custom_stereo_control),
};

int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol) {
	int rc = 0;
@@ -3938,6 +4092,9 @@ static int msm_routing_probe(struct snd_soc_platform *platform)
	snd_soc_add_platform_controls(platform, msm_voc_session_controls,
				      ARRAY_SIZE(msm_voc_session_controls));

	snd_soc_add_platform_controls(platform,
				stereo_to_custom_stereo_controls,
			ARRAY_SIZE(stereo_to_custom_stereo_controls));
	return 0;
}

+78 −0
Original line number Diff line number Diff line
@@ -280,6 +280,78 @@ fail_cmd:
	return ret;
}

int adm_set_stereo_to_custom_stereo(int port_id, unsigned int session_id,
				    char *params, uint32_t params_length)
{
	struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
	int sz, rc = 0, index = afe_get_port_index(port_id);

	pr_debug("%s\n", __func__);
	if (index < 0 || index >= AFE_MAX_PORTS) {
		pr_err("%s: invalid port idx %d port_id %#x\n", __func__, index,
			port_id);
		return -EINVAL;
	}
	sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
		params_length;
	adm_params = kzalloc(sz, GFP_KERNEL);
	if (!adm_params) {
		pr_err("%s, adm params memory alloc failed\n", __func__);
		return -ENOMEM;
	}

	memcpy(((u8 *)adm_params +
		sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)),
		params, params_length);
	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
					APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
	adm_params->hdr.pkt_size = sz;
	adm_params->hdr.src_svc = APR_SVC_ADM;
	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
	adm_params->hdr.src_port = port_id;
	adm_params->hdr.dest_svc = APR_SVC_ADM;
	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
	adm_params->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
	adm_params->hdr.token = port_id;
	adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5;
	adm_params->payload_addr_lsw = 0;
	adm_params->payload_addr_msw = 0;
	adm_params->mem_map_handle = 0;
	adm_params->payload_size = params_length;
	/* direction RX as 0 */
	adm_params->direction = 0;
	/* session id for this cmd to be applied on */
	adm_params->sessionid = session_id;
	/* valid COPP id for LPCM */
	adm_params->deviceid = atomic_read(&this_adm.copp_id[index]);
	adm_params->reserved = 0;
	pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n",
		__func__, adm_params->deviceid, adm_params->sessionid,
		adm_params->hdr.src_port, adm_params->hdr.dest_port);
	atomic_set(&this_adm.copp_stat[index], 0);
	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
	if (rc < 0) {
		pr_err("%s: Set params failed port = %#x\n",
			__func__, port_id);
		rc = -EINVAL;
		goto set_stereo_to_custom_stereo_return;
	}
	/* Wait for the callback */
	rc = wait_event_timeout(this_adm.wait[index],
		atomic_read(&this_adm.copp_stat[index]),
		msecs_to_jiffies(TIMEOUT_MS));
	if (!rc) {
		pr_err("%s: Set params timed out port = %#x\n", __func__,
			port_id);
		rc = -EINVAL;
		goto set_stereo_to_custom_stereo_return;
	}
	rc = 0;
set_stereo_to_custom_stereo_return:
	kfree(adm_params);
	return rc;
}

int adm_dolby_dap_send_params(int port_id, char *params, uint32_t params_length)
{
	struct adm_cmd_set_pp_params_v5	*adm_params = NULL;
@@ -544,6 +616,12 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv)
						data->payload_size);
				}
				break;
			case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5:
				pr_debug("%s:ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5\n",
					__func__);
				atomic_set(&this_adm.copp_stat[index], 1);
				wake_up(&this_adm.wait[index]);
				break;
			default:
				pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
								payload[0]);