From ae36f8494520ae2682aa486e2016de8f3fbc363d Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Sat, 14 Sep 2024 18:27:27 +0800 Subject: [PATCH] feat(i2s): enable i2s test on c5 --- components/esp_driver_i2s/i2s_common.c | 8 +- components/esp_driver_i2s/i2s_private.h | 4 + .../include/driver/i2s_common.h | 9 +- .../test_apps/.build-test-rules.yml | 4 - .../test_apps/i2s/main/CMakeLists.txt | 7 +- .../test_apps/i2s/main/test_i2s.c | 2 +- ...t_i2s_slp_retention.c => test_i2s_sleep.c} | 106 +++++++++++++----- .../test_apps/i2s/pytest_i2s.py | 2 +- components/soc/esp32c5/i2s_periph.c | 5 +- components/soc/esp32c6/i2s_periph.c | 5 +- components/soc/esp32h2/i2s_periph.c | 9 +- components/soc/esp32p4/i2s_periph.c | 9 +- components/soc/include/soc/i2s_periph.h | 8 +- docs/en/api-reference/peripherals/i2s.rst | 9 ++ docs/zh_CN/api-reference/peripherals/i2s.rst | 9 ++ 15 files changed, 135 insertions(+), 61 deletions(-) rename components/esp_driver_i2s/test_apps/i2s/main/{test_i2s_slp_retention.c => test_i2s_sleep.c} (58%) diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 6586c3f1f0..06f4449d25 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -41,7 +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" @@ -258,7 +260,7 @@ static i2s_controller_t *i2s_acquire_controller_obj(int id) #endif #if I2S_USE_RETENTION_LINK - sleep_retention_module_t module = i2s_periph_signal[id].retention_module; + sleep_retention_module_t module = i2s_reg_retention_info[id].retention_module; sleep_retention_module_init_param_t init_param = { .cbs = { .create = { @@ -936,7 +938,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * 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->backup_before_sleep, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); + 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; @@ -997,7 +999,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * i2s_obj->full_duplex = true; } #if I2S_USE_RETENTION_LINK - if (chan_cfg->backup_before_sleep) { + if (chan_cfg->allow_pd) { s_i2s_create_retention_module(i2s_obj); } #endif diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 0cfccbaf94..bc2cc1f8b4 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -26,7 +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" @@ -135,8 +137,10 @@ typedef struct { 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 10c0f5211e..fd7a249da3 100644 --- a/components/esp_driver_i2s/include/driver/i2s_common.h +++ b/components/esp_driver_i2s/include/driver/i2s_common.h @@ -26,7 +26,7 @@ extern "C" { .dma_frame_num = 240, \ .auto_clear_after_cb = false, \ .auto_clear_before_cb = false, \ - .backup_before_sleep = false, \ + .allow_pd = false, \ .intr_priority = 0, \ } @@ -74,9 +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 backup_before_sleep; /*!< If 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 */ + 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 63623c66c7..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,15 +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() -if(CONFIG_SOC_I2S_SUPPORT_SLEEP_RETENTION) - list(APPEND srcs "test_i2s_slp_retention.c") -endif() - idf_component_register(SRCS ${srcs} 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 dc86526b16..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 @@ -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_slp_retention.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c similarity index 58% rename from components/esp_driver_i2s/test_apps/i2s/main/test_i2s_slp_retention.c rename to components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c index 22d94ac7c8..6b5b944f31 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_slp_retention.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c @@ -19,11 +19,11 @@ #include "esp_private/esp_pmu.h" #include "../../test_inc/test_i2s.h" -#if SOC_I2S_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#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 test_i2s_enter_light_sleep(int sec) +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); @@ -42,17 +42,71 @@ static void test_i2s_enter_light_sleep(int sec) printf("Woke up from light sleep\n"); TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); - TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP); +#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); } -TEST_CASE("I2S_sleep_retention_test", "[i2s]") +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.backup_before_sleep = true; + 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), @@ -63,31 +117,13 @@ TEST_CASE("I2S_sleep_retention_test", "[i2s]") TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); - /* 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. */ -#if !SOC_GDMA_SUPPORT_SLEEP_RETENTION - i2s_tx_conf_reg_t tx_reg_before_slp = I2S0.tx_conf; - i2s_rx_conf_reg_t rx_reg_before_slp = I2S0.rx_conf; -#endif - - /* Enter light sleep and wake up after 1 second */ - test_i2s_enter_light_sleep(1); - -#if SOC_GDMA_SUPPORT_SLEEP_RETENTION - /* 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); -#else - /* 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)); + 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"); @@ -98,4 +134,12 @@ TEST_CASE("I2S_sleep_retention_test", "[i2s]") TEST_ESP_OK(i2s_del_channel(rx_handle)); } -#endif // SOC_I2S_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +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 455fe049da..a2fa85533b 100644 --- a/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py +++ b/components/esp_driver_i2s/test_apps/i2s/pytest_i2s.py @@ -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/soc/esp32c5/i2s_periph.c b/components/soc/esp32c5/i2s_periph.c index ff683a2f81..41c41b69d8 100644 --- a/components/soc/esp32c5/i2s_periph.c +++ b/components/soc/esp32c5/i2s_periph.c @@ -31,7 +31,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .retention_module = SLEEP_RETENTION_MODULE_I2S0, } }; @@ -52,6 +51,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { #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), \ @@ -60,9 +60,11 @@ static const uint32_t i2s_regs_map[4] = {0x12360f, 0x0, 0x0, 0x0}; i2s_regs_map[0], i2s_regs_map[1], \ i2s_regs_map[2], i2s_regs_map[3]), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the RX_UPDATE after the retention to make sure the RX configurations are synchronized */ \ [1] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x01), I2S_RX_CONF_REG(i2s_port), I2S_RX_UPDATE, I2S_RX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the TX_UPDATE after the retention to make sure the TX configurations are synchronized */ \ [2] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x02), I2S_TX_CONF_REG(i2s_port), I2S_TX_UPDATE, I2S_TX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)} \ @@ -72,6 +74,7 @@ static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION 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/i2s_periph.c b/components/soc/esp32c6/i2s_periph.c index 3f3ed3974a..2aebe5e03d 100644 --- a/components/soc/esp32c6/i2s_periph.c +++ b/components/soc/esp32c6/i2s_periph.c @@ -31,7 +31,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .retention_module = SLEEP_RETENTION_MODULE_I2S0, } }; @@ -52,6 +51,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { #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), \ @@ -60,9 +60,11 @@ static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0}; i2s_regs_map[0], i2s_regs_map[1], \ i2s_regs_map[2], i2s_regs_map[3]), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the RX_UPDATE after the retention to make sure the RX configurations are synchronized */ \ [1] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x01), I2S_RX_CONF_REG(i2s_port), I2S_RX_UPDATE, I2S_RX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the TX_UPDATE after the retention to make sure the TX configurations are synchronized */ \ [2] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x02), I2S_TX_CONF_REG(i2s_port), I2S_TX_UPDATE, I2S_TX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)} \ @@ -72,6 +74,7 @@ static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION 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/i2s_periph.c b/components/soc/esp32h2/i2s_periph.c index 582e624c4f..60a1ee3f09 100644 --- a/components/soc/esp32h2/i2s_periph.c +++ b/components/soc/esp32h2/i2s_periph.c @@ -30,7 +30,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sig = I2SI_SD_IN_IDX, .irq = ETS_I2S1_INTR_SOURCE, - .retention_module = SLEEP_RETENTION_MODULE_I2S0, } }; @@ -51,6 +50,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { #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), \ @@ -59,11 +59,13 @@ static const uint32_t i2s_regs_map[4] = {0x12330f, 0x0, 0x0, 0x0}; i2s_regs_map[0], i2s_regs_map[1], \ i2s_regs_map[2], i2s_regs_map[3]), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the RX_UPDATE after the retention to make sure the RX configurations are synchronized */ \ [1] = { .config = REGDMA_LINK_WRITE_INIT( \ - REGDMA_I2S_LINK(0x01), I2S_RX_CONF_REG(i2s_port), I2S_RX_UPDATE, I2S_RX_UPDATE_M, 1, 0), \ + REGDMA_I2S_LINK(0x01), I2S_RX_CONF_REG, I2S_RX_UPDATE, I2S_RX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the TX_UPDATE after the retention to make sure the TX configurations are synchronized */ \ [2] = { .config = REGDMA_LINK_WRITE_INIT( \ - REGDMA_I2S_LINK(0x02), I2S_TX_CONF_REG(i2s_port), I2S_TX_UPDATE, I2S_TX_UPDATE_M, 1, 0), \ + REGDMA_I2S_LINK(0x02), I2S_TX_CONF_REG, I2S_TX_UPDATE, I2S_TX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)} \ }; @@ -71,6 +73,7 @@ static const regdma_entries_config_t i2s0_regs_retention[] = I2S_SLEEP_RETENTION 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/esp32p4/i2s_periph.c b/components/soc/esp32p4/i2s_periph.c index 5f623aea53..b16e262615 100644 --- a/components/soc/esp32p4/i2s_periph.c +++ b/components/soc/esp32p4/i2s_periph.c @@ -35,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, - .retention_module = SLEEP_RETENTION_MODULE_I2S0, }, [1] = { .mck_out_sig = I2S1_MCLK_PAD_OUT_IDX, @@ -59,7 +58,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = -1, .irq = ETS_I2S1_INTR_SOURCE, - .retention_module = SLEEP_RETENTION_MODULE_I2S1, }, [2] = { .mck_out_sig = I2S2_MCLK_PAD_OUT_IDX, @@ -83,7 +81,6 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { .data_in_sigs[3] = -1, .irq = ETS_I2S2_INTR_SOURCE, - .retention_module = SLEEP_RETENTION_MODULE_I2S2, }, }; @@ -131,6 +128,7 @@ const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM] = { #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), \ @@ -139,9 +137,11 @@ static const uint32_t i2s_regs_map[4] = {0x12370f, 0x0, 0x0, 0x0}; i2s_regs_map[0], i2s_regs_map[1], \ i2s_regs_map[2], i2s_regs_map[3]), \ .owner = ENTRY(0)}, \ + /* Set the RX_UPDATE after the retention to make sure the RX configurations are synchronized */ \ [1] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x01), I2S_RX_CONF_REG(i2s_port), I2S_RX_UPDATE, I2S_RX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)}, \ + /* Set the TX_UPDATE after the retention to make sure the TX configurations are synchronized */ \ [2] = { .config = REGDMA_LINK_WRITE_INIT( \ REGDMA_I2S_LINK(0x02), I2S_TX_CONF_REG(i2s_port), I2S_TX_UPDATE, I2S_TX_UPDATE_M, 1, 0), \ .owner = ENTRY(0) | ENTRY(2)} \ @@ -153,14 +153,17 @@ static const regdma_entries_config_t i2s2_regs_retention[] = I2S_SLEEP_RETENTION 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/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index 8100bb9a18..0dfc4a1196 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -6,10 +6,12 @@ #pragma once #include "soc/soc.h" -#include "soc/retention_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" @@ -53,9 +55,6 @@ typedef struct { }; const uint8_t irq; -#if SOC_I2S_SUPPORT_SLEEP_RETENTION - const periph_retention_module_t retention_module; -#endif } i2s_signal_conn_t; extern const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM]; @@ -68,6 +67,7 @@ extern const i2s_signal_conn_t lp_i2s_periph_signal[SOC_LP_I2S_NUM]; #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; 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/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 的寄存器内容。它可以帮助降低轻度睡眠时的功耗,但需要花费一些额外的存储来保存寄存器的配置。 + 有限状态机 ^^^^^^^^^^