From 2e81f9c3b46c5213239b6d181b85e254ce8f3642 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 27 Jul 2021 22:10:07 +0200 Subject: [PATCH 1/4] pipeline: move STOP / PAUSE trigger to the pipeline task A recent commit moved START and RESUME trigger processing to the pipeline task for timer domain pipelines. This patch does the same for STOP and PAUSE. Signed-off-by: Guennadi Liakhovetski --- src/audio/pipeline/pipeline-schedule.c | 102 +++++++++++++++++-------- src/audio/pipeline/pipeline-stream.c | 53 +++++++------ src/include/sof/audio/pipeline.h | 1 + 3 files changed, 104 insertions(+), 52 deletions(-) diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index 16df23e7edb6..da7a8db8e82f 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -35,14 +35,17 @@ static void pipeline_schedule_cancel(struct pipeline *p) /* enable system agent panic, when there are no longer * DMA driven pipelines */ - if (!pipeline_is_timer_driven(p)) - sa_set_panic_on_delay(true); + sa_set_panic_on_delay(true); } static enum task_state pipeline_task(void *arg) { + struct sof_ipc_reply reply = { + .hdr.cmd = SOF_IPC_GLB_REPLY, + .hdr.size = sizeof(reply), + }; struct pipeline *p = arg; - int err; + int err, cmd = p->trigger.cmd; pipe_dbg(p, "pipeline_task()"); @@ -79,30 +82,48 @@ static enum task_state pipeline_task(void *arg) return SOF_TASK_STATE_RESCHEDULE; } - if (p->trigger.cmd >= 0) { - /* First pipeline task run for either START or RELEASE: PRE stage */ - struct sof_ipc_reply reply = { - .hdr.cmd = SOF_IPC_GLB_REPLY, - .hdr.size = sizeof(reply), - }; - int cmd = p->trigger.cmd; + if (cmd >= 0) { + if (!p->trigger.host) { + p->trigger.cmd = -EINVAL; + return p->status == COMP_STATE_PAUSED ? SOF_TASK_STATE_COMPLETED : + SOF_TASK_STATE_RESCHEDULE; + } + /* First pipeline task run for either START or RELEASE: PRE stage */ err = pipeline_trigger_run(p, p->trigger.host, cmd); if (err < 0) { pipe_err(p, "pipeline_task(): failed to trigger components: %d", err); reply.error = err; err = SOF_TASK_STATE_COMPLETED; - } else if (err == PPL_STATUS_PATH_STOP) { - pipe_warn(p, "pipeline_task(): stopping for xrun"); - err = SOF_TASK_STATE_COMPLETED; - } else if (p->trigger.cmd != cmd) { - /* PRE stage completed */ - if (p->trigger.delay) - return SOF_TASK_STATE_RESCHEDULE; - /* No delay: the final stage has already run too */ } else { - p->status = COMP_STATE_ACTIVE; - err = SOF_TASK_STATE_RESCHEDULE; + switch (cmd) { + case COMP_TRIGGER_START: + case COMP_TRIGGER_RELEASE: + p->status = COMP_STATE_ACTIVE; + break; + case COMP_TRIGGER_PRE_START: + case COMP_TRIGGER_PRE_RELEASE: + p->status = COMP_STATE_PRE_ACTIVE; + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + p->status = COMP_STATE_PAUSED; + } + + if (err == PPL_STATUS_PATH_STOP) { + pipe_warn(p, "pipeline_task(): stopping for xrun"); + err = SOF_TASK_STATE_COMPLETED; + } else if (p->trigger.cmd != cmd) { + /* PRE stage completed */ + if (p->trigger.delay) + return SOF_TASK_STATE_RESCHEDULE; + /* No delay: the final stage has already run too */ + } else if (p->status == COMP_STATE_PAUSED && !p->trigger.aborted) { + err = SOF_TASK_STATE_COMPLETED; + } else { + p->status = COMP_STATE_ACTIVE; + err = SOF_TASK_STATE_RESCHEDULE; + } } p->trigger.cmd = -EINVAL; @@ -112,6 +133,13 @@ static enum task_state pipeline_task(void *arg) return err; } + if (p->status == COMP_STATE_PAUSED) + /* + * One of pipelines, being stopped, but not the one, that + * triggers all components + */ + return SOF_TASK_STATE_COMPLETED; + /* * The first execution of the pipeline task above has triggered all * pipeline components. Subsequent iterations actually perform data @@ -190,23 +218,37 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, case COMP_TRIGGER_STOP: list_for_item(tlist, &ctx->pipelines) { p = container_of(tlist, struct pipeline, list); - pipeline_schedule_cancel(p); - p->status = COMP_STATE_PAUSED; + if (pipeline_is_timer_driven(p) && + p->status != COMP_STATE_PAUSED) { + /* + * Paused pipelines have their tasks stopped + * already, use a running pipeline to trigger + * components. + */ + p->trigger.cmd = cmd; + p->trigger.host = ppl_data->start; + ppl_data->start = NULL; + } else { + pipeline_schedule_cancel(p); + p->status = COMP_STATE_PAUSED; + } } break; case COMP_TRIGGER_PRE_RELEASE: case COMP_TRIGGER_PRE_START: list_for_item(tlist, &ctx->pipelines) { p = container_of(tlist, struct pipeline, list); - if (pipeline_is_timer_driven(p)) { - /* Use the first of connected pipelines to trigger */ - if (first_pipe) { - p->trigger.cmd = cmd; - p->trigger.host = ppl_data->start; - first_pipe = false; - } + p->xrun_bytes = 0; + if (pipeline_is_timer_driven(p) && first_pipe) { + /* + * Use the first of connected pipelines to + * trigger, mark all other connected pipelines + * active immediately. + */ + p->trigger.cmd = cmd; + p->trigger.host = ppl_data->start; + first_pipe = false; } else { - p->xrun_bytes = 0; p->status = COMP_STATE_ACTIVE; } pipeline_schedule_copy(p, 0); diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index 1f4ba3966c07..7a132eb97cb4 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -68,7 +68,7 @@ static int pipeline_comp_trigger(struct comp_dev *current, { struct pipeline_data *ppl_data = ctx->comp_data; bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start); - bool is_same_sched, async; + bool is_same_sched; int err; pipe_dbg(current->pipeline, @@ -76,14 +76,6 @@ static int pipeline_comp_trigger(struct comp_dev *current, dev_comp_id(current), dir); switch (ppl_data->cmd) { - case COMP_TRIGGER_PAUSE: - case COMP_TRIGGER_STOP: - /* - * PAUSE and STOP are triggered in IPC context, not from the - * pipeline task - */ - async = true; - break; case COMP_TRIGGER_PRE_RELEASE: case COMP_TRIGGER_PRE_START: if (comp_get_endpoint_type(current) == COMP_ENDPOINT_DAI) { @@ -91,14 +83,14 @@ static int pipeline_comp_trigger(struct comp_dev *current, ppl_data->delay_ms = dai_get_init_delay_ms(dd->dai); } - - COMPILER_FALLTHROUGH; - case COMP_TRIGGER_RELEASE: - case COMP_TRIGGER_START: - async = !pipeline_is_timer_driven(current->pipeline); break; default: return -EINVAL; + case COMP_TRIGGER_PAUSE: + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_RELEASE: + case COMP_TRIGGER_START: + break; } is_same_sched = pipeline_is_same_sched_comp(current->pipeline, @@ -119,14 +111,22 @@ static int pipeline_comp_trigger(struct comp_dev *current, /* send command to the component and update pipeline state */ err = comp_trigger(current, ppl_data->cmd); + current->pipeline->trigger.aborted = err == PPL_STATUS_PATH_STOP; if (err < 0 || err == PPL_STATUS_PATH_STOP) return err; + switch (ppl_data->cmd) { + case COMP_TRIGGER_PAUSE: + case COMP_TRIGGER_STOP: + if (pipeline_is_timer_driven(current->pipeline)) + current->pipeline->status = COMP_STATE_PAUSED; + } + /* - * Add scheduling components to the list. This is only needed for the - * stopping flow. + * Add scheduling components to the list. This is only needed for + * asynchronous flows. */ - if (async) + if (!pipeline_is_timer_driven(current->pipeline)) pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx); return pipeline_for_each_comp(current, ctx, dir); @@ -309,14 +309,22 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) pipe_info(p, "pipe trigger cmd %d", cmd); + p->trigger.aborted = false; + switch (cmd) { case COMP_TRIGGER_PAUSE: case COMP_TRIGGER_STOP: - /* Execute immediately */ - ret = pipeline_trigger_run(p, host, cmd); - return ret == PPL_STATUS_PATH_STOP ? 0 : ret; + if (p->status == COMP_STATE_PAUSED || p->xrun_bytes) { + /* The task isn't running, trigger inline */ + ret = pipeline_trigger_run(p, host, cmd); + return ret < 0 ? ret : 0; + } + + COMPILER_FALLTHROUGH; case COMP_TRIGGER_XRUN: - pipeline_trigger_xrun(p, &host); + if (cmd == COMP_TRIGGER_XRUN) + pipeline_trigger_xrun(p, &host); + COMPILER_FALLTHROUGH; case COMP_TRIGGER_PRE_RELEASE: case COMP_TRIGGER_PRE_START: @@ -324,7 +332,7 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) ret = pipeline_trigger_list(p, host, cmd); if (ret < 0) return ret; - /* IPC response will be sent from the task */ + /* IPC response will be sent from the task, unless it was paused */ return 1; } @@ -348,6 +356,7 @@ int pipeline_trigger_run(struct pipeline *p, struct comp_dev *host, int cmd) pipe_dbg(p, "execute trigger cmd %d on pipe %u", cmd, p->pipeline_id); list_init(&walk_ctx.pipelines); + p->trigger.aborted = false; /* handle pipeline global checks before going into each components */ if (p->xrun_bytes) { diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index a11519ba1645..c19f6c0a69d1 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -77,6 +77,7 @@ struct pipeline { int cmd; struct comp_dev *host; unsigned int delay; /* period count */ + bool aborted; } trigger; }; From 0252a787a20442c0969420c3877fd9f65c4a400a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 7 Sep 2021 15:27:57 +0200 Subject: [PATCH 2/4] pipeline: (cosmetic) move a block of code to a function Improve readability by moving a block of code into a function. Signed-off-by: Guennadi Liakhovetski --- src/audio/pipeline/pipeline-schedule.c | 109 +++++++++++++------------ 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index da7a8db8e82f..f774e2d8539d 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -38,6 +38,60 @@ static void pipeline_schedule_cancel(struct pipeline *p) sa_set_panic_on_delay(true); } +static enum task_state pipeline_task_cmd(struct pipeline *p, + struct sof_ipc_reply *reply) +{ + int err, cmd = p->trigger.cmd; + + if (!p->trigger.host) { + p->trigger.cmd = -EINVAL; + return p->status == COMP_STATE_PAUSED ? SOF_TASK_STATE_COMPLETED : + SOF_TASK_STATE_RESCHEDULE; + } + + err = pipeline_trigger_run(p, p->trigger.host, cmd); + if (err < 0) { + pipe_err(p, "pipeline_task(): failed to trigger components: %d", err); + reply->error = err; + err = SOF_TASK_STATE_COMPLETED; + } else { + switch (cmd) { + case COMP_TRIGGER_START: + case COMP_TRIGGER_RELEASE: + p->status = COMP_STATE_ACTIVE; + break; + case COMP_TRIGGER_PRE_START: + case COMP_TRIGGER_PRE_RELEASE: + p->status = COMP_STATE_PRE_ACTIVE; + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + p->status = COMP_STATE_PAUSED; + } + + if (err == PPL_STATUS_PATH_STOP) { + pipe_warn(p, "pipeline_task(): stopping for xrun"); + err = SOF_TASK_STATE_COMPLETED; + } else if (p->trigger.cmd != cmd) { + /* PRE stage completed */ + if (p->trigger.delay) + return SOF_TASK_STATE_RESCHEDULE; + /* No delay: the final stage has already run too */ + } else if (p->status == COMP_STATE_PAUSED && !p->trigger.aborted) { + err = SOF_TASK_STATE_COMPLETED; + } else { + p->status = COMP_STATE_ACTIVE; + err = SOF_TASK_STATE_RESCHEDULE; + } + } + + p->trigger.cmd = -EINVAL; + + ipc_msg_reply(reply); + + return err; +} + static enum task_state pipeline_task(void *arg) { struct sof_ipc_reply reply = { @@ -45,7 +99,7 @@ static enum task_state pipeline_task(void *arg) .hdr.size = sizeof(reply), }; struct pipeline *p = arg; - int err, cmd = p->trigger.cmd; + int err; pipe_dbg(p, "pipeline_task()"); @@ -82,56 +136,9 @@ static enum task_state pipeline_task(void *arg) return SOF_TASK_STATE_RESCHEDULE; } - if (cmd >= 0) { - if (!p->trigger.host) { - p->trigger.cmd = -EINVAL; - return p->status == COMP_STATE_PAUSED ? SOF_TASK_STATE_COMPLETED : - SOF_TASK_STATE_RESCHEDULE; - } - - /* First pipeline task run for either START or RELEASE: PRE stage */ - err = pipeline_trigger_run(p, p->trigger.host, cmd); - if (err < 0) { - pipe_err(p, "pipeline_task(): failed to trigger components: %d", err); - reply.error = err; - err = SOF_TASK_STATE_COMPLETED; - } else { - switch (cmd) { - case COMP_TRIGGER_START: - case COMP_TRIGGER_RELEASE: - p->status = COMP_STATE_ACTIVE; - break; - case COMP_TRIGGER_PRE_START: - case COMP_TRIGGER_PRE_RELEASE: - p->status = COMP_STATE_PRE_ACTIVE; - break; - case COMP_TRIGGER_STOP: - case COMP_TRIGGER_PAUSE: - p->status = COMP_STATE_PAUSED; - } - - if (err == PPL_STATUS_PATH_STOP) { - pipe_warn(p, "pipeline_task(): stopping for xrun"); - err = SOF_TASK_STATE_COMPLETED; - } else if (p->trigger.cmd != cmd) { - /* PRE stage completed */ - if (p->trigger.delay) - return SOF_TASK_STATE_RESCHEDULE; - /* No delay: the final stage has already run too */ - } else if (p->status == COMP_STATE_PAUSED && !p->trigger.aborted) { - err = SOF_TASK_STATE_COMPLETED; - } else { - p->status = COMP_STATE_ACTIVE; - err = SOF_TASK_STATE_RESCHEDULE; - } - } - - p->trigger.cmd = -EINVAL; - - ipc_msg_reply(&reply); - - return err; - } + if (p->trigger.cmd >= 0) + /* Process an offloaded command */ + return pipeline_task_cmd(p, &reply); if (p->status == COMP_STATE_PAUSED) /* From ff3b75f97e85f6b1022c40d600347c1a7d5294a3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 7 Sep 2021 15:29:16 +0200 Subject: [PATCH 3/4] pipeline: (cosmetic) fix function names in trace text Fix trace messages after moving them into different functions. Signed-off-by: Guennadi Liakhovetski --- src/audio/pipeline/pipeline-schedule.c | 4 ++-- src/audio/pipeline/pipeline-stream.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index f774e2d8539d..c5fb484bd080 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -51,7 +51,7 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, err = pipeline_trigger_run(p, p->trigger.host, cmd); if (err < 0) { - pipe_err(p, "pipeline_task(): failed to trigger components: %d", err); + pipe_err(p, "pipeline_task_cmd(): failed to trigger components: %d", err); reply->error = err; err = SOF_TASK_STATE_COMPLETED; } else { @@ -70,7 +70,7 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, } if (err == PPL_STATUS_PATH_STOP) { - pipe_warn(p, "pipeline_task(): stopping for xrun"); + pipe_warn(p, "pipeline_task_cmd(): stopping for xrun"); err = SOF_TASK_STATE_COMPLETED; } else if (p->trigger.cmd != cmd) { /* PRE stage completed */ diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index 7a132eb97cb4..c7076dc66c57 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -403,7 +403,7 @@ int pipeline_trigger_run(struct pipeline *p, struct comp_dev *host, int cmd) ret = walk_ctx.comp_func(host, NULL, &walk_ctx, host->direction); if (ret < 0) - pipe_err(p, "pipeline_trigger(): ret = %d, host->comp.id = %u, cmd = %d", + pipe_err(p, "pipeline_trigger_run(): ret = %d, host->comp.id = %u, cmd = %d", ret, dev_comp_id(host), cmd); else if (ret == PPL_STATUS_PATH_STOP) ret = 0; From 554e6a9d305510adc6a43486f3f67985c7f4bb28 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 20 Sep 2021 17:22:54 +0200 Subject: [PATCH 4/4] pipeline: (cosmetic) move a function Move a static function closer to where it is used. Signed-off-by: Guennadi Liakhovetski --- src/audio/pipeline/pipeline-stream.c | 142 +++++++++++++-------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index c7076dc66c57..baa67b958d87 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -61,77 +61,6 @@ pipeline_should_report_enodata_on_trigger(struct comp_dev *rsrc, return false; } -/* Runs in IPC or in pipeline task context */ -static int pipeline_comp_trigger(struct comp_dev *current, - struct comp_buffer *calling_buf, - struct pipeline_walk_context *ctx, int dir) -{ - struct pipeline_data *ppl_data = ctx->comp_data; - bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start); - bool is_same_sched; - int err; - - pipe_dbg(current->pipeline, - "pipeline_comp_trigger(), current->comp.id = %u, dir = %u", - dev_comp_id(current), dir); - - switch (ppl_data->cmd) { - case COMP_TRIGGER_PRE_RELEASE: - case COMP_TRIGGER_PRE_START: - if (comp_get_endpoint_type(current) == COMP_ENDPOINT_DAI) { - struct dai_data *dd = comp_get_drvdata(current); - - ppl_data->delay_ms = dai_get_init_delay_ms(dd->dai); - } - break; - default: - return -EINVAL; - case COMP_TRIGGER_PAUSE: - case COMP_TRIGGER_STOP: - case COMP_TRIGGER_RELEASE: - case COMP_TRIGGER_START: - break; - } - - is_same_sched = pipeline_is_same_sched_comp(current->pipeline, - ppl_data->start->pipeline); - - /* trigger should propagate to the connected pipelines, - * which need to be scheduled together - */ - if (!is_single_ppl && !is_same_sched) { - pipe_dbg(current->pipeline, - "pipeline_comp_trigger(), current is from another pipeline"); - - if (pipeline_should_report_enodata_on_trigger(current, ctx, dir)) - return -ENODATA; - - return 0; - } - - /* send command to the component and update pipeline state */ - err = comp_trigger(current, ppl_data->cmd); - current->pipeline->trigger.aborted = err == PPL_STATUS_PATH_STOP; - if (err < 0 || err == PPL_STATUS_PATH_STOP) - return err; - - switch (ppl_data->cmd) { - case COMP_TRIGGER_PAUSE: - case COMP_TRIGGER_STOP: - if (pipeline_is_timer_driven(current->pipeline)) - current->pipeline->status = COMP_STATE_PAUSED; - } - - /* - * Add scheduling components to the list. This is only needed for - * asynchronous flows. - */ - if (!pipeline_is_timer_driven(current->pipeline)) - pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx); - - return pipeline_for_each_comp(current, ctx, dir); -} - static int pipeline_comp_copy(struct comp_dev *current, struct comp_buffer *calling_buf, struct pipeline_walk_context *ctx, int dir) @@ -339,6 +268,77 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd) return 0; } +/* Runs in IPC or in pipeline task context */ +static int pipeline_comp_trigger(struct comp_dev *current, + struct comp_buffer *calling_buf, + struct pipeline_walk_context *ctx, int dir) +{ + struct pipeline_data *ppl_data = ctx->comp_data; + bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start); + bool is_same_sched; + int err; + + pipe_dbg(current->pipeline, + "pipeline_comp_trigger(), current->comp.id = %u, dir = %u", + dev_comp_id(current), dir); + + switch (ppl_data->cmd) { + case COMP_TRIGGER_PRE_RELEASE: + case COMP_TRIGGER_PRE_START: + if (comp_get_endpoint_type(current) == COMP_ENDPOINT_DAI) { + struct dai_data *dd = comp_get_drvdata(current); + + ppl_data->delay_ms = dai_get_init_delay_ms(dd->dai); + } + break; + default: + return -EINVAL; + case COMP_TRIGGER_PAUSE: + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_RELEASE: + case COMP_TRIGGER_START: + break; + } + + is_same_sched = pipeline_is_same_sched_comp(current->pipeline, + ppl_data->start->pipeline); + + /* trigger should propagate to the connected pipelines, + * which need to be scheduled together + */ + if (!is_single_ppl && !is_same_sched) { + pipe_dbg(current->pipeline, + "pipeline_comp_trigger(), current is from another pipeline"); + + if (pipeline_should_report_enodata_on_trigger(current, ctx, dir)) + return -ENODATA; + + return 0; + } + + /* send command to the component and update pipeline state */ + err = comp_trigger(current, ppl_data->cmd); + current->pipeline->trigger.aborted = err == PPL_STATUS_PATH_STOP; + if (err < 0 || err == PPL_STATUS_PATH_STOP) + return err; + + switch (ppl_data->cmd) { + case COMP_TRIGGER_PAUSE: + case COMP_TRIGGER_STOP: + if (pipeline_is_timer_driven(current->pipeline)) + current->pipeline->status = COMP_STATE_PAUSED; + } + + /* + * Add scheduling components to the list. This is only needed for + * asynchronous flows. + */ + if (!pipeline_is_timer_driven(current->pipeline)) + pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx); + + return pipeline_for_each_comp(current, ctx, dir); +} + /* actually execute pipeline trigger, including components: either in IPC or in task context */ int pipeline_trigger_run(struct pipeline *p, struct comp_dev *host, int cmd) {