diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index 1fe81e11cb..b69b8e8405 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c @@ -237,7 +237,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s .length = length_bytes_in, .flags = { .mark_eof = true, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; gdma_link_mount_buffers(bsl->tx_link_list, 0, &in_buf_mount_config, 1, NULL); @@ -247,7 +247,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s .length = length_bytes_out, .flags = { .mark_eof = false, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; gdma_link_mount_buffers(bsl->rx_link_list, 0, &out_buf_mount_config, 1, NULL); diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 820e3d1065..b1da9d1cae 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -371,6 +371,40 @@ err: return ret; } +#if SOC_I2S_HW_VERSION_1 +esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id) +{ + I2S_NULL_POINTER_CHECK(TAG, handle); + ESP_RETURN_ON_FALSE(id >= 0 && id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid I2S port id"); + if (id == handle->controller->id) { + return ESP_OK; + } + i2s_controller_t *i2s_obj = i2s_acquire_controller_obj(id); + if (!i2s_obj || !i2s_take_available_channel(i2s_obj, handle->dir)) { + return ESP_ERR_NOT_FOUND; + } + i2s_controller_t *old_i2s_obj = handle->controller; + portENTER_CRITICAL(&g_i2s.spinlock); + if (handle->dir == I2S_DIR_TX) { + i2s_obj->tx_chan = handle; + i2s_obj->chan_occupancy |= I2S_DIR_TX; + old_i2s_obj->tx_chan = NULL; + old_i2s_obj->full_duplex = false; + old_i2s_obj->chan_occupancy &= ~I2S_DIR_TX; + } else { + i2s_obj->rx_chan = handle; + i2s_obj->chan_occupancy |= I2S_DIR_RX; + old_i2s_obj->rx_chan = NULL; + old_i2s_obj->full_duplex = false; + old_i2s_obj->chan_occupancy &= ~I2S_DIR_RX; + } + handle->controller = i2s_obj; + portEXIT_CRITICAL(&g_i2s.spinlock); + + return ESP_OK; +} +#endif + #ifndef __cplusplus /* To make sure the i2s_event_callbacks_t is same size as i2s_event_callbacks_internal_t */ _Static_assert(sizeof(i2s_event_callbacks_t) == sizeof(i2s_event_callbacks_internal_t), "Invalid size of i2s_event_callbacks_t structure"); @@ -417,6 +451,9 @@ uint32_t i2s_get_buf_size(i2s_chan_handle_t handle, uint32_t data_bit_width, uin uint32_t bytes_per_sample = (data_bit_width + 7) / 8; #endif // CONFIG_IDF_TARGET_ESP32 uint32_t bytes_per_frame = bytes_per_sample * active_chan; + if (bytes_per_frame == 0) { + return 0; + } uint32_t bufsize = dma_frame_num * bytes_per_frame; #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE /* bufsize need to align with cache line size */ @@ -998,6 +1035,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_TX, chan_cfg->dma_desc_num), err, TAG, "register I2S tx channel failed"); i2s_obj->tx_chan->role = chan_cfg->role; + i2s_obj->tx_chan->is_port_auto = id == I2S_NUM_AUTO; i2s_obj->tx_chan->intr_prio_flags = chan_cfg->intr_priority ? BIT(chan_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED; i2s_obj->tx_chan->dma.auto_clear_after_cb = chan_cfg->auto_clear_after_cb; i2s_obj->tx_chan->dma.auto_clear_before_cb = chan_cfg->auto_clear_before_cb; @@ -1013,6 +1051,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_RX, chan_cfg->dma_desc_num), err, TAG, "register I2S rx channel failed"); i2s_obj->rx_chan->role = chan_cfg->role; + i2s_obj->rx_chan->is_port_auto = id == I2S_NUM_AUTO; i2s_obj->rx_chan->intr_prio_flags = chan_cfg->intr_priority ? BIT(chan_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED; i2s_obj->rx_chan->dma.desc_num = chan_cfg->dma_desc_num; i2s_obj->rx_chan->dma.frame_num = chan_cfg->dma_frame_num; diff --git a/components/esp_driver_i2s/i2s_pdm.c b/components/esp_driver_i2s/i2s_pdm.c index 760c2838fc..280518418c 100644 --- a/components/esp_driver_i2s/i2s_pdm.c +++ b/components/esp_driver_i2s/i2s_pdm.c @@ -95,11 +95,14 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx #endif portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_tx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_tx_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -119,6 +122,7 @@ static esp_err_t i2s_pdm_tx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_ handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -422,11 +426,14 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_rx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_rx_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); @@ -445,6 +452,7 @@ static esp_err_t i2s_pdm_rx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_ handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index b734f650d8..fdd697fcf8 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -160,6 +160,7 @@ struct i2s_channel_obj_t { int intr_prio_flags;/*!< i2s interrupt priority flags */ void *mode_info; /*!< Slot, clock and gpio information of each mode */ struct { + bool is_port_auto: 1; /*!< Whether the port is auto-assigned */ bool is_etm_start: 1; /*!< Whether start by etm tasks */ bool is_etm_stop: 1; /*!< Whether stop by etm tasks */ bool is_raw_pdm: 1; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */ @@ -340,6 +341,19 @@ void i2s_output_gpio_reserve(i2s_chan_handle_t handle, int gpio_num); */ void i2s_output_gpio_revoke(i2s_chan_handle_t handle, uint64_t gpio_mask); +#if SOC_I2S_HW_VERSION_1 +/** + * @brief Change the port of the I2S channel + * + * @param handle I2S channel handle + * @param id I2S port id + * @return + * - ESP_OK Change port success + * - ESP_ERR_NOT_FOUND No available I2S port found + */ +esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id); +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i2s/i2s_std.c b/components/esp_driver_i2s/i2s_std.c index 701b6df4ee..9f8ce6abce 100644 --- a/components/esp_driver_i2s/i2s_std.c +++ b/components/esp_driver_i2s/i2s_std.c @@ -96,12 +96,14 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(std_cfg->clk_cfg), clk_cfg, sizeof(i2s_std_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -117,6 +119,7 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -248,13 +251,34 @@ static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t ha if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) { handle->controller->full_duplex = true; ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id); - } + } else { #if SOC_I2S_HW_VERSION_1 - else { - ESP_LOGE(TAG, "Can't set different channel configurations on a same port"); - return ESP_ERR_INVALID_ARG; - } + bool port_changed = false; + if (handle->is_port_auto) { + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); + for (int i = 0; i < SOC_I2S_NUM; i++) { + if (i == handle->controller->id) { + continue; + } + ESP_LOGD(TAG, "Trying to move %s channel from port %d to %d", + handle->dir == I2S_DIR_TX ? "TX" : "RX", handle->controller->id, i); + if (i2s_channel_change_port(handle, i) == ESP_OK) { + ESP_LOGD(TAG, "Move success!"); + port_changed = true; + break; + } else { + ESP_LOGD(TAG, "Move failed..."); + } + } + } + if (!port_changed) { + ESP_LOGE(TAG, "Can't set different channel configurations on a same port"); + return ESP_ERR_INVALID_ARG; + } +#else + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); #endif + } } /* Switch to the slave role if needed */ if (handle->controller->full_duplex && diff --git a/components/esp_driver_i2s/i2s_tdm.c b/components/esp_driver_i2s/i2s_tdm.c index 1b81620494..b6ce716a2e 100644 --- a/components/esp_driver_i2s/i2s_tdm.c +++ b/components/esp_driver_i2s/i2s_tdm.c @@ -93,12 +93,14 @@ static esp_err_t i2s_tdm_set_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_c } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -121,6 +123,7 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c "total slots(%"PRIu32") * slot_bit_width(%"PRIu32") exceeds the maximum %d", handle->total_slot, slot_bits, (int)I2S_LL_SLOT_FRAME_BIT_MAX); uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -252,6 +255,8 @@ static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle, if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_tdm_config_t)) == 0) { handle->controller->full_duplex = true; ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id); + } else { + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); } } /* Switch to the slave role if needed */ diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c index e8ede206bb..eb44a62e7f 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c @@ -238,6 +238,8 @@ TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]") } static volatile bool task_run_flag; +static volatile bool read_task_success = true; +static volatile bool write_task_success = true; #define TEST_I2S_DATA 0x78 @@ -270,6 +272,7 @@ static void i2s_read_task(void *args) ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300); if (ret == ESP_ERR_TIMEOUT) { printf("Read timeout count: %"PRIu32"\n", cnt++); + read_task_success = false; } } @@ -291,6 +294,7 @@ static void i2s_write_task(void *args) ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300); if (ret == ESP_ERR_TIMEOUT) { printf("Write timeout count: %"PRIu32"\n", cnt++); + write_task_success = false; } } @@ -431,6 +435,7 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") }, }, }; + /* Part 1: test common lazy duplex mode */ TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL)); TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); TEST_ESP_OK(i2s_channel_enable(tx_handle)); @@ -451,7 +456,7 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") xTaskCreate(i2s_read_check_task, "i2s_read_check_task", 4096, rx_handle, 5, NULL); printf("RX started\n"); - /* Wait 3 seconds to see if any failures occur */ + /* Wait 1 seconds to see if any failures occur */ vTaskDelay(pdMS_TO_TICKS(1000)); printf("Finished\n"); @@ -467,6 +472,72 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") /* Delete the channels */ TEST_ESP_OK(i2s_del_channel(tx_handle)); TEST_ESP_OK(i2s_del_channel(rx_handle)); + + /* Part 2: Test no lazy duplex mode with port auto assignment */ + chan_cfg.id = I2S_NUM_AUTO; + TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle)); + TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL)); + TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); + + /* Change the config to not constitute full-duplex */ + std_cfg.gpio_cfg.mclk = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.bclk = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.ws = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.din = I2S_GPIO_UNUSED; +#if CONFIG_IDF_TARGET_ESP32S2 + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, i2s_channel_init_std_mode(tx_handle, &std_cfg)); + /* Delete the channels */ + TEST_ESP_OK(i2s_del_channel(tx_handle)); + TEST_ESP_OK(i2s_del_channel(rx_handle)); + return; +#else + TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); +#endif + +#if CONFIG_IDF_TARGET_ESP32 + /* On ESP32, if failed to constitute full-duplex with `I2S_NUM_AUTO`, + the channel will be re-assigned to the next availableport */ + i2s_chan_info_t chan_info; + TEST_ESP_OK(i2s_channel_get_info(rx_handle, &chan_info)); + TEST_ASSERT(chan_info.id == I2S_NUM_0); + TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info)); + TEST_ASSERT(chan_info.id == I2S_NUM_1); +#endif + + TEST_ESP_OK(i2s_channel_enable(tx_handle)); + TEST_ESP_OK(i2s_channel_enable(rx_handle)); + + task_run_flag = true; + read_task_success = true; + write_task_success = true; + /* writing task to keep writing */ + xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL); + printf("TX started\n"); + vTaskDelay(pdMS_TO_TICKS(1000)); + /* reading task to keep reading */ + xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL); + printf("RX started\n"); + + /* Wait 1 seconds to see if any failures occur */ + vTaskDelay(pdMS_TO_TICKS(1000)); + printf("Finished\n"); + + /* Stop those three tasks */ + task_run_flag = false; + + /* Wait for the three thread deleted */ + vTaskDelay(pdMS_TO_TICKS(1000)); + + /* Disable the channels, they will keep waiting until the current reading / writing finished */ + TEST_ESP_OK(i2s_channel_disable(tx_handle)); + TEST_ESP_OK(i2s_channel_disable(rx_handle)); + /* Delete the channels */ + TEST_ESP_OK(i2s_del_channel(tx_handle)); + TEST_ESP_OK(i2s_del_channel(rx_handle)); + /* Check if the reading and writing tasks are successful */ + TEST_ASSERT(read_task_success); + TEST_ASSERT(write_task_success); } static bool whether_contains_exapected_data(uint16_t *src, uint32_t src_len, uint32_t src_step, uint32_t start_val, uint32_t val_step) diff --git a/components/esp_driver_parlio/include/driver/parlio_rx.h b/components/esp_driver_parlio/include/driver/parlio_rx.h index 9774d871b4..bb0ac5c0d0 100644 --- a/components/esp_driver_parlio/include/driver/parlio_rx.h +++ b/components/esp_driver_parlio/include/driver/parlio_rx.h @@ -22,6 +22,7 @@ extern "C" { typedef struct { size_t trans_queue_depth; /*!< Depth of internal transaction queue */ size_t max_recv_size; /*!< Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */ + size_t dma_burst_size; /*!< DMA burst size, in bytes */ size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't be greater than PARLIO_RX_UNIT_MAX_DATA_WIDTH */ parlio_clock_source_t clk_src; /*!< Parallel IO clock source */ uint32_t ext_clk_freq_hz; /*!< The external source clock frequency. Only be valid when select PARLIO_CLK_SRC_EXTERNAL as clock source */ diff --git a/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h new file mode 100644 index 0000000000..9e74c61368 --- /dev/null +++ b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/parlio_rx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Trigger the fake EOF interrupt + * @note This function is a workaround for the case that level delimiter needs to receive more than 64KB data in one transaction. + * The hardware can't generate the EOF interrupt when the data length is greater than 64KB due to the limitation of the hardware, + * so this function is used to trigger the fake EOF interrupt. + * @note This function will reset the whole parlio module, + * If the pair tx unit is in using, + * the reset operation will affect the TX unit and lead to unknown behavior + * @usage If the application needs to receive more than 64KB data in one transaction, you can follow the steps below: + * 1. Create a level delimiter with a length greater than 64KB + * 2. Register the interrupt of the end edge on the valid GPIO + * 3. Call this function to trigger the fake EOF interrupt in the GPIO interrupt handler + * 4. Receive the transaction that is greater than 64KB + * + * @param rx_unit Parallel IO RX unit that created by `parlio_new_rx_unit` + * @param need_yield Pointer to a status flag to record whether a task switch is needed if this API is being called in an ISR + * @return + * - ESP_OK: Trigger the fake EOF interrupt successfully + * - ESP_ERR_INVALID_ARG: Invalid argument like NULL pointer + * - ESP_ERR_INVALID_STATE: Tx unit is in using, can't be called when pair tx unit is in using + */ +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_parlio/include/esp_private/parlio_private.h b/components/esp_driver_parlio/include/esp_private/parlio_tx_private.h similarity index 100% rename from components/esp_driver_parlio/include/esp_private/parlio_private.h rename to components/esp_driver_parlio/include/esp_private/parlio_tx_private.h diff --git a/components/esp_driver_parlio/linker.lf b/components/esp_driver_parlio/linker.lf index 76b5b7f0cf..3c61d44c7f 100644 --- a/components/esp_driver_parlio/linker.lf +++ b/components/esp_driver_parlio/linker.lf @@ -25,3 +25,15 @@ entries: if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y: gdma_link: gdma_link_get_buffer (noflash) + if PARLIO_RX_ISR_HANDLER_IN_IRAM = y: + gdma_link: gdma_link_mount_buffers (noflash) + gdma_link: gdma_link_get_buffer (noflash) + gdma_link: gdma_link_get_length (noflash) + esp_dma_utils: esp_dma_split_rx_buffer_to_cache_aligned (noflash) + esp_dma_utils: esp_dma_merge_aligned_rx_buffers (noflash) + +[mapping:parlio_driver_soc_periph] +archive: libsoc.a +entries: + if PARLIO_RX_ISR_HANDLER_IN_IRAM = y: + parlio_periph: parlio_periph_signals (noflash) diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index b413cc21f4..3dcd2f1554 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -19,12 +19,6 @@ #define PARLIO_MAX_ALIGNED_DMA_BUF_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED #endif -#if defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AHB) -typedef dma_descriptor_align4_t parlio_dma_desc_t; -#elif defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI) -typedef dma_descriptor_align8_t parlio_dma_desc_t; -#endif - #define PARLIO_DMA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA) /** @@ -32,13 +26,14 @@ typedef dma_descriptor_align8_t parlio_dma_desc_t; */ typedef struct { parlio_rx_delimiter_handle_t delimiter; /*!< Delimiter of this transaction */ - void *payload; /*!< The payload of this transaction, will be mounted to DMA descriptor */ - size_t size; /*!< The payload size in byte */ + dma_buffer_split_array_t aligned_payload; /*!< The aligned payload of this transaction, will be mounted to DMA link list node */ + size_t tot_trans_size; /*!< The total size of the transaction */ size_t recv_bytes; /*!< The received bytes of this transaction will be reset when all data filled in the infinite transaction */ + size_t alignment; /*!< The alignment of the payload buffer */ struct { uint32_t infinite : 1; /*!< Whether this is an infinite transaction */ - uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the descriptor indirectly via an internal DMA buffer */ + uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the link list node indirectly via an internal DMA buffer */ } flags; } parlio_rx_transaction_t; @@ -70,13 +65,18 @@ typedef struct parlio_rx_unit_t { /* DMA Resources */ gdma_channel_handle_t dma_chan; /*!< DMA channel */ size_t max_recv_size; /*!< Maximum receive size for a normal transaction */ - size_t desc_num; /*!< DMA descriptor number */ - size_t desc_size; /*!< DMA descriptors total size */ - parlio_dma_desc_t **dma_descs; /*!< DMA descriptor array pointer */ - parlio_dma_desc_t *curr_desc; /*!< The pointer of the current descriptor */ - void *usr_recv_buf; /*!< The pointe to the user's receiving buffer */ + size_t dma_burst_size; /*!< DMA burst size, in bytes */ + gdma_link_list_handle_t dma_link; /*!< DMA link list handle */ + uint32_t node_num; /*!< The number of nodes in the DMA link list */ + size_t int_mem_align; /*!< Alignment for internal memory */ + size_t ext_mem_align; /*!< Alignment for external memory */ + uint32_t curr_node_id; /*!< The index of the current node in the DMA link list */ + void *usr_recv_buf; /*!< The point to the user's receiving buffer */ /* Infinite transaction specific */ void *dma_buf; /*!< Additional internal DMA buffer only for infinite transactions */ + /* Unaligned DMA buffer management */ + uint8_t *stash_buf[2]; /*!< The ping-pong stash buffer for unaligned DMA buffer */ + uint8_t stash_buf_idx; /*!< The index of the current stash buffer */ /* Callback */ parlio_rx_event_callbacks_t cbs; /*!< The group of callback function pointers */ @@ -116,13 +116,13 @@ typedef struct parlio_rx_delimiter_t { } flags; } parlio_rx_delimiter_t; -#define PRALIO_RX_MOUNT_SIZE_CALC(total_size, div, align) ((((total_size) / (align)) / (div)) * (align)) +#define PARLIO_RX_MOUNT_SIZE_CALC(total_size, div, align) ((((total_size) / (align)) / (div)) * (align)) +#define PARLIO_RX_CHECK_ISR(condition, err) if (!(condition)) { return err; } static portMUX_TYPE s_rx_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans) { - parlio_dma_desc_t **p_desc = rx_unit->dma_descs; /* Update the current transaction to the next one, and declare the delimiter is under using of the rx unit */ memcpy(&rx_unit->curr_trans, trans, sizeof(parlio_rx_transaction_t)); portENTER_CRITICAL_SAFE(&s_rx_spinlock); @@ -131,50 +131,66 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli } portEXIT_CRITICAL_SAFE(&s_rx_spinlock); - uint32_t desc_num = trans->size / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - uint32_t remain_num = trans->size % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - /* If there are still data remained, need one more descriptor */ - desc_num += remain_num ? 1 : 0; - if (trans->flags.infinite && desc_num < 2) { - /* At least 2 descriptors needed */ - desc_num = 2; + /* Calculate the number of nodes needed for the transaction */ + uint32_t body_node_num = trans->aligned_payload.buf.body.length / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + uint32_t body_remain_size = trans->aligned_payload.buf.body.length % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + /* If there are still data remained, need one more node */ + body_node_num += body_remain_size ? 1 : 0; + + uint32_t head_node_num = trans->aligned_payload.buf.head.length ? 1 : 0; + uint32_t tail_node_num = trans->aligned_payload.buf.tail.length ? 1 : 0; + uint32_t required_node_num = body_node_num + head_node_num + tail_node_num; + + if (trans->flags.infinite && required_node_num < 2) { + /* At least 2 nodes needed */ + required_node_num = 2; } + rx_unit->node_num = required_node_num; + + gdma_buffer_mount_config_t mount_config[required_node_num] = {}; + /* Mount head buffer */ + if (head_node_num) { + mount_config[0].buffer = trans->aligned_payload.buf.head.aligned_buffer; + mount_config[0].buffer_alignment = trans->alignment; + mount_config[0].length = trans->aligned_payload.buf.head.length; + mount_config[0].flags.bypass_buffer_align_check = false; + mount_config[0].flags.mark_eof = false; + mount_config[0].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; + } + /* Mount body buffer */ size_t mount_size = 0; size_t offset = 0; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - uint32_t alignment = rx_unit->base.group->dma_align; -#else - uint32_t alignment = 4; -#endif - /* Loop the descriptors to assign the data */ - for (int i = 0; i < desc_num; i++) { - size_t rest_size = trans->size - offset; - + size_t rest_size = trans->aligned_payload.buf.body.length; + for (int i = head_node_num; i < required_node_num - tail_node_num; i++) { if (rest_size >= 2 * PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = PRALIO_RX_MOUNT_SIZE_CALC(trans->size, desc_num, alignment); + mount_size = PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; } else if (rest_size <= PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = (desc_num == 2) && (i == 0) ? PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment) : rest_size; + mount_size = ((required_node_num - tail_node_num) == 2) && (i == 0) ? PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment) : rest_size; } else { - mount_size = PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment); - } - p_desc[i]->buffer = (void *)((uint8_t *)trans->payload + offset); - p_desc[i]->dw0.size = mount_size; - p_desc[i]->dw0.length = mount_size; - p_desc[i]->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - // Link the descriptor - if (i < desc_num - 1) { - p_desc[i]->next = p_desc[i + 1]; - } else { - /* For infinite transaction, link the descriptor as a ring */ - p_desc[i]->next = trans->flags.infinite ? p_desc[0] : NULL; + mount_size = PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment); } + mount_config[i].buffer = (void *)((uint8_t *)trans->aligned_payload.buf.body.aligned_buffer + offset); + mount_config[i].buffer_alignment = trans->alignment; + mount_config[i].length = mount_size; + mount_config[i].flags.bypass_buffer_align_check = false; + mount_config[i].flags.mark_eof = false; + mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; offset += mount_size; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(p_desc[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif + rest_size -= mount_size; } + /* Mount tail buffer */ + if (tail_node_num) { + mount_config[required_node_num - 1].buffer = trans->aligned_payload.buf.tail.aligned_buffer; + mount_config[required_node_num - 1].buffer_alignment = trans->alignment; + mount_config[required_node_num - 1].length = trans->aligned_payload.buf.tail.length; + mount_config[required_node_num - 1].flags.bypass_buffer_align_check = false; + } + /* For infinite transaction, link the node as a ring */ + mount_config[required_node_num - 1].flags.mark_final = !trans->flags.infinite ? GDMA_FINAL_LINK_TO_NULL : GDMA_FINAL_LINK_TO_HEAD; + mount_config[required_node_num - 1].flags.mark_eof = true; + gdma_link_mount_buffers(rx_unit->dma_link, 0, mount_config, required_node_num, NULL); /* Reset the current DMA node */ - rx_unit->curr_desc = p_desc[0]; + rx_unit->curr_node_id = 0; return offset; } @@ -324,6 +340,7 @@ static bool parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_ need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data); } } else { + esp_dma_merge_aligned_rx_buffers(&rx_unit->curr_trans.aligned_payload); /* If received a normal EOF, it's a receive done event on parlio RX */ if (rx_unit->cbs.on_receive_done) { evt_data.data = rx_unit->usr_recv_buf; @@ -350,7 +367,7 @@ static bool parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_ } /* Mount the new transaction buffer and start the new transaction */ parlio_rx_mount_transaction_buffer(rx_unit, &next_trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->dma_descs[0]); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -381,20 +398,28 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, return false; } - /* Get the finished descriptor from the current descriptor */ - parlio_dma_desc_t *finished_desc = rx_unit->curr_desc; + /* Get the finished node from the current node */ + void *finished_buffer = gdma_link_get_buffer(rx_unit->dma_link, rx_unit->curr_node_id); + size_t finished_length = gdma_link_get_length(rx_unit->dma_link, rx_unit->curr_node_id); #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE esp_err_t ret = ESP_OK; - ret |= esp_cache_msync((void *)finished_desc, rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ret |= esp_cache_msync((void *)(finished_desc->buffer), finished_desc->dw0.size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + size_t sync_size = finished_length; + /* The sych length should be the cache line size for the un-aligned head and tail part */ + for (int i = 0; i < 2; i++) { + if (finished_buffer == rx_unit->stash_buf[i]) { + sync_size = rx_unit->int_mem_align; + break; + } + } + ret = esp_cache_msync(finished_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); if (ret != ESP_OK) { ESP_EARLY_LOGW(TAG, "failed to sync dma buffer from memory to cache"); } #endif parlio_rx_event_data_t evt_data = { .delimiter = rx_unit->curr_trans.delimiter, - .data = finished_desc->buffer, - .recv_bytes = finished_desc->dw0.length, + .data = finished_buffer, + .recv_bytes = finished_length, }; if (rx_unit->cbs.on_partial_receive) { need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data); @@ -404,61 +429,43 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes); } else { portENTER_CRITICAL_ISR(&s_rx_spinlock); - rx_unit->curr_trans.delimiter->under_using = false; + if (rx_unit->curr_trans.delimiter) { + rx_unit->curr_trans.delimiter->under_using = false; + } portEXIT_CRITICAL_ISR(&s_rx_spinlock); } /* Update received bytes */ - if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.size) { + if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.tot_trans_size) { rx_unit->curr_trans.recv_bytes = 0; } rx_unit->curr_trans.recv_bytes += evt_data.recv_bytes; - /* Move to the next DMA descriptor */ - rx_unit->curr_desc = rx_unit->curr_desc->next; + /* Move to the next DMA node */ + rx_unit->curr_node_id++; + rx_unit->curr_node_id %= rx_unit->node_num; return need_yield; } -static esp_err_t parlio_rx_create_dma_descriptors(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) +static esp_err_t parlio_rx_create_dma_link(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) { ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid param"); esp_err_t ret = ESP_OK; - uint32_t desc_num = max_recv_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + 1; - /* set at least 2 descriptors */ - if (desc_num < 2) { - desc_num = 2; - } - rx_unit->desc_num = desc_num; - /* Allocated and link the descriptor nodes */ - rx_unit->dma_descs = heap_caps_calloc(desc_num, sizeof(parlio_dma_desc_t *), MALLOC_CAP_DMA); - ESP_RETURN_ON_FALSE(rx_unit->dma_descs, ESP_ERR_NO_MEM, TAG, "no memory for DMA descriptor array"); - uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - size_t alignment = MAX(cache_line_size, PARLIO_DMA_DESC_ALIGNMENT); - rx_unit->desc_size = ALIGN_UP(sizeof(parlio_dma_desc_t), alignment); - for (int i = 0; i < desc_num; i++) { - rx_unit->dma_descs[i] = heap_caps_aligned_calloc(alignment, 1, rx_unit->desc_size, PARLIO_DMA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(rx_unit->dma_descs[i], ESP_ERR_NO_MEM, err, TAG, "no memory for DMA descriptors"); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(rx_unit->dma_descs[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif - } + // calculated the total node number, add 2 for the aligned stash buffer + size_t tot_node_num = esp_dma_calculate_node_count(max_recv_size, rx_unit->int_mem_align, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) + 2; + gdma_link_list_config_t dma_link_config = { + .num_items = tot_node_num, + .item_alignment = PARLIO_DMA_DESC_ALIGNMENT, + }; + + // create DMA link list, throw the error to the caller if failed + ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &rx_unit->dma_link), TAG, "create DMA link list failed"); rx_unit->max_recv_size = max_recv_size; - - return ret; -err: - for (int i = 0; i < desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; return ret; } -static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit) +static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t dma_burst_size) { /* Allocate and connect the GDMA channel */ gdma_channel_alloc_config_t dma_chan_config = { @@ -477,6 +484,21 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit) }; gdma_apply_strategy(rx_unit->dma_chan, &gdma_strategy_conf); + // configure DMA transfer parameters + rx_unit->dma_burst_size = dma_burst_size ? dma_burst_size : 16; + gdma_transfer_config_t trans_cfg = { + .max_data_burst_size = rx_unit->dma_burst_size, // Enable DMA burst transfer for better performance, + .access_ext_mem = true, + }; + ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); + ESP_RETURN_ON_ERROR(gdma_get_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align), TAG, "get alignment constraints failed"); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + rx_unit->int_mem_align = rx_unit->int_mem_align > cache_line_size ? rx_unit->int_mem_align : cache_line_size; +#endif + uint32_t ext_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + rx_unit->ext_mem_align = rx_unit->ext_mem_align > ext_cache_line_size ? rx_unit->ext_mem_align : ext_cache_line_size; + /* Register callbacks */ gdma_rx_event_callbacks_t cbs = { .on_recv_eof = parlio_rx_default_eof_callback, @@ -575,21 +597,22 @@ static esp_err_t parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit) ESP_RETURN_ON_ERROR(gdma_disconnect(rx_unit->dma_chan), TAG, "disconnect dma channel failed"); ESP_RETURN_ON_ERROR(gdma_del_channel(rx_unit->dma_chan), TAG, "delete dma channel failed"); } - /* Free the DMA descriptors */ - if (rx_unit->dma_descs) { - for (int i = 0; i < rx_unit->desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; + /* Free the DMA link list */ + if (rx_unit->dma_link) { + gdma_del_link_list(rx_unit->dma_link); + rx_unit->dma_link = NULL; } /* Free the internal DMA buffer */ if (rx_unit->dma_buf) { free(rx_unit->dma_buf); } + /* Free the stash buffer */ + for (uint8_t i = 0; i < 2; i++) { + if (rx_unit->stash_buf[i]) { + free(rx_unit->stash_buf[i]); + rx_unit->stash_buf[i] = NULL; + } + } /* Unregister the RX unit from the PARLIO group */ if (rx_unit->base.group) { parlio_unregister_unit_from_group(&rx_unit->base); @@ -638,7 +661,6 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un unit->trans_que = xQueueCreateWithCaps(config->trans_queue_depth, sizeof(parlio_rx_transaction_t), PARLIO_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(unit->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue"); - ESP_GOTO_ON_ERROR(parlio_rx_create_dma_descriptors(unit, config->max_recv_size), err, TAG, "create dma descriptor failed"); /* Register and attach the rx unit to the group */ ESP_GOTO_ON_ERROR(parlio_register_unit_to_group(&unit->base), err, TAG, "failed to register the rx unit to the group"); memcpy(&unit->cfg, config, sizeof(parlio_rx_unit_config_t)); @@ -652,7 +674,15 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un /* Initialize GPIO */ ESP_GOTO_ON_ERROR(parlio_rx_unit_set_gpio(unit, config), err, TAG, "failed to set GPIO"); /* Install DMA service */ - ESP_GOTO_ON_ERROR(parlio_rx_unit_init_dma(unit), err, TAG, "install rx DMA failed"); + ESP_GOTO_ON_ERROR(parlio_rx_unit_init_dma(unit, config->dma_burst_size), err, TAG, "install rx DMA failed"); + ESP_GOTO_ON_ERROR(parlio_rx_create_dma_link(unit, config->max_recv_size), err, TAG, "create dma link list failed"); + + for (uint8_t i = 0; i < 2; i++) { + uint32_t max_alignment = unit->int_mem_align > unit->ext_mem_align ? unit->int_mem_align : unit->ext_mem_align; + unit->stash_buf[i] = heap_caps_aligned_calloc(max_alignment, 2, max_alignment, PARLIO_MEM_ALLOC_CAPS | MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(unit->stash_buf[i], ESP_ERR_NO_MEM, err, TAG, "no memory for stash buffer"); + } + /* Reset RX module */ PARLIO_RCC_ATOMIC() { parlio_ll_rx_reset_clock(hal->regs); @@ -737,7 +767,16 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu xSemaphoreGive(rx_unit->trans_sem); } else if (xQueueReceive(rx_unit->trans_que, &trans, 0) == pdTRUE) { // The semaphore always supposed to be taken successfully - assert(xSemaphoreTake(rx_unit->trans_sem, 0) == pdTRUE); + BaseType_t res = xSemaphoreTake(rx_unit->trans_sem, 0); + (void)res; + assert(res == pdTRUE); + + if (trans.flags.indirect_mount && trans.flags.infinite && rx_unit->dma_buf == NULL) { + rx_unit->dma_buf = heap_caps_aligned_calloc(rx_unit->int_mem_align, 1, trans.aligned_payload.buf.body.length, PARLIO_DMA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, err, TAG, "No memory for the internal DMA buffer"); + trans.aligned_payload.buf.body.aligned_buffer = rx_unit->dma_buf; + trans.aligned_payload.buf.body.recovery_address = rx_unit->dma_buf; + } if (rx_unit->cfg.flags.free_clk) { PARLIO_CLOCK_SRC_ATOMIC() { parlio_ll_rx_enable_clock(hal->regs, false); @@ -746,7 +785,7 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu assert(trans.delimiter); parlio_rx_set_delimiter_config(rx_unit, trans.delimiter); parlio_rx_mount_transaction_buffer(rx_unit, &trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(hal->regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -935,7 +974,7 @@ static esp_err_t parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit, parlio_rx_mount_transaction_buffer(rx_unit, trans); // Take semaphore without block time here, only indicate there are transactions on receiving xSemaphoreTake(rx_unit->trans_sem, 0); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -958,18 +997,11 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, ESP_RETURN_ON_FALSE(rx_unit && payload && recv_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified"); ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large"); - uint32_t alignment = rx_unit->base.group->dma_align; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - ESP_RETURN_ON_FALSE(payload_size % alignment == 0, ESP_ERR_INVALID_ARG, TAG, "The payload size should align with %"PRIu32, alignment); + size_t alignment = rx_unit->int_mem_align; if (recv_cfg->flags.partial_rx_en) { ESP_RETURN_ON_FALSE(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG, TAG, "The payload size should greater than %"PRIu32, 2 * alignment); } -#endif -#if CONFIG_PARLIO_RX_ISR_CACHE_SAFE - ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM"); -#else - ESP_RETURN_ON_FALSE(recv_cfg->flags.indirect_mount || esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM"); -#endif + if (recv_cfg->delimiter->eof_data_len) { ESP_RETURN_ON_FALSE(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG, TAG, "payload size should be greater than eof_data_len"); @@ -985,8 +1017,8 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, rx_units[rx_unit->base.unit_id]. data_sigs[recv_cfg->delimiter->valid_sig_line_id]; } - void *p_buffer = payload; + dma_buffer_split_array_t dma_buf_array = {0}; /* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */ if (recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount) { ESP_RETURN_ON_FALSE(!rx_unit->dma_buf, ESP_ERR_INVALID_STATE, TAG, "infinite transaction is using the internal DMA buffer"); @@ -994,14 +1026,21 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, rx_unit->dma_buf = heap_caps_aligned_calloc(alignment, 1, payload_size, PARLIO_DMA_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, TAG, "No memory for the internal DMA buffer"); /* Use the internal DMA buffer so that the user buffer can always be available */ - p_buffer = rx_unit->dma_buf; + dma_buf_array.buf.body.aligned_buffer = rx_unit->dma_buf; + dma_buf_array.buf.body.recovery_address = rx_unit->dma_buf; + dma_buf_array.buf.body.length = payload_size; + } else { + ESP_RETURN_ON_ERROR(esp_dma_split_rx_buffer_to_cache_aligned(payload, payload_size, &dma_buf_array, &rx_unit->stash_buf[rx_unit->stash_buf_idx]), + TAG, "failed to split the unaligned DMA buffer"); + rx_unit->stash_buf_idx = !rx_unit->stash_buf_idx; } /* Create the transaction */ parlio_rx_transaction_t transaction = { .delimiter = recv_cfg->delimiter, - .payload = p_buffer, - .size = payload_size, + .aligned_payload = dma_buf_array, + .tot_trans_size = payload_size, + .alignment = alignment, .recv_bytes = 0, .flags.infinite = recv_cfg->flags.partial_rx_en, .flags.indirect_mount = recv_cfg->flags.indirect_mount, @@ -1053,3 +1092,52 @@ err: xSemaphoreGive(rx_unit->mutex); return ret; } + +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield) +{ + ESP_RETURN_ON_FALSE_ISR(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + int uint_id = rx_unit->base.unit_id; + parlio_unit_base_handle_t pair_tx_unit = rx_unit->base.group->tx_units[uint_id]; + /* This function will reset the whole parlio module, + If the pair tx unit is in using, + the reset operation will affect the TX unit and lead to unknown behavior */ + ESP_RETURN_ON_FALSE_ISR(!pair_tx_unit, ESP_ERR_INVALID_STATE, TAG, "can't be called when pair tx unit is in using"); + + /* Stop and reset the DMA channel first */ + ESP_RETURN_ON_ERROR_ISR(gdma_stop(rx_unit->dma_chan), TAG, "stop DMA channel failed"); + ESP_RETURN_ON_ERROR_ISR(gdma_reset(rx_unit->dma_chan), TAG, "reset DMA channel failed"); + + parlio_hal_context_t *hal = &rx_unit->base.group->hal; + portENTER_CRITICAL_SAFE(&s_rx_spinlock); + /* Save the current register values */ + parl_io_dev_t save_curr_regs = *(parl_io_dev_t *)hal->regs; + /* Reset the hardware FSM of the parlio module */ + PARLIO_RCC_ATOMIC() { + parlio_ll_reset_register(rx_unit->base.group->group_id); + } + /* Switch to the default clock source to ensure the register values can be written back successfully */ + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_rx_set_clock_source(hal->regs, PARLIO_CLK_SRC_DEFAULT); + } + portEXIT_CRITICAL_SAFE(&s_rx_spinlock); + /* Restore the register values and clock source*/ + memcpy(hal->regs, &save_curr_regs, sizeof(parl_io_dev_t)); + parlio_ll_rx_update_config(hal->regs); + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_rx_set_clock_source(hal->regs, rx_unit->clk_src); + } + + /* Force to trigger the EOF interrupt */ + gdma_event_data_t event_data = { + .flags.normal_eof = 1 + }; + bool _need_yield = false; + _need_yield |= parlio_rx_default_desc_done_callback(rx_unit->dma_chan, &event_data, rx_unit); + _need_yield |= parlio_rx_default_eof_callback(rx_unit->dma_chan, &event_data, rx_unit); + if (need_yield) { + *need_yield |= _need_yield; + } + + return ESP_OK; +} diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index baf1d295c7..329fcdfcd5 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -481,7 +481,7 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_ .flags = { // if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559 .mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true, - .mark_final = !t->flags.loop_transmission, + .mark_final = t->flags.loop_transmission ? GDMA_FINAL_LINK_TO_START : GDMA_FINAL_LINK_TO_NULL, } }; diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c index cc454aa7b4..138637e62f 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include "soc/parlio_periph.h" #include "esp_attr.h" #include "test_board.h" +#include "esp_private/parlio_rx_private.h" #define TEST_SPI_HOST SPI2_HOST #define TEST_I2S_PORT I2S_NUM_0 @@ -57,6 +58,10 @@ #define TEST_TASK_DATA_READY_BIT 0x01 #define TEST_TASK_FINISHED_BIT 0x02 +#define TEST_TASK_RECV_READY_BIT 0x04 +#define TEST_TASK_LARGE_TRANS_BIT 0x08 + +#define TEST_TASK_LARGE_TRANS_SIZE 155584 // Use an unaligned size to ensure the reliability typedef struct { uint32_t partial_recv_cnt; @@ -177,8 +182,6 @@ static void pulse_delimiter_sender_task_i2s(void *args) } } -#if CONFIG_IDF_TARGET_ESP32C6 // TODO: IDF-9806 fix the bit shift issue in other target - static void cs_high(spi_transaction_t *trans) { gpio_set_level(TEST_VALID_GPIO, 1); @@ -194,6 +197,7 @@ static void cs_low(spi_transaction_t *trans) static void level_delimiter_sender_task_spi(void *args) { uint32_t *task_flags = (uint32_t *)args; + bool is_large_trans = *task_flags & TEST_TASK_LARGE_TRANS_BIT; spi_device_handle_t dev_handle; spi_bus_config_t bus_cfg = { @@ -210,11 +214,11 @@ static void level_delimiter_sender_task_spi(void *args) .clock_speed_hz = TEST_SPI_CLK_FREQ, .mode = 0, .duty_cycle_pos = 128, - .spics_io_num = TEST_VALID_GPIO, + .spics_io_num = is_large_trans ? -1 : TEST_VALID_GPIO, .queue_size = 5, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS, - .pre_cb = cs_high, - .post_cb = cs_low, + .pre_cb = is_large_trans ? NULL : cs_high, + .post_cb = is_large_trans ? NULL : cs_low, }; //Initialize the SPI bus and add device TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO)); @@ -240,8 +244,14 @@ static void level_delimiter_sender_task_spi(void *args) parlio_periph_signals.groups[0].rx_units[0].data_sigs[0]); // Prepare the data the be transmitted - uint8_t *data = (uint8_t *)calloc(1, TEST_EOF_DATA_LEN); - for (int i = 0; i < TEST_EOF_DATA_LEN; i += 4) { + uint8_t *data = NULL; + size_t data_size = TEST_EOF_DATA_LEN; + if (*task_flags & TEST_TASK_LARGE_TRANS_BIT) { + data_size = 1024; + } + data = (uint8_t *)calloc(1, data_size); + TEST_ASSERT_NOT_NULL(data); + for (int i = 0; i < data_size; i += 4) { data[i] = 0x12; data[i + 1] = 0x34; data[i + 2] = 0x56; @@ -249,17 +259,31 @@ static void level_delimiter_sender_task_spi(void *args) } spi_transaction_t t = { .cmd = 0, - .length = TEST_EOF_DATA_LEN * 8, + .length = data_size * 8, .flags = 0, .tx_buffer = data, .user = NULL, }; // Transmit data every 1ms, until the main test thread finished receiving - while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { - TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); - vTaskDelay(pdMS_TO_TICKS(1)); - *task_flags |= TEST_TASK_DATA_READY_BIT; + if (is_large_trans) { + while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { + if (!((*task_flags) & TEST_TASK_RECV_READY_BIT)) { + gpio_set_level(TEST_VALID_GPIO, 1); + for (int i = 0; i < 80; i++) { + TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); + } + gpio_set_level(TEST_VALID_GPIO, 0); + *task_flags |= TEST_TASK_DATA_READY_BIT; + } + vTaskDelay(pdMS_TO_TICKS(2)); + } + } else { + while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { + TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); + vTaskDelay(pdMS_TO_TICKS(2)); + *task_flags |= TEST_TASK_DATA_READY_BIT; + } } // Remove the SPI device and free the bus @@ -275,7 +299,6 @@ static void level_delimiter_sender_task_spi(void *args) vTaskDelay(portMAX_DELAY); } } -#endif static bool test_delimiter(parlio_rx_delimiter_handle_t deli, bool free_running_clk, void (*sender_task_thread)(void *args)) { @@ -292,7 +315,7 @@ static bool test_delimiter(parlio_rx_delimiter_handle_t deli, bool free_running_ static uint32_t task_flags = 0; xTaskCreate(sender_task_thread, "sender task", 4096, &task_flags, 5, &sender_task); // Waiting for the data ready on line - while ((task_flags & TEST_TASK_DATA_READY_BIT)) { + while (!(task_flags & TEST_TASK_DATA_READY_BIT)) { vTaskDelay(1); } @@ -477,7 +500,7 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_PAYLOAD_SIZE, &recv_config)); TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); - TEST_ASSERT_EQUAL_UINT32(2, test_data.partial_recv_cnt); + TEST_ASSERT_GREATER_OR_EQUAL_UINT32(2, test_data.partial_recv_cnt); TEST_ASSERT_EQUAL_UINT32(1, test_data.recv_done_cnt); memset(&test_data, 0, sizeof(test_data_t)); @@ -489,7 +512,7 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") } TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); - TEST_ASSERT_EQUAL_UINT32(10, test_data.partial_recv_cnt); + TEST_ASSERT_GREATER_OR_EQUAL_UINT32(10, test_data.partial_recv_cnt); TEST_ASSERT_EQUAL_UINT32(5, test_data.recv_done_cnt); memset(&test_data, 0, sizeof(test_data_t)); @@ -525,6 +548,49 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") free(payload); }; +#if SOC_PSRAM_DMA_CAPABLE +TEST_CASE("parallel_rx_unit_receive_external_memory_test", "[parlio_rx]") +{ + parlio_rx_unit_handle_t rx_unit = NULL; + parlio_rx_delimiter_handle_t deli = NULL; + size_t payload_size = 1000; + + parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_DEFAULT, 1000000); + config.flags.free_clk = 1; + TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit)); + + parlio_rx_soft_delimiter_config_t sft_deli_cfg = { + .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .eof_data_len = payload_size, + .timeout_ticks = 0, + }; + TEST_ESP_OK(parlio_new_rx_soft_delimiter(&sft_deli_cfg, &deli)); + + TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true)); + + parlio_receive_config_t recv_config = { + .delimiter = deli, + .flags.partial_rx_en = false, + }; + + /* Do not specify alignment, check if the driver can work correctly */ + uint8_t *payload = heap_caps_calloc_prefer(1, payload_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM, TEST_PARLIO_DMA_MEM_ALLOC_CAPS); + printf("payload addr: %p size: %u\n", payload, payload_size); + TEST_ASSERT(payload); + + printf("Testing the external memory receive functionality...\n"); + TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true)); + TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, payload_size, &recv_config)); + TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); + TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); + + TEST_ESP_OK(parlio_rx_unit_disable(rx_unit)); + TEST_ESP_OK(parlio_del_rx_delimiter(deli)); + TEST_ESP_OK(parlio_del_rx_unit(rx_unit)); + free(payload); +} +#endif // SOC_PSRAM_DMA_CAPABLE + TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]") { printf("init a gpio to simulate valid signal\r\n"); @@ -592,3 +658,122 @@ TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]") TEST_ESP_OK(gpio_reset_pin(TEST_VALID_GPIO)); free(payload); } + +typedef struct { + uint32_t partial_recv_cnt; + uint32_t recv_done_cnt; + uint32_t timeout_cnt; + uint32_t isr_send_cnt; + uint32_t isr_send_success_cnt; + parlio_rx_unit_handle_t rx_unit; + parlio_rx_delimiter_handle_t delimiter; + uint8_t *isr_payload; + size_t isr_payload_size; + bool enable_isr_send; +} test_isr_data_t; + +/** + * @brief This ISR is to indicate the SPI transaction finished + */ +static void test_gpio_neg_edge_intr(void *arg) +{ + parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t)arg; + bool need_yield = false; + parlio_rx_unit_trigger_fake_eof(rx_unit, &need_yield); + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + +TEST_CASE("parallel_rx_unit_force_trigger_eof_test", "[parlio_rx]") +{ + parlio_rx_unit_handle_t rx_unit = NULL; + + parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_EXTERNAL, 1000000); + config.flags.free_clk = 0; + config.max_recv_size = TEST_TASK_LARGE_TRANS_SIZE; + TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit)); + + parlio_rx_level_delimiter_config_t lvl_deli_cfg = { + .valid_sig_line_id = TEST_VALID_SIG, + .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, + /* Normally the EOF won't be triggered for the level delimiter that eof_data_len larger than 64KB */ + .eof_data_len = TEST_TASK_LARGE_TRANS_SIZE, + .timeout_ticks = 0, + .flags = { + .active_low_en = 0, + }, + }; + parlio_rx_delimiter_handle_t deli = NULL; + TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli)); + + parlio_rx_event_callbacks_t cbs = { + .on_receive_done = test_parlio_rx_done_callback, + }; + test_data_t test_data = { + .partial_recv_cnt = 0, + .recv_done_cnt = 0, + }; + TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &cbs, &test_data)); + TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true)); + + TaskHandle_t sender_task; + /* The flag to transport finish information between main test thread and the sender thread + * Set it as static to make sure it'll be valid in another thread */ + static uint32_t task_flags = TEST_TASK_LARGE_TRANS_BIT; + xTaskCreate(level_delimiter_sender_task_spi, "sender task", 4096, &task_flags, 5, &sender_task); + + parlio_receive_config_t recv_config = { + .delimiter = deli, + .flags.partial_rx_en = false, + }; + uint8_t *recv_buff = NULL; + uint32_t alignment = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + alignment = alignment < 4 ? 4 : alignment; + size_t buff_size = ALIGN_UP(TEST_TASK_LARGE_TRANS_SIZE, alignment); + recv_buff = heap_caps_aligned_calloc(alignment, 1, buff_size, TEST_PARLIO_DMA_MEM_ALLOC_CAPS); + TEST_ASSERT_NOT_NULL(recv_buff); + + gpio_set_intr_type(TEST_VALID_GPIO, GPIO_INTR_NEGEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_VALID_GPIO, test_gpio_neg_edge_intr, rx_unit); + gpio_intr_enable(TEST_VALID_GPIO); + + uint32_t recv_cnt = 3; + for (int i = 0; i < recv_cnt; i++) { + TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, recv_buff, buff_size, &recv_config)); + printf("[%d] recv ready\n", i); + task_flags |= TEST_TASK_RECV_READY_BIT; + while (!task_flags & TEST_TASK_DATA_READY_BIT) { + vTaskDelay(1); + } + task_flags &= ~TEST_TASK_DATA_READY_BIT; + printf("[%d] send done\n", i); + TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 10000)); + task_flags &= ~TEST_TASK_RECV_READY_BIT; + printf("[%d] recv done\n", i); + } + // Indicate the test finished, no need to send data + task_flags |= TEST_TASK_FINISHED_BIT; + + bool is_success = true; + is_success &= test_data.recv_done_cnt == recv_cnt; + + gpio_intr_disable(TEST_VALID_GPIO); + gpio_isr_handler_remove(TEST_VALID_GPIO); + gpio_uninstall_isr_service(); + // Waiting for the sender task quit + while (task_flags) { + vTaskDelay(1); + } + // Delete the sender task + vTaskDelete(sender_task); + free(recv_buff); + + TEST_ESP_OK(parlio_rx_unit_disable(rx_unit)); + TEST_ESP_OK(parlio_del_rx_delimiter(deli)); + TEST_ESP_OK(parlio_del_rx_unit(rx_unit)); + + TEST_ASSERT(is_success); +} diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c index 8fc99cd40a..e035b48bea 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c @@ -94,7 +94,7 @@ static void test_parlio_sleep_retention(bool allow_pd) parlio_rx_level_delimiter_config_t lvl_deli_cfg = { .valid_sig_line_id = PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1, - .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .sample_edge = PARLIO_SAMPLE_EDGE_NEG, // opposite to tx unit in case of timing issue .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, .eof_data_len = TEST_PAYLOAD_SIZE, .timeout_ticks = 0, diff --git a/components/esp_driver_touch_sens/common/touch_sens_common.c b/components/esp_driver_touch_sens/common/touch_sens_common.c index 1b0d726f72..39c12615c4 100644 --- a/components/esp_driver_touch_sens/common/touch_sens_common.c +++ b/components/esp_driver_touch_sens/common/touch_sens_common.c @@ -43,12 +43,22 @@ static void touch_channel_pin_init(int id) { gpio_num_t pin = touch_sensor_channel_io_map[id]; assert(pin >= 0); + /* Touch pad will output the sawtooth wave on the pin, + so it needs to be reserved, in case of conflict with other output signals */ if (esp_gpio_reserve(BIT64(pin)) & BIT64(pin)) { ESP_LOGW(TAG, "The GPIO%d is conflict with other module", (int)pin); } gpio_config_as_analog(pin); } +static void touch_channel_pin_deinit(int id) +{ + gpio_num_t pin = touch_sensor_channel_io_map[id]; + assert(pin >= 0); + /* esp_gpio_revoke is called in gpio_reset_pin */ + gpio_reset_pin(pin); +} + static void s_touch_free_resource(touch_sensor_handle_t sens_handle) { if (!sens_handle) { @@ -211,6 +221,7 @@ esp_err_t touch_sensor_del_channel(touch_channel_handle_t chan_handle) TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK); sens_handle->chan_mask &= ~(1UL << chan_handle->id); TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK); + touch_channel_pin_deinit(chan_handle->id); free(g_touch->ch[ch_offset]); g_touch->ch[ch_offset] = NULL; diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index b0a40a6a23..e390d047c9 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -269,7 +269,7 @@ static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transactio .length = trans->buffer_size, .flags = { .mark_eof = true, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; @@ -332,7 +332,7 @@ esp_err_t uhci_receive(uhci_controller_handle_t uhci_ctrl, uint8_t *read_buffer, .buffer_alignment = buffer_alignment, .length = uhci_ctrl->rx_dir.buffer_size_per_desc_node[i], .flags = { - .mark_final = false, + .mark_final = GDMA_FINAL_LINK_TO_DEFAULT, } }; ESP_LOGD(TAG, "The DMA node %d has %d byte", i, uhci_ctrl->rx_dir.buffer_size_per_desc_node[i]); diff --git a/components/esp_hw_support/dma/async_memcpy_cp_dma.c b/components/esp_hw_support/dma/async_memcpy_cp_dma.c index add2cec2e5..3e36e6b997 100644 --- a/components/esp_hw_support/dma/async_memcpy_cp_dma.c +++ b/components/esp_hw_support/dma/async_memcpy_cp_dma.c @@ -232,7 +232,7 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void * .length = n, .flags = { .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; @@ -256,7 +256,7 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void * .length = n, .flags = { .mark_eof = false, // EOF is set by TX side - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; diff --git a/components/esp_hw_support/dma/async_memcpy_gdma.c b/components/esp_hw_support/dma/async_memcpy_gdma.c index a6e1047dd4..f53b0aaf48 100644 --- a/components/esp_hw_support/dma/async_memcpy_gdma.c +++ b/components/esp_hw_support/dma/async_memcpy_gdma.c @@ -365,7 +365,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s .length = n, .flags = { .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; diff --git a/components/esp_hw_support/dma/esp_dma_utils.c b/components/esp_hw_support/dma/esp_dma_utils.c index ad97b84e7e..a820291bcd 100644 --- a/components/esp_hw_support/dma/esp_dma_utils.c +++ b/components/esp_hw_support/dma/esp_dma_utils.c @@ -29,7 +29,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe { esp_err_t ret = ESP_OK; uint8_t* stash_buffer = NULL; - ESP_RETURN_ON_FALSE(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE_ISR(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); // read the cache line size of internal and external memory, we also use this information to check if a given memory is behind the cache size_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); @@ -41,80 +41,83 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe } else if (esp_ptr_internal(rx_buffer)) { split_line_size = int_mem_cache_line_size; } - ESP_LOGV(TAG, "split_line_size:%zu", split_line_size); + bool align_required = split_line_size > 0; + ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size); - // allocate the stash buffer from internal RAM - // Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine - stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - ESP_RETURN_ON_FALSE(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer"); + if (*ret_stash_buffer == NULL) { + // If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM + // Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine + stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ESP_RETURN_ON_FALSE_ISR(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer"); + } else { + // If the stash buffer is offered by the caller, check if it is aligned + ESP_RETURN_ON_FALSE_ISR(split_line_size == 0 || (uintptr_t)(*ret_stash_buffer) % split_line_size == 0, + ESP_ERR_INVALID_ARG, TAG, "the offered stash buffer is not aligned"); + // If the stash buffer is offered by the caller, use it + stash_buffer = *ret_stash_buffer; + } // clear align_array to avoid garbage data memset(align_buf_array, 0, sizeof(dma_buffer_split_array_t)); bool need_cache_sync[3] = {false}; - // if split_line_size is non-zero, split the buffer into head, body and tail - if (split_line_size > 0) { + // if align_required, split the buffer into head, body and tail + if (align_required) { // calculate head_overflow_len size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size; head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0; - ESP_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len); + ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len); // calculate tail_overflow_len size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size; - ESP_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len); - - uint8_t extra_buf_count = 0; - uint8_t* input_buffer = (uint8_t*)rx_buffer; - align_buf_array->buf.head.recovery_address = input_buffer; - align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; - align_buf_array->buf.head.length = head_overflow_len; - need_cache_sync[0] = int_mem_cache_line_size > 0; - align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len; - align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len; - align_buf_array->buf.body.length = buffer_len - head_overflow_len - tail_overflow_len; - need_cache_sync[1] = true; - align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len; - align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; - align_buf_array->buf.tail.length = tail_overflow_len; - need_cache_sync[2] = int_mem_cache_line_size > 0; + ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len); // special handling when input_buffer length is no more than buffer alignment - if (head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len) { - align_buf_array->buf.head.length = buffer_len ; - align_buf_array->buf.body.length = 0 ; - align_buf_array->buf.tail.length = 0 ; + bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len; + uint8_t extra_buf_count = 0; + uint8_t* input_buffer = (uint8_t*)rx_buffer; + if (head_overflow_len || is_small_buf) { + align_buf_array->buf.head.recovery_address = input_buffer; + align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; + align_buf_array->buf.head.length = is_small_buf ? buffer_len : head_overflow_len; + need_cache_sync[0] = int_mem_cache_line_size > 0; + } + int body_len = (int)buffer_len - (int)head_overflow_len - (int)tail_overflow_len; + if (body_len > 0) { + align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len; + align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len; + align_buf_array->buf.body.length = body_len; + need_cache_sync[1] = true; + } + if (tail_overflow_len && !is_small_buf) { + align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len; + align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; + align_buf_array->buf.tail.length = tail_overflow_len; + need_cache_sync[2] = int_mem_cache_line_size > 0; } } else { align_buf_array->buf.body.aligned_buffer = rx_buffer; align_buf_array->buf.body.recovery_address = rx_buffer; align_buf_array->buf.body.length = buffer_len; - need_cache_sync[1] = false; - } - - for (int i = 0; i < 3; i++) { - if (align_buf_array->aligned_buffer[i].length == 0) { - align_buf_array->aligned_buffer[i].aligned_buffer = NULL; - align_buf_array->aligned_buffer[i].recovery_address = NULL; - need_cache_sync[i] = false; - } } // invalidate the aligned buffer if necessary for (int i = 0; i < 3; i++) { - if (need_cache_sync[i]) { - size_t sync_size = align_buf_array->aligned_buffer[i].length; + size_t sync_size = align_buf_array->aligned_buffer[i].length; + if (need_cache_sync[i] && sync_size > 0) { if (sync_size < split_line_size) { // If the buffer is smaller than the cache line size, we need to sync the whole buffer sync_size = split_line_size; } esp_err_t res = esp_cache_msync(align_buf_array->aligned_buffer[i].aligned_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ESP_GOTO_ON_ERROR(res, err, TAG, "failed to do cache sync"); + ESP_GOTO_ON_ERROR_ISR(res, err, TAG, "failed to do cache sync"); } } *ret_stash_buffer = stash_buffer; return ESP_OK; err: - if (stash_buffer) { + // Only free the stash buffer if it is not offered by the caller + if (stash_buffer && *ret_stash_buffer == NULL) { free(stash_buffer); } return ret; diff --git a/components/esp_hw_support/dma/gdma_link.c b/components/esp_hw_support/dma/gdma_link.c index 1011abb224..0caf76db92 100644 --- a/components/esp_hw_support/dma/gdma_link.c +++ b/components/esp_hw_support/dma/gdma_link.c @@ -203,9 +203,22 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i lli_nc->dw0.size = lli_nc->dw0.length; // mark the EOF node lli_nc->dw0.suc_eof = (config->flags.mark_eof == 1) && (i == num_items_need - 1); - // mark the final node - if ((config->flags.mark_final == 1) && (i == num_items_need - 1)) { - lli_nc->next = NULL; + if (i == num_items_need - 1) { + // mark the final node + switch (config->flags.mark_final) { + case GDMA_FINAL_LINK_TO_NULL: + lli_nc->next = NULL; + break; + case GDMA_FINAL_LINK_TO_HEAD: + lli_nc->next = (gdma_link_list_item_t *)(list->items); + break; + case GDMA_FINAL_LINK_TO_START: + lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size); + break; + default: + lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size); + break; + } } else { lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size); } diff --git a/components/esp_hw_support/dma/include/esp_private/gdma_link.h b/components/esp_hw_support/dma/include/esp_private/gdma_link.h index 6bccb4c802..37d9964a3a 100644 --- a/components/esp_hw_support/dma/include/esp_private/gdma_link.h +++ b/components/esp_hw_support/dma/include/esp_private/gdma_link.h @@ -56,6 +56,17 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li */ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list); +/** + * @brief Types for the next node of the final item in the DMA link list + * + */ +typedef enum { + GDMA_FINAL_LINK_TO_DEFAULT = 0, /*!< The next node is linked to the default next item in the link list */ + GDMA_FINAL_LINK_TO_NULL = 1, /*!< No next node is linked */ + GDMA_FINAL_LINK_TO_HEAD = 2, /*!< The next node is linked to the head item in the link list */ + GDMA_FINAL_LINK_TO_START = 3, /*!< The next node is linked to the start item in the link list */ +} gdma_final_node_link_type_t; + /** * @brief DMA buffer mount configurations */ @@ -66,12 +77,11 @@ typedef struct { struct gdma_buffer_mount_flags { uint32_t mark_eof: 1; /*!< Whether to mark the list item as the "EOF" item. Note, an "EOF" descriptor can be interrupted differently by peripheral. - But it doesn't mean to terminate a DMA link (use `mark_final` instead). + But it doesn't mean to terminate a DMA link (set `mark_final` to GDMA_FINAL_LINK_TO_NULL instead). EOF link list item can also trigger an interrupt. */ - uint32_t mark_final: 1; /*!< Whether to terminate the DMA link list at this item. - Note, DMA engine will stop at this item and trigger an interrupt. - If `mark_final` is not set, this list item will point to the next item, and - wrap around to the head item if it's the last one in the list. */ + gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount. + For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect. + Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */ uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check. Only enable it when you know what you are doing. */ } flags; //!< Flags for buffer mount configurations diff --git a/components/esp_hw_support/test_apps/dma/main/test_gdma.c b/components/esp_hw_support/test_apps/dma/main/test_gdma.c index ce97d0a0b4..bb487b2a2b 100644 --- a/components/esp_hw_support/test_apps/dma/main/test_gdma.c +++ b/components/esp_hw_support/test_apps/dma/main/test_gdma.c @@ -278,7 +278,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe #if !SOC_DMA_CAN_ACCESS_FLASH .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } #endif }, @@ -289,7 +289,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe .length = src_string_len, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }, #endif @@ -448,7 +448,7 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_ .length = data_length, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; @@ -609,7 +609,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") .length = COPY_SIZE, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }; TEST_ESP_OK(gdma_link_mount_buffers(tx_link_list, 0, &tx_buf_mount_config, 1, NULL)); @@ -619,7 +619,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") .buffer_alignment = 32, .length = COPY_SIZE, .flags = { - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }; TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c index bfd15c0d29..83c6918075 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c @@ -523,7 +523,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons gdma_buffer_mount_config_t mount_config = { .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; @@ -599,7 +599,7 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons gdma_buffer_mount_config_t mount_config = { .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; @@ -724,7 +724,7 @@ static void i2s_lcd_trigger_quick_trans_done_event(esp_lcd_i80_bus_handle_t bus) .length = 4, .flags = { .mark_eof = true, // mark the "EOF" flag to trigger I2S EOF interrupt - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); @@ -811,7 +811,7 @@ static IRAM_ATTR void i2s_lcd_default_isr_handler(void *args) .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index f0ffb1b1a4..1977f6ab0f 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -500,7 +500,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); @@ -821,7 +821,7 @@ IRAM_ATTR static void i80_lcd_default_isr_handler(void *args) .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); diff --git a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c index 1d96b03518..efdba5926a 100644 --- a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c +++ b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c @@ -33,7 +33,7 @@ #include "driver/parlio_tx.h" #include "driver/parlio_types.h" #include "esp_private/gpio.h" -#include "esp_private/parlio_private.h" +#include "esp_private/parlio_tx_private.h" #include "esp_lcd_panel_io_interface.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_common.h" diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 9fd7e91e31..616e68bb6b 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -1052,7 +1052,7 @@ static esp_err_t lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *rgb_panel) gdma_buffer_mount_config_t mount_cfg = { .length = rgb_panel->fb_size, .flags = { - .mark_final = rgb_panel->flags.stream_mode ? false : true, + .mark_final = rgb_panel->flags.stream_mode ? GDMA_FINAL_LINK_TO_DEFAULT : GDMA_FINAL_LINK_TO_NULL, .mark_eof = true, }, }; diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index e953ee0280..bddee9ad42 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -102,10 +102,13 @@ Peripheral clock sources such as ``REF_TICK``, ``XTAL``, ``RC_FAST`` (i.e., ``RT Currently, the following peripheral drivers are aware of DFS and use the ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction: -- SPI master -- I2C -- I2S (If the APLL clock is used, then it will use the ``ESP_PM_NO_LIGHT_SLEEP`` lock) -- SDMMC +.. list:: + + - SPI master + - I2C + :SOC_I2S_HW_VERSION_1 or not SOC_I2S_SUPPORTS_APLL: - I2S + :not SOC_I2S_HW_VERSION_1 and SOC_I2S_SUPPORTS_APLL: - I2S (if the APLL clock is used, then it will use the ``ESP_PM_NO_LIGHT_SLEEP`` lock) + - SDMMC The following drivers hold the ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled: diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 11fdde8cbb..dd568616a9 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -102,10 +102,13 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, 目前以下外设驱动程序可感知动态调频,并在调频期间使用 ``ESP_PM_APB_FREQ_MAX`` 锁: -- SPI master -- I2C -- I2S(如果 APLL 锁在使用中,I2S 则会启用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁) -- SDMMC +.. list:: + + - SPI master + - I2C + :SOC_I2S_HW_VERSION_1 or not SOC_I2S_SUPPORTS_APLL: - I2S + :not SOC_I2S_HW_VERSION_1 and SOC_I2S_SUPPORTS_APLL: - I2S(如果 APLL 锁在使用中,I2S 则会启用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁) + - SDMMC 启用以下驱动程序时,将占用 ``ESP_PM_APB_FREQ_MAX`` 锁: