diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index dceb78bfe17c68..b1f425b39db94f 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2081,7 +2081,9 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * break; case SOF_DAI_INTEL_ALH: if (data) { - config->dai_index = data->dai_index; + /* save the dai_index during hw_params and reuse it for hw_free */ + if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) + config->dai_index = data->dai_index; config->alh.stream_id = data->dai_data; } break; @@ -2089,7 +2091,30 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * break; } - config->flags = flags; + /* + * The dai_config op is invoked several times and the flags argument varies as below: + * BE DAI hw_params: When the op is invoked during the BE DAI hw_params, flags contains + * SOF_DAI_CONFIG_FLAGS_HW_PARAMS along with quirks + * FE DAI hw_params: When invoked during FE DAI hw_params after the DAI widget has + * just been set up in the DSP, flags is set to SOF_DAI_CONFIG_FLAGS_HW_PARAMS with no + * quirks + * BE DAI trigger: When invoked during the BE DAI trigger, flags is set to + * SOF_DAI_CONFIG_FLAGS_PAUSE and contains no quirks + * BE DAI hw_free: When invoked during the BE DAI hw_free, flags is set to + * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks + * FE DAI hw_free: When invoked during the FE DAI hw_free, flags is set to + * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks + * + * The DAI_CONFIG IPC is sent to the DSP, only after the widget is set up during the FE + * DAI hw_params. But since the BE DAI hw_params precedes the FE DAI hw_params, the quirks + * need to be preserved when assigning the flags before sending the IPC. + * For the case of PAUSE/HW_FREE, since there are no quirks, flags can be used as is. + */ + + if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) + config->flags |= flags; + else + config->flags = flags; /* only send the IPC if the widget is set up in the DSP */ if (swidget->use_count > 0) { @@ -2097,6 +2122,9 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); + + /* clear the flags once the IPC has been sent even if it fails */ + config->flags = SOF_DAI_CONFIG_FLAGS_NONE; } return ret; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index dc44ba2ec71c2f..ae02cc152f8795 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -980,6 +980,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) ipc4_copier = dai->private; if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { + struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_alh_configuration_blob *blob; unsigned int group_id; @@ -989,6 +990,9 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) ALH_MULTI_GTW_BASE; ida_free(&alh_group_ida, group_id); } + + /* clear the node ID */ + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; } } @@ -1940,8 +1944,15 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * pipeline->skip_during_fe_trigger = true; fallthrough; case SOF_DAI_INTEL_ALH: - copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; - copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data); + /* + * Do not clear the node ID when this op is invoked with + * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during + * unprepare. + */ + if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data); + } break; case SOF_DAI_INTEL_DMIC: case SOF_DAI_INTEL_SSP: diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 760621bfc80284..d7df29f2ada856 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -50,9 +50,27 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); + /* free DAI config and continue to free widget even if it fails */ + if (WIDGET_IS_DAI(swidget->id)) { + struct snd_sof_dai_config_data data; + unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE; + + data.dai_data = DMA_CHAN_INVALID; + + if (tplg_ops && tplg_ops->dai_config) { + err = tplg_ops->dai_config(sdev, swidget, flags, &data); + if (err < 0) + dev_err(sdev->dev, "failed to free config for widget %s\n", + swidget->widget->name); + } + } + /* continue to disable core even if IPC fails */ - if (tplg_ops && tplg_ops->widget_free) - err = tplg_ops->widget_free(sdev, swidget); + if (tplg_ops && tplg_ops->widget_free) { + ret = tplg_ops->widget_free(sdev, swidget); + if (ret < 0 && !err) + err = ret; + } /* * disable widget core. continue to route setup status and complete flag @@ -151,8 +169,12 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, /* send config for DAI components */ if (WIDGET_IS_DAI(swidget->id)) { - unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE; + unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; + /* + * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does + * not use the flags argument. + */ if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, NULL); if (ret < 0)