diff --git a/src/audio/pipeline/pipeline-schedule.c b/src/audio/pipeline/pipeline-schedule.c index 2dc3ee5924a8..e7dd7f227c02 100644 --- a/src/audio/pipeline/pipeline-schedule.c +++ b/src/audio/pipeline/pipeline-schedule.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -201,6 +202,25 @@ static enum task_state pipeline_task(void *arg) * copying below. */ err = pipeline_copy(p); + if (p->status == COMP_STATE_DRAINING) { + /* check the return value of the copy operation. + * + * if it's <= 0 then the draining opearation is over + * and we need to send a notification to the host + */ + if (err <= 0) { + /* set pipeline status back to ACTIVE */ + p->status = COMP_STATE_ACTIVE; + + /* notify host that draining is over */ + err = ipc_stream_drain_notify(); + + if (err < 0) { + pipe_err(p, "pipeline_task(): failed drain notify"); + return SOF_TASK_STATE_COMPLETED; + } + } + } if (err < 0) { /* try to recover */ err = pipeline_xrun_recover(p); @@ -320,6 +340,13 @@ void pipeline_schedule_triggered(struct pipeline_walk_context *ctx, */ p->xrun_bytes = 1; } + break; + case COMP_TRIGGER_DRAIN: + list_for_item(tlist, &ctx->pipelines) { + p = container_of(tlist, struct pipeline, list); + p->xrun_bytes = 0; + p->status = COMP_STATE_DRAINING; + } } irq_local_enable(flags); diff --git a/src/audio/pipeline/pipeline-stream.c b/src/audio/pipeline/pipeline-stream.c index c649cda4691d..e1609fe7f0ad 100644 --- a/src/audio/pipeline/pipeline-stream.c +++ b/src/audio/pipeline/pipeline-stream.c @@ -328,6 +328,7 @@ static int pipeline_comp_trigger(struct comp_dev *current, case COMP_TRIGGER_STOP: case COMP_TRIGGER_RELEASE: case COMP_TRIGGER_START: + case COMP_TRIGGER_DRAIN: break; } @@ -349,14 +350,21 @@ static int pipeline_comp_trigger(struct comp_dev *current, current->pipeline->trigger.pending = false; - /* send command to the component and update pipeline state */ - err = comp_trigger(current, ppl_data->cmd); - if (err < 0) - return err; + /* send command to the component and update pipeline state. + * + * if we're dealing with DRAIN trigger we don't want to update + * the state of the components since we're not using that + * information + */ + if (ppl_data->cmd != COMP_TRIGGER_DRAIN) { + err = comp_trigger(current, ppl_data->cmd); + if (err < 0) + return err; - if (err == PPL_STATUS_PATH_STOP) { - current->pipeline->trigger.aborted = true; - return err; + if (err == PPL_STATUS_PATH_STOP) { + current->pipeline->trigger.aborted = true; + return err; + } } switch (ppl_data->cmd) { diff --git a/src/include/ipc/stream.h b/src/include/ipc/stream.h index 2d76fdc18693..fac6c209e45e 100644 --- a/src/include/ipc/stream.h +++ b/src/include/ipc/stream.h @@ -158,4 +158,9 @@ struct sof_ipc_stream_posn { int32_t xrun_size; /**< XRUN size in bytes */ } __attribute__((packed, aligned(4))); +/* drain information */ +struct sof_ipc_stream_drain { + struct sof_ipc_reply rhdr; +} __attribute__((packed, aligned(4))); + #endif /* __IPC_STREAM_H__ */ diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 0c68af51ca55..33ff9a4868af 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -65,6 +65,7 @@ struct timestamp_data; #define COMP_STATE_PAUSED 4 /**< Component paused */ #define COMP_STATE_ACTIVE 5 /**< Component active */ #define COMP_STATE_PRE_ACTIVE 6 /**< Component after early initialisation */ +#define COMP_STATE_DRAINING 7 /**< Component draining */ /** @}*/ /** \name Standard Component Stream Commands @@ -91,6 +92,7 @@ enum { COMP_TRIGGER_POST_STOP, /**< Finalize stop component stream */ COMP_TRIGGER_POST_PAUSE, /**< Finalize pause component stream */ COMP_TRIGGER_NO_ACTION, /**< No action required */ + COMP_TRIGGER_DRAIN, /**< Start drain operation */ }; /** @}*/ diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index d357c6f79360..d6d21bbedea4 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -197,4 +197,9 @@ void ipc_msg_reply(struct sof_ipc_reply *reply); */ void ipc_complete_cmd(struct ipc *ipc); +/** + * \brief Notify host that the drain operation is done. + */ +int ipc_stream_drain_notify(void); + #endif /* __SOF_DRIVERS_IPC_H__ */ diff --git a/src/ipc/ipc3/handler.c b/src/ipc/ipc3/handler.c index 981eac2561e5..9c0ee5eee2dd 100644 --- a/src/ipc/ipc3/handler.c +++ b/src/ipc/ipc3/handler.c @@ -422,6 +422,51 @@ static int ipc_stream_position(uint32_t header) return 1; } +int ipc_stream_drain_notify(void) +{ + struct ipc *ipc = ipc_get(); + struct sof_ipc_stream stream; + struct sof_ipc_stream_drain drain; + struct ipc_comp_dev *pcm_dev; + + /* copy message with ABI safe method */ + IPC_COPY_CMD(stream, ipc->comp_data); + + /* get the pcm dev */ + pcm_dev = ipc_get_comp_by_id(ipc, stream.comp_id); + if (!pcm_dev) { + tr_err(&ipc_tr, "ipc: comp %d not found", stream.comp_id); + return -ENODEV; + } + + /* check core */ + if (!cpu_is_me(pcm_dev->core)) + return ipc_process_on_core(pcm_dev->core, false); + + /* prepare message */ + memset(&drain, 0, sizeof(drain)); + drain.rhdr.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_TRIG_DRAIN | + stream.comp_id; + drain.rhdr.hdr.size = sizeof(drain); + + if (!pcm_dev->cd->pipeline) { + tr_err(&ipc_tr, "ipc: comp %d pipeline not found", + stream.comp_id); + return -EINVAL; + } + + /* write message */ + mailbox_stream_write(pcm_dev->cd->pipeline->posn_offset, &drain, + sizeof(drain)); + + /* send IPC notification */ + struct ipc_msg *msg = ipc_msg_init(drain.rhdr.hdr.cmd, drain.rhdr.hdr.size); + + ipc_msg_send(msg, &drain, false); + + return 0; +} + static int ipc_stream_trigger(uint32_t header) { struct ipc *ipc = ipc_get(); @@ -461,6 +506,9 @@ static int ipc_stream_trigger(uint32_t header) case SOF_IPC_STREAM_TRIG_RELEASE: cmd = COMP_TRIGGER_PRE_RELEASE; break; + case SOF_IPC_STREAM_TRIG_DRAIN: + cmd = COMP_TRIGGER_DRAIN; + break; /* XRUN is special case- TODO */ case SOF_IPC_STREAM_TRIG_XRUN: return 0;