Skip to content

Commit 768141b

Browse files
committed
ASoC: SOF: ipc4: Add support for triggering pipelines in order
IPC4 requires pipelines to be triggered starting at the sink pipeline and walking all the way to source in order. Add a helper function to determine all the pipelines that need to triggered in the FE DAI trigger in order and trigger them all simultaenously using the new sof_ipc4_set_multi_pipeline_state() helper function. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1 parent ab2102f commit 768141b

File tree

3 files changed

+183
-11
lines changed

3 files changed

+183
-11
lines changed

include/sound/sof/ipc4/header.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state {
185185
#define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0)
186186
#define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
187187

188+
#define SOF_IPC4_GLB_PIPE_MULTI BIT(0)
189+
#define SOF_IPC4_GLB_PIPE_MULTI_SYNC_STOP BIT(1)
190+
188191
/* load library ipc msg */
189192
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16
190193
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)

sound/soc/sof/ipc4-pcm.c

Lines changed: 158 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,34 @@
1313
#include "ipc4-priv.h"
1414
#include "ipc4-topology.h"
1515

16+
static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
17+
struct sof_ipc4_multi_pipeline_info *info)
18+
{
19+
struct ipc4_pipeline_set_state_data data;
20+
struct sof_ipc4_msg msg = {{ 0 }};
21+
u32 primary, ipc_size;
22+
int i;
23+
24+
primary = state;
25+
primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
26+
primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
27+
primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
28+
29+
msg.primary = primary;
30+
31+
msg.extension = SOF_IPC4_GLB_PIPE_MULTI;
32+
33+
data.count = info->count;
34+
for (i = 0; i < info->count; i++)
35+
data.pipeline_ids[i] = info->pipelines[i]->instance_id;
36+
37+
ipc_size = sizeof(u32) * (info->count + 1);
38+
msg.data_size = ipc_size;
39+
msg.data_ptr = &data;
40+
41+
return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
42+
}
43+
1644
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
1745
{
1846
struct sof_ipc4_msg msg = {{ 0 }};
@@ -32,65 +60,184 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
3260
}
3361
EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
3462

63+
/* helper function to get the list of pipeline widgets that need to be triggered */
64+
static int
65+
sof_ipc4_get_pipelines_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
66+
struct snd_soc_dapm_widget_list *list,
67+
struct sof_ipc4_multi_pipeline_info *pipeline_info)
68+
{
69+
struct snd_sof_widget *swidget = widget->dobj.private;
70+
struct snd_sof_widget *pipeline_widget;
71+
struct sof_ipc4_pipeline *pipeline;
72+
struct snd_soc_dapm_path *p;
73+
uint32_t pipeline_count;
74+
int ret, i;
75+
76+
if (!swidget)
77+
goto src_add;
78+
79+
/* find pipeline widget for the pipeline that this widget belongs to */
80+
pipeline_widget = swidget->pipe_widget;
81+
pipeline = pipeline_widget->private;
82+
83+
/* do not add backend pipelines */
84+
if (pipeline->is_backend)
85+
goto src_add;
86+
87+
/* check if pipeline has already been added to the list */
88+
for (i = 0 ; i < pipeline_info->count; i++)
89+
if (pipeline_info->pipelines[i] == pipeline_widget)
90+
goto src_add;
91+
92+
pipeline_count = pipeline_info->count++;
93+
if (pipeline_info->count > SOF_IPC4_TRIGGER_MAX_PIPELINE_COUNT) {
94+
dev_err(sdev->dev, "Cannot trigger %d pipelines at once\n", pipeline_info->count);
95+
return -EINVAL;
96+
}
97+
98+
pipeline_info->pipelines[pipeline_count] = pipeline_widget;
99+
100+
src_add:
101+
/* add pipelines in the source paths */
102+
snd_soc_dapm_widget_for_each_source_path(widget, p) {
103+
if (!widget_in_list(list, p->source))
104+
continue;
105+
106+
if (!p->walking && p->source->dobj.private) {
107+
p->walking = true;
108+
ret = sof_ipc4_get_pipelines_in_order(sdev, p->source, list, pipeline_info);
109+
p->walking = false;
110+
if (ret < 0)
111+
return ret;
112+
}
113+
}
114+
115+
return 0;
116+
}
117+
35118
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
36119
struct snd_pcm_substream *substream, int state)
37120
{
38121
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
39122
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
123+
struct sof_ipc4_multi_pipeline_info pipeline_info;
40124
struct snd_sof_widget *pipeline_widget;
41125
struct snd_soc_dapm_widget_list *list;
42126
struct snd_soc_dapm_widget *widget;
43127
struct sof_ipc4_pipeline *pipeline;
44128
struct snd_sof_widget *swidget;
45129
struct snd_sof_pcm *spcm;
46130
int ret = 0;
47-
int num_widgets;
131+
int num_widgets, i;
48132

49133
spcm = snd_sof_find_spcm_dai(component, rtd);
50134
if (!spcm)
51135
return -EINVAL;
52136

53137
list = spcm->stream[substream->stream].list;
54138

139+
pipeline_info.count = 0;
140+
141+
/* get the list of pipeline widgets that need to be triggered */
55142
for_each_dapm_widgets(list, num_widgets, widget) {
56143
swidget = widget->dobj.private;
57144

58145
if (!swidget)
59146
continue;
60147

61-
/* find pipeline widget for the pipeline that this widget belongs to */
62-
pipeline_widget = swidget->pipe_widget;
63-
pipeline = pipeline_widget->private;
148+
/*
149+
* IPC4 requires pipelines to be triggered in order starting at the sink and
150+
* walking all the way to the source. So the starting widget for playback is of
151+
* DAI type and for capture, it is of AIF type.
152+
*/
153+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_DAI(widget->id))
154+
continue;
64155

65-
/* skip triggering backend pipelines */
66-
if (pipeline->is_backend || pipeline->state == state)
156+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_AIF(widget->id))
67157
continue;
68158

