feat(asrc): Add sleep retention

This commit is contained in:
Ma Jing Jing
2026-07-16 19:56:17 +08:00
parent f6e69e00fc
commit 93b5dc8eba
15 changed files with 263 additions and 10 deletions
+6 -1
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)
+14
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
+96 -2
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)
@@ -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 */
@@ -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),
},
};
@@ -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),
},
};
@@ -8,6 +8,7 @@
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {