Browse Source

Merge "asoc: add support to set custom channel mixer coefficients"

Linux Build Service Account 6 years ago
parent
commit
753eedb972
6 changed files with 2190 additions and 6 deletions
  1. 613 4
      asoc/msm-compress-q6-v2.c
  2. 684 1
      asoc/msm-pcm-loopback-v2.c
  3. 702 0
      asoc/msm-pcm-q6-v2.c
  4. 3 1
      asoc/msm-pcm-q6-v2.h
  5. 178 0
      asoc/msm-pcm-routing-v2.c
  6. 10 0
      asoc/msm-pcm-routing-v2.h

+ 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*/