diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 8659694466..06f4449d25 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -41,6 +41,9 @@ #include "esp_private/i2s_platform.h" #include "esp_private/esp_clk.h" +#if SOC_I2S_SUPPORT_SLEEP_RETENTION +#include "esp_private/sleep_retention.h" +#endif #include "driver/gpio.h" #include "esp_private/gpio.h" @@ -85,6 +88,34 @@ inline void *i2s_dma_calloc(i2s_chan_handle_t handle, size_t num, size_t size) Scope: This file only ----------------------------------------------------------------------------*/ +#if I2S_USE_RETENTION_LINK +static esp_err_t s_i2s_create_sleep_retention_link_cb(void *arg) +{ + i2s_controller_t *i2s_obj = (i2s_controller_t *)arg; + ESP_RETURN_ON_ERROR(sleep_retention_entries_create(i2s_reg_retention_info[i2s_obj->id].entry_array, + i2s_reg_retention_info[i2s_obj->id].array_size, + REGDMA_LINK_PRI_I2S, i2s_obj->slp_retention_mod), + TAG, "create retention link failed"); + return ESP_OK; +} + +static void s_i2s_create_retention_module(i2s_controller_t *i2s_obj) +{ + sleep_retention_module_t module = i2s_obj->slp_retention_mod; + + _lock_acquire(&i2s_obj->mutex); + if (i2s_obj->retention_link_created == false) { + if (sleep_retention_module_allocate(module) != ESP_OK) { + // even though the sleep retention module create failed, I2S driver should still work, so just warning here + ESP_LOGW(TAG, "create retention module failed, power domain can't turn off"); + } else { + i2s_obj->retention_link_created = true; + } + } + _lock_release(&i2s_obj->mutex); +} +#endif // I2S_USE_RETENTION_LINK + static void i2s_tx_channel_start(i2s_chan_handle_t handle) { i2s_hal_tx_reset(&(handle->controller->hal)); @@ -175,6 +206,14 @@ static esp_err_t i2s_destroy_controller_obj(i2s_controller_t **i2s_obj) #if SOC_I2S_HW_VERSION_1 i2s_ll_enable_dma((*i2s_obj)->hal.dev, false); #endif +#if I2S_USE_RETENTION_LINK + if ((*i2s_obj)->slp_retention_mod) { + if ((*i2s_obj)->retention_link_created) { + sleep_retention_module_free((*i2s_obj)->slp_retention_mod); + } + sleep_retention_module_deinit((*i2s_obj)->slp_retention_mod); + } +#endif // I2S_USE_RETENTION_LINK free(*i2s_obj); *i2s_obj = NULL; return i2s_platform_release_occupation(I2S_CTLR_HP, id); @@ -219,6 +258,25 @@ static i2s_controller_t *i2s_acquire_controller_obj(int id) adc_ll_digi_set_data_source(0); } #endif + +#if I2S_USE_RETENTION_LINK + sleep_retention_module_t module = i2s_reg_retention_info[id].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = s_i2s_create_sleep_retention_link_cb, + .arg = i2s_obj, + }, + }, + .depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM) + }; + if (sleep_retention_module_init(module, &init_param) == ESP_OK) { + i2s_obj->slp_retention_mod = module; + } else { + // even the sleep retention module init failed, I2S driver should still work, so just warning here + ESP_LOGW(TAG, "init sleep retention failed for I2S%d, power domain may be turned off during sleep", id); + } +#endif // I2S_USE_RETENTION_LINK } else { free(pre_alloc); portENTER_CRITICAL(&g_i2s.spinlock); @@ -879,6 +937,9 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * ESP_RETURN_ON_FALSE(chan_cfg->id < SOC_I2S_NUM || chan_cfg->id == I2S_NUM_AUTO, ESP_ERR_INVALID_ARG, TAG, "invalid I2S port id"); ESP_RETURN_ON_FALSE(chan_cfg->dma_desc_num >= 2, ESP_ERR_INVALID_ARG, TAG, "there should be at least 2 DMA buffers"); ESP_RETURN_ON_FALSE(chan_cfg->intr_priority >= 0 && chan_cfg->intr_priority <= 7, ESP_ERR_INVALID_ARG, TAG, "intr_priority should be within 0~7"); +#if !SOC_I2S_SUPPORT_SLEEP_RETENTION + ESP_RETURN_ON_FALSE(!chan_cfg->allow_pd, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#endif esp_err_t ret = ESP_OK; i2s_controller_t *i2s_obj = NULL; @@ -937,6 +998,11 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * if ((tx_handle != NULL) && (rx_handle != NULL)) { i2s_obj->full_duplex = true; } +#if I2S_USE_RETENTION_LINK + if (chan_cfg->allow_pd) { + s_i2s_create_retention_module(i2s_obj); + } +#endif return ESP_OK; /* i2s_obj allocated but register channel failed */ diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 40ff52fce5..bc2cc1f8b4 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -6,6 +6,7 @@ #pragma once +#include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/queue.h" @@ -25,6 +26,9 @@ #endif #include "esp_private/periph_ctrl.h" #include "esp_private/esp_gpio_reserve.h" +#if SOC_I2S_SUPPORT_SLEEP_RETENTION +#include "esp_private/sleep_retention.h" +#endif #include "esp_pm.h" #include "esp_err.h" #include "sdkconfig.h" @@ -56,6 +60,8 @@ extern "C" { #define I2S_RCC_ATOMIC() #endif +#define I2S_USE_RETENTION_LINK (SOC_I2S_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + #define I2S_NULL_POINTER_CHECK(tag, p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, tag, "input parameter '"#p"' is NULL") /** @@ -130,6 +136,11 @@ typedef struct { bool full_duplex; /*!< is full_duplex */ i2s_chan_handle_t tx_chan; /*!< tx channel handler */ i2s_chan_handle_t rx_chan; /*!< rx channel handler */ + _lock_t mutex; /*!< mutex for controller */ +#if SOC_I2S_SUPPORT_SLEEP_RETENTION + sleep_retention_module_t slp_retention_mod; /*!< Sleep retention module */ + bool retention_link_created; /*!< Whether the retention link is created */ +#endif int mclk; /*!< MCK out pin, shared by tx/rx*/ #if CONFIG_IDF_TARGET_ESP32 esp_clock_output_mapping_handle_t mclk_out_hdl; /*!< The handle of MCLK output signal */ diff --git a/components/esp_driver_i2s/include/driver/i2s_common.h b/components/esp_driver_i2s/include/driver/i2s_common.h index eaeb397a0d..fd7a249da3 100644 --- a/components/esp_driver_i2s/include/driver/i2s_common.h +++ b/components/esp_driver_i2s/include/driver/i2s_common.h @@ -26,6 +26,7 @@ extern "C" { .dma_frame_num = 240, \ .auto_clear_after_cb = false, \ .auto_clear_before_cb = false, \ + .allow_pd = false, \ .intr_priority = 0, \ } @@ -73,6 +74,10 @@ typedef struct { bool auto_clear_before_cb; /*!< Set to auto clear DMA TX buffer before `on_sent` callback, I2S will always send zero automatically if no data to send * So that user can access data in the callback that just finished to send. */ + bool allow_pd; /*!< Set to allow power down. When this flag set, the driver will backup/restore the I2S registers before/after entering/exist sleep mode. + * By this approach, the system can power off I2S's power domain. + * This can save power, but at the expense of more RAM being consumed. + */ int intr_priority; /*!< I2S interrupt priority, range [0, 7], if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */ } i2s_chan_config_t; 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 7ba0e4b61b..8ecfb0daeb 100644 --- a/components/esp_driver_i2s/test_apps/.build-test-rules.yml +++ b/components/esp_driver_i2s/test_apps/.build-test-rules.yml @@ -3,10 +3,6 @@ components/esp_driver_i2s/test_apps/i2s: disable: - if: SOC_I2S_SUPPORTED != 1 - disable_test: - - if: IDF_TARGET == "esp32c5" - temporary: true - reason: target test failed # TODO [ESP32C5] IDF-10343 depends_components: - esp_driver_i2s - esp_driver_pcnt diff --git a/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt b/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt index 34b5a6d39f..d90f51c9f4 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt +++ b/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt @@ -1,11 +1,12 @@ set(srcs "test_app_main.c" "test_i2s.c" - "test_i2s_iram.c") + "test_i2s_iram.c" + "test_i2s_sleep.c") if(CONFIG_SOC_I2S_SUPPORTS_ETM AND CONFIG_SOC_GPIO_SUPPORT_ETM) set(srcs ${srcs} "test_i2s_etm.c") endif() idf_component_register(SRCS ${srcs} - PRIV_REQUIRES unity esp_driver_pcnt spi_flash esp_driver_gpio esp_driver_i2s + PRIV_REQUIRES unity esp_driver_pcnt spi_flash esp_driver_gpio esp_driver_i2s esp_driver_uart WHOLE_ARCHIVE) diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c index bf1172c3c7..43b2c70526 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c @@ -94,7 +94,7 @@ static void i2s_test_io_config(int mode) } } -static void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan) +void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan) { #define I2S_SEND_BUF_LEN 100 #define I2S_RECV_BUF_LEN 10000 @@ -776,7 +776,7 @@ static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_c printf("[%"PRIu32" Hz] %d pulses, expected %d, err %d\n", test_freq[i], real_pulse, expt_pulse, real_pulse - expt_pulse); TEST_ESP_OK(i2s_channel_disable(rx_chan)); // Check if the error between real pulse number and expected pulse number is within 1% - TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse); + TEST_ASSERT_INT_WITHIN(expt_pulse * 0.02, expt_pulse, real_pulse); } TEST_ESP_OK(pcnt_del_channel(pcnt_chan)); TEST_ESP_OK(pcnt_unit_stop(pcnt_unit)); diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c new file mode 100644 index 0000000000..6b5b944f31 --- /dev/null +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_utils.h" +#include "driver/i2s_std.h" +#include "driver/uart.h" +#include "soc/i2s_struct.h" +#include "esp_sleep.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" +#include "../../test_inc/test_i2s.h" + +#define TEST_I2S_PD_SLEEP (SOC_I2S_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + +extern void i2s_read_write_test(i2s_chan_handle_t tx_chan, i2s_chan_handle_t rx_chan); + +static void s_test_i2s_enter_light_sleep(int sec, bool allow_power_down) +{ + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + printf("Entering light sleep for %d seconds\n", sec); +#if ESP_SLEEP_POWER_DOWN_CPU + printf("Enable CPU power down\n"); + TEST_ESP_OK(sleep_cpu_configure(true)); +#endif + uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM); + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(sec * 1000 * 1000)); + TEST_ESP_OK(esp_light_sleep_start()); + +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(false)); +#endif + printf("Woke up from light sleep\n"); + + TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); +#if SOC_I2S_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(allow_power_down ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); +} + +static void s_test_i2s_sleep(i2s_chan_handle_t tx_handle, i2s_chan_handle_t rx_handle, bool allow_power_down) +{ + /* Enter light sleep before I2S channel enabled and wake up after 1 second */ + s_test_i2s_enter_light_sleep(1, allow_power_down); + /* Check whether I2S can work correctly after light sleep */ + TEST_ESP_OK(i2s_channel_enable(tx_handle)); + TEST_ESP_OK(i2s_channel_enable(rx_handle)); + i2s_read_write_test(tx_handle, rx_handle); +} + +static void s_test_i2s_power_on_sleep(i2s_chan_handle_t tx_handle, i2s_chan_handle_t rx_handle) +{ + s_test_i2s_sleep(tx_handle, rx_handle, false); +} + +#if TEST_I2S_PD_SLEEP +static void s_test_i2s_power_down_sleep(i2s_chan_handle_t tx_handle, i2s_chan_handle_t rx_handle) +{ +#if SOC_GDMA_SUPPORT_SLEEP_RETENTION + s_test_i2s_sleep(tx_handle, rx_handle, true); +#else + /* I2S retention is depended on GDMA retention. + * Only take two registers as sample to check the I2S retention when GDMA retention has not been supported. */ + i2s_tx_conf_reg_t tx_reg_before_slp = I2S0.tx_conf; + i2s_rx_conf_reg_t rx_reg_before_slp = I2S0.rx_conf; + /* Enter light sleep before I2S channel enabled and wake up after 1 second */ + s_test_i2s_enter_light_sleep(1, true); + /* Only check whether the register values are restored if GDMA retention has not been supported */ + i2s_tx_conf_reg_t tx_reg_after_slp = I2S0.tx_conf; + i2s_rx_conf_reg_t rx_reg_after_slp = I2S0.rx_conf; + + TEST_ASSERT_EQUAL_UINT32(tx_reg_before_slp.val, tx_reg_after_slp.val); + TEST_ASSERT_EQUAL_UINT32(rx_reg_before_slp.val, rx_reg_after_slp.val); + + TEST_ESP_OK(i2s_channel_enable(tx_handle)); + TEST_ESP_OK(i2s_channel_enable(rx_handle)); + + tx_reg_before_slp.val = I2S0.tx_conf.val; + rx_reg_before_slp.val = I2S0.rx_conf.val; + /* Enter light sleep before I2S channel enabled and wake up after 1 second */ + s_test_i2s_enter_light_sleep(1, true); + /* Only check whether the register values are restored if GDMA retention has not been supported */ + tx_reg_after_slp.val = I2S0.tx_conf.val; + rx_reg_after_slp.val = I2S0.rx_conf.val; + + TEST_ASSERT_EQUAL_UINT32(tx_reg_before_slp.val, tx_reg_after_slp.val); + TEST_ASSERT_EQUAL_UINT32(rx_reg_before_slp.val, rx_reg_after_slp.val); +#endif // SOC_GDMA_SUPPORT_SLEEP_RETENTION +} +#endif // TEST_I2S_PD_SLEEP + +void test_i2s_sleep_usability(bool allow_power_down) +{ + i2s_chan_handle_t tx_handle; + i2s_chan_handle_t rx_handle; + + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); + chan_cfg.allow_pd = allow_power_down; + i2s_std_config_t std_cfg = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE), + .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO), + .gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN, + }; + std_cfg.gpio_cfg.din = std_cfg.gpio_cfg.dout; + TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle)); + TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); + TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); + + if (!allow_power_down) { + s_test_i2s_power_on_sleep(tx_handle, rx_handle); + } +#if TEST_I2S_PD_SLEEP + else { + s_test_i2s_power_down_sleep(tx_handle, rx_handle); + } +#endif + + printf("I2S works as expected after light sleep\n"); + + TEST_ESP_OK(i2s_channel_disable(tx_handle)); + TEST_ESP_OK(i2s_channel_disable(rx_handle)); + TEST_ESP_OK(i2s_del_channel(tx_handle)); + TEST_ESP_OK(i2s_del_channel(rx_handle)); +} + +TEST_CASE("I2S_light_sleep_usability_test", "[i2s]") +{ + printf("\nTesting I2S power on light sleep...\n"); + test_i2s_sleep_usability(false); +#if TEST_I2S_PD_SLEEP + printf("\nTesting I2S power down light sleep...\n"); + test_i2s_sleep_usability(true); +#endif +} diff --git a/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py b/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py index eeb7eefaaf..a2fa85533b 100644 --- a/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py +++ b/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import pytest from pytest_embedded import Dut @@ -7,7 +7,7 @@ from pytest_embedded import Dut @pytest.mark.esp32 @pytest.mark.esp32s2 @pytest.mark.esp32c3 -# @pytest.mark.esp32c5 # TODO: [ESP32C5] IDF-10343 +@pytest.mark.esp32c5 @pytest.mark.esp32c6 @pytest.mark.esp32s3 @pytest.mark.esp32h2 diff --git a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.ci.release b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.ci.release index 91d93f163e..998cfb51f8 100644 --- a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.ci.release +++ b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.ci.release @@ -3,3 +3,4 @@ CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y diff --git a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults index b56409727b..0f8b7b2f54 100644 --- a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults +++ b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults @@ -1,2 +1,4 @@ CONFIG_I2S_ENABLE_DEBUG_LOG=y CONFIG_ESP_TASK_WDT_EN=n +# primitives for checking sleep internal state +CONFIG_ESP_SLEEP_DEBUG=y diff --git a/components/esp_hw_support/port/esp32c5/pmu_param.c b/components/esp_hw_support/port/esp32c5/pmu_param.c index c9b691c719..e676f8c478 100644 --- a/components/esp_hw_support/port/esp32c5/pmu_param.c +++ b/components/esp_hw_support/port/esp32c5/pmu_param.c @@ -293,6 +293,7 @@ const pmu_hp_system_analog_param_t * pmu_hp_system_analog_param_default(pmu_hp_m }, \ .backup_clk = ( \ BIT(PMU_ICG_FUNC_ENA_REGDMA) | \ + BIT(PMU_ICG_FUNC_ENA_GDMA) | \ BIT(PMU_ICG_FUNC_ENA_TG0) | \ BIT(PMU_ICG_FUNC_ENA_TG1) | \ BIT(PMU_ICG_FUNC_ENA_HPBUS) | \ diff --git a/components/esp_hw_support/port/esp32c61/pmu_param.c b/components/esp_hw_support/port/esp32c61/pmu_param.c index 5343316f8a..37105bef53 100644 --- a/components/esp_hw_support/port/esp32c61/pmu_param.c +++ b/components/esp_hw_support/port/esp32c61/pmu_param.c @@ -292,6 +292,7 @@ const pmu_hp_system_analog_param_t * pmu_hp_system_analog_param_default(pmu_hp_m }, \ .backup_clk = ( \ BIT(PMU_ICG_FUNC_ENA_REGDMA) | \ + BIT(PMU_ICG_FUNC_ENA_GDMA) | \ BIT(PMU_ICG_FUNC_ENA_TG0) | \ BIT(PMU_ICG_FUNC_ENA_TG1) | \ BIT(PMU_ICG_FUNC_ENA_HPBUS) | \ diff --git a/components/soc/esp32/i2s_periph.c b/components/soc/esp32/i2s_periph.c index 83c2920af6..9f06c85f40 100644 --- a/components/soc/esp32/i2s_periph.c +++ b/components/soc/esp32/i2s_periph.c @@ -29,7 +29,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2S0I_DATA_IN15_IDX, .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, }, { .mck_out_sig = -1, // Unavailable @@ -49,6 +48,5 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2S1I_DATA_IN15_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, } }; diff --git a/components/soc/esp32c3/i2s_periph.c b/components/soc/esp32c3/i2s_periph.c index ec14aa72a2..ff597698b5 100644 --- a/components/soc/esp32c3/i2s_periph.c +++ b/components/soc/esp32c3/i2s_periph.c @@ -30,6 +30,5 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, } }; diff --git a/components/soc/esp32c5/i2s_periph.c b/components/soc/esp32c5/i2s_periph.c index dbd1fdc816..619da2ca46 100644 --- a/components/soc/esp32c5/i2s_periph.c +++ b/components/soc/esp32c5/i2s_periph.c @@ -5,6 +5,7 @@ */ #include "soc/i2s_periph.h" +#include "soc/i2s_reg.h" #include "soc/gpio_sig_map.h" /* @@ -30,6 +31,43 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .module = -1, } }; + +/** + * I2S Registers to be saved during sleep retention + * - I2S_RX_CONF_REG + * - I2S_TX_CONF_REG + * - I2S_RX_CONF1_REG + * - I2S_TX_CONF1_REG + * - I2S_TX_PCM2PDM_CONF_REG + * - I2S_TX_PCM2PDM_CONF1_REG + * - I2S_RX_TDM_CTRL_REG + * - I2S_TX_TDM_CTRL_REG + * - I2S_RXEOF_NUM_REG + * - I2S_ETM_CONF_REG +*/ +#define I2S_RETENTION_REGS_CNT 10 +#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i) +static const uint32_t i2s_regs_map[4] = {0x12360f, 0x0, 0x0, 0x0}; +#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \ + /* Save/restore the register values */ \ + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \ + REGDMA_I2S_LINK(0x00), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_CNT, 0, 0, \ + i2s_regs_map[0], i2s_regs_map[1], \ + i2s_regs_map[2], i2s_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2)}, \ +}; + +static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0); + +const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_NUM] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S0, + .entry_array = i2s0_regs_retention, + .array_size = ARRAY_SIZE(i2s0_regs_retention) + }, +}; diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index de44eb10f0..8943b4f279 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -675,6 +675,10 @@ config SOC_I2S_TDM_FULL_DATA_WIDTH bool default y +config SOC_I2S_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_LEDC_SUPPORT_PLL_DIV_CLOCK bool default y diff --git a/components/soc/esp32c5/include/soc/retention_periph_defs.h b/components/soc/esp32c5/include/soc/retention_periph_defs.h index 6fd2cdda6c..02002a7d0f 100644 --- a/components/soc/esp32c5/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c5/include/soc/retention_periph_defs.h @@ -36,6 +36,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_RMT0 = 13, SLEEP_RETENTION_MODULE_UART0 = 14, SLEEP_RETENTION_MODULE_UART1 = 15, + SLEEP_RETENTION_MODULE_I2S0 = 16, /* modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -74,6 +75,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_RMT0 = BIT(SLEEP_RETENTION_MODULE_RMT0), SLEEP_RETENTION_MODULE_BM_UART0 = BIT(SLEEP_RETENTION_MODULE_UART0), SLEEP_RETENTION_MODULE_BM_UART1 = BIT(SLEEP_RETENTION_MODULE_UART1), + SLEEP_RETENTION_MODULE_BM_I2S0 = BIT(SLEEP_RETENTION_MODULE_I2S0), SLEEP_RETENTION_MODULE_BM_GDMA_CH0 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH0), SLEEP_RETENTION_MODULE_BM_GDMA_CH1 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH1), @@ -94,6 +96,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_RMT0 \ | SLEEP_RETENTION_MODULE_BM_UART0 \ | SLEEP_RETENTION_MODULE_BM_UART1 \ + | SLEEP_RETENTION_MODULE_BM_I2S0 \ ) #ifdef __cplusplus } diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 1fde2ac0a3..5badeefac1 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -288,6 +288,7 @@ #define SOC_I2S_PDM_MAX_TX_LINES (2) #define SOC_I2S_SUPPORTS_TDM (1) #define SOC_I2S_TDM_FULL_DATA_WIDTH (1) /*!< No limitation to data bit width when using multiple slots */ +#define SOC_I2S_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up I2S registers before sleep */ /*-------------------------- LEDC CAPS ---------------------------------------*/ #define SOC_LEDC_SUPPORT_PLL_DIV_CLOCK (1) diff --git a/components/soc/esp32c6/i2s_periph.c b/components/soc/esp32c6/i2s_periph.c index cb0a6a14ff..0eba95654e 100644 --- a/components/soc/esp32c6/i2s_periph.c +++ b/components/soc/esp32c6/i2s_periph.c @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2022 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/i2s_reg.h" #include "soc/gpio_sig_map.h" /* @@ -30,6 +31,43 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, } }; + +/** + * I2S Registers to be saved during sleep retention + * - I2S_RX_CONF_REG + * - I2S_TX_CONF_REG + * - I2S_RX_CONF1_REG + * - I2S_TX_CONF1_REG + * - I2S_TX_PCM2PDM_CONF_REG + * - I2S_TX_PCM2PDM_CONF1_REG + * - I2S_RX_TDM_CTRL_REG + * - I2S_TX_TDM_CTRL_REG + * - I2S_RXEOF_NUM_REG + * - I2S_ETM_CONF_REG +*/ +#define I2S_RETENTION_REGS_CNT 10 +#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i) +static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0}; +#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \ + /* Save/restore the register values */ \ + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \ + REGDMA_I2S_LINK(0x00), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_CNT, 0, 0, \ + i2s_regs_map[0], i2s_regs_map[1], \ + i2s_regs_map[2], i2s_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2)}, \ +}; + +static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0); + +const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_NUM] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S0, + .entry_array = i2s0_regs_retention, + .array_size = ARRAY_SIZE(i2s0_regs_retention) + }, +}; diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 47dc43d9d5..9493c491a5 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -671,6 +671,10 @@ config SOC_I2S_SUPPORTS_TDM bool default y +config SOC_I2S_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_LEDC_SUPPORT_PLL_DIV_CLOCK bool default y diff --git a/components/soc/esp32c6/include/soc/retention_periph_defs.h b/components/soc/esp32c6/include/soc/retention_periph_defs.h index a1dee8299a..5cdbc108e8 100644 --- a/components/soc/esp32c6/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c6/include/soc/retention_periph_defs.h @@ -36,6 +36,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_RMT0 = 13, SLEEP_RETENTION_MODULE_UART0 = 14, SLEEP_RETENTION_MODULE_UART1 = 15, + SLEEP_RETENTION_MODULE_I2S0 = 16, /* Modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -68,6 +69,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_RMT0 = BIT(SLEEP_RETENTION_MODULE_RMT0), SLEEP_RETENTION_MODULE_BM_UART0 = BIT(SLEEP_RETENTION_MODULE_UART0), SLEEP_RETENTION_MODULE_BM_UART1 = BIT(SLEEP_RETENTION_MODULE_UART1), + SLEEP_RETENTION_MODULE_BM_I2S0 = BIT(SLEEP_RETENTION_MODULE_I2S0), /* modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BM_WIFI_MAC = BIT(SLEEP_RETENTION_MODULE_WIFI_MAC), SLEEP_RETENTION_MODULE_BM_WIFI_BB = BIT(SLEEP_RETENTION_MODULE_WIFI_BB), @@ -90,6 +92,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_RMT0 \ | SLEEP_RETENTION_MODULE_BM_UART0 \ | SLEEP_RETENTION_MODULE_BM_UART1 \ + | SLEEP_RETENTION_MODULE_BM_I2S0 \ ) #ifdef __cplusplus diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index b997a19b90..58463193d0 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -273,6 +273,7 @@ #define SOC_I2S_SUPPORTS_PDM_TX (1) #define SOC_I2S_PDM_MAX_TX_LINES (2) #define SOC_I2S_SUPPORTS_TDM (1) +#define SOC_I2S_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up I2S registers before sleep */ /*-------------------------- LEDC CAPS ---------------------------------------*/ #define SOC_LEDC_SUPPORT_PLL_DIV_CLOCK (1) diff --git a/components/soc/esp32h2/i2s_periph.c b/components/soc/esp32h2/i2s_periph.c index cf70f2cb94..6e9b12c487 100644 --- a/components/soc/esp32h2/i2s_periph.c +++ b/components/soc/esp32h2/i2s_periph.c @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/i2s_periph.h" +#include "soc/i2s_reg.h" #include "soc/gpio_sig_map.h" /* @@ -29,6 +30,43 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, } }; + +/** + * I2S Registers to be saved during sleep retention + * - I2S_RX_CONF_REG + * - I2S_TX_CONF_REG + * - I2S_RX_CONF1_REG + * - I2S_TX_CONF1_REG + * - I2S_TX_PCM2PDM_CONF_REG + * - I2S_TX_PCM2PDM_CONF1_REG + * - I2S_RX_TDM_CTRL_REG + * - I2S_TX_TDM_CTRL_REG + * - I2S_RXEOF_NUM_REG + * - I2S_ETM_CONF_REG +*/ +#define I2S_RETENTION_REGS_CNT 10 +#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG +static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0}; +#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \ + /* Save/restore the register values */ \ + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \ + REGDMA_I2S_LINK(0x00), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_CNT, 0, 0, \ + i2s_regs_map[0], i2s_regs_map[1], \ + i2s_regs_map[2], i2s_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2)}, \ +}; + +static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0); + +const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_NUM] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S0, + .entry_array = i2s0_regs_retention, + .array_size = ARRAY_SIZE(i2s0_regs_retention) + }, +}; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index f66714fbae..ab71115106 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -675,6 +675,10 @@ config SOC_I2S_TDM_FULL_DATA_WIDTH bool default y +config SOC_I2S_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_LEDC_SUPPORT_PLL_DIV_CLOCK bool default y diff --git a/components/soc/esp32h2/include/soc/retention_periph_defs.h b/components/soc/esp32h2/include/soc/retention_periph_defs.h index 9c8ea0ea05..819af3a2b3 100644 --- a/components/soc/esp32h2/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h2/include/soc/retention_periph_defs.h @@ -37,6 +37,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_RMT0 = 14, SLEEP_RETENTION_MODULE_UART0 = 15, SLEEP_RETENTION_MODULE_UART1 = 16, + SLEEP_RETENTION_MODULE_I2S0 = 17, /* Modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BLE_MAC = 28, @@ -68,6 +69,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_RMT0 = BIT(SLEEP_RETENTION_MODULE_RMT0), SLEEP_RETENTION_MODULE_BM_UART0 = BIT(SLEEP_RETENTION_MODULE_UART0), SLEEP_RETENTION_MODULE_BM_UART1 = BIT(SLEEP_RETENTION_MODULE_UART1), + SLEEP_RETENTION_MODULE_BM_I2S0 = BIT(SLEEP_RETENTION_MODULE_I2S0), /* modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BM_BLE_MAC = BIT(SLEEP_RETENTION_MODULE_BLE_MAC), SLEEP_RETENTION_MODULE_BM_BT_BB = BIT(SLEEP_RETENTION_MODULE_BT_BB), @@ -89,6 +91,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_RMT0 \ | SLEEP_RETENTION_MODULE_BM_UART0 \ | SLEEP_RETENTION_MODULE_BM_UART1 \ + | SLEEP_RETENTION_MODULE_BM_I2S0 \ ) #ifdef __cplusplus diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 8831aa1a3c..d5ae0377d1 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -274,6 +274,7 @@ #define SOC_I2S_PDM_MAX_TX_LINES (2) #define SOC_I2S_SUPPORTS_TDM (1) #define SOC_I2S_TDM_FULL_DATA_WIDTH (1) /*!< No limitation to data bit width when using multiple slots */ +#define SOC_I2S_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up I2S registers before sleep */ /*-------------------------- LEDC CAPS ---------------------------------------*/ #define SOC_LEDC_SUPPORT_PLL_DIV_CLOCK (1) diff --git a/components/soc/esp32p4/i2s_periph.c b/components/soc/esp32p4/i2s_periph.c index a0208659f1..76d39c7435 100644 --- a/components/soc/esp32p4/i2s_periph.c +++ b/components/soc/esp32p4/i2s_periph.c @@ -5,6 +5,7 @@ */ #include "soc/i2s_periph.h" +#include "soc/i2s_reg.h" #include "soc/gpio_sig_map.h" #include "soc/lp_gpio_sig_map.h" @@ -34,7 +35,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = I2S0_I_SD3_PAD_IN_IDX, .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, }, [1] = { .mck_out_sig = I2S1_MCLK_PAD_OUT_IDX, @@ -58,7 +58,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = -1, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, }, [2] = { .mck_out_sig = I2S2_MCLK_PAD_OUT_IDX, @@ -82,7 +81,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = -1, .irq = ETS_I2S2_INTR_SOURCE, - .module = PERIPH_I2S2_MODULE, }, }; @@ -111,3 +109,54 @@ const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM] = { .irq = ETS_LP_I2S_INTR_SOURCE, }, }; + +/** + * I2S Registers to be saved during sleep retention + * - I2S_RX_CONF_REG + * - I2S_TX_CONF_REG + * - I2S_RX_CONF1_REG + * - I2S_TX_CONF1_REG + * - I2S_TX_PCM2PDM_CONF_REG + * - I2S_TX_PCM2PDM_CONF1_REG + * - I2S_RX_PDM2PCM_CONF_REG + * - I2S_RX_TDM_CTRL_REG + * - I2S_TX_TDM_CTRL_REG + * - I2S_RXEOF_NUM_REG + * - I2S_ETM_CONF_REG +*/ +#define I2S_RETENTION_REGS_CNT 11 +#define I2S_RETENTION_REGS_BASE(i) I2S_RX_CONF_REG(i) +static const uint32_t i2s_regs_map[4] = {0x12370f, 0x0, 0x0, 0x0}; +#define I2S_SLEEP_RETENTION_ENTRIES(i2s_port) { \ + /* Save/restore the register values */ \ + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \ + REGDMA_I2S_LINK(0x00), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_BASE(i2s_port), \ + I2S_RETENTION_REGS_CNT, 0, 0, \ + i2s_regs_map[0], i2s_regs_map[1], \ + i2s_regs_map[2], i2s_regs_map[3]), \ + .owner = ENTRY(0)}, \ +}; + +static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(0); +static const regdma_entries_config_t i2s1_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(1); +static const regdma_entries_config_t i2s2_regs_retention[] = I2S_SLEEP_RETENTION_ENTRIES(2); + +const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_NUM] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S0, + .entry_array = i2s0_regs_retention, + .array_size = ARRAY_SIZE(i2s0_regs_retention) + }, + [1] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S1, + .entry_array = i2s1_regs_retention, + .array_size = ARRAY_SIZE(i2s1_regs_retention) + }, + [2] = { + .retention_module = SLEEP_RETENTION_MODULE_I2S2, + .entry_array = i2s2_regs_retention, + .array_size = ARRAY_SIZE(i2s2_regs_retention) + }, +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 32f555421e..a883653c74 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -843,6 +843,10 @@ config SOC_I2S_TDM_FULL_DATA_WIDTH bool default y +config SOC_I2S_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_LP_I2S_NUM int default 1 diff --git a/components/soc/esp32p4/include/soc/retention_periph_defs.h b/components/soc/esp32p4/include/soc/retention_periph_defs.h index f70293e0f7..b069f7babd 100644 --- a/components/soc/esp32p4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32p4/include/soc/retention_periph_defs.h @@ -41,6 +41,10 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_AXI_DMA_CH1 = 17, SLEEP_RETENTION_MODULE_AXI_DMA_CH2 = 18, + SLEEP_RETENTION_MODULE_I2S0 = 19, + SLEEP_RETENTION_MODULE_I2S1 = 20, + SLEEP_RETENTION_MODULE_I2S2 = 21, + SLEEP_RETENTION_MODULE_MAX = 31 } periph_retention_module_t; @@ -70,6 +74,9 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_UART3 = BIT(SLEEP_RETENTION_MODULE_UART3), SLEEP_RETENTION_MODULE_BM_UART4 = BIT(SLEEP_RETENTION_MODULE_UART4), SLEEP_RETENTION_MODULE_BM_RMT0 = BIT(SLEEP_RETENTION_MODULE_RMT0), + SLEEP_RETENTION_MODULE_BM_I2S0 = BIT(SLEEP_RETENTION_MODULE_I2S0), + SLEEP_RETENTION_MODULE_BM_I2S1 = BIT(SLEEP_RETENTION_MODULE_I2S1), + SLEEP_RETENTION_MODULE_BM_I2S2 = BIT(SLEEP_RETENTION_MODULE_I2S2), SLEEP_RETENTION_MODULE_BM_ALL = (uint32_t)-1 } periph_retention_module_bitmap_t; @@ -91,6 +98,9 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_UART3 \ | SLEEP_RETENTION_MODULE_BM_UART4 \ | SLEEP_RETENTION_MODULE_BM_RMT0 \ + | SLEEP_RETENTION_MODULE_BM_I2S0 \ + | SLEEP_RETENTION_MODULE_BM_I2S1 \ + | SLEEP_RETENTION_MODULE_BM_I2S2 \ ) #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 33cf1c070a..d7dcd4200f 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -328,6 +328,7 @@ #define SOC_I2S_PDM_MAX_TX_LINES (2) // On I2S0 #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 */ +#define SOC_I2S_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up I2S registers before sleep */ /*-------------------------- LP_I2S CAPS -------------------------------------*/ #define SOC_LP_I2S_NUM (1U) diff --git a/components/soc/esp32s2/i2s_periph.c b/components/soc/esp32s2/i2s_periph.c index 25b4804cb2..c13b47926d 100644 --- a/components/soc/esp32s2/i2s_periph.c +++ b/components/soc/esp32s2/i2s_periph.c @@ -29,6 +29,5 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2S0I_DATA_IN15_IDX, .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, } }; diff --git a/components/soc/esp32s3/i2s_periph.c b/components/soc/esp32s3/i2s_periph.c index 853541bd9e..9d040cfc54 100644 --- a/components/soc/esp32s3/i2s_periph.c +++ b/components/soc/esp32s3/i2s_periph.c @@ -33,7 +33,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = I2S0I_SD3_IN_IDX, .irq = ETS_I2S0_INTR_SOURCE, - .module = PERIPH_I2S0_MODULE, }, { .mck_out_sig = I2S1_MCLK_OUT_IDX, @@ -57,6 +56,5 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = -1, .irq = ETS_I2S1_INTR_SOURCE, - .module = PERIPH_I2S1_MODULE, } }; diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index cf2d483f48..0dfc4a1196 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -1,13 +1,17 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "soc/soc.h" -#include "soc/periph_defs.h" +#include "soc/interrupts.h" #include "soc/soc_caps.h" +#include "soc/regdma.h" +#if SOC_I2S_SUPPORT_SLEEP_RETENTION +#include "soc/retention_periph_defs.h" +#endif #if SOC_I2S_SUPPORTED #include "soc/i2s_struct.h" @@ -51,7 +55,6 @@ typedef struct { }; const uint8_t irq; - const periph_module_t module; } i2s_signal_conn_t; extern const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM]; @@ -62,6 +65,16 @@ extern const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM]; #endif // SOC_I2S_SUPPORTED +#if SOC_I2S_SUPPORT_SLEEP_RETENTION +typedef struct { + const periph_retention_module_t retention_module; + const regdma_entries_config_t *entry_array; + uint32_t array_size; +} i2s_reg_retention_info_t; + +extern const i2s_reg_retention_info_t i2s_reg_retention_info[SOC_I2S_NUM]; +#endif // SOC_I2S_SUPPORT_SLEEP_RETENTION + #ifdef __cplusplus } #endif diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index c48c66d0eb..c3c62081f0 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -53,6 +53,7 @@ extern "C" { #define REGDMA_TG1_WDT_LINK(_pri) ((0x1B << 8) | _pri) #define REGDMA_TG0_TIMER_LINK(_pri) ((0x1C << 8) | _pri) #define REGDMA_TG1_TIMER_LINK(_pri) ((0x1D << 8) | _pri) +#define REGDMA_I2S_LINK(_pri) ((0x1E << 8) | _pri) #define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) #define REGDMA_LINK_PRI_SYS_CLK REGDMA_LINK_PRI_0 @@ -69,6 +70,7 @@ extern "C" { #define REGDMA_LINK_PRI_RMT REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_GPTIMER REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_I2C REGDMA_LINK_PRI_GENERAL_PERIPH +#define REGDMA_LINK_PRI_I2S REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_UART REGDMA_LINK_PRI_GENERAL_PERIPH typedef enum { diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index c1415498f3..9d0ad91bb1 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -234,6 +234,15 @@ When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the The I2S driver can prevent the system from changing or stopping the source clock by acquiring a power management lock. When the source clock is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX` and when the source clock is APLL (if supported), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the user is reading or writing via I2S (i.e., calling :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`), the driver guarantees that the power management lock is acquired. Likewise, the driver releases the lock after the reading or writing finishes. +.. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION + + Sleep Retention + """"""""""""""" + + {IDF_TARGET_NAME} supports to retain the I2S register context before entering **light sleep** and restore them after woke up. Which means you don't have to re-init the I2S driver even the peripheral is power off during the light sleep. + + This feature can be enabled by setting the flag :cpp:member:`i2s_chan_config_t::allow_pd`. It will allow the system to power down the I2S in light sleep, meanwhile save the I2S register context. It can help to save more power consumption with some extra cost of the memory. + Finite State Machine ^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index da52d19b09..8a7b9c37ba 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -153,6 +153,7 @@ The following peripheral drivers are not aware of DFS yet. Applications need to - SYSTIMER :SOC_RMT_SUPPORT_SLEEP_RETENTION: - RMT :SOC_I2C_SUPPORT_SLEEP_RETENTION: - I2C + :SOC_I2S_SUPPORT_SLEEP_RETENTION: - I2S :SOC_UART_SUPPORT_SLEEP_RETENTION: - All UARTs The following peripherals are not yet supported: @@ -164,7 +165,6 @@ The following peripheral drivers are not aware of DFS yet. Applications need to - Trace - Crypto: AES/ECC/HMAC/RSA/SHA/DS/XTA_AES/ECDSA - SPI2 - - I2S - PCNT - USB-Serial-JTAG - TWAI diff --git a/docs/zh_CN/api-reference/peripherals/i2s.rst b/docs/zh_CN/api-reference/peripherals/i2s.rst index 369f1d24b0..0620798def 100644 --- a/docs/zh_CN/api-reference/peripherals/i2s.rst +++ b/docs/zh_CN/api-reference/peripherals/i2s.rst @@ -234,6 +234,15 @@ I2S 驱动中的资源可分为三个级别: I2S 驱动可以获取电源管理锁,从而防止系统设置更改或时钟源被禁用。时钟源为 APB 时,锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。时钟源为 APLL(若支持)时,锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`。用户通过 I2S 读写时(即调用 :cpp:func:`i2s_channel_read` 或 :cpp:func:`i2s_channel_write`),驱动程序将获取电源管理锁,并在读写完成后释放锁。 +.. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION + + 睡眠保留 + """""""" + + {IDF_TARGET_NAME} 支持在进入 **轻度睡眠** 之前保留 I2S 寄存器中的内容,并在唤醒后恢复。也就是说外设若因进入 **轻度睡眠** 而掉电,程序不需要在唤醒后重新配置 I2S。 + + 该特性可以通过置位配置中的 :cpp:member:`i2s_chan_config_t::allow_pd` 标志位启用。启用后驱动允许系统在轻度睡眠时对 I2S 掉电,同时保存 I2S 的寄存器内容。它可以帮助降低轻度睡眠时的功耗,但需要花费一些额外的存储来保存寄存器的配置。 + 有限状态机 ^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 3762008505..6d1e464abc 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -152,7 +152,8 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, - SPI0/1 - SYSTIMER :SOC_RMT_SUPPORT_SLEEP_RETENTION: - RMT - :SOC_I2C_SUPPORT_SLEEP_RETENTION:- I2C + :SOC_I2C_SUPPORT_SLEEP_RETENTION: - I2C + :SOC_I2S_SUPPORT_SLEEP_RETENTION: - I2S :SOC_UART_SUPPORT_SLEEP_RETENTION: - All UARTs 以下外设尚未支持: @@ -164,7 +165,6 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, - Trace - Crypto: AES/ECC/HMAC/RSA/SHA/DS/XTA_AES/ECDSA - SPI2 - - I2S - PCNT - USB-Serial-JTAG - TWAI