Merge branch 'backport/recent_backport_collection_v5.4' into 'release/v5.4'

backport: backport recent i2s/parlio rx related to v5.4

See merge request espressif/esp-idf!43795
This commit is contained in:
morris
2025-12-01 11:00:48 +08:00
7 changed files with 159 additions and 8 deletions
+39
View File
@@ -370,6 +370,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");
@@ -416,6 +450,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 */
@@ -980,6 +1017,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;
@@ -995,6 +1033,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;
+2
View File
@@ -94,6 +94,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");
@@ -397,6 +398,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");
+15 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -159,6 +159,7 @@ struct i2s_channel_obj_t {
/* Stored configurations */
int intr_prio_flags;/*!< i2s interrupt priority flags */
void *mode_info; /*!< Slot, clock and gpio information of each mode */
bool is_port_auto; /*!< Whether the port is auto-assigned */
bool is_etm_start; /*!< Whether start by etm tasks */
bool is_etm_stop; /*!< Whether stop by etm tasks */
bool full_duplex_slave; /*!< whether the channel is forced to switch to slave role for full duplex */
@@ -333,6 +334,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
+27 -5
View File
@@ -106,6 +106,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");
@@ -237,13 +238,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 &&
+3
View File
@@ -114,6 +114,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");
@@ -245,6 +246,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 */
@@ -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)
@@ -292,7 +292,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);
}