diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 6007dd01642c..4ec85cb64fba 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -494,8 +494,8 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) } buffer = comp_dev_get_first_data_producer(dai_copier); - pipe_reg.stream_start_offset = posn.dai_posn + - latency * audio_stream_period_bytes(&buffer->stream, dev->frames); + pipe_reg.stream_start_offset = posn.dai_posn + (uint64_t)latency * + audio_stream_period_bytes(&buffer->stream, dev->frames); 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) { @@ -510,7 +510,15 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) 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; + + /* stream_end_offset was sampled from the dai position when the pipeline + * was paused, so the current position should be ahead of it. The value + * is read back from the host visible mailbox and cannot be trusted: a + * stale or out of order one would make the unsigned subtraction wrap + * and corrupt the offset reported to the host driver. + */ + if (posn.dai_posn > pipe_reg.stream_end_offset) + pipe_reg.stream_start_offset += posn.dai_posn - pipe_reg.stream_end_offset; if (list_is_empty(&dai_copier->bsource_list)) { comp_err(dev, "No source buffer bound to dai_copier"); @@ -518,7 +526,7 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd) } buffer = comp_dev_get_first_data_producer(dai_copier); - pipe_reg.stream_start_offset += latency * + pipe_reg.stream_start_offset += (uint64_t)latency * audio_stream_period_bytes(&buffer->stream, dev->frames); mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg.stream_start_offset, sizeof(pipe_reg.stream_start_offset)); diff --git a/src/audio/pipeline/pipeline-graph.c b/src/audio/pipeline/pipeline-graph.c index f6f83a0e8dc6..b4e5d5cb4ff5 100644 --- a/src/audio/pipeline/pipeline-graph.c +++ b/src/audio/pipeline/pipeline-graph.c @@ -699,9 +699,20 @@ struct comp_dev *pipeline_get_dai_comp_latency(uint32_t pipeline_id, uint32_t *l if (ret < 0) return NULL; - if (input_data && output_data && input_base_cfg.ibs && output_base_cfg.obs) - *latency += input_data / input_base_cfg.ibs - - output_data / output_base_cfg.obs; + if (input_data && output_data && input_base_cfg.ibs && output_base_cfg.obs) { + uint64_t in_blocks = input_data / input_base_cfg.ibs; + uint64_t out_blocks = output_data / output_base_cfg.obs; + + /* The latency is a count of blocks buffered between the + * source and the sink and cannot be negative. The sink may + * legitimately lead the source, e.g. when the dai is started + * before the host because they are in different pipelines, + * so clamp instead of letting the unsigned subtraction wrap + * into a huge value. + */ + if (in_blocks > out_blocks) + *latency += in_blocks - out_blocks; + } /* If the component doesn't have a sink buffer, it can be a dai. */ if (list_is_empty(&ipc_sink->cd->bsink_list))