159+
ret = sof_ipc4_get_pipelines_in_order(sdev, widget, list, &pipeline_info);
160+
if (ret < 0)
161+
return ret;
162+
}
163+
164+
if (!pipeline_info.count)
165+
return ret;
166+
167+
/* trigger a single pipeline if count is 1 */
168+
if (pipeline_info.count == 1) {
169+
pipeline_widget = pipeline_info.pipelines[0];
170+
pipeline = pipeline_widget->private;
171+
69172
/* first set the pipeline to PAUSED state */
70173
if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
71174
ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id,
72175
SOF_IPC4_PIPE_PAUSED);
176+
73177
if (ret < 0) {
74178
dev_err(sdev->dev, "failed to pause pipeline %d\n",
75-
swidget->pipeline_id);
179+
pipeline_widget->pipeline_id);
76180
return ret;
77181
}
78182
}
79183

80184
pipeline->state = SOF_IPC4_PIPE_PAUSED;
81185

82186
if (pipeline->state == state)
83-
continue;
187+
return ret;
188+
84189

85190
/* then set the final state */
86191
ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state);
87192
if (ret < 0) {
88-
dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
89-
state, swidget->pipeline_id);
90-
break;
193+
dev_err(sdev->dev, "failed to set state %d pipeline %d\n",
194+
state, pipeline_widget->pipeline_id);
195+
return ret;
91196
}
92197

93198
pipeline->state = state;
199+
return ret;
200+
}
201+
202+
/*
203+
* check if the pipelines are paused already.
204+
* It's enough to check the first pipeline's state
205+
*/
206+
pipeline_widget = pipeline_info.pipelines[0];
207+
pipeline = pipeline_widget->private;
208+
if (pipeline->state == SOF_IPC4_PIPE_PAUSED)
209+
goto out;
210+
211+
/* pause all pipelines */
212+
ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, &pipeline_info);
213+
if (ret < 0) {
214+
dev_err(sdev->dev, "failed to pause all pipelines\n");
215+
return ret;
216+
}
217+
218+
for (i = 0; i < pipeline_info.count; i++) {
219+
pipeline_widget = pipeline_info.pipelines[i];
220+
pipeline = pipeline_widget->private;
221+
pipeline->state = SOF_IPC4_PIPE_PAUSED;
222+
}
223+
224+
out:
225+
/* check if the pipelines are in the final state already */
226+
pipeline_widget = pipeline_info.pipelines[0];
227+
pipeline = pipeline_widget->private;
228+
if (pipeline->state == state)
229+
return ret;
230+
231+
ret = sof_ipc4_set_multi_pipeline_state(sdev, state, &pipeline_info);
232+
if (ret < 0) {
233+
dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
234+
return ret;
235+
}
236+
237+
for (i = 0; i < pipeline_info.count; i++) {
238+
pipeline_widget = pipeline_info.pipelines[i];
239+
pipeline = pipeline_widget->private;
240+
pipeline->state = state;
94241
}
95242

96243
return ret;

sound/soc/sof/ipc4-topology.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
/* A magic number from FW */
6565
#define ALH_MULTI_GTW_COUNT 8
6666

67+
#define SOF_IPC4_TRIGGER_MAX_PIPELINE_COUNT 16
68+
6769
/**
6870
* struct sof_ipc4_pipeline - pipeline config data
6971
* @priority: Priority of this pipeline
@@ -82,6 +84,26 @@ struct sof_ipc4_pipeline {
8284
struct sof_ipc4_msg msg;
8385
};
8486

87+
/**
88+
* struct sof_ipc4_multi_pipeline_info - multi pipeline trigger IPC info
89+
* @pipelines: Array of pipeline widgets to be triggered
90+
* @count: number of pipelines to be triggered
91+
*/
92+
struct sof_ipc4_multi_pipeline_info {
93+
struct snd_sof_widget *pipelines[SOF_IPC4_TRIGGER_MAX_PIPELINE_COUNT];
94+
uint32_t count;
95+
};
96+
97+
/**
98+
* struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data
99+
* @count: Number of pipelines to be triggered
100+
* @pipeline_ids: Arrays of IDs of the pipelines to be triggered
101+
*/
102+
struct ipc4_pipeline_set_state_data {
103+
uint32_t count;
104+
uint32_t pipeline_ids[SOF_IPC4_TRIGGER_MAX_PIPELINE_COUNT];
105+
};
106+
85107
/**
86108
* struct sof_ipc4_available_audio_format - Available audio formats
87109
* @base_config: Available base config

0 commit comments

Comments
 (0)