Jelajahi Sumber

asoc: add support to set custom channel mixer coefficients

Enhance Channel Mixer control for MultiMedia Frontends to
get custom channel mixer coefficients from userspace and
use it during device routings to control stream to device
channel mappings.

Change-Id: Icf950377dede46b097c8656174af5eb311bf719a
Signed-off-by: Cong Tang <[email protected]>
Signed-off-by: Dhananjay Kumar <[email protected]>
Dhananjay Kumar 6 tahun lalu
induk
melakukan
807f7e9521

+ 613 - 4
asoc/msm-compress-q6-v2.c

@@ -101,6 +101,7 @@ struct msm_compr_pdata {
 	struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
 	struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
 	bool is_in_use[MSM_FRONTEND_DAI_MAX];
+	struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE];
 };
 
 struct msm_compr_audio {
@@ -3711,6 +3712,7 @@ static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
 	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
 			snd_soc_component_get_drvdata(comp);
 	int rc = 0, i;
+	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
 
 	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
 
@@ -3726,6 +3728,17 @@ static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
 		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
 			pdata->ch_map[fe_id]->channel_map[i] =
 				(char)(ucontrol->value.integer.value[i]);
+
+		/* update chmixer_pspd chmap cached with routing driver as well */
+		chmixer_pspd = pdata->chmixer_pspd[fe_id];
+		if (chmixer_pspd && chmixer_pspd->enable) {
+			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+				chmixer_pspd->in_ch_map[i] =
+					pdata->ch_map[fe_id]->channel_map[i];
+			chmixer_pspd->override_in_ch_map = true;
+			msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+					SESSION_TYPE_RX, chmixer_pspd);
+		}
 	} else {
 		pr_debug("%s: no memory for ch_map, default will be set\n",
 			__func__);
@@ -3971,10 +3984,11 @@ static int msm_compr_probe(struct snd_soc_component *component)
 	const char *qdsp_version;
 
 	pr_debug("%s\n", __func__);
-	pdata = (struct msm_compr_pdata *)
-			kzalloc(sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		return -ENOMEM;
+	pdata = (struct msm_compr_pdata *) dev_get_drvdata(component->dev);
+	if (!pdata) {
+		pr_err("%s platform data not set\n", __func__);
+		return -EINVAL;
+	}
 
 	snd_soc_component_set_drvdata(component, pdata);
 
@@ -4640,6 +4654,580 @@ static int msm_compr_add_event_ack_cmd_control(struct snd_soc_pcm_runtime *rtd)
 	return ret;
 }
 
+static struct msm_pcm_channel_mixer *msm_compr_get_chmixer(
+			struct msm_compr_pdata *pdata, u64 fe_id)
+{
+	if (!pdata) {
+		pr_err("%s: missing pdata\n", __func__);
+		return NULL;
+	}
+
+	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+		pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+		return NULL;
+	}
+
+	return pdata->chmixer_pspd[fe_id];
+}
+
+static int msm_compr_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int ret = 0, i = 0, stream_id = 0, be_id = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct snd_compr_stream *cstream = NULL;
+	struct msm_compr_audio *prtd = NULL;
+	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+	u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+	bool reset_override_out_ch_map = false;
+	bool reset_override_in_ch_map = false;
+
+	if ((session_type != SESSION_TYPE_TX) &&
+		(session_type != SESSION_TYPE_RX)) {
+		pr_err("%s: invalid session type %d\n", __func__, session_type);
+		return -EINVAL;
+	}
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->enable = ucontrol->value.integer.value[0];
+	chmixer_pspd->rule = ucontrol->value.integer.value[1];
+	chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+	chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+	chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+	if (chmixer_pspd->enable) {
+		if (session_type == SESSION_TYPE_RX &&
+			!chmixer_pspd->override_in_ch_map) {
+			if (pdata->ch_map[fe_id]->set_ch_map) {
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->in_ch_map[i] =
+						pdata->ch_map[fe_id]->channel_map[i];
+			} else {
+				q6asm_map_channels(asm_ch_map,
+					chmixer_pspd->input_channel, false);
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+			}
+			chmixer_pspd->override_in_ch_map = true;
+			reset_override_in_ch_map = true;
+		} else if (session_type == SESSION_TYPE_TX &&
+				!chmixer_pspd->override_out_ch_map) {
+			if (pdata->ch_map[fe_id]->set_ch_map) {
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->out_ch_map[i] =
+						pdata->ch_map[fe_id]->channel_map[i];
+			} else {
+				q6asm_map_channels(asm_ch_map,
+					chmixer_pspd->output_channel, false);
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+			}
+			chmixer_pspd->override_out_ch_map = true;
+			reset_override_out_ch_map = true;
+		}
+	} else {
+		chmixer_pspd->override_out_ch_map = false;
+		chmixer_pspd->override_in_ch_map = false;
+	}
+
+	/* cache value and take effect during adm_open stage */
+	msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+			session_type,
+			chmixer_pspd);
+
+	cstream = pdata->cstream[fe_id];
+	if (chmixer_pspd->enable && cstream && cstream->runtime) {
+		prtd = cstream->runtime->private_data;
+		if (!prtd) {
+			pr_err("%s invalid prtd\n", __func__);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		if (prtd->audio_client) {
+			stream_id = prtd->audio_client->session;
+			be_id = chmixer_pspd->port_idx;
+			ret = msm_pcm_routing_set_channel_mixer_runtime(be_id,
+					stream_id, session_type, chmixer_pspd);
+		}
+	}
+
+	if (reset_override_out_ch_map)
+		chmixer_pspd->override_out_ch_map = false;
+	if (reset_override_in_ch_map)
+		chmixer_pspd->override_in_ch_map = false;
+
+done:
+	return ret;
+}
+
+static int msm_compr_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+	ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+	ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+	ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+	ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+	return 0;
+}
+
+static int msm_compr_channel_mixer_output_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int i = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_out_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->out_ch_map[i] =
+			ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_compr_channel_mixer_output_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int i = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->out_ch_map[i];
+	return 0;
+}
+
+static int msm_compr_channel_mixer_input_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int i = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_in_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_compr_channel_mixer_input_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int i = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->in_ch_map[i];
+	return 0;
+}
+
+static int msm_compr_channel_mixer_weight_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->channel_weight[channel][i] =
+			ucontrol->value.integer.value[i];
+	return 0;
+}
+
+static int msm_compr_channel_mixer_weight_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+					snd_soc_component_get_drvdata(comp);
+	int i = 0;
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->channel_weight[channel][i];
+	return 0;
+}
+
+static int msm_compr_add_platform_controls(struct snd_kcontrol_new *kctl,
+			struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+			const char *name_suffix, int session_type, int channels)
+{
+	int ret = -EINVAL;
+	char *mixer_name = NULL;
+	const char *deviceNo = "NN";
+	const char *channelNo = "NN";
+	int ctl_len = 0;
+	struct snd_soc_component *component = NULL;
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+		strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+	mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+	if (mixer_name == NULL)
+		return -ENOMEM;
+
+	if (channels >= 0) {
+		snprintf(mixer_name, ctl_len, "%s %d %s %d",
+			name_prefix, rtd->pcm->device, name_suffix, channels);
+		kctl->private_value = (rtd->dai_link->id) | (channels << 16);
+	} else {
+		snprintf(mixer_name, ctl_len, "%s %d %s",
+			name_prefix, rtd->pcm->device, name_suffix);
+		kctl->private_value = (rtd->dai_link->id);
+	}
+	if (session_type != INVALID_SESSION)
+		kctl->private_value |= (session_type << 8);
+
+	kctl->name = mixer_name;
+	ret = snd_soc_add_component_controls(component, kctl, 1);
+	kfree(mixer_name);
+	return ret;
+}
+
+static int msm_compr_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_compr_add_channel_mixer_output_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Output Map";
+	const char *mixer_ctl_name  = NULL;
+	int ret = 0, session_type = INVALID_SESSION, channel = -1;
+	struct snd_kcontrol_new channel_mixer_output_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_compr_channel_mixer_output_map_info,
+		.put = msm_compr_channel_mixer_output_map_ctl_put,
+		.get = msm_compr_channel_mixer_output_map_ctl_get,
+		.private_value = 0,
+	};
+
+	mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+			playback_mixer_ctl_name : capture_mixer_ctl_name ;
+	ret = msm_compr_add_platform_controls(&channel_mixer_output_map_control,
+			rtd, mixer_ctl_name, suffix, session_type, channel);
+	if (ret < 0) {
+		pr_err("%s: failed add platform ctl, err = %d\n",
+			 __func__, ret);
+	}
+
+	return ret;
+}
+
+static int msm_compr_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_compr_add_channel_mixer_input_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix = "ChMixer Input Map";
+	const char *mixer_ctl_name  = NULL;
+	int ret = 0, session_type = INVALID_SESSION, channel = -1;
+	struct snd_kcontrol_new channel_mixer_input_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_compr_channel_mixer_input_map_info,
+		.put = msm_compr_channel_mixer_input_map_ctl_put,
+		.get = msm_compr_channel_mixer_input_map_ctl_get,
+		.private_value = 0,
+	};
+
+	mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+			playback_mixer_ctl_name : capture_mixer_ctl_name ;
+	ret = msm_compr_add_platform_controls(&channel_mixer_input_map_control,
+			rtd, mixer_ctl_name, suffix, session_type, channel);
+	if (ret < 0) {
+		pr_err("%s: failed add platform ctl, err = %d\n",
+			 __func__, ret);
+	}
+
+	return ret;
+}
+
+static int msm_compr_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	/* five int values: enable, rule, in_channels, out_channels and port_id */
+	uinfo->count = 5;
+	/* Valid range is all positive values to support above controls */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = INT_MAX;
+	return 0;
+}
+
+static int msm_compr_add_channel_mixer_cfg_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Cfg";
+	const char *mixer_ctl_name = NULL;
+	int ret = 0, session_type = INVALID_SESSION, channel = -1;
+	struct snd_kcontrol_new channel_mixer_cfg_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_compr_channel_mixer_cfg_info,
+		.put = msm_compr_channel_mixer_cfg_ctl_put,
+		.get = msm_compr_channel_mixer_cfg_ctl_get,
+		.private_value = 0,
+	};
+
+	if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+		session_type = SESSION_TYPE_RX;
+		mixer_ctl_name = playback_mixer_ctl_name;
+	} else {
+		session_type = SESSION_TYPE_TX;
+		mixer_ctl_name = capture_mixer_ctl_name;
+	}
+
+	ret = msm_compr_add_platform_controls(&channel_mixer_cfg_control,
+			rtd, mixer_ctl_name, suffix, session_type, channel);
+	if (ret < 0) {
+		pr_err("%s: failed add platform ctl, err = %d\n",
+			 __func__, ret);
+	}
+
+	return ret;
+}
+
+static int msm_compr_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid range: 0 to 0x4000(Unity) gain weightage */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0x4000;
+	return 0;
+}
+
+static int msm_compr_add_channel_mixer_weight_controls(
+		struct snd_soc_pcm_runtime *rtd,
+		int channel)
+{
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Weight Ch";
+	const char *mixer_ctl_name = NULL;
+	int ret = 0, session_type = INVALID_SESSION;
+	struct snd_kcontrol_new channel_mixer_weight_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_compr_channel_mixer_weight_info,
+		.put = msm_compr_channel_mixer_weight_ctl_put,
+		.get = msm_compr_channel_mixer_weight_ctl_get,
+		.private_value = 0,
+	};
+
+	mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+			playback_mixer_ctl_name : capture_mixer_ctl_name ;
+	ret = msm_compr_add_platform_controls(&channel_mixer_weight_control,
+			rtd, mixer_ctl_name, suffix, session_type, channel);
+	if (ret < 0) {
+		pr_err("%s: failed add platform ctl, err = %d\n",
+			 __func__, ret);
+	}
+
+	return ret;
+}
+
+static int msm_compr_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int i, ret = 0;
+	struct msm_compr_pdata *pdata = NULL;
+	struct snd_soc_component *component = NULL;
+
+	if (!rtd) {
+		pr_err("%s NULL rtd\n", __func__);
+		return -EINVAL;
+	}
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pdata = (struct msm_compr_pdata *)
+		snd_soc_component_get_drvdata(component);
+	if (!pdata) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!pdata->chmixer_pspd[rtd->dai_link->id]) {
+		pdata->chmixer_pspd[rtd->dai_link->id] =
+			kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+		if (!pdata->chmixer_pspd[rtd->dai_link->id])
+			return -ENOMEM;
+	}
+
+	ret = msm_compr_add_channel_mixer_cfg_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_compr_add_channel_mixer_input_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_compr_add_channel_mixer_output_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+
+	for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+		ret =  msm_compr_add_channel_mixer_weight_controls(rtd, i);
+		if (ret) {
+			pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+				__func__, ret);
+			goto fail;
+		}
+	}
+	return 0;
+
+fail:
+	kfree(pdata->chmixer_pspd[rtd->dai_link->id]);
+	pdata->chmixer_pspd[rtd->dai_link->id] = NULL;
+	return ret;
+}
+
 static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
 {
 	int rc;
@@ -4690,6 +5278,10 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
 	if (rc)
 		pr_err("%s: Could not add Compr Channel Map Control\n",
 			__func__);
+	rc = msm_compr_add_channel_mixer_controls(rtd);
+	if (rc)
+		pr_err("%s: Could not add Compr Channel Mixer Controls\n",
+			__func__);
 	return 0;
 }
 
