Skip to content

Commit 9a7a5ce

Browse files
lyakhlgirdwood
authored andcommitted
pipeline: trigger START from the task context
When the firmware receives a START or RELEASE IPC message, it immediately triggers all involved components, which starts DMA. Then it schedules the pipeline task, but since the scheduler can be already running at that time, the task might be scheduled when DMA data isn't available yet or has already overflowed. To fix this change the control flow to also trigger all components from the task during its first run. Actual data processing then begins with the next period. Note, that this is currently only possible with pipelines, using timer-based scheduling. Pipelines, using DMA interrupts for scheduling are unaffected. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
1 parent 886b7ee commit 9a7a5ce

File tree

9 files changed

+228
-41
lines changed

9 files changed

+228
-41
lines changed

src/audio/pipeline/pipeline-graph.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t
127127
p->priority = priority;
128128
p->pipeline_id = pipeline_id;
129129
p->status = COMP_STATE_INIT;
130+
p->trigger.cmd = -EINVAL;
130131
ret = memcpy_s(&p->tctx, sizeof(struct tr_ctx), &pipe_tr,
131132
sizeof(struct tr_ctx));
132133
assert(!ret);

src/audio/pipeline/pipeline-schedule.c

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,44 @@ static enum task_state pipeline_task(void *arg)
5555
return SOF_TASK_STATE_COMPLETED;
5656
}
5757

