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: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Link: https://lore.kernel.org/r/20220426171743.171061-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
33a3facdf8
commit
5da0590a09
@ -253,29 +253,135 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
|
|||||||
return 0;
|
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)
|
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;
|
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_list *list = spcm->stream[dir].list;
|
||||||
struct snd_soc_dapm_widget *widget;
|
struct snd_soc_dapm_widget *widget;
|
||||||
int i, ret, num_widgets;
|
int i, ret;
|
||||||
|
|
||||||
/* nothing to set up */
|
/* nothing to set up */
|
||||||
if (!list)
|
if (!list)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* set up widgets in the list */
|
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_SETUP);
|
||||||
for_each_dapm_widgets(list, num_widgets, widget) {
|
if (ret < 0)
|
||||||
struct snd_sof_widget *swidget = widget->dobj.private;
|
return ret;
|
||||||
|
|
||||||
if (!swidget)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* set up the widget */
|
|
||||||
ret = sof_widget_setup(sdev, swidget);
|
|
||||||
if (ret < 0)
|
|
||||||
goto widget_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* error in setting pipeline connections will result in route status being reset for
|
* 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;
|
return 0;
|
||||||
|
|
||||||
widget_free:
|
widget_free:
|
||||||
/* free all widgets that have been set up successfully */
|
sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -335,37 +430,18 @@ widget_free:
|
|||||||
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
|
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_list *list = spcm->stream[dir].list;
|
||||||
struct snd_soc_dapm_widget *widget;
|
int ret;
|
||||||
int i, ret;
|
|
||||||
int ret1 = 0;
|
|
||||||
|
|
||||||
/* nothing to free */
|
/* nothing to free */
|
||||||
if (!list)
|
if (!list)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_soc_dapm_dai_free_widgets(&list);
|
snd_soc_dapm_dai_free_widgets(&list);
|
||||||
spcm->stream[dir].list = NULL;
|
spcm->stream[dir].list = NULL;
|
||||||
|
|
||||||
return ret1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -29,10 +29,17 @@
|
|||||||
#define DMA_CHAN_INVALID 0xFFFFFFFF
|
#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_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_MCLK 0
|
||||||
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
|
#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
|
* Volume fractional word length define to 16 sets
|
||||||
* the volume linear gain value to use Qx.16 format
|
* the volume linear gain value to use Qx.16 format
|
||||||
|
Loading…
x
Reference in New Issue
Block a user