@@ -4717,14 +5309,31 @@ static struct snd_soc_component_driver msm_soc_component = {
 
 static int msm_compr_dev_probe(struct platform_device *pdev)
 {
+	struct msm_compr_pdata *pdata = NULL;
 
 	pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+	pdata = (struct msm_compr_pdata *)
+			kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+	dev_set_drvdata(&pdev->dev, pdata);
+
 	return snd_soc_register_component(&pdev->dev,
 					&msm_soc_component, NULL, 0);
 }
 
 static int msm_compr_remove(struct platform_device *pdev)
 {
+	int i = 0;
+	struct msm_compr_pdata *pdata = NULL;
+
+	pdata = dev_get_drvdata(&pdev->dev);
+	if (pdata) {
+		for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++)
+			kfree(pdata->chmixer_pspd[i]);
+	}
+	kfree(pdata);
+
 	snd_soc_unregister_component(&pdev->dev);
 	return 0;
 }

+ 684 - 1
asoc/msm-pcm-loopback-v2.c

@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/init.h>
@@ -64,6 +64,8 @@ static u32 hfp_tx_mute;
 
 struct msm_pcm_pdata {
 	int perf_mode;
+	struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+	struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
 };
 
 static void stop_pcm(struct msm_pcm_loopback *pcm);
@@ -734,6 +736,671 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
 	return 0;
 }
 
+static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
+			struct msm_pcm_pdata *pdata,
+			u64 fe_id, int session_type)
+{
+	if (!pdata) {
+		pr_err("%s: missing pdata\n", __func__);
+		return NULL;
+	}
+
+	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+		pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+		return NULL;
+	}
+
+	if ((session_type != SESSION_TYPE_TX) &&
+		(session_type != SESSION_TYPE_RX)) {
+		pr_err("%s: invalid session type %d\n", __func__, session_type);
+		return NULL;
+	}
+
+	return pdata->chmixer_pspd[fe_id][session_type];
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int ret = 0;
+	int stream_id = 0;
+	int be_id = 0, i = 0;
+	struct msm_pcm_loopback *prtd = NULL;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct snd_pcm *pcm = NULL;
+	struct snd_pcm_substream *substream = NULL;
+	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+	u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+	bool reset_override_out_ch_map = false;
+	bool reset_override_in_ch_map = false;
+
+	pcm = pdata->pcm_device[fe_id];
+	if (!pcm) {
+		pr_err("%s invalid pcm handle for fe_id %llu\n",
+				__func__, fe_id);
+		return -EINVAL;
+	}
+
+	if (session_type == SESSION_TYPE_RX)
+		substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	else
+		substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->enable = ucontrol->value.integer.value[0];
+	chmixer_pspd->rule = ucontrol->value.integer.value[1];
+	chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+	chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+	chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+	if (chmixer_pspd->enable) {
+		if (session_type == SESSION_TYPE_RX &&
+			!chmixer_pspd->override_in_ch_map) {
+			q6asm_map_channels(asm_ch_map,
+				chmixer_pspd->input_channel, false);
+			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+				chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+			chmixer_pspd->override_in_ch_map = true;
+			reset_override_in_ch_map = true;
+		} else if (session_type == SESSION_TYPE_TX &&
+			!chmixer_pspd->override_out_ch_map) {
+			q6asm_map_channels(asm_ch_map,
+				chmixer_pspd->output_channel, false);
+			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+				chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+			chmixer_pspd->override_out_ch_map = true;
+			reset_override_out_ch_map = true;
+		}
+	} else {
+		chmixer_pspd->override_out_ch_map = false;
+		chmixer_pspd->override_in_ch_map = false;
+	}
+
+	/* cache value and take effect during adm_open stage */
+	msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+			session_type,
+			chmixer_pspd);
+
+	if (chmixer_pspd->enable && substream->runtime) {
+		prtd = substream->runtime->private_data;
+		if (!prtd) {
+			pr_err("%s find invalid prtd fail\n", __func__);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		if (prtd->audio_client) {
+			stream_id = prtd->audio_client->session;
+			be_id = chmixer_pspd->port_idx;
+			msm_pcm_routing_set_channel_mixer_runtime(be_id,
+					stream_id,
+					session_type,
+					chmixer_pspd);
+		}
+	}
+
+	if (reset_override_out_ch_map)
+		chmixer_pspd->override_out_ch_map = false;
+	if (reset_override_in_ch_map)
+		chmixer_pspd->override_in_ch_map = false;
+
+done:
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+	ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+	ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+	ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+	ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_out_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->out_ch_map[i] =
+			ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->out_ch_map[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_in_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->in_ch_map[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->channel_weight[channel][i] =
+			ucontrol->value.integer.value[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+	int i = 0;
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->channel_weight[channel][i];
+	return 0;
+}
+
+static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
+			struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+			const char *name_suffix, int session_type, int channels)
+{
+	int ret = -EINVAL;
+	char *mixer_name = NULL;
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *deviceNo = "NN";
+	const char *channelNo = "NN";
+	int ctl_len = 0;
+	struct snd_soc_component *component = NULL;
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+		strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+	mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+	if (mixer_name == NULL)
+		return -ENOMEM;
+
+	if (channels >= 0) {
+		snprintf(mixer_name, ctl_len, "%s %d %s %d",
+			name_prefix, pcm->device, name_suffix, channels);
+		kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
+							(channels << 16);
+	} else {
+		snprintf(mixer_name, ctl_len, "%s %d %s",
+			name_prefix, pcm->device, name_suffix);
+		kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
+	}
+
+	kctl->name = mixer_name;
+	ret = snd_soc_add_component_controls(component, kctl, 1);
+	kfree(mixer_name);
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Output Map";
+	int session_type = 0, ret = 0, channel = -1;
+	struct snd_kcontrol_new channel_mixer_output_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_output_map_info,
+		.put = msm_pcm_channel_mixer_output_map_ctl_put,
+		.get = msm_pcm_channel_mixer_output_map_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix = "ChMixer Input Map";
+	int session_type = 0, ret = 0, channel = -1;
+	struct snd_kcontrol_new channel_mixer_input_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_input_map_info,
+		.put = msm_pcm_channel_mixer_input_map_ctl_put,
+		.get = msm_pcm_channel_mixer_input_map_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	/* five int values: enable, rule, in_channels, out_channels and port_id */
+	uinfo->count = 5;
+	/* Valid range is all positive values to support above controls */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = INT_MAX;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Cfg";
+	int session_type = 0, ret = 0, channel = -1;
+	struct msm_pcm_pdata *pdata = NULL;
+	struct snd_soc_component *component = NULL;
+	struct snd_kcontrol_new channel_mixer_cfg_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_cfg_info,
+		.put = msm_pcm_channel_mixer_cfg_ctl_put,
+		.get = msm_pcm_channel_mixer_cfg_ctl_get,
+		.private_value = 0,
+	};
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+	pdata = (struct msm_pcm_pdata *)
+		dev_get_drvdata(component->dev);
+	if (pdata == NULL) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+
+	pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid range: 0 to 0x4000(Unity) gain weightage */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0x4000;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+		struct snd_soc_pcm_runtime *rtd,
+		int channel)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Weight Ch";
+	int session_type = 0, ret = 0;
+	struct snd_kcontrol_new channel_mixer_weight_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_weight_info,
+		.put = msm_pcm_channel_mixer_weight_ctl_put,
+		.get = msm_pcm_channel_mixer_weight_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int i, ret = 0;
+	struct snd_pcm *pcm = NULL;
+	struct msm_pcm_pdata *pdata = NULL;
+	struct snd_soc_component *component = NULL;
+
+	if (!rtd || !rtd->pcm) {
+		pr_err("%s invalid rtd or pcm\n", __func__);
+		return -EINVAL;
+	}
+	pcm = rtd->pcm;
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pdata = (struct msm_pcm_pdata *)
+				dev_get_drvdata(component->dev);
+	if (!pdata) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
+		!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+		pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
+			kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+		if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
+		!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+		pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
+			kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+		if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+
+	for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+		ret =  msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+		if (ret) {
+			pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+					__func__, ret);
+			goto fail;
+		}
+	}
+	return 0;
+
+fail:
+	kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
+	kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
+	pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
+	pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
+
+	return ret;
+}
+
 static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
 {
 	int ret = 0;
@@ -747,6 +1414,11 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
 	if (ret)
 		pr_err("%s: pcm add app type controls failed:%d\n",
 			__func__, ret);
+
+	ret = msm_pcm_add_channel_mixer_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add channel mixer controls failed:%d\n",
+			__func__, ret);
 	return ret;
 }
 