58+
if (p->trigger.cmd >= 0) {
59+
/* First pipeline task run for either START or RELEASE */
60+
struct sof_ipc_reply reply = {
61+
.hdr.cmd = SOF_IPC_GLB_REPLY,
62+
.hdr.size = sizeof(reply),
63+
};
64+
65+
err = pipeline_trigger_run(p, p->trigger.host, p->trigger.cmd);
66+
p->trigger.cmd = -EINVAL;
67+
68+
if (err < 0) {
69+
pipe_err(p, "pipeline_task(): failed to trigger components: %d", err);
70+
reply.error = err;
71+
err = SOF_TASK_STATE_COMPLETED;
72+
} else if (err == PPL_STATUS_PATH_STOP) {
73+
pipe_warn(p, "pipeline_task(): stopping for xrun");
74+
err = SOF_TASK_STATE_COMPLETED;
75+
} else {
76+
p->status = COMP_STATE_ACTIVE;
77+
err = SOF_TASK_STATE_RESCHEDULE;
78+
}
79+
80+
ipc_msg_reply(&reply);
81+
82+
return err;
83+
}
84+
85+
/*
86+
* The first execution of the pipeline task above has triggered all
87+
* pipeline components. Subsequent iterations actually perform data
88+
* copying below.
89+
*/
5890
err = pipeline_copy(p);
5991
if (err < 0) {
6092
/* try to recover */
6193
err = pipeline_xrun_recover(p);
6294
if (err < 0) {
63-
pipe_err(p, "pipeline_task(): xrun recover failed! pipeline will be stopped!");
95+
pipe_err(p, "pipeline_task(): xrun recovery failed! pipeline is stopped.");
6496
/* failed - host will stop this pipeline */
6597
return SOF_TASK_STATE_COMPLETED;
6698
}
@@ -107,9 +139,11 @@ int pipeline_schedule_config(struct pipeline *p, uint32_t sched_id,
107139
return 0;
108140
}
109141

142+
/* trigger connected pipelines: either immediately or schedule them */
110143
void pipeline_schedule_triggered(struct pipeline_walk_context *ctx,
111144
int cmd)
112145
{
146+
struct pipeline_data *ppl_data = ctx->comp_data;
113147
struct list_item *tlist;
114148
struct pipeline *p;
115149
uint32_t flags;
@@ -121,25 +155,31 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx,
121155
*/
122156
irq_local_disable(flags);
123157

124-
list_for_item(tlist, &ctx->pipelines) {
125-
p = container_of(tlist, struct pipeline, list);
126-
127-
switch (cmd) {
128-
case COMP_TRIGGER_PAUSE:
129-
case COMP_TRIGGER_STOP:
158+
switch (cmd) {
159+
case COMP_TRIGGER_PAUSE:
160+
case COMP_TRIGGER_STOP:
161+
list_for_item(tlist, &ctx->pipelines) {
162+
p = container_of(tlist, struct pipeline, list);
130163
pipeline_schedule_cancel(p);
131164
p->status = COMP_STATE_PAUSED;
132-
break;
133-
case COMP_TRIGGER_RELEASE:
134-
case COMP_TRIGGER_START:
165+
}
166+
break;
167+
case COMP_TRIGGER_RELEASE:
168+
case COMP_TRIGGER_START:
169+
list_for_item(tlist, &ctx->pipelines) {
170+
p = container_of(tlist, struct pipeline, list);
171+
if (pipeline_is_timer_driven(p)) {
172+
/* Use the first of connected pipelines to trigger */
173+
if (cmd >= 0) {
174+
p->trigger.cmd = cmd;
175+
p->trigger.host = ppl_data->start;
176+
cmd = -EINVAL;
177+
}
178+
} else {
179+
p->xrun_bytes = 0;
180+
p->status = COMP_STATE_ACTIVE;
181+
}
135182
pipeline_schedule_copy(p, 0);
136-
p->xrun_bytes = 0;
137-
p->status = COMP_STATE_ACTIVE;
138-
break;
139-
case COMP_TRIGGER_SUSPEND:
140-
case COMP_TRIGGER_RESUME:
141-
default:
142-
break;
143183
}
144184
}
145185

src/audio/pipeline/pipeline-stream.c

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,40 @@ pipeline_should_report_enodata_on_trigger(struct comp_dev *rsrc,
5959
return false;
6060
}
6161

62+
/* Runs in IPC or in pipeline task context */
6263
static int pipeline_comp_trigger(struct comp_dev *current,
6364
struct comp_buffer *calling_buf,
6465
struct pipeline_walk_context *ctx, int dir)
6566
{
6667
struct pipeline_data *ppl_data = ctx->comp_data;
6768
bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start);
68-
bool is_same_sched =
69-
pipeline_is_same_sched_comp(current->pipeline,
70-
ppl_data->start->pipeline);
69+
bool is_same_sched, async;
7170
int err;
7271

7372
pipe_dbg(current->pipeline,
7473
"pipeline_comp_trigger(), current->comp.id = %u, dir = %u",
7574
dev_comp_id(current), dir);
7675

76+
switch (ppl_data->cmd) {
77+
case COMP_TRIGGER_PAUSE:
78+
case COMP_TRIGGER_STOP:
79+
/*
80+
* PAUSE and STOP are triggered in IPC context, not from the
81+
* pipeline task
82+
*/
83+
async = true;
84+
break;
85+
case COMP_TRIGGER_RELEASE:
86+
case COMP_TRIGGER_START:
87+
async = !pipeline_is_timer_driven(current->pipeline);
88+
break;
89+
default:
90+
return -EINVAL;
91+
}
92+
93+
is_same_sched = pipeline_is_same_sched_comp(current->pipeline,
94+
ppl_data->start->pipeline);
95+
7796
/* trigger should propagate to the connected pipelines,
7897
* which need to be scheduled together
7998
*/
@@ -92,7 +111,12 @@ static int pipeline_comp_trigger(struct comp_dev *current,
92111
if (err < 0 || err == PPL_STATUS_PATH_STOP)
93112
return err;
94113

95-
pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx);
114+
/*
115+
* Add scheduling components to the list. This is only needed for the
116+
* stopping flow.
117+
*/
118+
if (async)
119+
pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx);
96120

97121
return pipeline_for_each_comp(current, ctx, dir);
98122
}
@@ -172,18 +196,95 @@ int pipeline_copy(struct pipeline *p)
172196
return ret;
173197
}
174198

175-
/* trigger pipeline */
199+
/* only collect scheduling components */
200+
static int pipeline_comp_list(struct comp_dev *current,
201+
struct comp_buffer *calling_buf,
202+
struct pipeline_walk_context *ctx, int dir)
203+
{
204+
struct pipeline_data *ppl_data = ctx->comp_data;
205+
bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start);
206+
bool is_same_sched = pipeline_is_same_sched_comp(current->pipeline,
207+
ppl_data->start->pipeline);
208+
209+
if (!is_single_ppl && !is_same_sched) {
210+
pipe_dbg(current->pipeline,
211+
"pipeline_comp_list(), current is from another pipeline");
212+
return 0;
213+
}
214+
215+
/* Add scheduling components to the list */
216+
pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx);
217+
218+
return pipeline_for_each_comp(current, ctx, dir);
219+
}
220+
221+
/* build a list of connected pipelines' scheduling components and trigger them */
222+
static int pipeline_trigger_list(struct pipeline *p, struct comp_dev *host, int cmd)
223+
{
224+
struct pipeline_data data = {
225+
.start = host,
226+
.cmd = cmd,
227+
};
228+
struct pipeline_walk_context walk_ctx = {
229+
.comp_func = pipeline_comp_list,
230+
.comp_data = &data,
231+
.skip_incomplete = true,
232+
};
233+
int ret;
234+
235+
list_init(&walk_ctx.pipelines);
236+
237+
ret = walk_ctx.comp_func(host, NULL, &walk_ctx, host->direction);
238+
if (ret < 0)
239+
pipe_err(p, "pipeline_trigger_list(): ret = %d, host->comp.id = %u, cmd = %d",
240+
ret, dev_comp_id(host), cmd);
241+
else
242+
pipeline_schedule_triggered(&walk_ctx, cmd);
243+
244+
return ret;
245+
}
246+
247+
/* trigger pipeline in IPC context */
176248
int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd)
177249
{
178-
struct pipeline_data data;
250+
int ret;
251+
252+
pipe_info(p, "pipe trigger cmd %d", cmd);
253+
254+
switch (cmd) {
255+
case COMP_TRIGGER_PAUSE:
256+
case COMP_TRIGGER_STOP:
257+
/* Execute immediately */
258+
ret = pipeline_trigger_run(p, host, cmd);
259+
return ret == PPL_STATUS_PATH_STOP ? 0 : ret;
260+
case COMP_TRIGGER_RELEASE:
261+
case COMP_TRIGGER_START:
262+
/* Add all connected pipelines to the list and schedule them all */
263+
ret = pipeline_trigger_list(p, host, cmd);
264+
if (ret < 0)
265+
return ret;
266+
/* IPC response will be sent from the task */
267+
return 1;
268+
}
269+
270+
return 0;
271+
}
272+
273+
/* actually execute pipeline trigger, including components: either in IPC or in task context */
274+
int pipeline_trigger_run(struct pipeline *p, struct comp_dev *host, int cmd)
275+
{
276+
struct pipeline_data data = {
277+
.start = host,
278+
.cmd = cmd,
279+
};
179280
struct pipeline_walk_context walk_ctx = {
180281
.comp_func = pipeline_comp_trigger,
181282
.comp_data = &data,
182283
.skip_incomplete = true,
183284
};
184285
int ret;
185286

186-
pipe_info(p, "pipe trigger cmd %d", cmd);
287+
pipe_dbg(p, "execute trigger cmd %d on pipe %u", cmd, p->pipeline_id);
187288

188289
list_init(&walk_ctx.pipelines);
189290

@@ -193,20 +294,23 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd)
193294
if (ret < 0) {
194295
pipe_err(p, "xrun handle: ret = %d", ret);
195296
return ret;
196-
} else if (ret == PPL_STATUS_PATH_STOP)
297+
}
298+
299+
if (ret == PPL_STATUS_PATH_STOP)
197300
/* no further action needed*/
198-
return 0;
301+
return pipeline_is_timer_driven(p);
199302
}
200303

