Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 92 additions & 43 deletions src/audio/pipeline/pipeline-schedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
163 changes: 86 additions & 77 deletions src/audio/pipeline/pipeline-stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -309,28 +238,107 @@ 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:
/* Add all connected pipelines to the list and trigger them all */
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)
{
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/include/sof/audio/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct pipeline {
int cmd;
struct comp_dev *host;
unsigned int delay; /* period count */
bool aborted;
} trigger;
};

Expand Down