From 409e7b5012738be0826bdfd11ba4693d8679279e Mon Sep 17 00:00:00 2001 From: Ma Jing Jing Date: Fri, 10 Jul 2026 15:31:39 +0800 Subject: [PATCH] feat(asrc): Add sleep retention --- components/esp_asrc_adapter/CMakeLists.txt | 7 +- components/esp_asrc_adapter/Kconfig | 14 +++ components/esp_asrc_adapter/asrc_adapter.c | 98 ++++++++++++++++++- .../esp_asrc_adapter/asrc_adapter_priv.h | 42 ++++++++ .../esp_asrc_adapter/esp32h4/asrc_retention.c | 42 ++++++++ .../esp32s31/asrc_retention.c | 42 ++++++++ .../esp_asrc_adapter/include/asrc_adapter.h | 1 + .../esp32h4/include/hal/asrc_periph.h | 1 + .../esp32s31/include/hal/asrc_periph.h | 1 + .../port/esp32h4/peripheral_domain_pd.c | 3 + .../port/esp32s31/peripheral_domain_pd.c | 3 + components/soc/esp32h4/include/soc/reg_base.h | 2 +- .../include/soc/retention_periph_defs.h | 13 +-- .../include/soc/retention_periph_defs.h | 2 + components/soc/include/soc/regdma.h | 2 + 15 files changed, 263 insertions(+), 10 deletions(-) create mode 100644 components/esp_asrc_adapter/Kconfig create mode 100644 components/esp_asrc_adapter/asrc_adapter_priv.h create mode 100644 components/esp_asrc_adapter/esp32h4/asrc_retention.c create mode 100644 components/esp_asrc_adapter/esp32s31/asrc_retention.c diff --git a/components/esp_asrc_adapter/CMakeLists.txt b/components/esp_asrc_adapter/CMakeLists.txt index 742304d8af0..643d8287dfe 100644 --- a/components/esp_asrc_adapter/CMakeLists.txt +++ b/components/esp_asrc_adapter/CMakeLists.txt @@ -8,8 +8,13 @@ set(srcs) # ASRC related source files if(CONFIG_SOC_ASRC_SUPPORTED) list(APPEND srcs "asrc_adapter.c") + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "${target}/asrc_retention.c") + endif() endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes} - REQUIRES esp_hal_asrc esp_driver_dma esp_mm) + PRIV_INCLUDE_DIRS "." + REQUIRES esp_hal_asrc esp_driver_dma esp_mm + PRIV_REQUIRES esp_hw_support esp_pm) diff --git a/components/esp_asrc_adapter/Kconfig b/components/esp_asrc_adapter/Kconfig new file mode 100644 index 00000000000..fe2dc186fc2 --- /dev/null +++ b/components/esp_asrc_adapter/Kconfig @@ -0,0 +1,14 @@ +menu "ESP-ASRC Configurations" + depends on SOC_ASRC_SUPPORTED + + config ASRC_ALLOW_PD + bool "Allow power down of ASRC in light sleep" + default n + depends on PM_ENABLE && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + help + If enabled, the ASRC driver will backup/restore ASRC registers before/after + entering/exiting light sleep via the sleep retention framework. + By this approach, the system can power off ASRC's power domain (TOP PD). + This can save power, but at the expense of more RAM being consumed. + +endmenu diff --git a/components/esp_asrc_adapter/asrc_adapter.c b/components/esp_asrc_adapter/asrc_adapter.c index af6a2ec3d2e..dcdeccbb8a5 100644 --- a/components/esp_asrc_adapter/asrc_adapter.c +++ b/components/esp_asrc_adapter/asrc_adapter.c @@ -16,11 +16,23 @@ #include "esp_cache.h" #include "esp_memory_utils.h" #include "esp_private/esp_cache_private.h" -#include "asrc_adapter.h" +#include "sys/lock.h" +#include "sdkconfig.h" +#include "asrc_adapter_priv.h" -#define TAG "ASRC_ADAPTER" +#if ASRC_USE_RETENTION_LINK +#include "esp_private/sleep_retention.h" +#endif /* ASRC_USE_RETENTION_LINK */ + +#define TAG "ASRC_ADAPTER" #define ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_16B_ALIGNED +#if ASRC_USE_RETENTION_LINK +/* Note: sleep retention APIs use OS locks, so use a lock rather than a light-weight critical section */ +static _lock_t s_asrc_sleep_retention_lock; +static uint8_t s_asrc_retention_ref_count; +#endif /* ASRC_USE_RETENTION_LINK */ + static bool IRAM_ATTR asrc_hw_rx_eof(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { portBASE_TYPE higher_priority_task_awoken = pdFALSE; @@ -29,6 +41,79 @@ static bool IRAM_ATTR asrc_hw_rx_eof(gdma_channel_handle_t dma_chan, gdma_event_ return higher_priority_task_awoken; } +#if ASRC_USE_RETENTION_LINK +static esp_err_t s_asrc_sleep_retention_init_cb(void *arg) +{ + esp_err_t ret = sleep_retention_entries_create(asrc_reg_retention_info[0].entry_array, + asrc_reg_retention_info[0].array_size, + REGDMA_LINK_PRI_ASRC, asrc_reg_retention_info[0].retention_module); + ESP_RETURN_ON_ERROR(ret, TAG, "failed to allocate mem for sleep retention"); + return ret; +} + +static void asrc_acquire_sleep_retention(void) +{ + sleep_retention_module_t module = asrc_reg_retention_info[0].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = s_asrc_sleep_retention_init_cb, + .arg = NULL, + }, + }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM), + }; + + _lock_acquire(&s_asrc_sleep_retention_lock); + if (s_asrc_retention_ref_count == 0) { + esp_err_t err = sleep_retention_module_init(module, &init_param); + if (err != ESP_OK) { + ESP_LOGW(TAG, "init sleep retention failed, ASRC configuration may be lost after sleep wakeup"); + } else { +#if defined(CONFIG_ASRC_ALLOW_PD) && CONFIG_ASRC_ALLOW_PD + err = sleep_retention_module_allocate(module); + if (err != ESP_OK) { + ESP_LOGW(TAG, "create retention module failed, power domain can't turn off"); + /* don't call sleep_retention_module_deinit here, otherwise ASRC may be powered off during sleep */ + } else if (sleep_retention_module_attach(module) != ESP_OK) { + ESP_LOGW(TAG, "attach retention module failed, power domain can't turn off"); + } +#endif /* CONFIG_ASRC_ALLOW_PD */ + } + } + s_asrc_retention_ref_count++; + _lock_release(&s_asrc_sleep_retention_lock); +} + +static void asrc_release_sleep_retention(void) +{ + sleep_retention_module_t module = asrc_reg_retention_info[0].retention_module; + + _lock_acquire(&s_asrc_sleep_retention_lock); + if (s_asrc_retention_ref_count == 0) { + _lock_release(&s_asrc_sleep_retention_lock); + return; + } + s_asrc_retention_ref_count--; + if (s_asrc_retention_ref_count == 0) { +#if defined(CONFIG_ASRC_ALLOW_PD) && CONFIG_ASRC_ALLOW_PD + if (sleep_retention_module_detach(module) == ESP_OK) { + if (sleep_retention_module_free(module) != ESP_OK) { + ESP_LOGW(TAG, "fail to free the retention link list"); + } + } else { + ESP_LOGW(TAG, "fail to detach the retention link list"); + } +#endif /* CONFIG_ASRC_ALLOW_PD */ + if (sleep_retention_module_deinit(module) != ESP_OK) { + ESP_LOGW(TAG, "fail to deinit the retention module"); + } + } + _lock_release(&s_asrc_sleep_retention_lock); +} +#endif /* ASRC_USE_RETENTION_LINK */ + esp_err_t asrc_hw_gdma_create_channel(int asrc_idx, void *user_data, uint16_t max_data_burst_size, asrc_hw_gdma_channel_handle_t *dma_tx_chan, asrc_hw_gdma_channel_handle_t *dma_rx_chan) { @@ -65,6 +150,10 @@ esp_err_t asrc_hw_gdma_create_channel(int asrc_idx, void *user_data, uint16_t ma ESP_GOTO_ON_ERROR(gdma_config_transfer(dma_tx, &transfer_config), cleanup, TAG, "Fail to config tx transfer"); gdma_rx_event_callbacks_t cb = {.on_recv_eof = asrc_hw_rx_eof}; ESP_GOTO_ON_ERROR(gdma_register_rx_event_callbacks(dma_rx, &cb, user_data), cleanup, TAG, "Fail to register rx event callbacks"); +#if ASRC_USE_RETENTION_LINK + /* Register retention while creating channel, same style as GDMA */ + asrc_acquire_sleep_retention(); +#endif /* ASRC_USE_RETENTION_LINK */ *dma_tx_chan = dma_tx; *dma_rx_chan = dma_rx; return ESP_OK; @@ -172,6 +261,11 @@ void asrc_hw_gdma_destroy_channel(asrc_hw_gdma_channel_handle_t dma_tx_chan, asr gdma_disconnect(dma_rx_chan_handle); gdma_del_channel(dma_rx_chan_handle); } +#if ASRC_USE_RETENTION_LINK + if (dma_tx_chan != NULL || dma_rx_chan != NULL) { + asrc_release_sleep_retention(); + } +#endif /* ASRC_USE_RETENTION_LINK */ } esp_err_t asrc_hw_get_buffer_alignment(uint32_t heap_caps, size_t *out_alignment) diff --git a/components/esp_asrc_adapter/asrc_adapter_priv.h b/components/esp_asrc_adapter/asrc_adapter_priv.h new file mode 100644 index 00000000000..7f3b70d88f8 --- /dev/null +++ b/components/esp_asrc_adapter/asrc_adapter_priv.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "asrc_adapter.h" + +#if SOC_PAU_SUPPORTED +#include "soc/regdma.h" +#include "soc/retention_periph_defs.h" +#endif /* SOC_PAU_SUPPORTED */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if SOC_PAU_SUPPORTED +/** + * @brief Use retention link only when PM is enabled and peripheral power-down in light sleep is allowed + */ +#define ASRC_USE_RETENTION_LINK (CONFIG_PM_ENABLE && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + +/** + * @brief ASRC register sleep retention information + */ +typedef struct { + const periph_retention_module_t retention_module; + const regdma_entries_config_t *entry_array; + uint32_t array_size; +} asrc_reg_retention_info_t; + +extern const asrc_reg_retention_info_t asrc_reg_retention_info[1]; +#endif /* SOC_PAU_SUPPORTED */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/components/esp_asrc_adapter/esp32h4/asrc_retention.c b/components/esp_asrc_adapter/esp32h4/asrc_retention.c new file mode 100644 index 00000000000..497a3f8d08d --- /dev/null +++ b/components/esp_asrc_adapter/esp32h4/asrc_retention.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "soc/asrc_reg.h" +#include "asrc_adapter_priv.h" + +/** + * ASRC Registers to be saved during sleep retention + * + * Retained R/W registers per channel (WT/RO registers excluded): + * CHNL0: CFG0(0x00) CFG1(0x04) CFG2(0x08) CFG4(0x10) CFG5(0x14) CFG6(0x18) INT_ENA(0x28) + * CHNL1: CFG0(0x3c) CFG1(0x40) CFG2(0x44) CFG4(0x4c) CFG5(0x50) CFG6(0x54) INT_ENA(0x64) + * + */ +#define ASRC_RETENTION_REGS_CNT 14 +#define ASRC_RETENTION_REGS_BASE (DR_REG_ASRC_BASE + 0x0) + +static const uint32_t asrc_regs_map[4] = {0x023B8477, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t asrc_regdma_entries[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_ASRC_LINK(0x00), + ASRC_RETENTION_REGS_BASE, ASRC_RETENTION_REGS_BASE, + ASRC_RETENTION_REGS_CNT, 0, 0, + asrc_regs_map[0], asrc_regs_map[1], + asrc_regs_map[2], asrc_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const asrc_reg_retention_info_t asrc_reg_retention_info[1] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_ASRC, + .entry_array = asrc_regdma_entries, + .array_size = ARRAY_SIZE(asrc_regdma_entries), + }, +}; diff --git a/components/esp_asrc_adapter/esp32s31/asrc_retention.c b/components/esp_asrc_adapter/esp32s31/asrc_retention.c new file mode 100644 index 00000000000..497a3f8d08d --- /dev/null +++ b/components/esp_asrc_adapter/esp32s31/asrc_retention.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "soc/asrc_reg.h" +#include "asrc_adapter_priv.h" + +/** + * ASRC Registers to be saved during sleep retention + * + * Retained R/W registers per channel (WT/RO registers excluded): + * CHNL0: CFG0(0x00) CFG1(0x04) CFG2(0x08) CFG4(0x10) CFG5(0x14) CFG6(0x18) INT_ENA(0x28) + * CHNL1: CFG0(0x3c) CFG1(0x40) CFG2(0x44) CFG4(0x4c) CFG5(0x50) CFG6(0x54) INT_ENA(0x64) + * + */ +#define ASRC_RETENTION_REGS_CNT 14 +#define ASRC_RETENTION_REGS_BASE (DR_REG_ASRC_BASE + 0x0) + +static const uint32_t asrc_regs_map[4] = {0x023B8477, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t asrc_regdma_entries[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_ASRC_LINK(0x00), + ASRC_RETENTION_REGS_BASE, ASRC_RETENTION_REGS_BASE, + ASRC_RETENTION_REGS_CNT, 0, 0, + asrc_regs_map[0], asrc_regs_map[1], + asrc_regs_map[2], asrc_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const asrc_reg_retention_info_t asrc_reg_retention_info[1] = { + [0] = { + .retention_module = SLEEP_RETENTION_MODULE_ASRC, + .entry_array = asrc_regdma_entries, + .array_size = ARRAY_SIZE(asrc_regdma_entries), + }, +}; diff --git a/components/esp_asrc_adapter/include/asrc_adapter.h b/components/esp_asrc_adapter/include/asrc_adapter.h index 329d0597867..fa63966971a 100644 --- a/components/esp_asrc_adapter/include/asrc_adapter.h +++ b/components/esp_asrc_adapter/include/asrc_adapter.h @@ -8,6 +8,7 @@ #include #include "esp_err.h" +#include "sdkconfig.h" #ifdef __cplusplus extern "C" { diff --git a/components/esp_hal_asrc/esp32h4/include/hal/asrc_periph.h b/components/esp_hal_asrc/esp32h4/include/hal/asrc_periph.h index 98ef1694bdb..6e8122d97ee 100644 --- a/components/esp_hal_asrc/esp32h4/include/hal/asrc_periph.h +++ b/components/esp_hal_asrc/esp32h4/include/hal/asrc_periph.h @@ -7,6 +7,7 @@ #pragma once #include +#include "soc/soc_caps.h" #ifdef __cplusplus extern "C" { diff --git a/components/esp_hal_asrc/esp32s31/include/hal/asrc_periph.h b/components/esp_hal_asrc/esp32s31/include/hal/asrc_periph.h index 98ef1694bdb..6e8122d97ee 100644 --- a/components/esp_hal_asrc/esp32s31/include/hal/asrc_periph.h +++ b/components/esp_hal_asrc/esp32s31/include/hal/asrc_periph.h @@ -7,6 +7,7 @@ #pragma once #include +#include "soc/soc_caps.h" #ifdef __cplusplus extern "C" { diff --git a/components/esp_hw_support/port/esp32h4/peripheral_domain_pd.c b/components/esp_hw_support/port/esp32h4/peripheral_domain_pd.c index 00ace077271..f534054522d 100644 --- a/components/esp_hw_support/port/esp32h4/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32h4/peripheral_domain_pd.c @@ -82,6 +82,9 @@ bool peripheral_domain_pd_allowed(void) // ESP32H4 supports SDM sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_SDM0); + // ESP32H4 supports ASRC sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_ASRC); + const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask); const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask); bool ic = sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules); diff --git a/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c index 9a0ac8ce7f1..cb737146e34 100644 --- a/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c @@ -102,6 +102,9 @@ bool peripheral_domain_pd_allowed(void) //ESP32S31 supports 2D-DMA sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_DMA2D); + // ESP32S31 supports ASRC sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_ASRC); + const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask); const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask); const sleep_retention_module_bitmap_t peripheral_domain_retained_modules = sleep_retention_module_bitmap_and(retained_modules, mask); diff --git a/components/soc/esp32h4/include/soc/reg_base.h b/components/soc/esp32h4/include/soc/reg_base.h index be1b13e32d2..934efac5aef 100644 --- a/components/soc/esp32h4/include/soc/reg_base.h +++ b/components/soc/esp32h4/include/soc/reg_base.h @@ -34,7 +34,7 @@ #define DR_REG_MEM_MONITOR_BASE 0x60025000 #define DR_REG_PVT_BASE 0x60026000 #define DR_REG_PCNT_BASE 0x60027000 -#define DR_REG_SAMPLE_RATE_CONVERTER_BASE 0x60028000 +#define DR_REG_ASRC_BASE 0x60028000 #define DR_REG_ZERO_DET_BASE 0x60029000 #define DR_REG_USB_OTG_FS_CORE0_BASE 0x60040000 #define DR_REG_USB_OTG_FS_CORE1_BASE 0x6007F000 diff --git a/components/soc/esp32h4/include/soc/retention_periph_defs.h b/components/soc/esp32h4/include/soc/retention_periph_defs.h index 7ff5ad0d42b..a1ab02d76e0 100644 --- a/components/soc/esp32h4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h4/include/soc/retention_periph_defs.h @@ -50,18 +50,19 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MCPWM0 = 26, SLEEP_RETENTION_MODULE_MCPWM1 = 27, SLEEP_RETENTION_MODULE_SDM0 = 28, + SLEEP_RETENTION_MODULE_ASRC = 29, /* Modem module, which includes BLE and 802.15.4 */ - SLEEP_RETENTION_MODULE_CLOCK_MODEM = 29, - SLEEP_RETENTION_MODULE_BLE_MAC = 30, - SLEEP_RETENTION_MODULE_BT_BB = 31, - SLEEP_RETENTION_MODULE_802154_MAC = 32, - SLEEP_RETENTION_MODULE_POWER = 33, + SLEEP_RETENTION_MODULE_CLOCK_MODEM = 30, + SLEEP_RETENTION_MODULE_BLE_MAC = 31, + SLEEP_RETENTION_MODULE_BT_BB = 32, + SLEEP_RETENTION_MODULE_802154_MAC = 33, + SLEEP_RETENTION_MODULE_POWER = 34, SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; -#define is_top_domain_module(m) ((m) <= SLEEP_RETENTION_MODULE_SDM0) +#define is_top_domain_module(m) ((m) <= SLEEP_RETENTION_MODULE_ASRC) #ifdef __cplusplus } diff --git a/components/soc/esp32s31/include/soc/retention_periph_defs.h b/components/soc/esp32s31/include/soc/retention_periph_defs.h index 747d45f839b..1f18489dd3e 100644 --- a/components/soc/esp32s31/include/soc/retention_periph_defs.h +++ b/components/soc/esp32s31/include/soc/retention_periph_defs.h @@ -79,6 +79,8 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_CLOCK_MODEM = 51, SLEEP_RETENTION_MODULE_MODEM_PHY = 52, + SLEEP_RETENTION_MODULE_ASRC = 53, + SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index e39da11a022..fac17549610 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -70,6 +70,7 @@ extern "C" { #define REGDMA_H264_LINK(_pri) ((0x2A << 8) | _pri) #define REGDMA_PPA_LINK(_pri) ((0x2B << 8) | _pri) #define REGDMA_DMA2D_LINK(_pri) ((0x2C << 8) | _pri) +#define REGDMA_ASRC_LINK(_pri) ((0x2D << 8) | _pri) #define REGDMA_POWER_LINK(_pri) ((0xFD << 8) | _pri) #define REGDMA_CLOCK_ICG_LINK(_pri) ((0xFE << 8) | _pri) @@ -108,6 +109,7 @@ extern "C" { #define REGDMA_LINK_PRI_H264 REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_PPA REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_DMA2D REGDMA_LINK_PRI_GENERAL_PERIPH +#define REGDMA_LINK_PRI_ASRC REGDMA_LINK_PRI_GENERAL_PERIPH typedef enum { REGDMA_LINK_PRI_0 = 0,