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
1 change: 1 addition & 0 deletions src/audio/pipeline/pipeline-graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ struct pipeline *pipeline_new(uint32_t pipeline_id, uint32_t priority, uint32_t
p->priority = priority;
p->pipeline_id = pipeline_id;
p->status = COMP_STATE_INIT;
p->trigger.cmd = -EINVAL;
ret = memcpy_s(&p->tctx, sizeof(struct tr_ctx), &pipe_tr,
sizeof(struct tr_ctx));
assert(!ret);
Expand Down
81 changes: 60 additions & 21 deletions src/audio/pipeline/pipeline-schedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,44 @@ static enum task_state pipeline_task(void *arg)
return SOF_TASK_STATE_COMPLETED;
}

if (p->trigger.cmd >= 0) {
/* First pipeline task run for either START or RELEASE */
struct sof_ipc_reply reply = {
.hdr.cmd = SOF_IPC_GLB_REPLY,
.hdr.size = sizeof(reply),
};

err = pipeline_trigger_run(p, p->trigger.host, p->trigger.cmd);
p->trigger.cmd = -EINVAL;

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 {
p->status = COMP_STATE_ACTIVE;
err = SOF_TASK_STATE_RESCHEDULE;
}

ipc_msg_reply(&reply);

return err;
}

/*
* The first execution of the pipeline task above has triggered all
* pipeline components. Subsequent iterations actually perform data
* copying below.
*/
err = pipeline_copy(p);
if (err < 0) {
/* try to recover */
err = pipeline_xrun_recover(p);
if (err < 0) {
pipe_err(p, "pipeline_task(): xrun recover failed! pipeline will be stopped!");
pipe_err(p, "pipeline_task(): xrun recovery failed! pipeline is stopped.");
/* failed - host will stop this pipeline */
return SOF_TASK_STATE_COMPLETED;
}
Expand All @@ -71,8 +103,7 @@ static enum task_state pipeline_task(void *arg)
return SOF_TASK_STATE_RESCHEDULE;
}

