Define the dai_link_fixup PCM op for IPC3 and use it Signed-off-by: Ranjani Sridharan <ranjani.sridharan@xxxxxxxxxxxxxxx> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@xxxxxxxxxxxxxxx> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> Reviewed-by: Bard Liao <yung-chuan.liao@xxxxxxxxxxxxxxx> --- sound/soc/sof/ipc3-pcm.c | 186 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 174 +----------------------------------- 2 files changed, 189 insertions(+), 171 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index d83d54540bbe..58b75943cf6d 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -179,8 +179,194 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, sizeof(stream), &reply, sizeof(reply)); } +static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, + struct snd_pcm_hw_params *params) +{ + struct sof_ipc_dai_config *config; + struct snd_sof_dai *dai; + int i; + + /* + * Search for all matching DAIs as we can have both playback and capture DAI + * associated with the same link. + */ + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name || strcmp(link_name, dai->name)) + continue; + for (i = 0; i < dai->number_configs; i++) { + struct sof_dai_private_data *private = dai->private; + + config = &private->dai_config[i]; + if (config->ssp.fsync_rate == params_rate(params)) { + dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); + dai->current_config = i; + break; + } + } + } +} + +static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private; + struct snd_soc_dpcm *dpcm; + + if (!dai) { + dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + private = dai->private; + if (!private) { + dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (private->comp_dai->config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(component->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (private->dai_config->type) { + case SOF_DAI_INTEL_SSP: + /* search for config to pcm params match, if not found use default */ + ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); + + rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; + rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; + channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; + channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* DMIC only supports 16 or 32 bit formats */ + if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(component->dev, "Invalid fmt %d for DAI type %d\n", + private->comp_dai->config.frame_fmt, + private->dai_config->type); + } + break; + case SOF_DAI_INTEL_HDA: + /* + * HDAudio does not follow the default trigger + * sequence due to firmware implementation + */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + SND_SOC_DPCM_TRIGGER_POST; + } + break; + case SOF_DAI_INTEL_ALH: + /* + * Dai could run with different channel count compared with + * front end, so get dai channel count from topology + */ + channels->min = private->dai_config->alh.channels; + channels->max = private->dai_config->alh.channels; + break; + case SOF_DAI_IMX_ESAI: + rate->min = private->dai_config->esai.fsync_rate; + rate->max = private->dai_config->esai.fsync_rate; + channels->min = private->dai_config->esai.tdm_slots; + channels->max = private->dai_config->esai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = private->dai_config->afe.rate; + rate->max = private->dai_config->afe.rate; + channels->min = private->dai_config->afe.channels; + channels->max = private->dai_config->afe.channels; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_IMX_SAI: + rate->min = private->dai_config->sai.fsync_rate; + rate->max = private->dai_config->sai.fsync_rate; + channels->min = private->dai_config->sai.tdm_slots; + channels->max = private->dai_config->sai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_BT: + rate->min = private->dai_config->acpbt.fsync_rate; + rate->max = private->dai_config->acpbt.fsync_rate; + channels->min = private->dai_config->acpbt.tdm_slots; + channels->max = private->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = private->dai_config->acpsp.fsync_rate; + rate->max = private->dai_config->acpsp.fsync_rate; + channels->min = private->dai_config->acpsp.tdm_slots; + channels->max = private->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = private->dai_config->acpdmic.fsync_rate; + rate->max = private->dai_config->acpdmic.fsync_rate; + channels->min = private->dai_config->acpdmic.tdm_slots; + channels->max = private->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + default: + dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); + break; + } + + return 0; +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, .trigger = sof_ipc3_pcm_trigger, + .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index dc55a04a1588..658cd8966c9a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -556,33 +556,6 @@ static int sof_pcm_new(struct snd_soc_component *component, return 0; } -static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, - struct snd_pcm_hw_params *params) -{ - struct sof_ipc_dai_config *config; - struct snd_sof_dai *dai; - int i; - - /* - * Search for all matching DAIs as we can have both playback and capture DAI - * associated with the same link. - */ - list_for_each_entry(dai, &sdev->dai_list, list) { - if (!dai->name || strcmp(link_name, dai->name)) - continue; - for (i = 0; i < dai->number_configs; i++) { - struct sof_dai_private_data *private = dai->private; - - config = &private->dai_config[i]; - if (config->ssp.fsync_rate == params_rate(params)) { - dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); - dai->current_config = i; - break; - } - } - } -} - /* fixup the BE DAI link to match any values from topology */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -596,8 +569,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_dai_private_data *private = dai->private; - struct snd_soc_dpcm *dpcm; + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; /* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -618,148 +590,8 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa return 0; } - /* read format from topology */ - snd_mask_none(fmt); - - switch (private->comp_dai->config.frame_fmt) { - case SOF_IPC_FRAME_S16_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); - break; - case SOF_IPC_FRAME_S24_4LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); - break; - case SOF_IPC_FRAME_S32_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - break; - default: - dev_err(component->dev, "error: No available DAI format!\n"); - return -EINVAL; - } - - /* read rate and channels from topology */ - switch (private->dai_config->type) { - case SOF_DAI_INTEL_SSP: - /* search for config to pcm params match, if not found use default */ - ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - - rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; - rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; - channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; - channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - - break; - case SOF_DAI_INTEL_DMIC: - /* DMIC only supports 16 or 32 bit formats */ - if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { - dev_err(component->dev, - "error: invalid fmt %d for DAI type %d\n", - private->comp_dai->config.frame_fmt, - private->dai_config->type); - } - break; - case SOF_DAI_INTEL_HDA: - /* - * HDAudio does not follow the default trigger - * sequence due to firmware implementation - */ - for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { - struct snd_soc_pcm_runtime *fe = dpcm->fe; - - fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = - SND_SOC_DPCM_TRIGGER_POST; - } - break; - case SOF_DAI_INTEL_ALH: - /* - * Dai could run with different channel count compared with - * front end, so get dai channel count from topology - */ - channels->min = private->dai_config->alh.channels; - channels->max = private->dai_config->alh.channels; - break; - case SOF_DAI_IMX_ESAI: - rate->min = private->dai_config->esai.fsync_rate; - rate->max = private->dai_config->esai.fsync_rate; - channels->min = private->dai_config->esai.tdm_slots; - channels->max = private->dai_config->esai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_MEDIATEK_AFE: - rate->min = private->dai_config->afe.rate; - rate->max = private->dai_config->afe.rate; - channels->min = private->dai_config->afe.channels; - channels->max = private->dai_config->afe.channels; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_IMX_SAI: - rate->min = private->dai_config->sai.fsync_rate; - rate->max = private->dai_config->sai.fsync_rate; - channels->min = private->dai_config->sai.tdm_slots; - channels->max = private->dai_config->sai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_BT: - rate->min = private->dai_config->acpbt.fsync_rate; - rate->max = private->dai_config->acpbt.fsync_rate; - channels->min = private->dai_config->acpbt.tdm_slots; - channels->max = private->dai_config->acpbt.tdm_slots; - - dev_dbg(component->dev, - "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_BT channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_SP: - rate->min = private->dai_config->acpsp.fsync_rate; - rate->max = private->dai_config->acpsp.fsync_rate; - channels->min = private->dai_config->acpsp.tdm_slots; - channels->max = private->dai_config->acpsp.tdm_slots; - - dev_dbg(component->dev, - "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_SP channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_DMIC: - rate->min = private->dai_config->acpdmic.fsync_rate; - rate->max = private->dai_config->acpdmic.fsync_rate; - channels->min = private->dai_config->acpdmic.tdm_slots; - channels->max = private->dai_config->acpdmic.tdm_slots; - - dev_dbg(component->dev, - "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_DMIC channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - default: - dev_err(component->dev, "error: invalid DAI type %d\n", - private->dai_config->type); - break; - } + if (pcm_ops->dai_link_fixup) + return pcm_ops->dai_link_fixup(rtd, params); return 0; } -- 2.25.1