@@ -51,6 +51,7 @@
#define SOF_TKN_SCHED_CORE 203
#define SOF_TKN_SCHED_FRAMES 204
#define SOF_TKN_SCHED_TIME_DOMAIN 205
+#define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206
/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
@@ -81,6 +81,8 @@ struct snd_sof_control {
bool comp_data_dirty;
};
+struct snd_sof_widget;
+
/* ASoC SOF DAPM widget */
struct snd_sof_widget {
struct snd_soc_component *scomp;
@@ -90,8 +92,19 @@ struct snd_sof_widget {
int core;
int id;
+ /*
+ * Flag indicating if the widget should be set up dynamically when a PCM is opened.
+ * This flag is only set for the scheduler type widget in topology. During topology
+ * loading, this flag is propagated to all the widgets belonging to the same pipeline.
+ * When this flag is not set, a widget is set up at the time of topology loading
+ * and retained until the DSP enters D3. It will need to be set up again when resuming
+ * from D3.
+ */
+ bool dynamic_pipeline_widget;
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
+ struct snd_sof_widget *pipe_widget;
/* extended data for UUID components */
struct sof_ipc_comp_ext comp_ext;
@@ -572,6 +572,12 @@ static const struct sof_topology_token sched_tokens[] = {
offsetof(struct sof_ipc_pipe_new, time_domain), 0},
};
+static const struct sof_topology_token pipeline_tokens[] = {
+ {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0},
+
+};
+
/* volume */
static const struct sof_topology_token volume_tokens[] = {
{SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
@@ -1765,6 +1771,15 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
goto err;
}
+ ret = sof_parse_tokens(scomp, swidget, pipeline_tokens,
+ ARRAY_SIZE(pipeline_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n",
+ private->size);
+ goto err;
+ }
+
dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n",
swidget->widget->name, pipeline->period, pipeline->priority,
pipeline->period_mips, pipeline->core, pipeline->frames_per_sched);
@@ -3567,11 +3582,45 @@ int snd_sof_complete_pipeline(struct device *dev,
return 1;
}
+/**
+ * sof_set_pipe_widget - Set pipe_widget for a component
+ * @sdev: pointer to struct snd_sof_dev
+ * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
+ *
+ * Return: 0 if successful, -EINVAL on error.
+ * The function checks if @swidget is associated with any volatile controls. If so, setting
+ * the dynamic_pipeline_widget is disallowed.
+ */
+static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
+ struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+
+ if (pipe_widget->dynamic_pipeline_widget) {
+ /* dynamic widgets cannot have volatile kcontrols */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id &&
+ (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) {
+ dev_err(sdev->dev,
+ "error: volatile control found for dynamic widget %s\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+ }
+
+ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
+ swidget->pipe_widget = pipe_widget;
+ swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
+
+ return 0;
+}
+
/* completion - called at completion of firmware loading */
static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_widget *swidget;
+ struct snd_sof_widget *swidget, *comp_swidget;
int ret;
/* some widget types require completion notificattion */
@@ -3586,6 +3635,17 @@ static int sof_complete(struct snd_soc_component *scomp)
return ret;
swidget->complete = ret;
+
+ /*
+ * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+ * for all widgets that have the same pipeline ID as the scheduler widget
+ */
+ list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list)
+ if (comp_swidget->pipeline_id == swidget->pipeline_id) {
+ ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
+ if (ret < 0)
+ return ret;
+ }
break;
default:
break;