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

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.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 33a3facd
Loading
Loading
Loading
Loading
+123 −47
Original line number Diff line number Diff line
@@ -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);
	ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_SETUP);
	if (ret < 0)
			goto widget_free;
	}
		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;
}

/*
+7 −0
Original line number Diff line number Diff line
@@ -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