@@ -797,6 +1469,17 @@ static int msm_pcm_probe(struct platform_device *pdev)
 
 static int msm_pcm_remove(struct platform_device *pdev)
 {
+	struct msm_pcm_pdata *pdata;
+	int i = 0;
+
+	pdata = dev_get_drvdata(&pdev->dev);
+	if (pdata) {
+		for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
+			kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
+			kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
+		}
+	}
+	kfree(pdata);
 	snd_soc_unregister_component(&pdev->dev);
 	return 0;
 }

+ 702 - 0
asoc/msm-pcm-q6-v2.c

@@ -1601,6 +1601,11 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	struct snd_pcm_substream *substream;
 	struct msm_audio *prtd;
+	struct snd_soc_pcm_runtime *rtd = NULL;
+	struct msm_plat_data *pdata = NULL;
+	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	u64 fe_id = 0;
 
 	pr_debug("%s", __func__);
 	substream = snd_pcm_chmap_substream(info, idx);
@@ -1615,6 +1620,24 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
 				prtd->channel_map[i] =
 				(char)(ucontrol->value.integer.value[i]);
+
+		/* update chmixer_pspd chmap cached with routing driver as well */
+		rtd = substream->private_data;
+		if (rtd) {
+			fe_id = rtd->dai_link->id;
+			pdata = (struct msm_plat_data *)
+					dev_get_drvdata(component->dev);
+			chmixer_pspd = pdata ?
+				pdata->chmixer_pspd[fe_id][SESSION_TYPE_RX] : NULL;
+
+			if (chmixer_pspd && chmixer_pspd->enable) {
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->in_ch_map[i] = prtd->channel_map[i];
+				chmixer_pspd->override_in_ch_map = true;
+				msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+						SESSION_TYPE_RX, chmixer_pspd);
+			}
+		}
 	}
 	return 0;
 }
