Merge branch 'feature/add_asrc_sleep_retention' into 'master'

feat(asrc): Add sleep retention

See merge request espressif/esp-idf!50719
This commit is contained in:
Ma Jing Jing
2026-07-20 02:00:19 +00:00
15 changed files with 263 additions and 10 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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 */

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#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),
},
};

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#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),
},
};

View File

@@ -8,6 +8,7 @@
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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,