static struct task *pipeline_task_init(struct pipeline *p, uint32_t type,
enum task_state (*func)(void *data))
static struct task *pipeline_task_init(struct pipeline *p, uint32_t type)
{
struct pipeline_task *task = NULL;

Expand All @@ -82,7 +113,7 @@ static struct task *pipeline_task_init(struct pipeline *p, uint32_t type,
return NULL;

if (schedule_task_init_ll(&task->task, SOF_UUID(pipe_task_uuid), type,
p->priority, func,
p->priority, pipeline_task,
p, p->core, 0) < 0) {
rfree(task);
return NULL;
Expand All @@ -108,9 +139,11 @@ int pipeline_schedule_config(struct pipeline *p, uint32_t sched_id,
return 0;
}

/* trigger connected pipelines: either immediately or schedule them */
void pipeline_schedule_triggered(struct pipeline_walk_context *ctx,
int cmd)
{
struct pipeline_data *ppl_data = ctx->comp_data;
struct list_item *tlist;
struct pipeline *p;
uint32_t flags;
Expand All @@ -122,25 +155,31 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx,
*/
irq_local_disable(flags);

list_for_item(tlist, &ctx->pipelines) {
p = container_of(tlist, struct pipeline, list);

switch (cmd) {
case COMP_TRIGGER_PAUSE:
case COMP_TRIGGER_STOP:
switch (cmd) {
case COMP_TRIGGER_PAUSE:
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;
break;
case COMP_TRIGGER_RELEASE:
case COMP_TRIGGER_START:
}
break;
case COMP_TRIGGER_RELEASE:
case COMP_TRIGGER_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 (cmd >= 0) {
p->trigger.cmd = cmd;
p->trigger.host = ppl_data->start;
cmd = -EINVAL;
}
} else {
p->xrun_bytes = 0;
p->status = COMP_STATE_ACTIVE;
}
pipeline_schedule_copy(p, 0);
p->xrun_bytes = 0;
p->status = COMP_STATE_ACTIVE;
break;
case COMP_TRIGGER_SUSPEND:
case COMP_TRIGGER_RESUME:
default:
break;
}
}

Expand All @@ -159,7 +198,7 @@ int pipeline_comp_task_init(struct pipeline *p)
type = pipeline_is_timer_driven(p) ? SOF_SCHEDULE_LL_TIMER :
SOF_SCHEDULE_LL_DMA;

p->pipe_task = pipeline_task_init(p, type, pipeline_task);
p->pipe_task = pipeline_task_init(p, type);
if (!p->pipe_task) {
pipe_err(p, "pipeline_comp_task_init(): task init failed");
return -ENOMEM;
Expand Down
150 changes: 128 additions & 22 deletions src/audio/pipeline/pipeline-stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,48 @@ 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;
int is_single_ppl = comp_is_single_pipeline(current, ppl_data->start);
int is_same_sched =
pipeline_is_same_sched_comp(current->pipeline,
ppl_data->start->pipeline);
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",
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_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");
pipe_dbg(current->pipeline,
"pipeline_comp_trigger(), current is from another pipeline");

if (pipeline_should_report_enodata_on_trigger(current, ctx,
dir))
if (pipeline_should_report_enodata_on_trigger(current, ctx, dir))
return -ENODATA;

return 0;
Expand All @@ -91,7 +111,12 @@ static int pipeline_comp_trigger(struct comp_dev *current,
if (err < 0 || err == PPL_STATUS_PATH_STOP)
return err;

pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx);
/*
* 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);
}
Expand All @@ -101,14 +126,15 @@ static int pipeline_comp_copy(struct comp_dev *current,
struct pipeline_walk_context *ctx, int dir)
{
struct pipeline_data *ppl_data = ctx->comp_data;
int is_single_ppl = comp_is_single_pipeline(current, ppl_data->start);
bool is_single_ppl = comp_is_single_pipeline(current, ppl_data->start);
int err;

pipe_dbg(current->pipeline, "pipeline_comp_copy(), current->comp.id = %u, dir = %u",
dev_comp_id(current), dir);

if (!is_single_ppl) {
pipe_dbg(current->pipeline, "pipeline_comp_copy(), current is from another pipeline and can't be scheduled together");
pipe_dbg(current->pipeline,
"pipeline_comp_copy(), current is from another pipeline and can't be scheduled together");
return 0;
}

Expand Down Expand Up @@ -170,18 +196,95 @@ int pipeline_copy(struct pipeline *p)
return ret;
}

/* trigger pipeline */
/* only collect scheduling components */
static int pipeline_comp_list(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 = pipeline_is_same_sched_comp(current->pipeline,
ppl_data->start->pipeline);

if (!is_single_ppl && !is_same_sched) {
pipe_dbg(current->pipeline,
"pipeline_comp_list(), current is from another pipeline");
return 0;
}

/* Add scheduling components to the list */
pipeline_comp_trigger_sched_comp(current->pipeline, current, ctx);

return pipeline_for_each_comp(current, ctx, dir);
}

/* build a list of connected pipelines' scheduling components and trigger them */
static int pipeline_trigger_list(struct pipeline *p, struct comp_dev *host, int cmd)
{
struct pipeline_data data = {
.start = host,
.cmd = cmd,
};
struct pipeline_walk_context walk_ctx = {
.comp_func = pipeline_comp_list,
.comp_data = &data,
.skip_incomplete = true,
};
int ret;

list_init(&walk_ctx.pipelines);

ret = walk_ctx.comp_func(host, NULL, &walk_ctx, host->direction);
if (ret < 0)
pipe_err(p, "pipeline_trigger_list(): ret = %d, host->comp.id = %u, cmd = %d",
ret, dev_comp_id(host), cmd);
else
pipeline_schedule_triggered(&walk_ctx, cmd);

return ret;
}

/* trigger pipeline in IPC context */
int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd)
{
struct pipeline_data data;
int ret;

pipe_info(p, "pipe trigger cmd %d", cmd);

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;
case COMP_TRIGGER_RELEASE:
case COMP_TRIGGER_START:
/* Add all connected pipelines to the list and schedule them all */
ret = pipeline_trigger_list(p, host, cmd);
if (ret < 0)
return ret;
/* IPC response will be sent from the task */
return 1;
}

return 0;
}

/* 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)
{
struct pipeline_data data = {
.start = host,
.cmd = cmd,
};
struct pipeline_walk_context walk_ctx = {
.comp_func = pipeline_comp_trigger,
.comp_data = &data,
.skip_incomplete = true,
};
int ret;

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

list_init(&walk_ctx.pipelines);

Expand All @@ -191,20 +294,23 @@ int pipeline_trigger(struct pipeline *p, struct comp_dev *host, int cmd)
if (ret < 0) {
pipe_err(p, "xrun handle: ret = %d", ret);
return ret;
} else if (ret == PPL_STATUS_PATH_STOP)
}

if (ret == PPL_STATUS_PATH_STOP)
/* no further action needed*/
return 0;
return pipeline_is_timer_driven(p);
}

data.start = host;
data.cmd = 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",
if (ret < 0)
pipe_err(p, "pipeline_trigger_run(): ret = %d, host->comp.id = %u, cmd = %d",
ret, dev_comp_id(host), cmd);
}

/*
* When called from the pipeline task, pipeline_comp_trigger() will not
* add pipelines to the list, so pipeline_schedule_triggered() will have
* no effect.
*/
pipeline_schedule_triggered(&walk_ctx, cmd);

return ret;
Expand Down
2 changes: 1 addition & 1 deletion src/audio/pipeline/pipeline-xrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev,
return;

/* notify all pipeline comps we are in XRUN, and stop copying */
ret = pipeline_trigger(p, p->source_comp, COMP_TRIGGER_XRUN);
ret = pipeline_trigger_run(p, p->source_comp, COMP_TRIGGER_XRUN);
if (ret < 0)
pipe_err(p, "pipeline_xrun(): Pipelines notification about XRUN failed, ret = %d",
ret);
Expand Down
7 changes: 6 additions & 1 deletion src/drivers/intel/cavs/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,13 @@ enum task_state ipc_platform_do_cmd(void *data)
void ipc_platform_complete_cmd(void *data)
{
struct ipc *ipc = data;
uint32_t flags;

if (!cpu_is_me(ipc->core))
if (!cpu_is_me(ipc->core) || ipc->delayed_response)
return;

spin_lock_irq(&ipc->lock, flags);

/* write 1 to clear busy, and trigger interrupt to host*/
#if CAVS_VERSION == CAVS_VERSION_1_5
ipc_write(IPC_DIPCT, ipc_read(IPC_DIPCT) | IPC_DIPCT_BUSY);
Expand All @@ -229,6 +232,8 @@ void ipc_platform_complete_cmd(void *data)
}

#endif

spin_unlock_irq(&ipc->lock, flags);
}

int ipc_platform_send_msg(struct ipc_msg *msg)
Expand Down
Loading