@@ -1838,6 +1861,674 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
 	return 0;
 }
 
+static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
+			struct msm_plat_data *pdata,
+			u64 fe_id, int session_type)
+{
+	if (!pdata) {
+		pr_err("%s: missing pdata\n", __func__);
+		return NULL;
+	}
+
+	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+		pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+		return NULL;
+	}
+
+	if ((session_type != SESSION_TYPE_TX) &&
+		(session_type != SESSION_TYPE_RX)) {
+		pr_err("%s: invalid session type %d\n", __func__, session_type);
+		return NULL;
+	}
+
+	return pdata->chmixer_pspd[fe_id][session_type];
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int ret = 0;
+	int stream_id = 0;
+	int be_id = 0, i = 0;
+	struct msm_audio *prtd = NULL;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct snd_pcm *pcm = NULL;
+	struct snd_pcm_substream *substream = NULL;
+	struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+	u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+	bool reset_override_out_ch_map = false;
+	bool reset_override_in_ch_map = false;
+
+	pcm = pdata->pcm_device[fe_id];
+	if (!pcm) {
+		pr_err("%s invalid pcm handle for fe_id %llu\n",
+				__func__, fe_id);
+		return -EINVAL;
+	}
+
+	if (session_type == SESSION_TYPE_RX)
+		substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	else
+		substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->enable = ucontrol->value.integer.value[0];
+	chmixer_pspd->rule = ucontrol->value.integer.value[1];
+	chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+	chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+	chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+	if (chmixer_pspd->enable) {
+		if (session_type == SESSION_TYPE_RX &&
+			!chmixer_pspd->override_in_ch_map) {
+			if (pdata->ch_map[fe_id] &&
+				pdata->ch_map[fe_id]->set_ch_map) {
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->in_ch_map[i] =
+						pdata->ch_map[fe_id]->channel_map[i];
+			} else {
+				q6asm_map_channels(asm_ch_map,
+					chmixer_pspd->input_channel, false);
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+			}
+			chmixer_pspd->override_in_ch_map = true;
+			reset_override_in_ch_map = true;
+		} else if (session_type == SESSION_TYPE_TX &&
+				!chmixer_pspd->override_out_ch_map) {
+			if (pdata->ch_map[fe_id] &&
+				pdata->ch_map[fe_id]->set_ch_map) {
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->out_ch_map[i] =
+						pdata->ch_map[fe_id]->channel_map[i];
+			} else {
+				q6asm_map_channels(asm_ch_map,
+					chmixer_pspd->output_channel, false);
+				for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+					chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+			}
+			chmixer_pspd->override_out_ch_map = true;
+			reset_override_out_ch_map = true;
+		}
+	} else {
+		chmixer_pspd->override_out_ch_map = false;
+		chmixer_pspd->override_in_ch_map = false;
+	}
+
+	/* cache value and take effect during adm_open stage */
+	msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+			session_type,
+			chmixer_pspd);
+
+	if (chmixer_pspd->enable && substream->runtime) {
+		prtd = substream->runtime->private_data;
+		if (!prtd) {
+			pr_err("%s find invalid prtd fail\n", __func__);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		if (prtd->audio_client) {
+			stream_id = prtd->audio_client->session;
+			be_id = chmixer_pspd->port_idx;
+			msm_pcm_routing_set_channel_mixer_runtime(be_id,
+					stream_id,
+					session_type,
+					chmixer_pspd);
+		}
+	}
+
+	if (reset_override_out_ch_map)
+		chmixer_pspd->override_out_ch_map = false;
+	if (reset_override_in_ch_map)
+		chmixer_pspd->override_in_ch_map = false;
+
+done:
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+	ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+	ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+	ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+	ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_out_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->out_ch_map[i] =
+			ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->out_ch_map[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	chmixer_pspd->override_in_ch_map = true;
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->in_ch_map[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	int i = 0;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		chmixer_pspd->channel_weight[channel][i] =
+			ucontrol->value.integer.value[i];
+	return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+		struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value & 0xFF;
+	int session_type = (kcontrol->private_value >> 8) & 0xFF;
+	int channel = (kcontrol->private_value >> 16) & 0xFF;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+	int i = 0;
+	struct msm_pcm_channel_mixer *chmixer_pspd;
+
+	if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+		pr_err("%s: invalid channel number %d\n", __func__, channel);
+		return -EINVAL;
+	}
+	channel--;
+
+	chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+	if (!chmixer_pspd) {
+		pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+		ucontrol->value.integer.value[i] =
+			chmixer_pspd->channel_weight[channel][i];
+	return 0;
+}
+
+static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
+			struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+			const char *name_suffix, int session_type, int channels)
+{
+	int ret = -EINVAL;
+	char *mixer_name = NULL;
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *deviceNo = "NN";
+	const char *channelNo = "NN";
+	int ctl_len = 0;
+	struct snd_soc_component *component = NULL;
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+		strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+	mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+	if (mixer_name == NULL)
+		return -ENOMEM;
+
+	if (channels >= 0) {
+		snprintf(mixer_name, ctl_len, "%s %d %s %d",
+			name_prefix, pcm->device, name_suffix, channels);
+		kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
+							(channels << 16);
+	} else {
+		snprintf(mixer_name, ctl_len, "%s %d %s",
+			name_prefix, pcm->device, name_suffix);
+		kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
+	}
+
+	kctl->name = mixer_name;
+	ret = snd_soc_add_component_controls(component, kctl, 1);
+	kfree(mixer_name);
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Output Map";
+	int session_type = 0, ret = 0, channel = -1;
+	struct snd_kcontrol_new channel_mixer_output_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_output_map_info,
+		.put = msm_pcm_channel_mixer_output_map_ctl_put,
+		.get = msm_pcm_channel_mixer_output_map_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid channel map value ranges from 1 to 64 */
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 64;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix = "ChMixer Input Map";
+	int session_type = 0, ret = 0, channel = -1;
+	struct snd_kcontrol_new channel_mixer_input_map_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_input_map_info,
+		.put = msm_pcm_channel_mixer_input_map_ctl_put,
+		.get = msm_pcm_channel_mixer_input_map_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	/* five int values: enable, rule, in_channels, out_channels and port_id */
+	uinfo->count = 5;
+	/* Valid range is all positive values to support above controls */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = INT_MAX;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+		struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Cfg";
+	int session_type = 0, ret = 0, channel = -1;
+	struct msm_plat_data *pdata = NULL;
+	struct snd_soc_component *component = NULL;
+	struct snd_kcontrol_new channel_mixer_cfg_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_cfg_info,
+		.put = msm_pcm_channel_mixer_cfg_ctl_put,
+		.get = msm_pcm_channel_mixer_cfg_ctl_get,
+		.private_value = 0,
+	};
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pdata = (struct msm_plat_data *)
+		dev_get_drvdata(component->dev);
+
+	pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+	/* Valid range: 0 to 0x4000(Unity) gain weightage */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0x4000;
+	return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+		struct snd_soc_pcm_runtime *rtd,
+		int channel)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	const char *playback_mixer_ctl_name	= "AudStr";
+	const char *capture_mixer_ctl_name	= "AudStr Capture";
+	const char *suffix		= "ChMixer Weight Ch";
+	int session_type = 0, ret = 0;
+	struct snd_kcontrol_new channel_mixer_weight_control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "?",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = msm_pcm_channel_mixer_weight_info,
+		.put = msm_pcm_channel_mixer_weight_ctl_put,
+		.get = msm_pcm_channel_mixer_weight_ctl_get,
+		.private_value = 0,
+	};
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+		session_type = SESSION_TYPE_RX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+				rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+		session_type = SESSION_TYPE_TX;
+		ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+				rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+		if (ret < 0)
+			goto fail;
+	}
+	return 0;
+
+fail:
+	pr_err("%s: failed add platform ctl, err = %d\n",
+		 __func__, ret);
+
+	return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int i, ret = 0;
+	struct snd_pcm *pcm = NULL;
+	struct msm_plat_data *pdata = NULL;
+	struct snd_soc_component *component = NULL;
+
+	if (!rtd || !rtd->pcm) {
+		pr_err("%s invalid rtd or pcm\n", __func__);
+		return -EINVAL;
+	}
+	pcm = rtd->pcm;
+
+	component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pdata = (struct msm_plat_data *)
+				dev_get_drvdata(component->dev);
+	if (!pdata) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
+		!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+		pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
+			kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+		if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
+		!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+		pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
+			kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+		if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+	ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+	if (ret) {
+		pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+				__func__, ret);
+		goto fail;
+	}
+
+	for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+		ret =  msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+		if (ret) {
+			pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+				__func__, ret);
+			goto fail;
+		}
+	}
+	return 0;
+
+fail:
+	kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
+	kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
+	pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
+	pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
+
+	return ret;
+}
+
 static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
 {
 	int ret = 0;
@@ -1850,6 +2541,10 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
 	if (ret)
 		pr_err("%s: pcm add app type controls failed:%d\n",
 			__func__, ret);
+	ret = msm_pcm_add_channel_mixer_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add channel mixer controls failed:%d\n",
+			__func__, ret);
 	return ret;
 }
 
