From ecf557a19e05a0568d6910a6c26142c63bf09e86 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 9 Feb 2022 21:12:02 +0800 Subject: [PATCH] ipc4: copier: add stream_start|end_offset support host driver uses DMA link counter to calculate stream position for playback, but it is not fit for the following 3 cases: (1) dai is enabled before host since they are in different pipelines (2) multiple stream are mixed into one dai (3) one stream is paused but the dai is still working with other stream host uses stream_start_offset & stream_end_offset to deal with such cases: if (stream_start_offset) { position = DMA counter - stream_start_offset if (stream_stop_offset) position = stream_stop_offset -stream_start_offset; } else { position = 0; } When host component is started stream_start_offset is set to DMA counter plus the latency from host to dai since the accurate DMA counter should be used when the data arrives dai from host. When pipeline is paused, stream_end_offset will save current DMA counter for resume case When pipeline is resumed, calculate stream_start_offset based on current DMA counter and stream_end_offset Signed-off-by: Rander Wang --- src/audio/copier.c | 73 +++++++++++++++++++++++------ src/audio/pipeline/pipeline-graph.c | 41 ++++++++++++++++ src/include/sof/audio/pipeline.h | 8 ++++ 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/src/audio/copier.c b/src/audio/copier.c index ed4ca2fc5..7c9203643 100644 --- a/src/audio/copier.c +++ b/src/audio/copier.c @@ -542,6 +542,11 @@ static int copier_reset(struct comp_dev *dev) static int copier_comp_trigger(struct comp_dev *dev, int cmd) { struct copier_data *cd = comp_get_drvdata(dev); + struct sof_ipc_stream_posn posn; + struct comp_dev *dai_copier; + struct copier_data *dai_cd; + struct comp_buffer *buffer; + uint32_t latency; int ret; comp_dbg(dev, "copier_comp_trigger()"); @@ -559,13 +564,64 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) if (ret < 0 || !cd->endpoint || !cd->pipeline_reg_offset) return ret; - /* update stream start addr for running message in host copier*/ - if (dev->state != COMP_STATE_ACTIVE && cmd == COMP_TRIGGER_START) { + dai_copier = pipeline_get_dai_comp(dev->pipeline->pipeline_id, &latency); + if (!dai_copier) { + comp_err(dev, "failed to find dai comp"); + return ret; + } + dai_cd = comp_get_drvdata(dai_copier); + comp_position(dai_cd->endpoint, &posn); + + /* update stream start and end offset for running message in host copier + * host driver uses DMA link counter to calculate stream position, but it is + * not fit for the following 3 cases: + * (1) dai is enabled before host since they are in different pipelines + * (2) multiple stream are mixed into one dai + * (3) one stream is paused but the dai is still working with other stream + * + * host uses stream_start_offset & stream_end_offset to deal with such cases: + * if (stream_start_offset) { + * position = DMA counter - stream_start_offset + * if (stream_stop_offset) + * position = stream_stop_offset -stream_start_offset; + * } else { + * position = 0; + * } + * When host component is started stream_start_offset is set to DMA counter + * plus the latency from host to dai since the accurate DMA counter should be + * used when the data arrives dai from host. + * + * When pipeline is paused, stream_end_offset will save current DMA counter + * for resume case + * + * When pipeline is resumed, calculate stream_start_offset based on current + * DMA counter and stream_end_offset + */ + if (cmd == COMP_TRIGGER_START) { struct ipc4_pipeline_registers pipe_reg; - pipe_reg.stream_start_offset = 0; + buffer = list_first_item(&dai_copier->bsource_list, struct comp_buffer, sink_list); + pipe_reg.stream_start_offset = posn.dai_posn + latency * buffer->stream.size; pipe_reg.stream_end_offset = 0; mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg, sizeof(pipe_reg)); + } else if (cmd == COMP_TRIGGER_PAUSE) { + uint64_t stream_end_offset; + + stream_end_offset = posn.dai_posn; + mailbox_sw_regs_write(cd->pipeline_reg_offset + sizeof(uint64_t), + &stream_end_offset, sizeof(stream_end_offset)); + } else if (cmd == COMP_TRIGGER_RELEASE) { + struct ipc4_pipeline_registers pipe_reg; + + pipe_reg.stream_start_offset = mailbox_sw_reg_read64(cd->pipeline_reg_offset); + pipe_reg.stream_end_offset = mailbox_sw_reg_read64(cd->pipeline_reg_offset + + sizeof(pipe_reg.stream_start_offset)); + pipe_reg.stream_start_offset += posn.dai_posn - pipe_reg.stream_end_offset; + + buffer = list_first_item(&dai_copier->bsource_list, struct comp_buffer, sink_list); + pipe_reg.stream_start_offset += latency * buffer->stream.size; + mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg.stream_start_offset, + sizeof(pipe_reg.stream_start_offset)); } return ret; @@ -630,8 +686,6 @@ static int do_conversion_copy(struct comp_dev *dev, static int copier_copy(struct comp_dev *dev) { struct copier_data *cd = comp_get_drvdata(dev); - struct ipc4_pipeline_registers pipe_reg; - struct sof_ipc_stream_posn posn; int ret; comp_dbg(dev, "copier_copy()"); @@ -688,15 +742,6 @@ static int copier_copy(struct comp_dev *dev) ret = 0; } - if (ret < 0 || !cd->endpoint || !cd->pipeline_reg_offset) - return ret; - - comp_position(cd->endpoint, &posn); - cd->host_position += posn.host_posn; - pipe_reg.stream_start_offset = cd->host_position; - pipe_reg.stream_end_offset = 0; - mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg, sizeof(pipe_reg)); - return ret; } diff --git a/src/audio/pipeline/pipeline-graph.c b/src/audio/pipeline/pipeline-graph.c index aab792f43..f51777cd1 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -401,3 +401,44 @@ int pipeline_for_each_comp(struct comp_dev *current, return 0; } + +/* visit connected pipeline to find the dai comp and latency */ +struct comp_dev *pipeline_get_dai_comp(uint32_t pipeline_id, uint32_t *latency) +{ + struct ipc_comp_dev *sink; + struct ipc *ipc = ipc_get(); + + *latency = 0; + + sink = ipc_get_ppl_sink_comp(ipc, pipeline_id); + if (!sink) + return NULL; + + while (sink) { + struct comp_dev *buff_comp; + struct comp_buffer *buffer; + + *latency += sink->cd->pipeline->period; + /* dai is found */ + if (list_is_empty(&sink->cd->bsink_list)) { + struct list_item *list; + + list = comp_buffer_list(sink->cd, PPL_DIR_UPSTREAM); + buffer = buffer_from_list(list->next, struct comp_buffer, PPL_DIR_UPSTREAM); + break; + } + + buffer = buffer_from_list(comp_buffer_list(sink->cd, PPL_DIR_DOWNSTREAM)->next, + struct comp_buffer, PPL_DIR_DOWNSTREAM); + buff_comp = buffer_get_comp(buffer, PPL_DIR_DOWNSTREAM); + sink = ipc_get_ppl_sink_comp(ipc, buff_comp->pipeline->pipeline_id); + + if (!sink) + return NULL; + } + + /* convert it to ms */ + *latency /= 1000; + + return sink->cd; +} diff --git a/src/include/sof/audio/pipeline.h b/src/include/sof/audio/pipeline.h index f7f3b188b..1873eb5b1 100644 --- a/src/include/sof/audio/pipeline.h +++ b/src/include/sof/audio/pipeline.h @@ -198,6 +198,14 @@ int pipeline_reset(struct pipeline *p, struct comp_dev *host_cd); int pipeline_for_each_comp(struct comp_dev *current, struct pipeline_walk_context *ctx, int dir); +/** + * \brief Walks pipeline graph to find dai component and latency. + * \param[in] pipeline_id is the start pipeline id. + * \param[out] latency to dai. + * \return dai component. + */ +struct comp_dev *pipeline_get_dai_comp(uint32_t pipeline_id, uint32_t *latency); + /** * Retrieves pipeline id from pipeline. * @param p pipeline.