diff --git a/components/esp_driver_dma/test_apps/dma/main/test_gdma.c b/components/esp_driver_dma/test_apps/dma/main/test_gdma.c index 8f39f97e865..b08989b9b4b 100644 --- a/components/esp_driver_dma/test_apps/dma/main/test_gdma.c +++ b/components/esp_driver_dma/test_apps/dma/main/test_gdma.c @@ -922,12 +922,18 @@ TEST_CASE("GDMA interrupt priority configuration", "[GDMA]") TEST_ESP_OK(gdma_del_channel(rx_chan)); } -#if SOC_HAS(AHB_GDMA) -TEST_CASE("GDMA rejects invalid AHB burst sizes", "[GDMA]") +#if SOC_HAS(AHB_GDMA) || SOC_HAS(LP_AHB_GDMA) || SOC_HAS(AXI_GDMA) +typedef esp_err_t (*gdma_new_channel_func_t)(const gdma_channel_alloc_config_t *config, + gdma_channel_handle_t *ret_tx_chan, + gdma_channel_handle_t *ret_rx_chan); + +static void test_gdma_burst_size_validation(gdma_new_channel_func_t new_channel, + size_t unsupported_burst_size, + size_t invalid_psram_burst_size) { gdma_channel_handle_t tx_chan = NULL; gdma_channel_alloc_config_t channel_config = {}; - TEST_ESP_OK(gdma_new_ahb_channel(&channel_config, &tx_chan, NULL)); + TEST_ESP_OK(new_channel(&channel_config, &tx_chan, NULL)); gdma_transfer_config_t transfer_config = { .max_data_burst_size = 16, @@ -935,52 +941,60 @@ TEST_CASE("GDMA rejects invalid AHB burst sizes", "[GDMA]") }; TEST_ESP_OK(gdma_config_transfer(tx_chan, &transfer_config)); -#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE) - // Non power-of-two must be rejected when the burst size is programmable. transfer_config.max_data_burst_size = 3; TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); - // 8 is a power of two but is outside every current AHB supported-burst mask. - transfer_config.max_data_burst_size = 8; - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); + if (unsupported_burst_size) { + transfer_config.max_data_burst_size = unsupported_burst_size; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); + } -#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) - transfer_config.access_ext_mem = true; - transfer_config.max_data_burst_size = GDMA_LL_MAX_BURST_SIZE_PSRAM * 2; - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); -#endif // GDMA_LL_GET(AHB_PSRAM_CAPABLE) -#endif // GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE) + if (invalid_psram_burst_size) { + transfer_config.access_ext_mem = true; + transfer_config.max_data_burst_size = invalid_psram_burst_size; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); + } TEST_ESP_OK(gdma_del_channel(tx_chan)); } +#endif // SOC_HAS(AHB_GDMA) || SOC_HAS(LP_AHB_GDMA) || SOC_HAS(AXI_GDMA) + +#if SOC_HAS(AHB_GDMA) || SOC_HAS(LP_AHB_GDMA) +TEST_CASE("GDMA rejects invalid AHB burst sizes", "[GDMA]") +{ +#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE) + const size_t unsupported_burst_size = 8; +#else + const size_t unsupported_burst_size = 0; +#endif + +#if SOC_HAS(AHB_GDMA) +#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) + test_gdma_burst_size_validation(gdma_new_ahb_channel, unsupported_burst_size, + GDMA_LL_MAX_BURST_SIZE_PSRAM * 2); +#else + test_gdma_burst_size_validation(gdma_new_ahb_channel, unsupported_burst_size, 0); +#endif #endif // SOC_HAS(AHB_GDMA) +#if SOC_HAS(LP_AHB_GDMA) +#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE) + test_gdma_burst_size_validation(gdma_new_lp_ahb_channel, unsupported_burst_size, + GDMA_LL_MAX_BURST_SIZE_PSRAM * 2); +#else + test_gdma_burst_size_validation(gdma_new_lp_ahb_channel, unsupported_burst_size, 0); +#endif +#endif // SOC_HAS(LP_AHB_GDMA) +} +#endif // SOC_HAS(AHB_GDMA) || SOC_HAS(LP_AHB_GDMA) + #if SOC_HAS(AXI_GDMA) TEST_CASE("GDMA rejects invalid AXI burst sizes", "[GDMA]") { - gdma_channel_handle_t tx_chan = NULL; - gdma_channel_alloc_config_t channel_config = {}; - TEST_ESP_OK(gdma_new_axi_channel(&channel_config, &tx_chan, NULL)); - - gdma_transfer_config_t transfer_config = { - .max_data_burst_size = 16, - .access_ext_mem = false, - }; - TEST_ESP_OK(gdma_config_transfer(tx_chan, &transfer_config)); - - transfer_config.max_data_burst_size = 3; - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); - - // 4 is a power of two but is outside the AXI supported-burst mask. - transfer_config.max_data_burst_size = 4; - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); - #if GDMA_LL_GET(AXI_PSRAM_CAPABLE) - transfer_config.access_ext_mem = true; - transfer_config.max_data_burst_size = GDMA_LL_MAX_BURST_SIZE_PSRAM * 2; - TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config)); + test_gdma_burst_size_validation(gdma_new_axi_channel, 4, GDMA_LL_MAX_BURST_SIZE_PSRAM * 2); +#else + test_gdma_burst_size_validation(gdma_new_axi_channel, 4, 0); #endif // GDMA_LL_GET(AXI_PSRAM_CAPABLE) - - TEST_ESP_OK(gdma_del_channel(tx_chan)); } #endif // SOC_HAS(AXI_GDMA) diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 585902791ea..7fda0972c20 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -40,6 +40,9 @@ #if SOC_HAS(PAU) #include "esp_private/sleep_retention.h" #endif +#if SOC_GDMA_SUPPORTED +#include "hal/gdma_ll.h" +#endif #include "driver/gpio.h" #include "esp_private/gpio.h" @@ -65,38 +68,50 @@ #include "esp_memory_utils.h" #define I2S_DMA_BUFFER_MAX_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE +#define I2S_DMA_DEFAULT_BURST_SIZE 32 static const char *TAG = "i2s_common"; +static uint32_t i2s_resolve_dma_burst_size(size_t requested) +{ + uint32_t burst_size = (uint32_t)requested; + if (burst_size == 0) { + burst_size = I2S_DMA_DEFAULT_BURST_SIZE; + ESP_LOGD(TAG, "dma_burst_size is 0, using default %d", I2S_DMA_DEFAULT_BURST_SIZE); + } + +#if SOC_GDMA_SUPPORTED && !GDMA_LL_AHB_BURST_SIZE_ADJUSTABLE + if (requested != 0) { + ESP_LOGW(TAG, "chip does not support configurable DMA burst size, using default burst size instead"); + } + burst_size = I2S_DMA_DEFAULT_BURST_SIZE; +#endif + + return burst_size; +} + #if SOC_I2S_SUPPORTS_TX_FIFO_SYNC static void s_i2s_channel_update_tx_sync_callback(i2s_chan_handle_t tx_handle, i2s_tx_fifo_sync_callback_t cb, void *user_data); #endif -static inline void *i2s_dma_calloc(i2s_chan_handle_t handle, size_t num, size_t size) +FORCE_INLINE_ATTR void *i2s_dma_calloc(i2s_chan_handle_t handle, size_t num, size_t size) { uint32_t caps = handle->dma.buffer_in_psram ? (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT) : I2S_DMA_ALLOC_CAPS; return heap_caps_aligned_calloc(handle->dma.buf_alignment, num, size, caps); } -static inline void IRAM_ATTR i2s_dma_buf_sync_c2m(void *buf, size_t size) -{ - if (esp_cache_get_line_size_by_addr(buf) > 0) { - esp_cache_msync(buf, size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - } -} - -static inline void IRAM_ATTR i2s_dma_buf_sync_m2c(i2s_chan_handle_t handle, void *buf, size_t size) +FORCE_INLINE_ATTR void IRAM_ATTR i2s_dma_buf_sync(i2s_chan_handle_t handle, void *buf, size_t size, int flags) { #if CONFIG_SPIRAM - if (handle->dma.buffer_in_psram) { + if ((flags & ESP_CACHE_MSYNC_FLAG_DIR_M2C) && handle->dma.buffer_in_psram) { esp_psram_mspi_mb(); } #endif if (esp_cache_get_line_size_by_addr(buf) > 0) { - esp_cache_msync(buf, size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + esp_cache_msync(buf, size, flags); } } @@ -106,11 +121,8 @@ static void i2s_dma_bufs_sync_for_start(i2s_chan_handle_t handle) return; } for (int i = 0; i < handle->dma.desc_num; i++) { - void *buf = handle->dma.bufs[i]; - if (esp_cache_get_line_size_by_addr(buf) > 0) { - esp_cache_msync(buf, handle->dma.buf_size, - ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); - } + i2s_dma_buf_sync(handle, handle->dma.bufs[i], handle->dma.buf_size, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); } } @@ -729,7 +741,6 @@ static esp_err_t i2s_mount_dma_link(i2s_chan_handle_t handle, uint32_t bufsize) { esp_err_t ret = ESP_OK; uint32_t num = handle->dma.desc_num; - size_t buffer_alignment = handle->dma.buf_alignment; gdma_link_list_config_t link_config = { .num_items = num, .item_alignment = 4, @@ -870,7 +881,7 @@ static bool i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_ (void)dma_chan; (void)event_data; handle->dma.link_index = (finish_index + 1) % handle->dma.desc_num; - i2s_dma_buf_sync_m2c(handle, finish_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, finish_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); i2s_event_data_t evt = { .dma_buf = finish_buf, .size = handle->dma.buf_size, @@ -914,7 +925,7 @@ static bool i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_ } /* Sync buffer after the callback in case users update the buffer in the callback */ if (handle->dma.auto_clear_before_cb || handle->callbacks.on_sent) { - i2s_dma_buf_sync_c2m(curr_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, curr_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } if (xQueueIsQueueFullFromISR(handle->msg_queue)) { xQueueReceiveFromISR(handle->msg_queue, &dummy, &need_yield1); @@ -925,7 +936,7 @@ static bool i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_ } if (handle->dma.auto_clear_after_cb) { memset(curr_buf, 0, handle->dma.buf_size); - i2s_dma_buf_sync_c2m(curr_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, curr_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } xQueueSendFromISR(handle->msg_queue, &curr_buf, &need_yield2); @@ -953,7 +964,7 @@ static void i2s_dma_rx_callback(void *arg) uint32_t finish_index = handle->dma.link_index; void *finish_buf = gdma_link_get_buffer(handle->dma.dma_link, finish_index); handle->dma.link_index = (finish_index + 1) % handle->dma.desc_num; - i2s_dma_buf_sync_m2c(handle, finish_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, finish_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); evt.dma_buf = finish_buf; evt.size = handle->dma.buf_size; if (handle->callbacks.on_recv) { @@ -1004,7 +1015,7 @@ static void i2s_dma_tx_callback(void *arg) } /* Sync buffer after the callback in case users update the buffer in the callback */ if (handle->dma.auto_clear_before_cb || handle->callbacks.on_sent) { - i2s_dma_buf_sync_c2m(curr_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, curr_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } if (xQueueIsQueueFullFromISR(handle->msg_queue)) { xQueueReceiveFromISR(handle->msg_queue, &dummy, &need_yield1); @@ -1015,7 +1026,7 @@ static void i2s_dma_tx_callback(void *arg) // Auto clear the dma buffer after data sent if (handle->dma.auto_clear_after_cb) { memset(curr_buf, 0, handle->dma.buf_size); - i2s_dma_buf_sync_c2m(curr_buf, handle->dma.buf_size); + i2s_dma_buf_sync(handle, curr_buf, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } xQueueSendFromISR(handle->msg_queue, &curr_buf, &need_yield2); } @@ -1050,15 +1061,16 @@ esp_err_t i2s_prepare_dma(i2s_chan_handle_t handle) } gdma_transfer_config_t transfer_cfg = { - .max_data_burst_size = 0, + .max_data_burst_size = handle->dma.burst_size, .access_ext_mem = handle->dma.buffer_in_psram, }; ESP_GOTO_ON_ERROR(gdma_config_transfer(handle->dma.dma_chan, &transfer_cfg), err, TAG, "config dma transfer error"); - ESP_GOTO_ON_ERROR(gdma_get_alignment_constraints(handle->dma.dma_chan, - handle->dma.buffer_in_psram ? NULL : &handle->dma.buf_alignment, - handle->dma.buffer_in_psram ? &handle->dma.buf_alignment : NULL), - err, TAG, "get dma alignment constraints failed"); + gdma_channel_alignment_info_t alignment_info = {}; + ESP_GOTO_ON_ERROR(gdma_get_channel_alignment_constraints(handle->dma.dma_chan, &alignment_info), + err, TAG, "get DMA alignment constraints failed"); + handle->dma.buf_alignment = handle->dma.buffer_in_psram ? + alignment_info.ext_enc_mem_alignment : alignment_info.int_mem_alignment; #else // I2S dedicated DMA (ESP32 / ESP32-S2) if (handle->dma.buffer_in_psram) { #if I2S_LL_SUPPORT(DMA_EXT_MEM) @@ -1311,7 +1323,8 @@ esp_err_t i2s_check_set_mclk(i2s_chan_handle_t handle, int id, int gpio_num, i2s } static esp_err_t i2s_setup_channel(i2s_controller_t *i2s_obj, i2s_dir_t dir, - const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *ret_handle) + const i2s_chan_config_t *chan_cfg, uint32_t dma_burst_size, + i2s_chan_handle_t *ret_handle) { i2s_destination_t dest = (dir == I2S_DIR_TX) ? chan_cfg->tx_destination : chan_cfg->rx_destination; ESP_RETURN_ON_ERROR(i2s_register_channel(i2s_obj, dir, chan_cfg->dma_desc_num, dest == I2S_DESTINATION_DMA), @@ -1324,6 +1337,7 @@ static esp_err_t i2s_setup_channel(i2s_controller_t *i2s_obj, i2s_dir_t dir, chan->dma.buffer_in_psram = chan_cfg->dma_buffer_in_psram; chan->dma.desc_num = chan_cfg->dma_desc_num; chan->dma.frame_num = chan_cfg->dma_frame_num; + chan->dma.burst_size = dma_burst_size; chan->destination = dest; if (dir == I2S_DIR_TX) { chan->dma.auto_clear_after_cb = chan_cfg->auto_clear_after_cb; @@ -1365,6 +1379,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * (chan_cfg->auto_clear_before_cb || chan_cfg->auto_clear_after_cb)), ESP_ERR_NOT_SUPPORTED, TAG, "auto clear is not supported for PSRAM DMA buffers with IRAM-safe ISR"); #endif + uint32_t dma_burst_size = i2s_resolve_dma_burst_size(chan_cfg->dma_burst_size); #if !SOC_HAS(PAU) ESP_RETURN_ON_FALSE(!chan_cfg->allow_pd, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); #endif @@ -1418,11 +1433,11 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * } ESP_GOTO_ON_FALSE(channel_found, ESP_ERR_NOT_FOUND, err, TAG, "no available channel found"); if (tx_handle) { - ESP_GOTO_ON_ERROR(i2s_setup_channel(i2s_obj, I2S_DIR_TX, chan_cfg, tx_handle), + ESP_GOTO_ON_ERROR(i2s_setup_channel(i2s_obj, I2S_DIR_TX, chan_cfg, dma_burst_size, tx_handle), err, TAG, "register I2S tx channel failed"); } if (rx_handle) { - ESP_GOTO_ON_ERROR(i2s_setup_channel(i2s_obj, I2S_DIR_RX, chan_cfg, rx_handle), + ESP_GOTO_ON_ERROR(i2s_setup_channel(i2s_obj, I2S_DIR_RX, chan_cfg, dma_burst_size, rx_handle), err, TAG, "register I2S rx channel failed"); } @@ -1728,7 +1743,7 @@ esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, } /* Load the data from the last loaded position */ memcpy((uint8_t *)(tx_handle->dma.curr_ptr + tx_handle->dma.rw_pos), data_ptr, bytes_can_load); - i2s_dma_buf_sync_c2m(tx_handle->dma.curr_ptr, tx_handle->dma.buf_size); + i2s_dma_buf_sync(tx_handle, tx_handle->dma.curr_ptr, tx_handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); data_ptr += bytes_can_load; // Move forward the data pointer total_loaded_bytes += bytes_can_load; // Add to the total loaded bytes remain_bytes -= bytes_can_load; // Update the remaining bytes to be loaded @@ -1780,7 +1795,7 @@ esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t si bytes_can_write = size; } memcpy(data_ptr, src_byte, bytes_can_write); - i2s_dma_buf_sync_c2m(handle->dma.curr_ptr, handle->dma.buf_size); + i2s_dma_buf_sync(handle, handle->dma.curr_ptr, handle->dma.buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); size -= bytes_can_write; src_byte += bytes_can_write; handle->dma.rw_pos += bytes_can_write; diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 11954459034..b6a885a6b15 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -119,6 +119,7 @@ typedef struct { uint32_t link_index; /*!< Index of the next completed DMA link item */ uint32_t desc_num; /*!< I2S DMA buffer number, it is also the number of DMA descriptor */ uint32_t frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots */ + uint32_t burst_size; /*!< Resolved DMA data burst size in bytes */ uint32_t buf_size; /*!< dma buffer size */ size_t buf_alignment; /*!< DMA buffer alignment required for address and size */ bool buffer_in_psram; /*!< Whether the DMA buffers are allocated in PSRAM */ diff --git a/components/esp_driver_i2s/include/driver/i2s_common.h b/components/esp_driver_i2s/include/driver/i2s_common.h index 04d07bcc9af..e7fae39e919 100644 --- a/components/esp_driver_i2s/include/driver/i2s_common.h +++ b/components/esp_driver_i2s/include/driver/i2s_common.h @@ -24,6 +24,7 @@ extern "C" { .role = i2s_role, \ .dma_desc_num = 6, \ .dma_frame_num = 240, \ + .dma_burst_size = 0, \ .auto_clear_after_cb = false, \ .auto_clear_before_cb = false, \ .dma_buffer_in_psram = false, \ @@ -73,6 +74,10 @@ typedef struct { uint32_t dma_frame_num; /*!< I2S frame number in one DMA buffer. One frame means one-time sample data in all slots, * it should be the multiple of `3` when the data bit width is 24. */ + size_t dma_burst_size; /*!< DMA data burst size in bytes. Set to 0 to use driver default (32). + * When non-zero, must be a chip-supported power of 2 (see GDMA driver or chip TRM). + * Ignored on chips that do not support configurable burst size. + */ union { bool auto_clear; /*!< Alias of `auto_clear_after_cb` */ bool auto_clear_after_cb; /*!< Set to auto clear DMA TX buffer after `on_sent` callback, I2S will always send zero automatically if no data to send. diff --git a/components/esp_hal_dma/esp32s31/include/hal/ahb_dma_ll.h b/components/esp_hal_dma/esp32s31/include/hal/ahb_dma_ll.h index 080d5d81a12..a2da783d1eb 100644 --- a/components/esp_hal_dma/esp32s31/include/hal/ahb_dma_ll.h +++ b/components/esp_hal_dma/esp32s31/include/hal/ahb_dma_ll.h @@ -25,6 +25,11 @@ extern "C" { GDMA_BURST_SIZE_SUPPORT_32 | \ GDMA_BURST_SIZE_SUPPORT_64) +#define LP_AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \ + GDMA_BURST_SIZE_SUPPORT_16 | \ + GDMA_BURST_SIZE_SUPPORT_32 | \ + GDMA_BURST_SIZE_SUPPORT_64) + // any "dummy" peripheral ID can be used for M2M mode #define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x8200) #define AHB_DMA_LL_RX_EVENT_MASK (0x7F) diff --git a/components/esp_hal_dma/gdma_hal_ahb_v2.c b/components/esp_hal_dma/gdma_hal_ahb_v2.c index 4527b6b72b9..0d0706258b8 100644 --- a/components/esp_hal_dma/gdma_hal_ahb_v2.c +++ b/components/esp_hal_dma/gdma_hal_ahb_v2.c @@ -319,7 +319,7 @@ static gdma_hal_priv_data_t gdma_lp_ahb_hal_priv_data = { .tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK, .rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK, #if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE) - .supported_burst_size_mask = AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK, + .supported_burst_size_mask = LP_AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK, #endif }; diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index c1c49be3a0a..374f3bf6282 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -291,6 +291,8 @@ The data transport of the I2S peripheral, including sending and receiving, is re Both :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` are blocking functions. They keeps waiting until the whole source buffer is sent or the whole destination buffer is loaded, unless they exceed the max blocking time, where the error code ``ESP_ERR_TIMEOUT`` returns. To send or receive data asynchronously, callbacks can be registered by :cpp:func:`i2s_channel_register_event_callback`. Users are able to access the DMA buffer directly in the callback function instead of transmitting or receiving by the two blocking functions. However, please be aware that it is an interrupt callback, so do not add complex logic, run floating operation, or call non-reentrant functions in the callback. +:cpp:member:`i2s_chan_config_t::dma_burst_size` sets the DMA burst size in bytes. Set it to ``0`` (the value used by :c:macro:`I2S_CHANNEL_DEFAULT_CONFIG`) to apply the driver default of 32 bytes. A non-zero value must be a power of 2 supported by the chip GDMA. The field is ignored on chips that do not support configurable DMA burst size. + .. only:: SOC_PSRAM_DMA_CAPABLE To reduce internal RAM usage, set :cpp:member:`i2s_chan_config_t::dma_buffer_in_psram` to allocate the driver-owned DMA buffers in PSRAM. DMA descriptors remain in internal RAM. The driver returns an error if PSRAM DMA is unavailable or the allocation fails; it does not fall back to internal RAM. When :ref:`CONFIG_I2S_ISR_IRAM_SAFE` is enabled, TX auto-clear cannot be used with PSRAM DMA buffers, and callbacks must not access the DMA buffer while the cache is disabled.