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.