diff --git a/src/audio/buffer.c b/src/audio/buffer.c index 59b92d90b890..3cc9fae5008b 100644 --- a/src/audio/buffer.c +++ b/src/audio/buffer.c @@ -32,20 +32,12 @@ DECLARE_SOF_RT_UUID("buffer", buffer_uuid, 0x42544c92, 0x8e92, 0x4e41, 0xb6, 0x79, 0x34, 0x51, 0x9f, 0x1c, 0x1d, 0x28); DECLARE_TR_CTX(buffer_tr, SOF_UUID(buffer_uuid), LOG_LEVEL_INFO); -struct comp_buffer *buffer_alloc(uint32_t size, uint32_t caps, uint32_t flags, uint32_t align, - bool is_shared) +static struct comp_buffer *buffer_alloc_struct(void *stream_addr, size_t size, uint32_t caps, + uint32_t flags, bool is_shared) { struct comp_buffer *buffer; - void *stream_addr; - tr_dbg(&buffer_tr, "buffer_alloc()"); - - /* validate request */ - if (size == 0) { - tr_err(&buffer_tr, "buffer_alloc(): new size = %u is invalid", - size); - return NULL; - } + tr_dbg(&buffer_tr, "buffer_alloc_struct()"); /* allocate new buffer */ enum mem_zone zone = is_shared ? SOF_MEM_ZONE_RUNTIME_SHARED : SOF_MEM_ZONE_RUNTIME; @@ -53,24 +45,18 @@ struct comp_buffer *buffer_alloc(uint32_t size, uint32_t caps, uint32_t flags, u buffer = rzalloc(zone, 0, SOF_MEM_CAPS_RAM, sizeof(*buffer)); if (!buffer) { - tr_err(&buffer_tr, "buffer_alloc(): could not alloc structure"); + tr_err(&buffer_tr, "buffer_alloc_struct(): could not alloc structure"); return NULL; } CORE_CHECK_STRUCT_INIT(buffer, is_shared); buffer->is_shared = is_shared; - stream_addr = rballoc_align(0, caps, size, align); - if (!stream_addr) { - rfree(buffer); - tr_err(&buffer_tr, "buffer_alloc(): could not alloc size = %u bytes of type = %u", - size, caps); - return NULL; - } + buffer->caps = caps; /* From here no more uncached access to the buffer object, except its list headers */ audio_stream_set_addr(&buffer->stream, stream_addr); - buffer_init(buffer, size, caps); + buffer_init_stream(buffer, size); audio_stream_set_underrun(&buffer->stream, !!(flags & SOF_BUF_UNDERRUN_PERMITTED)); audio_stream_set_overrun(&buffer->stream, !!(flags & SOF_BUF_OVERRUN_PERMITTED)); @@ -81,6 +67,36 @@ struct comp_buffer *buffer_alloc(uint32_t size, uint32_t caps, uint32_t flags, u return buffer; } +struct comp_buffer *buffer_alloc(size_t size, uint32_t caps, uint32_t flags, uint32_t align, + bool is_shared) +{ + struct comp_buffer *buffer; + void *stream_addr; + + tr_dbg(&buffer_tr, "buffer_alloc()"); + + /* validate request */ + if (size == 0) { + tr_err(&buffer_tr, "buffer_alloc(): new size = %zu is invalid", size); + return NULL; + } + + stream_addr = rballoc_align(0, caps, size, align); + if (!stream_addr) { + tr_err(&buffer_tr, "buffer_alloc(): could not alloc size = %zu bytes of type = %u", + size, caps); + return NULL; + } + + buffer = buffer_alloc_struct(stream_addr, size, caps, flags, is_shared); + if (!buffer) { + tr_err(&buffer_tr, "buffer_alloc(): could not alloc buffer structure"); + rfree(stream_addr); + } + + return buffer; +} + #if CONFIG_ZEPHYR_DP_SCHEDULER int buffer_create_shadow_dp_queue(struct comp_buffer *buffer, bool at_input) { @@ -148,6 +164,49 @@ int buffer_sync_shadow_dp_queue(struct comp_buffer *buffer, size_t limit) } #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ +struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_size, uint32_t caps, + uint32_t flags, uint32_t align, bool is_shared) +{ + struct comp_buffer *buffer; + size_t size; + void *stream_addr = NULL; + + tr_dbg(&buffer_tr, "buffer_alloc_range(): %zu -- %zu bytes", minimum_size, preferred_size); + + /* validate request */ + if (minimum_size == 0 || preferred_size < minimum_size) { + tr_err(&buffer_tr, "buffer_alloc_range(): new size range %zu -- %zu is invalid", + minimum_size, preferred_size); + return NULL; + } + + /* Align preferred size to a multiple of the minimum size */ + if (preferred_size % minimum_size) + preferred_size += minimum_size - preferred_size % minimum_size; + + for (size = preferred_size; size >= minimum_size; size -= minimum_size) { + stream_addr = rballoc_align(0, caps, size, align); + if (stream_addr) + break; + } + + tr_dbg(&buffer_tr, "buffer_alloc_range(): allocated %zu bytes", size); + + if (!stream_addr) { + tr_err(&buffer_tr, "buffer_alloc_range(): could not alloc size = %zu bytes of type = %u", + minimum_size, caps); + return NULL; + } + + buffer = buffer_alloc_struct(stream_addr, size, caps, flags, is_shared); + if (!buffer) { + tr_err(&buffer_tr, "buffer_alloc_range(): could not alloc buffer structure"); + rfree(stream_addr); + } + + return buffer; +} + void buffer_zero(struct comp_buffer *buffer) { buf_dbg(buffer, "stream_zero()"); @@ -193,7 +252,64 @@ int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignmen if (new_ptr) buffer->stream.addr = new_ptr; - buffer_init(buffer, size, buffer->caps); + buffer_init_stream(buffer, size); + + return 0; +} + +int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, size_t minimum_size, + uint32_t alignment) +{ + void *ptr = audio_stream_get_addr(&buffer->stream); + const size_t actual_size = audio_stream_get_size(&buffer->stream); + void *new_ptr = NULL; + size_t new_size; + + CORE_CHECK_STRUCT(buffer); + + /* validate request */ + if (minimum_size == 0 || preferred_size < minimum_size) { + buf_err(buffer, "resize size range %zu -- %zu is invalid", minimum_size, + preferred_size); + return -EINVAL; + } + + /* Align preferred size to a multiple of the minimum size */ + if (preferred_size % minimum_size) + preferred_size += minimum_size - preferred_size % minimum_size; + + if (preferred_size == actual_size) + return 0; + + if (!alignment) { + for (new_size = preferred_size; new_size >= minimum_size; + new_size -= minimum_size) { + new_ptr = rbrealloc(ptr, SOF_MEM_FLAG_NO_COPY, buffer->caps, new_size, + actual_size); + if (new_ptr) + break; + } + } else { + for (new_size = preferred_size; new_size >= minimum_size; + new_size -= minimum_size) { + new_ptr = rbrealloc_align(ptr, SOF_MEM_FLAG_NO_COPY, buffer->caps, new_size, + actual_size, alignment); + if (new_ptr) + break; + } + } + + /* we couldn't allocate bigger chunk */ + if (!new_ptr && new_size > actual_size) { + buf_err(buffer, "resize can't alloc %zu bytes type %u", new_size, buffer->caps); + return -ENOMEM; + } + + /* use bigger chunk, else just use the old chunk but set smaller */ + if (new_ptr) + buffer->stream.addr = new_ptr; + + buffer_init_stream(buffer, new_size); return 0; } diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 872a1ee9bd61..48f20d589abc 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -797,6 +797,7 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, uint32_t period_count; uint32_t period_bytes; uint32_t buffer_size; + uint32_t buffer_size_preferred; uint32_t addr_align; uint32_t align; int err; @@ -873,21 +874,23 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, comp_err(dev, "dai_set_dma_buffer(): no valid dma buffer period count"); return -EINVAL; } - buffer_size = ALIGN_UP(period_count * period_bytes, align); - *pc = period_count; + period_count = MAX(period_count, + SOF_DIV_ROUND_UP(dd->ipc_config.dma_buffer_size, period_bytes)); + buffer_size_preferred = ALIGN_UP(period_count * period_bytes, align); /* alloc DMA buffer or change its size if exists */ if (dd->dma_buffer) { - err = buffer_set_size(dd->dma_buffer, buffer_size, addr_align); + err = buffer_set_size_range(dd->dma_buffer, buffer_size_preferred, buffer_size, + addr_align); if (err < 0) { comp_err(dev, "dai_set_dma_buffer(): buffer_size = %u failed", buffer_size); return err; } } else { - dd->dma_buffer = buffer_alloc(buffer_size, SOF_MEM_CAPS_DMA, 0, - addr_align, false); + dd->dma_buffer = buffer_alloc_range(buffer_size_preferred, buffer_size, + SOF_MEM_CAPS_DMA, 0, addr_align, false); if (!dd->dma_buffer) { comp_err(dev, "dai_set_dma_buffer(): failed to alloc dma buffer"); return -ENOMEM; @@ -904,6 +907,7 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, dd->sampling = get_sample_bytes(hw_params.frame_fmt); } + *pc = audio_stream_get_size(&dd->dma_buffer->stream) / period_bytes; dd->fast_mode = dd->ipc_config.feature_mask & BIT(IPC4_COPIER_FAST_MODE); return 0; } diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 6f9d00a32401..5b6ba474f4d7 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -748,6 +748,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, uint32_t period_count; uint32_t period_bytes; uint32_t buffer_size; + uint32_t buffer_size_preferred; uint32_t addr_align; uint32_t align; int i, channel, err; @@ -821,8 +822,9 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, /* calculate DMA buffer size */ round_up_size = (params->frame_fmt == SOF_IPC_FRAME_S24_3LE) ? (3 * align) : align; buffer_size = ROUND_UP(period_bytes, round_up_size) * period_count; + buffer_size_preferred = buffer_size; if (hd->ipc_host.dma_buffer_size != 0) - buffer_size = ROUND_UP(hd->ipc_host.dma_buffer_size, buffer_size); + buffer_size_preferred = ROUND_UP(hd->ipc_host.dma_buffer_size, buffer_size); /* alloc DMA buffer or change its size if exists */ /* @@ -830,7 +832,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, * but we have to write back caches after we finish anywae */ if (hd->dma_buffer) { - err = buffer_set_size(hd->dma_buffer, buffer_size, addr_align); + err = buffer_set_size_range(hd->dma_buffer, buffer_size_preferred, buffer_size, + addr_align); if (err < 0) { comp_err(dev, "host_params(): buffer_set_size() failed, buffer_size = %u", buffer_size); @@ -838,8 +841,8 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, } } else { /* allocate not shared buffer */ - hd->dma_buffer = buffer_alloc(buffer_size, SOF_MEM_CAPS_DMA, 0, - addr_align, false); + hd->dma_buffer = buffer_alloc_range(buffer_size_preferred, buffer_size, + SOF_MEM_CAPS_DMA, 0, addr_align, false); if (!hd->dma_buffer) { comp_err(dev, "host_params(): failed to alloc dma buffer"); return -ENOMEM; @@ -861,6 +864,7 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, config->dest_width = config->src_width; hd->dma_buffer_size = audio_stream_get_size(&hd->dma_buffer->stream); } + buffer_size = audio_stream_get_size(&hd->dma_buffer->stream); /* create SG DMA elems for local DMA buffer */ err = create_local_elems(hd, dev, period_count, buffer_size / period_count, diff --git a/src/include/sof/audio/buffer.h b/src/include/sof/audio/buffer.h index 662e329c1027..4d89f98beb20 100644 --- a/src/include/sof/audio/buffer.h +++ b/src/include/sof/audio/buffer.h @@ -188,8 +188,10 @@ struct buffer_cb_free { } while (0) /* pipeline buffer creation and destruction */ -struct comp_buffer *buffer_alloc(uint32_t size, uint32_t caps, uint32_t flags, uint32_t align, +struct comp_buffer *buffer_alloc(size_t size, uint32_t caps, uint32_t flags, uint32_t align, bool is_shared); +struct comp_buffer *buffer_alloc_range(size_t preferred_size, size_t minimum_size, uint32_t caps, + uint32_t flags, uint32_t align, bool is_shared); struct comp_buffer *buffer_new(const struct sof_ipc_buffer *desc, bool is_shared); #if CONFIG_ZEPHYR_DP_SCHEDULER /* @@ -226,6 +228,8 @@ int buffer_create_shadow_dp_queue(struct comp_buffer *buffer, bool at_input); int buffer_sync_shadow_dp_queue(struct comp_buffer *buffer, size_t limit); #endif /* CONFIG_ZEPHYR_DP_SCHEDULER */ int buffer_set_size(struct comp_buffer *buffer, uint32_t size, uint32_t alignment); +int buffer_set_size_range(struct comp_buffer *buffer, size_t preferred_size, size_t minimum_size, + uint32_t alignment); void buffer_free(struct comp_buffer *buffer); void buffer_zero(struct comp_buffer *buffer); @@ -286,11 +290,8 @@ static inline void buffer_reset_pos(struct comp_buffer *buffer, void *data) } /* Run-time buffer re-configuration calls this too, so it must use cached access */ -static inline void buffer_init(struct comp_buffer *buffer, - uint32_t size, uint32_t caps) +static inline void buffer_init_stream(struct comp_buffer *buffer, size_t size) { - buffer->caps = caps; - /* addr should be set in alloc function */ audio_stream_init(&buffer->stream, buffer->stream.addr, size); }