diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 9f33b5994527..19045e2eda07 100755 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -67,6 +67,7 @@ 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]; + struct msm_pcm_channel_mixer *chmixer_ec_ref[MSM_FRONTEND_DAI_MM_SIZE]; }; static void stop_pcm(struct msm_pcm_loopback *pcm); @@ -1448,6 +1449,459 @@ fail: return ret; } + +static int msm_pcm_chmixer_ec_ref_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* 2 int values: input_channel, output_channel */ + uinfo->count = 2; + /* Valid range is all positive values to support above controls */ + uinfo->value.integer.min = 1; + uinfo->value.integer.max = PCM_FORMAT_MAX_NUM_CHANNEL_V8; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_cfg_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = chmixer_ec_ref->input_channel; + ucontrol->value.integer.value[1] = chmixer_ec_ref->output_channel; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_cfg_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->input_channel = ucontrol->value.integer.value[0]; + chmixer_ec_ref->output_channel = ucontrol->value.integer.value[1]; + + if (chmixer_ec_ref->input_channel < 0 || + chmixer_ec_ref->input_channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8 || + chmixer_ec_ref->output_channel < 0 || + chmixer_ec_ref->output_channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: Invalid channels, in %d, out %d\n", + __func__, chmixer_ec_ref->input_channel, + chmixer_ec_ref->output_channel); + return -EINVAL; + } + + /* cache value and take effect during adm open stage */ + msm_pcm_routing_set_stream_ec_ref_chmix_cfg(fe_id, chmixer_ec_ref); + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_cfg_info, + .put = msm_pcm_chmixer_ec_ref_cfg_ctl_put, + .get = msm_pcm_chmixer_ec_ref_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); + + pdata->pcm_device[rtd->dai_link->id] = rtd->pcm; + + /* only apply for TX path */ + 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_chmixer_ec_ref_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_chmixer_ec_ref_weight_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + int channel = (kcontrol->private_value >> 16) & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: invalid channel %d\n", + __func__, channel); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->channel_weight[channel-1][i]; + + return 0; +} + + +static int msm_pcm_chmixer_ec_ref_weight_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + int channel = (kcontrol->private_value >> 16) & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: invalid channel %d\n", + __func__, channel); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->channel_weight[channel-1][i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_weight_controls( + struct snd_soc_pcm_runtime *rtd, + int channel) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_weight_info, + .put = msm_pcm_chmixer_ec_ref_weight_ctl_put, + .get = msm_pcm_chmixer_ec_ref_weight_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_chmixer_ec_ref_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 = 0; + uinfo->value.integer.max = 64; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_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 = 0; + uinfo->value.integer.max = 64; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_input_map_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->in_ch_map[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_input_map_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->override_in_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->in_ch_map[i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_output_map_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->out_ch_map[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_output_map_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 0xFF; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->override_out_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->out_ch_map[i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_input_map_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_input_map_info, + .put = msm_pcm_chmixer_ec_ref_input_map_ctl_put, + .get = msm_pcm_chmixer_ec_ref_input_map_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_add_chmixer_ec_ref_output_map_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_output_map_info, + .put = msm_pcm_chmixer_ec_ref_output_map_ctl_put, + .get = msm_pcm_chmixer_ec_ref_output_map_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) { int i, ret = 0; @@ -1494,6 +1948,18 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) } } + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream && + !pdata->chmixer_ec_ref[rtd->dai_link->id]) { + pdata->chmixer_ec_ref[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_pcm_channel_mixer), + GFP_KERNEL); + if (!pdata->chmixer_ec_ref[rtd->dai_link->id]) { + pr_err("%s: fail to allocate memory\n", __func__); + 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", @@ -1513,6 +1979,26 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) goto fail; } + ret = msm_pcm_add_chmixer_ec_ref_controls(rtd); + if (ret) { + pr_err("%s: pcm add ef_ref channel mixer cfg controls failed:%d\n", + __func__, ret); + goto fail; + } + + ret = msm_pcm_add_chmixer_ec_ref_input_map_controls(rtd); + if (ret) { + pr_err("%s: pcm add ec_ref channel mixer input map controls failed:%d\n", + __func__, ret); + goto fail; + } + ret = msm_pcm_add_chmixer_ec_ref_output_map_controls(rtd); + if (ret) { + pr_err("%s: pcm add ec_ref 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) { @@ -1520,6 +2006,13 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) __func__, ret); goto fail; } + + ret = msm_pcm_add_chmixer_ec_ref_weight_controls(rtd, i); + if (ret) { + pr_err("%s: pcm add ec_ref channel weight controls failed:%d\n", + __func__, ret); + goto fail; + } } return 0; @@ -1528,6 +2021,8 @@ fail: 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; + kfree(pdata->chmixer_ec_ref[rtd->dai_link->id]); + pdata->chmixer_ec_ref[rtd->dai_link->id] = NULL; return ret; } @@ -1800,6 +2295,7 @@ static int msm_pcm_remove(struct platform_device *pdev) 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->chmixer_ec_ref[i]); } } kfree(pdata); diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 7d8916a11b3b..8062b79c68b2 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -2829,6 +2829,458 @@ fail: return ret; } +static int msm_pcm_chmixer_ec_ref_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + /* 2 int values: input_channel, output_channel */ + uinfo->count = 2; + /* Valid range is all positive values to support above controls */ + uinfo->value.integer.min = 1; + uinfo->value.integer.max = PCM_FORMAT_MAX_NUM_CHANNEL_V8; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_cfg_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = chmixer_ec_ref->input_channel; + ucontrol->value.integer.value[1] = chmixer_ec_ref->output_channel; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_cfg_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->input_channel = ucontrol->value.integer.value[0]; + chmixer_ec_ref->output_channel = ucontrol->value.integer.value[1]; + + if (chmixer_ec_ref->input_channel < 0 || + chmixer_ec_ref->input_channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8 || + chmixer_ec_ref->output_channel < 0 || + chmixer_ec_ref->output_channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: Invalid channels, in %d, out %d\n", + __func__, chmixer_ec_ref->input_channel, + chmixer_ec_ref->output_channel); + return -EINVAL; + } + + /* cache value and take effect during adm open stage */ + msm_pcm_routing_set_stream_ec_ref_chmix_cfg(fe_id, chmixer_ec_ref); + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_cfg_info, + .put = msm_pcm_chmixer_ec_ref_cfg_ctl_put, + .get = msm_pcm_chmixer_ec_ref_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; + + /* only apply for TX path */ + 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_chmixer_ec_ref_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_chmixer_ec_ref_weight_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: invalid channel %d\n", + __func__, channel); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->channel_weight[channel-1][i]; + + return 0; +} + + +static int msm_pcm_chmixer_ec_ref_weight_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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); + struct msm_pcm_channel_mixer *chmixer_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: invalid channel %d\n", + __func__, channel); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->channel_weight[channel-1][i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_weight_controls( + struct snd_soc_pcm_runtime *rtd, + int channel) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_weight_info, + .put = msm_pcm_chmixer_ec_ref_weight_ctl_put, + .get = msm_pcm_chmixer_ec_ref_weight_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_chmixer_ec_ref_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 = 0; + uinfo->value.integer.max = 64; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_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 = 0; + uinfo->value.integer.max = 64; + return 0; +} + +static int msm_pcm_chmixer_ec_ref_input_map_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->in_ch_map[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_input_map_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->override_in_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->in_ch_map[i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_output_map_ctl_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + chmixer_ec_ref->out_ch_map[i]; + + return 0; +} + +static int msm_pcm_chmixer_ec_ref_output_map_ctl_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = kcontrol->private_value & 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_ec_ref = NULL; + int i = 0; + + if (!pdata) { + pr_err("%s missing pdata\n", __func__); + return -EINVAL; + } + + chmixer_ec_ref = pdata->chmixer_ec_ref[fe_id]; + if (!chmixer_ec_ref) { + pr_err("%s: invalid chmixer_ec_ref in pdata", __func__); + return -EINVAL; + } + + chmixer_ec_ref->override_out_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + chmixer_ec_ref->out_ch_map[i] = + ucontrol->value.integer.value[i]; + + return 0; +} + +static int msm_pcm_add_chmixer_ec_ref_input_map_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_input_map_info, + .put = msm_pcm_chmixer_ec_ref_input_map_ctl_put, + .get = msm_pcm_chmixer_ec_ref_input_map_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_add_chmixer_ec_ref_output_map_controls( + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + const char *capture_mixer_ctl_name = "AudStr Cap"; + const char *suffix = "EC Ref 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_chmixer_ec_ref_output_map_info, + .put = msm_pcm_chmixer_ec_ref_output_map_ctl_put, + .get = msm_pcm_chmixer_ec_ref_output_map_ctl_get, + .private_value = 0, + }; + + /* only apply for TX path */ + 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_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) { int i, ret = 0; @@ -2875,6 +3327,18 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) } } + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream && + !pdata->chmixer_ec_ref[rtd->dai_link->id]) { + pdata->chmixer_ec_ref[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_pcm_channel_mixer), + GFP_KERNEL); + if (!pdata->chmixer_ec_ref[rtd->dai_link->id]) { + pr_err("%s: fail to allocate memory\n", __func__); + 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", @@ -2887,6 +3351,7 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) __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", @@ -2894,6 +3359,26 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) goto fail; } + ret = msm_pcm_add_chmixer_ec_ref_controls(rtd); + if (ret) { + pr_err("%s: pcm add ef_ref channel mixer cfg controls failed:%d\n", + __func__, ret); + goto fail; + } + + ret = msm_pcm_add_chmixer_ec_ref_input_map_controls(rtd); + if (ret) { + pr_err("%s: pcm add ec_ref channel mixer input map controls failed:%d\n", + __func__, ret); + goto fail; + } + ret = msm_pcm_add_chmixer_ec_ref_output_map_controls(rtd); + if (ret) { + pr_err("%s: pcm add ec_ref 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) { @@ -2901,6 +3386,13 @@ static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd) __func__, ret); goto fail; } + + ret = msm_pcm_add_chmixer_ec_ref_weight_controls(rtd, i); + if (ret) { + pr_err("%s: pcm add ec_ref channel weight controls failed:%d\n", + __func__, ret); + goto fail; + } } return 0; @@ -2909,6 +3401,8 @@ fail: 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; + kfree(pdata->chmixer_ec_ref[rtd->dai_link->id]); + pdata->chmixer_ec_ref[rtd->dai_link->id] = NULL; return ret; } @@ -3066,6 +3560,7 @@ static int msm_pcm_remove(struct platform_device *pdev) 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->chmixer_ec_ref[i]); } } mutex_destroy(&pdata->lock); diff --git a/asoc/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h index 6498f07fb4ed..979b36000aa7 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, 2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, 2019, 2021 The Linux Foundation. All rights reserved. */ #ifndef _MSM_PCM_H @@ -117,6 +117,7 @@ struct msm_plat_data { 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_channel_mixer *chmixer_ec_ref[MSM_FRONTEND_DAI_MM_SIZE]; struct mutex lock; }; diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index d3e437aff02d..27432de81657 100755 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -94,6 +94,8 @@ static int num_app_cfg_types; static int msm_ec_ref_port_id; static int afe_loopback_tx_port_index; static int afe_loopback_tx_port_id = -1; +static struct msm_pcm_channel_mixer ec_ref_chmix_cfg[MSM_FRONTEND_DAI_MAX]; +static struct msm_ec_ref_port_cfg ec_ref_port_cfg; #define WEIGHT_0_DB 0x4000 /* all the FEs which can support channel mixer */ @@ -1258,6 +1260,85 @@ int msm_pcm_routing_set_channel_mixer_cfg( } EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_cfg); + +/** + * msm_pcm_routing_set_stream_ec_ref_chmix_cfg + * + * Receives fedai_id, ec_ref chmix configuration. + * Returns 0 on success. On failure returns + * -EINVAL and does not alter passed values. + * + * fedai_id - Passed value, front end ID for which ec_ref config is wanted + * cfg_data - Passed value, configuration data used by ec_ref + */ +int msm_pcm_routing_set_stream_ec_ref_chmix_cfg( + int fedai_id, struct msm_pcm_channel_mixer *cfg_data) +{ + int i, j, ret = 0; + + if (cfg_data == NULL) { + pr_err("%s: Received NULL pointer for cfg_data\n", __func__); + ret = -EINVAL; + goto done; + } + if (cfg_data->input_channel != msm_ec_ref_ch) { + pr_err("%s: mismatched input ch %d with port config ch %d\n", + __func__, cfg_data->input_channel, + msm_ec_ref_ch); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: fedai_id %d, input_channel %d output_channel %d\n", + __func__, fedai_id, + cfg_data->input_channel, cfg_data->output_channel); + + for (i = 0; i < cfg_data->output_channel; i++) + for (j = 0; j < cfg_data->input_channel; j++) + pr_debug("%s: ch[%d][%d] weight = %d", + __func__, i, j, cfg_data->channel_weight[i][j]); + + if (fedai_id < 0 || fedai_id > MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fedai_id %d\n", + __func__, fedai_id); + ret = -EINVAL; + goto done; + } + + mutex_lock(&routing_lock); + + ec_ref_chmix_cfg[fedai_id].input_channel = + cfg_data->input_channel; + ec_ref_chmix_cfg[fedai_id].output_channel = + cfg_data->output_channel; + for (i = 0; i < cfg_data->output_channel; i++) + for (j = 0; j < cfg_data->input_channel; j++) + ec_ref_chmix_cfg[fedai_id].channel_weight[i][j] = + cfg_data->channel_weight[i][j]; + + ec_ref_chmix_cfg[fedai_id].override_in_ch_map = + cfg_data->override_in_ch_map; + if (ec_ref_chmix_cfg[fedai_id].override_in_ch_map) { + for (i = 0; i < ec_ref_chmix_cfg[fedai_id].input_channel; i++) + ec_ref_chmix_cfg[fedai_id].in_ch_map[i] = + cfg_data->in_ch_map[i]; + } + + ec_ref_chmix_cfg[fedai_id].override_out_ch_map = + cfg_data->override_out_ch_map; + if (ec_ref_chmix_cfg[fedai_id].override_out_ch_map) { + for (i = 0; i < ec_ref_chmix_cfg[fedai_id].output_channel; i++) + ec_ref_chmix_cfg[fedai_id].out_ch_map[i] = + cfg_data->out_ch_map[i]; + } + + mutex_unlock(&routing_lock); + +done: + return ret; +} +EXPORT_SYMBOL(msm_pcm_routing_set_stream_ec_ref_chmix_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) @@ -2174,7 +2255,33 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, && be_bit_width == 32) bits_per_sample = msm_routing_get_bit_width( SNDRV_PCM_FORMAT_S32_LE); - copp_idx = adm_open(port_id, path_type, + if ((session_type == SESSION_TYPE_TX) && + (ec_ref_chmix_cfg[fedai_id].output_channel)) { + /* per-session ec_ref configuration */ + ec_ref_port_cfg.rx = msm_route_ec_ref_rx; + ec_ref_port_cfg.port_id = msm_ec_ref_port_id; + ec_ref_port_cfg.ch = msm_ec_ref_ch; + ec_ref_port_cfg.sampling_rate = + msm_ec_ref_sampling_rate; + if (msm_ec_ref_bit_format == + SNDRV_PCM_FORMAT_S16_LE) + ec_ref_port_cfg.bit_width = 16; + else if (msm_ec_ref_bit_format == + SNDRV_PCM_FORMAT_S24_LE) + ec_ref_port_cfg.bit_width = 24; + + copp_idx = adm_open_v2(port_id, path_type, + sample_rate, channels, topology, + perf_mode, bits_per_sample, + app_type, acdb_dev_id, + session_type, passthr_mode, + copp_token, + &ec_ref_port_cfg, + &ec_ref_chmix_cfg[fedai_id]); + /* reset ec_ref config */ + ec_ref_chmix_cfg[fedai_id].output_channel = 0; + } else + copp_idx = adm_open(port_id, path_type, sample_rate, channels, topology, perf_mode, bits_per_sample, app_type, acdb_dev_id, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index efed7f05351b..a6933474da98 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ #ifndef _MSM_PCM_ROUTING_H #define _MSM_PCM_ROUTING_H @@ -663,6 +663,9 @@ int msm_pcm_routing_set_channel_mixer_runtime( int session_type, struct msm_pcm_channel_mixer *params); +int msm_pcm_routing_set_stream_ec_ref_chmix_cfg( + int fedai_id, struct msm_pcm_channel_mixer *cfg_data); + #ifndef SND_PCM_ADD_VOLUME_CTL /* PCM Volume control API */