201-
data.start = host;
202-
data.cmd = cmd;
203-
204304
ret = walk_ctx.comp_func(host, NULL, &walk_ctx, host->direction);
205-
if (ret < 0) {
206-
pipe_err(p, "pipeline_trigger(): ret = %d, host->comp.id = %u, cmd = %d",
305+
if (ret < 0)
306+
pipe_err(p, "pipeline_trigger_run(): ret = %d, host->comp.id = %u, cmd = %d",
207307
ret, dev_comp_id(host), cmd);
208-
}
209308

309+
/*
310+
* When called from the pipeline task, pipeline_comp_trigger() will not
311+
* add pipelines to the list, so pipeline_schedule_triggered() will have
312+
* no effect.
313+
*/
210314
pipeline_schedule_triggered(&walk_ctx, cmd);
211315

212316
return ret;

src/audio/pipeline/pipeline-xrun.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev,
153153
return;
154154

155155
/* notify all pipeline comps we are in XRUN, and stop copying */
156-
ret = pipeline_trigger(p, p->source_comp, COMP_TRIGGER_XRUN);
156+
ret = pipeline_trigger_run(p, p->source_comp, COMP_TRIGGER_XRUN);
157157
if (ret < 0)
158158
pipe_err(p, "pipeline_xrun(): Pipelines notification about XRUN failed, ret = %d",
159159
ret);

src/drivers/intel/cavs/ipc.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,13 @@ enum task_state ipc_platform_do_cmd(void *data)
201201
void ipc_platform_complete_cmd(void *data)
202202
{
203203
struct ipc *ipc = data;
204+
uint32_t flags;
204205

205-
if (!cpu_is_me(ipc->core))
206+
if (!cpu_is_me(ipc->core) || ipc->delayed_response)
206207
return;
207208

209+
spin_lock_irq(&ipc->lock, flags);
210+
208211
/* write 1 to clear busy, and trigger interrupt to host*/
209212
#if CAVS_VERSION == CAVS_VERSION_1_5
210213
ipc_write(IPC_DIPCT, ipc_read(IPC_DIPCT) | IPC_DIPCT_BUSY);
@@ -229,6 +232,8 @@ void ipc_platform_complete_cmd(void *data)
229232
}
230233

231234
#endif
235+
236+
spin_unlock_irq(&ipc->lock, flags);
232237
}
233238

