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 <rander.wang@intel.com>
This commit is contained in:
Rander Wang 2022-02-09 21:12:02 +08:00 committed by Liam Girdwood
parent 24b036930b
commit ecf557a19e
3 changed files with 108 additions and 14 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.