diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 35caa4e02e60..ae9d2cb8fc91 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -1516,6 +1516,9 @@ int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct comp_dev *dev, return ret; } + if (dd[i]->dma->plat_data.caps & SOF_DMA_CAP_HDA) + audio_stream_sync_to_hw(&dd[i]->dma_buffer->stream, &stat); + avail_bytes = MIN(avail_bytes, stat.pending_length); free_bytes = MIN(free_bytes, stat.free); } @@ -1647,6 +1650,18 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun return ret; } + /* HDA DMA Write and Read Position registers are not cleared by hardware on + * dma_stop() or dma_start(). However, the dma_buffer is recreated after a reset + * with its w_ptr and r_ptr set to NULL. The w_ptr and r_ptr must be kept in sync + * with the hardware DMA Write and Read Positions. + * Other types of DMA clear their hardware Read/Write Positions upon dma_stop() + * or dma_start(). Additionally, some DMAs, such as GPDMA, do not populate + * dma_status::write_position and read_position. Therefore, synchronization is + * done here only for HDA DMA. + */ + if (dd->dma->plat_data.caps & SOF_DMA_CAP_HDA) + audio_stream_sync_to_hw(&dd->dma_buffer->stream, &stat); + avail_bytes = stat.pending_length; free_bytes = stat.free; diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 874a69478ebd..c167e5256d95 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -386,6 +386,23 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev return 0; } + /* HDA DMA Write and Read Position registers are not cleared by hardware on + * dma_stop() or dma_start(). However, the dma_buffer is recreated after a reset + * with its w_ptr and r_ptr set to NULL. The w_ptr and r_ptr must be kept in sync + * with the hardware DMA Write and Read Positions. + * Other types of DMA clear their hardware Read/Write Positions upon dma_stop() + * or dma_start(). Additionally, some DMAs, such as GPDMA, do not populate + * dma_status::write_position and read_position. Therefore, synchronization is + * done here only for HDA DMA. + * + * For deep buffers, dma_reload() is called less frequently than consume/produce + * on dma_buffer. Replicate the hardware state of the DMA buffer to the dma_buffer + * struct only when no consume/produce operations on dma_buffer have been called + * since the last dma_reload() (hence hd->partial_size == 0 check here). + */ + if ((hd->dma->plat_data.caps & SOF_DMA_CAP_HDA) && hd->partial_size == 0) + audio_stream_sync_to_hw(&hd->dma_buffer->stream, &dma_stat); + dma_sample_bytes = hd->config.src_width; /* calculate minimum size to copy */ diff --git a/src/include/sof/audio/audio_stream.h b/src/include/sof/audio/audio_stream.h index f930c7af3d8a..d400b823e0c1 100644 --- a/src/include/sof/audio/audio_stream.h +++ b/src/include/sof/audio/audio_stream.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -661,6 +662,35 @@ static inline void audio_stream_consume(struct audio_stream *buffer, uint32_t by buffer->free = buffer->size - buffer->avail; } +#ifdef __ZEPHYR__ +/** + * Replicates the hardware state of the DMA buffer as a struct audio_stream. + * @param stream Stream to update. + * @param dma_status DMA buffer hardware state (struct dma_status from Zephyr). + */ +static inline void audio_stream_sync_to_hw(struct audio_stream *stream, + const struct dma_status *dma_status) +{ + uintptr_t buf_start = (uintptr_t)audio_stream_get_addr(stream); + + /* Note: It is assumed here that dma_status values are reported as bytes. However, + * this is actually platform-specific. Although unlikely, they could be, for example, + * words on some particular platform, and changes should be made here to address + * such case. + */ + + assert(dma_status->write_position < audio_stream_get_size(stream)); + assert(dma_status->read_position < audio_stream_get_size(stream)); + assert(dma_status->pending_length <= audio_stream_get_size(stream)); + assert(dma_status->free <= audio_stream_get_size(stream)); + + audio_stream_set_wptr(stream, (void *)(buf_start + dma_status->write_position)); + audio_stream_set_rptr(stream, (void *)(buf_start + dma_status->read_position)); + audio_stream_set_avail(stream, dma_status->pending_length); + audio_stream_set_free(stream, dma_status->free); +} +#endif /* __ZEPHYR__ */ + /** * Resets the buffer. * @param buffer Buffer to reset.