@@ -1973,8 +2668,15 @@ static int msm_pcm_probe(struct platform_device *pdev)
 static int msm_pcm_remove(struct platform_device *pdev)
 {
 	struct msm_plat_data *pdata;
+	int i = 0;
 
 	pdata = dev_get_drvdata(&pdev->dev);
+	if (pdata) {
+		for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
+			kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
+			kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
+		}
+	}
 	kfree(pdata);
 	snd_soc_unregister_component(&pdev->dev);
 	return 0;

+ 3 - 1
asoc/msm-pcm-q6-v2.h

@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved.
  */
 
 #ifndef _MSM_PCM_H
@@ -115,6 +115,8 @@ struct msm_plat_data {
 	int perf_mode;
 	struct snd_pcm *pcm;
 	struct msm_pcm_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
+	struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+	struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
 };
 
 struct msm_pcm_ch_map {

+ 178 - 0
asoc/msm-pcm-routing-v2.c

@@ -84,6 +84,11 @@ static int msm_ec_ref_port_id;
 #define WEIGHT_0_DB 0x4000
 /* all the FEs which can support channel mixer */
 static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
+
+/* all the FES which can support channel mixer for bidirection */
+static struct msm_pcm_channel_mixer
+	channel_mixer_v2[MSM_FRONTEND_DAI_MM_SIZE][2];
+
 /* input BE for each FE */
 static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS];
 
