diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 2bde61af10..4b77fbd674 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/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; } diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index b516634fd1..a68698438e 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/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 @@ -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; } diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 5886736286..bea58c9e07 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/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; diff --git a/asoc/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h index 8699a4825c..62fcebd3cb 100644 --- a/asoc/msm-pcm-q6-v2.h +++ b/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 { diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 00f603e36e..11a2718811 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/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) { diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 9b663fffd8..fc93059d3a 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -4,6 +4,7 @@ #ifndef _MSM_PCM_ROUTING_H #define _MSM_PCM_ROUTING_H #include +#include /* * 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*/