234239
int ipc_platform_send_msg(struct ipc_msg *msg)

src/include/sof/audio/pipeline.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ struct pipeline {
7373
/* position update */
7474
uint32_t posn_offset; /* position update array offset*/
7575
struct ipc_msg *msg;
76+
struct {
77+
int cmd;
78+
struct comp_dev *host;
79+
} trigger;
7680
};
7781

7882
struct pipeline_walk_context {
@@ -218,14 +222,22 @@ int pipeline_prepare(struct pipeline *p, struct comp_dev *cd);
218222
*/
219223

220224
/**
221-
* \brief Trigger pipeline - atomic
225+
* \brief Trigger pipeline - IPC context
222226
* \param[in] p pipeline.
223-
* \param[in] host_cd Host DMA component.
227+
* \param[in] host Host DMA component.
224228
* \param[in] cmd Pipeline trigger command.
225229
* \return 0 on success.
226230
*/
227-
/* */
228-
int pipeline_trigger(struct pipeline *p, struct comp_dev *host_cd, int cmd);
231+
int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd);
232+
233+
/**
234+
* \brief Trigger pipeline - either IPC or pipeline task context
235+
* \param[in] p pipeline.
236+
* \param[in] host Host DMA component.
237+
* \param[in] cmd Pipeline trigger command.
238+
* \return 0 on success.
239+
*/
240+
int pipeline_trigger_run(struct pipeline *p, struct comp_dev *host, int cmd);
229241

230242
/**
231243
* \brief Copy data along a pipeline.

src/include/sof/ipc/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct ipc {
6868

6969
struct list_item msg_list; /* queue of messages to be sent */
7070
bool is_notification_pending; /* notification is being sent to host */
71+
bool delayed_response; /* response will be sent from a different context */
7172
unsigned int core; /* core, processing the IPC */
7273

7374
struct list_item comp_list; /* list of component devices */
@@ -196,4 +197,10 @@ void ipc_cmd(ipc_cmd_hdr *hdr);
196197
*/
197198
int ipc_process_on_core(uint32_t core, bool blocking);
198199

200+
/**
201+
* \brief reply to an IPC message.
202+
* @param[in] reply pointer to the reply structure.
203+
*/
204+
void ipc_msg_reply(struct sof_ipc_reply *reply);
205+
199206
#endif /* __SOF_DRIVERS_IPC_H__ */

0 commit comments

Comments
 (0)