Commit 66344c6d authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown

ASoC: SOF: Add a prepare op to IPC topology widget ops

In order to set up a pipeline with IPC4, the total memory usage for the
pipeline needs to be calculated based on the list of connected widgets.
Add a new ipc_prepare() op to struct sof_ipc_tplg_widget_ops that will be
used to calculate the memory usage for each widget in the pipelines
associated with a PCM and prepare the widget for getting set up in the
DSP. The prepare step will be used to allocate memory for the IPC
payload, assign instance ID and update the config data for the widget
based on the runtime PCM params. Once prepared, the setup step is used
to send the IPC to create the widget in the DSP.

Add an ipc_unprepare() op to unprepare the widget i.e free the memory
allocated during prepare, free the instance ID etc. This should be
invoked after the widget is freed.

A new flag "prepared" is added to struct snd_sof_widget to track the
prepared status of widgets.

Also, IPC4 requires the platform_params and the runtime PCM params in
order to prepare a widget for set up. So modify the signature of
sof_pcm_setup_connected_widgets() and sof_widget_list_setup() to accept
these as arguments.
Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Link: https://lore.kernel.org/r/20220426171743.171061-12-ranjani.sridharan@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 463a809b
......@@ -84,7 +84,8 @@ EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
static int
sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
struct snd_sof_pcm *spcm, int dir)
struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params, int dir)
{
struct snd_soc_dai *dai;
int ret, j;
......@@ -103,7 +104,7 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
spcm->stream[dir].list = list;
ret = sof_widget_list_setup(sdev, spcm, dir);
ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
spcm->pcm.pcm_id, dir);
......@@ -159,7 +160,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
substream->stream);
if (ret < 0)
return ret;
}
......
......@@ -257,6 +257,79 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
return 0;
}
static void
sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
goto sink_unprepare;
/* unprepare the source widget */
widget_ops[widget->id].ipc_unprepare(swidget);
swidget->prepared = false;
sink_unprepare:
/* unprepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
sof_unprepare_widgets_in_path(sdev, p->sink);
p->walking = false;
}
}
}
static int
sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
struct snd_pcm_hw_params *pipeline_params, int dir)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
int ret;
if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
goto sink_prepare;
/* prepare the source widget */
ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
pipeline_params, dir);
if (ret < 0) {
dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
return ret;
}
swidget->prepared = true;
sink_prepare:
/* prepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
platform_params, pipeline_params, dir);
p->walking = false;
if (ret < 0) {
/* unprepare the source widget */
if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
widget_ops[widget->id].ipc_unprepare(swidget);
swidget->prepared = false;
}
return ret;
}
}
}
return 0;
}
/*
* free all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback)
......@@ -336,11 +409,15 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
}
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)
sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params, int dir,
enum sof_widget_op op)
{
struct snd_soc_dapm_widget *widget;
int ret, i;
char *str;
int ret = 0;
int i;
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
......@@ -354,17 +431,38 @@ sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm
switch (op) {
case SOF_WIDGET_SETUP:
ret = sof_set_up_widgets_in_path(sdev, widget, dir);
str = "set up";
break;
case SOF_WIDGET_FREE:
ret = sof_free_widgets_in_path(sdev, widget, dir);
str = "free";
break;
case SOF_WIDGET_PREPARE:
{
struct snd_pcm_hw_params pipeline_params;
str = "prepare";
/*
* When walking the list of connected widgets, the pipeline_params for each
* widget is modified by the source widget in the path. Use a local
* copy of the runtime params as the pipeline_params so that the runtime
* params does not get overwritten.
*/
memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
platform_params, &pipeline_params, dir);
break;
}
case SOF_WIDGET_UNPREPARE:
sof_unprepare_widgets_in_path(sdev, widget);
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");
dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
return ret;
}
}
......@@ -372,7 +470,10 @@ sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm
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,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
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;
......@@ -383,10 +484,24 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
if (!list)
return 0;
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_SETUP);
/*
* Prepare widgets for set up. The prepare step is used to allocate memory, assign
* instance ID and pick the widget configuration based on the runtime PCM params.
*/
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_PREPARE);
if (ret < 0)
return ret;
/* Set up is used to send the IPC to the DSP to create the widget */
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
if (ret < 0) {
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_UNPREPARE);
return ret;
}
/*
* error in setting pipeline connections will result in route status being reset for
* routes that were successfully set up when the widgets are freed.
......@@ -426,7 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
return 0;
widget_free:
sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
SOF_WIDGET_FREE);
sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
return ret;
}
......@@ -440,7 +557,11 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
if (!list)
return 0;
ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE);
/* send IPC to free widget in the DSP */
ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
/* unprepare the widget */
sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
......
......@@ -36,8 +36,10 @@
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
enum sof_widget_op {
SOF_WIDGET_FREE,
SOF_WIDGET_PREPARE,
SOF_WIDGET_SETUP,
SOF_WIDGET_FREE,
SOF_WIDGET_UNPREPARE,
};
/*
......@@ -129,6 +131,8 @@ struct sof_ipc_tplg_control_ops {
* @token_list: List of token ID's that should be parsed for the widget
* @token_list_size: number of elements in token_list
* @bind_event: Function pointer for binding events to the widget
* @ipc_prepare: Optional op for preparing a widget for set up
* @ipc_unprepare: Optional op for unpreparing a widget
*/
struct sof_ipc_tplg_widget_ops {
int (*ipc_setup)(struct snd_sof_widget *swidget);
......@@ -137,6 +141,11 @@ struct sof_ipc_tplg_widget_ops {
int token_list_size;
int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
u16 event_type);
int (*ipc_prepare)(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
struct snd_pcm_hw_params *source_params, int dir);
void (*ipc_unprepare)(struct snd_sof_widget *swidget);
};
/**
......@@ -332,6 +341,11 @@ struct snd_sof_widget {
* widgets. It is unused for all other widget types.
*/
int complete;
/*
* the prepared flag is used to indicate that a widget has been prepared for getting set
* up in the DSP.
*/
bool prepared;
int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id; /* id is the DAPM widget type */
......@@ -491,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
struct snd_soc_dapm_widget *wsink);
/* PCM */
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,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
......
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