diff --git a/components/esp_driver_i2s/Kconfig b/components/esp_driver_i2s/Kconfig index dc57c8c211..8f4641babb 100644 --- a/components/esp_driver_i2s/Kconfig +++ b/components/esp_driver_i2s/Kconfig @@ -8,6 +8,13 @@ menu "ESP-Driver:I2S Configurations" Ensure the I2S interrupt is IRAM-Safe by allowing the interrupt handler to be executable when the cache is disabled (e.g. SPI Flash write). + config I2S_CTRL_FUNC_IN_IRAM + bool "Place I2S control functions into IRAM" + default n + help + Place I2S control functions into IRAM, + so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. + config I2S_ENABLE_DEBUG_LOG bool "Enable I2S debug log" default n diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index a99bdd1d23..48b88f2f1e 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -47,6 +47,7 @@ #include "driver/gpio.h" #include "esp_private/gpio.h" +#include "esp_private/i2s_sync.h" #include "driver/i2s_common.h" #include "i2s_private.h" @@ -1483,3 +1484,46 @@ void i2s_sync_reset_fifo_count(i2s_chan_handle_t tx_handle) i2s_ll_tx_reset_fifo_sync_counter(tx_handle->controller->hal.dev); } #endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT + +#if SOC_I2S_SUPPORTS_ETM_SYNC +uint32_t i2s_sync_get_fifo_sync_diff_count(i2s_chan_handle_t tx_handle) +{ + return i2s_ll_tx_get_fifo_sync_diff_count(tx_handle->controller->hal.dev); +} + +void i2s_sync_reset_fifo_sync_diff_count(i2s_chan_handle_t tx_handle) +{ + i2s_ll_tx_reset_fifo_sync_diff_counter(tx_handle->controller->hal.dev); +} + +esp_err_t i2s_sync_enable_hw_fifo_sync(i2s_chan_handle_t tx_handle, bool enable) +{ + if (tx_handle->dir == I2S_DIR_RX) { + return ESP_ERR_NOT_SUPPORTED; + } + i2s_ll_tx_enable_hw_fifo_sync(tx_handle->controller->hal.dev, enable); + return ESP_OK; +} + +esp_err_t i2s_sync_config_hw_fifo_sync(i2s_chan_handle_t tx_handle, const i2s_sync_fifo_sync_config_t *config) +{ + if (!(tx_handle && config)) { + return ESP_ERR_INVALID_ARG; + } + if (tx_handle->dir == I2S_DIR_RX) { + return ESP_ERR_NOT_SUPPORTED; + } + if (config->sw_high_thresh < config->hw_low_thresh) { + return ESP_ERR_INVALID_ARG; + } + + i2s_ll_tx_set_etm_sync_ideal_cnt(tx_handle->controller->hal.dev, config->ideal_cnt); + i2s_ll_tx_set_fifo_sync_diff_conter_sw_threshold(tx_handle->controller->hal.dev, config->sw_high_thresh); + i2s_ll_tx_set_fifo_sync_diff_conter_hw_threshold(tx_handle->controller->hal.dev, config->hw_low_thresh); + i2s_ll_tx_set_hw_fifo_sync_suppl_mode(tx_handle->controller->hal.dev, (uint32_t)config->suppl_mode); + if (config->suppl_mode == I2S_SYNC_SUPPL_MODE_STATIC_DATA) { + i2s_ll_tx_set_hw_fifo_sync_static_suppl_data(tx_handle->controller->hal.dev, config->suppl_data); + } + return ESP_OK; +} +#endif diff --git a/components/esp_driver_i2s/i2s_etm.c b/components/esp_driver_i2s/i2s_etm.c index c73058a4c7..725775dd31 100644 --- a/components/esp_driver_i2s/i2s_etm.c +++ b/components/esp_driver_i2s/i2s_etm.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -84,6 +84,11 @@ esp_err_t i2s_new_etm_task(i2s_chan_handle_t handle, const i2s_etm_task_config_t { ESP_RETURN_ON_FALSE(handle && config && out_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(config->task_type < I2S_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid task type"); +#if SOC_I2S_SUPPORTS_ETM_SYNC + ESP_RETURN_ON_FALSE(config->task_type != I2S_ETM_TASK_SYNC_CHECK || handle->dir == I2S_DIR_TX, + ESP_ERR_NOT_SUPPORTED, TAG, "rx is not supported"); +#endif + i2s_etm_task_t *task = heap_caps_calloc(1, sizeof(i2s_etm_task_t), ETM_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(task, ESP_ERR_NO_MEM, TAG, "no memory for ETM task"); diff --git a/components/esp_driver_i2s/include/esp_private/i2s_sync.h b/components/esp_driver_i2s/include/esp_private/i2s_sync.h index 83c4190397..68660ad7ab 100644 --- a/components/esp_driver_i2s/include/esp_private/i2s_sync.h +++ b/components/esp_driver_i2s/include/esp_private/i2s_sync.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,6 +31,7 @@ extern "C" { /** * @brief Get the counter number of BCLK ticks * @note The BCLK tick count reflects the real data that have sent on line + * @note It will be reset automatically when `I2S_ETM_TASK_SYNC_CHECK` is triggered * * @param[in] tx_handle The I2S tx channel handle * @return @@ -43,6 +44,7 @@ uint32_t i2s_sync_get_bclk_count(i2s_chan_handle_t tx_handle); * @note The FIFO count reflects how many slots have processed * Normally, fifo_cnt = slot_bit_width * bclk_cnt * If fifo_cnt < slot_bit_width * bclk_cnt, that means some data are still stuck in the I2S controller + * @note It will be reset automatically when `I2S_ETM_TASK_SYNC_CHECK` is triggered * * @param[in] tx_handle The I2S tx channel handle * @return @@ -66,6 +68,78 @@ void i2s_sync_reset_fifo_count(i2s_chan_handle_t tx_handle); #endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT +#if SOC_I2S_SUPPORTS_ETM_SYNC +/** + * @brief I2S hardware FIFO synchronization supplement mode + * @note When the FIFO sync difference count is out of threshold, the hardware will supplement data automatically + * This type is to specify which data will be supplemented + */ +typedef enum { + I2S_SYNC_SUPPL_MODE_LAST_DATA = 0, /*!< Supplement with the last transmitted data */ + I2S_SYNC_SUPPL_MODE_STATIC_DATA = 1, /*!< Supplement with static data specified in config */ +} i2s_sync_suppl_mode_t; + +/** + * @brief I2S hardware FIFO synchronization configuration + * @note This configuration is used for multi I2S port synchronization via ETM + */ +typedef struct { + uint32_t hw_low_thresh; /*!< Lower threshold for FIFO sync difference counter + - If difference count < hw_low_thresh, do nothing + - If difference count >= hw_low_thresh, the hardware will supplement data automatically */ + uint32_t sw_high_thresh; /*!< Upper threshold for FIFO sync difference counter + - If difference count <= sw_high_thresh, the hardware supplement data automatically + - If difference count > sw_high_thresh, sync interrupt triggered and + the software is responsible to decide how to handle this severe asynchronization */ + uint32_t ideal_cnt; /*!< Ideal count for FIFO sync difference counter, it depends on the ETM sync task interval and the data rate */ + i2s_sync_suppl_mode_t suppl_mode; /*!< Data supplement mode when FIFO sync difference is out of threshold */ + uint32_t suppl_data; /*!< Static supplement data, only valid when suppl_mode is I2S_SYNC_SUPPL_MODE_STATIC_DATA */ +} i2s_sync_fifo_sync_config_t; + +/** + * @brief Get the counter number of FIFO sync difference + * @note The FIFO sync difference count reflects the difference between current FIFO count and ideal count + * + * @param[in] tx_handle The I2S tx channel handle + * @return + * - FIFO sync difference count + */ +uint32_t i2s_sync_get_fifo_sync_diff_count(i2s_chan_handle_t tx_handle); + +/** + * @brief Reset the FIFO sync difference counter + * + * @param[in] tx_handle The I2S tx channel handle + */ +void i2s_sync_reset_fifo_sync_diff_count(i2s_chan_handle_t tx_handle); + +/** + * @brief Enable or disable hardware FIFO synchronization + * @note When enabled, hardware will automatically supplement data when FIFO sync difference is greater than hw_low_thresh + * + * @param[in] tx_handle The I2S tx channel handle + * @param[in] enable true to enable, false to disable + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_SUPPORTED if called on RX channel + */ +esp_err_t i2s_sync_enable_hw_fifo_sync(i2s_chan_handle_t tx_handle, bool enable); + +/** + * @brief Configure hardware FIFO synchronization parameters + * @note This function configures the thresholds and supplement mode for hardware FIFO sync + * + * @param[in] tx_handle The I2S tx channel handle + * @param[in] config Configuration for hardware FIFO synchronization + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if invalid arguments + * - ESP_ERR_NOT_SUPPORTED if called on RX channel + */ +esp_err_t i2s_sync_config_hw_fifo_sync(i2s_chan_handle_t tx_handle, const i2s_sync_fifo_sync_config_t *config); + +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i2s/linker.lf b/components/esp_driver_i2s/linker.lf new file mode 100644 index 0000000000..9bb2330d24 --- /dev/null +++ b/components/esp_driver_i2s/linker.lf @@ -0,0 +1,14 @@ +[mapping:i2s_driver] +archive: libesp_driver_i2s.a +entries: + if I2S_CTRL_FUNC_IN_IRAM = y: + if SOC_I2S_SUPPORTS_TX_SYNC_CNT = y: + i2s_common: i2s_sync_get_bclk_count (noflash) + i2s_common: i2s_sync_get_fifo_count (noflash) + i2s_common: i2s_sync_reset_bclk_count (noflash) + i2s_common: i2s_sync_reset_fifo_count (noflash) + if SOC_I2S_SUPPORTS_ETM_SYNC = y: + i2s_common: i2s_sync_get_fifo_sync_diff_count (noflash) + i2s_common: i2s_sync_reset_fifo_sync_diff_count (noflash) + i2s_common: i2s_sync_enable_hw_fifo_sync (noflash) + i2s_common: i2s_sync_config_hw_fifo_sync (noflash) diff --git a/components/hal/esp32h4/include/hal/i2s_ll.h b/components/hal/esp32h4/include/hal/i2s_ll.h index 4abbb4f07d..9197e0d2b4 100644 --- a/components/hal/esp32h4/include/hal/i2s_ll.h +++ b/components/hal/esp32h4/include/hal/i2s_ll.h @@ -60,7 +60,7 @@ extern "C" { [I2S_DIR_RX - 1] = { \ [I2S_ETM_TASK_START] = I2S0_TASK_START_RX, \ [I2S_ETM_TASK_STOP] = I2S0_TASK_STOP_RX, \ - [I2S_ETM_TASK_SYNC_CHECK] = I2S0_TASK_SYNC_CHECK, \ + [I2S_ETM_TASK_SYNC_CHECK] = -1, \ }, \ [I2S_DIR_TX - 1] = { \ [I2S_ETM_TASK_START] = I2S0_TASK_START_TX, \ @@ -1245,6 +1245,113 @@ static inline uint32_t i2s_ll_tx_get_bclk_sync_count(i2s_dev_t *hw) return hw->bck_cnt.tx_bck_cnt; } +/** + * @brief Enable TX FIFO synchronization hardware mode + * + * @param hw Peripheral I2S hardware instance address. + * @param enable Set true to enable hardware mode + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_enable_hw_fifo_sync(i2s_dev_t *hw, bool enable) +{ + hw->hw_sync_conf.tx_hw_sync_en = enable; +} + +/** + * @brief Get TX FIFO synchronization difference count value + * + * @param hw Peripheral I2S hardware instance address. + * @return + * fifo count value + */ +__attribute__((always_inline)) +static inline uint32_t i2s_ll_tx_get_fifo_sync_diff_count(i2s_dev_t *hw) +{ + return hw->cnt_diff.tx_cnt_diff; +} + +/** + * @brief Reset TX FIFO synchronization difference counter + * + * @param hw Peripheral I2S hardware instance address. + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_reset_fifo_sync_diff_counter(i2s_dev_t *hw) +{ + hw->cnt_diff.tx_cnt_diff_rst = 1; + hw->cnt_diff.tx_cnt_diff_rst = 0; +} + +/** + * @brief Set TX FIFO synchronization difference counter software threshold + * @note It determines the up threshold that the hardware synchronize the data automatically. + * - If diff_count <= sw_threshold, the hardware will synchronize the data automatically. + * - If diff_count > sw_threshold, the automatic synchronization is not proper for this case, + * interrupt will be triggered to let the software decide how to handle this case. + * + * @param hw Peripheral I2S hardware instance address. + * @param thresh The threshold that send + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_set_fifo_sync_diff_conter_sw_threshold(i2s_dev_t *hw, uint32_t thresh) +{ + hw->sync_sw_thres.tx_cnt_diff_sw_thres = thresh; +} + +/** + * @brief Set TX FIFO synchronization difference counter hardware threshold + * @note It determines the down threshold that the hardware synchronize the data automatically. + * - If diff_count < hw_threshold, synchronization check pass, do nothing + * - If diff_count >= hw_threshold, the hardware will synchronize the data automatically. + * + * @param hw Peripheral I2S hardware instance address. + * @param thresh The threshold that send + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_set_fifo_sync_diff_conter_hw_threshold(i2s_dev_t *hw, uint32_t thresh) +{ + hw->sync_hw_thres.tx_cnt_diff_hw_thres = thresh; +} + +/** + * @brief Set TX FIFO synchronization hardware data supplementation mode + * @note It determines the supplementation data when the actual sent data is less than the `diff_count - threshold` + * + * @param hw Peripheral I2S hardware instance address. + * @param mode Data supplementation mode + * - 0: Supplement the last data + * - 1: Supplement the data configured in `hw_sync_data` reg + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_set_hw_fifo_sync_suppl_mode(i2s_dev_t *hw, uint32_t mode) +{ + hw->hw_sync_conf.tx_hw_sync_suppl_mode = mode; +} + +/** + * @brief Set TX FIFO synchronization hardware supplementation data when `tx_hw_sync_suppl_mode` is 1 + * + * @param hw Peripheral I2S hardware instance address. + * @param data Data to be supplemented when `tx_hw_sync_suppl_mode` is 1 + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_set_hw_fifo_sync_static_suppl_data(i2s_dev_t *hw, uint32_t data) +{ + hw->hw_sync_data.tx_hw_sync_suppl_data = data; +} + +/** + * @brief Set the TX ETM synchronization ideal count + * + * @param hw Peripheral I2S hardware instance address. + * @param ideal_cnt The ideal FIFO count when I2S_ETM_TASK_SYNC_CHECK triggered. + */ +__attribute__((always_inline)) +static inline void i2s_ll_tx_set_etm_sync_ideal_cnt(i2s_dev_t *hw, uint32_t ideal_cnt) +{ + hw->ideal_cnt.tx_ideal_cnt = ideal_cnt; +} + /** * @brief Set the TX ETM threshold of REACH_THRESH event * diff --git a/components/hal/include/hal/i2s_types.h b/components/hal/include/hal/i2s_types.h index 773a8d1ac5..90c0d7a213 100644 --- a/components/hal/include/hal/i2s_types.h +++ b/components/hal/include/hal/i2s_types.h @@ -234,7 +234,7 @@ typedef enum { I2S_ETM_TASK_START, /*!< Start the I2S channel */ I2S_ETM_TASK_STOP, /*!< Stop the I2S channel */ #if SOC_I2S_SUPPORTS_ETM_SYNC - I2S_ETM_TASK_SYNC_CHECK, /*!< Stop the I2S channel */ + I2S_ETM_TASK_SYNC_CHECK, /*!< Check the I2S TX channel sync status */ #endif I2S_ETM_TASK_MAX, /*!< Maximum number of tasks */ } i2s_etm_task_type_t;