Commit 5da0590a authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown

ASoC: SOF: sof-audio: Set up widgets from source to sink

For IPC3, the order of setting up the widgets associated with a PCM
doesn't matter. But for IPC4, widgets must be set up from the source to
the sink in order. In order to accommodate this, change the
sof_widget_list_setup/free() functions to set up/free widgets starting
with the source widget all the way to the sink widget for all pipelines.
Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20220426171743.171061-4-ranjani.sridharan@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 33a3facd
......@@ -253,29 +253,135 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
return 0;
}
/*
* free all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback)
*/
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
int dir)
{
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
/* free all widgets even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
p->walking = true;
if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
err = sof_widget_free(sdev, widget->dobj.private);
if (err < 0)
ret = err;
}
err = sof_widget_free(sdev, p->sink->dobj.private);
if (err < 0)
ret = err;
err = sof_free_widgets_in_path(sdev, p->sink, dir);
if (err < 0)
ret = err;
p->walking = false;
}
}
return ret;
}
/*
* set up all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback).
* The error path in this function ensures that all successfully set up widgets getting freed.
*/
static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
int dir)
{
struct snd_soc_dapm_path *p;
int ret;
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
p->walking = true;
if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
ret = sof_widget_setup(sdev, widget->dobj.private);
if (ret < 0)
goto out;
}
ret = sof_widget_setup(sdev, p->sink->dobj.private);
if (ret < 0) {
if (WIDGET_IS_AIF_OR_DAI(widget->id))
sof_widget_free(sdev, widget->dobj.private);
goto out;
}
ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
if (ret < 0) {
if (WIDGET_IS_AIF_OR_DAI(widget->id))
sof_widget_free(sdev, widget->dobj.private);
sof_widget_free(sdev, p->sink->dobj.private);
}
out:
p->walking = false;
if (ret < 0)
return ret;
}
}
return 0;
}
static int
sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
bool dir, enum sof_widget_op op)
{
struct snd_soc_dapm_widget *widget;
int ret, i;
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
continue;
/* starting widget for capture is DAI type */
if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
continue;
switch (op) {
case SOF_WIDGET_SETUP:
ret = sof_set_up_widgets_in_path(sdev, widget, dir);
break;
case SOF_WIDGET_FREE:
ret = sof_free_widgets_in_path(sdev, widget, dir);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
return -EINVAL;
}
if (ret < 0) {
dev_err(sdev->dev, "Failed to %s connected widgets\n",
op == SOF_WIDGET_SETUP ? "set up" : "free");
return ret;
}
}
return 0;
}
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret, num_widgets;
int i, ret;
/* nothing to set up */
if (!list)
return 0;
/* set up widgets in the list */
for_each_dapm_widgets(list, num_widgets, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
/* set up the widget */
ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
goto widget_free;
}
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_SETUP);
if (ret < 0)
return ret;
/*
* error in setting pipeline connections will result in route status being reset for
......@@ -316,18 +422,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
return 0;
widget_free:
/* free all widgets that have been set up successfully */
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
if (!num_widgets--)
break;
sof_widget_free(sdev, swidget);
}
sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
return ret;
}
......@@ -335,37 +430,18 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret;
int ret1 = 0;
int ret;
/* nothing to free */
if (!list)
return 0;
/*
* Free widgets in the list. This can fail but continue freeing other widgets to keep
* use_counts balanced.
*/
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
/*
* free widget and its pipe_widget. Either of these can fail, but free as many as
* possible before freeing the list and returning the error.
*/
ret = sof_widget_free(sdev, swidget);
if (ret < 0)
ret1 = ret;
}
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
return ret1;
return ret;
}
/*
......
......@@ -29,10 +29,17 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
enum sof_widget_op {
SOF_WIDGET_FREE,
SOF_WIDGET_SETUP,
};
/*
* Volume fractional word length define to 16 sets
* the volume linear gain value to use Qx.16 format
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment