diff --git a/components/driver/deprecated/adc_dma_legacy.c b/components/driver/deprecated/adc_dma_legacy.c index 04eb8c9024..a2e2a2dc0e 100644 --- a/components/driver/deprecated/adc_dma_legacy.c +++ b/components/driver/deprecated/adc_dma_legacy.c @@ -101,7 +101,7 @@ extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate posi #define adc_dma_disable_intr(adc_dma) i2s_ll_enable_intr(s_adc_digi_ctx->adc_i2s_dev, ADC_DMA_INTR_MASK, false); #define adc_dma_deinit(adc_dma) do { \ esp_intr_free(s_adc_digi_ctx->intr_hdl); \ - i2s_platform_release_occupation(ADC_DMA_I2S_HOST); \ + i2s_platform_release_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST); \ } while (0) #endif @@ -328,7 +328,7 @@ esp_err_t adc_digi_initialize(const adc_digi_init_config_t *init_config) s_adc_digi_ctx->adc_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST); #elif CONFIG_IDF_TARGET_ESP32 //ADC utilises I2S0 DMA on ESP32 - ret = i2s_platform_acquire_occupation(ADC_DMA_I2S_HOST, "adc"); + ret = i2s_platform_acquire_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST, "adc"); if (ret != ESP_OK) { ret = ESP_ERR_NOT_FOUND; goto cleanup; diff --git a/components/driver/deprecated/i2s_legacy.c b/components/driver/deprecated/i2s_legacy.c index f89bbf5f73..1f54c9cf06 100644 --- a/components/driver/deprecated/i2s_legacy.c +++ b/components/driver/deprecated/i2s_legacy.c @@ -1637,7 +1637,7 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) } #endif /* Disable module clock */ - i2s_platform_release_occupation(i2s_num); + i2s_platform_release_occupation(I2S_CTLR_HP, i2s_num); free(obj); p_i2s[i2s_num] = NULL; return ESP_OK; @@ -1656,7 +1656,7 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, /* Step 2: Allocate driver object and register to platform */ 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_platform_acquire_occupation(i2s_num, "i2s_legacy") != ESP_OK) { + if (i2s_platform_acquire_occupation(I2S_CTLR_HP, i2s_num, "i2s_legacy") != ESP_OK) { free(i2s_obj); ESP_LOGE(TAG, "register I2S object to platform failed"); return ESP_ERR_INVALID_STATE; diff --git a/components/esp_adc/esp32/adc_dma.c b/components/esp_adc/esp32/adc_dma.c index 4ba169883a..0aceacd769 100644 --- a/components/esp_adc/esp32/adc_dma.c +++ b/components/esp_adc/esp32/adc_dma.c @@ -51,7 +51,7 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma) { esp_err_t ret = ESP_OK; //ADC utilises I2S0 DMA on ESP32 - ret = i2s_platform_acquire_occupation(ADC_DMA_I2S_HOST, "adc"); + ret = i2s_platform_acquire_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST, "adc"); if (ret != ESP_OK) { return ESP_ERR_NOT_FOUND; } @@ -62,7 +62,7 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma) esp_err_t adc_dma_deinit(adc_dma_t adc_dma) { esp_intr_free(adc_dma.dma_intr_hdl); - i2s_platform_release_occupation(ADC_DMA_I2S_HOST); + i2s_platform_release_occupation(I2S_CTLR_HP, ADC_DMA_I2S_HOST); return ESP_OK; } diff --git a/components/esp_driver_dac/esp32/dac_dma.c b/components/esp_driver_dac/esp32/dac_dma.c index 84cd0b4ad7..7d27988ca9 100644 --- a/components/esp_driver_dac/esp32/dac_dma.c +++ b/components/esp_driver_dac/esp32/dac_dma.c @@ -113,7 +113,7 @@ esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll) #endif esp_err_t ret = ESP_OK; /* Acquire DMA peripheral */ - ESP_RETURN_ON_ERROR(i2s_platform_acquire_occupation(DAC_DMA_PERIPH_I2S_NUM, "dac_dma"), TAG, "Failed to acquire DAC DMA peripheral"); + ESP_RETURN_ON_ERROR(i2s_platform_acquire_occupation(I2S_CTLR_HP, DAC_DMA_PERIPH_I2S_NUM, "dac_dma"), TAG, "Failed to acquire DAC DMA peripheral"); /* Allocate DAC DMA peripheral object */ s_ddp = (dac_dma_periph_i2s_t *)heap_caps_calloc(1, sizeof(dac_dma_periph_i2s_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); ESP_GOTO_ON_FALSE(s_ddp, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC DMA object"); @@ -150,7 +150,7 @@ err: esp_err_t dac_dma_periph_deinit(void) { ESP_RETURN_ON_FALSE(s_ddp->intr_handle == NULL, ESP_ERR_INVALID_STATE, TAG, "The interrupt is not deregistered yet"); - ESP_RETURN_ON_ERROR(i2s_platform_release_occupation(DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to release DAC DMA peripheral"); + ESP_RETURN_ON_ERROR(i2s_platform_release_occupation(I2S_CTLR_HP, DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to release DAC DMA peripheral"); i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, false); if (s_ddp) { if (s_ddp->use_apll) { diff --git a/components/esp_driver_dac/test_apps/dac/main/test_dac.c b/components/esp_driver_dac/test_apps/dac/main/test_dac.c index 5d49d73418..e77d2fd758 100644 --- a/components/esp_driver_dac/test_apps/dac/main/test_dac.c +++ b/components/esp_driver_dac/test_apps/dac/main/test_dac.c @@ -100,13 +100,13 @@ TEST_CASE("DAC_API_basic_logic_test", "[dac]") }; /* DMA peripheral availability test */ #if CONFIG_IDF_TARGET_ESP32 - TEST_ESP_OK(i2s_platform_acquire_occupation(0, "dac_test")); + TEST_ESP_OK(i2s_platform_acquire_occupation(I2S_CTLR_HP, 0, "dac_test")); #elif CONFIG_IDF_TARGET_ESP32S2 TEST_ASSERT(spicommon_periph_claim(SPI3_HOST, "dac_test")); #endif TEST_ASSERT(dac_continuous_new_channels(&cont_cfg, &cont_handle) == ESP_ERR_NOT_FOUND); #if CONFIG_IDF_TARGET_ESP32 - TEST_ESP_OK(i2s_platform_release_occupation(0)); + TEST_ESP_OK(i2s_platform_release_occupation(I2S_CTLR_HP, 0)); #elif CONFIG_IDF_TARGET_ESP32S2 TEST_ASSERT(spicommon_periph_free(SPI3_HOST)); #endif diff --git a/components/esp_driver_i2s/CMakeLists.txt b/components/esp_driver_i2s/CMakeLists.txt index 6c8bd1ced2..cc39943ae6 100644 --- a/components/esp_driver_i2s/CMakeLists.txt +++ b/components/esp_driver_i2s/CMakeLists.txt @@ -10,7 +10,6 @@ set(include "include") # I2S related source files if(CONFIG_SOC_I2S_SUPPORTED) list(APPEND srcs "i2s_common.c" - "i2s_platform.c" "i2s_std.c") if(CONFIG_SOC_I2S_SUPPORTS_PDM) list(APPEND srcs "i2s_pdm.c") @@ -23,6 +22,14 @@ if(CONFIG_SOC_I2S_SUPPORTED) endif() endif() +if(CONFIG_SOC_I2S_SUPPORTED OR CONFIG_SOC_LP_I2S_SUPPORTED) + list(APPEND srcs "i2s_platform.c") +endif() + +if(CONFIG_SOC_LP_I2S_SUPPORTED) + list(APPEND srcs "lp_i2s.c" "lp_i2s_std.c" "lp_i2s_pdm.c") +endif() + idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${include} PRIV_REQUIRES esp_driver_gpio esp_pm esp_mm diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 4cf02f5ecc..407abb042a 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -164,7 +164,7 @@ static esp_err_t i2s_destroy_controller_obj(i2s_controller_t **i2s_obj) #endif free(*i2s_obj); *i2s_obj = NULL; - return i2s_platform_release_occupation(id); + return i2s_platform_release_occupation(I2S_CTLR_HP, id); } /** @@ -196,7 +196,7 @@ static i2s_controller_t *i2s_acquire_controller_obj(int id) i2s_controller_t *i2s_obj = NULL; /* Try to occupy this i2s controller */ - if (i2s_platform_acquire_occupation(id, "i2s_driver") == ESP_OK) { + if (i2s_platform_acquire_occupation(I2S_CTLR_HP, id, "i2s_driver") == ESP_OK) { portENTER_CRITICAL(&g_i2s.spinlock); i2s_obj = pre_alloc; g_i2s.controller[id] = i2s_obj; diff --git a/components/esp_driver_i2s/i2s_platform.c b/components/esp_driver_i2s/i2s_platform.c index 07a49c1de3..7c5757e0bd 100644 --- a/components/esp_driver_i2s/i2s_platform.c +++ b/components/esp_driver_i2s/i2s_platform.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_check.h" #include "i2s_private.h" +#include "esp_private/i2s_platform.h" static const char *TAG = "i2s_platform"; @@ -17,6 +18,10 @@ i2s_platform_t g_i2s = { .spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, .controller[0 ...(SOC_I2S_NUM - 1)] = NULL, // groups will be lazy installed .comp_name[0 ...(SOC_I2S_NUM - 1)] = NULL, +#if SOC_LP_I2S_SUPPORTED + .lp_controller[0 ...(SOC_LP_I2S_NUM - 1)] = NULL, + .lp_comp_name[0 ...(SOC_LP_I2S_NUM - 1)] = NULL, +#endif }; /*--------------------------------------------------------------------------- @@ -25,47 +30,87 @@ i2s_platform_t g_i2s = { Scope: This file and ADC/DAC/LCD driver ----------------------------------------------------------------------------*/ -esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name) +esp_err_t i2s_platform_acquire_occupation(i2s_ctlr_t type, int id, const char *comp_name) { esp_err_t ret = ESP_OK; const char *occupied_comp = NULL; ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id"); - portENTER_CRITICAL(&g_i2s.spinlock); - if ((!g_i2s.controller[id]) && (g_i2s.comp_name[id] == NULL)) { - g_i2s.comp_name[id] = comp_name; - /* Enable module clock */ - I2S_RCC_ATOMIC() { - i2s_ll_enable_bus_clock(id, true); - i2s_ll_reset_register(id); - i2s_ll_enable_core_clock(I2S_LL_GET_HW(id), true); + + if (type == I2S_CTLR_HP) { + portENTER_CRITICAL(&g_i2s.spinlock); + if ((!g_i2s.controller[id]) && (g_i2s.comp_name[id] == NULL)) { + g_i2s.comp_name[id] = comp_name; + /* Enable module clock */ + I2S_RCC_ATOMIC() { + i2s_ll_enable_bus_clock(id, true); + i2s_ll_reset_register(id); + i2s_ll_enable_core_clock(I2S_LL_GET_HW(id), true); + } + } else { + occupied_comp = g_i2s.comp_name[id]; + ret = ESP_ERR_NOT_FOUND; } - } else { - occupied_comp = g_i2s.comp_name[id]; - ret = ESP_ERR_NOT_FOUND; + portEXIT_CRITICAL(&g_i2s.spinlock); } - portEXIT_CRITICAL(&g_i2s.spinlock); +#if SOC_LP_I2S_SUPPORTED + else { + portENTER_CRITICAL(&g_i2s.spinlock); + if ((!g_i2s.lp_controller[id]) && (g_i2s.lp_comp_name[id] == NULL)) { + g_i2s.lp_comp_name[id] = comp_name; + /* Enable module clock */ + I2S_RCC_ATOMIC() { + lp_i2s_ll_enable_module_clock(id, true); + lp_i2s_ll_reset_module_clock(id); + lp_i2s_ll_enable_rx_module_clock(id, true); + } + } else { + occupied_comp = g_i2s.lp_comp_name[id]; + ret = ESP_ERR_NOT_FOUND; + } + portEXIT_CRITICAL(&g_i2s.spinlock); + } +#endif if (occupied_comp != NULL) { ESP_LOGW(TAG, "i2s controller %d has been occupied by %s", id, occupied_comp); } return ret; } -esp_err_t i2s_platform_release_occupation(int id) +esp_err_t i2s_platform_release_occupation(i2s_ctlr_t type, int id) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid i2s port id"); - portENTER_CRITICAL(&g_i2s.spinlock); - if (!g_i2s.controller[id]) { - g_i2s.comp_name[id] = NULL; - /* Disable module clock */ - I2S_RCC_ATOMIC() { - i2s_ll_enable_bus_clock(id, false); - i2s_ll_enable_core_clock(I2S_LL_GET_HW(id), false); + + if (type == I2S_CTLR_HP) { + portENTER_CRITICAL(&g_i2s.spinlock); + if (!g_i2s.controller[id]) { + g_i2s.comp_name[id] = NULL; + /* Disable module clock */ + I2S_RCC_ATOMIC() { + i2s_ll_enable_bus_clock(id, false); + i2s_ll_enable_core_clock(I2S_LL_GET_HW(id), false); + } + } else { + ret = ESP_ERR_INVALID_STATE; } - } else { - ret = ESP_ERR_INVALID_STATE; + portEXIT_CRITICAL(&g_i2s.spinlock); } - portEXIT_CRITICAL(&g_i2s.spinlock); +#if SOC_LP_I2S_SUPPORTED + else { + portENTER_CRITICAL(&g_i2s.spinlock); + if (!g_i2s.lp_controller[id]) { + g_i2s.lp_comp_name[id] = NULL; + /* Disable module clock */ + I2S_RCC_ATOMIC() { + lp_i2s_ll_enable_module_clock(id, false); + lp_i2s_ll_enable_rx_module_clock(id, false); + } + } else { + ret = ESP_ERR_INVALID_STATE; + } + portEXIT_CRITICAL(&g_i2s.spinlock); + } +#endif return ret; } diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 3675d29897..0071d157f8 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -12,6 +12,10 @@ #include "soc/lldesc.h" #include "soc/soc_caps.h" #include "hal/i2s_hal.h" +#include "hal/lp_i2s_hal.h" +#if SOC_LP_I2S_SUPPORTED +#include "hal/lp_i2s_ll.h" +#endif #include "driver/i2s_types.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp_clock_output.h" @@ -85,6 +89,14 @@ typedef struct { */ } i2s_event_callbacks_internal_t; +/** + * @brief LP I2S callbacks + */ +typedef struct { + lp_i2s_callback_t on_thresh_met; ///< Triggered when the received bytes are bigger than `lp_i2s_chan_config_t:threshold` + lp_i2s_callback_t on_request_new_trans; ///< Triggered when a new transaction buffer is needed, when this callback is registered, you don't need to use `lp_i2s_channel_read` to get data, you can get data via this callback asynchronously +} lp_i2s_evt_cbs_internal_t; + /** * @brief i2s channel level configurations * @note It performs as channel handle @@ -154,14 +166,46 @@ struct i2s_channel_obj_t { void (*stop)(i2s_chan_handle_t); /*!< stop tx/rx channel */ }; +/** + * @brief lp i2s controller type + */ +typedef struct { + int id; /*!< lp i2s port id */ + lp_i2s_hal_context_t hal; /*!< hal context */ + uint32_t chan_occupancy; /*!< channel occupancy (rx/tx) */ + lp_i2s_chan_handle_t rx_chan; /*!< rx channel handle */ + intr_handle_t intr; /*!< interrupt handle */ +} lp_i2s_controller_t; + +/** + * @brief lp i2s channel object type + */ +struct lp_i2s_channel_obj_t { + /* Channel basic information */ + lp_i2s_controller_t *ctlr; /*!< Parent pointer to controller object */ + i2s_comm_mode_t mode; /*!< lp i2s channel communication mode */ + i2s_role_t role; /*!< lp i2s role */ + i2s_dir_t dir; /*!< lp i2s channel direction */ + i2s_state_t state; /*!< lp i2s driver state. Ensuring the driver working in a correct sequence */ + SemaphoreHandle_t semphr; /*!< lp i2s event semphr*/ + lp_i2s_trans_t trans; /*!< transaction */ + size_t threshold; /*!< lp i2s threshold*/ + lp_i2s_evt_cbs_internal_t cbs; /*!< callbacks */ + void *user_data; /*!< user data */ +}; + /** * @brief i2s platform level configurations * @note All i2s controllers' resources are involved */ typedef struct { - portMUX_TYPE spinlock; /*!< Platform level lock */ - i2s_controller_t *controller[SOC_I2S_NUM]; /*!< Controller object */ - const char *comp_name[SOC_I2S_NUM]; /*!< The component name that occupied i2s controller */ + portMUX_TYPE spinlock; /*!< Platform level lock */ + i2s_controller_t *controller[SOC_I2S_NUM]; /*!< Controller object */ + const char *comp_name[SOC_I2S_NUM]; /*!< The component name that occupied i2s controller */ +#if SOC_LP_I2S_SUPPORTED + lp_i2s_controller_t *lp_controller[SOC_LP_I2S_NUM]; /*!< LP controller object*/ + const char *lp_comp_name[SOC_I2S_NUM]; /*!< The component name that occupied lp i2s controller */ +#endif } i2s_platform_t; extern i2s_platform_t g_i2s; /*!< Global i2s instance for driver internal use */ diff --git a/components/esp_driver_i2s/include/driver/i2s_types.h b/components/esp_driver_i2s/include/driver/i2s_types.h index 9f103f0718..f7a602fdde 100644 --- a/components/esp_driver_i2s/include/driver/i2s_types.h +++ b/components/esp_driver_i2s/include/driver/i2s_types.h @@ -62,6 +62,15 @@ typedef enum { I2S_MCLK_MULTIPLE_1152 = 1152, /*!< MCLK = sample_rate * 1152 (24-bit compatible) */ } i2s_mclk_multiple_t; +/** + * @brief LP I2S transaction type + */ +typedef struct { + void *buffer; ///< Pointer to buffer + size_t buflen; ///< Buffer len, this should be in the multiple of 4 + size_t received_size; ///< Received size +} lp_i2s_trans_t; + /** * @brief Event structure used in I2S event queue */ @@ -78,7 +87,15 @@ typedef struct { */ } i2s_event_data_t; -typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/ +/** + * @brief Event data structure for LP I2S + */ +typedef struct { + lp_i2s_trans_t trans; ///< LP I2S transaction +} lp_i2s_evt_data_t; + +typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/ +typedef struct lp_i2s_channel_obj_t *lp_i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/ /** * @brief I2S event callback @@ -90,6 +107,17 @@ typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object */ typedef bool (*i2s_isr_callback_t)(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx); +/** + * @brief LP I2S event callback type + * + * @param[in] handle LP I2S channel handle + * @param[in] event Event data + * @param[in] user_ctx User data + * + * @return Whether a high priority task has been waken up by this callback function + */ +typedef bool (*lp_i2s_callback_t)(lp_i2s_chan_handle_t handle, lp_i2s_evt_data_t *event, void *user_ctx); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i2s/include/driver/lp_i2s.h b/components/esp_driver_i2s/include/driver/lp_i2s.h new file mode 100644 index 0000000000..09db9c057e --- /dev/null +++ b/components/esp_driver_i2s/include/driver/lp_i2s.h @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/i2s_types.h" +#include "hal/i2s_types.h" + +#include "esp_types.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LP_I2S max timeout value + */ +#define LP_I2S_MAX_DELAY UINT32_MAX + +/** + * @brief LP I2S controller channel configuration +*/ +typedef struct { + int id; /*!< LP I2S port id */ + i2s_role_t role; /*!< LP I2S role, only I2S_ROLE_SLAVE supported */ + size_t threshold; /*!< When LP I2S received bytes are bigger than this value, the `on_thresh_met` callback will be triggered. Must be in multiple of 4 */ +} lp_i2s_chan_config_t; + +/** + * @brief LP I2S event callbacks + */ +typedef struct { + lp_i2s_callback_t on_thresh_met; ///< Triggered when the received bytes are bigger than `lp_i2s_chan_config_t:threshold` + lp_i2s_callback_t on_request_new_trans; ///< Triggered when a new transaction buffer is needed, when this callback is registered, you don't need to use `lp_i2s_channel_read` to get data, you can get data via this callback asynchronously +} lp_i2s_evt_cbs_t; + +/** + * @brief Allocate new LP I2S channel(s) + * + * @param[in] chan_cfg LP I2S controller channel configurations + * @param[out] ret_tx_handle LP I2S channel handler used for managing the sending channel(optional), this one is not supported and is kept here for future-proof. + * @param[out] ret_rx_handle LP I2S channel handler used for managing the receiving channel(optional) + * @return + * - ESP_OK Allocate new channel(s) success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or illegal parameter in lp_i2s_chan_config_t + * - ESP_ERR_NOT_FOUND No available LP I2S channel found + */ +esp_err_t lp_i2s_new_channel(const lp_i2s_chan_config_t *chan_cfg, lp_i2s_chan_handle_t *ret_tx_handle, lp_i2s_chan_handle_t *ret_rx_handle); + +/** + * @brief Register LP I2S event callbacks + * + * @param[in] handle LP I2S channel handle + * @param[in] cbs Callbacks + * @param[in] user_data User data + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t lp_i2s_register_event_callbacks(lp_i2s_chan_handle_t handle, const lp_i2s_evt_cbs_t *cbs, void *user_data); + +/** + * @brief Enable LP I2S driver + * + * @param[in] handle LP I2S channel handle + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t lp_i2s_channel_enable(lp_i2s_chan_handle_t chan); + +/** + * @brief Read LP I2S received data + * + * @param[in] handle LP I2S channel handle + * @param[in] trans LP I2S transaction + * @param[in] timeout_ms Timeout in ms, set to `LP_I2S_MAX_DELAY` to wait until read is done + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state, e.g. `on_request_new_trans` callback is registered + */ +esp_err_t lp_i2s_channel_read(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans, uint32_t timeout_ms); + +/** + * @brief Read LP I2S received data until certain bytes + * + * @param[in] handle LP I2S channel handle + * @param[in] trans LP I2S transaction + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state, e.g. `on_request_new_trans` callback is registered + */ +esp_err_t lp_i2s_channel_read_until_bytes(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans); + +/** + * @brief Disable LP I2S driver + * + * @param[in] handle LP I2S channel handle + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t lp_i2s_channel_disable(lp_i2s_chan_handle_t chan); + +/** + * @brief Delete the LP I2S channel + * + * @param[in] handle LP I2S channel handler + * + * @return + * - ESP_OK Delete successfully + * - ESP_ERR_INVALID_ARG NULL pointer + */ +esp_err_t lp_i2s_del_channel(lp_i2s_chan_handle_t handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_i2s/include/driver/lp_i2s_pdm.h b/components/esp_driver_i2s/include/driver/lp_i2s_pdm.h new file mode 100644 index 0000000000..e1cc256327 --- /dev/null +++ b/components/esp_driver_i2s/include/driver/lp_i2s_pdm.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_types.h" +#include "esp_err.h" +#include "driver/i2s_types.h" +#include "hal/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @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 LP_I2S_PDM_RX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .data_bit_width = bits_per_sample, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \ + .slot_mode = mono_or_stereo, \ + .slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \ + I2S_PDM_SLOT_LEFT : I2S_PDM_SLOT_BOTH, \ + .hp_en = true, \ + .hp_cut_off_freq_hz = 35.5, \ + .amplify_num = 1, \ +} + +/** + * @brief LP I2S pin configurations + */ +typedef struct { + int clk; /*!< clk pin number */ + int din; /*!< din pin number */ +} lp_i2s_pdm_rx_gpio_config_t; + +/* + 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 RX mode + */ +typedef struct { + /* General fields */ + 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 */ + i2s_pdm_slot_mask_t slot_mask; /*!< Choose the slots to activate */ + bool ws_pol; /*!< WS signal polarity, set true to enable high lever first */ + 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 amplify_num; /*!< The amplification number of the final conversion result. + * The data that have converted from PDM to PCM module, will time `amplify_num` additionally to amplify the final result. + * Note that it's only a multiplier of the digital PCM data, not the gain of the analog signal + * range 1~15, default 1 */ +} lp_i2s_pdm_rx_slot_config_t; + +/** + * @brief LP I2S PDM configuration + */ +typedef struct { + lp_i2s_pdm_rx_gpio_config_t pin_cfg; /*!< Pin configuration */ + lp_i2s_pdm_rx_slot_config_t slot_cfg; /*!< PDM mode slot configuration */ + /* LP I2S only support slave mode, not support to configure the clock */ +} lp_i2s_pdm_rx_config_t; + +/** + * @brief Init LP I2S to PDM mode + * + * @param[in] handle LP I2S channel handle + * @param[in] pdm_cfg PDM configuration + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t lp_i2s_channel_init_pdm_rx_mode(lp_i2s_chan_handle_t handle, const lp_i2s_pdm_rx_config_t *pdm_cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_i2s/include/driver/lp_i2s_std.h b/components/esp_driver_i2s/include/driver/lp_i2s_std.h new file mode 100644 index 0000000000..28b886c5af --- /dev/null +++ b/components/esp_driver_i2s/include/driver/lp_i2s_std.h @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_types.h" +#include "esp_err.h" +#include "driver/i2s_types.h" +#include "hal/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Philips 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 + */ +#define LP_I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \ + .slot_mode = mono_or_stereo, \ + .slot_mask = I2S_STD_SLOT_BOTH, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = true, \ + .left_align = true, \ + .big_endian = false, \ + .bit_order_lsb = false, \ +} + +/** + * @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 + */ +#define LP_I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \ + .slot_mode = mono_or_stereo, \ + .slot_mask = I2S_STD_SLOT_BOTH, \ + .ws_width = bits_per_sample, \ + .ws_pol = false, \ + .bit_shift = false, \ + .left_align = true, \ + .big_endian = false, \ + .bit_order_lsb = false, \ +} + +/** + * @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 + */ +#define LP_I2S_STD_PCM_SHORT_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \ + .data_bit_width = (bits_per_sample), \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \ + .slot_mode = mono_or_stereo, \ + .slot_mask = I2S_STD_SLOT_BOTH, \ + .ws_width = 1, \ + .ws_pol = true, \ + .bit_shift = true, \ + .left_align = true, \ + .big_endian = false, \ + .bit_order_lsb = false, \ +} + +/** + * @brief LP I2S pin configurations + */ +typedef struct { + int bck; /*!< bck pin number */ + int ws; /*!< ws pin number */ + int din; /*!< din pin number */ +} lp_i2s_std_gpio_config_t; + +/** + * @brief LP I2S slot configuration for standard mode + */ +typedef struct { + /* General fields */ + 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 */ + i2s_std_slot_mask_t slot_mask; /*!< Select the left, right or both slot */ + 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 Philips 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 */ +} lp_i2s_std_slot_config_t; + +/** + * @brief LP I2S STD configuration + */ +typedef struct { + lp_i2s_std_gpio_config_t pin_cfg; /*!< Pin configuration */ + lp_i2s_std_slot_config_t slot_cfg; /*!< STD mode slot configuration, can be generated by macros I2S_STD_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIPS/MSB/PCM_SHORT/PCM_LONG */ + /* LP I2S only support slave mode, not support to configure the clock */ +} lp_i2s_std_config_t; + +/** + * @brief Init LP I2S to STD mode + * + * @param[in] handle LP I2S channel handle + * @param[in] std_cfg STD configuration + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t lp_i2s_channel_init_std_mode(lp_i2s_chan_handle_t handle, const lp_i2s_std_config_t *std_cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_i2s/include/esp_private/i2s_platform.h b/components/esp_driver_i2s/include/esp_private/i2s_platform.h index 8deea86a0e..d5cfe52e7b 100644 --- a/components/esp_driver_i2s/include/esp_private/i2s_platform.h +++ b/components/esp_driver_i2s/include/esp_private/i2s_platform.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,12 +15,21 @@ extern "C" { #endif +/** + * @brief I2S controller type + */ +typedef enum { + I2S_CTLR_HP, ///< HP I2S + I2S_CTLR_LP, ///< LP I2S +} i2s_ctlr_t; + /** * @brief Hold the I2S port occupation * * @note This private API is used to avoid applications from using the same I2S instance for different purpose. * @note This function will help enable the peripheral APB clock as well. * + * @param[in] type I2S controller type * @param id I2S port number * @param comp_name The name of compnant that occupied this i2s controller * @return @@ -28,20 +37,21 @@ extern "C" { * - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id * - ESP_ERR_NOT_FOUND Specific I2S port is not available */ -esp_err_t i2s_platform_acquire_occupation(int id, const char *comp_name); +esp_err_t i2s_platform_acquire_occupation(i2s_ctlr_t type, int id, const char *comp_name); /** * @brief Release the I2S port occupation * * @note This function will help disable the peripheral APB clock as well. * + * @param[in] type I2S controller type * @param id I2S port number * @return * - ESP_OK: Deregister I2S port successfully (i.e. that I2S port can used used by other users after this function returns) * - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id * - ESP_ERR_INVALID_STATE: Specific I2S port is free already */ -esp_err_t i2s_platform_release_occupation(int id); +esp_err_t i2s_platform_release_occupation(i2s_ctlr_t type, int id); /** * @brief This function is only used for getting DMA buffer offset in `test_i2s_iram.c` diff --git a/components/esp_driver_i2s/lp_i2s.c b/components/esp_driver_i2s/lp_i2s.c new file mode 100644 index 0000000000..0e4e0329cd --- /dev/null +++ b/components/esp_driver_i2s/lp_i2s.c @@ -0,0 +1,331 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#if CONFIG_I2S_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "esp_log.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_clk_tree.h" +#include "hal/hal_utils.h" +#include "hal/lp_i2s_hal.h" +#include "hal/lp_i2s_ll.h" +#include "driver/i2s_types.h" +#include "driver/lp_i2s.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/i2s_platform.h" +#include "i2s_private.h" +#include "soc/i2s_periph.h" + +#define LP_I2S_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) + +static const char *TAG = "LP_I2S"; + +static esp_err_t s_i2s_register_channel(lp_i2s_controller_t *ctlr, i2s_dir_t dir); +static inline bool s_lp_i2s_take_available_channel(lp_i2s_controller_t *ctlr, uint8_t chan_search_mask); +static void s_i2s_default_isr(void *arg); + +esp_err_t lp_i2s_new_channel(const lp_i2s_chan_config_t *chan_cfg, lp_i2s_chan_handle_t *ret_tx_handle, lp_i2s_chan_handle_t *ret_rx_handle) +{ +#if CONFIG_I2S_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + ESP_RETURN_ON_FALSE(chan_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(chan_cfg->id < SOC_LP_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid LP I2S port id"); + ESP_RETURN_ON_FALSE(chan_cfg->role == I2S_ROLE_SLAVE, ESP_ERR_INVALID_ARG, TAG, "invalid argument: LP I2S not support master"); +#if LP_I2S_LL_TX_SUPPORTED + ESP_RETURN_ON_FALSE(ret_tx_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); +#else + ESP_RETURN_ON_FALSE(!ret_tx_handle, ESP_ERR_INVALID_ARG, TAG, "tx not supported"); +#endif +#if LP_I2S_LL_RX_SUPPORTED + ESP_RETURN_ON_FALSE(ret_rx_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); +#else + ESP_RETURN_ON_FALSE(!ret_rx_handle, ESP_ERR_INVALID_ARG, TAG, "rx not supported"); +#endif + ESP_RETURN_ON_FALSE(chan_cfg->threshold % 4 == 0, ESP_ERR_INVALID_ARG, TAG, "threshold must be in multiple of 4"); + ESP_RETURN_ON_FALSE(chan_cfg->threshold <= LP_I2S_LL_RX_MEM_THRESH_BYTES_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid threshold"); + + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_ERROR(i2s_platform_acquire_occupation(I2S_CTLR_LP, chan_cfg->id, "lp_i2s_driver"), TAG, "failed to acquire lp i2s controller"); + + lp_i2s_controller_t *ctlr = heap_caps_calloc(1, sizeof(lp_i2s_controller_t), LP_I2S_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(ctlr, ESP_ERR_NO_MEM, TAG, "no mem"); + ctlr->id = chan_cfg->id; + + ESP_GOTO_ON_ERROR(esp_intr_alloc(lp_i2s_periph_signal[ctlr->id].irq, ESP_INTR_FLAG_IRAM, s_i2s_default_isr, ctlr, &ctlr->intr), err1, TAG, "allocate interrupt failed"); + + uint8_t chan_search_mask = 0; + chan_search_mask |= ret_tx_handle ? I2S_DIR_TX : 0; + chan_search_mask |= ret_rx_handle ? I2S_DIR_RX : 0; + + portENTER_CRITICAL(&g_i2s.spinlock); + g_i2s.lp_controller[chan_cfg->id] = ctlr; + portEXIT_CRITICAL(&g_i2s.spinlock); + + bool channel_found = s_lp_i2s_take_available_channel(ctlr, chan_search_mask); + ESP_GOTO_ON_FALSE(channel_found, ESP_ERR_NOT_FOUND, err1, TAG, "no available channel found"); + + if (ret_rx_handle) { + ESP_GOTO_ON_ERROR(s_i2s_register_channel(ctlr, I2S_DIR_RX), err1, TAG, "register lp i2s rx channel failed"); + ctlr->rx_chan->role = chan_cfg->role; + ctlr->rx_chan->threshold = chan_cfg->threshold; + *ret_rx_handle = ctlr->rx_chan; + ESP_LOGD(TAG, "rx channel is registered on LP_I2S%d successfully", ctlr->id); + + ctlr->rx_chan->semphr = xSemaphoreCreateBinaryWithCaps(LP_I2S_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(ctlr->rx_chan->semphr, ESP_ERR_NO_MEM, err0, TAG, "No memory for binary semaphore"); + } + + PERIPH_RCC_ATOMIC() { + lp_i2s_ll_enable_module_clock(chan_cfg->id, true); + lp_i2s_ll_reset_module_clock(chan_cfg->id); + if (chan_search_mask & I2S_DIR_RX) { + lp_i2s_ll_enable_rx_module_clock(chan_cfg->id, true); + } + } + lp_i2s_hal_init(&ctlr->hal, ctlr->id); + lp_i2s_ll_enable_mem(chan_cfg->id, true); + lp_i2s_ll_clk_gate_en(ctlr->hal.dev, true); + lp_i2s_ll_rx_mem_clk_gate_en(ctlr->hal.dev, true); + lp_i2s_ll_rx_reg_clk_gate_en(ctlr->hal.dev, true); + lp_i2s_ll_rx_reset(ctlr->hal.dev); + lp_i2s_ll_rx_reset_fifo(ctlr->hal.dev); + lp_i2s_ll_rx_reset_fifo_mem(ctlr->hal.dev); + + if (ctlr->rx_chan->role == I2S_ROLE_SLAVE) { + lp_i2s_ll_set_rx_master_slave_mode(ctlr->hal.dev, true); + } + lp_i2s_ll_set_rx_stop_mode(ctlr->hal.dev, LP_I2S_LL_RX_STOP_MODE_START_CLEAR); + lp_i2s_ll_set_rx_mem_threshold(ctlr->hal.dev, chan_cfg->threshold / 4); + lp_i2s_ll_rx_clear_interrupt_status(ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT); + + return ESP_OK; + +err0: + if (ctlr->rx_chan) { + vSemaphoreDeleteWithCaps(ctlr->rx_chan->semphr); + free(ctlr->rx_chan); + ctlr->rx_chan = NULL; + } + +err1: + /* if the controller object has no channel, find the corresponding global object and destroy it */ + if (ctlr != NULL && ctlr->rx_chan == NULL) { + esp_intr_free(ctlr->intr); + i2s_platform_release_occupation(I2S_CTLR_LP, chan_cfg->id); + free(ctlr); + } + return ret; +} + +esp_err_t lp_i2s_channel_enable(lp_i2s_chan_handle_t chan) +{ + ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(chan->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel is in enabled state already"); + + lp_i2s_evt_data_t evt_data = {}; + if (chan->cbs.on_request_new_trans) { + chan->cbs.on_request_new_trans(chan, &evt_data, chan->user_data); + ESP_RETURN_ON_FALSE(evt_data.trans.buffer, ESP_ERR_INVALID_STATE, TAG, "no transaction buffer"); + ESP_RETURN_ON_FALSE(((evt_data.trans.buflen % LP_I2S_LL_RX_MEM_POP_BYTES) == 0), ESP_ERR_INVALID_STATE, TAG, "invalid transaction buflen, not aligned to %d", LP_I2S_LL_RX_MEM_POP_BYTES); + ESP_LOGD(TAG, "evt_data.trans.buffer: %p, evt_data.trans.buflen: %d", evt_data.trans.buffer, evt_data.trans.buflen); + chan->trans = evt_data.trans; + } + + chan->state = I2S_CHAN_STATE_RUNNING; + portENTER_CRITICAL(&g_i2s.spinlock); + lp_i2s_ll_rx_enable_interrupt(chan->ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT, true); + lp_i2s_ll_rx_start(chan->ctlr->hal.dev); + portEXIT_CRITICAL(&g_i2s.spinlock); + + return ESP_OK; +} + +esp_err_t lp_i2s_channel_read(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans, uint32_t timeout_ms) +{ + ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(chan->state == I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel isn't enabled"); + ESP_RETURN_ON_FALSE(!chan->cbs.on_request_new_trans, ESP_ERR_INVALID_STATE, TAG, "on_request_new_trans registered, no use of this read API"); + + TickType_t ticks_to_wait = timeout_ms / portTICK_PERIOD_MS; + if (timeout_ms == LP_I2S_MAX_DELAY) { + ticks_to_wait = portMAX_DELAY; + } + + BaseType_t r = xSemaphoreTake(chan->semphr, ticks_to_wait); + if (r != pdTRUE) { + ESP_LOGW(TAG, "lp_i2s read API, new data receiving timeout"); + return ESP_ERR_TIMEOUT; + } + + size_t len = MIN(lp_i2s_ll_get_rx_mem_fifo_cnt(chan->ctlr->hal.dev), trans->buflen); + lp_i2s_ll_read_buffer(chan->ctlr->hal.dev, trans->buffer, len); + trans->received_size = len; + portENTER_CRITICAL(&g_i2s.spinlock); + lp_i2s_ll_rx_clear_interrupt_status(chan->ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT); + lp_i2s_ll_rx_enable_interrupt(chan->ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT, true); + portEXIT_CRITICAL(&g_i2s.spinlock); + + return ESP_OK; +} + +esp_err_t lp_i2s_channel_read_until_bytes(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans) +{ + size_t received_size = 0; + size_t recv_len = trans->buflen; + lp_i2s_trans_t t = {}; + while (1) { + t.buffer = trans->buffer + received_size; + t.buflen = MIN(chan->threshold, recv_len); + ESP_RETURN_ON_ERROR(lp_i2s_channel_read(chan, &t, LP_I2S_MAX_DELAY), TAG, "failed to do lp i2s read"); + received_size += t.received_size; + recv_len -= t.received_size; + ESP_LOGD(TAG, "received_size: %d, recv_len: %d, t.received_size: %d", received_size, recv_len, t.received_size); + if (recv_len == 0) { + break; + } + } + trans->received_size = received_size; + + return ESP_OK; +} + +esp_err_t lp_i2s_channel_disable(lp_i2s_chan_handle_t chan) +{ + ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(chan->state > I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, TAG, "the channel is disabled already"); + chan->state = I2S_CHAN_STATE_READY; + + portENTER_CRITICAL(&g_i2s.spinlock); + lp_i2s_ll_rx_enable_interrupt(chan->ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT, false); + lp_i2s_ll_rx_stop(chan->ctlr->hal.dev); + portEXIT_CRITICAL(&g_i2s.spinlock); + + return ESP_OK; +} + +esp_err_t lp_i2s_del_channel(lp_i2s_chan_handle_t chan) +{ + ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(chan->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel can't be deleted unless it is disabled"); + + int id = chan->ctlr->id; + portENTER_CRITICAL(&g_i2s.spinlock); + if (chan->dir == I2S_DIR_RX) { + g_i2s.lp_controller[chan->ctlr->id]->rx_chan = NULL; + } + + g_i2s.lp_controller[id] = NULL; + portEXIT_CRITICAL(&g_i2s.spinlock); + + ESP_RETURN_ON_ERROR(esp_intr_free(chan->ctlr->intr), TAG, "failed to free intr"); + vSemaphoreDeleteWithCaps(chan->semphr); + ESP_RETURN_ON_ERROR(i2s_platform_release_occupation(I2S_CTLR_LP, id), TAG, "failed to release lp i2s controller"); + free(chan->ctlr); + free(chan); + + return ESP_OK; +} + +#ifndef __cplusplus +/* To make sure the i2s_event_callbacks_t is same size as i2s_event_callbacks_internal_t */ +_Static_assert(sizeof(lp_i2s_evt_cbs_t) == sizeof(lp_i2s_evt_cbs_internal_t), "Invalid size of lp_i2s_evt_cbs_t structure"); +#endif + +esp_err_t lp_i2s_register_event_callbacks(lp_i2s_chan_handle_t handle, const lp_i2s_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(handle && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(handle->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel is in enabled state already"); + + handle->cbs.on_thresh_met = cbs->on_thresh_met; + handle->cbs.on_request_new_trans = cbs->on_request_new_trans; + handle->user_data = user_data; + + return ESP_OK; +} + +static inline bool s_lp_i2s_take_available_channel(lp_i2s_controller_t *ctlr, uint8_t chan_search_mask) +{ + bool is_available = false; + + portENTER_CRITICAL(&g_i2s.spinlock); + if (!(chan_search_mask & ctlr->chan_occupancy)) { + ctlr->chan_occupancy |= chan_search_mask; + is_available = true; + } + portEXIT_CRITICAL(&g_i2s.spinlock); + return is_available; +} + +static esp_err_t s_i2s_register_channel(lp_i2s_controller_t *ctlr, i2s_dir_t dir) +{ + esp_err_t ret = ESP_OK; + + lp_i2s_chan_handle_t new_chan = (lp_i2s_chan_handle_t)heap_caps_calloc(1, sizeof(struct lp_i2s_channel_obj_t), LP_I2S_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(new_chan, ESP_ERR_NO_MEM, TAG, "No memory for new channel"); + new_chan->mode = I2S_COMM_MODE_NONE; + new_chan->role = I2S_ROLE_MASTER; + new_chan->dir = dir; + new_chan->state = I2S_CHAN_STATE_REGISTER; + new_chan->ctlr = ctlr; + + if (dir == I2S_DIR_RX) { + ctlr->rx_chan = new_chan; + } + + return ret; +} + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +static void IRAM_ATTR s_i2s_default_isr(void *arg) +{ + lp_i2s_controller_t *ctlr = (lp_i2s_controller_t *)arg; + bool need_yield = false; + BaseType_t high_task_woken = pdFALSE; + ESP_DRAM_LOGD(TAG, "in isr, rx_mem_fifo_cnt: %d bytes", lp_i2s_ll_get_rx_mem_fifo_cnt(ctlr->hal.dev)); + + if (ctlr->rx_chan->cbs.on_request_new_trans) { + size_t len = MIN(lp_i2s_ll_get_rx_mem_fifo_cnt(ctlr->hal.dev), ctlr->rx_chan->trans.buflen); + ESP_DRAM_LOGD(TAG, "len: %d", len); + lp_i2s_ll_read_buffer(ctlr->hal.dev, ctlr->rx_chan->trans.buffer, len); + lp_i2s_ll_rx_clear_interrupt_status(ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT); + ctlr->rx_chan->trans.received_size = len; + + lp_i2s_evt_data_t edata = { + .trans = ctlr->rx_chan->trans, + }; + if (ctlr->rx_chan->cbs.on_thresh_met) { + need_yield |= ctlr->rx_chan->cbs.on_thresh_met(ctlr->rx_chan, &edata, ctlr->rx_chan->user_data); + } + + assert(ctlr->rx_chan->cbs.on_request_new_trans); + lp_i2s_evt_data_t new_edata = {}; + ctlr->rx_chan->cbs.on_request_new_trans(ctlr->rx_chan, &new_edata, ctlr->rx_chan->user_data); + memcpy(&ctlr->rx_chan->trans, &new_edata.trans, sizeof(lp_i2s_trans_t)); + + } else { + portENTER_CRITICAL_ISR(&g_i2s.spinlock); + lp_i2s_ll_rx_enable_interrupt(ctlr->hal.dev, LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT, false); + portEXIT_CRITICAL_ISR(&g_i2s.spinlock); + xSemaphoreGiveFromISR(ctlr->rx_chan->semphr, &high_task_woken); + } + + need_yield |= high_task_woken == pdTRUE; + if (need_yield) { + portYIELD_FROM_ISR(); + } +} diff --git a/components/esp_driver_i2s/lp_i2s_pdm.c b/components/esp_driver_i2s/lp_i2s_pdm.c new file mode 100644 index 0000000000..31d7c309aa --- /dev/null +++ b/components/esp_driver_i2s/lp_i2s_pdm.c @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "hal/lp_i2s_hal.h" +#include "hal/lp_i2s_ll.h" +#include "driver/i2s_types.h" +#include "driver/lp_i2s.h" +#include "driver/lp_i2s_pdm.h" +#include "driver/lp_io.h" +#include "driver/rtc_io.h" +#include "esp_private/periph_ctrl.h" +#include "i2s_private.h" + +static const char *TAG = "LP_I2S_PDM"; +static esp_err_t s_io_init(lp_i2s_chan_handle_t chan, const lp_i2s_pdm_rx_gpio_config_t *config); + +static void s_get_cut_off_coef(uint32_t freq, uint32_t *param0, uint32_t *param5) +{ + uint32_t cut_off_coef[LP_I2S_LL_CUT_OFF_COEF_X_NUM][LP_I2S_LL_CUT_OFF_COEF_Y_NUM] = LP_I2S_LL_CUT_OFF_COEF; + uint8_t cnt = 0; + uint32_t min = 10000; + /* Find the closest cut-off frequency and its coefficients */ + for (int i = 0; i < LP_I2S_LL_CUT_OFF_COEF_X_NUM; i++) { + uint32_t tmp = cut_off_coef[i][0] < freq ? freq - cut_off_coef[i][0] : cut_off_coef[i][0] - freq; + if (tmp < min) { + min = tmp; + cnt = i; + } + } + *param0 = (uint32_t)cut_off_coef[cnt][1]; + *param5 = (uint32_t)cut_off_coef[cnt][2]; +} + +esp_err_t lp_i2s_channel_init_pdm_rx_mode(lp_i2s_chan_handle_t handle, const lp_i2s_pdm_rx_config_t *pdm_cfg) +{ + ESP_RETURN_ON_FALSE(handle && pdm_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(handle->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel is in enabled state already"); + + //Pin config + ESP_RETURN_ON_ERROR(s_io_init(handle, &pdm_cfg->pin_cfg), TAG, "failed to init IOs"); + + //Clock config + if (handle->role == I2S_ROLE_SLAVE) { + PERIPH_RCC_ATOMIC() { + lp_i2s_ll_select_rx_clk_source(handle->ctlr->id, LP_I2S_CLK_SRC_XTAL_D2); + } + lp_i2s_ll_clk_source_div_num(handle->ctlr->id, 2); + lp_i2s_ll_rx_set_raw_clk_div(handle->ctlr->id, 0, 0); + lp_i2s_ll_rx_set_bck_div_num(handle->ctlr->hal.dev, 1); + } + // TODO: make this divisions configurable when support master mode. + + //Slot config + uint32_t slot_bit_width = (int)pdm_cfg->slot_cfg.slot_bit_width < (int)pdm_cfg->slot_cfg.data_bit_width ? + pdm_cfg->slot_cfg.data_bit_width : pdm_cfg->slot_cfg.slot_bit_width; + + lp_i2s_ll_rx_set_sample_bit(handle->ctlr->hal.dev, slot_bit_width, pdm_cfg->slot_cfg.data_bit_width); + lp_i2s_ll_rx_enable_mono_mode(handle->ctlr->hal.dev, false); + + uint32_t slot_mask = pdm_cfg->slot_cfg.slot_mode == I2S_SLOT_MODE_STEREO ? I2S_PDM_SLOT_BOTH : pdm_cfg->slot_cfg.slot_mask; + lp_i2s_ll_rx_set_active_chan(handle->ctlr->hal.dev, slot_mask); + lp_i2s_ll_rx_set_half_sample_bits(handle->ctlr->hal.dev, 16); + lp_i2s_ll_rx_set_ws_pol(handle->ctlr->hal.dev, pdm_cfg->slot_cfg.ws_pol); + + uint32_t param0 = 0; + uint32_t param5 = 0; + s_get_cut_off_coef(pdm_cfg->slot_cfg.hp_cut_off_freq_hz, ¶m0, ¶m5); + lp_i2s_ll_rx_enable_pdm_hp_filter(handle->ctlr->hal.dev, pdm_cfg->slot_cfg.hp_en); + lp_i2s_ll_rx_set_pdm_hp_filter_param0(handle->ctlr->hal.dev, param0); + lp_i2s_ll_rx_set_pdm_hp_filter_param5(handle->ctlr->hal.dev, param5); + /* Set the amplification number, the default and the minimum value is 1. 0 will mute the channel */ + lp_i2s_ll_rx_set_pdm_amplify_num(handle->ctlr->hal.dev, pdm_cfg->slot_cfg.amplify_num ? pdm_cfg->slot_cfg.amplify_num : 1); + + lp_i2s_ll_rx_enable_pdm(handle->ctlr->hal.dev); + lp_i2s_ll_rx_set_pdm2pcm_en(handle->ctlr->hal.dev, true); + + return ESP_OK; +} + +/*--------------------------------------------------------------- + IO +---------------------------------------------------------------*/ +static esp_err_t s_io_init(lp_i2s_chan_handle_t chan, const lp_i2s_pdm_rx_gpio_config_t *config) +{ + if (config->clk >= 0) { + rtc_gpio_set_direction(config->clk, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(config->clk); + rtc_gpio_iomux_func_sel(config->clk, 0); + if (chan->role == I2S_ROLE_MASTER) { + lp_gpio_connect_in_signal(config->clk, lp_i2s_periph_signal[chan->ctlr->id].m_rx_ws_sig, 0); + } else { + lp_gpio_connect_in_signal(config->clk, lp_i2s_periph_signal[chan->ctlr->id].s_rx_ws_sig, 0); + } + ESP_LOGD(TAG, "clk io: %d, role: %d, signal: %"PRId8, config->clk, chan->role, chan->role ? lp_i2s_periph_signal[chan->ctlr->id].s_rx_ws_sig : lp_i2s_periph_signal[chan->ctlr->id].m_rx_ws_sig); + } + + if (config->din >= 0) { + rtc_gpio_set_direction(config->din, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(config->din); + rtc_gpio_iomux_func_sel(config->din, 0); + lp_gpio_connect_in_signal(config->din, lp_i2s_periph_signal[chan->ctlr->id].data_in_sigs[0], 0); + ESP_LOGD(TAG, "din io: %d, signal: %"PRId8, config->din, lp_i2s_periph_signal[chan->ctlr->id].data_in_sigs[0]); + } + + return ESP_OK; +} diff --git a/components/esp_driver_i2s/lp_i2s_std.c b/components/esp_driver_i2s/lp_i2s_std.c new file mode 100644 index 0000000000..a0a71c486e --- /dev/null +++ b/components/esp_driver_i2s/lp_i2s_std.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "hal/lp_i2s_hal.h" +#include "hal/lp_i2s_ll.h" +#include "driver/i2s_types.h" +#include "driver/lp_i2s.h" +#include "driver/lp_i2s_std.h" +#include "driver/lp_io.h" +#include "driver/rtc_io.h" +#include "esp_private/periph_ctrl.h" +#include "i2s_private.h" + +static const char *TAG = "LP_I2S_STD"; +static esp_err_t s_io_init(lp_i2s_chan_handle_t chan, const lp_i2s_std_gpio_config_t *config); + +esp_err_t lp_i2s_channel_init_std_mode(lp_i2s_chan_handle_t handle, const lp_i2s_std_config_t *std_cfg) +{ + ESP_RETURN_ON_FALSE(handle && std_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(std_cfg->slot_cfg.data_bit_width <= LP_I2S_LL_TDM_MAX_DATA_BIT_WIDTH && std_cfg->slot_cfg.slot_bit_width <= LP_I2S_LL_TDM_MAX_CHAN_BIT_WIDTH, ESP_ERR_INVALID_ARG, TAG, "invalid argument: wrong data bit width or slot bit width"); + ESP_RETURN_ON_FALSE(handle->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, TAG, "the channel is in enabled state already"); + + //Pin config + ESP_RETURN_ON_ERROR(s_io_init(handle, &std_cfg->pin_cfg), TAG, "failed to init IOs"); + + //Clock config + if (handle->role == I2S_ROLE_SLAVE) { + PERIPH_RCC_ATOMIC() { + lp_i2s_ll_select_rx_clk_source(handle->ctlr->id, LP_I2S_CLK_SRC_XTAL_D2); + } + lp_i2s_ll_clk_source_div_num(handle->ctlr->id, 2); + lp_i2s_ll_rx_set_raw_clk_div(handle->ctlr->id, 0, 0); + lp_i2s_ll_rx_set_bck_div_num(handle->ctlr->hal.dev, 1); + } + // TODO: make this divisions configurable when support master mode. + + //Slot config + uint32_t slot_bit_width = (int)std_cfg->slot_cfg.slot_bit_width < (int)std_cfg->slot_cfg.data_bit_width ? + std_cfg->slot_cfg.data_bit_width : std_cfg->slot_cfg.slot_bit_width; + + lp_i2s_ll_rx_set_sample_bit(handle->ctlr->hal.dev, slot_bit_width, std_cfg->slot_cfg.data_bit_width); + lp_i2s_ll_rx_enable_mono_mode(handle->ctlr->hal.dev, std_cfg->slot_cfg.slot_mode == I2S_SLOT_MODE_MONO); + lp_i2s_ll_rx_enable_msb_shift(handle->ctlr->hal.dev, std_cfg->slot_cfg.bit_shift); + lp_i2s_ll_rx_set_ws_width(handle->ctlr->hal.dev, std_cfg->slot_cfg.ws_width); + lp_i2s_ll_rx_select_std_chan(handle->ctlr->hal.dev, (uint32_t)std_cfg->slot_cfg.slot_mask); + lp_i2s_ll_rx_set_half_sample_bits(handle->ctlr->hal.dev, slot_bit_width); + lp_i2s_ll_rx_set_ws_pol(handle->ctlr->hal.dev, std_cfg->slot_cfg.ws_pol); + lp_i2s_ll_rx_set_bit_order(handle->ctlr->hal.dev, std_cfg->slot_cfg.bit_order_lsb); + lp_i2s_ll_rx_set_alignment_mode(handle->ctlr->hal.dev, std_cfg->slot_cfg.left_align); + lp_i2s_ll_rx_set_endian(handle->ctlr->hal.dev, std_cfg->slot_cfg.big_endian); + lp_i2s_ll_rx_enable_tdm(handle->ctlr->hal.dev); + + return ESP_OK; +} + +/*--------------------------------------------------------------- + IO +---------------------------------------------------------------*/ +static esp_err_t s_io_init(lp_i2s_chan_handle_t chan, const lp_i2s_std_gpio_config_t *config) +{ + if (config->bck >= 0) { + rtc_gpio_set_direction(config->bck, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(config->bck); + rtc_gpio_iomux_func_sel(config->bck, 0); + if (chan->role == I2S_ROLE_MASTER) { + lp_gpio_connect_in_signal(config->bck, lp_i2s_periph_signal[chan->ctlr->id].m_rx_bck_sig, 0); + } else { + lp_gpio_connect_in_signal(config->bck, lp_i2s_periph_signal[chan->ctlr->id].s_rx_bck_sig, 0); + } + ESP_LOGD(TAG, "bck io: %d, role: %d, signal: %"PRId8, config->bck, chan->role, chan->role ? lp_i2s_periph_signal[chan->ctlr->id].s_rx_bck_sig : lp_i2s_periph_signal[chan->ctlr->id].m_rx_bck_sig); + } + + if (config->ws >= 0) { + rtc_gpio_set_direction(config->ws, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(config->ws); + rtc_gpio_iomux_func_sel(config->ws, 0); + if (chan->role == I2S_ROLE_MASTER) { + lp_gpio_connect_in_signal(config->ws, lp_i2s_periph_signal[chan->ctlr->id].m_rx_ws_sig, 0); + } else { + lp_gpio_connect_in_signal(config->ws, lp_i2s_periph_signal[chan->ctlr->id].s_rx_ws_sig, 0); + } + ESP_LOGD(TAG, "ws io: %d, role: %d, signal: %"PRId8, config->ws, chan->role, chan->role ? lp_i2s_periph_signal[chan->ctlr->id].s_rx_ws_sig : lp_i2s_periph_signal[chan->ctlr->id].m_rx_ws_sig); + } + + if (config->din >= 0) { + rtc_gpio_set_direction(config->din, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(config->din); + rtc_gpio_iomux_func_sel(config->din, 0); + lp_gpio_connect_in_signal(config->din, lp_i2s_periph_signal[chan->ctlr->id].data_in_sigs[0], 0); + ESP_LOGD(TAG, "din io: %d, signal: %"PRId8, config->din, lp_i2s_periph_signal[chan->ctlr->id].data_in_sigs[0]); + } + + return ESP_OK; +} diff --git a/components/esp_driver_i2s/test_apps/.build-test-rules.yml b/components/esp_driver_i2s/test_apps/.build-test-rules.yml index cf1abd358c..7ba0e4b61b 100644 --- a/components/esp_driver_i2s/test_apps/.build-test-rules.yml +++ b/components/esp_driver_i2s/test_apps/.build-test-rules.yml @@ -21,3 +21,9 @@ components/esp_driver_i2s/test_apps/i2s_multi_dev: reason: lack of runners depends_components: - esp_driver_i2s + +components/esp_driver_i2s/test_apps/lp_i2s: + disable: + - if: SOC_LP_I2S_SUPPORTED != 1 + depends_components: + - esp_driver_i2s diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c index 3889b74703..68a4514db6 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c @@ -167,7 +167,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc // LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform int bus_id = -1; for (int i = 0; i < SOC_LCD_I80_BUSES; i++) { - if (i2s_platform_acquire_occupation(i, "esp_lcd_panel_io_i2s") == ESP_OK) { + if (i2s_platform_acquire_occupation(I2S_CTLR_HP, i, "esp_lcd_panel_io_i2s") == ESP_OK) { bus_id = i; break; } @@ -231,7 +231,7 @@ err: esp_intr_free(bus->intr); } if (bus->bus_id >= 0) { - i2s_platform_release_occupation(bus->bus_id); + i2s_platform_release_occupation(I2S_CTLR_HP, bus->bus_id); } if (bus->format_buffer) { free(bus->format_buffer); @@ -250,7 +250,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus) ESP_GOTO_ON_FALSE(bus, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty"); int bus_id = bus->bus_id; - i2s_platform_release_occupation(bus_id); + i2s_platform_release_occupation(I2S_CTLR_HP, bus_id); esp_intr_free(bus->intr); if (bus->pm_lock) { esp_pm_lock_delete(bus->pm_lock); diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 0e9e12a6fc..35ca479f78 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -92,6 +92,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "isp_hal.c") endif() + if(CONFIG_SOC_LP_I2S_SUPPORTED) + list(APPEND srcs "lp_i2s_hal.c") + endif() + if(CONFIG_SOC_RMT_SUPPORTED) list(APPEND srcs "rmt_hal.c") endif() diff --git a/components/hal/esp32p4/include/hal/lp_i2s_ll.h b/components/hal/esp32p4/include/hal/lp_i2s_ll.h index c25e3c57df..a2e509db45 100644 --- a/components/hal/esp32p4/include/hal/lp_i2s_ll.h +++ b/components/hal/esp32p4/include/hal/lp_i2s_ll.h @@ -16,8 +16,12 @@ #include #include "hal/misc.h" #include "hal/assert.h" +#include "hal/i2s_types.h" #include "soc/lp_i2s_struct.h" #include "soc/lpperi_struct.h" +#include "soc/lp_system_struct.h" +#include "soc/clk_tree_defs.h" +#include "soc/reg_base.h" #ifdef __cplusplus extern "C" { @@ -29,9 +33,28 @@ extern "C" { #define LP_I2S_LL_EVENT_RX_HUNG_INT_INT (1<<1) #define LP_I2S_LL_EVENT_RX_FIFOMEM_UDF_INT (1<<2) #define LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT (1<<5) - #define LP_I2S_LL_TDM_CH_MASK (0x03UL) +#define LP_I2S_LL_RX_SUPPORTED 1 + +#define LP_I2S_LL_TDM_MAX_DATA_BIT_WIDTH 16 +#define LP_I2S_LL_TDM_MAX_CHAN_BIT_WIDTH 16 +#define LP_I2S_LL_RX_MEM_THRESH_BYTES_MAX 1020 +#define LP_I2S_LL_RX_MEM_POP_BYTES 4 + +#define LP_I2S_LL_CUT_OFF_COEF_X_NUM 21 +#define LP_I2S_LL_CUT_OFF_COEF_Y_NUM 3 +/* PDM high pass filter cut-off frequency and coefficients list + * [0]: cut-off frequency * 10; [1]: param0; [2]: param5 + * NOTE: the cut-off frequency was timed 10 to use integer type */ +#define LP_I2S_LL_CUT_OFF_COEF {{1850, 0, 0}, {1720, 0, 1}, {1600, 1, 1}, \ + {1500, 1, 2}, {1370, 2, 2}, {1260, 2, 3}, \ + {1200, 0, 3}, {1150, 3, 3}, {1060, 1, 7}, \ + {1040, 2, 4}, {920, 4, 4}, {915, 2, 7}, \ + {810, 4, 5}, {772, 3, 7}, {690, 5, 5}, \ + {630, 4, 7}, {580, 5, 6}, {490, 5, 7}, \ + {460, 6, 6}, {355, 6, 7}, {233, 7, 7}} + /** * @brief LP I2S rx stop mode enum type */ @@ -72,6 +95,26 @@ typedef enum { LP_I2S_LL_RX_BIG_ENDIAN, } lp_i2s_ll_rx_endian_t; + +/*--------------------------------------------------------------- + Mem +---------------------------------------------------------------*/ +/** + * @brief Enable the internal memory for LP I2S module + * + * @param id Instance id + * @param en enable / disable + */ +static inline void lp_i2s_ll_enable_mem(int id, bool en) +{ + LP_SYS.lp_mem_aux_ctrl.lp_mem_aux_ctrl = !en; +} + +static inline void lp_i2s_ll_set_rx_mem_threshold(lp_i2s_dev_t *hw, uint32_t words) +{ + LP_I2S.rx_mem_conf.rx_mem_threshold = words; +} + /*--------------------------------------------------------------- Clock ---------------------------------------------------------------*/ @@ -120,6 +163,33 @@ static inline void lp_i2s_ll_enable_rx_module_clock(int id, bool en) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define lp_i2s_ll_enable_rx_module_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; lp_i2s_ll_enable_rx_module_clock(__VA_ARGS__) +/** + * @brief Select ISP clock source + * + * @param id Instance id + * @param clk_src clock source, see valid sources in type `soc_periph_lp_i2s_clk_src_t` + */ +static inline void lp_i2s_ll_select_rx_clk_source(int id, soc_periph_lp_i2s_clk_src_t clk_src) +{ + uint8_t clk_val = 0; + switch (clk_src) { + case LP_I2S_CLK_SRC_LP_PERI: + clk_val = 0; + break; + case LP_I2S_CLK_SRC_XTAL_D2: + clk_val = 1; + break; + default: + HAL_ASSERT(false); + break; + } + LPPERI.core_clk_sel.lp_i2s_rx_clk_sel = clk_val; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define lp_i2s_ll_select_rx_clk_source(...) (void)__DECLARE_RCC_ATOMIC_ENV; lp_i2s_ll_select_rx_clk_source(__VA_ARGS__) + /** * @brief Set LP I2S clock source div num * @@ -146,7 +216,7 @@ static inline void lp_i2s_ll_clk_source_div_num(int id, uint32_t val) * @param a div a * @param b div b */ -static inline void lp_i2s_ll_tx_set_raw_clk_div(int id, uint32_t a, uint32_t b) +static inline void lp_i2s_ll_rx_set_raw_clk_div(int id, uint32_t a, uint32_t b) { if (b <= a / 2) { LPPERI.lp_i2s_rxclk_div_xyz.lp_i2s_rx_clkm_div_yn1 = 0; @@ -170,7 +240,7 @@ static inline void lp_i2s_ll_tx_set_raw_clk_div(int id, uint32_t a, uint32_t b) /** * @brief Set LP I2S rx bck div num * - * @param id Instance id + * @param hw LP I2S hardware instance * @param val value to set rx bck div num */ static inline void lp_i2s_ll_rx_set_bck_div_num(lp_i2s_dev_t *hw, uint32_t val) @@ -178,13 +248,46 @@ static inline void lp_i2s_ll_rx_set_bck_div_num(lp_i2s_dev_t *hw, uint32_t val) hw->rx_conf1.rx_bck_div_num = val - 1; } +/** + * @brief Enable/Disable clock gate + * + * @param hw LP I2S hardware instance + * @param enable enable or disable + */ +static inline void lp_i2s_ll_clk_gate_en(lp_i2s_dev_t *hw, bool enable) +{ + hw->clk_gate.clk_en = enable; +} + +/** + * @brief Enable/Disable rx mem clock gate + * + * @param hw LP I2S hardware instance + * @param enable enable or disable + */ +static inline void lp_i2s_ll_rx_mem_clk_gate_en(lp_i2s_dev_t *hw, bool enable) +{ + hw->clk_gate.rx_mem_cg_force_on = enable; +} + +/** + * @brief Enable/Disable rx reg clock gate + * + * @param hw LP I2S hardware instance + * @param enable enable or disable + */ +static inline void lp_i2s_ll_rx_reg_clk_gate_en(lp_i2s_dev_t *hw, bool enable) +{ + hw->clk_gate.rx_reg_cg_force_on = enable; +} + /*--------------------------------------------------------------- Reset ---------------------------------------------------------------*/ /** * @brief Reset LP I2S RX module * - * @param id Instance id + * @param hw LP I2S hardware instance */ static inline void lp_i2s_ll_rx_reset(lp_i2s_dev_t *hw) { @@ -195,7 +298,7 @@ static inline void lp_i2s_ll_rx_reset(lp_i2s_dev_t *hw) /** * @brief Reset LP I2S RX FIFO * - * @param id Instance id + * @param hw LP I2S hardware instance */ static inline void lp_i2s_ll_rx_reset_fifo(lp_i2s_dev_t *hw) { @@ -203,13 +306,24 @@ static inline void lp_i2s_ll_rx_reset_fifo(lp_i2s_dev_t *hw) hw->rx_conf.rx_fifo_reset = 0; } +/** + * @brief Reset LP I2S RX FIFO mem + * + * @param hw LP I2S hardware instance + */ +static inline void lp_i2s_ll_rx_reset_fifo_mem(lp_i2s_dev_t *hw) +{ + hw->rx_conf.rx_fifomem_reset = 1; + hw->rx_conf.rx_fifomem_reset = 0; +} + /*--------------------------------------------------------------- Master/Slave ---------------------------------------------------------------*/ /** * @brief Start LP I2S RX * - * @param id Instance id + * @param hw LP I2S hardware instance */ static inline void lp_i2s_ll_rx_start(lp_i2s_dev_t *hw) { @@ -221,7 +335,7 @@ static inline void lp_i2s_ll_rx_start(lp_i2s_dev_t *hw) /** * @brief Set LP I2S RX stop mode * - * @param id Instance id + * @param hw LP I2S hardware instance */ static inline void lp_i2s_ll_set_rx_stop_mode(lp_i2s_dev_t *hw, lp_i2s_ll_rx_stop_mode_t rx_stop_mode) { @@ -231,7 +345,7 @@ static inline void lp_i2s_ll_set_rx_stop_mode(lp_i2s_dev_t *hw, lp_i2s_ll_rx_sto /** * @brief Configure the received length to trigger in_suc_eof interrupt * - * @param id Instance id + * @param hw LP I2S hardware instance * @param eof_num the byte length to trigger in_suc_eof interrupt */ static inline void lp_i2s_ll_rx_set_eof_num(lp_i2s_dev_t *hw, int eof_num) @@ -242,13 +356,24 @@ static inline void lp_i2s_ll_rx_set_eof_num(lp_i2s_dev_t *hw, int eof_num) /** * @brief Stop LP I2S RX * - * @param id Instance id + * @param hw LP I2S hardware instance */ static inline void lp_i2s_ll_rx_stop(lp_i2s_dev_t *hw) { hw->rx_conf.rx_start = 0; } +/** + * @brief Stop LP I2S RX + * + * @param hw LP I2S hardware instance + * @param is_slave Is slave or master + */ +static inline void lp_i2s_ll_set_rx_master_slave_mode(lp_i2s_dev_t *hw, bool is_slave) +{ + hw->rx_conf.rx_slave_mod = is_slave; +} + /*--------------------------------------------------------------- Receive ---------------------------------------------------------------*/ @@ -291,11 +416,64 @@ static inline void lp_i2s_ll_rx_enable_pdm(lp_i2s_dev_t *hw) hw->rx_conf.rx_tdm_en = 0; } +/** + * @brief Enable LP I2S RX PDM high pass filter + * + * @param hw LP I2S hardware instance + * @param enable Set true to enable I2S RX PDM high pass filter, set false to bypass it + */ +static inline void lp_i2s_ll_rx_enable_pdm_hp_filter(lp_i2s_dev_t *hw, bool enable) +{ + hw->rx_pdm_conf.rx_pdm_hp_bypass = !enable; +} + +/** + * @brief Set LP_I2S RX PDM high pass filter param0 + * + * @param hw Peripheral LP_I2S hardware instance address. + * @param param The fourth parameter of PDM RX IIR_HP filter stage 1 is (504 + LP_I2S_RX_IIR_HP_MULT12_0[2:0]) + */ +static inline void lp_i2s_ll_rx_set_pdm_hp_filter_param0(lp_i2s_dev_t *hw, uint32_t param) +{ + hw->rx_pdm_conf.rx_iir_hp_mult12_0 = param; +} + +/** + * @brief Set LP I2S RX PDM high pass filter param5 + * + * @param hw Peripheral LP_I2S hardware instance address. + * @param param The fourth parameter of PDM RX IIR_HP filter stage 2 is (504 + LP_I2S_RX_IIR_HP_MULT12_5[2:0]) + */ +static inline void lp_i2s_ll_rx_set_pdm_hp_filter_param5(lp_i2s_dev_t *hw, uint32_t param) +{ + hw->rx_pdm_conf.rx_iir_hp_mult12_5 = param; +} + +/** + * @brief Configure RX PDM amplify number + * @note This is the amplification number of the digital amplifier, + * which is added after the PDM to PCM conversion result and mainly used for + * amplify the small PDM signal under the VAD scenario + * pcm_result = pdm_input * amplify_num + * pcm_result = 0 if amplify_num = 0 + * + * @param hw Peripheral LP_I2S hardware instance address. + * @param amp_num PDM RX amplify number + */ +static inline void lp_i2s_ll_rx_set_pdm_amplify_num(lp_i2s_dev_t *hw, uint32_t amp_num) +{ + hw->rx_pdm_conf.rx_pdm2pcm_amplify_num = amp_num; +} + /*--------------------------------------------------------------- General ---------------------------------------------------------------*/ /** * @brief Configure LP I2S rx channel bits and bits mode + * + * @param hw LP I2S hardware instance + * @param chan_bits Chan bits + * @param bits_mode Bits mode */ static inline void lp_i2s_ll_rx_set_sample_bit(lp_i2s_dev_t *hw, int chan_bits, int bits_mode) { @@ -351,6 +529,34 @@ static inline void lp_i2s_ll_rx_set_active_chan(lp_i2s_dev_t *hw, uint32_t chan_ hw->rx_tdm_ctrl.val = tdm_ctrl; } +/** + * @brief Set LP I2S rx std channels + * + * @param hw LP I2S hardware instance + * @param chan_mask select chan to receive data + */ +static inline void lp_i2s_ll_rx_select_std_chan(lp_i2s_dev_t *hw, i2s_std_slot_mask_t chan_mask) +{ + hw->rx_tdm_ctrl.rx_tdm_tot_chan_num = 1; + uint32_t mask = 0; + switch (chan_mask) + { + case I2S_STD_SLOT_LEFT: + mask = 0x01; + break; + case I2S_STD_SLOT_RIGHT: + mask = 0x02; + break; + case I2S_STD_SLOT_BOTH: + mask = 0x03; + break; + default: + HAL_ASSERT(false); + break; + } + lp_i2s_ll_rx_set_active_chan(hw, mask); +} + /** * @brief Set LP I2S rx pdm2pcm enable * @@ -406,6 +612,68 @@ static inline void lp_i2s_ll_rx_set_endian(lp_i2s_dev_t *hw, lp_i2s_ll_rx_endian hw->rx_conf.rx_big_endian = endian; } +/** + * @brief Enable RX mono mode + * + * @param hw LP I2S hardware instance + * @param mono_ena Set true to enable mono mde. + */ +static inline void lp_i2s_ll_rx_enable_mono_mode(lp_i2s_dev_t *hw, bool mono_ena) +{ + hw->rx_conf.rx_mono = mono_ena; + hw->rx_conf.rx_mono_fst_vld = mono_ena; +} + +/** + * @brief Enable RX MSB shift, the data will be launch at the first BCK clock + * + * @param hw LP I2S hardware instance + * @param msb_shift_enable Set true to enable MSB shift + */ +static inline void lp_i2s_ll_rx_enable_msb_shift(lp_i2s_dev_t *hw, bool msb_shift_enable) +{ + hw->rx_conf1.rx_msb_shift = msb_shift_enable; +} + +/** + * Read from LP I2S hardware data buffer. + * + * @param hw Beginning address of the peripheral registers. + * @param buffer_to_rcv Address of a buffer to read data from hardware data buffer + * @param byte_len Length to read, in bytes. + */ +static inline void lp_i2s_ll_read_buffer(lp_i2s_dev_t *hw, void *buffer_to_rcv, size_t byte_len) +{ + for (int i = 0; i < byte_len; i += 4) { + *(uint32_t *)(buffer_to_rcv + i) = *((uint32_t *)LP_I2S_RAM_BASE); + } +} + +/** + * Write from LP I2S hardware data buffer. + * + * @param hw Beginning address of the peripheral registers. + * @param buffer_to_rcv Address of a buffer to write data from hardware data buffer + * @param bitlbyte_lenen Length to write, in bytes. + */ +static inline void lp_i2s_ll_write_buffer(lp_i2s_dev_t *hw, const void *buffer_to_send, size_t byte_len) +{ + for (int i = 0; i < byte_len; i += 4) { + //Use memcpy to get around alignment issues for txdata + *((uint32_t *)LP_I2S_RAM_BASE) = *(uint32_t *)(buffer_to_send + i); + } +} + +/** + * Get RX mem fifo cnt, in bytes + * + * @param hw Beginning address of the peripheral registers. + */ +static inline uint32_t lp_i2s_ll_get_rx_mem_fifo_cnt(lp_i2s_dev_t *hw) +{ + return hw->rx_mem_conf.rx_mem_fifo_cnt * 4; +} + /*--------------------------------------------------------------- Interrupt ---------------------------------------------------------------*/ @@ -425,6 +693,18 @@ static inline uint32_t lp_i2s_ll_rx_get_interrupt_status(lp_i2s_dev_t *hw, bool } } +/** + * @brief Get interrupt status reg address + * + * @param[in] hw LP I2S hardware instance + * + * @return Interrupt status reg address + */ +static inline uint32_t lp_i2s_ll_get_intr_status_reg_addr(lp_i2s_dev_t *hw) +{ + return (uint32_t)&(hw->int_st); +} + /** * @brief Enable LP I2S RX channel interrupt * diff --git a/components/hal/include/hal/lp_i2s_hal.h b/components/hal/include/hal/lp_i2s_hal.h new file mode 100644 index 0000000000..e2f8f33552 --- /dev/null +++ b/components/hal/include/hal/lp_i2s_hal.h @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_LP_I2S_SUPPORTED +typedef struct lp_i2s_dev_t *lp_i2s_soc_handle_t; // LP I2S SOC layer handle +#else +typedef uint32_t *lp_i2s_soc_handle_t; // LP I2S SOC layer handle +#endif + +/** + * Context that should be maintained by both the driver and the HAL + */ +typedef struct { + lp_i2s_soc_handle_t dev; // LP I2S SOC layer handle +} lp_i2s_hal_context_t; + +/** + * @brief Init the LP I2S hal and set the LP I2S to the default configuration. + * @note This function should be called first before other hal layer function is called. + * + * @param hal Context of the HAL layer + * @param group_id LP I2S group ID + */ +void lp_i2s_hal_init(lp_i2s_hal_context_t *hal, int group_id); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/lp_i2s_hal.c b/components/hal/lp_i2s_hal.c new file mode 100644 index 0000000000..0e827128f5 --- /dev/null +++ b/components/hal/lp_i2s_hal.c @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The HAL layer for LP I2S + +#include "soc/soc.h" +#include "hal/lp_i2s_hal.h" +#include "hal/lp_i2s_ll.h" + +void lp_i2s_hal_init(lp_i2s_hal_context_t *hal, int group_id) +{ + hal->dev = LP_I2S_LL_GET_HW(group_id); +} diff --git a/components/soc/esp32p4/i2s_periph.c b/components/soc/esp32p4/i2s_periph.c index 39078caab4..a0208659f1 100644 --- a/components/soc/esp32p4/i2s_periph.c +++ b/components/soc/esp32p4/i2s_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/i2s_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/lp_gpio_sig_map.h" /* Bunch of constants for every I2S peripheral: GPIO signals, irqs, hw addr of registers etc @@ -84,3 +85,29 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .module = PERIPH_I2S2_MODULE, }, }; + +const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM] = { + [0] = { + .mck_out_sig = -1, + .mck_in_sig = -1, + + .m_tx_bck_sig = LP_I2S_O_BCK_PAD_OUT_IDX, + .m_rx_bck_sig = LP_I2S_I_BCK_PAD_OUT_IDX, + .m_tx_ws_sig = LP_I2S_O_WS_PAD_OUT_IDX, + .m_rx_ws_sig = LP_I2S_I_WS_PAD_OUT_IDX, + + .s_tx_bck_sig = LP_I2S_O_BCK_PAD_IN_IDX, + .s_rx_bck_sig = LP_I2S_I_BCK_PAD_IN_IDX, + .s_tx_ws_sig = LP_I2S_O_WS_PAD_IN_IDX, + .s_rx_ws_sig = LP_I2S_I_WS_PAD_IN_IDX, + + .data_out_sigs[0] = LP_I2S_O_SD_PAD_OUT_IDX, + .data_out_sigs[1] = -1, + .data_in_sigs[0] = LP_I2S_I_SD_PAD_IN_IDX, + .data_in_sigs[1] = -1, + .data_in_sigs[2] = -1, + .data_in_sigs[3] = -1, + + .irq = ETS_LP_I2S_INTR_SOURCE, + }, +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 1617c26026..2d79748472 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -243,6 +243,10 @@ config SOC_LP_I2C_SUPPORTED bool default y +config SOC_LP_I2S_SUPPORTED + bool + default y + config SOC_LP_SPI_SUPPORTED bool default y @@ -815,6 +819,10 @@ config SOC_I2S_TDM_FULL_DATA_WIDTH bool default y +config SOC_LP_I2S_NUM + int + default 1 + config SOC_ISP_BF_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/lp_i2s_struct.h b/components/soc/esp32p4/include/soc/lp_i2s_struct.h index d56710bc27..c68f2c09f7 100644 --- a/components/soc/esp32p4/include/soc/lp_i2s_struct.h +++ b/components/soc/esp32p4/include/soc/lp_i2s_struct.h @@ -889,7 +889,7 @@ typedef union { } lp_i2s_date_reg_t; -typedef struct { +typedef struct lp_i2s_dev_t { volatile lp_i2s_vad_conf_reg_t vad_conf; volatile lp_i2s_vad_result_reg_t vad_result; volatile lp_i2s_rx_mem_conf_reg_t rx_mem_conf; diff --git a/components/soc/esp32p4/include/soc/reg_base.h b/components/soc/esp32p4/include/soc/reg_base.h index 9a4b6fc065..0a0bd2c5c9 100644 --- a/components/soc/esp32p4/include/soc/reg_base.h +++ b/components/soc/esp32p4/include/soc/reg_base.h @@ -163,9 +163,11 @@ /** * @brief: Special memory address */ +#define LP_I2S_RAM_BASE 0x50125c00 #define MIPI_CSI_BRG_MEM_BASE 0x50104000 #define MIPI_DSI_BRG_MEM_BASE 0x50105000 + /** * This are module helper MACROs for quick module reference * including some module(renamed) address diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 852ce6e9ed..be4380a1ba 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -78,6 +78,7 @@ #define SOC_LP_GPIO_MATRIX_SUPPORTED 1 #define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_LP_I2C_SUPPORTED 1 +#define SOC_LP_I2S_SUPPORTED 1 #define SOC_LP_SPI_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 @@ -325,6 +326,9 @@ #define SOC_I2S_PDM_MAX_RX_LINES (4) // On I2S0 #define SOC_I2S_TDM_FULL_DATA_WIDTH (1) /*!< No limitation to data bit width when using multiple slots */ +/*-------------------------- LP_I2S CAPS -------------------------------------*/ +#define SOC_LP_I2S_NUM (1U) + /*-------------------------- ISP CAPS ----------------------------------------*/ #define SOC_ISP_BF_SUPPORTED 1 #define SOC_ISP_CCM_SUPPORTED 1 diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index 683576bf2b..cf2d483f48 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -56,6 +56,10 @@ typedef struct { extern const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM]; +#if SOC_LP_I2S_SUPPORTED +extern const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM]; +#endif + #endif // SOC_I2S_SUPPORTED #ifdef __cplusplus