@@ -928,6 +933,51 @@ int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt,
 }
 EXPORT_SYMBOL(msm_pcm_routing_send_chmix_cfg);
 
+/**
+ * msm_pcm_routing_set_channel_mixer_cfg - cache channel mixer
+ * setting before use case start.
+ *
+ * @fe_id: frontend idx
+ * @type: stream direction type
+ * @params: parameters of channel mixer setting
+ *
+ * Return 0 for success
+ */
+int msm_pcm_routing_set_channel_mixer_cfg(
+	int fe_id, int type,
+	struct msm_pcm_channel_mixer *params)
+{
+	int i, j = 0;
+
+	channel_mixer_v2[fe_id][type].enable = params->enable;
+	channel_mixer_v2[fe_id][type].rule = params->rule;
+	channel_mixer_v2[fe_id][type].input_channel =
+		params->input_channel;
+	channel_mixer_v2[fe_id][type].output_channel =
+		params->output_channel;
+	channel_mixer_v2[fe_id][type].port_idx = params->port_idx;
+
+	for (i = 0; i < ADM_MAX_CHANNELS; i++)
+		channel_mixer_v2[fe_id][type].in_ch_map[i] =
+			params->in_ch_map[i];
+	for (i = 0; i < ADM_MAX_CHANNELS; i++)
+		channel_mixer_v2[fe_id][type].out_ch_map[i] =
+			params->out_ch_map[i];
+
+	for (i = 0; i < ADM_MAX_CHANNELS; i++)
+		for (j = 0; j < ADM_MAX_CHANNELS; j++)
+			channel_mixer_v2[fe_id][type].channel_weight[i][j] =
+				params->channel_weight[i][j];
+
+	channel_mixer_v2[fe_id][type].override_in_ch_map =
+			params->override_in_ch_map;
+	channel_mixer_v2[fe_id][type].override_out_ch_map =
+			params->override_out_ch_map;
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_cfg);
+
 int msm_pcm_routing_reg_stream_app_type_cfg(
 	int fedai_id, int session_type, int be_id,
 	struct msm_pcm_stream_app_type_cfg *cfg_data)
@@ -1490,6 +1540,60 @@ static u32 msm_pcm_routing_get_voc_sessionid(u16 val)
 	return session_id;
 }
 
