From f17edba20b625e4216afad82a9762088b433f883 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Mon, 14 Mar 2022 11:34:46 +0800 Subject: [PATCH] i2s: extract std/pdm/tdm modes Type structures of these modes are defined. Driver and HAL layer are modified to fit these concepts. --- components/driver/i2s.c | 2216 +++++++---------- .../driver/include/esp_private/i2s_platform.h | 2 +- components/esp_lcd/src/esp_lcd_panel_io_i2s.c | 7 +- components/hal/adc_hal.c | 7 +- components/hal/esp32/include/hal/i2s_ll.h | 140 +- components/hal/esp32c3/include/hal/i2s_ll.h | 179 +- components/hal/esp32h2/include/hal/i2s_ll.h | 179 +- components/hal/esp32s2/include/hal/i2s_ll.h | 130 +- components/hal/esp32s3/include/hal/i2s_ll.h | 187 +- components/hal/i2s_hal.c | 582 ++--- components/hal/include/hal/i2s_hal.h | 557 ++--- components/hal/include/hal/i2s_pdm.h | 195 ++ components/hal/include/hal/i2s_std.h | 175 ++ components/hal/include/hal/i2s_tdm.h | 163 ++ components/hal/include/hal/i2s_types_priv.h | 107 + .../soc/esp32/include/soc/Kconfig.soc_caps.in | 16 + components/soc/esp32/include/soc/soc_caps.h | 8 +- .../esp32c3/include/soc/Kconfig.soc_caps.in | 12 +- components/soc/esp32c3/include/soc/soc_caps.h | 4 +- .../esp32h2/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32h2/include/soc/soc_caps.h | 2 + .../esp32s2/include/soc/Kconfig.soc_caps.in | 20 + components/soc/esp32s2/include/soc/soc_caps.h | 13 +- .../esp32s3/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32s3/include/soc/soc_caps.h | 2 + 25 files changed, 2793 insertions(+), 2126 deletions(-) create mode 100644 components/hal/include/hal/i2s_pdm.h create mode 100644 components/hal/include/hal/i2s_std.h create mode 100644 components/hal/include/hal/i2s_tdm.h create mode 100644 components/hal/include/hal/i2s_types_priv.h diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 562d36f0ce..5eafedf65b 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -14,14 +14,15 @@ #include "soc/lldesc.h" #include "driver/gpio.h" -#include "driver/i2s.h" #include "hal/gpio_hal.h" #include "hal/i2s_hal.h" +#include "hal/i2s_std.h" +#include "hal/i2s_pdm.h" +#include "hal/i2s_tdm.h" +#include "driver/i2s.h" #if SOC_I2S_SUPPORTS_DAC #include "driver/dac.h" -#endif // SOC_I2S_SUPPORTS_DAC - -#if SOC_I2S_SUPPORTS_ADC +#include "driver/adc.h" #include "adc1_private.h" #endif // SOC_I2S_SUPPORTS_ADC @@ -52,12 +53,8 @@ static const char *TAG = "I2S"; #define I2S_DMA_BUFFER_MAX_SIZE 4092 -#if !SOC_GDMA_SUPPORTED -#define I2S_INTR_IN_SUC_EOF BIT(9) -#define I2S_INTR_OUT_EOF BIT(12) -#define I2S_INTR_IN_DSCR_ERR BIT(13) -#define I2S_INTR_OUT_DSCR_ERR BIT(14) -#define I2S_INTR_MAX (~0) +#if SOC_I2S_SUPPORTS_ADC_DAC +#define I2S_COMM_MODE_ADC_DAC -1 #endif /** @@ -82,8 +79,6 @@ typedef struct { i2s_port_t i2s_num; /*!< I2S port number*/ int queue_size; /*!< I2S event queue size*/ QueueHandle_t i2s_queue; /*!< I2S queue handler*/ - int dma_desc_num; /*!< DMA buffer count, number of buffer*/ - int dma_frame_num; /*!< DMA buffer length, length of each buffer*/ uint32_t last_buf_size; /*!< DMA last buffer size */ i2s_dma_t *tx; /*!< DMA Tx buffer*/ i2s_dma_t *rx; /*!< DMA Rx buffer*/ @@ -91,8 +86,10 @@ typedef struct { gdma_channel_handle_t rx_dma_chan; /*!< I2S rx gDMA channel handle*/ gdma_channel_handle_t tx_dma_chan; /*!< I2S tx gDMA channel handle*/ #else - i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ + intr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ #endif + uint32_t dma_desc_num; + uint32_t dma_frame_num; bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ bool use_apll; /*!< I2S use APLL clock */ int fixed_mclk; /*!< I2S fixed MLCK clock */ @@ -102,247 +99,29 @@ typedef struct { esp_pm_lock_handle_t pm_lock; #endif i2s_hal_context_t hal; /*!< I2S hal context*/ - i2s_hal_config_t hal_cfg; /*!< I2S hal configurations*/ + + /* New config */ + i2s_dir_t dir; + i2s_role_t role; + i2s_comm_mode_t mode; + void *slot_cfg; + void *clk_cfg; + uint32_t active_slot; /*!< Active slot number */ + uint32_t total_slot; /*!< Total slot number */ } i2s_obj_t; -static i2s_obj_t *p_i2s[SOC_I2S_NUM]; +static i2s_obj_t *p_i2s[SOC_I2S_NUM] = { + [0 ... SOC_I2S_NUM - 1] = NULL, +}; static portMUX_TYPE i2s_platform_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE i2s_spinlock[SOC_I2S_NUM] = { [0 ... SOC_I2S_NUM - 1] = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, }; -#if SOC_I2S_SUPPORTS_ADC -static int _i2s_adc_unit = -1; -static int _i2s_adc_channel = -1; -#endif - -/* - * This block is an overview of APIs in i2s.c - * Functions with [main] tag are summary functions that provide main i2s service - * Functions with [helper] tag are helper functions that served for summary functions - * Functions with [intr] tag are interrupt handling functions or interrupt callback functions - ------------------------------------------------------------- - I2S GPIO operation - ------------------------------------------------------------- - - [helper] gpio_matrix_out_check_and_set - - [helper] gpio_matrix_in_check_and_set - - [helper] i2s_check_set_mclk - - [main] i2s_set_pin - ------------------------------------------------------------- - I2S DMA operation - ------------------------------------------------------------- - - [intr] i2s_dma_rx_callback - - [intr] i2s_dma_tx_callback - - [intr] i2s_intr_handler_default - - [helper] i2s_dma_intr_init - - [helper] i2s_tx_reset - - [helper] i2s_rx_reset - - [helper] i2s_tx_start - - [helper] i2s_rx_start - - [helper] i2s_tx_stop - - [helper] i2s_rx_stop - ------------------------------------------------------------- - I2S buffer operation - ------------------------------------------------------------- - - [helper] i2s_get_buf_size - - [helper] i2s_delete_dma_buffer - - [helper] i2s_alloc_dma_buffer - - [main] i2s_realloc_dma_buffer - - [main] i2s_destroy_dma_object - - [main] i2s_create_dma_object - - [main] i2s_zero_dma_buffer - ------------------------------------------------------------- - I2S clock operation - ------------------------------------------------------------- - - [helper] i2s_config_source_clock - - [helper] i2s_calculate_adc_dac_clock - - [helper] i2s_calculate_pdm_tx_clock - - [helper] i2s_calculate_pdm_rx_clock - - [helper] i2s_calculate_common_clock - - [main] i2s_calculate_clock - ------------------------------------------------------------- - I2S configuration - ------------------------------------------------------------- - - [helper] i2s_get_max_channel_num - - [helper] i2s_get_active_channel_num - - [helper] i2s_set_dac_mode - - [helper] _i2s_adc_mode_recover - - [main] i2s_set_adc_mode - - [main] i2s_adc_enable - - [main] i2s_adc_disable - - [helper] i2s_set_sample_rates - - [main] i2s_pcm_config - - [helper] i2s_set_pdm_rx_down_sample - - [helper] i2s_set_pdm_tx_up_sample - - [helper] i2s_check_cfg_validity - - [helper] i2s_tx_set_clk_and_channel - - [helper] i2s_rx_set_clk_and_channel - - [main] i2s_get_clk - - [main] i2s_set_clk - ------------------------------------------------------------- - I2S driver operation - ------------------------------------------------------------- - - [main] i2s_start - - [main] i2s_stop - - [helper] i2s_driver_init - - [helper] i2s_dma_object_init - - [main] i2s_driver_install - - [main] i2s_driver_uninstall - - [main] i2s_write - - [main] i2s_write_expand - - [main] i2s_read - -------------------------------------------------------------*/ - -/*------------------------------------------------------------- - I2S GPIO operation - -------------------------------------------------------------*/ -/** - * @brief I2S GPIO matrix set ouput - * - * @param gpio GPIO number - * @param singal_idx GPIO singnal ID, refer to 'gpio_sig_map.h' - * @param out_inv Output invert enable - * @param oen_inv Output eanble control invert enable - */ -static void gpio_matrix_out_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) -{ - //if pin = -1, do not need to configure - if (gpio != -1) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - gpio_set_direction(gpio, GPIO_MODE_OUTPUT); - esp_rom_gpio_connect_out_signal(gpio, signal_idx, out_inv, oen_inv); - } -} - -/** - * @brief I2S GPIO matrix set input - * - * @param gpio GPIO number - * @param singal_idx GPIO singnal ID, refer to 'gpio_sig_map.h' - * @param out_inv Output invert enable - * @param oen_inv Output eanble control invert enable - */ -static void gpio_matrix_in_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool inv) -{ - if (gpio != -1) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - /* Set direction, for some GPIOs, the input function are not enabled as default */ - gpio_set_direction(gpio, GPIO_MODE_INPUT); - esp_rom_gpio_connect_in_signal(gpio, signal_idx, inv); - } -} - -/** - * @brief I2S set GPIO for mclk - * - * @param i2s_num I2S device number - * @param gpio_num GPIO number for mclk - * @return - * - ESP_OK Check or set success - * - ESP_ERR_INVALID_ARG GPIO is not available - */ -static esp_err_t i2s_check_set_mclk(i2s_port_t i2s_num, gpio_num_t gpio_num) -{ - if (gpio_num == -1) { - return ESP_OK; - } -#if CONFIG_IDF_TARGET_ESP32 - ESP_RETURN_ON_FALSE((gpio_num == GPIO_NUM_0 || gpio_num == GPIO_NUM_1 || gpio_num == GPIO_NUM_3), - ESP_ERR_INVALID_ARG, TAG, - "ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num); - bool is_i2s0 = i2s_num == I2S_NUM_0; - if (gpio_num == GPIO_NUM_0) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFFF0 : 0xFFFF); - } else if (gpio_num == GPIO_NUM_1) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3); - WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xF0F0 : 0xF0FF); - } else { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2); - WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFF00 : 0xFF0F); - } -#else - ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid"); - gpio_matrix_out_check_and_set(gpio_num, i2s_periph_signal[i2s_num].mck_out_sig, 0, 0); -#endif - ESP_LOGI(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num); - return ESP_OK; -} - -/** - * @brief Set gpio pins for I2S - * - * @param i2s_num I2S device number - * @param pin Pin configuration - * @return - * - ESP_OK Set pin success - * - ESP_ERR_INVALID_ARG GPIO is not available - */ -esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) -{ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - if (pin == NULL) { -#if SOC_I2S_SUPPORTS_DAC - return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); -#else - return ESP_ERR_INVALID_ARG; -#endif - } - /* Check validity of selected pins */ - ESP_RETURN_ON_FALSE((pin->bck_io_num == -1 || GPIO_IS_VALID_GPIO(pin->bck_io_num)), - ESP_ERR_INVALID_ARG, TAG, "bck_io_num invalid"); - ESP_RETURN_ON_FALSE((pin->ws_io_num == -1 || GPIO_IS_VALID_GPIO(pin->ws_io_num)), - ESP_ERR_INVALID_ARG, TAG, "ws_io_num invalid"); - ESP_RETURN_ON_FALSE((pin->data_out_num == -1 || GPIO_IS_VALID_GPIO(pin->data_out_num)), - ESP_ERR_INVALID_ARG, TAG, "data_out_num invalid"); - ESP_RETURN_ON_FALSE((pin->data_in_num == -1 || GPIO_IS_VALID_GPIO(pin->data_in_num)), - ESP_ERR_INVALID_ARG, TAG, "data_in_num invalid"); - - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_SLAVE) { - /* For "tx + rx + slave" or "rx + slave" mode, we should select RX signal index for ws and bck */ - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_rx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_rx_bck_sig, 0); - /* For "tx + slave" mode, we should select TX signal index for ws and bck */ - } else { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_tx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_tx_bck_sig, 0); - } - } else { - /* mclk only available in master mode */ - ESP_RETURN_ON_ERROR(i2s_check_set_mclk(i2s_num, pin->mck_io_num), TAG, "mclk config failed"); - /* For "tx + rx + master" or "tx + master" mode, we should select TX signal index for ws and bck */ - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_tx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_tx_bck_sig, 0, 0); - /* For "rx + master" mode, we should select RX signal index for ws and bck */ - } else { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_rx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_rx_bck_sig, 0, 0); - } - } - - /* Set data input/ouput GPIO */ - gpio_matrix_out_check_and_set(pin->data_out_num, i2s_periph_signal[i2s_num].data_out_sig, 0, 0); - gpio_matrix_in_check_and_set(pin->data_in_num, i2s_periph_signal[i2s_num].data_in_sig, 0); - return ESP_OK; -} - /*------------------------------------------------------------- I2S DMA operation -------------------------------------------------------------*/ #if SOC_GDMA_SUPPORTED -/** - * @brief GDMA rx callback function - * @note This function is called by GDMA default ISR handler - * - * @param dma_chan GDMA channel handler - * @param event_data GDMA rx event data - * @param user_data GDMA user data - * @return - * - true need yield - * - false no need - */ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -375,16 +154,6 @@ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_e return need_awoke; } -/** - * @brief GDMA tx callback function - * @note This function is called by GDMA default ISR handler - * - * @param dma_chan GDMA channel handler - * @param event_data GDMA tx event data - * @param user_data GDMA user data - * @return - * - whether need yield - */ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -421,13 +190,6 @@ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_e } #else - -/** - * @brief I2S defalut interrupt handler - * @note This function is triggered by I2S dedicated DMA interrupt - * - * @param arg Argument transport to ISR, here is the pointer to I2S object - */ static void IRAM_ATTR i2s_intr_handler_default(void *arg) { i2s_obj_t *p_i2s = (i2s_obj_t *) arg; @@ -442,7 +204,7 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) portBASE_TYPE need_awoke = 0; portBASE_TYPE tmp = 0; uint32_t finish_desc = 0; - if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) { + if ((status & I2S_LL_EVENT_TX_DSCR_ERR) || (status & I2S_LL_EVENT_RX_DSCR_ERR)) { ESP_EARLY_LOGE(TAG, "dma error, interrupt status: 0x%08x", status); if (p_i2s->i2s_queue) { i2s_event.type = I2S_EVENT_DMA_ERROR; @@ -455,7 +217,7 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) } } - if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) { + if ((status & I2S_LL_EVENT_TX_EOF) && p_i2s->tx) { i2s_hal_get_out_eof_des_addr(&(p_i2s->hal), &finish_desc); i2s_event.size = ((lldesc_t *)finish_desc)->size; // All buffers are empty. This means we have an underflow on our hands. @@ -483,7 +245,7 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) } } - if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) { + if ((status & I2S_LL_EVENT_RX_EOF) && p_i2s->rx) { // All buffers are full. This means we have an overflow. i2s_hal_get_in_eof_des_addr(&(p_i2s->hal), &finish_desc); i2s_event.size = ((lldesc_t *)finish_desc)->size; @@ -512,18 +274,6 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) } #endif - -/** - * @brief I2S DMA interrupt initialization - * @note I2S will use GDMA if chip supports, and the interrupt is triggered by GDMA. - * - * @param i2s_num I2S device number - * @return - * - ESP_OK I2S DMA interrupt initialize success - * - ESP_ERR_NOT_FOUND GDMA channel not found - * - ESP_ERR_INVALID_ARG Invalid arguments - * - ESP_ERR_INVALID_STATE GDMA state error - */ static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) { #if SOC_GDMA_SUPPORTED @@ -543,7 +293,7 @@ static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) /* Set GDMA config */ gdma_channel_alloc_config_t dma_cfg = {}; - if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + if ( p_i2s[i2s_num]->dir & I2S_DIR_TX) { dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; /* Register a new GDMA tx channel */ ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->tx_dma_chan), TAG, "Register tx dma channel error"); @@ -552,7 +302,7 @@ static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) /* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */ gdma_register_tx_event_callbacks(p_i2s[i2s_num]->tx_dma_chan, &cb, p_i2s[i2s_num]); } - if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + if ( p_i2s[i2s_num]->dir & I2S_DIR_RX) { dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; /* Register a new GDMA rx channel */ ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->rx_dma_chan), TAG, "Register rx dma channel error"); @@ -568,22 +318,17 @@ static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) return ESP_OK; } -/** - * @brief I2S tx reset - * - * @param i2s_num I2S device number - */ static void i2s_tx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->tx->curr_ptr = NULL; p_i2s[i2s_num]->tx->rw_pos = 0; - i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_reset(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_reset(p_i2s[i2s_num]->tx_dma_chan); #else - i2s_hal_reset_txdma(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_reset_dma(&(p_i2s[i2s_num]->hal)); #endif - i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_reset_fifo(&(p_i2s[i2s_num]->hal)); } /** @@ -595,118 +340,125 @@ static void i2s_rx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->rx->curr_ptr = NULL; p_i2s[i2s_num]->rx->rw_pos = 0; - i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_reset(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_reset(p_i2s[i2s_num]->rx_dma_chan); #else - i2s_hal_reset_rxdma(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_reset_dma(&(p_i2s[i2s_num]->hal)); #endif - i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_reset_fifo(&(p_i2s[i2s_num]->hal)); } -/** - * @brief I2S tx start - * - * @param i2s_num I2S device number - */ static void i2s_tx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED gdma_start(p_i2s[i2s_num]->tx_dma_chan, (uint32_t) p_i2s[i2s_num]->tx->desc[0]); #else - i2s_hal_enable_tx_dma(&(p_i2s[i2s_num]->hal)); - i2s_hal_enable_tx_intr(&(p_i2s[i2s_num]->hal)); - i2s_hal_start_tx_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->tx->desc[0]); + i2s_hal_tx_enable_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_enable_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_start_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->tx->desc[0]); #endif - i2s_hal_start_tx(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_start(&(p_i2s[i2s_num]->hal)); } -/** - * @brief I2S rx start - * - * @param i2s_num I2S device number - */ static void i2s_rx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED gdma_start(p_i2s[i2s_num]->rx_dma_chan, (uint32_t) p_i2s[i2s_num]->rx->desc[0]); #else - i2s_hal_enable_rx_dma(&(p_i2s[i2s_num]->hal)); - i2s_hal_enable_rx_intr(&(p_i2s[i2s_num]->hal)); - i2s_hal_start_rx_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->rx->desc[0]); + i2s_hal_rx_enable_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_enable_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_start_link(&(p_i2s[i2s_num]->hal), (uint32_t) p_i2s[i2s_num]->rx->desc[0]); #endif - i2s_hal_start_rx(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_start(&(p_i2s[i2s_num]->hal)); } -/** - * @brief I2S tx stop - * - * @param i2s_num I2S device number - */ static void i2s_tx_stop(i2s_port_t i2s_num) { - i2s_hal_stop_tx(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_stop(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->tx_dma_chan); #else - i2s_hal_stop_tx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_tx_intr(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_tx_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_stop_link(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_disable_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_tx_disable_dma(&(p_i2s[i2s_num]->hal)); #endif } -/** - * @brief I2S rx stop - * - * @param i2s_num I2S device number - */ static void i2s_rx_stop(i2s_port_t i2s_num) { - i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_stop(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->rx_dma_chan); #else - i2s_hal_stop_rx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_rx_intr(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_rx_dma(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_stop_link(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_disable_intr(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_disable_dma(&(p_i2s[i2s_num]->hal)); #endif } +esp_err_t i2s_start(i2s_port_t i2s_num) +{ + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + //start DMA link + I2S_ENTER_CRITICAL(i2s_num); + + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_tx_reset(i2s_num); + i2s_tx_start(i2s_num); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_rx_reset(i2s_num); + i2s_rx_start(i2s_num); + } +#if !SOC_GDMA_SUPPORTED + esp_intr_enable(p_i2s[i2s_num]->i2s_isr_handle); +#endif + I2S_EXIT_CRITICAL(i2s_num); + return ESP_OK; +} + +esp_err_t i2s_stop(i2s_port_t i2s_num) +{ + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + I2S_ENTER_CRITICAL(i2s_num); +#if !SOC_GDMA_SUPPORTED + esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle); +#endif + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_tx_stop(i2s_num); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_rx_stop(i2s_num); + } +#if !SOC_GDMA_SUPPORTED + i2s_hal_clear_intr_status(&(p_i2s[i2s_num]->hal), I2S_INTR_MAX); +#endif + I2S_EXIT_CRITICAL(i2s_num); + return ESP_OK; +} + /*------------------------------------------------------------- I2S buffer operation -------------------------------------------------------------*/ -/** - * @brief I2S get DMA buffer size - * - * @param i2s_num I2S device number - * @return - * - DMA buffer size - */ static inline uint32_t i2s_get_buf_size(i2s_port_t i2s_num) { + i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg; /* Calculate bytes per sample, align to 16 bit */ - uint32_t bytes_per_sample = ((p_i2s[i2s_num]->hal_cfg.sample_bits + 15) / 16) * 2; + uint32_t bytes_per_sample = ((slot_cfg->data_bit_width + 15) / 16) * 2; /* The DMA buffer limitation is 4092 bytes */ - uint32_t bytes_per_frame = bytes_per_sample * p_i2s[i2s_num]->hal_cfg.active_chan; + uint32_t bytes_per_frame = bytes_per_sample * p_i2s[i2s_num]->active_slot; p_i2s[i2s_num]->dma_frame_num = (p_i2s[i2s_num]->dma_frame_num * bytes_per_frame > I2S_DMA_BUFFER_MAX_SIZE) ? I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame : p_i2s[i2s_num]->dma_frame_num; return p_i2s[i2s_num]->dma_frame_num * bytes_per_frame; } -/** - * @brief Delete DMA buffer and descriptor - * - * @param i2s_num I2S device number - * @param dma_obj DMA object - * @return - * - ESP_OK DMA buffer delete success - * - ESP_ERR_INVALID_ARG dma_obj is NULL - */ static esp_err_t i2s_delete_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) { ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); + uint32_t buf_cnt = p_i2s[i2s_num]->dma_desc_num; /* Loop to destroy every descriptor and buffer */ - for (int cnt = 0; cnt < p_i2s[i2s_num]->dma_desc_num; cnt++) { + for (int cnt = 0; cnt < buf_cnt; cnt++) { if (dma_obj->desc && dma_obj->desc[cnt]) { free(dma_obj->desc[cnt]); dma_obj->desc[cnt] = NULL; @@ -719,15 +471,6 @@ static esp_err_t i2s_delete_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) return ESP_OK; } -/** - * @brief Allocate memory for DMA buffer and descriptor - * - * @param i2s_num I2S device number - * @param dma_obj DMA object - * @return - * - ESP_OK Allocate success - * - ESP_ERR_NO_MEM No memory for DMA buffer - */ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) { esp_err_t ret = ESP_OK; @@ -759,6 +502,9 @@ static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) /* Link to the next descriptor */ dma_obj->desc[cnt]->empty = (uint32_t)((cnt < (buf_cnt - 1)) ? (dma_obj->desc[cnt + 1]) : dma_obj->desc[0]); } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_ll_rx_set_eof_num(p_i2s[i2s_num]->hal.dev, dma_obj->buf_size); + } ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_desc_num=%d", dma_obj->buf_size, buf_cnt); return ESP_OK; err: @@ -767,16 +513,6 @@ err: return ret; } -/** - * @brief Realloc I2S dma buffer - * - * @param i2s_num I2S device number - * @param dma_obj DMA object - * - * @return - * - ESP_OK Success - * - ESP_ERR_NO_MEM No memory for I2S tx dma buffer - */ static esp_err_t i2s_realloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) { ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); @@ -789,15 +525,6 @@ static esp_err_t i2s_realloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) return ESP_OK; } -/** - * @brief I2S destroy the whole DMA object - * - * @param i2s_num I2S device number - * @param dma Secondary pointer to the DMA object - * @return - * - ESP_OK I2S DMA buffer has been destroyed successfully - * - ESP_ERR_INVALID_ARG I2S driver has not installed yet - */ static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) { /* Check if DMA truely need destroy */ @@ -828,26 +555,13 @@ static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) return ESP_OK; } -/** - * @brief Create I2S DMA object - * @note This function only create I2S DMA object but will not allocate memory - * for DMA descriptor and buffer, call 'i2s_alloc_dma_buffer' additionally to - * allocate DMA buffer - * - * @param i2s_num I2S device number - * @param dma The secondary pointer of DMA object - * @return - * - ESP_OK The pointer of DMA object - * - ESP_ERR_INVALID_ARG NULL pointer error or DMA object has been created - * - ESP_ERR_NO_MEM No memory for new DMA object - */ static esp_err_t i2s_create_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) { ESP_RETURN_ON_FALSE(dma, ESP_ERR_INVALID_ARG, TAG, "DMA object secondary pointer is NULL"); ESP_RETURN_ON_FALSE((*dma == NULL), ESP_ERR_INVALID_ARG, TAG, "DMA object has been created"); uint32_t buf_cnt = p_i2s[i2s_num]->dma_desc_num; /* Allocate new DMA structure */ - *dma = (i2s_dma_t *) malloc(sizeof(i2s_dma_t)); + *dma = (i2s_dma_t *) calloc(1, sizeof(i2s_dma_t)); ESP_RETURN_ON_FALSE(*dma, ESP_ERR_NO_MEM, TAG, "DMA object allocate failed"); /* Allocate DMA buffer poiter */ (*dma)->buf = (char **)heap_caps_calloc(buf_cnt, sizeof(char *), MALLOC_CAP_DMA); @@ -877,55 +591,9 @@ err: return ESP_ERR_NO_MEM; } -/** - * @brief Zero the contents of the TX DMA buffer. - * @note Pushes zero-byte samples into the TX DMA buffer, until it is full. - * - * @param i2s_num I2S device number - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ -esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) -{ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - - /* Clear I2S RX DMA buffer */ - if (p_i2s[i2s_num]->rx && p_i2s[i2s_num]->rx->buf != NULL && p_i2s[i2s_num]->rx->buf_size != 0) { - for (int i = 0; i < p_i2s[i2s_num]->dma_desc_num; i++) { - memset(p_i2s[i2s_num]->rx->buf[i], 0, p_i2s[i2s_num]->rx->buf_size); - } - } - - /* Clear I2S TX DMA buffer */ - if (p_i2s[i2s_num]->tx && p_i2s[i2s_num]->tx->buf != NULL && p_i2s[i2s_num]->tx->buf_size != 0) { - /* Finish to write all tx data */ - int bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; - if (bytes_left) { - size_t zero_bytes = 0, bytes_written; - i2s_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); - } - for (int i = 0; i < p_i2s[i2s_num]->dma_desc_num; i++) { - memset(p_i2s[i2s_num]->tx->buf[i], 0, p_i2s[i2s_num]->tx->buf_size); - } - } - return ESP_OK; -} - /*------------------------------------------------------------- I2S clock operation -------------------------------------------------------------*/ -/** - * @brief Config I2S source clock and get its frequency - * - * @param i2s_num I2S device number - * @param use_apll Whether use apll, only take effect when chip supports - * @param mclk module clock - * - * @return - * - 0 use I2S_CLK_APLL as clock source, no I2S system clock to set - * - I2S_LL_BASE_CLK use I2S_CLK_D2CLK as clock source, return APB clock frequency - */ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint32_t mclk) { #if SOC_I2S_SUPPORTS_APLL @@ -949,228 +617,155 @@ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint3 ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq); } ESP_LOGI(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq); - /* Set I2S_APLL as I2S module clock source */ - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_APLL); /* In APLL mode, there is no sclk but only mclk, so return 0 here to indicate APLL mode */ return real_freq; } - /* Set I2S_D2CLK (160M) as default I2S module clock source */ - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); return I2S_LL_BASE_CLK; #else if (use_apll) { ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source"); } - /* Set I2S_D2CLK (160M) as I2S module clock source */ - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); return I2S_LL_BASE_CLK; #endif } #if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC -/** - * @brief I2S calculate clock for built-in ADC/DAC mode - * - * @param i2s_num I2S device number - * @param clk_cfg Struct to restore clock confiuration - * @return - * - ESP_OK Get clock success - * - ESP_ERR_INVALID_ARG Invalid args - */ -static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_clock_info_t *clk_info) { - ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); - ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_ARG, TAG, "current mode is not built-in ADC/DAC"); - + /* For ADC/DAC mode, the built-in ADC/DAC is driven by 'mclk' instead of 'bclk' + * 'bclk' should be fixed to the double of sample rate + * 'bclk_div' is the real coefficient that affects the slot bit */ + i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg; + uint32_t slot_bits = slot_cfg->slot_bit_width; /* Set I2S bit clock */ - clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_AD_BCK_FACTOR; + clk_info->bclk = clk_cfg->sample_rate_hz * I2S_LL_AD_BCK_FACTOR; /* Set I2S bit clock default division */ - clk_cfg->bclk_div = p_i2s[i2s_num]->hal_cfg.chan_bits; - /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ - clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? - p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + clk_info->bclk_div = slot_bits; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = bclk * bclk_div */ + clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div; /* Calculate bclk_div = mclk / bclk */ - clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + clk_info->bclk_div = clk_info->mclk / clk_info->bclk; /* Get I2S system clock by config source clock */ - clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + clk_info->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_info->mclk); /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ - clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->sclk / (float)clk_info->mclk > 1.99, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large, the mclk division is below minimum value 2"); + ESP_RETURN_ON_FALSE(clk_info->mclk_div < 256, ESP_ERR_INVALID_ARG, TAG, "sample rate is too small, the mclk division exceed the maximum value 255"); return ESP_OK; } #endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC #if SOC_I2S_SUPPORTS_PDM_TX -/** - * @brief I2S calculate clock for PDM tx mode - * - * @param i2s_num I2S device number - * @param clk_cfg Struct to restore clock confiuration - * @return - * - ESP_OK Get clock success - * - ESP_ERR_INVALID_ARG Invalid args - */ -static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_clock_info_t *clk_info) { - ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); - ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); + i2s_pdm_tx_clk_config_t *clk_cfg = (i2s_pdm_tx_clk_config_t *)p_i2s[i2s_num]->clk_cfg; - int fp = i2s_hal_get_tx_pdm_fp(&(p_i2s[i2s_num]->hal)); - int fs = i2s_hal_get_tx_pdm_fs(&(p_i2s[i2s_num]->hal)); + int fp = clk_cfg->up_sample_fp; + int fs = clk_cfg->up_sample_fs; /* Set I2S bit clock */ - clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * fp / fs; + clk_info->bclk = clk_cfg->sample_rate_hz * I2S_LL_PDM_BCK_FACTOR * fp / fs; /* Set I2S bit clock default division */ - clk_cfg->bclk_div = 8; - /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ - clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? - p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + clk_info->bclk_div = 8; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate_hz * multiple */ + clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div; /* Calculate bclk_div = mclk / bclk */ - clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + clk_info->bclk_div = clk_info->mclk / clk_info->bclk; /* Get I2S system clock by config source clock */ - clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + clk_info->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_info->mclk); /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ - clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->sclk / (float)clk_info->mclk > 1.99, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large, the mclk division is below minimum value 2"); + ESP_RETURN_ON_FALSE(clk_info->mclk_div < 256, ESP_ERR_INVALID_ARG, TAG, "sample rate is too small, the mclk division exceed the maximum value 255"); return ESP_OK; } #endif // SOC_I2S_SUPPORTS_PDM_TX #if SOC_I2S_SUPPORTS_PDM_RX -/** - * @brief I2S calculate clock for PDM rx mode - * - * @param i2s_num I2S device number - * @param clk_cfg Struct to restore clock confiuration - * @return - * - ESP_OK Get clock success - * - ESP_ERR_INVALID_ARG Invalid args - */ -static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_clock_info_t *clk_info) { - ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); - ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); - - i2s_pdm_dsr_t dsr; - i2s_hal_get_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), &dsr); + i2s_pdm_rx_clk_config_t *clk_cfg = (i2s_pdm_rx_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_pdm_dsr_t dsr = clk_cfg->dn_sample_mode; /* Set I2S bit clock */ - clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); + clk_info->bclk = clk_cfg->sample_rate_hz * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); /* Set I2S bit clock default division */ - clk_cfg->bclk_div = 8; - /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ - clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? - p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + clk_info->bclk_div = 8; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate_hz * multiple */ + clk_info->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_info->bclk * clk_info->bclk_div; /* Calculate bclk_div = mclk / bclk */ - clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + clk_info->bclk_div = clk_info->mclk / clk_info->bclk; /* Get I2S system clock by config source clock */ - clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + clk_info->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_info->mclk); /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ - clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->sclk / (float)clk_info->mclk > 1.99, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large, the mclk division is below minimum value 2"); + ESP_RETURN_ON_FALSE(clk_info->mclk_div < 256, ESP_ERR_INVALID_ARG, TAG, "sample rate is too small, the mclk division exceed the maximum value 255"); return ESP_OK; } #endif // SOC_I2S_SUPPORTS_PDM_RX - -/** - * @brief I2S calculate clock for common mode (philip, MSB, PCM) - * - * @param i2s_num I2S device number - * @param clk_cfg Struct to restore clock confiuration - * @return - * - ESP_OK Get clock success - * - ESP_ERR_INVALID_ARG Invalid args - */ -static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_clock_info_t *clk_info) { - ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); - - uint32_t rate = p_i2s[i2s_num]->hal_cfg.sample_rate; - uint32_t chan_num = p_i2s[i2s_num]->hal_cfg.total_chan < 2 ? 2 : p_i2s[i2s_num]->hal_cfg.total_chan; - uint32_t chan_bit = p_i2s[i2s_num]->hal_cfg.chan_bits; - uint32_t multi; + i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg; + uint32_t rate = clk_cfg->sample_rate_hz; + uint32_t slot_num = p_i2s[i2s_num]->total_slot < 2 ? 2 : p_i2s[i2s_num]->total_slot; + uint32_t slot_bits = slot_cfg->slot_bit_width; /* Calculate multiple */ - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_MASTER || p_i2s[i2s_num]->use_apll) { - multi = p_i2s[i2s_num]->mclk_multiple ? p_i2s[i2s_num]->mclk_multiple : I2S_MCLK_MULTIPLE_256; + if (p_i2s[i2s_num]->role == I2S_ROLE_MASTER) { + clk_info->bclk = rate * slot_num * slot_bits; + clk_info->mclk = rate * clk_cfg->mclk_multiple; + clk_info->bclk_div = clk_info->mclk / clk_info->bclk; } else { - /* Only need to set the multiple of mclk to sample rate for MASTER mode, - * because BCK and WS clock are provided by the external codec in SLAVE mode. - * The multiple should be big enough to get a high module clock which could detect the edges of externel clock more accurately, - * otherwise the data we receive or send would get a large latency and go wrong due to the slow module clock. - * But on ESP32 and ESP32S2, due to the different clock work mode in hardware, - * their multiple should be set to an appropriate range according to the sample bits, - * and this particular multiple finally aims at guaranteeing the bclk_div not smaller than 8, - * if not, the I2S may can't send data or send wrong data. - * Here use 'SOC_I2S_SUPPORTS_TDM' to differentialize other chips with ESP32 and ESP32S2. - */ -#if SOC_I2S_SUPPORTS_TDM - multi = I2S_LL_BASE_CLK / rate; -#else - multi = 64 * chan_bit; -#endif + /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 8 first */ + clk_info->bclk_div = 8; + clk_info->bclk = rate * slot_num * slot_bits; + clk_info->mclk = clk_info->bclk * clk_info->bclk_div; } - /* Set I2S bit clock */ - clk_cfg->bclk = rate * chan_num * chan_bit; - /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ - clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? - p_i2s[i2s_num]->fixed_mclk : (rate * multi); - /* Calculate bclk_div = mclk / bclk */ - clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; /* Get I2S system clock by config source clock */ - clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + clk_info->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_info->mclk); /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ - clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->mclk <= clk_info->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); return ESP_OK; } -/** - * @brief I2S calculate clocks according to the selected I2S mode - * - * @param i2s_num I2S device number - * @param clk_cfg Struct to restore clock confiuration - * @return - * - ESP_OK Claculate clock success - * - ESP_ERR_INVALID_ARG Invalid args - */ -static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) + +static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_clock_info_t *clk_info) { - /* Calculate clock for ADC mode */ -#if SOC_I2S_SUPPORTS_ADC - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { - ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "ADC clock calculate failed"); + /* Calculate clock for ADC/DAC mode */ +#if SOC_I2S_SUPPORTS_ADC_DAC + if ((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) { + ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_info), TAG, "ADC/DAC clock calculate failed"); return ESP_OK; } #endif // SOC_I2S_SUPPORTS_ADC - /* Calculate clock for DAC mode */ -#if SOC_I2S_SUPPORTS_DAC - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_DAC_BUILT_IN) { - ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "DAC clock calculate failed"); - return ESP_OK; - } -#endif // SOC_I2S_SUPPORTS_DAC /* Calculate clock for PDM mode */ -#if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM) { +#if SOC_I2S_SUPPORTS_PDM + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) { #if SOC_I2S_SUPPORTS_PDM_TX - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - ESP_RETURN_ON_ERROR(i2s_calculate_pdm_tx_clock(i2s_num, clk_cfg), TAG, "PDM TX clock calculate failed"); + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_tx_clock(i2s_num, clk_info), TAG, "PDM TX clock calculate failed"); } #endif // SOC_I2S_SUPPORTS_PDM_TX #if SOC_I2S_SUPPORTS_PDM_RX - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - ESP_RETURN_ON_ERROR(i2s_calculate_pdm_rx_clock(i2s_num, clk_cfg), TAG, "PDM RX clock calculate failed"); + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_rx_clock(i2s_num, clk_info), TAG, "PDM RX clock calculate failed"); } #endif // SOC_I2S_SUPPORTS_PDM_RX return ESP_OK; @@ -1178,7 +773,7 @@ static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *cl #endif // SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX /* Calculate clock for common mode */ - ESP_RETURN_ON_ERROR(i2s_calculate_common_clock(i2s_num, clk_cfg), TAG, "Common clock calculate failed"); + ESP_RETURN_ON_ERROR(i2s_calculate_common_clock(i2s_num, clk_info), TAG, "Common clock calculate failed"); return ESP_OK; } @@ -1186,72 +781,45 @@ static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *cl I2S configuration -------------------------------------------------------------*/ #if SOC_I2S_SUPPORTS_TDM -/** - * @brief Get max actived channel number - * - * @param chan_mask I2S channel mask that indicates which channels are actived - * @return - * - Max actived channel number - */ -static uint32_t i2s_get_max_channel_num(i2s_channel_t chan_mask) + +static uint32_t i2s_get_max_channel_num(uint32_t chan_mask) { - uint32_t max_chan = 0; - uint32_t channel = chan_mask >> 16; - for (int i = 0; channel && i < 16; i++, channel >>= 1) { - if (channel & 0x01) { - max_chan = i + 1; - } - } + int max_chan; + for (max_chan = 0; chan_mask; max_chan++, chan_mask >>= 1); /* Can't be smaller than 2 */ return max_chan < 2 ? 2 : max_chan; } + +static uint32_t i2s_get_active_channel_num(uint32_t chan_mask) +{ + uint32_t num = 0; + for (int i = 0; chan_mask; i++, chan_mask >>= 1) { + if (chan_mask & 0x01) { + num++; + } + } + return num; +} #endif -/** - * @brief Get active channel number according to channel format - * @note In 'I2S_CHANNEL_FMT_MULTIPLE' format, this function will check - * 'total_chan' and fix it if it is not correct. - * - * @param hal_cfg [input/output] I2S hal configuration structer - * @return - * - Active channel number - */ -static uint32_t i2s_get_active_channel_num(const i2s_hal_config_t *hal_cfg) +#if SOC_I2S_SUPPORTS_ADC_DAC +static void i2s_dac_set_slot_legacy(void) { - switch (hal_cfg->chan_fmt) { - case I2S_CHANNEL_FMT_RIGHT_LEFT: //fall through - case I2S_CHANNEL_FMT_ALL_RIGHT: //fall through - case I2S_CHANNEL_FMT_ALL_LEFT: - return 2; - case I2S_CHANNEL_FMT_ONLY_RIGHT: //fall through - case I2S_CHANNEL_FMT_ONLY_LEFT: - return 1; -#if SOC_I2S_SUPPORTS_TDM - case I2S_CHANNEL_FMT_MULTIPLE: { - uint32_t num = 0; - uint32_t chan_mask = hal_cfg->chan_mask >> 16; - for (int i = 0; chan_mask && i < 16; i++, chan_mask >>= 1) { - if (chan_mask & 0x01) { - num++; - } - } - return num; - } -#endif - default: - return 0; - } + i2s_dev_t *dev = p_i2s[0]->hal.dev; + i2s_slot_config_t *slot_cfg = p_i2s[0]->slot_cfg; + + i2s_ll_tx_reset(dev); + i2s_ll_tx_set_slave_mod(dev, false); + i2s_ll_tx_set_sample_bit(dev, slot_cfg->slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_tx_enable_mono_mode(dev, false); // DAC not work in mono mode + i2s_ll_tx_enable_msb_shift(dev, false); + i2s_ll_tx_set_ws_width(dev, slot_cfg->slot_bit_width); + i2s_ll_tx_enable_msb_right(dev, false); + i2s_ll_tx_enable_right_first(dev, true); + /* Should always enable fifo */ + i2s_ll_tx_force_enable_fifo_mod(dev, true); } -#if SOC_I2S_SUPPORTS_DAC -/** - * @brief I2S set built-in DAC mode - * - * @param dac_mode DAC mode - * @return - * - ESP_OK Set DAC success - * - ESP_ERR_INVALID_ARG Wrong DAC mode - */ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) { ESP_RETURN_ON_FALSE((dac_mode < I2S_DAC_CHANNEL_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s dac mode error"); @@ -1273,31 +841,32 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) } return ESP_OK; } -#endif // SOC_I2S_SUPPORTS_DAC -#if SOC_I2S_SUPPORTS_ADC -/** - * @brief ADC mode recover - * - * @return - * - ESP_OK ADC Recover success - * - ESP_ERR_INVALID_ARG ADC not initialized yet - */ +static void i2s_adc_set_slot_legacy(void) +{ + i2s_dev_t *dev = p_i2s[0]->hal.dev; + i2s_slot_config_t *slot_cfg = p_i2s[0]->slot_cfg; + // When ADC/DAC are installed as duplex mode, ADC will share the WS and BCLK clock by working in slave mode + i2s_ll_rx_set_slave_mod(dev, false); + i2s_ll_rx_set_sample_bit(dev, slot_cfg->slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_rx_enable_mono_mode(dev, true); // ADC should use mono mode to meet the sample rate + i2s_ll_rx_enable_msb_shift(dev, false); + i2s_ll_rx_set_ws_width(dev, slot_cfg->slot_bit_width); + i2s_ll_rx_enable_msb_right(dev, false); + i2s_ll_rx_enable_right_first(dev, false); + /* Should always enable fifo */ + i2s_ll_rx_force_enable_fifo_mod(dev, true); +} + +static int _i2s_adc_unit = -1; +static int _i2s_adc_channel = -1; + static esp_err_t _i2s_adc_mode_recover(void) { ESP_RETURN_ON_FALSE(((_i2s_adc_unit != -1) && (_i2s_adc_channel != -1)), ESP_ERR_INVALID_ARG, TAG, "i2s ADC recover error, not initialized..."); return adc_i2s_mode_init(_i2s_adc_unit, _i2s_adc_channel); } -/** - * @brief I2S set adc mode - * - * @param adc_unit ADC unit number - * @param adc_channel ADC channel - * @return - * - ESP_OK ADC Recover success - * - ESP_ERR_INVALID_ARG ADC not initialized yet - */ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) { ESP_RETURN_ON_FALSE((adc_unit < ADC_UNIT_2), ESP_ERR_INVALID_ARG, TAG, "i2s ADC unit error, only support ADC1 for now"); @@ -1307,161 +876,44 @@ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) return adc_i2s_mode_init(adc_unit, adc_channel); } -/** - * @brief I2S enable ADC mode - * - * @param i2s_num I2S device number - * @return - * - ESP_OK Enable ADC success - * - ESP_ERR_INVALID_ARG Invalid argument - * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC - */ esp_err_t i2s_adc_enable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX), + ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); adc1_dma_mode_acquire(); _i2s_adc_mode_recover(); i2s_rx_reset(i2s_num); - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); + return i2s_start(i2s_num); } -/** - * @brief I2S disable ADC - * - * @param i2s_num I2S device number - * @return - * - ESP_OK I2S ADC mode successfully disabled - * - ESP_ERR_INVALID_ARG Invalid argument - * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC - */ esp_err_t i2s_adc_disable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE(((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) && (p_i2s[i2s_num]->dir & I2S_DIR_RX), + ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); - i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); + i2s_hal_rx_stop(&(p_i2s[i2s_num]->hal)); adc1_lock_release(); return ESP_OK; } #endif -/** - * @brief Set sample rate used for I2S RX and TX. - * @note The bit clock rate is determined by the sample rate and i2s_config_t configuration parameters (number of channels, bits_per_sample). - * `bit_clock = rate * (number of channels) * bits_per_sample` - * - * @param i2s_num I2S device number - * @param rate I2S sample rate (ex: 8000, 44100...) - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ -esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) +static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, const i2s_config_t *cfg) { + /* Step 1: Check the validity of input parameters */ + /* Check the validity of i2s device number */ ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.sample_bits > 0), ESP_ERR_INVALID_ARG, TAG, "sample bits not set"); - return i2s_set_clk(i2s_num, rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); -} + ESP_RETURN_ON_FALSE(p_i2s[i2s_num] == NULL, ESP_ERR_INVALID_STATE, TAG, "this i2s port is in use"); + ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL"); + /* Check the size of DMA buffer */ + ESP_RETURN_ON_FALSE((cfg->dma_desc_num >= 2 && cfg->dma_desc_num <= 128), ESP_ERR_INVALID_ARG, TAG, "I2S buffer count less than 128 and more than 2"); + ESP_RETURN_ON_FALSE((cfg->dma_frame_num >= 8 && cfg->dma_frame_num <= 1024), ESP_ERR_INVALID_ARG, TAG, "I2S buffer length at most 1024 and more than 8"); -#if SOC_I2S_SUPPORTS_PCM -/** - * @brief Configure I2S a/u-law decompress or compress - * @note This function should be called after i2s driver installed - * Only take effect when the i2s 'communication_format' is set to 'I2S_COMM_FORMAT_STAND_PCM_SHORT' or 'I2S_COMM_FORMAT_STAND_PCM_LONG' - * - * @param i2s_num I2S_NUM_0 - * @param pcm_cfg Including mode selection and a/u-law decompress or compress configuration paramater - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ -esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) -{ - ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.comm_fmt & I2S_COMM_FORMAT_STAND_PCM_SHORT), - ESP_ERR_INVALID_ARG, TAG, "i2s communication mode is not PCM mode"); - i2s_stop(i2s_num); - I2S_ENTER_CRITICAL(i2s_num); - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - i2s_hal_tx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); - } else if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - i2s_hal_rx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); - } - I2S_EXIT_CRITICAL(i2s_num); - i2s_start(i2s_num); - return ESP_OK; -} -#endif -#if SOC_I2S_SUPPORTS_PDM_RX -/** - * @brief Set PDM mode down-sample rate - * In PDM RX mode, there would be 2 rounds of downsample process in hardware. - * In the first downsample process, the sampling number can be 16 or 8. - * In the second downsample process, the sampling number is fixed as 8. - * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly. - * @note After calling this function, it would call i2s_set_clk inside to update the clock frequency. - * Please call this function after I2S driver has been initialized. - * - * @param i2s_num I2S device number - * @param downsample i2s RX down sample rate for PDM mode. - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ -esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsample) -{ - ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); - i2s_stop(i2s_num); - i2s_hal_set_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), downsample); - // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); -} -#endif - -#if SOC_I2S_SUPPORTS_PDM_TX -/** - * @brief Set TX PDM mode up-sample rate - * @note If you have set PDM mode while calling 'i2s_driver_install', - * default PDM TX upsample parameters have already been set, - * no need to call this function again if you don't have to change the default configuration - * - * @param i2s_num I2S device number - * @param upsample_cfg Set I2S PDM up-sample rate configuration - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ -esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample_cfg_t *upsample_cfg) -{ - ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); - i2s_stop(i2s_num); - i2s_hal_set_tx_pdm_fpfs(&(p_i2s[i2s_num]->hal), upsample_cfg->fp, upsample_cfg->fs); - // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); -} -#endif - -/** - * @brief I2S check the validity of configuration - * - * @param i2s_num I2S device number - * @param cfg I2S HAL configuration - * @return - * - ESP_OK I2S configuration is valid - * - ESP_ERR_INVALID_ARG I2S configuration is invalid - */ -static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, i2s_hal_config_t *cfg) -{ #if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX /* Check PDM mode */ if (cfg->mode & I2S_MODE_PDM) { @@ -1486,166 +938,146 @@ static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, i2s_hal_config_t *cf /* Check the transmit/receive mode */ ESP_RETURN_ON_FALSE((cfg->mode & I2S_MODE_TX) || (cfg->mode & I2S_MODE_RX), ESP_ERR_INVALID_ARG, TAG, "I2S no TX/RX mode selected"); /* Check communication format */ - ESP_RETURN_ON_FALSE(cfg->comm_fmt && (cfg->comm_fmt < I2S_COMM_FORMAT_STAND_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid communication formats"); + ESP_RETURN_ON_FALSE(cfg->communication_format && (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid communication formats"); #endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC return ESP_OK; } -static void i2s_tx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static void i2s_set_slot_legacy(i2s_port_t i2s_num) { - i2s_hal_tx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); - i2s_hal_set_tx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); - i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + bool is_tx_slave = p_i2s[i2s_num]->role == I2S_ROLE_SLAVE; + bool is_rx_slave = is_tx_slave; + if (p_i2s[i2s_num]->dir == (I2S_DIR_TX | I2S_DIR_RX)) { + i2s_ll_share_bck_ws(p_i2s[i2s_num]->hal.dev, true); + /* Since bck and ws are shared, only tx or rx can be master + Force to set rx as slave to avoid conflict of clock signal */ + is_rx_slave = true; + } else { + i2s_ll_share_bck_ws(p_i2s[i2s_num]->hal.dev, false); + } + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_std_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, p_i2s[i2s_num]->slot_cfg ); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_std_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg ); + } + } +#if SOC_I2S_SUPPORTS_PDM + else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) { + #if SOC_I2S_SUPPORTS_PDM_TX + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_pdm_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, p_i2s[i2s_num]->slot_cfg ); + } + #endif + #if SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_pdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg ); + } + #endif + } +#endif +#if SOC_I2S_SUPPORTS_TDM + else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_tdm_set_tx_slot(&(p_i2s[i2s_num]->hal), is_tx_slave, p_i2s[i2s_num]->slot_cfg ); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_tdm_set_rx_slot(&(p_i2s[i2s_num]->hal), is_rx_slave, p_i2s[i2s_num]->slot_cfg ); + } + } +#endif +#if SOC_I2S_SUPPORTS_ADC_DAC + else if ((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_dac_set_slot_legacy(); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_adc_set_slot_legacy(); + } + } +#endif } -static void i2s_rx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +static void i2s_set_clock_legacy(i2s_port_t i2s_num) { - i2s_hal_rx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); - i2s_hal_set_rx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); - i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_clock_info_t clk_info; + i2s_calculate_clock(i2s_num, &clk_info); + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_set_tx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_set_rx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src); + } } -/** - * @brief Get clock set on particular port number. - * - * @param i2s_num I2S device number - * @return - * - sample rate - */ float i2s_get_clk(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - return (float)p_i2s[i2s_num]->hal_cfg.sample_rate; + i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + return (float)clk_cfg->sample_rate_hz; } -/** - * @brief Set clock & bit width used for I2S RX and TX. - * Similar to i2s_set_sample_rates(), but also sets bit width. - * - * 1. stop i2s - * 2. calculate mclk, bck, bck_factor - * 3. set clock configurations - * 4. realloc dma buffer if DMA buffer size changed - * 5. start i2s - * - * @param i2s_num I2S device number - * @param rate I2S sample rate (ex: 8000, 44100...) - * @param bits_cfg I2S bits configuration - * the low 16 bits is for data bits per sample in one channel (see 'i2s_bits_per_sample_t') - * the high 16 bits is for total bits in one channel (see 'i2s_bits_per_chan_t') - * high 16bits =0 means same as the bits per sample. - * @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO or specific channel in TDM mode) - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch) { - esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S%d has not installed yet", i2s_num); - i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; - /* Stop I2S */ i2s_stop(i2s_num); - /* If not the first time, update configuration */ - if (p_i2s[i2s_num]->last_buf_size) { - cfg->sample_rate = rate; - cfg->sample_bits = bits_cfg & 0xFFFF; - cfg->chan_bits = (bits_cfg >> 16) > cfg->sample_bits ? (bits_cfg >> 16) : cfg->sample_bits; + i2s_clk_config_t *clk_cfg = (i2s_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg; + + clk_cfg->sample_rate_hz = rate; + slot_cfg->data_bit_width = bits_cfg & 0xFFFF; + ESP_RETURN_ON_FALSE((slot_cfg->data_bit_width % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + slot_cfg->slot_bit_width = (bits_cfg >> 16) > slot_cfg->data_bit_width ? + (bits_cfg >> 16) : slot_cfg->data_bit_width; + ESP_RETURN_ON_FALSE((slot_cfg->slot_bit_width % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per channel"); + ESP_RETURN_ON_FALSE(((int)slot_cfg->slot_bit_width <= (int)I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + slot_cfg->slot_mode = ((ch & 0xFFFF) == I2S_CHANNEL_MONO) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO; #if SOC_I2S_SUPPORTS_TDM - /* The total channel once set during installation is not allowed to change mode - * Thus the input channel mask should within this max channel number */ - ESP_RETURN_ON_FALSE(cfg->total_chan >= i2s_get_max_channel_num(ch), ESP_ERR_INVALID_ARG, TAG, - "The max channel number can't be greater than CH%d\n", cfg->total_chan - 1); - if (ch & I2S_CHANNEL_MONO) { - cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_RIGHT; // i.e. mono - cfg->chan_mask = I2S_TDM_ACTIVE_CH0; // Only activate one channel in mono - } else { - if (cfg->total_chan == 2) { // For standard 2 channel mode - cfg->chan_fmt = I2S_CHANNEL_FMT_RIGHT_LEFT; - cfg->chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; - } else if (cfg->total_chan > 2) { // For TDM multiple channel mode - cfg->chan_fmt = I2S_CHANNEL_FMT_MULTIPLE; - cfg->chan_mask = ch & 0xFFFF0000; - } + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) { + uint32_t slot_mask = ch >> 16; + if (slot_mask == 0) { + slot_mask = (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? 1 : 2; } -#else - /* Default */ - cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; - cfg->active_chan = i2s_get_active_channel_num(cfg); - cfg->total_chan = 2; + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->total_slot >= i2s_get_max_channel_num(slot_mask), ESP_ERR_INVALID_ARG, TAG, + "The max channel number can't be greater than CH%d\n", p_i2s[i2s_num]->total_slot); + p_i2s[i2s_num]->active_slot = i2s_get_active_channel_num(slot_mask); + } else #endif - if (cfg->mode & I2S_MODE_TX) { - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (TickType_t)portMAX_DELAY); - i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); - xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - } - if (cfg->mode & I2S_MODE_RX) { - xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (TickType_t)portMAX_DELAY); - i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); - xSemaphoreGive(p_i2s[i2s_num]->rx->mux); - } + { + p_i2s[i2s_num]->active_slot = (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? 1 : 2; } - uint32_t data_bits = cfg->sample_bits; - /* Check the validity of sample bits */ - ESP_RETURN_ON_FALSE((data_bits % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); - ESP_RETURN_ON_FALSE((data_bits <= I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); - - i2s_hal_clock_cfg_t clk_cfg; - /* To get sclk, mclk, mclk_div bclk and bclk_div */ - i2s_calculate_clock(i2s_num, &clk_cfg); + i2s_set_slot_legacy(i2s_num); + i2s_set_clock_legacy(i2s_num); uint32_t buf_size = i2s_get_buf_size(i2s_num); - bool need_realloc = p_i2s[i2s_num]->last_buf_size != buf_size; + bool need_realloc = buf_size != p_i2s[i2s_num]->last_buf_size; - /* TX mode clock reset */ - if (cfg->mode & I2S_MODE_TX) { - ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->tx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); - /* Waiting for transmit finish */ - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (TickType_t)portMAX_DELAY); - i2s_tx_set_clk_and_channel(i2s_num, &clk_cfg); - /* If buffer size changed, the DMA buffer need realloc */ - if (need_realloc) { + if (need_realloc) { + esp_err_t ret = ESP_OK; + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY); p_i2s[i2s_num]->tx->buf_size = buf_size; ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->tx); + xQueueReset(p_i2s[i2s_num]->tx->queue); + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d tx DMA buffer malloc failed", i2s_num); } - /* If not the first time, update I2S tx channel style */ - if (p_i2s[i2s_num]->last_buf_size) { - i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - /* Reset the queue to avoid receive invalid data */ - xQueueReset(p_i2s[i2s_num]->tx->queue); - xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d tx DMA buffer malloc failed", i2s_num); - } - /* RX mode clock reset */ - if (cfg->mode & I2S_MODE_RX) { - ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->rx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); - /* Waiting for receive finish */ - xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (TickType_t)portMAX_DELAY); - i2s_rx_set_clk_and_channel(i2s_num, &clk_cfg); - /* If buffer size changed, the DMA buffer need realloc */ - if (need_realloc) { + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, portMAX_DELAY); p_i2s[i2s_num]->rx->buf_size = buf_size; ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->rx); - /* Reset the end-of-frame number */ - i2s_hal_set_rx_eof_num(&(p_i2s[i2s_num]->hal), buf_size); + xQueueReset(p_i2s[i2s_num]->rx->queue); + xSemaphoreGive(p_i2s[i2s_num]->rx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d rx DMA buffer malloc failed", i2s_num); } - /* If not the first time, update I2S rx channel style */ - if (p_i2s[i2s_num]->last_buf_size) { - i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - /* Reset the queue to avoid receiving invalid data */ - xQueueReset(p_i2s[i2s_num]->rx->queue); - xSemaphoreGive(p_i2s[i2s_num]->rx->mux); - ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d rx DMA buffer malloc failed", i2s_num); } /* Update last buffer size */ p_i2s[i2s_num]->last_buf_size = buf_size; @@ -1656,271 +1088,519 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ return ESP_OK; } -/*------------------------------------------------------------- - I2S driver operation - -------------------------------------------------------------*/ -/** - * @brief Start I2S driver - * @note It is not necessary to call this function after i2s_driver_install() (it is started automatically), however it is necessary to call it after i2s_stop(). - * - * @param i2s_num I2S device number - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ -esp_err_t i2s_start(i2s_port_t i2s_num) +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - //start DMA link - I2S_ENTER_CRITICAL(i2s_num); - - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - i2s_tx_reset(i2s_num); - i2s_tx_start(i2s_num); - } - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - i2s_rx_reset(i2s_num); - i2s_rx_start(i2s_num); - } -#if !SOC_GDMA_SUPPORTED - esp_intr_enable(p_i2s[i2s_num]->i2s_isr_handle); -#endif - I2S_EXIT_CRITICAL(i2s_num); - return ESP_OK; -} - -/** - * @brief Stop I2S driver - * @note There is no need to call i2s_stop() before calling i2s_driver_uninstall(). - * Disables I2S TX/RX, until i2s_start() is called. - * - * @param i2s_num I2S device number - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ -esp_err_t i2s_stop(i2s_port_t i2s_num) -{ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - I2S_ENTER_CRITICAL(i2s_num); -#if !SOC_GDMA_SUPPORTED - esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle); -#endif - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - i2s_tx_stop(i2s_num); - } - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - i2s_rx_stop(i2s_num); - } -#if !SOC_GDMA_SUPPORTED - i2s_hal_clear_intr_status(&(p_i2s[i2s_num]->hal), I2S_INTR_MAX); -#endif - I2S_EXIT_CRITICAL(i2s_num); - return ESP_OK; -} - -/** - * @brief Initialize I2S driver configurations - * - * @param i2s_num I2S device number - * @param i2s_config I2S configurations - see i2s_config_t struct - * @return - * - ESP_OK I2S initialize success - * - ESP_ERR_INVALID_ARG No channel enabled in multiple channel format - */ -static esp_err_t i2s_driver_init(i2s_port_t i2s_num, const i2s_config_t *i2s_config) -{ - ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "The pointer of I2S configuration structure is NULL"); - - /* I2S driver configuration assignment */ - p_i2s[i2s_num]->i2s_num = i2s_num; - p_i2s[i2s_num]->dma_desc_num = i2s_config->dma_desc_num; - p_i2s[i2s_num]->dma_frame_num = i2s_config->dma_frame_num; - p_i2s[i2s_num]->last_buf_size = 0; - p_i2s[i2s_num]->use_apll = i2s_config->use_apll; - p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; - p_i2s[i2s_num]->mclk_multiple = i2s_config->mclk_multiple; - p_i2s[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - - /* I2S HAL configuration assignment */ - p_i2s[i2s_num]->hal_cfg.mode = i2s_config->mode; - p_i2s[i2s_num]->hal_cfg.sample_rate = i2s_config->sample_rate; - p_i2s[i2s_num]->hal_cfg.comm_fmt = i2s_config->communication_format; - p_i2s[i2s_num]->hal_cfg.chan_fmt = i2s_config->channel_format; - p_i2s[i2s_num]->hal_cfg.sample_bits = i2s_config->bits_per_sample; - p_i2s[i2s_num]->hal_cfg.chan_bits = (uint32_t)i2s_config->bits_per_chan < (uint32_t)i2s_config->bits_per_sample ? - (uint32_t)i2s_config->bits_per_sample : (uint32_t)i2s_config->bits_per_chan; - + i2s_slot_config_t *slot_cfg = (i2s_slot_config_t *)p_i2s[i2s_num]->slot_cfg; + uint32_t mask = 0; #if SOC_I2S_SUPPORTS_TDM - /* I2S HAL TDM configuration assignment */ - p_i2s[i2s_num]->hal_cfg.left_align = i2s_config->left_align; - p_i2s[i2s_num]->hal_cfg.big_edin = i2s_config->big_edin; - p_i2s[i2s_num]->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; - p_i2s[i2s_num]->hal_cfg.skip_msk = i2s_config->skip_msk; - - /* Set chan_mask according to channel format */ - switch (i2s_config->channel_format) { - case I2S_CHANNEL_FMT_RIGHT_LEFT: // fall through - case I2S_CHANNEL_FMT_ALL_RIGHT: // fall through - case I2S_CHANNEL_FMT_ALL_LEFT: - p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; - p_i2s[i2s_num]->hal_cfg.total_chan = 2; - break; - case I2S_CHANNEL_FMT_ONLY_RIGHT: // fall through - case I2S_CHANNEL_FMT_ONLY_LEFT: - p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0; - p_i2s[i2s_num]->hal_cfg.total_chan = 2; - break; - case I2S_CHANNEL_FMT_MULTIPLE: - ESP_RETURN_ON_FALSE((i2s_config->chan_mask >> 16), ESP_ERR_INVALID_ARG, TAG, "i2s all channel are disabled"); - p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->chan_mask & 0xFFFF0000; - /* Get the max actived channel number */ - uint32_t max_channel = i2s_get_max_channel_num(p_i2s[i2s_num]->hal_cfg.chan_mask); - /* If total channel is smaller than max actived channel number then set it to the max active channel number */ - p_i2s[i2s_num]->hal_cfg.total_chan = p_i2s[i2s_num]->hal_cfg.total_chan < max_channel ? max_channel : - p_i2s[i2s_num]->hal_cfg.total_chan; - break; - default: - ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "wrong i2s channel format, going to uninstall i2s"); + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) { + mask = ((i2s_tdm_slot_config_t *)slot_cfg)->slot_mask;; } - - /* Calculate actived channel number in channel mask */ - p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); - -#else - /* Calculate actived channel number in channel mask */ - p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); - /* Total channel number is equal to the actived channel number in non-TDM mode */ - p_i2s[i2s_num]->hal_cfg.total_chan = 2; #endif - return ESP_OK; + return i2s_set_clk(i2s_num, rate, slot_cfg->data_bit_width, slot_cfg->slot_mode | (mask << 16)); } -/** - * @brief Initialize I2S DMA object - * - * @param i2s_num I2S device number - * @return - * - ESP_OK DMA object initialize success - * - ESP_ERR_NO_MEM No memory for DMA object - */ +#if SOC_I2S_SUPPORTS_PCM + +esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) +{ + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); + + i2s_stop(i2s_num); + I2S_ENTER_CRITICAL(i2s_num); + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_ll_tx_set_pcm_type(p_i2s[i2s_num]->hal.dev, pcm_cfg->pcm_type); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_ll_rx_set_pcm_type(p_i2s[i2s_num]->hal.dev, pcm_cfg->pcm_type); + } + I2S_EXIT_CRITICAL(i2s_num); + i2s_start(i2s_num); + return ESP_OK; +} +#endif + +#if SOC_I2S_SUPPORTS_PDM_RX +esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsample) +{ + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + i2s_stop(i2s_num); + i2s_pdm_rx_slot_config_t *slot_cfg = (i2s_pdm_rx_slot_config_t*)p_i2s[i2s_num]; + i2s_pdm_rx_clk_config_t *clk_cfg = (i2s_pdm_rx_clk_config_t*)p_i2s[i2s_num]; + clk_cfg->dn_sample_mode = downsample; + i2s_ll_rx_set_pdm_dsr(p_i2s[i2s_num]->hal.dev, downsample); + + // i2s will start in 'i2s_set_clk' + return i2s_set_clk(i2s_num, clk_cfg->sample_rate_hz, slot_cfg->data_bit_width, slot_cfg->slot_mode); +} +#endif + +#if SOC_I2S_SUPPORTS_PDM_TX +esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample_cfg_t *upsample_cfg) +{ + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) && (p_i2s[i2s_num]->dir & I2S_DIR_TX), + ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + i2s_stop(i2s_num); + + i2s_pdm_tx_clk_config_t *clk_cfg = (i2s_pdm_tx_clk_config_t *)p_i2s[i2s_num]->clk_cfg; + i2s_pdm_tx_slot_config_t *slot_cfg = (i2s_pdm_tx_slot_config_t *)p_i2s[i2s_num]->slot_cfg; + clk_cfg->up_sample_fp = upsample_cfg->fp; + clk_cfg->up_sample_fs = upsample_cfg->fs; + i2s_ll_tx_set_pdm_fpfs(p_i2s[i2s_num]->hal.dev, upsample_cfg->fp, upsample_cfg->fs); + // i2s will start in 'i2s_set_clk' + return i2s_set_clk(i2s_num, clk_cfg->sample_rate_hz, slot_cfg->data_bit_width, slot_cfg->slot_mode); +} +#endif + static esp_err_t i2s_dma_object_init(i2s_port_t i2s_num) { uint32_t buf_size = i2s_get_buf_size(i2s_num); + p_i2s[i2s_num]->last_buf_size = buf_size; /* Create DMA object */ - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->tx), TAG, "I2S TX DMA object create failed"); p_i2s[i2s_num]->tx->buf_size = buf_size; } - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->rx), TAG, "I2S RX DMA object create failed"); p_i2s[i2s_num]->rx->buf_size = buf_size; } return ESP_OK; } -/** - * @brief Install and start I2S driver. - * @note This function must be called before any I2S driver read/write operations. - * - * - * @param i2s_num I2S device number - * @param i2s_config I2S configurations - see i2s_config_t struct - * @param queue_size I2S event queue size/depth. - * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - * - ESP_ERR_INVALID_STATE Current I2S port is in use - */ +static void i2s_mode_identify(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + + p_i2s[i2s_num]->mode = I2S_COMM_MODE_STD; + + if (i2s_config->mode & I2S_MODE_MASTER) { + p_i2s[i2s_num]->role = I2S_ROLE_MASTER; + } else if (i2s_config->mode & I2S_MODE_SLAVE) { + p_i2s[i2s_num]->role = I2S_ROLE_SLAVE; + } + if (i2s_config->mode & I2S_MODE_TX) { + p_i2s[i2s_num]->dir |= I2S_DIR_TX; + } + if (i2s_config->mode & I2S_MODE_RX) { + p_i2s[i2s_num]->dir |= I2S_DIR_RX; + } +#if SOC_I2S_SUPPORTS_PDM + if (i2s_config->mode & I2S_MODE_PDM) { + p_i2s[i2s_num]->mode = I2S_COMM_MODE_PDM; + } +#endif // SOC_I2S_SUPPORTS_PDM + +#if SOC_I2S_SUPPORTS_TDM + if (i2s_config->channel_format == I2S_CHANNEL_FMT_MULTIPLE) { + p_i2s[i2s_num]->mode = I2S_COMM_MODE_TDM; + } +#endif // SOC_I2S_SUPPORTS_TDM + +#if SOC_I2S_SUPPORTS_ADC_DAC + if ((i2s_config->mode & I2S_MODE_DAC_BUILT_IN) || + (i2s_config->mode & I2S_MODE_ADC_BUILT_IN)) { + p_i2s[i2s_num]->mode = (i2s_comm_mode_t)I2S_COMM_MODE_ADC_DAC; + } +#endif // SOC_I2S_SUPPORTS_ADC_DAC +} + +static esp_err_t i2s_config_transfer(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + /* Convert legacy configuration into general part of slot and clock configuration */ + i2s_slot_config_t slot_cfg; + slot_cfg.mode = p_i2s[i2s_num]->mode; + slot_cfg.data_bit_width = i2s_config->bits_per_sample; + slot_cfg.slot_bit_width = (int)i2s_config->bits_per_chan < (int)i2s_config->bits_per_sample ? + i2s_config->bits_per_sample : i2s_config->bits_per_chan; + i2s_clk_config_t clk_cfg; + clk_cfg.sample_rate_hz = i2s_config->sample_rate; + clk_cfg.mclk_multiple = i2s_config->mclk_multiple == 0 ? I2S_MCLK_MULTIPLE_256 : i2s_config->mclk_multiple; + clk_cfg.clk_src = I2S_CLK_D2CLK; + p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; + p_i2s[i2s_num]->use_apll = false; + #if SOC_I2S_SUPPORTS_APLL + clk_cfg.clk_src = i2s_config->use_apll ? I2S_CLK_APLL : I2S_CLK_D2CLK; + p_i2s[i2s_num]->use_apll = i2s_config->use_apll; + #endif // SOC_I2S_SUPPORTS_APLL + + /* Convert legacy configuration into particular part of slot and clock configuration */ + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) { + /* Generate STD slot configuration */ + i2s_std_slot_config_t *std_slot = (i2s_std_slot_config_t *)calloc(1, sizeof(i2s_std_slot_config_t)); + ESP_RETURN_ON_FALSE(std_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct"); + memcpy(std_slot, &slot_cfg, sizeof(i2s_slot_config_t)); + std_slot->slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? + I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO; + std_slot->ws_width = i2s_config->bits_per_sample; + std_slot->ws_pol = false; + if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) { + std_slot->bit_shift = true; + } + if (i2s_config->communication_format & I2S_COMM_FORMAT_STAND_PCM_SHORT) { + std_slot->bit_shift = true; + std_slot->ws_width = 1; + std_slot->ws_pol = true; + } + #if SOC_I2S_HW_VERSION_1 + std_slot->msb_right = false; + #elif SOC_I2S_HW_VERSION_2 + std_slot->left_align = i2s_config->left_align; + std_slot->big_endian = i2s_config->big_edin; + std_slot->bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect + #endif // SOC_I2S_HW_VERSION_1 + p_i2s[i2s_num]->slot_cfg = std_slot; + + /* Generate STD clock configuration */ + i2s_std_clk_config_t *std_clk = (i2s_std_clk_config_t *)calloc(1, sizeof(i2s_std_clk_config_t)); + ESP_RETURN_ON_FALSE(std_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct"); + memcpy(std_clk, &clk_cfg, sizeof(i2s_clk_config_t)); + p_i2s[i2s_num]->clk_cfg = std_clk; + p_i2s[i2s_num]->active_slot = (int)std_slot->slot_mode; + p_i2s[i2s_num]->total_slot = 2; + goto finish; + } +#if SOC_I2S_SUPPORTS_PDM_TX + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) { + /* Generate PDM TX slot configuration */ + i2s_pdm_tx_slot_config_t *pdm_tx_slot = (i2s_pdm_tx_slot_config_t *)calloc(1, sizeof(i2s_pdm_tx_slot_config_t)); + ESP_RETURN_ON_FALSE(pdm_tx_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct"); + memcpy(pdm_tx_slot, &slot_cfg, sizeof(i2s_slot_config_t)); + pdm_tx_slot->slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? + I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO; + pdm_tx_slot->sd_prescale = 0; + pdm_tx_slot->sd_scale = I2S_PDM_SIG_SCALING_MUL_1; + pdm_tx_slot->hp_scale = I2S_PDM_SIG_SCALING_MUL_1; + pdm_tx_slot->lp_scale = I2S_PDM_SIG_SCALING_MUL_1; + pdm_tx_slot->sinc_scale = I2S_PDM_SIG_SCALING_MUL_1; + #if SOC_I2S_HW_VERSION_2 + pdm_tx_slot->sd_en = true; + pdm_tx_slot->hp_en = true; + pdm_tx_slot->hp_cut_off_freq_hz = 49; + pdm_tx_slot->sd_dither = 0; + pdm_tx_slot->sd_dither2 = 0; + #endif // SOC_I2S_HW_VERSION_2 + p_i2s[i2s_num]->slot_cfg = pdm_tx_slot; + + /* Generate PDM TX clock configuration */ + i2s_pdm_tx_clk_config_t *pdm_tx_clk = (i2s_pdm_tx_clk_config_t *)calloc(1, sizeof(i2s_pdm_tx_clk_config_t)); + ESP_RETURN_ON_FALSE(pdm_tx_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct"); + memcpy(pdm_tx_clk, &clk_cfg, sizeof(i2s_clk_config_t)); + pdm_tx_clk->up_sample_fp = 960; + pdm_tx_clk->up_sample_fs = i2s_config->sample_rate / 100; + p_i2s[i2s_num]->clk_cfg = pdm_tx_clk; + p_i2s[i2s_num]->active_slot = (int)pdm_tx_slot->slot_mode; + p_i2s[i2s_num]->total_slot = 2; + goto finish; + } +#endif // SOC_I2S_SUPPORTS_PDM_TX + +#if SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) { + /* Generate PDM RX slot configuration */ + i2s_pdm_rx_slot_config_t *pdm_rx_slot = (i2s_pdm_rx_slot_config_t *)calloc(1, sizeof(i2s_pdm_rx_slot_config_t)); + ESP_RETURN_ON_FALSE(pdm_rx_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct"); + memcpy(pdm_rx_slot, &slot_cfg, sizeof(i2s_slot_config_t)); + pdm_rx_slot->slot_mode = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? + I2S_SLOT_MODE_STEREO : I2S_SLOT_MODE_MONO; + p_i2s[i2s_num]->slot_cfg = pdm_rx_slot; + + + /* Generate PDM RX clock configuration */ + i2s_pdm_rx_clk_config_t *pdm_rx_clk = (i2s_pdm_rx_clk_config_t *)calloc(1, sizeof(i2s_pdm_rx_clk_config_t)); + ESP_RETURN_ON_FALSE(pdm_rx_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct"); + memcpy(pdm_rx_clk, &clk_cfg, sizeof(i2s_clk_config_t)); + pdm_rx_clk->dn_sample_mode = I2S_PDM_DSR_8S; + p_i2s[i2s_num]->clk_cfg = pdm_rx_clk; + p_i2s[i2s_num]->active_slot = (int)pdm_rx_slot->slot_mode; + p_i2s[i2s_num]->total_slot = 2; + goto finish; + } +#endif // SOC_I2S_SUPPOTYS_PDM_RX + +#if SOC_I2S_SUPPORTS_TDM + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) { + /* Generate TDM slot configuration */ + i2s_tdm_slot_config_t *tdm_slot = (i2s_tdm_slot_config_t *)calloc(1, sizeof(i2s_tdm_slot_config_t)); + ESP_RETURN_ON_FALSE(tdm_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct"); + memcpy(tdm_slot, &slot_cfg, sizeof(i2s_slot_config_t)); + tdm_slot->slot_mask = i2s_config->chan_mask >> 16; + uint32_t mx_slot = i2s_get_max_channel_num(tdm_slot->slot_mask); + tdm_slot->total_slot = mx_slot < i2s_config->total_chan ? mx_slot : i2s_config->total_chan; + tdm_slot->ws_width = I2S_TDM_AUTO_WS_WIDTH; + tdm_slot->slot_mode = I2S_SLOT_MODE_STEREO; + tdm_slot->ws_pol = false; + if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_I2S) { + tdm_slot->bit_shift = true; + } + else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_SHORT) { + tdm_slot->bit_shift = true; + tdm_slot->ws_width = 1; + tdm_slot->ws_pol = true; + } + else if (i2s_config->communication_format == I2S_COMM_FORMAT_STAND_PCM_LONG) { + tdm_slot->bit_shift = true; + tdm_slot->ws_width = tdm_slot->slot_bit_width; + tdm_slot->ws_pol = true; + } + tdm_slot->left_align = i2s_config->left_align; + tdm_slot->big_endian = i2s_config->big_edin; + tdm_slot->bit_order_lsb = i2s_config->bit_order_msb; // The old name is incorrect + tdm_slot->skip_mask = i2s_config->skip_msk; + p_i2s[i2s_num]->slot_cfg = tdm_slot; + + /* Generate TDM clock configuration */ + i2s_tdm_clk_config_t *tdm_clk = (i2s_tdm_clk_config_t *)calloc(1, sizeof(i2s_tdm_clk_config_t)); + ESP_RETURN_ON_FALSE(tdm_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct"); + memcpy(tdm_clk, &clk_cfg, sizeof(i2s_clk_config_t)); + p_i2s[i2s_num]->clk_cfg = tdm_clk; + p_i2s[i2s_num]->active_slot = i2s_get_active_channel_num(tdm_slot->slot_mode); + p_i2s[i2s_num]->total_slot = tdm_slot->total_slot; + goto finish; + } +#endif // SOC_I2S_SUPPORTS_TDM + +#if SOC_I2S_SUPPORTS_ADC_DAC + if ((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) { + i2s_slot_config_t *adc_dac_slot = (i2s_slot_config_t *)calloc(1, sizeof(i2s_slot_config_t)); + ESP_RETURN_ON_FALSE(adc_dac_slot, ESP_ERR_NO_MEM, TAG, "no memory for slot configuration struct"); + memcpy(adc_dac_slot, &slot_cfg, sizeof(i2s_slot_config_t)); + p_i2s[i2s_num]->slot_cfg = adc_dac_slot; + + i2s_clk_config_t *adc_dac_clk = (i2s_clk_config_t *)calloc(1, sizeof(i2s_clk_config_t)); + ESP_RETURN_ON_FALSE(adc_dac_clk, ESP_ERR_NO_MEM, TAG, "no memory for clock configuration struct"); + memcpy(adc_dac_clk, &clk_cfg, sizeof(i2s_clk_config_t)); + p_i2s[i2s_num]->clk_cfg = adc_dac_clk; + p_i2s[i2s_num]->active_slot = (p_i2s[i2s_num]->dir & I2S_DIR_TX) ? 2 : 1; + p_i2s[i2s_num]->total_slot = 2; + } +#endif // SOC_I2S_SUPPORTS_ADC_DAC + +finish: + return ESP_OK; +} + +static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag) +{ + /* Create power management lock */ +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX; +#if SOC_I2S_SUPPORTS_APLL + if (p_i2s[i2s_num]->use_apll) { + pm_lock = ESP_PM_NO_LIGHT_SLEEP; + } +#endif // SOC_I2S_SUPPORTS_APLL + ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock), TAG, "I2S pm lock error"); +#endif //CONFIG_PM_ENABLE + +#if SOC_I2S_SUPPORTS_APLL + if (p_i2s[i2s_num]->use_apll) { + periph_rtc_apll_acquire(); + } +#endif + + /* Enable communicaiton mode */ + if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_STD) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_std_enable_tx_channel(&(p_i2s[i2s_num]->hal)); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_std_enable_rx_channel(&(p_i2s[i2s_num]->hal)); + } + } +#if SOC_I2S_SUPPORTS_PDM + else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_PDM) { + #if SOC_I2S_SUPPORTS_PDM_TX + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_pdm_enable_tx_channel(&(p_i2s[i2s_num]->hal)); + } + #endif + #if SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_pdm_enable_rx_channel(&(p_i2s[i2s_num]->hal)); + } + #endif + } +#endif +#if SOC_I2S_SUPPORTS_TDM + else if (p_i2s[i2s_num]->mode == I2S_COMM_MODE_TDM) { + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_hal_tdm_enable_tx_channel(&(p_i2s[i2s_num]->hal)); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_hal_tdm_enable_rx_channel(&(p_i2s[i2s_num]->hal)); + } + } +#endif +#if SOC_I2S_SUPPORTS_ADC_DAC + if ((int)p_i2s[i2s_num]->mode == I2S_COMM_MODE_ADC_DAC) { + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + adc_power_acquire(); + adc_set_i2s_data_source(ADC_I2S_DATA_SRC_ADC); + i2s_ll_enable_builtin_adc(p_i2s[i2s_num]->hal.dev, true); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_ll_enable_builtin_dac(p_i2s[i2s_num]->hal.dev, true); + } + } else { + adc_set_i2s_data_source(ADC_I2S_DATA_SRC_IO_SIG); + i2s_ll_enable_builtin_adc(p_i2s[i2s_num]->hal.dev, false); + i2s_ll_enable_builtin_dac(p_i2s[i2s_num]->hal.dev, false); + } +#endif + + i2s_set_slot_legacy(i2s_num); + i2s_set_clock_legacy(i2s_num); + ESP_RETURN_ON_ERROR(i2s_dma_intr_init(i2s_num, intr_alloc_flag), TAG, "I2S interrupt initailze failed"); + ESP_RETURN_ON_ERROR(i2s_dma_object_init(i2s_num), TAG, "I2S dma object create failed"); + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + ESP_RETURN_ON_ERROR(i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->tx), TAG, "Allocate I2S dma tx buffer failed"); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + ESP_RETURN_ON_ERROR(i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->rx), TAG, "Allocate I2S dma rx buffer failed"); + } + + /* Initialize I2S DMA object */ +#if SOC_I2S_HW_VERSION_2 + /* Enable tx/rx submodule clock */ + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + i2s_ll_tx_enable_clock(p_i2s[i2s_num]->hal.dev); + } + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + i2s_ll_rx_enable_clock(p_i2s[i2s_num]->hal.dev); + } +#endif + + return ESP_OK; +} + +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) +{ + ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num); + i2s_obj_t *obj = p_i2s[i2s_num]; + i2s_stop(i2s_num); + +#if SOC_I2S_SUPPORTS_ADC_DAC + if ((int)(obj->mode) == I2S_COMM_MODE_ADC_DAC) { + if (obj->dir & I2S_DIR_TX) { + // Deinit DAC + i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); + } + if (obj->dir & I2S_DIR_RX) { + // Deinit ADC + adc_set_i2s_data_source(ADC_I2S_DATA_SRC_IO_SIG); + adc_power_release(); + } + } +#endif +#if SOC_GDMA_SUPPORTED + if (obj->tx_dma_chan) { + gdma_disconnect(obj->tx_dma_chan); + gdma_del_channel(obj->tx_dma_chan); + } + if (obj->rx_dma_chan) { + gdma_disconnect(obj->rx_dma_chan); + gdma_del_channel(obj->rx_dma_chan); + } +#else + if (obj->i2s_isr_handle) { + esp_intr_free(obj->i2s_isr_handle); + } +#endif + /* Destroy dma object if exist */ + i2s_destroy_dma_object(i2s_num, &obj->tx); + i2s_destroy_dma_object(i2s_num, &obj->rx); + + if (obj->i2s_queue) { + vQueueDelete(obj->i2s_queue); + obj->i2s_queue = NULL; + } + +#if SOC_I2S_SUPPORTS_APLL + if (obj->use_apll) { + // switch back to PLL clock source + if (obj->dir & I2S_DIR_TX) { + i2s_ll_tx_clk_set_src(obj->hal.dev, I2S_CLK_D2CLK); + } + if (obj->dir & I2S_DIR_RX) { + i2s_ll_rx_clk_set_src(obj->hal.dev, I2S_CLK_D2CLK); + } + periph_rtc_apll_release(); + } +#endif + +#ifdef CONFIG_PM_ENABLE + if (obj->pm_lock) { + esp_pm_lock_delete(obj->pm_lock); + obj->pm_lock = NULL; + } +#endif +#if SOC_I2S_HW_VERSION_2 + if (obj->dir & I2S_DIR_TX) { + i2s_ll_tx_disable_clock(obj->hal.dev); + } + if (obj->dir & I2S_DIR_RX) { + i2s_ll_rx_disable_clock(obj->hal.dev); + } +#endif + /* Disable module clock */ + i2s_priv_deregister_object(i2s_num); + if (obj->clk_cfg) { + free(obj->clk_cfg); + obj->clk_cfg = NULL; + } + if (obj->slot_cfg) { + free(obj->slot_cfg); + obj->slot_cfg = NULL; + } + free(obj); + p_i2s[i2s_num] = NULL; + return ESP_OK; +} + + esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue) { esp_err_t ret = ESP_OK; /* Step 1: Check the validity of input parameters */ - /* Check the validity of i2s device number */ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL"); - /* Check the size of DMA buffer */ - ESP_RETURN_ON_FALSE((i2s_config->dma_desc_num >= 2 && i2s_config->dma_desc_num <= 128), ESP_ERR_INVALID_ARG, TAG, "I2S buffer count less than 128 and more than 2"); - ESP_RETURN_ON_FALSE((i2s_config->dma_frame_num >= 8 && i2s_config->dma_frame_num <= 1024), ESP_ERR_INVALID_ARG, TAG, "I2S buffer length at most 1024 and more than 8"); + ESP_RETURN_ON_ERROR(i2s_check_cfg_validity(i2s_num, i2s_config), TAG, "I2S configuration is invalid"); /* Step 2: Allocate driver object and register to platform */ - i2s_obj_t *pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); - ESP_RETURN_ON_FALSE(pre_alloc_i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver"); - ret = i2s_priv_register_object(pre_alloc_i2s_obj, i2s_num); - if (ret != ESP_OK) { - free(pre_alloc_i2s_obj); + i2s_obj_t *i2s_obj = calloc(1, sizeof(i2s_obj_t)); + ESP_RETURN_ON_FALSE(i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver"); + if (i2s_priv_register_object(i2s_obj, i2s_num) != ESP_OK) { + free(i2s_obj); ESP_LOGE(TAG, "register I2S object to platform failed"); return ESP_ERR_INVALID_STATE; } + i2s_hal_init(&i2s_obj->hal, i2s_num); - /* Step 3: Initialize I2S object, assign configarations */ - ESP_GOTO_ON_ERROR(i2s_driver_init(i2s_num, i2s_config), err, TAG, "I2S init failed"); - /* Check the validity of I2S configuration */ - ESP_GOTO_ON_ERROR(i2s_check_cfg_validity(i2s_num, &(pre_alloc_i2s_obj->hal_cfg)), err, TAG, "I2S configuration is invalid"); + /* Step 3: Store and assign configarations */ + i2s_mode_identify(i2s_num, i2s_config); + ESP_GOTO_ON_ERROR(i2s_config_transfer(i2s_num, i2s_config), err, TAG, "I2S install failed"); + i2s_obj->dma_desc_num = i2s_config->dma_desc_num; + i2s_obj->dma_frame_num = i2s_config->dma_frame_num; + i2s_obj->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - /* Get device instance */ - i2s_hal_init(&(pre_alloc_i2s_obj->hal), i2s_num); + /* Step 4: Apply configurations and init hardware */ + ESP_GOTO_ON_ERROR(i2s_init_legacy(i2s_num, i2s_config->intr_alloc_flags), err, TAG, "I2S init failed"); -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX; -#if SOC_I2S_SUPPORTS_APLL - if (i2s_config->use_apll) { - pm_lock = ESP_PM_NO_LIGHT_SLEEP; - } -#endif // SOC_I2S_SUPPORTS_APLL - ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock), err, TAG, "I2S pm lock error"); -#endif //CONFIG_PM_ENABLE - - /* Step 4: Initialize I2S DMA interrupt and DMA object */ - ESP_GOTO_ON_ERROR(i2s_dma_intr_init(i2s_num, i2s_config->intr_alloc_flags), err, TAG, "I2S interrupt initailze failed"); - /* Initialize I2S DMA object */ - ESP_GOTO_ON_ERROR(i2s_dma_object_init(i2s_num), err, TAG, "I2S dma object create failed"); - -#if SOC_I2S_SUPPORTS_ADC - /* If using built-in ADC, we need to enable ADC power manerge*/ - if (pre_alloc_i2s_obj->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { - adc_power_acquire(); - } -#endif - /* Enable module clock */ - i2s_hal_enable_module_clock(&p_i2s[i2s_num]->hal); -#if SOC_I2S_SUPPORTS_TDM - /* Enable tx/rx submodule clock */ - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_tx_enable_clock(p_i2s[i2s_num]->hal.dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_rx_enable_clock(p_i2s[i2s_num]->hal.dev); - } -#endif - - /* Step 5: Initialize I2S configuration and set the configurations to register */ - i2s_hal_config_param(&(pre_alloc_i2s_obj->hal), &pre_alloc_i2s_obj->hal_cfg); - - /* Step 6: Initialise i2s event queue if user needs */ + /* Step 5: Initialise i2s event queue if user needs */ if (i2s_queue) { - pre_alloc_i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - ESP_GOTO_ON_FALSE(pre_alloc_i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed"); - *((QueueHandle_t *) i2s_queue) = pre_alloc_i2s_obj->i2s_queue; - ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(pre_alloc_i2s_obj->i2s_queue)); + i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + ESP_GOTO_ON_FALSE(i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed"); + *((QueueHandle_t *) i2s_queue) = i2s_obj->i2s_queue; + ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(i2s_obj->i2s_queue)); } else { - pre_alloc_i2s_obj->i2s_queue = NULL; + i2s_obj->i2s_queue = NULL; } -#if SOC_I2S_SUPPORTS_APLL - /* Power up APLL clock */ - if (i2s_config->use_apll) { - periph_rtc_apll_acquire(); - } -#endif - - /* Step 7: Set I2S clocks and start. No need to give parameters since configurations has been set in 'i2s_driver_init' */ - ESP_GOTO_ON_ERROR(i2s_set_clk(i2s_num, 0, 0, 0), err, TAG, "I2S set clock failed"); + /* Step 6: Start I2S for backward compatibility */ + ESP_GOTO_ON_ERROR(i2s_start(i2s_num), err, TAG, "I2S start failed"); return ESP_OK; @@ -1930,100 +1610,15 @@ err: return ret; } -/** - * @brief Uninstall I2S driver. - * - * @param i2s_num I2S device number - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ -esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) -{ - ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num); - i2s_obj_t *obj = p_i2s[i2s_num]; - i2s_stop(i2s_num); -#if SOC_I2S_SUPPORTS_DAC - i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); -#endif -#if SOC_GDMA_SUPPORTED - if (p_i2s[i2s_num]->tx_dma_chan) { - gdma_disconnect(p_i2s[i2s_num]->tx_dma_chan); - gdma_del_channel(p_i2s[i2s_num]->tx_dma_chan); - } - if (p_i2s[i2s_num]->rx_dma_chan) { - gdma_disconnect(p_i2s[i2s_num]->rx_dma_chan); - gdma_del_channel(p_i2s[i2s_num]->rx_dma_chan); - } -#else - if (p_i2s[i2s_num]->i2s_isr_handle) { - esp_intr_free(p_i2s[i2s_num]->i2s_isr_handle); - } -#endif - /* Destroy dma object if exist */ - i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->tx); - i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->rx); - - if (p_i2s[i2s_num]->i2s_queue) { - vQueueDelete(p_i2s[i2s_num]->i2s_queue); - p_i2s[i2s_num]->i2s_queue = NULL; - } - -#if SOC_I2S_SUPPORTS_APLL - if (p_i2s[i2s_num]->use_apll) { - // switch back to PLL clock source - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); - periph_rtc_apll_release(); - } -#endif - -#ifdef CONFIG_PM_ENABLE - if (p_i2s[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); - p_i2s[i2s_num]->pm_lock = NULL; - } -#endif -#if SOC_I2S_SUPPORTS_TDM - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { - i2s_ll_tx_disable_clock(p_i2s[i2s_num]->hal.dev); - } - if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { - i2s_ll_rx_disable_clock(p_i2s[i2s_num]->hal.dev); - } -#endif - /* Disable module clock */ - i2s_hal_disable_module_clock(&p_i2s[i2s_num]->hal); - i2s_priv_deregister_object(i2s_num); - free(obj); - return ESP_OK; -} - -/** - * @brief Write data to I2S DMA transmit buffer. - * @note Many ticks pass without space becoming available in the DMA - * transmit buffer, then the function will return (note that if the - * data is written to the DMA buffer in pieces, the overall operation - * may still take longer than this timeout.) Pass portMAX_DELAY for no - * timeout. - * - * @param i2s_num I2S device number - * @param src Source address to write from - * @param size Size of data in bytes - * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. - * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait) { - char *data_ptr, *src_byte; + char *data_ptr; + char *src_byte; size_t bytes_can_write; *bytes_written = 0; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled"); - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (TickType_t)portMAX_DELAY); + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); #endif @@ -2055,31 +1650,14 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by return ESP_OK; } -/** - * @brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. - * @note Many ticks pass without space becoming available in the DMA - * transmit buffer, then the function will return (note that if the - * data is written to the DMA buffer in pieces, the overall operation - * may still take longer than this timeout.) Pass portMAX_DELAY for no - * timeout. - * Format of the data in source buffer is determined by the I2S configuration (see i2s_config_t). - * - * @param i2s_num I2S device number - * @param src Source address to write from - * @param size Size of data in bytes - * @param src_bits Source audio bit - * @param aim_bits Bit wanted, no more than 32, and must be greater than src_bits - * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. - * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait) { char *data_ptr; - int bytes_can_write, tail; - int src_bytes, aim_bytes, zero_bytes; + int bytes_can_write; + int tail; + int src_bytes; + int aim_bytes; + int zero_bytes; *bytes_written = 0; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((size > 0), ESP_ERR_INVALID_ARG, TAG, "size must greater than zero"); @@ -2105,7 +1683,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz src_bytes = src_bits / 8; aim_bytes = aim_bits / 8; zero_bytes = aim_bytes - src_bytes; - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (TickType_t)portMAX_DELAY); + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, portMAX_DELAY); size = size * aim_bytes / src_bytes; ESP_LOGD(TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, size); while (size > 0) { @@ -2137,29 +1715,16 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz return ESP_OK; } -/** - * @brief Read data from I2S DMA receive buffer - * @note If the built-in ADC mode is enabled, we should call i2s_adc_enable and i2s_adc_disable around the whole reading process, - * to prevent the data getting corrupted. - * - * @param i2s_num I2S device number - * @param dest Destination address to read into - * @param size Size of data in bytes - * @param[out] bytes_read Number of bytes read, if timeout, bytes read will be less than the size passed in. - * @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many ticks pass without bytes becoming available in the DMA receive buffer, then the function will return (note that if data is read from the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout. - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait) { - char *data_ptr, *dest_byte; + char *data_ptr;; + char*dest_byte; int bytes_can_read; *bytes_read = 0; dest_byte = (char *)dest; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "RX mode is not enabled"); - xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (TickType_t)portMAX_DELAY); + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); #endif @@ -2189,6 +1754,135 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re return ESP_OK; } +/*------------------------------------------------------------- + I2S GPIO operation + -------------------------------------------------------------*/ +static void gpio_matrix_out_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) +{ + //if pin = -1, do not need to configure + if (gpio != -1) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + gpio_set_direction(gpio, GPIO_MODE_OUTPUT); + esp_rom_gpio_connect_out_signal(gpio, signal_idx, out_inv, oen_inv); + } +} + +static void gpio_matrix_in_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool inv) +{ + if (gpio != -1) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + /* Set direction, for some GPIOs, the input function are not enabled as default */ + gpio_set_direction(gpio, GPIO_MODE_INPUT); + esp_rom_gpio_connect_in_signal(gpio, signal_idx, inv); + } +} + +static esp_err_t i2s_check_set_mclk(i2s_port_t i2s_num, gpio_num_t gpio_num) +{ + if (gpio_num == -1) { + return ESP_OK; + } +#if CONFIG_IDF_TARGET_ESP32 + ESP_RETURN_ON_FALSE((gpio_num == GPIO_NUM_0 || gpio_num == GPIO_NUM_1 || gpio_num == GPIO_NUM_3), + ESP_ERR_INVALID_ARG, TAG, + "ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num); + bool is_i2s0 = i2s_num == I2S_NUM_0; + if (gpio_num == GPIO_NUM_0) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFFF0 : 0xFFFF); + } else if (gpio_num == GPIO_NUM_1) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xF0F0 : 0xF0FF); + } else { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2); + WRITE_PERI_REG(PIN_CTRL, is_i2s0 ? 0xFF00 : 0xFF0F); + } +#else + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid"); + gpio_matrix_out_check_and_set(gpio_num, i2s_periph_signal[i2s_num].mck_out_sig, 0, 0); +#endif + ESP_LOGI(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num); + return ESP_OK; +} + +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) +{ + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + uint32_t buf_cnt = p_i2s[i2s_num]->dma_desc_num; + + /* Clear I2S RX DMA buffer */ + if (p_i2s[i2s_num]->rx && p_i2s[i2s_num]->rx->buf != NULL && p_i2s[i2s_num]->rx->buf_size != 0) { + for (int i = 0; i < buf_cnt; i++) { + memset(p_i2s[i2s_num]->rx->buf[i], 0, p_i2s[i2s_num]->rx->buf_size); + } + } + + /* Clear I2S TX DMA buffer */ + if (p_i2s[i2s_num]->tx && p_i2s[i2s_num]->tx->buf != NULL && p_i2s[i2s_num]->tx->buf_size != 0) { + /* Finish to write all tx data */ + int bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; + if (bytes_left) { + size_t zero_bytes = 0; + size_t bytes_written; + i2s_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); + } + for (int i = 0; i < buf_cnt; i++) { + memset(p_i2s[i2s_num]->tx->buf[i], 0, p_i2s[i2s_num]->tx->buf_size); + } + } + return ESP_OK; +} + +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +{ + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + if (pin == NULL) { +#if SOC_I2S_SUPPORTS_DAC + return i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); +#else + return ESP_ERR_INVALID_ARG; +#endif + } + /* Check validity of selected pins */ + ESP_RETURN_ON_FALSE((pin->bck_io_num == -1 || GPIO_IS_VALID_GPIO(pin->bck_io_num)), + ESP_ERR_INVALID_ARG, TAG, "bck_io_num invalid"); + ESP_RETURN_ON_FALSE((pin->ws_io_num == -1 || GPIO_IS_VALID_GPIO(pin->ws_io_num)), + ESP_ERR_INVALID_ARG, TAG, "ws_io_num invalid"); + ESP_RETURN_ON_FALSE((pin->data_out_num == -1 || GPIO_IS_VALID_GPIO(pin->data_out_num)), + ESP_ERR_INVALID_ARG, TAG, "data_out_num invalid"); + ESP_RETURN_ON_FALSE((pin->data_in_num == -1 || GPIO_IS_VALID_GPIO(pin->data_in_num)), + ESP_ERR_INVALID_ARG, TAG, "data_in_num invalid"); + + if (p_i2s[i2s_num]->role == I2S_ROLE_SLAVE) { + /* For "tx + rx + slave" or "rx + slave" mode, we should select RX signal index for ws and bck */ + if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_rx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_rx_bck_sig, 0); + /* For "tx + slave" mode, we should select TX signal index for ws and bck */ + } else { + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_tx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_tx_bck_sig, 0); + } + } else { + /* mclk only available in master mode */ + ESP_RETURN_ON_ERROR(i2s_check_set_mclk(i2s_num, pin->mck_io_num), TAG, "mclk config failed"); + /* For "tx + rx + master" or "tx + master" mode, we should select TX signal index for ws and bck */ + if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_tx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_tx_bck_sig, 0, 0); + /* For "rx + master" mode, we should select RX signal index for ws and bck */ + } else { + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_rx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_rx_bck_sig, 0, 0); + } + } + + /* Set data input/ouput GPIO */ + gpio_matrix_out_check_and_set(pin->data_out_num, i2s_periph_signal[i2s_num].data_out_sig, 0, 0); + gpio_matrix_in_check_and_set(pin->data_in_num, i2s_periph_signal[i2s_num].data_in_sig, 0); + return ESP_OK; +} + esp_err_t i2s_priv_register_object(void *driver_obj, int port_id) { esp_err_t ret = ESP_ERR_NOT_FOUND; diff --git a/components/driver/include/esp_private/i2s_platform.h b/components/driver/include/esp_private/i2s_platform.h index ff1ebdf17a..6b5d294e76 100644 --- a/components/driver/include/esp_private/i2s_platform.h +++ b/components/driver/include/esp_private/i2s_platform.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c index e1f805500f..be23d46481 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c @@ -604,12 +604,7 @@ static esp_err_t i2s_lcd_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "unsupported clock source: %d", src); break; } - i2s_ll_mclk_div_t clk_cal_config = { - .mclk_div = LCD_PERIPH_CLOCK_PRE_SCALE, - .a = 1, - .b = 0, - }; - i2s_ll_tx_set_clk(bus->hal.dev, &clk_cal_config); + i2s_ll_set_raw_mclk_div(bus->hal.dev, LCD_PERIPH_CLOCK_PRE_SCALE, 1, 0); return ret; } diff --git a/components/hal/adc_hal.c b/components/hal/adc_hal.c index 409e3154c9..00a8612f12 100644 --- a/components/hal/adc_hal.c +++ b/components/hal/adc_hal.c @@ -256,12 +256,7 @@ static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t fre #else i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_D2CLK); /*!< Clock from PLL_D2_CLK(160M)*/ uint32_t bck = I2S_BASE_CLK / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_B_DEFAULT / ADC_LL_CLKM_DIV_A_DEFAULT) / 2 / freq; - i2s_ll_mclk_div_t clk = { - .mclk_div = ADC_LL_CLKM_DIV_NUM_DEFAULT, - .a = ADC_LL_CLKM_DIV_A_DEFAULT, - .b = ADC_LL_CLKM_DIV_B_DEFAULT, - }; - i2s_ll_rx_set_clk(hal->dev, &clk); + i2s_ll_set_raw_mclk_div(hal->dev, ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT); i2s_ll_rx_set_bck_div_num(hal->dev, bck); #endif } diff --git a/components/hal/esp32/include/hal/i2s_ll.h b/components/hal/esp32/include/hal/i2s_ll.h index 7b521e9398..0cca009f16 100644 --- a/components/hal/esp32/include/hal/i2s_ll.h +++ b/components/hal/esp32/include/hal/i2s_ll.h @@ -19,6 +19,7 @@ #include "soc/i2s_periph.h" #include "soc/i2s_struct.h" #include "hal/i2s_types.h" +#include "hal/i2s_types_priv.h" #ifdef __cplusplus extern "C" { @@ -34,14 +35,19 @@ extern "C" { #define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6) #define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) -#define I2S_LL_EVENT_TX_EOF (1 << 12) #define I2S_LL_BCK_MAX_PRESCALE (64) +#define I2S_LL_EVENT_RX_EOF BIT(9) +#define I2S_LL_EVENT_TX_EOF BIT(12) +#define I2S_LL_EVENT_RX_DSCR_ERR BIT(13) +#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14) +#define I2S_INTR_MAX (UINT32_MAX) + /* I2S clock configuration structure */ typedef struct { - uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) + uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; - uint16_t b; // The decimal part of module clock devider, the decimal is: b/a + uint16_t b; // The decimal part of module clock divider, the decimal is: b/a } i2s_ll_mclk_div_t; /** @@ -271,16 +277,68 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S TX clock devider + * @brief Configure I2S TX module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); - hw->clkm_conf.clkm_div_b = set->b; - hw->clkm_conf.clkm_div_a = set->a; + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = numerator; + hw->clkm_conf.clkm_div_a = denominator; +} + +/** + * @brief Configure I2S module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel + * mclk = sclk / (mclk_div + b/a) + * + * @param hw Peripheral I2S hardware instance address. + * @param mclk_div integer part of the division from sclk to mclk + * @param a Denominator of decimal part + * @param b Numerator of decimal part + */ +static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = b; + hw->clkm_conf.clkm_div_a = a; } /** @@ -295,16 +353,17 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S RX clock devider + * @brief Configure I2S RX module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); - hw->clkm_conf.clkm_div_b = set->b; - hw->clkm_conf.clkm_div_a = set->a; + i2s_ll_tx_set_mclk(hw, sclk, mclk, mclk_div); } /** @@ -331,7 +390,6 @@ static inline void i2s_ll_enable_intr(i2s_dev_t *hw, uint32_t mask, bool en) static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 1; - hw->int_ena.out_dscr_err = 1; } /** @@ -342,7 +400,6 @@ static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 0; - hw->int_ena.out_dscr_err = 0; } /** @@ -353,7 +410,6 @@ static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) { hw->int_ena.in_suc_eof = 1; - hw->int_ena.in_dscr_err = 1; } /** @@ -364,7 +420,6 @@ static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) static inline void i2s_ll_rx_disable_intr(i2s_dev_t *hw) { hw->int_ena.in_suc_eof = 0; - hw->int_ena.in_dscr_err = 0; } /** @@ -582,7 +637,7 @@ static inline void i2s_ll_tx_set_bits_mod(i2s_dev_t *hw, uint32_t val) */ static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2); hw->sample_rate_conf.tx_bits_mod = data_bit; } @@ -595,7 +650,7 @@ static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int */ static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2); hw->sample_rate_conf.rx_bits_mod = data_bit; } @@ -696,7 +751,7 @@ static inline void i2s_ll_tx_set_chan_mod(i2s_dev_t *hw, uint32_t val) static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.tx_bits_mod; - hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena; hw->conf_chan.tx_chan_mod = mono_ena; } @@ -709,7 +764,7 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.rx_bits_mod; - hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena; hw->conf_chan.rx_chan_mod = mono_ena; } @@ -749,28 +804,53 @@ static inline void i2s_ll_rx_get_pdm_dsr(i2s_dev_t *hw, i2s_pdm_dsr_t *dsr) *dsr = (i2s_pdm_dsr_t)hw->pdm_conf.rx_sinc_dsr_16_en; } +/** + * @brief Enable I2S TX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw) +{ + hw->conf2.val = 0; + hw->pdm_conf.tx_pdm_en = false; + hw->pdm_conf.pcm2pdm_conv_en = false; +} + +/** + * @brief Enable I2S RX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw) +{ + hw->conf2.val = 0; + hw->pdm_conf.rx_pdm_en = false; + hw->pdm_conf.pdm2pcm_conv_en = false; +} + /** * @brief Enable I2S TX PDM mode * * @param hw Peripheral I2S hardware instance address. * @param pdm_ena Set true to enable TX PDM mode */ -static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_ena) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw) { - hw->pdm_conf.tx_pdm_en = pdm_ena; - hw->pdm_conf.pcm2pdm_conv_en = pdm_ena; + hw->conf2.val = 0; + hw->pdm_conf.tx_pdm_en = true; + hw->pdm_conf.pcm2pdm_conv_en = true; } /** * @brief Enable I2S RX PDM mode * * @param hw Peripheral I2S hardware instance address. - * @param pdm_ena Set true to enable RX PDM mode */ -static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_ena) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw) { - hw->pdm_conf.rx_pdm_en = pdm_ena; - hw->pdm_conf.pdm2pcm_conv_en = pdm_ena; + hw->conf2.val = 0; + hw->pdm_conf.rx_pdm_en = true; + hw->pdm_conf.pdm2pcm_conv_en = true; } /** diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index f7cb496b4b..9eb3439813 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -33,9 +33,9 @@ extern "C" { /* I2S clock configuration structure */ typedef struct { - uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) + uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; - uint16_t b; // The decimal part of module clock devider, the decimal is: b/a + uint16_t b; // The decimal part of module clock divider, the decimal is: b/a } i2s_ll_mclk_div_t; /** @@ -103,7 +103,7 @@ static inline void i2s_ll_rx_disable_clock(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_tx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 0; } @@ -113,7 +113,7 @@ static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_rx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 1; } @@ -218,31 +218,65 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S TX clock devider + * @brief Configure I2S TX module clock divider * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; hw->tx_clkm_div_conf.tx_clkm_div_y = 0; hw->tx_clkm_div_conf.tx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); - hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator); + hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; } else { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; - hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); } /** @@ -257,31 +291,66 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S RX clock devider + * @brief Configure I2S RX module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; hw->rx_clkm_div_conf.rx_clkm_div_y = 0; hw->rx_clkm_div_conf.rx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); - hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator); + hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; } else { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; - hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); } /** @@ -503,17 +572,59 @@ static inline void i2s_ll_rx_set_ws_idle_pol(i2s_dev_t *hw, bool ws_pol_level) hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } +/** + * @brief Enable I2S TX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_tdm(i2s_dev_t *hw) +{ + hw->tx_conf.tx_pdm_en = false; + hw->tx_conf.tx_tdm_en = true; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = false; +} + +/** + * @brief Enable I2S RX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_tdm(i2s_dev_t *hw) +{ + hw->rx_conf.rx_pdm_en = false; + hw->rx_conf.rx_tdm_en = true; +} + +/** + * @brief Enable I2S TX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_tx_enable_tdm(hw); +} + +/** + * @brief Enable I2S RX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_rx_enable_tdm(hw); +} + /** * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw) { - hw->tx_conf.tx_pdm_en = pdm_enable; - hw->tx_conf.tx_tdm_en = !pdm_enable; - hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; + hw->tx_conf.tx_pdm_en = true; + hw->tx_conf.tx_tdm_en = false; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = true; } /** diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index 38a700c5f6..fb531cbb24 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,9 +34,9 @@ extern "C" { /* I2S clock configuration structure */ typedef struct { - uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) + uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; - uint16_t b; // The decimal part of module clock devider, the decimal is: b/a + uint16_t b; // The decimal part of module clock divider, the decimal is: b/a } i2s_ll_mclk_div_t; /** @@ -104,7 +104,7 @@ static inline void i2s_ll_rx_disable_clock(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_tx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 0; } @@ -114,7 +114,7 @@ static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_rx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 1; } @@ -219,31 +219,65 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S TX clock devider + * @brief Configure I2S TX module clock divider * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; hw->tx_clkm_div_conf.tx_clkm_div_y = 0; hw->tx_clkm_div_conf.tx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); - hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator); + hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; } else { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; - hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); } /** @@ -258,31 +292,66 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S RX clock devider + * @brief Configure I2S RX module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; hw->rx_clkm_div_conf.rx_clkm_div_y = 0; hw->rx_clkm_div_conf.rx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); - hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator); + hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; } else { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; - hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); } /** @@ -504,17 +573,59 @@ static inline void i2s_ll_rx_set_ws_idle_pol(i2s_dev_t *hw, bool ws_pol_level) hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } +/** + * @brief Enable I2S TX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_tdm(i2s_dev_t *hw) +{ + hw->tx_conf.tx_pdm_en = false; + hw->tx_conf.tx_tdm_en = true; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = false; +} + +/** + * @brief Enable I2S RX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_tdm(i2s_dev_t *hw) +{ + hw->rx_conf.rx_pdm_en = false; + hw->rx_conf.rx_tdm_en = true; +} + +/** + * @brief Enable I2S TX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_tx_enable_tdm(hw); +} + +/** + * @brief Enable I2S RX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_rx_enable_tdm(hw); +} + /** * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw) { - hw->tx_conf.tx_pdm_en = pdm_enable; - hw->tx_conf.tx_tdm_en = !pdm_enable; - hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; + hw->tx_conf.tx_pdm_en = true; + hw->tx_conf.tx_tdm_en = false; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = true; } /** diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 7f4a435b92..a9ffa641bc 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -29,19 +29,24 @@ extern "C" { #define I2S_LL_BASE_CLK (2 * APB_CLK_FREQ) +#define I2S_LL_BCK_MAX_PRESCALE (64) + #define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6) #define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1) +#define I2S_LL_EVENT_RX_EOF BIT(9) +#define I2S_LL_EVENT_TX_EOF BIT(12) +#define I2S_LL_EVENT_RX_DSCR_ERR BIT(13) +#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14) +#define I2S_INTR_MAX (UINT32_MAX) + /* I2S clock configuration structure */ typedef struct { - uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) + uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; - uint16_t b; // The decimal part of module clock devider, the decimal is: b/a + uint16_t b; // The decimal part of module clock divider, the decimal is: b/a } i2s_ll_mclk_div_t; -#define I2S_LL_EVENT_TX_EOF (1 << 12) -#define I2S_LL_BCK_MAX_PRESCALE (64) - /** * @brief Enable DMA descriptor owner check * @@ -267,16 +272,68 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S TX clock devider + * @brief Configure I2S TX module clock divider + * @note mclk on ESP32S2 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); - hw->clkm_conf.clkm_div_b = set->b; - hw->clkm_conf.clkm_div_a = set->a; + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = numerator; + hw->clkm_conf.clkm_div_a = denominator; +} + +/** + * @brief Configure I2S module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel + * mclk = sclk / (mclk_div + b/a) + * + * @param hw Peripheral I2S hardware instance address. + * @param mclk_div integer part of the division from sclk to mclk + * @param a Denominator of decimal part + * @param b Numerator of decimal part + */ +static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = b; + hw->clkm_conf.clkm_div_a = a; } /** @@ -291,16 +348,17 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S RX clock devider + * @brief Configure I2S RX module clock divider + * @note mclk on ESP32S2 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, set->mclk_div); - hw->clkm_conf.clkm_div_b = set->b; - hw->clkm_conf.clkm_div_a = set->a; + i2s_ll_tx_set_mclk(hw, sclk, mclk, mclk_div); } /** @@ -327,7 +385,6 @@ static inline void i2s_ll_enable_intr(i2s_dev_t *hw, uint32_t mask, bool en) static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 1; - hw->int_ena.out_dscr_err = 1; } /** @@ -338,7 +395,6 @@ static inline void i2s_ll_tx_enable_intr(i2s_dev_t *hw) static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) { hw->int_ena.out_eof = 0; - hw->int_ena.out_dscr_err = 0; } /** @@ -349,7 +405,6 @@ static inline void i2s_ll_tx_disable_intr(i2s_dev_t *hw) static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) { hw->int_ena.in_suc_eof = 1; - hw->int_ena.in_dscr_err = 1; } /** @@ -360,7 +415,6 @@ static inline void i2s_ll_rx_enable_intr(i2s_dev_t *hw) static inline void i2s_ll_rx_disable_intr(i2s_dev_t *hw) { hw->int_ena.in_suc_eof = 0; - hw->int_ena.in_dscr_err = 0; } /** @@ -419,15 +473,34 @@ static inline void i2s_ll_rx_reset_dma(i2s_dev_t *hw) hw->lc_conf.in_rst = 0; } +/** + * @brief Enable I2S TX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw) +{ + hw->conf2.val = 0; +} + +/** + * @brief Enable I2S RX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw) +{ + hw->conf2.val = 0; +} + /** * @brief Enable TX PDM mode. * @note ESP32-S2 doesn't support pdm * This function is used to be compatible with those support pdm * * @param hw Peripheral I2S hardware instance address (ignored) - * @param pdm_enable Set true to TX enable PDM mode (ignored) */ -static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw) { // Remain empty } @@ -438,9 +511,8 @@ static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) * This function is used to be compatible with those support pdm * * @param hw Peripheral I2S hardware instance address (ignored) - * @param pdm_enable Set true to RX enable PDM mode (ignored) */ -static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw) { // Remain empty } @@ -592,7 +664,7 @@ static inline void i2s_ll_rx_set_eof_num(i2s_dev_t *hw, uint32_t eof_num) */ static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->fifo_conf.tx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2); hw->sample_rate_conf.tx_bits_mod = data_bit; } @@ -605,7 +677,7 @@ static inline void i2s_ll_tx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int */ static inline void i2s_ll_rx_set_sample_bit(i2s_dev_t *hw, uint8_t chan_bit, int data_bit) { - hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_BITS_PER_SAMPLE_16BIT ? 0 : 2); + hw->fifo_conf.rx_fifo_mod = (chan_bit <= I2S_DATA_BIT_WIDTH_16BIT ? 0 : 2); hw->sample_rate_conf.rx_bits_mod = data_bit; } @@ -794,7 +866,7 @@ static inline void i2s_ll_tx_enable_dma_equal(i2s_dev_t *hw, bool en) static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.tx_bits_mod; - hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena; hw->conf.tx_dma_equal = mono_ena; hw->conf_chan.tx_chan_mod = mono_ena; } @@ -808,7 +880,7 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.rx_bits_mod; - hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; + hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_DATA_BIT_WIDTH_16BIT ? mono_ena : 2 + mono_ena; hw->conf.rx_dma_equal = mono_ena; hw->conf_chan.rx_chan_mod = mono_ena; } diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index af9e3443fd..8d6387e24e 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/components/hal/esp32s3/include/hal/i2s_ll.h @@ -34,9 +34,9 @@ extern "C" { /* I2S clock configuration structure */ typedef struct { - uint16_t mclk_div; // I2S module clock devider, Fmclk = Fsclk /(mclk_div+b/a) + uint16_t mclk_div; // I2S module clock divider, Fmclk = Fsclk /(mclk_div+b/a) uint16_t a; - uint16_t b; // The decimal part of module clock devider, the decimal is: b/a + uint16_t b; // The decimal part of module clock divider, the decimal is: b/a } i2s_ll_mclk_div_t; /** @@ -104,7 +104,7 @@ static inline void i2s_ll_rx_disable_clock(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_tx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 0; } @@ -114,7 +114,7 @@ static inline void i2s_ll_mclk_use_tx_clk(i2s_dev_t *hw) * * @param hw Peripheral I2S hardware instance address. */ -static inline void i2s_ll_mclk_use_rx_clk(i2s_dev_t *hw) +static inline void i2s_ll_mclk_bind_to_rx_clk(i2s_dev_t *hw) { hw->rx_clkm_conf.mclk_sel = 1; } @@ -221,31 +221,65 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S TX clock devider + * @brief Configure I2S TX module clock divider * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_tx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->tx_clkm_div_conf.tx_clkm_div_x = 0; hw->tx_clkm_div_conf.tx_clkm_div_y = 0; hw->tx_clkm_div_conf.tx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % (set->a - set->b); - hw->tx_clkm_div_conf.tx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator); + hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; } else { - hw->tx_clkm_div_conf.tx_clkm_div_x = set->a / set->b - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = set->a % set->b + 1; - hw->tx_clkm_div_conf.tx_clkm_div_z = set->b; + hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; + hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator + 1; + hw->tx_clkm_div_conf.tx_clkm_div_z = numerator; hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); } /** @@ -260,31 +294,66 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) } /** - * @brief Configure I2S RX clock devider + * @brief Configure I2S RX module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel * * @param hw Peripheral I2S hardware instance address. - * @param set Pointer to I2S clock devider configuration paramater + * @param sclk system clock, 0 means use apll + * @param mclk module clock + * @param mclk_div integer part of the division from sclk to mclk */ -static inline void i2s_ll_rx_set_clk(i2s_dev_t *hw, i2s_ll_mclk_div_t *set) +static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mclk, uint32_t mclk_div) { - if (set->a == 0 || set->b == 0) { + int ma = 0; + int mb = 0; + int denominator = 1; + int numerator = 0; + + uint32_t freq_diff = abs(sclk - mclk * mclk_div); + if (!freq_diff) { + goto finish; + } + float decimal = freq_diff / (float)mclk; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 + if (decimal > 125.0 / 126.0) { + mclk_div++; + goto finish; + } + uint32_t min = ~0; + for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { + int b = (int)(a * (freq_diff / (double)mclk) + 0.5); + ma = freq_diff * a; + mb = mclk * b; + if (ma == mb) { + denominator = a; + numerator = b; + goto finish; + } + if (abs((mb - ma)) < min) { + denominator = a; + numerator = b; + min = abs(mb - ma); + } + } +finish: + if (denominator == 0 || numerator == 0) { hw->rx_clkm_div_conf.rx_clkm_div_x = 0; hw->rx_clkm_div_conf.rx_clkm_div_y = 0; hw->rx_clkm_div_conf.rx_clkm_div_z = 0; } else { - if (set->b > set->a / 2) { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / (set->a - set->b) - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % (set->a - set->b); - hw->rx_clkm_div_conf.rx_clkm_div_z = set->a - set->b; + if (numerator > denominator / 2) { + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator); + hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; } else { - hw->rx_clkm_div_conf.rx_clkm_div_x = set->a / set->b - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = set->a % set->b + 1; - hw->rx_clkm_div_conf.rx_clkm_div_z = set->b; + hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; + hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator + 1; + hw->rx_clkm_div_conf.rx_clkm_div_z = numerator; hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; } } - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, set->mclk_div); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); } /** @@ -506,30 +575,72 @@ static inline void i2s_ll_rx_set_ws_idle_pol(i2s_dev_t *hw, bool ws_pol_level) hw->rx_conf.rx_ws_idle_pol = ws_pol_level; } +/** + * @brief Enable I2S TX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_tdm(i2s_dev_t *hw) +{ + hw->tx_conf.tx_pdm_en = false; + hw->tx_conf.tx_tdm_en = true; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = false; +} + +/** + * @brief Enable I2S RX TDM mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_tdm(i2s_dev_t *hw) +{ + hw->rx_conf.rx_pdm_en = false; + hw->rx_conf.rx_tdm_en = true; + hw->rx_conf.rx_pdm2pcm_en = false; +} + +/** + * @brief Enable I2S TX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_tx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_tx_enable_tdm(hw); +} + +/** + * @brief Enable I2S RX STD mode + * + * @param hw Peripheral I2S hardware instance address. + */ +static inline void i2s_ll_rx_enable_std(i2s_dev_t *hw) +{ + i2s_ll_rx_enable_tdm(hw); +} + /** * @brief Enable TX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param pdm_enable Set true to TX enable PDM mode */ -static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw) { - hw->tx_conf.tx_pdm_en = pdm_enable; - hw->tx_conf.tx_tdm_en = !pdm_enable; - hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pdm_enable; + hw->tx_conf.tx_pdm_en = true; + hw->tx_conf.tx_tdm_en = false; + hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = true; } /** * @brief Enable RX PDM mode. * * @param hw Peripheral I2S hardware instance address. - * @param pdm_enable Set true to RX enable PDM mode */ -static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable) +static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw) { - hw->rx_conf.rx_pdm_en = pdm_enable; - hw->rx_conf.rx_tdm_en = !pdm_enable; - hw->rx_conf.rx_pdm2pcm_en = pdm_enable; + hw->rx_conf.rx_pdm_en = true; + hw->rx_conf.rx_tdm_en = false; + hw->rx_conf.rx_pdm2pcm_en = true; } /** diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index 9df3c82816..532acca408 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,356 +8,282 @@ // The HAL layer for I2S (common part) #include "soc/soc.h" -#include "soc/soc_caps.h" #include "hal/i2s_hal.h" -/** - * @brief Calculate the closest sample rate clock configuration. - * clock relationship: - * Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) - * - * @param clk_cfg I2S clock configuration(input) - * @param cal Point to `i2s_ll_mclk_div_t` structure(output). - */ -static void i2s_hal_mclk_div_decimal_cal(i2s_hal_clock_cfg_t *clk_cfg, i2s_ll_mclk_div_t *cal) -{ - int ma = 0; - int mb = 0; - cal->mclk_div = clk_cfg->mclk_div; - cal->a = 1; - cal->b = 0; +#include "hal/i2s_std.h" +#include "hal/i2s_pdm.h" +#include "hal/i2s_tdm.h" - uint32_t freq_diff = abs((int)clk_cfg->sclk - (int)(clk_cfg->mclk * cal->mclk_div)); - if (!freq_diff) { - return; - } - float decimal = freq_diff / (float)clk_cfg->mclk; - // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0 - if (decimal > 125.0 / 126.0) { - cal->mclk_div++; - return; - } - uint32_t min = ~0; - for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) { - // Calculate the closest 'b' in this loop, no need to loop 'b' to seek the closest value - int b = (int)(a * (freq_diff / (double)clk_cfg->mclk) + 0.5); - ma = freq_diff * a; - mb = clk_cfg->mclk * b; - if (ma == mb) { - cal->a = a; - cal->b = b; - return; - } - if (abs((mb - ma)) < min) { - cal->a = a; - cal->b = b; - min = abs(mb - ma); - } - } -} +#if SOC_I2S_HW_VERSION_2 && SOC_I2S_SUPPORTS_PDM_TX +/* PDM tx high pass filter cut-off frequency and coeffecients list + * [0]: cut-off frequency; [1]: param0; [2]: param5 */ +static const float cut_off_coef[21][3] = { + {185, 0, 0}, {172, 0, 1}, {160, 1, 1}, + {150, 1, 2}, {137, 2, 2}, {126, 2, 3}, + {120, 0, 3}, {115, 3, 3}, {106, 1, 7}, + {104, 2, 4}, {92, 4, 4}, {91.5, 2, 7}, + {81, 4, 5}, {77.2, 3, 7}, {69, 5, 5}, + {63, 4, 7}, {58, 5, 6}, {49, 5, 7}, + {46, 6, 6}, {35.5, 6, 7}, {23.3, 7, 7}}; +#endif -void i2s_hal_set_clock_src(i2s_hal_context_t *hal, i2s_clock_src_t sel) -{ - i2s_ll_tx_clk_set_src(hal->dev, sel); - i2s_ll_rx_clk_set_src(hal->dev, sel); -} - -void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg) -{ - i2s_ll_mclk_div_t mclk_set; - i2s_hal_mclk_div_decimal_cal(clk_cfg, &mclk_set); - i2s_ll_tx_set_clk(hal->dev, &mclk_set); - i2s_ll_tx_set_bck_div_num(hal->dev, clk_cfg->bclk_div); -} - -void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg) -{ - i2s_ll_mclk_div_t mclk_set; - i2s_hal_mclk_div_decimal_cal(clk_cfg, &mclk_set); - i2s_ll_rx_set_clk(hal->dev, &mclk_set); - i2s_ll_rx_set_bck_div_num(hal->dev, clk_cfg->bclk_div); -} - -void i2s_hal_enable_master_fd_mode(i2s_hal_context_t *hal) -{ - i2s_ll_tx_set_slave_mod(hal->dev, false); //TX master - i2s_ll_rx_set_slave_mod(hal->dev, true); //RX Slave -} - -void i2s_hal_enable_slave_fd_mode(i2s_hal_context_t *hal) -{ - i2s_ll_tx_set_slave_mod(hal->dev, true); //TX Slave - i2s_ll_rx_set_slave_mod(hal->dev, true); //RX Slave -} - -void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num) +void i2s_hal_init(i2s_hal_context_t *hal, int port_id) { /* Get hardware instance */ - hal->dev = I2S_LL_GET_HW(i2s_num); + hal->dev = I2S_LL_GET_HW(port_id); } -#if SOC_I2S_SUPPORTS_PDM_TX -void i2s_hal_tx_set_pdm_mode_default(i2s_hal_context_t *hal, uint32_t sample_rate) +void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src) { - /* enable pdm tx mode */ - i2s_ll_tx_enable_pdm(hal->dev, true); -#if SOC_I2S_SUPPORTS_TDM +#if SOC_I2S_HW_VERSION_2 i2s_ll_tx_enable_clock(hal->dev); - i2s_ll_tx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default - i2s_ll_mclk_use_tx_clk(hal->dev); - /* Still need to enable the first 2 TDM channel mask to get the correct number of frame */ - i2s_ll_tx_set_active_chan_mask(hal->dev, I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1); -#else - i2s_ll_tx_force_enable_fifo_mod(hal->dev, true); + i2s_ll_mclk_bind_to_tx_clk(hal->dev); #endif - /* set pdm tx default presacle */ - i2s_ll_tx_set_pdm_prescale(hal->dev, 0); - /* set pdm tx default sacle of high pass filter */ - i2s_ll_tx_set_pdm_hp_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); - /* set pdm tx default sacle of low pass filter */ - i2s_ll_tx_set_pdm_lp_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); - /* set pdm tx default sacle of sinc filter */ - i2s_ll_tx_set_pdm_sinc_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); - /* set pdm tx default sacle of sigma-delta filter */ - i2s_ll_tx_set_pdm_sd_scale(hal->dev, I2S_PDM_SIG_SCALING_MUL_1); - /* set pdm tx sample rate */ - i2s_ll_tx_set_pdm_fpfs(hal->dev, 960, sample_rate / 100); - -#if SOC_I2S_SUPPORTS_PDM_CODEC - /* enable pdm high pass filter */ - i2s_ll_tx_enable_pdm_hp_filter(hal->dev, true); - /* set pdm tx high pass filter parameters */ - i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, 6); - i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, 7); - /* enable pdm sigma-delta codec */ - i2s_ll_tx_enable_pdm_sd_codec(hal->dev, true); - /* set pdm tx sigma-delta codec dither */ - i2s_ll_tx_set_pdm_sd_dither(hal->dev, 0); - i2s_ll_tx_set_pdm_sd_dither2(hal->dev, 0); - -#endif // SOC_I2S_SUPPORTS_PDM_CODEC + i2s_ll_tx_clk_set_src(hal->dev, clk_src); + i2s_ll_tx_set_mclk(hal->dev, clk_info->sclk, clk_info->mclk, clk_info->mclk_div); + i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div); } -#endif // SOC_I2S_SUPPORTS_PDM_TX + +void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src) +{ +#if SOC_I2S_HW_VERSION_2 + i2s_ll_rx_enable_clock(hal->dev); + i2s_ll_mclk_bind_to_rx_clk(hal->dev); +#endif + i2s_ll_rx_clk_set_src(hal->dev, clk_src); + i2s_ll_rx_set_mclk(hal->dev, clk_info->sclk, clk_info->mclk, clk_info->mclk_div); + i2s_ll_rx_set_bck_div_num(hal->dev, clk_info->bclk_div); +} + +/*------------------------------------------------------------------------- + | STD Specific Slot Configurations | + -------------------------------------------------------------------------*/ +void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) +{ + i2s_std_slot_config_t *slot_cfg = (i2s_std_slot_config_t*)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + i2s_ll_tx_reset(hal->dev); + i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave + i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); + i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->bit_shift); + i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->ws_width); +#if SOC_I2S_HW_VERSION_1 + i2s_ll_tx_enable_msb_right(hal->dev, slot_cfg->msb_right); + i2s_ll_tx_enable_right_first(hal->dev, slot_cfg->ws_pol); + /* Should always enable fifo */ + i2s_ll_tx_force_enable_fifo_mod(hal->dev, true); +#elif SOC_I2S_HW_VERSION_2 + /* There are always 2 slots in std mode */ + i2s_ll_tx_set_chan_num(hal->dev, 2); + /* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot + * Otherwise always enable the first two slots */ + i2s_ll_tx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? 0x01 : 0x03); + i2s_ll_tx_set_half_sample_bit(hal->dev, slot_bit_width); + i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol); + i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb); + i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->left_align); + i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->big_endian); +#endif +} + +void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) +{ + i2s_std_slot_config_t *slot_cfg = (i2s_std_slot_config_t*)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + i2s_ll_rx_reset(hal->dev); + i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave + i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); + i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->bit_shift); + i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->ws_width); +#if SOC_I2S_HW_VERSION_1 + i2s_ll_rx_enable_msb_right(hal->dev, slot_cfg->msb_right); + i2s_ll_rx_enable_right_first(hal->dev, slot_cfg->ws_pol); + /* Should always enable fifo */ + i2s_ll_rx_force_enable_fifo_mod(hal->dev, true); +#elif SOC_I2S_HW_VERSION_2 + /* There are always 2 slots in std mode */ + i2s_ll_rx_set_chan_num(hal->dev, 2); + /* In mono mode, there only should be one slot enabled, another inactive slot will transmit same data as enabled slot + * Otherwise always enable the first two slots */ + i2s_ll_rx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? 0x01 : 0x03); + i2s_ll_rx_set_half_sample_bit(hal->dev, slot_bit_width); + i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol); + i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb); + i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->left_align); + i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->big_endian); +#endif +} + +void i2s_hal_std_enable_tx_channel(i2s_hal_context_t *hal) +{ + i2s_ll_tx_enable_std(hal->dev); +} + +void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal) +{ + i2s_ll_rx_enable_std(hal->dev); +} + +/*------------------------------------------------------------------------- + | PDM Specific Slot Configurations | + -------------------------------------------------------------------------*/ +#if SOC_I2S_SUPPORTS_PDM_TX +void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) +{ + i2s_pdm_tx_slot_config_t *slot_cfg = (i2s_pdm_tx_slot_config_t *)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + i2s_ll_tx_reset(hal->dev); + i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave + i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); + + i2s_ll_tx_set_pdm_prescale(hal->dev, slot_cfg->sd_prescale); + i2s_ll_tx_set_pdm_hp_scale(hal->dev, slot_cfg->hp_scale); + i2s_ll_tx_set_pdm_lp_scale(hal->dev, slot_cfg->lp_scale); + i2s_ll_tx_set_pdm_sinc_scale(hal->dev, slot_cfg->sinc_scale); + i2s_ll_tx_set_pdm_sd_scale(hal->dev, slot_cfg->sd_scale); + +#if SOC_I2S_HW_VERSION_1 + i2s_ll_tx_force_enable_fifo_mod(hal->dev, true); +#elif SOC_I2S_HW_VERSION_2 + /* Still need to enable the first 2 TDM channel mask to get the correct number of frame */ + i2s_ll_tx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1); + i2s_ll_tx_enable_pdm_hp_filter(hal->dev, slot_cfg->hp_en); + uint8_t cnt = 0; + float min = 1000; + float expt_cut_off = slot_cfg->hp_cut_off_freq_hz; + /* Find the closest cut-off frequency and its coefficients */ + for (int i = 0; i < 21; i++) { + float tmp = cut_off_coef[i][0] < expt_cut_off ? expt_cut_off - cut_off_coef[i][0] : cut_off_coef[i][0] - expt_cut_off; + if (tmp < min) { + min = tmp; + cnt = i; + } + } + i2s_ll_tx_set_pdm_hp_filter_param0(hal->dev, cut_off_coef[cnt][1]); + i2s_ll_tx_set_pdm_hp_filter_param5(hal->dev, cut_off_coef[cnt][2]); + i2s_ll_tx_enable_pdm_sd_codec(hal->dev, slot_cfg->sd_en); + i2s_ll_tx_set_pdm_sd_dither(hal->dev, slot_cfg->sd_dither); + i2s_ll_tx_set_pdm_sd_dither2(hal->dev, slot_cfg->sd_dither2); +#endif +} + +void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal) +{ + i2s_ll_tx_enable_pdm(hal->dev); +} +#endif #if SOC_I2S_SUPPORTS_PDM_RX -void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal) +void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) { - /* enable pdm rx mode */ - i2s_ll_rx_enable_pdm(hal->dev, true); - /* set pdm rx downsample number */ - i2s_ll_rx_set_pdm_dsr(hal->dev, I2S_PDM_DSR_8S); -#if SOC_I2S_SUPPORTS_TDM - i2s_ll_rx_enable_clock(hal->dev); - i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default - i2s_ll_mclk_use_rx_clk(hal->dev); + i2s_pdm_rx_slot_config_t *slot_cfg = (i2s_pdm_rx_slot_config_t *)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + i2s_ll_rx_reset(hal->dev); + i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave + i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); +#if SOC_I2S_HW_VERSION_1 + i2s_ll_rx_force_enable_fifo_mod(hal->dev, true); +#elif SOC_I2S_HW_VERSION_2 /* Still need to enable the first 2 TDM channel mask to get the correct number of frame */ - i2s_ll_rx_set_active_chan_mask(hal->dev, I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1); -#else - i2s_ll_rx_force_enable_fifo_mod(hal->dev, true); + i2s_ll_rx_set_active_chan_mask(hal->dev, I2S_TDM_SLOT0 | I2S_TDM_SLOT1); #endif } -#endif // SOC_I2S_SUPPORTS_PDM_RX - -void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal) { - /* Disable PDM tx mode and enable TDM mode (if support) */ - i2s_ll_tx_enable_pdm(hal->dev, false); + i2s_ll_rx_enable_pdm(hal->dev); +} +#endif +/*------------------------------------------------------------------------- + | TDM Specific Slot Configurations | + -------------------------------------------------------------------------*/ #if SOC_I2S_SUPPORTS_TDM - i2s_ll_tx_enable_clock(hal->dev); - i2s_ll_tx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default - i2s_ll_mclk_use_tx_clk(hal->dev); - - // In TDM mode(more than 2 channels), the ws polarity should be high first. - if (hal_cfg->total_chan > 2) { - i2s_ll_tx_set_ws_idle_pol(hal->dev, true); - } - i2s_ll_tx_enable_left_align(hal->dev, hal_cfg->left_align); - i2s_ll_tx_enable_big_endian(hal->dev, hal_cfg->big_edin); - i2s_ll_tx_set_bit_order(hal->dev, hal_cfg->bit_order_msb); - i2s_ll_tx_set_skip_mask(hal->dev, hal_cfg->skip_msk); -#else - i2s_ll_tx_enable_msb_right(hal->dev, false); - i2s_ll_tx_enable_right_first(hal->dev, false); - i2s_ll_tx_force_enable_fifo_mod(hal->dev, true); -#endif -} - -void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) +void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) { - /* Disable PDM rx mode and enable TDM rx mode (if support)*/ - i2s_ll_rx_enable_pdm(hal->dev, false); - -#if SOC_I2S_SUPPORTS_TDM - i2s_ll_rx_enable_clock(hal->dev); - i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_D2CLK); // Set I2S_CLK_D2CLK as default - i2s_ll_mclk_use_rx_clk(hal->dev); - - // In TDM mode(more than 2 channels), the ws polarity should be high first. - if (hal_cfg->total_chan > 2) { - i2s_ll_rx_set_ws_idle_pol(hal->dev, true); - } - i2s_ll_rx_enable_left_align(hal->dev, hal_cfg->left_align); - i2s_ll_rx_enable_big_endian(hal->dev, hal_cfg->big_edin); - i2s_ll_rx_set_bit_order(hal->dev, hal_cfg->bit_order_msb); -#else - i2s_ll_rx_enable_msb_right(hal->dev, false); - i2s_ll_rx_enable_right_first(hal->dev, false); - i2s_ll_rx_force_enable_fifo_mod(hal->dev, true); -#endif -} - -static uint32_t i2s_hal_get_ws_bit(i2s_comm_format_t fmt, uint32_t chan_num, uint32_t chan_bits) -{ - switch (fmt) { - case I2S_COMM_FORMAT_STAND_MSB: - return chan_num * chan_bits / 2; - case I2S_COMM_FORMAT_STAND_PCM_SHORT: - return 1; - case I2S_COMM_FORMAT_STAND_PCM_LONG: - return chan_bits; - default: //I2S_COMM_FORMAT_STAND_I2S - return chan_num * chan_bits / 2; - } -} - -void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) -{ - uint32_t chan_num = 2; - uint32_t chan_bits = hal_cfg->chan_bits; - uint32_t data_bits = hal_cfg->sample_bits; - bool is_mono = (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_RIGHT) || - (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT); - - /* Set channel number and valid data bits */ -#if SOC_I2S_SUPPORTS_TDM - chan_num = hal_cfg->total_chan; - i2s_ll_tx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask >> 16); - i2s_ll_tx_set_chan_num(hal->dev, chan_num); -#endif - i2s_ll_tx_set_sample_bit(hal->dev, chan_bits, data_bits); - i2s_ll_tx_enable_mono_mode(hal->dev, is_mono); - - /* Set communication format */ - bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; - uint32_t ws_width = i2s_hal_get_ws_bit(hal_cfg->comm_fmt, chan_num, chan_bits); - i2s_ll_tx_enable_msb_shift(hal->dev, shift_en); - i2s_ll_tx_set_ws_width(hal->dev, ws_width); -#if SOC_I2S_SUPPORTS_TDM - i2s_ll_tx_set_half_sample_bit(hal->dev, chan_num * chan_bits / 2); -#endif -} - -void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) -{ - uint32_t chan_num = 2; - uint32_t chan_bits = hal_cfg->chan_bits; - uint32_t data_bits = hal_cfg->sample_bits; - bool is_mono = (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_RIGHT) || - (hal_cfg->chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT); - -#if SOC_I2S_SUPPORTS_TDM - chan_num = hal_cfg->total_chan; - i2s_ll_rx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask >> 16); - i2s_ll_rx_set_chan_num(hal->dev, chan_num); -#endif - i2s_ll_rx_set_sample_bit(hal->dev, chan_bits, data_bits); - i2s_ll_rx_enable_mono_mode(hal->dev, is_mono); - - /* Set communication format */ - bool shift_en = hal_cfg->comm_fmt == I2S_COMM_FORMAT_STAND_I2S ? true : false; - uint32_t ws_width = i2s_hal_get_ws_bit(hal_cfg->comm_fmt, chan_num, chan_bits); - i2s_ll_rx_enable_msb_shift(hal->dev, shift_en); - i2s_ll_rx_set_ws_width(hal->dev, ws_width); -#if SOC_I2S_SUPPORTS_TDM - i2s_ll_rx_set_half_sample_bit(hal->dev, chan_num * chan_bits / 2); -#endif -} - -void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg) -{ -#if SOC_I2S_SUPPORTS_ADC - if (hal_cfg->mode & I2S_MODE_ADC_BUILT_IN) { - /* In ADC built-in mode, we need to call i2s_set_adc_mode to initialize the specific ADC channel. - * In the current stage, we only support ADC1 and single channel mode. - * In default data mode, the ADC data is in 12-bit resolution mode. - */ - i2s_ll_enable_builtin_adc(hal->dev, true); - return; - } - i2s_ll_enable_builtin_adc(hal->dev, false); -#endif -#if SOC_I2S_SUPPORTS_DAC - if (hal_cfg->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_ll_enable_builtin_dac(hal->dev, true); - return; - } - i2s_ll_enable_builtin_dac(hal->dev, false); -#endif - /* Set configurations for TX mode */ - if (hal_cfg->mode & I2S_MODE_TX) { - i2s_ll_tx_stop(hal->dev); - i2s_ll_tx_reset(hal->dev); - i2s_ll_tx_set_slave_mod(hal->dev, (hal_cfg->mode & I2S_MODE_SLAVE) != 0); //TX Slave -#if SOC_I2S_SUPPORTS_PDM_TX - if (hal_cfg->mode & I2S_MODE_PDM) { - /* Set tx pdm mode */ - i2s_hal_tx_set_pdm_mode_default(hal, hal_cfg->sample_rate); - } else -#endif - { - /* Set tx common mode */ - i2s_hal_tx_set_common_mode(hal, hal_cfg); - } - i2s_hal_tx_set_channel_style(hal, hal_cfg); + i2s_tdm_slot_config_t *slot_cfg = (i2s_tdm_slot_config_t*)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + uint32_t cnt; + uint32_t msk = slot_cfg->slot_mask; + /* Get the maximum slot number */ + cnt = 32 - __builtin_clz(msk); + /* There should be at least 2 slots in total even for mono mode */ + cnt = cnt < 2 ? 2 : cnt; + uint32_t total_slot = slot_cfg->total_slot > cnt ? slot_cfg->total_slot : cnt; + i2s_ll_tx_reset(hal->dev); + i2s_ll_tx_set_slave_mod(hal->dev, is_slave); //TX Slave + i2s_ll_tx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_tx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); + i2s_ll_tx_enable_msb_shift(hal->dev, slot_cfg->bit_shift); + if (slot_cfg->ws_width == I2S_TDM_AUTO_WS_WIDTH) { + i2s_ll_tx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2); + } else { + i2s_ll_tx_set_ws_width(hal->dev, slot_cfg->ws_width); } - /* Set configurations for RX mode */ - if (hal_cfg->mode & I2S_MODE_RX) { - i2s_ll_rx_stop(hal->dev); - i2s_ll_rx_reset(hal->dev); - i2s_ll_rx_set_slave_mod(hal->dev, (hal_cfg->mode & I2S_MODE_SLAVE) != 0); //RX Slave -#if SOC_I2S_SUPPORTS_PDM_RX - if (hal_cfg->mode & I2S_MODE_PDM) { - /* Set rx pdm mode */ - i2s_hal_rx_set_pdm_mode_default(hal); - } else + i2s_ll_tx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol); + i2s_ll_tx_set_chan_num(hal->dev, total_slot); + /* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */ + i2s_ll_tx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? + I2S_TDM_SLOT0 : (uint32_t)slot_cfg->slot_mask); + i2s_ll_tx_set_skip_mask(hal->dev, slot_cfg->skip_mask); + i2s_ll_tx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2); + i2s_ll_tx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb); + i2s_ll_tx_enable_left_align(hal->dev, slot_cfg->left_align); + i2s_ll_tx_enable_big_endian(hal->dev, slot_cfg->big_endian); +} + +void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config) +{ + i2s_tdm_slot_config_t *slot_cfg = (i2s_tdm_slot_config_t*)slot_config; + uint32_t slot_bit_width = (int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width ? + slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + uint32_t cnt; + uint32_t msk = slot_cfg->slot_mask; + for (cnt = 0; msk; cnt++, msk >>= 1); + /* Get the maximum slot number */ + cnt = 32 - __builtin_clz(msk); + /* There should be at least 2 slots in total even for mono mode */ + cnt = cnt < 2 ? 2 : cnt; + uint32_t total_slot = slot_cfg->total_slot > cnt ? slot_cfg->total_slot : cnt; + i2s_ll_rx_reset(hal->dev); + i2s_ll_rx_set_slave_mod(hal->dev, is_slave); //RX Slave + i2s_ll_rx_set_sample_bit(hal->dev, slot_bit_width, slot_cfg->data_bit_width); + i2s_ll_rx_enable_mono_mode(hal->dev, slot_cfg->slot_mode == I2S_SLOT_MODE_MONO); + i2s_ll_rx_enable_msb_shift(hal->dev, slot_cfg->bit_shift); + if (slot_cfg->ws_width == I2S_TDM_AUTO_WS_WIDTH) { + i2s_ll_rx_set_ws_width(hal->dev, (total_slot * slot_bit_width) / 2); + } else { + i2s_ll_rx_set_ws_width(hal->dev, slot_cfg->ws_width); + } + + i2s_ll_rx_set_ws_idle_pol(hal->dev, slot_cfg->ws_pol); + i2s_ll_rx_set_chan_num(hal->dev, total_slot); + /* In mono mode, there only should be one slot enabled, other inactive slots will transmit same data as enabled slot */ + i2s_ll_rx_set_active_chan_mask(hal->dev, (slot_cfg->slot_mode == I2S_SLOT_MODE_MONO) ? + I2S_TDM_SLOT0 : (uint32_t)slot_cfg->slot_mask); + i2s_ll_rx_set_half_sample_bit(hal->dev, total_slot * slot_bit_width / 2); + i2s_ll_rx_set_bit_order(hal->dev, slot_cfg->bit_order_lsb); + i2s_ll_rx_enable_left_align(hal->dev, slot_cfg->left_align); + i2s_ll_rx_enable_big_endian(hal->dev, slot_cfg->big_endian); +} + +void i2s_hal_tdm_enable_tx_channel(i2s_hal_context_t *hal) +{ + i2s_ll_tx_enable_tdm(hal->dev); +} + +void i2s_hal_tdm_enable_rx_channel(i2s_hal_context_t *hal) +{ + i2s_ll_rx_enable_tdm(hal->dev); +} #endif - { - /* Set rx common mode */ - i2s_hal_rx_set_common_mode(hal, hal_cfg); - } - i2s_hal_rx_set_channel_style(hal, hal_cfg); - } - - /* Set configurations for full-duplex mode */ - if ((hal_cfg->mode & I2S_MODE_RX) && (hal_cfg->mode & I2S_MODE_TX)) { - i2s_ll_share_bck_ws(hal->dev, true); - if (hal_cfg->mode & I2S_MODE_MASTER) { - i2s_hal_enable_master_fd_mode(hal); - } else { - i2s_hal_enable_slave_fd_mode(hal); - } - } -} - -void i2s_hal_start_tx(i2s_hal_context_t *hal) -{ - i2s_ll_tx_start(hal->dev); -} - -void i2s_hal_start_rx(i2s_hal_context_t *hal) -{ - i2s_ll_rx_start(hal->dev); -} - -void i2s_hal_stop_tx(i2s_hal_context_t *hal) -{ - i2s_ll_tx_stop(hal->dev); -} - -void i2s_hal_stop_rx(i2s_hal_context_t *hal) -{ - i2s_ll_rx_stop(hal->dev); -} diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index 037970fa2b..0de6904552 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/components/hal/include/hal/i2s_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,362 +15,251 @@ #pragma once -#include "soc/i2s_periph.h" #include "soc/soc_caps.h" #include "hal/i2s_types.h" +#include "hal/i2s_types_priv.h" #include "hal/i2s_ll.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief General clock configuration information + * @note It is a general purpose struct, not supposed to be used directly by user + */ +typedef struct { + uint32_t sample_rate_hz; /*!< I2S sample rate */ + i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */ +} i2s_clk_config_t; + +/** + * @brief General slot configuration information + * @note It is a general purpose struct, not supposed to be used directly by user + */ +typedef struct { + i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode that applied) */ + i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample) */ + i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) */ + i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */ +} i2s_slot_config_t; + /** * @brief I2S clock configuration */ typedef struct { - uint32_t sclk; /*!< I2S module clock */ - uint32_t mclk; /*!< I2S master clock */ - uint32_t bclk; /*!< I2S bit clock */ - uint16_t mclk_div; /*!< I2S master clock division */ - uint16_t bclk_div; /*!< I2S bit clock division*/ -} i2s_hal_clock_cfg_t; - - -/** - * @brief I2S HAL configurations - */ -typedef struct { - i2s_mode_t mode; /*!< I2S work mode, using ored mask of `i2s_mode_t`*/ - uint32_t sample_rate; /*!< I2S sample rate*/ - i2s_comm_format_t comm_fmt; /*!< I2S communication format */ - i2s_channel_fmt_t chan_fmt; /*!< I2S channel format, there are total 16 channels in TDM mode.*/ - uint32_t sample_bits; /*!< I2S sample bits in one channel */ - uint32_t chan_bits; /*!< I2S total bits in one channel. Should not be smaller than 'sample_bits', default '0' means equal to 'sample_bits' */ - uint32_t active_chan; /*!< I2S active channel number */ - uint32_t total_chan; /*!< Total number of I2S channels */ - -#if SOC_I2S_SUPPORTS_TDM - uint32_t chan_mask; /*!< Active channel bit mask, set value in `i2s_channel_t` to enable specific channel, the bit map of active channel can not exceed (0x1<dev); +void i2s_hal_init(i2s_hal_context_t *hal, int port_id); /** - * @brief Disable I2S module clock + * @brief Set tx channel clock + * + * @param hal Context of the HAL layer + * @param clk_info clock information + * @param clk_src clock source + */ +void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src); + +/** + * @brief Set rx channel clock + * + * @param hal Context of the HAL layer + * @param clk_info clock information + * @param clk_src clock source + */ +void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_clock_info_t *clk_info, i2s_clock_src_t clk_src); + + +/*------------------------------------------------------------------------- + | STD configuration | + -------------------------------------------------------------------------*/ +/** + * @brief Set tx slot to standard mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s standard mode + */ +void i2s_hal_std_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Set rx slot to standard mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s standard mode + */ +void i2s_hal_std_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Enable tx channel as standard mode * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_module_clock(hal) i2s_ll_disable_clock((hal)->dev); +void i2s_hal_std_enable_tx_channel(i2s_hal_context_t *hal); + +/** + * @brief Enable rx channel as standard mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_std_enable_rx_channel(i2s_hal_context_t *hal); + +/*------------------------------------------------------------------------- + | PDM configuration | + -------------------------------------------------------------------------*/ +#if SOC_I2S_SUPPORTS_PDM + +#if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief Set tx slot to pdm mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s pdm tx mode + */ +void i2s_hal_pdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Enable tx channel as pdm mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_pdm_enable_tx_channel(i2s_hal_context_t *hal); +#endif // SOC_I2S_SUPPORTS_PDM_TX + +#if SOC_I2S_SUPPORTS_PDM_RX +/** + * @brief Set rx slot to pdm mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s pdm rx mode + */ +void i2s_hal_pdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Enable rx channel as pdm mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_pdm_enable_rx_channel(i2s_hal_context_t *hal); +#endif // SOC_I2S_SUPPORTS_PDM_RX +#endif // SOC_I2S_SUPPORTS_PDM + +/*------------------------------------------------------------------------- + | TDM configuration | + -------------------------------------------------------------------------*/ +#if SOC_I2S_SUPPORTS_TDM +/** + * @brief Set tx slot to tdm mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s tdm mode + */ +void i2s_hal_tdm_set_tx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Set rx slot to tdm mode + * + * @param hal Context of the HAL layer + * @param is_slave If is slave role + * @param slot_config General slot configuration pointer, but will specified to i2s tdm mode + */ +void i2s_hal_tdm_set_rx_slot(i2s_hal_context_t *hal, bool is_slave, const i2s_slot_config_t *slot_config); + +/** + * @brief Enable tx channel as tdm mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_tdm_enable_tx_channel(i2s_hal_context_t *hal); + +/** + * @brief Enable rx channel as tdm mode + * + * @param hal Context of the HAL layer + */ +void i2s_hal_tdm_enable_rx_channel(i2s_hal_context_t *hal); +#endif + +/** + * @brief Start I2S TX channel + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_tx_start(hal) i2s_ll_tx_start((hal)->dev) + +/** + * @brief Stop I2S TX channel + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_tx_stop(hal) i2s_ll_tx_stop((hal)->dev) /** * @brief Reset I2S TX channel * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_tx(hal) i2s_ll_tx_reset((hal)->dev) +#define i2s_hal_tx_reset(hal) i2s_ll_tx_reset((hal)->dev) /** * @brief Reset I2S TX fifo * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_tx_fifo(hal) i2s_ll_tx_reset_fifo((hal)->dev) +#define i2s_hal_tx_reset_fifo(hal) i2s_ll_tx_reset_fifo((hal)->dev) + +/** + * @brief Start I2S RX channel + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_rx_start(hal) i2s_ll_rx_start((hal)->dev) + +/** + * @brief Stop I2S RX channel + * + * @param hal Context of the HAL layer + */ +#define i2s_hal_rx_stop(hal) i2s_ll_rx_stop((hal)->dev) /** * @brief Reset I2S RX channel * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_rx(hal) i2s_ll_rx_reset((hal)->dev) +#define i2s_hal_rx_reset(hal) i2s_ll_rx_reset((hal)->dev) /** * @brief Reset I2S RX fifo * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_rx_fifo(hal) i2s_ll_rx_reset_fifo((hal)->dev) +#define i2s_hal_rx_reset_fifo(hal) i2s_ll_rx_reset_fifo((hal)->dev) -/** - * @brief Get I2S hardware instance and enable I2S module clock - * @note This function should be called first before other hal layer function is called - * - * @param hal Context of the HAL layer - * @param i2s_num The uart port number, the max port number is (I2S_NUM_MAX -1) - */ -void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num); - -/** - * @brief Configure I2S source clock - * - * @param hal Context of the HAL layer - * @param sel The source clock index - */ -void i2s_hal_set_clock_src(i2s_hal_context_t *hal, i2s_clock_src_t sel); - -/** - * @brief Set Tx channel style - * - * @param hal Context of the HAL layer - * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` - */ -void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); - -/** - * @brief Set Rx channel style - * - * @param hal Context of the HAL layer - * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` - */ -void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); - -/** - * @brief Initialize I2S hardware - * - * @param hal Context of the HAL layer - * @param hal_cfg I2S hal configuration structer, refer to `i2s_hal_config_t` - */ -void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); - -/** - * @brief Enable I2S master full-duplex mode - * - * @param hal Context of the HAL layer - */ -void i2s_hal_enable_master_fd_mode(i2s_hal_context_t *hal); - -/** - * @brief Enable I2S slave full-duplex mode - * - * @param hal Context of the HAL layer - */ -void i2s_hal_enable_slave_fd_mode(i2s_hal_context_t *hal); - -/** - * @brief Start I2S tx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_start_tx(i2s_hal_context_t *hal); - -/** - * @brief Start I2S rx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_start_rx(i2s_hal_context_t *hal); - -/** - * @brief Stop I2S tx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_stop_tx(i2s_hal_context_t *hal); - -/** - * @brief Stop I2S rx - * - * @param hal Context of the HAL layer - */ -void i2s_hal_stop_rx(i2s_hal_context_t *hal); - -/** - * @brief Set the received data length to trigger `in_suc_eof` interrupt. - * - * @param hal Context of the HAL layer - * @param eof_byte The byte length that trigger in_suc_eof interrupt. - */ -#define i2s_hal_set_rx_eof_num(hal, eof_byte) i2s_ll_rx_set_eof_num((hal)->dev, eof_byte) - -/** - * @brief Set I2S TX sample bit - * - * @param hal Context of the HAL layer - * @param chan_bit I2S TX chan bit - * @param data_bit The sample data bit length. - */ -#define i2s_hal_set_tx_sample_bit(hal, chan_bit, data_bit) i2s_ll_tx_set_sample_bit((hal)->dev, chan_bit, data_bit) - -/** - * @brief Set I2S RX sample bit - * - * @param hal Context of the HAL layer - * @param chan_bit I2S RX chan bit - * @param data_bit The sample data bit length. - */ -#define i2s_hal_set_rx_sample_bit(hal, chan_bit, data_bit) i2s_ll_rx_set_sample_bit((hal)->dev, chan_bit, data_bit) - -/** - * @brief Configure I2S TX module clock devider - * - * @param hal Context of the HAL layer - * @param clk_cfg I2S clock configuration - */ -void i2s_hal_tx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg); - -/** - * @brief Configure I2S RX module clock devider - * - * @param hal Context of the HAL layer - * @param clk_cfg I2S clock configuration - */ -void i2s_hal_rx_clock_config(i2s_hal_context_t *hal, i2s_hal_clock_cfg_t *clk_cfg); - -/** - * @brief Set I2S tx clock source - * - * @param hal Context of the HAL layer - * @param clk_src i2s tx clock source (see 'i2s_clock_src_t') - */ -#define i2s_hal_tx_set_clock_source(hal, clk_src) i2s_ll_tx_clk_set_src((hal)->dev, clk_src) - -/** - * @brief Set I2S rx clock source - * - * @param hal Context of the HAL layer - * @param clk_src i2s rx clock source (see 'i2s_clock_src_t') - */ -#define i2s_hal_rx_set_clock_source(hal, clk_src) i2s_ll_rx_clk_set_src((hal)->dev, clk_src) - -/** - * @brief Enable I2S tx slave mode - * - * @param hal Context of the HAL layer - * @param enable set 'true' to enable tx slave mode - */ -#define i2s_hal_tx_enable_slave_mode(hal, enable) i2s_ll_tx_set_slave_mod((hal)->dev, enable) - -/** - * @brief Enable I2S rx slave mode - * - * @param hal Context of the HAL layer - * @param enable set 'true' to enable rx slave mode - */ -#define i2s_hal_rx_enable_slave_mode(hal, enable) i2s_ll_rx_set_slave_mod((hal)->dev, enable) - -/** - * @brief Enable loopback mode - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_enable_sig_loopback(hal) i2s_ll_share_bck_ws((hal)->dev, true) - -/** - * @brief Set I2S configuration for common TX mode - * @note Common mode is for non-PDM mode like philip/MSB/PCM - * - * @param hal Context of the HAL layer - * @param hal_cfg hal configuration structure - */ -void i2s_hal_tx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); - -/** - * @brief Set I2S configuration for common RX mode - * @note Common mode is for non-PDM mode like philip/MSB/PCM - * - * @param hal Context of the HAL layer - * @param hal_cfg hal configuration structure - */ -void i2s_hal_rx_set_common_mode(i2s_hal_context_t *hal, const i2s_hal_config_t *hal_cfg); - -#if SOC_I2S_SUPPORTS_PCM -/** - * @brief Configure I2S TX PCM encoder or decoder. - * - * @param hal Context of the HAL layer - * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` - */ -#define i2s_hal_tx_pcm_cfg(hal, cfg) i2s_ll_tx_set_pcm_type((hal)->dev, cfg) - -/** - * @brief Configure I2S RX PCM encoder or decoder. - * - * @param hal Context of the HAL layer - * @param cfg PCM configure paramater, refer to `i2s_pcm_compress_t` - */ -#define i2s_hal_rx_pcm_cfg(hal, cfg) i2s_ll_rx_set_pcm_type((hal)->dev, cfg) -#endif - -#if SOC_I2S_SUPPORTS_PDM_TX -/** - * @brief Configure I2S TX PDM sample rate - * Fpdm = 64*Fpcm*fp/fs - * - * @param hal Context of the HAL layer - * @param fp TX PDM fp paramater configuration - * @param fs TX PDM fs paramater configuration - */ -#define i2s_hal_set_tx_pdm_fpfs(hal, fp, fs) i2s_ll_tx_set_pdm_fpfs((hal)->dev, fp, fs) - -/** - * @brief Get I2S TX PDM fp - * - * @param hal Context of the HAL layer - * @return - * - fp configuration paramater - */ -#define i2s_hal_get_tx_pdm_fp(hal) i2s_ll_tx_get_pdm_fp((hal)->dev) - -/** - * @brief Get I2S TX PDM fs - * - * @param hal Context of the HAL layer - * @return - * - fs configuration paramater - */ -#define i2s_hal_get_tx_pdm_fs(hal) i2s_ll_tx_get_pdm_fs((hal)->dev) - -/** - * @brief Set I2S default configuration for PDM TX mode - * - * @param hal Context of the HAL layer - * @param sample_rate PDM sample rate - */ -void i2s_hal_tx_set_pdm_mode_default(i2s_hal_context_t *hal, uint32_t sample_rate); -#endif - -#if SOC_I2S_SUPPORTS_PDM_RX - -/** - * @brief Configure RX PDM downsample - * - * @param hal Context of the HAL layer - * @param dsr PDM downsample configuration paramater - */ -#define i2s_hal_set_rx_pdm_dsr(hal, dsr) i2s_ll_rx_set_pdm_dsr((hal)->dev, dsr) - -/** - * @brief Get RX PDM downsample configuration - * - * @param hal Context of the HAL layer - * @param dsr Pointer to accept PDM downsample configuration - */ -#define i2s_hal_get_rx_pdm_dsr(hal, dsr) i2s_ll_rx_get_pdm_dsr((hal)->dev, dsr) - -/** - * @brief Set I2S default configuration for PDM R mode - * - * @param hal Context of the HAL layer - */ -void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); -#endif #if !SOC_GDMA_SUPPORTED /** @@ -378,28 +267,28 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_tx_dma(hal) i2s_ll_enable_dma((hal)->dev,true) +#define i2s_hal_tx_enable_dma(hal) i2s_ll_enable_dma((hal)->dev,true) /** * @brief Enable I2S RX DMA * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_rx_dma(hal) i2s_ll_enable_dma((hal)->dev,true) +#define i2s_hal_rx_enable_dma(hal) i2s_ll_enable_dma((hal)->dev,true) /** * @brief Disable I2S TX DMA * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_tx_dma(hal) i2s_ll_enable_dma((hal)->dev,false) +#define i2s_hal_tx_disable_dma(hal) i2s_ll_enable_dma((hal)->dev,false) /** * @brief Disable I2S RX DMA * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_rx_dma(hal) i2s_ll_enable_dma((hal)->dev,false) +#define i2s_hal_rx_disable_dma(hal) i2s_ll_enable_dma((hal)->dev,false) /** * @brief Get I2S interrupt status @@ -423,28 +312,28 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_rx_intr(hal) i2s_ll_rx_enable_intr((hal)->dev) +#define i2s_hal_rx_enable_intr(hal) i2s_ll_rx_enable_intr((hal)->dev) /** * @brief Disable I2S RX interrupt * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_rx_intr(hal) i2s_ll_rx_disable_intr((hal)->dev) +#define i2s_hal_rx_disable_intr(hal) i2s_ll_rx_disable_intr((hal)->dev) /** * @brief Disable I2S TX interrupt * * @param hal Context of the HAL layer */ -#define i2s_hal_disable_tx_intr(hal) i2s_ll_tx_disable_intr((hal)->dev) +#define i2s_hal_tx_disable_intr(hal) i2s_ll_tx_disable_intr((hal)->dev) /** * @brief Enable I2S TX interrupt * * @param hal Context of the HAL layer */ -#define i2s_hal_enable_tx_intr(hal) i2s_ll_tx_enable_intr((hal)->dev) +#define i2s_hal_tx_enable_intr(hal) i2s_ll_tx_enable_intr((hal)->dev) /** * @brief Configure TX DMA descriptor address and start TX DMA @@ -452,7 +341,7 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); * @param hal Context of the HAL layer * @param link_addr DMA descriptor link address. */ -#define i2s_hal_start_tx_link(hal, link_addr) i2s_ll_tx_start_link((hal)->dev, link_addr) +#define i2s_hal_tx_start_link(hal, link_addr) i2s_ll_tx_start_link((hal)->dev, link_addr) /** * @brief Configure RX DMA descriptor address and start RX DMA @@ -460,35 +349,35 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); * @param hal Context of the HAL layer * @param link_addr DMA descriptor link address. */ -#define i2s_hal_start_rx_link(hal, link_addr) i2s_ll_rx_start_link((hal)->dev, link_addr) +#define i2s_hal_rx_start_link(hal, link_addr) i2s_ll_rx_start_link((hal)->dev, link_addr) /** * @brief Stop TX DMA link * * @param hal Context of the HAL layer */ -#define i2s_hal_stop_tx_link(hal) i2s_ll_tx_stop_link((hal)->dev) +#define i2s_hal_tx_stop_link(hal) i2s_ll_tx_stop_link((hal)->dev) /** * @brief Stop RX DMA link * * @param hal Context of the HAL layer */ -#define i2s_hal_stop_rx_link(hal) i2s_ll_rx_stop_link((hal)->dev) +#define i2s_hal_rx_stop_link(hal) i2s_ll_rx_stop_link((hal)->dev) /** * @brief Reset RX DMA * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_rxdma(hal) i2s_ll_rx_reset_dma((hal)->dev) +#define i2s_hal_rx_reset_dma(hal) i2s_ll_rx_reset_dma((hal)->dev) /** * @brief Reset TX DMA * * @param hal Context of the HAL layer */ -#define i2s_hal_reset_txdma(hal) i2s_ll_tx_reset_dma((hal)->dev) +#define i2s_hal_tx_reset_dma(hal) i2s_ll_tx_reset_dma((hal)->dev) /** * @brief Get I2S out eof descriptor address @@ -507,38 +396,6 @@ void i2s_hal_rx_set_pdm_mode_default(i2s_hal_context_t *hal); #define i2s_hal_get_in_eof_des_addr(hal, addr) i2s_ll_rx_get_eof_des_addr((hal)->dev, addr) #endif -#if SOC_I2S_SUPPORTS_ADC -/** - * @brief Enable Builtin DAC - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_enable_builtin_dac(hal) i2s_ll_enable_builtin_dac((hal)->dev, true); - -/** - * @brief Enable Builtin ADC - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_enable_builtin_adc(hal) i2s_ll_enable_builtin_adc((hal)->dev, true); - -/** - * @brief Disable Builtin ADC - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_disable_builtin_adc(hal) i2s_ll_enable_builtin_adc((hal)->dev, false); -#endif - -#if SOC_I2S_SUPPORTS_DAC -/** - * @brief Disable Builtin DAC - * - * @param hal Context of the HAL layer - */ -#define i2s_hal_disable_builtin_dac(hal) i2s_ll_enable_builtin_dac((hal)->dev, false); -#endif - #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/i2s_pdm.h b/components/hal/include/hal/i2s_pdm.h new file mode 100644 index 0000000000..611ff9718d --- /dev/null +++ b/components/hal/include/hal/i2s_pdm.h @@ -0,0 +1,195 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file is specified for I2S PDM communication mode + * Features: + * - Only support PDM tx/rx mode + * - Fixed to 2 slots + * - Data bit width only support 16 bits + */ +#pragma once + +#include "hal/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_I2S_SUPPORTS_PDM_RX + +/** + * @brief PDM format in 2 slots(RX) + * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_PDM_RX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_PDM, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ +} + +/** + * @brief i2s default pdm rx clock configuration + * @param rate sample rate + */ +#define I2S_PDM_RX_CLK_DEFAULT_CONFIG(rate) { \ + .sample_rate_hz = rate, \ + .clk_src = I2S_CLK_D2CLK, \ + .mclk_multiple = I2S_MCLK_MULTIPLE_256, \ + .dn_sample_mode = I2S_PDM_DSR_8S \ +} + +/** + * @breif I2S slot configuration for pdm rx mode + */ +typedef struct { + /* General fields */ + i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */ + i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */ + i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) , only support 16 bits for PDM mode */ + i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */ + +} i2s_pdm_rx_slot_config_t; + +/** + * @breif I2S clock configuration for pdm rx mode + */ +typedef struct { + /* General fields */ + uint32_t sample_rate_hz; /*!< I2S sample rate */ + i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */ + + /* Particular fields */ + i2s_pdm_dsr_t dn_sample_mode; /*!< Down-sampling rate mode */ +} i2s_pdm_rx_clk_config_t; + +#endif // SOC_I2S_SUPPORTS_PDM_RX + + +#if SOC_I2S_SUPPORTS_PDM_TX +#if SOC_I2S_HW_VERSION_2 +/** + * @brief PDM style in 2 slots(TX) + * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_PDM_TX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_PDM, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .sd_prescale = 0, \ + .sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .sd_en = true, \ + .hp_en = true, \ + .hp_cut_off_freq_hz = 49, \ + .sd_dither = 0, \ + .sd_dither2 = 0, \ +} +#else +/** + * @brief PDM style in 2 slots(TX) + * @param bits_per_sample i2s data bit width, only support 16 bits for PDM mode + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_PDM_TX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_PDM, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .dma_desc_num = 8, \ + .dma_frame_num = 200, \ + .auto_clear = false, \ + .slot_mode = mono_or_stereo, \ + .sd_prescale = 0, \ + .sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .hp_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + .sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \ +} +#endif // SOC_I2S_HW_VERSION_2 + +/** + * @brief i2s default pdm tx clock configuration + * @note TX PDM can only be set to the following two up-sampling rate configurations: + * 1: fp = 960, fs = sample_rate_hz / 100, in this case, Fpdm = 128*48000 + * 2: fp = 960, fs = 480, in this case, Fpdm = 128*Fpcm = 128*sample_rate_hz + * If the pdm receiver do not care the pdm serial clock, it's recommended set Fpdm = 128*48000. + * Otherwise, the second configuration should be applied. + * @param rate sample rate + */ +#define I2S_PDM_TX_CLK_DEFAULT_CONFIG(rate) { \ + .sample_rate_hz = rate, \ + .clk_src = I2S_CLK_D2CLK, \ + .mclk_multiple = I2S_MCLK_MULTIPLE_256, \ + .up_sample_fp = 960, \ + .up_sample_fs = ((rate) / 100), \ +} + +/* + High Pass Filter Cut-off Frequency Sheet + +----------------+------------------+----------------+------------------+----------------+------------------+ + | param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) | + +----------------+------------------+----------------+------------------+----------------+------------------+ + | (0, 0) | 185 | (3, 3) | 115 | (5, 5) | 69 | + | (0, 1) | 172 | (1, 7) | 106 | (4, 7) | 63 | + | (1, 1) | 160 | (2, 4) | 104 | (5, 6) | 58 | + | (1, 2) | 150 | (4, 4) | 92 | (5, 7) | 49 | + | (2, 2) | 137 | (2, 7) | 91.5 | (6, 6) | 46 | + | (2, 3) | 126 | (4, 5) | 81 | (6, 7) | 35.5 | + | (0, 3) | 120 | (3, 7) | 77.2 | (7, 7) | 23.3 | + +----------------+------------------+----------------+------------------+----------------+------------------+ + */ + +/** + * @brief I2S slot configuration for pdm tx mode + */ +typedef struct { + /* General fields */ + i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */ + i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode */ + i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot), only support 16 bits for PDM mode */ + i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */ + + /* Particular fields */ + uint32_t sd_prescale; /*!< Sigma-delta filter prescale */ + i2s_pdm_sig_scale_t sd_scale; /*!< Sigma-delta filter scaling value */ + i2s_pdm_sig_scale_t hp_scale; /*!< High pass filter scaling value */ + i2s_pdm_sig_scale_t lp_scale; /*!< Low pass filter scaling value */ + i2s_pdm_sig_scale_t sinc_scale; /*!< Sinc filter scaling value */ +#if SOC_I2S_HW_VERSION_2 + bool sd_en; /*!< Sigma-delta filter enable */ + bool hp_en; /*!< High pass filter enable */ + float hp_cut_off_freq_hz; /*!< High pass filter cut-off frequency, range 23.3Hz ~ 185Hz, see cut-off frequency sheet above */ + uint32_t sd_dither; /*!< Sigma-delta filter dither */ + uint32_t sd_dither2; /*!< Sigma-delta filter dither2 */ +#endif // SOC_I2S_HW_VERSION_2 +} i2s_pdm_tx_slot_config_t; + +/** + * @breif I2S clock configuration for pdm tx mode + */ +typedef struct { + /* General fields */ + uint32_t sample_rate_hz; /*!< I2S sample rate */ + i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */ + + /* Particular fields */ + uint32_t up_sample_fp; /*!< Up-sampling param fp */ + uint32_t up_sample_fs; /*!< Up-sampling param fs */ +} i2s_pdm_tx_clk_config_t; + +#endif // SOC_I2S_SUPPORTS_PDM_TX + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/i2s_std.h b/components/hal/include/hal/i2s_std.h new file mode 100644 index 0000000000..d4fb8b5dd1 --- /dev/null +++ b/components/hal/include/hal/i2s_std.h @@ -0,0 +1,175 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file is specified for I2S standard communication mode + * Features: + * - Philip/MSB/PCM are supported in standard mode + * - Fixed to 2 slots + */ +#pragma once + +#include "hal/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2 +/** + * @brief Philip format in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = true, \ + .msb_right = false, \ +} + +/** + * @brief PCM(short) format in 2 slots + * @note PCM(long) is sample as philip in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = 1, \ + .ws_pol = true, \ + .bit_shift = true, \ + .msb_right = false, \ +} + +/** + * @brief MSB format in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = false, \ + .msb_right = false, \ +} + +#else +/** + * @brief Philip format in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = true, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false \ +} + +/** + * @brief PCM(short) format in 2 slots + * @note PCM(long) is sample as philip in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = 1, \ + .ws_pol = true, \ + .bit_shift = true, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false \ +} + +/** + * @brief MSB format in 2 slots + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + */ +#define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .mode = I2S_COMM_MODE_STD, \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = false, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false \ +} +#endif + +/** + * @brief i2s default standard clock configuration + * @note Please set the mclk_multiple to I2S_MCLK_MULTIPLE_384 while using 24 bits data width + * Otherwise the sample rate might be imprecise since the bclk division is not a integer + * @param rate sample rate + */ +#define I2S_STD_CLK_DEFAULT_CONFIG(rate) { \ + .sample_rate_hz = rate, \ + .clk_src = I2S_CLK_D2CLK, \ + .mclk_multiple = I2S_MCLK_MULTIPLE_256, \ +} + +/** + * @breif I2S slot configuration for standard mode + */ +typedef struct { + /* General fields */ + i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */ + i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample) */ + i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) */ + i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */ + + /* Particular fields */ + uint32_t ws_width; /*!< WS signal width (i.e. the number of bclk ticks that ws signal is high) */ + bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */ + bool bit_shift; /*!< Set to enbale bit shift in Philip mode */ +#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2 + bool msb_right; /*!< Set to place right channel data at the MSB in the FIFO */ +#else + bool left_align; /*!< Set to enable left alignment */ + bool big_endian; /*!< Set to enable big endian */ + bool bit_order_lsb; /*!< Set to enable lsb first */ +#endif +} i2s_std_slot_config_t; + +/** + * @breif I2S clock configuration for standard mode + */ +typedef struct { + /* General fields */ + uint32_t sample_rate_hz; /*!< I2S sample rate */ + i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */ +} i2s_std_clk_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/i2s_tdm.h b/components/hal/include/hal/i2s_tdm.h new file mode 100644 index 0000000000..b5290bc020 --- /dev/null +++ b/components/hal/include/hal/i2s_tdm.h @@ -0,0 +1,163 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file is specified for I2S TDM communication mode + * Features: + * - More than 2 slots + */ +#pragma once + +#include "hal/i2s_types.h" + +#if SOC_I2S_SUPPORTS_TDM + +#ifdef __cplusplus +extern "C" { +#endif + +#define I2S_TDM_AUTO_SLOT_NUM (0) // Auto means total slot number will be equal to the maximum active slot number +#define I2S_TDM_AUTO_WS_WIDTH (0) // Auto means ws signal width will be equal to the half width of a frame + +/** + * @brief Philip format in active slot that enabled by mask + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + * @param mask active slot mask + */ +#define I2S_TDM_PHILIP_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \ + .mode = I2S_COMM_MODE_TDM, \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = I2S_TDM_AUTO_WS_WIDTH, \ + .ws_pol = false, \ + .bit_shift = true, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false, \ + .skip_mask = false, \ + .slot_mask = (mask), \ + .total_slot = I2S_TDM_AUTO_SLOT_NUM \ +} + +/** + * @brief MSB format in active slot enabled that by mask + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + * @param mask active slot mask + */ +#define I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \ + .mode = I2S_COMM_MODE_TDM, \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = I2S_TDM_AUTO_WS_WIDTH, \ + .ws_pol = false, \ + .bit_shift = false, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false, \ + .skip_mask = false ,\ + .slot_mask = (mask), \ + .total_slot = I2S_TDM_AUTO_SLOT_NUM \ +} + +/** + * @brief PCM(short) format in active slot that enabled by mask + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + * @param mask active slot mask + */ +#define I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \ + .mode = I2S_COMM_MODE_TDM, \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = 1, \ + .ws_pol = true, \ + .bit_shift = true, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false, \ + .skip_mask = false, \ + .slot_mask = (mask), \ + .total_slot = I2S_TDM_AUTO_SLOT_NUM \ +} + +/** + * @brief PCM(long) format in active slot that enabled by mask + * @param bits_per_sample i2s data bit width + * @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO + * @param mask active slot mask + */ +#define I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo, mask) { \ + .mode = I2S_COMM_MODE_TDM, \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_DEFAULT, \ + .slot_mode = mono_or_stereo, \ + .ws_width = (bits_per_sample), \ + .ws_pol = true, \ + .bit_shift = true, \ + .left_align = false, \ + .big_endian = false, \ + .bit_order_lsb = false, \ + .skip_mask = false, \ + .slot_mask = (mask), \ + .total_slot = I2S_TDM_AUTO_SLOT_NUM \ +} + +/** + * @brief i2s default tdm clock configuration + * @note Please set the mclk_multiple to I2S_MCLK_MULTIPLE_384 while the data width in slot configuration is set to 24 bits + * Otherwise the sample rate might be imprecise since the bclk division is not a integer + * @param rate sample rate + */ +#define I2S_TDM_CLK_DEFAULT_CONFIG(rate) { \ + .sample_rate_hz = rate, \ + .clk_src = I2S_CLK_D2CLK, \ + .mclk_multiple = I2S_MCLK_MULTIPLE_256, \ +} + +/** + * @breif I2S slot configuration for tdm mode + */ +typedef struct { + /* General fields */ + i2s_comm_mode_t mode; /*!< I2S communication mode, this field is for identification (MUST match the communication mode in 'i2s_chan_config_t') */ + i2s_data_bit_width_t data_bit_width; /*!< I2S sample data bit width (valid data bits per sample) */ + i2s_slot_bit_width_t slot_bit_width; /*!< I2S slot bit width (total bits per slot) */ + i2s_slot_mode_t slot_mode; /*!< Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO */ + + /* Particular fields */ + uint32_t ws_width; /*!< WS signal width ((i.e. the number of bclk ticks that ws signal is high)) */ + bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */ + bool bit_shift; /*!< Set true to enable bit shift in Philip mode */ + + bool left_align; /*!< Set true to enable left alignment */ + bool big_endian; /*!< Set true to enable big endian */ + bool bit_order_lsb; /*!< Set true to enable lsb first */ + + bool skip_mask; /*!< Set true to enable skip mask. If it is enabled, only the data of the enabled channels will be sent, otherwise all data stored in DMA TX buffer will be sent */ + i2s_tdm_slot_mask_t slot_mask; /*!< Slot mask. Activating slots by setting 1 to corresponding bits. When the activated slots is not consecutive, those data in unactivated slots will be ignored */ + uint32_t total_slot; /*!< I2S total number of slots. If it is smaller than the biggest activated channel number, it will be set to this number automatically. */ +} i2s_tdm_slot_config_t; + +/** + * @breif I2S clock configuration for tdm mode + */ +typedef struct { + /* General fields */ + uint32_t sample_rate_hz; /*!< I2S sample rate */ + i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate */ +} i2s_tdm_clk_config_t; + +#ifdef __cplusplus +} +#endif + +#endif // SOC_I2S_SUPPORTS_TDM diff --git a/components/hal/include/hal/i2s_types_priv.h b/components/hal/include/hal/i2s_types_priv.h new file mode 100644 index 0000000000..342327fcf8 --- /dev/null +++ b/components/hal/include/hal/i2s_types_priv.h @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "soc/soc_caps.h" +#include "i2s_types_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief I2S controller communication mode + */ +typedef enum { + I2S_COMM_MODE_STD, /*!< I2S controller using standard communication mode, support philip/MSB/PCM format */ +#if SOC_I2S_SUPPORTS_PDM + I2S_COMM_MODE_PDM, /*!< I2S controller using PDM communication mode, support PDM output or input */ +#endif +#if SOC_I2S_SUPPORTS_TDM + I2S_COMM_MODE_TDM, /*!< I2S controller using TDM communication mode, support up to 16 slots per frame */ +#endif + I2S_COMM_MODE_MAX +} i2s_comm_mode_t; + +/** + * @brief I2S channel slot mode + */ +typedef enum { + I2S_SLOT_MODE_MONO = 1, /*!< I2S channel slot format mono, transmit same data in all slots for tx mode, only receive the data in the first slots for rx mode. */ + I2S_SLOT_MODE_STEREO = 2, /*!< I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode. */ +} i2s_slot_mode_t; + +/** + * @brief I2S channel direction + */ +typedef enum { + I2S_DIR_RX = BIT(0), /*!< I2S channel direction RX */ + I2S_DIR_TX = BIT(1), /*!< I2S channel direction TX */ +} i2s_dir_t; + +/** + * @brief I2S controller role + */ +typedef enum { + I2S_ROLE_MASTER, /*!< I2S controller master role, bclk and ws signal will be set to output */ + I2S_ROLE_SLAVE /*!< I2S controller slave role, bclk and ws signal will be set to input */ +} i2s_role_t; + +/** + * @brief Available data bit width in one slot + */ +typedef enum { + I2S_DATA_BIT_WIDTH_8BIT = 8, /*!< I2S channel data bit-width: 8 */ + I2S_DATA_BIT_WIDTH_16BIT = 16, /*!< I2S channel data bit-width: 16 */ + I2S_DATA_BIT_WIDTH_24BIT = 24, /*!< I2S channel data bit-width: 24 */ + I2S_DATA_BIT_WIDTH_32BIT = 32, /*!< I2S channel data bit-width: 32 */ +} i2s_data_bit_width_t; + +/** + * @brief Total slot bit width in one slot + * + */ +typedef enum { + I2S_SLOT_BIT_WIDTH_DEFAULT = (0), /*!< I2S channel slot bit-width equals to data bit-width */ + I2S_SLOT_BIT_WIDTH_8BIT = (8), /*!< I2S channel slot bit-width: 8 */ + I2S_SLOT_BIT_WIDTH_16BIT = (16), /*!< I2S channel slot bit-width: 16 */ + I2S_SLOT_BIT_WIDTH_24BIT = (24), /*!< I2S channel slot bit-width: 24 */ + I2S_SLOT_BIT_WIDTH_32BIT = (32), /*!< I2S channel slot bit-width: 32 */ +} i2s_slot_bit_width_t; + +#if SOC_I2S_SUPPORTS_TDM +/** + * @brief tdm slot number + * @note There are 16 slots in TDM mode. + * For TX module, only the active slot send the audio data, the inactive slot send a constant or will be skipped if 'skip_msk' is set. + * For RX module, only receive the audio data in active slots, the data in inactive slots will be ignored. + * the bit map of active slot can not exceed (0x1<