diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index 16df23e7edb6..c5fb484bd080 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -35,12 +35,69 @@ 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_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_cmd(): 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_cmd(): 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 = { + .hdr.cmd = SOF_IPC_GLB_REPLY, + .hdr.size = sizeof(reply), + }; struct pipeline *p = arg; int err; @@ -79,38 +136,16 @@ 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; - - 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; - } + if (p->trigger.cmd >= 0) + /* Process an offloaded command */ + return pipeline_task_cmd(p, &reply); - p->trigger.cmd = -EINVAL; - - ipc_msg_reply(&reply); - - 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 @@ -190,23 +225,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..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, async; - 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_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) { - struct dai_data *dd = comp_get_drvdata(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; - } - - 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); - if (err < 0 || err == PPL_STATUS_PATH_STOP) - return err; - - /* - * Add scheduling components to the list. This is only needed for the - * stopping flow. - */ - if (async) - 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) @@ -309,14 +238,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,13 +261,84 @@ 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; } 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) { @@ -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) { @@ -394,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; 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; };