+static int msm_pcm_routing_channel_mixer_v2(int fe_id, bool perf_mode,
+				int dspst_id, int stream_type)
+{
+	int copp_idx = 0;
+	int sess_type = 0;
+	int j = 0, be_id = 0;
+	int ret = 0;
+
+	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+		pr_err("%s: invalid FE %d\n", __func__, fe_id);
+		return 0;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+		sess_type = SESSION_TYPE_RX;
+	else
+		sess_type = SESSION_TYPE_TX;
+
+	if (!(channel_mixer_v2[fe_id][sess_type].enable)) {
+		pr_debug("%s: channel mixer not enabled for FE %d direction %d\n",
+			__func__, fe_id, sess_type);
+		return 0;
+	}
+
+	be_id = channel_mixer_v2[fe_id][sess_type].port_idx - 1;
+	channel_mixer_v2[fe_id][sess_type].input_channels[0] =
+		channel_mixer_v2[fe_id][sess_type].input_channel;
+
+	pr_debug("%s sess type %d,fe_id %d,override in:%d out:%d,be active %d\n",
+			__func__, sess_type, fe_id,
+			channel_mixer_v2[fe_id][sess_type].override_in_ch_map,
+			channel_mixer_v2[fe_id][sess_type].override_out_ch_map,
+			msm_bedais[be_id].active);
+
+	if ((msm_bedais[be_id].active) &&
+		test_bit(fe_id, &msm_bedais[be_id].fe_sessions[0])) {
+		unsigned long copp =
+			session_copp_map[fe_id][sess_type][be_id];
+		for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+			if (test_bit(j, &copp)) {
+				copp_idx = j;
+				break;
+			}
+		}
+
+		ret = adm_programable_channel_mixer(
+			msm_bedais[be_id].port_id,
+			copp_idx, dspst_id, sess_type,
+			&channel_mixer_v2[fe_id][sess_type], 0);
+	}
+
+	return ret;
+}
+
 static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
 				int dspst_id, int stream_type)
 {
@@ -1498,6 +1602,14 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
 	int i = 0, j = 0, be_id;
 	int ret = 0;
 
+	ret = msm_pcm_routing_channel_mixer_v2(fe_id, perf_mode,
+				dspst_id, stream_type);
+	if (ret) {
+		pr_err("%s channel mixer v2 cmd  set failure%d\n", __func__,
+				fe_id);
+		return ret;
+	}
+
 	if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
 		pr_err("%s: invalid FE %d\n", __func__, fe_id);
 		return 0;
@@ -1546,6 +1658,72 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
 	return ret;
 }
 
+/**
+ * msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer
+ * setting during runtime.
+ *
+ * @be_id: backend index
+ * @session_id: session index
+ * @session_type: session type
+ * @params: parameters for channel mixer
+ *
+ * Retuen: 0 for success, else error
+ */
+int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
+			int session_type,
+			struct msm_pcm_channel_mixer *params)
+{
+	int rc = 0;
+	int port_id, copp_idx = 0;
+
+	be_id--;
+	if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: invalid backend id %d\n", __func__,
+				be_id);
+		return -EINVAL;
+	}
+
+	port_id = msm_bedais[be_id].port_id;
+	copp_idx = adm_get_default_copp_idx(port_id);
+	pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n",
+		 __func__, port_id, copp_idx, session_id);
+
+	if ((params->input_channel < 0) ||
+		(params->input_channel > ADM_MAX_CHANNELS)) {
+		pr_err("%s: invalid input channel %d\n", __func__,
+				params->input_channel);
+		return -EINVAL;
+	}
+
+	if ((params->output_channel < 0) ||
+		(params->output_channel > ADM_MAX_CHANNELS)) {
+		pr_err("%s: invalid output channel %d\n", __func__,
+				params->output_channel);
+		return -EINVAL;
+	}
+
+	params->input_channels[0] = params->input_channel;
+
+	pr_debug("%s sess type %d,override in:%d out:%d,be active %d\n",
+			__func__, session_type,
+			params->override_in_ch_map,
+			params->override_out_ch_map,
+			msm_bedais[be_id].active);
+
+	rc = adm_programable_channel_mixer(port_id,
+					copp_idx,
+					session_id,
+					session_type,
+					params,
+					0);
+	if (rc) {
+		pr_err("%s: send params failed rc=%d\n", __func__, rc);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
+
 int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
 					int dspst_id, int stream_type)
 {

+ 10 - 0
asoc/msm-pcm-routing-v2.h

@@ -4,6 +4,7 @@
 #ifndef _MSM_PCM_ROUTING_H
 #define _MSM_PCM_ROUTING_H
 #include <dsp/apr_audio-v2.h>
+#include <dsp/q6adm-v2.h>
 
 /*
  * These names are used by HAL to specify the BE. If any changes are
@@ -594,4 +595,13 @@ int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt,
 	int op_channel_cnt, int *ch_wght_coeff,
 	int session_type, int stream_type);
 int msm_pcm_routing_get_pp_ch_cnt(int fe_id, int session_type);
+
+int msm_pcm_routing_set_channel_mixer_cfg(
+	int fe_id, int session_type,
+	struct msm_pcm_channel_mixer *params);
+
+int msm_pcm_routing_set_channel_mixer_runtime(
+	int be_id, int session_id,
+	int session_type,
+	struct msm_pcm_channel_mixer *params);
 #endif /*_MSM_PCM_H*/