diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index baba00083b89..d3819191d278 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -45,8 +45,15 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, if (!p->trigger.host) { p->trigger.cmd = COMP_TRIGGER_NO_ACTION; - return p->status == COMP_STATE_PAUSED ? SOF_TASK_STATE_COMPLETED : - SOF_TASK_STATE_RESCHEDULE; + + switch (cmd) { + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + return p->trigger.aborted ? SOF_TASK_STATE_RUNNING : + SOF_TASK_STATE_COMPLETED; + } + + return SOF_TASK_STATE_RESCHEDULE; } err = pipeline_trigger_run(p, p->trigger.host, cmd); @@ -77,8 +84,18 @@ static enum task_state pipeline_task_cmd(struct pipeline *p, 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; + err = SOF_TASK_STATE_RESCHEDULE; + } else if (p->status == COMP_STATE_PAUSED) { + if (p->trigger.aborted) { + p->status = COMP_STATE_ACTIVE; + /* + * the pipeline aborted a STOP or a PAUSE + * command, proceed with copying + */ + err = SOF_TASK_STATE_RUNNING; + } else { + err = SOF_TASK_STATE_COMPLETED; + } } else { p->status = COMP_STATE_ACTIVE; err = SOF_TASK_STATE_RESCHEDULE; @@ -136,9 +153,12 @@ static enum task_state pipeline_task(void *arg) return SOF_TASK_STATE_RESCHEDULE; } - if (p->trigger.cmd != COMP_TRIGGER_NO_ACTION) + if (p->trigger.cmd != COMP_TRIGGER_NO_ACTION) { /* Process an offloaded command */ - return pipeline_task_cmd(p, &reply); + err = pipeline_task_cmd(p, &reply); + if (err != SOF_TASK_STATE_RUNNING) + return err; + } if (p->status == COMP_STATE_PAUSED) /* @@ -211,7 +231,6 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, struct list_item *tlist; struct pipeline *p; uint32_t flags; - bool first_pipe = true; /* * Interrupts have to be disabled while adding tasks to or removing them @@ -246,7 +265,7 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, list_for_item(tlist, &ctx->pipelines) { p = container_of(tlist, struct pipeline, list); p->xrun_bytes = 0; - if (pipeline_is_timer_driven(p) && first_pipe) { + if (pipeline_is_timer_driven(p)) { /* * Use the first of connected pipelines to * trigger, mark all other connected pipelines @@ -254,7 +273,7 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, */ p->trigger.cmd = cmd; p->trigger.host = ppl_data->start; - first_pipe = false; + ppl_data->start = NULL; } else { p->status = COMP_STATE_ACTIVE; } @@ -321,5 +340,12 @@ void pipeline_schedule_copy(struct pipeline *p, uint64_t start) if (!pipeline_is_timer_driven(p)) sa_set_panic_on_delay(false); - schedule_task(p->pipe_task, start, p->period); + if (p->sched_next && task_is_active(p->sched_next->pipe_task)) + schedule_task_before(p->pipe_task, start, p->period, + p->sched_next->pipe_task); + else if (p->sched_prev && task_is_active(p->sched_prev->pipe_task)) + schedule_task_after(p->pipe_task, start, p->period, + p->sched_prev->pipe_task); + else + schedule_task(p->pipe_task, start, p->period); } diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index ddd199c22d1e..850fef1c6a85 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -175,11 +175,33 @@ static int pipeline_trigger_list(struct pipeline *p, struct comp_dev *host, int list_init(&walk_ctx.pipelines); ret = walk_ctx.comp_func(host, NULL, &walk_ctx, host->direction); - if (ret < 0) + if (ret < 0) { pipe_err(p, "pipeline_trigger_list(): ret = %d, host->comp.id = %u, cmd = %d", ret, dev_comp_id(host), cmd); - else + } else { + if (cmd == COMP_TRIGGER_PRE_START) { + struct list_item *list; + struct pipeline *current, *upstream = NULL; + + /* Make sure the first pipeline has the highest priority */ + list_for_item(list, &walk_ctx.pipelines) { + current = list_item(list, struct pipeline, list); + + if (current->sched_comp->direction == SOF_IPC_STREAM_PLAYBACK) { + current->sched_prev = upstream; + if (upstream) + upstream->sched_next = current; + } else { + current->sched_next = upstream; + if (upstream) + upstream->sched_prev = current; + } + + upstream = current; + } + } pipeline_schedule_triggered(&walk_ctx, cmd); + } return ret; } diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index 3bb9cdf86f1f..d27d8728b98c 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ struct comp_buffer; struct comp_dev; struct ipc; struct ipc_msg; -struct task; /* Pipeline status to stop execution of current path */ #define PPL_STATUS_PATH_STOP 1 @@ -60,6 +60,8 @@ struct pipeline { /* scheduling */ struct task *pipe_task; /* pipeline processing task */ + struct pipeline *sched_next; /* pipeline scheduled after this */ + struct pipeline *sched_prev; /* pipeline scheduled before this */ /* component that drives scheduling in this pipe */ struct comp_dev *sched_comp; @@ -108,6 +110,15 @@ struct pipeline_data { uint32_t delay_ms; /* between PRE_{START,RELEASE} and {START,RELEASE} */ }; +/** \brief Task type registered by pipelines. */ +struct pipeline_task { + struct task task; /**< parent structure */ + bool registrable; /**< should task be registered on irq */ + struct comp_dev *sched_comp; /**< pipeline scheduling component */ +}; + +#define pipeline_task_get(t) container_of(t, struct pipeline_task, task) + /* * Pipeline Graph APIs * diff --git a/src/include/sof/schedule/schedule.h b/src/include/sof/schedule/schedule.h index af677d3fabad..5d069443ab07 100644 --- a/src/include/sof/schedule/schedule.h +++ b/src/include/sof/schedule/schedule.h @@ -60,6 +60,34 @@ struct scheduler_ops { int (*schedule_task)(void *data, struct task *task, uint64_t start, uint64_t period); + /** + * Schedules task with given scheduling parameters. + * @param data Private data of selected scheduler. + * @param task Task to be scheduled. + * @param start Start time of given task (in microseconds). + * @param period Scheduling period of given task (in microseconds). + * @param before Task to be scheduled before + * @return 0 if succeeded, error code otherwise. + * + * This operation is optional. + */ + int (*schedule_task_before)(void *data, struct task *task, uint64_t start, + uint64_t period, struct task *before); + + /** + * Schedules task with given scheduling parameters. + * @param data Private data of selected scheduler. + * @param task Task to be scheduled. + * @param start Start time of given task (in microseconds). + * @param period Scheduling period of given task (in microseconds). + * @param after Task to be scheduled after + * @return 0 if succeeded, error code otherwise. + * + * This operation is optional. + */ + int (*schedule_task_after)(void *data, struct task *task, uint64_t start, + uint64_t period, struct task *after); + /** * Sets task into running state along with executing additional * scheduler specific operations. @@ -223,9 +251,12 @@ static inline int schedule_task(struct task *task, uint64_t start, struct schedule_data *sch; struct list_item *slist; + if (!task) + return -EINVAL; + list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->schedule_task) + if (task->type == sch->type) return sch->ops->schedule_task(sch->data, task, start, period); } @@ -233,6 +264,58 @@ static inline int schedule_task(struct task *task, uint64_t start, return -ENODEV; } +/** See scheduler_ops::schedule_task_before */ +static inline int schedule_task_before(struct task *task, uint64_t start, + uint64_t period, struct task *before) +{ + struct schedulers *schedulers = *arch_schedulers_get(); + struct schedule_data *sch; + struct list_item *slist; + + if (!task || !before) + return -EINVAL; + + list_for_item(slist, &schedulers->list) { + sch = container_of(slist, struct schedule_data, list); + if (task->type == sch->type) { + if (sch->ops->schedule_task_before) + return sch->ops->schedule_task_before(sch->data, task, start, + period, before); + + return sch->ops->schedule_task(sch->data, task, start, + period); + } + } + + return -ENODEV; +} + +/** See scheduler_ops::schedule_task_after */ +static inline int schedule_task_after(struct task *task, uint64_t start, + uint64_t period, struct task *after) +{ + struct schedulers *schedulers = *arch_schedulers_get(); + struct schedule_data *sch; + struct list_item *slist; + + if (!task || !after) + return -EINVAL; + + list_for_item(slist, &schedulers->list) { + sch = container_of(slist, struct schedule_data, list); + if (task->type == sch->type) { + if (sch->ops->schedule_task_after) + return sch->ops->schedule_task_after(sch->data, task, start, + period, after); + + return sch->ops->schedule_task(sch->data, task, start, + period); + } + } + + return -ENODEV; +} + /** See scheduler_ops::reschedule_task */ static inline int reschedule_task(struct task *task, uint64_t start) { @@ -264,7 +347,7 @@ static inline int schedule_task_cancel(struct task *task) list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->schedule_task_cancel) + if (task->type == sch->type) return sch->ops->schedule_task_cancel(sch->data, task); } @@ -280,7 +363,7 @@ static inline int schedule_task_free(struct task *task) list_for_item(slist, &schedulers->list) { sch = container_of(slist, struct schedule_data, list); - if (task->type == sch->type && sch->ops->schedule_task_free) + if (task->type == sch->type) return sch->ops->schedule_task_free(sch->data, task); } diff --git a/src/include/sof/schedule/task.h b/src/include/sof/schedule/task.h index e38cf0b9ac77..a33bf0d5d599 100644 --- a/src/include/sof/schedule/task.h +++ b/src/include/sof/schedule/task.h @@ -75,14 +75,19 @@ struct task { #endif }; -/** \brief Task type registered by pipelines. */ -struct pipeline_task { - struct task task; /**< parent structure */ - bool registrable; /**< should task be registered on irq */ - struct comp_dev *sched_comp; /**< pipeline scheduling component */ -}; - -#define pipeline_task_get(t) container_of(t, struct pipeline_task, task) +static inline bool task_is_active(struct task *task) +{ + switch (task->state) { + case SOF_TASK_STATE_QUEUED: + case SOF_TASK_STATE_PENDING: + case SOF_TASK_STATE_RUNNING: + case SOF_TASK_STATE_PREEMPTED: + case SOF_TASK_STATE_RESCHEDULE: + return true; + default: + return false; + } +} static inline enum task_state task_run(struct task *task) { diff --git a/src/schedule/ll_schedule.c b/src/schedule/ll_schedule.c index 7e0c164919c2..cd1ecb36fa3d 100644 --- a/src/schedule/ll_schedule.c +++ b/src/schedule/ll_schedule.c @@ -7,6 +7,7 @@ // Tomasz Lauda #include +#include #include #include #include @@ -293,7 +294,7 @@ static void schedule_ll_tasks_run(void *data) static int schedule_ll_domain_set(struct ll_schedule_data *sch, struct task *task, uint64_t start, - uint64_t period) + uint64_t period, struct task *reference) { struct ll_schedule_domain *domain = sch->domain; int core = cpu_get_id(); @@ -320,7 +321,9 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, task_start_ticks = domain->ticks_per_ms * task_start_us / 1000; task_start = task_start_ticks + platform_timer_get_atomic(timer_get()); - if (domain->next_tick == UINT64_MAX) { + if (reference) { + task->start = reference->start; + } else if (domain->next_tick == UINT64_MAX) { /* first task, set domain */ domain_set(domain, task_start); task->start = domain->next_tick; @@ -329,11 +332,13 @@ static int schedule_ll_domain_set(struct ll_schedule_data *sch, task->start = task_start; if (task->start < domain->next_tick) domain_set(domain, task_start); - } else if (task_start < domain->next_tick) { - /* earlier periodic task, try to make it cadence-aligned with the existed task */ - offset = (domain->next_tick - task_start) % - (domain->ticks_per_ms * period / 1000); - task_start = task_start - task_start_ticks + offset; + } else if (task_start + task_start_ticks < domain->next_tick) { + /* + * Earlier periodic task, try to make it cadence-aligned with the existed task. + * In this case task_start_ticks is the number of ticks per period. + */ + offset = (domain->next_tick - task_start) % task_start_ticks; + task_start += offset; domain_set(domain, task_start); task->start = domain->next_tick; } else { @@ -407,10 +412,20 @@ static void schedule_ll_task_insert(struct task *task, struct list_item *tasks) list_item_append(&task->list, tasks); } -static int schedule_ll_task(void *data, struct task *task, uint64_t start, - uint64_t period) +static void schedule_ll_task_insert_before(struct task *task, struct task *before) +{ + list_item_append(&task->list, &before->list); +} + +static void schedule_ll_task_insert_after(struct task *task, struct task *after) +{ + list_item_prepend(&task->list, &after->list); +} + +static int schedule_ll_task_common(struct ll_schedule_data *sch, struct task *task, + uint64_t start, uint64_t period, + struct task *reference, bool before) { - struct ll_schedule_data *sch = data; struct ll_task_pdata *pdata; struct ll_task_pdata *reg_pdata; struct list_item *tlist; @@ -486,11 +501,16 @@ static int schedule_ll_task(void *data, struct task *task, uint64_t start, } /* insert task into the list */ - schedule_ll_task_insert(task, &sch->tasks); + if (!reference) + schedule_ll_task_insert(task, &sch->tasks); + else if (before) + schedule_ll_task_insert_before(task, reference); + else + schedule_ll_task_insert_after(task, reference); task->state = SOF_TASK_STATE_QUEUED; /* set schedule domain */ - ret = schedule_ll_domain_set(sch, task, start, period); + ret = schedule_ll_domain_set(sch, task, start, period, reference); if (ret < 0) { list_item_del(&task->list); goto out; @@ -503,6 +523,30 @@ static int schedule_ll_task(void *data, struct task *task, uint64_t start, return ret; } +static int schedule_ll_task(void *data, struct task *task, uint64_t start, + uint64_t period) +{ + struct ll_schedule_data *sch = data; + + return schedule_ll_task_common(sch, task, start, period, NULL, false); +} + +static int schedule_ll_task_before(void *data, struct task *task, uint64_t start, + uint64_t period, struct task *before) +{ + struct ll_schedule_data *sch = data; + + return schedule_ll_task_common(sch, task, start, period, before, true); +} + +static int schedule_ll_task_after(void *data, struct task *task, uint64_t start, + uint64_t period, struct task *after) +{ + struct ll_schedule_data *sch = data; + + return schedule_ll_task_common(sch, task, start, period, after, false); +} + int schedule_task_init_ll(struct task *task, const struct sof_uuid_entry *uid, uint16_t type, uint16_t priority, enum task_state (*run)(void *data), @@ -693,6 +737,8 @@ int scheduler_init_ll(struct ll_schedule_domain *domain) static const struct scheduler_ops schedule_ll_ops = { .schedule_task = schedule_ll_task, + .schedule_task_before = schedule_ll_task_before, + .schedule_task_after = schedule_ll_task_after, .schedule_task_free = schedule_ll_task_free, .schedule_task_cancel = schedule_ll_task_cancel, .reschedule_task = reschedule_ll_task, diff --git a/src/schedule/schedule.c b/src/schedule/schedule.c index 64a4d74d00fe..543f91e21914 100644 --- a/src/schedule/schedule.c +++ b/src/schedule/schedule.c @@ -61,6 +61,10 @@ void scheduler_init(int type, const struct scheduler_ops *ops, void *data) { struct schedule_data *sch; + if (!ops || !ops->schedule_task || !ops->schedule_task_cancel || + !ops->schedule_task_free) + return; + sch = rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, sizeof(*sch)); list_init(&sch->list); sch->type = type;