diff --git a/components/esp_driver_dma/CMakeLists.txt b/components/esp_driver_dma/CMakeLists.txt index ec28f99c5c0..2052cc4c7ca 100644 --- a/components/esp_driver_dma/CMakeLists.txt +++ b/components/esp_driver_dma/CMakeLists.txt @@ -37,6 +37,9 @@ endif() if(CONFIG_SOC_DMA2D_SUPPORTED) list(APPEND srcs "src/dma2d.c") + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "${target}/dma2d_retention.c") + endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_dma/esp32p4/dma2d_retention.c b/components/esp_driver_dma/esp32p4/dma2d_retention.c new file mode 100644 index 00000000000..28c42d900de --- /dev/null +++ b/components/esp_driver_dma/esp32p4/dma2d_retention.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "dma2d_priv.h" +#include "soc/dma2d_reg.h" + +/** DMA2D Registers to be saved during sleep retention + * + * DMA2D driver is implemented in the way that channels are used as a pool, + * therefore, only general registers are needed to be saved during sleep retention. + * + * DMA2D_RST_CONF_REG, + * DMA2D_INTR_MEM_START_ADDR_REG, DMA2D_INTR_MEM_END_ADDR_REG, DMA2D_EXTR_MEM_START_ADDR_REG, DMA2D_EXTR_MEM_END_ADDR_REG + * DMA2D_OUT_ARB_CONFIG_REG, DMA2D_IN_ARB_CONFIG_REG + */ +#define DMA2D_RETENTION_REGS_CNT 7 +#define DMA2D_RETENTION_REGS_BASE DMA2D_RST_CONF_REG +static const uint32_t dma2d_regs_map[4] = {0x7f, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t dma2d_regdma_entries[] = { + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_DMA2D_LINK(0x00), + DMA2D_RETENTION_REGS_BASE, DMA2D_RETENTION_REGS_BASE, + DMA2D_RETENTION_REGS_CNT, 0, 0, + dma2d_regs_map[0], dma2d_regs_map[1], + dma2d_regs_map[2], dma2d_regs_map[3]), + .owner = ENTRY(0), + }, +}; + +const dma2d_retention_desc_t dma2d_reg_retention_info = { + .module = SLEEP_RETENTION_MODULE_DMA2D, + .regdma_entry_array = dma2d_regdma_entries, + .array_size = ARRAY_SIZE(dma2d_regdma_entries), +}; diff --git a/components/esp_driver_dma/esp32s31/dma2d_retention.c b/components/esp_driver_dma/esp32s31/dma2d_retention.c new file mode 100644 index 00000000000..22bb7ac6475 --- /dev/null +++ b/components/esp_driver_dma/esp32s31/dma2d_retention.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "dma2d_priv.h" +#include "soc/dma2d_reg.h" + +/** DMA2D Registers to be saved during sleep retention + * + * DMA2D driver is implemented in the way that channels are used as a pool, + * therefore, only general registers are needed to be saved during sleep retention. + * + * DMA2D_RST_CONF_REG, + * DMA2D_INTR_MEM_START_ADDR_REG, DMA2D_INTR_MEM_END_ADDR_REG, DMA2D_EXTR_MEM_START_ADDR_REG, DMA2D_EXTR_MEM_END_ADDR_REG + * DMA2D_OUT_ARB_CONFIG_REG, DMA2D_IN_ARB_CONFIG_REG + */ +#define DMA2D_RETENTION_REGS_CNT 7 +#define DMA2D_RETENTION_REGS_BASE DMA2D_RST_CONF_REG +static const uint32_t dma2d_regs_map[4] = {0x7f, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t dma2d_regdma_entries[] = { + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_DMA2D_LINK(0x00), + DMA2D_RETENTION_REGS_BASE, DMA2D_RETENTION_REGS_BASE, + DMA2D_RETENTION_REGS_CNT, 0, 0, + dma2d_regs_map[0], dma2d_regs_map[1], + dma2d_regs_map[2], dma2d_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const dma2d_retention_desc_t dma2d_reg_retention_info = { + .module = SLEEP_RETENTION_MODULE_DMA2D, + .regdma_entry_array = dma2d_regdma_entries, + .array_size = ARRAY_SIZE(dma2d_regdma_entries), +}; diff --git a/components/esp_driver_dma/src/dma2d.c b/components/esp_driver_dma/src/dma2d.c index a6733dd47cf..de0f402e2bb 100644 --- a/components/esp_driver_dma/src/dma2d.c +++ b/components/esp_driver_dma/src/dma2d.c @@ -27,6 +27,7 @@ #include "soc/soc_caps.h" #include "esp_bit_defs.h" #include "esp_efuse.h" +#include "esp_private/sleep_retention.h" /** * The 2D-DMA driver is designed with a pool & client model + queue design pattern. @@ -351,6 +352,17 @@ static void dma2d_default_isr(void *args) } } +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +static esp_err_t dma2d_create_sleep_retention_link_cb(void *arg) +{ + sleep_retention_module_t module = dma2d_reg_retention_info.module; + esp_err_t err = sleep_retention_entries_create(dma2d_reg_retention_info.regdma_entry_array, + dma2d_reg_retention_info.array_size, + REGDMA_LINK_PRI_DMA2D, module); + return err; +} +#endif + esp_err_t dma2d_acquire_pool(const dma2d_pool_config_t *config, dma2d_pool_handle_t *ret_pool) { esp_err_t ret = ESP_OK; @@ -404,6 +416,34 @@ esp_err_t dma2d_acquire_pool(const dma2d_pool_config_t *config, dma2d_pool_handl dma2d_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context // Enable 2D-DMA module clock dma2d_ll_hw_enable(s_platform.groups[group_id]->hal.dev, true); + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + // acquire sleep retention + sleep_retention_module_t module = dma2d_reg_retention_info.module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = dma2d_create_sleep_retention_link_cb, + .arg = NULL, + }, + }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) + }; + if (sleep_retention_module_init(module, &init_param) != ESP_OK) { + // even though the sleep retention module init failed, DMA2D driver should still work, so just warning here + ESP_LOGW(TAG, "init sleep retention failed, power domain may be turned off during sleep"); + } else { + if (sleep_retention_module_allocate(module) != ESP_OK) { + ESP_LOGW(TAG, "fail to allocate retention link list"); + // don't call sleep_retention_module_deinit here, otherwise DMA2D peripheral 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 } else { ret = ESP_ERR_NO_MEM; free(pre_alloc_tx_channels); @@ -494,6 +534,19 @@ esp_err_t dma2d_release_pool(dma2d_pool_handle_t dma2d_pool) PERIPH_RCC_ATOMIC() { dma2d_ll_enable_bus_clock(group_id, false); } + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + sleep_retention_module_t module = dma2d_reg_retention_info.module; + if (sleep_retention_is_module_attached(module)) { + sleep_retention_module_detach(module); + } + if (sleep_retention_is_module_created(module)) { + sleep_retention_module_free(module); + } + if (sleep_retention_is_module_inited(module)) { + sleep_retention_module_deinit(module); + } +#endif } if (do_deinitialize) { diff --git a/components/esp_driver_dma/src/dma2d_priv.h b/components/esp_driver_dma/src/dma2d_priv.h index c34071127f6..20c8ca9e99f 100644 --- a/components/esp_driver_dma/src/dma2d_priv.h +++ b/components/esp_driver_dma/src/dma2d_priv.h @@ -16,6 +16,7 @@ #include "hal/dma2d_hal.h" #include "hal/dma2d_ll.h" #include "esp_private/dma2d.h" +#include "soc/regdma.h" #ifdef __cplusplus extern "C" { @@ -89,6 +90,18 @@ struct dma2d_rx_channel_t { uint32_t bundled_tx_channel_mask; // Bit mask indicating the TX channels together with the RX channel to do the transaction }; +#if SOC_PAU_SUPPORTED +#include "soc/retention_periph_defs.h" + +typedef struct { + const periph_retention_module_t module; + const regdma_entries_config_t *regdma_entry_array; + uint32_t array_size; +} dma2d_retention_desc_t; + +extern const dma2d_retention_desc_t dma2d_reg_retention_info; +#endif // SOC_PAU_SUPPORTED + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_dma/test_apps/dma2d/main/test_dma2d.c b/components/esp_driver_dma/test_apps/dma2d/main/test_dma2d.c index 27b1ef060b3..40f0be1a69e 100644 --- a/components/esp_driver_dma/test_apps/dma2d/main/test_dma2d.c +++ b/components/esp_driver_dma/test_apps/dma2d/main/test_dma2d.c @@ -732,3 +732,7 @@ TEST_CASE("DMA2D_M2M_2D_window", "[DMA2D]") TEST_ESP_OK(dma2d_m2m_deinit()); } + +// Note: +// Sleep retention functionality test is not covered yet, since all basic test cases can survive with 2D-DMA registers reset to default value. +// It shall be tested when arbiter feature is supported and its test case is added. diff --git a/components/esp_driver_ppa/CMakeLists.txt b/components/esp_driver_ppa/CMakeLists.txt index 5933dc04792..9666d600cfd 100644 --- a/components/esp_driver_ppa/CMakeLists.txt +++ b/components/esp_driver_ppa/CMakeLists.txt @@ -7,6 +7,9 @@ if(CONFIG_SOC_PPA_SUPPORTED) "src/ppa_srm.c" "src/ppa_blend.c" "src/ppa_fill.c") + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "${target}/ppa_retention.c") + endif() endif() if(${target} STREQUAL "linux") @@ -18,6 +21,7 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${public_include} + PRIV_INCLUDE_DIRS "src" REQUIRES esp_hal_ppa PRIV_REQUIRES "${priv_requires}" ) diff --git a/components/esp_driver_ppa/esp32p4/ppa_retention.c b/components/esp_driver_ppa/esp32p4/ppa_retention.c new file mode 100644 index 00000000000..fc21ad0ccc0 --- /dev/null +++ b/components/esp_driver_ppa/esp32p4/ppa_retention.c @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ppa_priv.h" +#include "soc/ppa_reg.h" + +/** PPA Registers to be saved during sleep retention + * + * For CLUT mem, we shall choose not to power down the CLUT mem domain; Otherwise, there are two blocks of memory that needs to be restored by writing to mem addr directly. + * Since PPA driver is operation based, most of the registers are configured per operation, so no need to save/restore. + * + * PPA_CLUT_CONF_REG, + * PPA_INT_ENA_REG, + * PPA_SR_MEM_PD_REG, PPA_REG_CONF_REG, PPA_SRAM_CTRL_REG, + * PPA_RGB2GRAY_REG (> rev3 only, but ok to include it for < rev3 chips) + */ +#define PPA_RETENTION_REGS_CNT 6 +#define PPA_RETENTION_REGS_BASE PPA_CLUT_CONF_REG +static const uint32_t ppa_regs_map[4] = {0x1800009, 0x9, 0x0, 0x0}; +static const regdma_entries_config_t ppa_regdma_entries[] = { + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PPA_LINK(0x00), + PPA_RETENTION_REGS_BASE, PPA_RETENTION_REGS_BASE, + PPA_RETENTION_REGS_CNT, 0, 0, + ppa_regs_map[0], ppa_regs_map[1], + ppa_regs_map[2], ppa_regs_map[3]), + .owner = ENTRY(0), + }, +}; + +const ppa_retention_desc_t ppa_reg_retention_info = { + .module = SLEEP_RETENTION_MODULE_PPA, + .regdma_entry_array = ppa_regdma_entries, + .array_size = ARRAY_SIZE(ppa_regdma_entries), +}; diff --git a/components/esp_driver_ppa/esp32s31/ppa_retention.c b/components/esp_driver_ppa/esp32s31/ppa_retention.c new file mode 100644 index 00000000000..784e681b632 --- /dev/null +++ b/components/esp_driver_ppa/esp32s31/ppa_retention.c @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ppa_priv.h" +#include "soc/ppa_reg.h" + +/** PPA Registers to be saved during sleep retention + * + * For CLUT mem, we shall choose not to power down the CLUT mem domain; Otherwise, there are two blocks of memory that needs to be restored by writing to mem addr directly. + * Since PPA driver is operation based, most of the registers are configured per operation, so no need to save/restore. + * + * PPA_CLUT_CONF_REG, + * PPA_INT_ENA_REG, + * PPA_SR_MEM_PD_REG, PPA_REG_CONF_REG, PPA_SRAM_CTRL_REG, + * PPA_RGB2GRAY_REG + */ +#define PPA_RETENTION_REGS_CNT 6 +#define PPA_RETENTION_REGS_BASE PPA_CLUT_CONF_REG +static const uint32_t ppa_regs_map[4] = {0x1800009, 0x9, 0x0, 0x0}; +static const regdma_entries_config_t ppa_regdma_entries[] = { + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PPA_LINK(0x00), + PPA_RETENTION_REGS_BASE, PPA_RETENTION_REGS_BASE, + PPA_RETENTION_REGS_CNT, 0, 0, + ppa_regs_map[0], ppa_regs_map[1], + ppa_regs_map[2], ppa_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const ppa_retention_desc_t ppa_reg_retention_info = { + .module = SLEEP_RETENTION_MODULE_PPA, + .regdma_entry_array = ppa_regdma_entries, + .array_size = ARRAY_SIZE(ppa_regdma_entries), +}; diff --git a/components/esp_driver_ppa/include/driver/ppa.h b/components/esp_driver_ppa/include/driver/ppa.h index 9eacd523bae..e16fa7f852f 100644 --- a/components/esp_driver_ppa/include/driver/ppa.h +++ b/components/esp_driver_ppa/include/driver/ppa.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -39,6 +39,11 @@ typedef struct { ppa_data_burst_length_t data_burst_length; /*!< The desired data burst length for all the transactions of the client. Use a small burst length will decrease PPA performance, but can save burst bandwidth for other peripheral usages. By default, it will be at the maximum burst length, `PPA_DATA_BURST_LENGTH_128` */ + struct { + uint32_t allow_pd: 1; /*!< If set, driver allows the power domain to be powered off when system enters sleep mode. + This can save power, but at the expense of more RAM being consumed to save register context. + All clients must have the same value for this flag. */ + } flags; /*!< Configuration flags */ } ppa_client_config_t; /** diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index 49289c22dc3..ec6f1e82cc7 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -34,6 +34,7 @@ #include "hal/color_hal.h" #include "hal/color_types.h" #include "esp_private/periph_ctrl.h" +#include "esp_private/sleep_retention.h" #include "esp_efuse.h" #include "soc/soc_caps.h" @@ -55,6 +56,25 @@ const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATI [PPA_OPERATION_FILL] = ppa_fill_transaction_on_picked, }; +/** PPA Power Management Strategy + * + * SRM and Blending have separate CPU_FREQ_MAX PM locks, and each PM lock is acquired whenever there is PPA operation in process. + * + * When no PPA operation is in process, sleep can happen, and PPA domain can be powered down if allow_pd is set. + * Necessary register context will be saved and restored by sleep retention. + */ + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +static esp_err_t ppa_create_sleep_retention_link_cb(void *arg) +{ + sleep_retention_module_t module = ppa_reg_retention_info.module; + esp_err_t err = sleep_retention_entries_create(ppa_reg_retention_info.regdma_entry_array, + ppa_reg_retention_info.array_size, + REGDMA_LINK_PRI_PPA, module); + return err; +} +#endif + static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine) { esp_err_t ret = ESP_OK; @@ -187,6 +207,40 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin ESP_LOGE(TAG, "install 2D-DMA failed"); goto wrap_up; } + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + s_platform.flags.allow_pd = config->flags.allow_pd; // Uses the first acquired engine's allow_pd flag as the platform's allow_pd flag + + // Initialize sleep retention module for PPA + sleep_retention_module_t module = ppa_reg_retention_info.module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = ppa_create_sleep_retention_link_cb, + .arg = NULL, + }, + }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) + }; + if (sleep_retention_module_init(module, &init_param) != ESP_OK) { + // even though the sleep retention module init failed, PPA driver should still work, so just warning here + ESP_LOGW(TAG, "init sleep retention failed, power domain may be turned off during sleep"); + } else if (s_platform.flags.allow_pd) { + if (sleep_retention_module_allocate(module) != ESP_OK) { + ESP_LOGW(TAG, "fail to allocate retention link list"); + // don't call sleep_retention_module_deinit here, otherwise PPA peripheral 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"); + } + } + } + } else { + if (s_platform.flags.allow_pd != config->flags.allow_pd) { + ESP_LOGW(TAG, "allow_pd flag mismatch among clients, will follow flags.allow_pd = %d", s_platform.flags.allow_pd); + } +#endif } } wrap_up: @@ -247,6 +301,20 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine) if (!s_platform.srm && !s_platform.blending) { assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0); +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + sleep_retention_module_t module = ppa_reg_retention_info.module; + if (sleep_retention_is_module_attached(module)) { + sleep_retention_module_detach(module); + } + if (sleep_retention_is_module_created(module)) { + sleep_retention_module_free(module); + } + if (sleep_retention_is_module_inited(module)) { + sleep_retention_module_deinit(module); + } + s_platform.flags.allow_pd = false; +#endif + if (s_platform.dma2d_pool_handle) { dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log? s_platform.dma2d_pool_handle = NULL; @@ -288,11 +356,13 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand if (config->oper_type == PPA_OPERATION_SRM) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_SRM, + .flags.allow_pd = config->flags.allow_pd, }; ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire SRM engine"); } else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) { ppa_engine_config_t engine_config = { .engine = PPA_ENGINE_TYPE_BLEND, + .flags.allow_pd = config->flags.allow_pd, }; ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire Blending engine"); } diff --git a/components/esp_driver_ppa/src/ppa_priv.h b/components/esp_driver_ppa/src/ppa_priv.h index d6ff470c8af..acabbebd7d7 100644 --- a/components/esp_driver_ppa/src/ppa_priv.h +++ b/components/esp_driver_ppa/src/ppa_priv.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,8 @@ #include "hal/ppa_types.h" #include "hal/ppa_hal.h" #include "esp_pm.h" +#include "soc/soc_caps.h" +#include "soc/regdma.h" #ifdef __cplusplus extern "C" { @@ -81,6 +83,9 @@ typedef struct ppa_blend_engine_t { typedef struct { ppa_engine_type_t engine; // Engine type + struct { + uint32_t allow_pd: 1; // If set, engine allows the power domain to be powered off when system enters sleep mode + } flags; // Configuration flags } ppa_engine_config_t; /******************************** CLIENT *************************************/ @@ -247,8 +252,22 @@ struct ppa_platform_t { size_t int_mem_align; // Alignment requirement for the internal outgoing buffer to satisfy cache line size size_t ext_mem_align; // Alignment requirement for the external outgoing buffer to satisfy cache line size uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size + struct { + uint32_t allow_pd: 1; // If set, driver allows the power domain to be powered off when system enters sleep mode + } flags; // Configuration flags }; +#if SOC_PAU_SUPPORTED +#include "soc/retention_periph_defs.h" +typedef struct { + const periph_retention_module_t module; + const regdma_entries_config_t *regdma_entry_array; + uint32_t array_size; +} ppa_retention_desc_t; + +extern const ppa_retention_desc_t ppa_reg_retention_info; +#endif // SOC_PAU_SUPPORTED + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_ppa/test_apps/main/CMakeLists.txt b/components/esp_driver_ppa/test_apps/main/CMakeLists.txt index 7bfd522225f..1996c2278f7 100644 --- a/components/esp_driver_ppa/test_apps/main/CMakeLists.txt +++ b/components/esp_driver_ppa/test_apps/main/CMakeLists.txt @@ -5,5 +5,5 @@ set(srcs "test_app_main.c" # the component can be registered as WHOLE_ARCHIVE idf_component_register(SRCS ${srcs} INCLUDE_DIRS "." - PRIV_REQUIRES esp_driver_ppa esp_psram unity esp_mm efuse + PRIV_REQUIRES esp_driver_ppa esp_psram unity esp_mm efuse esp_pm WHOLE_ARCHIVE) diff --git a/components/esp_driver_ppa/test_apps/main/test_ppa.cpp b/components/esp_driver_ppa/test_apps/main/test_ppa.cpp index 77a7a01555e..5ab02567ff8 100644 --- a/components/esp_driver_ppa/test_apps/main/test_ppa.cpp +++ b/components/esp_driver_ppa/test_apps/main/test_ppa.cpp @@ -20,6 +20,12 @@ #include "ppa_performance.h" #include "esp_random.h" #include "esp_efuse.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "esp_pm.h" +#include "esp_clk_tree.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -238,7 +244,20 @@ TEST_CASE("ppa_pending_transactions_in_queue", "[PPA]") free(buf_2); } -TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]") +static __attribute__((unused)) void enable_pm_strategy(bool auto_light_sleep) +{ + uint32_t xtal_hz = 0; + esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &xtal_hz); + esp_pm_config_t pm_config = {}; + pm_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; + pm_config.min_freq_mhz = static_cast(xtal_hz / 1000000); +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + pm_config.light_sleep_enable = auto_light_sleep; +#endif + TEST_ESP_OK(esp_pm_configure(&pm_config)); +} + +static void ppa_srm_basic_data_correctness_check(bool auto_light_sleep) { const uint32_t w = 4; const uint32_t h = 4; @@ -276,86 +295,138 @@ TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]") 0x0000, 0x0000, 0x0000, 0x0000 }; +#if CONFIG_PM_ENABLE + enable_pm_strategy(auto_light_sleep); +#endif + ppa_client_handle_t ppa_client_handle; ppa_client_config_t ppa_client_config = {}; ppa_client_config.oper_type = PPA_OPERATION_SRM; ppa_client_config.max_pending_trans_num = 1; +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false); +#endif TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); - ppa_srm_oper_config_t oper_config = {}; - oper_config.in.buffer = in_buf; - oper_config.in.pic_w = w; - oper_config.in.pic_h = h; - oper_config.in.block_w = block_w; - oper_config.in.block_h = block_h; - oper_config.in.block_offset_x = in_block_offset_x; - oper_config.in.block_offset_y = in_block_offset_y; - oper_config.in.srm_cm = cm; - oper_config.out.buffer = out_buf; - oper_config.out.buffer_size = out_buf_size; - oper_config.out.pic_w = w; - oper_config.out.pic_h = h; - oper_config.out.block_offset_x = out_block_offset_x; - oper_config.out.block_offset_y = out_block_offset_y; - oper_config.out.srm_cm = cm; - oper_config.rotation_angle = rotation; - oper_config.scale_x = scale_x; - oper_config.scale_y = scale_y; - oper_config.rgb_swap = 0; - oper_config.byte_swap = 0; - oper_config.mode = PPA_TRANS_MODE_BLOCKING; +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); +#endif - TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); + // loop 3 times if we are testing PPA sleep retention feature + int cnt = (auto_light_sleep ? 3 : 1); + for (int i = 0; i < cnt; i++) { +#if SOC_LIGHT_SLEEP_SUPPORTED + sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration + sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration - // Check result - for (int i = 0; i < buf_len; i++) { - if (i % 8 == 0) { - printf("\n"); + if (auto_light_sleep) { + vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature + + // Check if the sleep happened as expected +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP); +#endif + TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result); } - printf("0x%02X ", out_buf[i]); - } - printf("\n"); - TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len); +#endif + + ppa_srm_oper_config_t oper_config = {}; + oper_config.in.buffer = in_buf; + oper_config.in.pic_w = w; + oper_config.in.pic_h = h; + oper_config.in.block_w = block_w; + oper_config.in.block_h = block_h; + oper_config.in.block_offset_x = in_block_offset_x; + oper_config.in.block_offset_y = in_block_offset_y; + oper_config.in.srm_cm = cm; + oper_config.out.buffer = out_buf; + oper_config.out.buffer_size = out_buf_size; + oper_config.out.pic_w = w; + oper_config.out.pic_h = h; + oper_config.out.block_offset_x = out_block_offset_x; + oper_config.out.block_offset_y = out_block_offset_y; + oper_config.out.srm_cm = cm; + oper_config.rotation_angle = rotation; + oper_config.scale_x = scale_x; + oper_config.scale_y = scale_y; + oper_config.rgb_swap = 0; + oper_config.byte_swap = 0; + oper_config.mode = PPA_TRANS_MODE_BLOCKING; + + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); + + // Check result + for (int i = 0; i < buf_len; i++) { + if (i % 8 == 0) { + printf("\n"); + } + printf("0x%02X ", out_buf[i]); + } + printf("\n"); + TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len); #if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) - // Test a rgb2gray color conversion - memset(out_buf, 0, out_buf_size); - esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + // Test a rgb2gray color conversion + memset(out_buf, 0, out_buf_size); + esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - const uint8_t r_weight = 100; - const uint8_t g_weight = 56; - const uint8_t b_weight = 100; - TEST_ESP_OK(ppa_set_rgb2gray_formula(r_weight, g_weight, b_weight)); - oper_config.out.srm_cm = PPA_SRM_COLOR_MODE_GRAY8; - oper_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0; - uint8_t out_buf_expected_gray[16] = {}; - for (int i = 0; i < block_w * block_h; i++) { - const uint16_t pix = in_buf[(i / block_w + in_block_offset_y) * w + (i % block_w + in_block_offset_x)]; - uint8_t _r = ((pix >> 8) & 0xF8); - uint8_t _g = ((pix >> 3) & 0xFC); - uint8_t _b = ((pix << 3) & 0xF8); - out_buf_expected_gray[(i / block_w + out_block_offset_y) * w + (i % block_w + out_block_offset_x)] = (_r * r_weight + _g * g_weight + _b * b_weight) >> 8; - } - - TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); - - // Check result - for (int i = 0; i < w * h; i++) { - if (i % 4 == 0) { - printf("\n"); + const uint8_t r_weight = 100; + const uint8_t g_weight = 56; + const uint8_t b_weight = 100; + TEST_ESP_OK(ppa_set_rgb2gray_formula(r_weight, g_weight, b_weight)); + oper_config.out.srm_cm = PPA_SRM_COLOR_MODE_GRAY8; + oper_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0; + uint8_t out_buf_expected_gray[16] = {}; + for (int i = 0; i < block_w * block_h; i++) { + const uint16_t pix = in_buf[(i / block_w + in_block_offset_y) * w + (i % block_w + in_block_offset_x)]; + uint8_t _r = ((pix >> 8) & 0xF8); + uint8_t _g = ((pix >> 3) & 0xFC); + uint8_t _b = ((pix << 3) & 0xF8); + out_buf_expected_gray[(i / block_w + out_block_offset_y) * w + (i % block_w + out_block_offset_x)] = (_r * r_weight + _g * g_weight + _b * b_weight) >> 8; } - printf("0x%02X ", out_buf[i]); - } - printf("\n"); - TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected_gray, (void *)out_buf, w * h); + + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); + + // Check result + for (int i = 0; i < w * h; i++) { + if (i % 4 == 0) { + printf("\n"); + } + printf("0x%02X ", out_buf[i]); + } + printf("\n"); + TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected_gray, (void *)out_buf, w * h); #endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) + memset(out_buf, 0, out_buf_size); + esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + } +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_set_sleep_context(NULL); +#endif + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); +#if CONFIG_PM_ENABLE + enable_pm_strategy(false); +#endif + free(out_buf); } -TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") +TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]") +{ + ppa_srm_basic_data_correctness_check(false); +#if CONFIG_PM_ENABLE +#if !(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_FPU_RETENTION_BY_SW) // SRM uses FPU + printf("\nTest again with auto light sleep...\n"); + ppa_srm_basic_data_correctness_check(true); +#endif +#endif +} + +static void ppa_blend_basic_data_correctness_check(bool auto_light_sleep) { const uint32_t w = 2; const uint32_t h = 2; @@ -382,7 +453,7 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") 0xFF, 0xFF, 0xFF, /**/ 0x80, 0x40, 0xA0, /**/ // /*************************/ }; - uint8_t in_fg_buf[64] __attribute__((aligned(64))) = { + const uint8_t in_fg_buf_template[64] __attribute__((aligned(64))) = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // /*******************************/ 0xFF, 0xFF, 0xFF, 0xFF, /**/ 0x00, 0x80, 0x80, 0xC0, /**/ @@ -390,6 +461,10 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") // /*******************************/ // remaining bytes [16 ... 63] are value-initialized to 0 }; + uint8_t *in_fg_buf = static_cast(heap_caps_aligned_calloc(4, 64, sizeof(uint8_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA)); + TEST_ASSERT_NOT_NULL(in_fg_buf); + memcpy(in_fg_buf, in_fg_buf_template, 64); + esp_cache_msync((void *)in_fg_buf, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M); uint8_t *out_buf = in_fg_buf; // Expected blend output // Alpha Blending calculation: @@ -405,54 +480,91 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") // /*******************************/ }; +#if CONFIG_PM_ENABLE + enable_pm_strategy(auto_light_sleep); +#endif + ppa_client_handle_t ppa_client_handle; ppa_client_config_t ppa_client_config = {}; ppa_client_config.oper_type = PPA_OPERATION_BLEND; ppa_client_config.max_pending_trans_num = 1; +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false); +#endif TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); - ppa_blend_oper_config_t oper_config = {}; - oper_config.in_bg.buffer = in_bg_buf; - oper_config.in_bg.pic_w = w; - oper_config.in_bg.pic_h = h; - oper_config.in_bg.block_w = block_w; - oper_config.in_bg.block_h = block_h; - oper_config.in_bg.block_offset_x = block_offset_x; - oper_config.in_bg.block_offset_y = block_offset_y; - oper_config.in_bg.blend_cm = in_bg_cm; - oper_config.in_fg.buffer = in_fg_buf; - oper_config.in_fg.pic_w = w; - oper_config.in_fg.pic_h = h; - oper_config.in_fg.block_w = block_w; - oper_config.in_fg.block_h = block_h; - oper_config.in_fg.block_offset_x = block_offset_x; - oper_config.in_fg.block_offset_y = block_offset_y; - oper_config.in_fg.blend_cm = in_fg_cm; - oper_config.out.buffer = out_buf; - oper_config.out.buffer_size = out_buf_size; - oper_config.out.pic_w = w; - oper_config.out.pic_h = h; - oper_config.out.block_offset_x = block_offset_x; - oper_config.out.block_offset_y = block_offset_y; - oper_config.out.blend_cm = out_cm; - oper_config.bg_alpha_update_mode = PPA_ALPHA_SCALE; - oper_config.bg_alpha_scale_ratio = bg_alpha_scale_ratio; - oper_config.fg_alpha_update_mode = PPA_ALPHA_INVERT; - oper_config.bg_ck_en = false; - oper_config.fg_ck_en = false; - oper_config.mode = PPA_TRANS_MODE_BLOCKING; +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); +#endif - TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config)); + // loop 3 times if we are testing PPA sleep retention feature + int cnt = (auto_light_sleep ? 3 : 1); + for (int i = 0; i < cnt; i++) { +#if SOC_LIGHT_SLEEP_SUPPORTED + sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration + sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration - // Check result - for (int i = 0; i < out_buf_len; i++) { - if (i % 8 == 0) { - printf("\n"); + if (auto_light_sleep) { + vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature + + // Check if the sleep happened as expected +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP); +#endif + TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result); } - printf("0x%02X ", out_buf[i]); +#endif + + ppa_blend_oper_config_t oper_config = {}; + oper_config.in_bg.buffer = in_bg_buf; + oper_config.in_bg.pic_w = w; + oper_config.in_bg.pic_h = h; + oper_config.in_bg.block_w = block_w; + oper_config.in_bg.block_h = block_h; + oper_config.in_bg.block_offset_x = block_offset_x; + oper_config.in_bg.block_offset_y = block_offset_y; + oper_config.in_bg.blend_cm = in_bg_cm; + oper_config.in_fg.buffer = in_fg_buf; + oper_config.in_fg.pic_w = w; + oper_config.in_fg.pic_h = h; + oper_config.in_fg.block_w = block_w; + oper_config.in_fg.block_h = block_h; + oper_config.in_fg.block_offset_x = block_offset_x; + oper_config.in_fg.block_offset_y = block_offset_y; + oper_config.in_fg.blend_cm = in_fg_cm; + oper_config.out.buffer = out_buf; + oper_config.out.buffer_size = out_buf_size; + oper_config.out.pic_w = w; + oper_config.out.pic_h = h; + oper_config.out.block_offset_x = block_offset_x; + oper_config.out.block_offset_y = block_offset_y; + oper_config.out.blend_cm = out_cm; + oper_config.bg_alpha_update_mode = PPA_ALPHA_SCALE; + oper_config.bg_alpha_scale_ratio = bg_alpha_scale_ratio; + oper_config.fg_alpha_update_mode = PPA_ALPHA_INVERT; + oper_config.bg_ck_en = false; + oper_config.fg_ck_en = false; + oper_config.mode = PPA_TRANS_MODE_BLOCKING; + + TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config)); + + // Check result + for (int i = 0; i < out_buf_len; i++) { + if (i % 8 == 0) { + printf("\n"); + } + printf("0x%02X ", out_buf[i]); + } + printf("\n"); + TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len); + + memcpy(in_fg_buf, in_fg_buf_template, 64); + esp_cache_msync((void *)in_fg_buf, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } - printf("\n"); - TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len); +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_set_sleep_context(NULL); +#endif #if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) // Test YUV422/YUV420 blend @@ -533,9 +645,26 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") #endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); + +#if CONFIG_PM_ENABLE + enable_pm_strategy(false); +#endif + + free(in_fg_buf); } -TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]") +TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]") +{ + ppa_blend_basic_data_correctness_check(false); +#if CONFIG_PM_ENABLE +#if !(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_FPU_RETENTION_BY_SW) // Blend uses FPU + printf("\nTest again with auto light sleep...\n"); + ppa_blend_basic_data_correctness_check(true); +#endif +#endif +} + +static void ppa_fill_basic_data_correctness_check(bool auto_light_sleep) { const uint32_t w = 80; const uint32_t h = 120; @@ -558,55 +687,105 @@ TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]") memset(out_buf, 0xFF, out_buf_len); +#if CONFIG_PM_ENABLE + enable_pm_strategy(auto_light_sleep); +#endif + ppa_client_handle_t ppa_client_handle; ppa_client_config_t ppa_client_config = {}; ppa_client_config.oper_type = PPA_OPERATION_FILL; ppa_client_config.max_pending_trans_num = 1; +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false); +#endif TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); - ppa_fill_oper_config_t oper_config = {}; - oper_config.out.buffer = out_buf; - oper_config.out.buffer_size = out_buf_size; - oper_config.out.pic_w = w; - oper_config.out.pic_h = h; - oper_config.out.block_offset_x = block_offset_x; - oper_config.out.block_offset_y = block_offset_y; - oper_config.out.fill_cm = out_cm; - oper_config.fill_block_w = block_w; - oper_config.fill_block_h = block_h; - oper_config.fill_argb_color = fill_color; - oper_config.mode = PPA_TRANS_MODE_BLOCKING; +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); +#endif - TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config)); + // loop 3 times if we are testing PPA sleep retention feature + int cnt = (auto_light_sleep ? 3 : 1); + for (int i = 0; i < cnt; i++) { +#if SOC_LIGHT_SLEEP_SUPPORTED + sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration + sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration - // Check result - color_pixel_rgb565_data_t fill_pixel_expected = {}; - fill_pixel_expected.r = fill_color.r >> 3; - fill_pixel_expected.g = fill_color.g >> 2; - fill_pixel_expected.b = fill_color.b >> 3; - TEST_ASSERT_EACH_EQUAL_UINT16(fill_pixel_expected.val, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h); + if (auto_light_sleep) { + vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature + + // Check if the sleep happened as expected +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP); +#endif + TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result); + } +#endif + + ppa_fill_oper_config_t oper_config = {}; + oper_config.out.buffer = out_buf; + oper_config.out.buffer_size = out_buf_size; + oper_config.out.pic_w = w; + oper_config.out.pic_h = h; + oper_config.out.block_offset_x = block_offset_x; + oper_config.out.block_offset_y = block_offset_y; + oper_config.out.fill_cm = out_cm; + oper_config.fill_block_w = block_w; + oper_config.fill_block_h = block_h; + oper_config.fill_argb_color = fill_color; + oper_config.mode = PPA_TRANS_MODE_BLOCKING; + + TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config)); + + // Check result + color_pixel_rgb565_data_t fill_pixel_expected = {}; + fill_pixel_expected.r = fill_color.r >> 3; + fill_pixel_expected.g = fill_color.g >> 2; + fill_pixel_expected.b = fill_color.b >> 3; + TEST_ASSERT_EACH_EQUAL_UINT16(fill_pixel_expected.val, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h); #if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) - // Test a yuv color fill - oper_config.out.fill_cm = PPA_FILL_COLOR_MODE_YUV422_UYVY; // output YUV422 is with UYVY packed order - color_macroblock_yuv_data_t fill_yuv_color = {}; - fill_yuv_color.y = 0xFF; - fill_yuv_color.u = 0x55; - fill_yuv_color.v = 0xAA; - oper_config.fill_yuv_color = fill_yuv_color; - out_pixel_depth = color_hal_pixel_format_fourcc_get_bit_depth((esp_color_fourcc_t)PPA_FILL_COLOR_MODE_YUV422_UYVY); // bits - TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config)); + // Test a yuv color fill + oper_config.out.fill_cm = PPA_FILL_COLOR_MODE_YUV422_UYVY; // output YUV422 is with UYVY packed order + color_macroblock_yuv_data_t fill_yuv_color = {}; + fill_yuv_color.y = 0xFF; + fill_yuv_color.u = 0x55; + fill_yuv_color.v = 0xAA; + oper_config.fill_yuv_color = fill_yuv_color; + out_pixel_depth = color_hal_pixel_format_fourcc_get_bit_depth((esp_color_fourcc_t)PPA_FILL_COLOR_MODE_YUV422_UYVY); // bits + TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config)); - // Check result (2 pixels per macro pixel) - const uint32_t fill_pixel_expected_yuv422 = ((fill_yuv_color.y << 24) | (fill_yuv_color.v << 16) | (fill_yuv_color.y << 8) | (fill_yuv_color.u)); - TEST_ASSERT_EACH_EQUAL_UINT32(fill_pixel_expected_yuv422, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h / 2); + // Check result (2 pixels per macro pixel) + const uint32_t fill_pixel_expected_yuv422 = ((fill_yuv_color.y << 24) | (fill_yuv_color.v << 16) | (fill_yuv_color.y << 8) | (fill_yuv_color.u)); + TEST_ASSERT_EACH_EQUAL_UINT32(fill_pixel_expected_yuv422, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h / 2); #endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3) + memset(out_buf, 0xFF, out_buf_size); + esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + } +#if SOC_LIGHT_SLEEP_SUPPORTED + esp_sleep_set_sleep_context(NULL); +#endif + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); +#if CONFIG_PM_ENABLE + enable_pm_strategy(false); +#endif + free(out_buf); } +TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]") +{ + ppa_fill_basic_data_correctness_check(false); +#if CONFIG_PM_ENABLE + printf("\nTest again with auto light sleep...\n"); + ppa_fill_basic_data_correctness_check(true); +#endif +} + /* All performance tests are tested under the following situations: * - Testing PPA speed where in_buffer(s) and out_buffer all located in PSRAM * - Only 2D-DMA is using PSRAM diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.ci.esp32p4_rev1 b/components/esp_driver_ppa/test_apps/sdkconfig.ci.esp32p4_rev1 index 5f803a25ebf..7d83dbd6fce 100644 --- a/components/esp_driver_ppa/test_apps/sdkconfig.ci.esp32p4_rev1 +++ b/components/esp_driver_ppa/test_apps/sdkconfig.ci.esp32p4_rev1 @@ -4,6 +4,7 @@ CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y CONFIG_PM_ENABLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_PM_DFS_INIT_AUTO=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.ci.release b/components/esp_driver_ppa/test_apps/sdkconfig.ci.release index 199b0cf97c3..b9a047d536e 100644 --- a/components/esp_driver_ppa/test_apps/sdkconfig.ci.release +++ b/components/esp_driver_ppa/test_apps/sdkconfig.ci.release @@ -1,6 +1,7 @@ CONFIG_PM_ENABLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_PM_DFS_INIT_AUTO=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_ppa/test_apps/sdkconfig.defaults b/components/esp_driver_ppa/test_apps/sdkconfig.defaults index 1ee5d718bc2..3aa723a0af0 100644 --- a/components/esp_driver_ppa/test_apps/sdkconfig.defaults +++ b/components/esp_driver_ppa/test_apps/sdkconfig.defaults @@ -1,3 +1,5 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_TASK_WDT_EN=n CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +CONFIG_ESP_SLEEP_DEBUG=y diff --git a/components/esp_hal_dma/esp32p4/include/hal/dma2d_ll.h b/components/esp_hal_dma/esp32p4/include/hal/dma2d_ll.h index a524173ff42..89c164f6ac0 100644 --- a/components/esp_hal_dma/esp32p4/include/hal/dma2d_ll.h +++ b/components/esp_hal_dma/esp32p4/include/hal/dma2d_ll.h @@ -110,7 +110,7 @@ extern const int dma2d_csc_param_rgb2yuv_bt709_table[3][4]; * @param group_id Group ID * @param enable True to enable; false to disable */ -static inline void dma2d_ll_enable_bus_clock(int group_id, bool enable) +static inline void _dma2d_ll_enable_bus_clock(int group_id, bool enable) { (void)group_id; HP_SYS_CLKRST.soc_clk_ctrl1.reg_dma2d_sys_clk_en = enable; @@ -120,7 +120,7 @@ static inline void dma2d_ll_enable_bus_clock(int group_id, bool enable) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define dma2d_ll_enable_bus_clock(...) do { \ (void)__DECLARE_RCC_ATOMIC_ENV; \ - dma2d_ll_enable_bus_clock(__VA_ARGS__); \ + _dma2d_ll_enable_bus_clock(__VA_ARGS__); \ } while(0) /** @@ -128,7 +128,7 @@ static inline void dma2d_ll_enable_bus_clock(int group_id, bool enable) * * @param group_id Group ID */ -static inline void dma2d_ll_reset_register(int group_id) +static inline void _dma2d_ll_reset_register(int group_id) { (void)group_id; HP_SYS_CLKRST.hp_rst_en0.reg_rst_en_dma2d = 1; @@ -139,7 +139,7 @@ static inline void dma2d_ll_reset_register(int group_id) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define dma2d_ll_reset_register(...) do { \ (void)__DECLARE_RCC_ATOMIC_ENV; \ - dma2d_ll_reset_register(__VA_ARGS__); \ + _dma2d_ll_reset_register(__VA_ARGS__); \ } while(0) /** diff --git a/components/esp_hal_dma/include/hal/dma2d_periph.h b/components/esp_hal_dma/include/hal/dma2d_periph.h index f32baacd307..2e78ccf13c9 100644 --- a/components/esp_hal_dma/include/hal/dma2d_periph.h +++ b/components/esp_hal_dma/include/hal/dma2d_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/esp_hal_ppa/esp32p4/include/hal/ppa_ll.h b/components/esp_hal_ppa/esp32p4/include/hal/ppa_ll.h index 7f37a181764..4ac3e99a480 100644 --- a/components/esp_hal_ppa/esp32p4/include/hal/ppa_ll.h +++ b/components/esp_hal_ppa/esp32p4/include/hal/ppa_ll.h @@ -57,7 +57,7 @@ typedef enum { * * @param enable Set true to enable, false to disable */ -static inline void ppa_ll_enable_bus_clock(bool enable) +static inline void _ppa_ll_enable_bus_clock(bool enable) { HP_SYS_CLKRST.soc_clk_ctrl1.reg_ppa_sys_clk_en = enable; } @@ -66,13 +66,13 @@ static inline void ppa_ll_enable_bus_clock(bool enable) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define ppa_ll_enable_bus_clock(...) do { \ (void)__DECLARE_RCC_ATOMIC_ENV; \ - ppa_ll_enable_bus_clock(__VA_ARGS__); \ + _ppa_ll_enable_bus_clock(__VA_ARGS__); \ } while(0) /** * @brief Reset the PPA module */ -static inline void ppa_ll_reset_register(void) +static inline void _ppa_ll_reset_register(void) { HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_ppa = 1; HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_ppa = 0; @@ -82,7 +82,7 @@ static inline void ppa_ll_reset_register(void) /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define ppa_ll_reset_register(...) do { \ (void)__DECLARE_RCC_ATOMIC_ENV; \ - ppa_ll_reset_register(__VA_ARGS__); \ + _ppa_ll_reset_register(__VA_ARGS__); \ } while(0) /** diff --git a/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c b/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c index 147de4f11bc..4d06a018e88 100644 --- a/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -106,6 +106,12 @@ bool peripheral_domain_pd_allowed(void) // ESP32P4 supports H264 sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_H264); + // ESP32P4 supports PPA sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PPA); + + //ESP32P4 supports 2D-DMA sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_DMA2D); + 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/esp_hw_support/port/esp32s31/peripheral_domain_pd.c b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c index 9d6c43311b2..9a0ac8ce7f1 100644 --- a/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c @@ -96,6 +96,12 @@ bool peripheral_domain_pd_allowed(void) // ESP32S31 supports JPEG sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_JPEG); + // ESP32S31 supports PPA sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PPA); + + //ESP32S31 supports 2D-DMA sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_DMA2D); + 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/esp32p4/include/soc/retention_periph_defs.h b/components/soc/esp32p4/include/soc/retention_periph_defs.h index 0f77f989e18..36a115e42ae 100644 --- a/components/soc/esp32p4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32p4/include/soc/retention_periph_defs.h @@ -64,6 +64,8 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_JPEG = 38, SLEEP_RETENTION_MODULE_LCDCAM = 39, SLEEP_RETENTION_MODULE_H264 = 40, + SLEEP_RETENTION_MODULE_DMA2D = 41, + SLEEP_RETENTION_MODULE_PPA = 42, /* PMU REGDMA clock icg */ SLEEP_RETENTION_MODULE_CLOCK_ICG = SOC_PM_RETENTION_MODULE_NUM - 2, @@ -71,7 +73,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; -#define is_top_domain_module(m) (((m) <= SLEEP_RETENTION_MODULE_H264) || ((m) == SLEEP_RETENTION_MODULE_CLOCK_ICG)) +#define is_top_domain_module(m) (((m) <= SLEEP_RETENTION_MODULE_PPA) || ((m) == SLEEP_RETENTION_MODULE_CLOCK_ICG)) #ifdef __cplusplus } diff --git a/components/soc/esp32p4/system_retention_periph.c b/components/soc/esp32p4/system_retention_periph.c index cfca9276f6f..e1894730888 100644 --- a/components/soc/esp32p4/system_retention_periph.c +++ b/components/soc/esp32p4/system_retention_periph.c @@ -25,8 +25,13 @@ #include "soc/pvt_reg.h" /* Interrupt Matrix Registers Context */ +#if (CONFIG_ESP_REV_MIN_FULL < 300) #define N_REGS_INTR_CORE0() (((INTERRUPT_CORE0_CLOCK_GATE_REG - DR_REG_INTERRUPT_CORE0_BASE) / 4) + 1) #define N_REGS_INTR_CORE1() (((INTERRUPT_CORE1_CLOCK_GATE_REG - DR_REG_INTERRUPT_CORE1_BASE) / 4) + 1) +#else +#define N_REGS_INTR_CORE0() (((INTERRUPT_CORE0_INTR_SIG_IDX_ASSERT_IN_SEC_REG - DR_REG_INTERRUPT_CORE0_BASE) / 4) + 1) +#define N_REGS_INTR_CORE1() (((INTERRUPT_CORE1_INTR_SIG_IDX_ASSERT_IN_SEC_REG - DR_REG_INTERRUPT_CORE1_BASE) / 4) + 1) +#endif const regdma_entries_config_t intr_matrix_regs_retention[] = { [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_INTMTX_LINK(0x00), DR_REG_INTERRUPT_CORE0_BASE, DR_REG_INTERRUPT_CORE0_BASE, N_REGS_INTR_CORE0(), 0, 0), .owner = ENTRY(0) }, /* intr matrix */ [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_INTMTX_LINK(0x01), DR_REG_INTERRUPT_CORE1_BASE, DR_REG_INTERRUPT_CORE1_BASE, N_REGS_INTR_CORE1(), 0, 0), .owner = ENTRY(0) } /* intr matrix */ diff --git a/components/soc/esp32s31/include/soc/retention_periph_defs.h b/components/soc/esp32s31/include/soc/retention_periph_defs.h index 7efc815762b..747d45f839b 100644 --- a/components/soc/esp32s31/include/soc/retention_periph_defs.h +++ b/components/soc/esp32s31/include/soc/retention_periph_defs.h @@ -67,20 +67,22 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_SDM0 = 41, SLEEP_RETENTION_MODULE_LCDCAM = 42, SLEEP_RETENTION_MODULE_JPEG = 43, + SLEEP_RETENTION_MODULE_DMA2D = 44, + SLEEP_RETENTION_MODULE_PPA = 45, /* Modem module, which includes WiFi, BLE and 802.15.4 */ - SLEEP_RETENTION_MODULE_WIFI_MAC = 44, - SLEEP_RETENTION_MODULE_WIFI_BB = 45, - SLEEP_RETENTION_MODULE_BLE_MAC = 46, - SLEEP_RETENTION_MODULE_BT_BB = 47, - SLEEP_RETENTION_MODULE_802154_MAC = 48, - SLEEP_RETENTION_MODULE_CLOCK_MODEM = 49, - SLEEP_RETENTION_MODULE_MODEM_PHY = 50, + SLEEP_RETENTION_MODULE_WIFI_MAC = 46, + SLEEP_RETENTION_MODULE_WIFI_BB = 47, + SLEEP_RETENTION_MODULE_BLE_MAC = 48, + SLEEP_RETENTION_MODULE_BT_BB = 49, + SLEEP_RETENTION_MODULE_802154_MAC = 50, + SLEEP_RETENTION_MODULE_CLOCK_MODEM = 51, + SLEEP_RETENTION_MODULE_MODEM_PHY = 52, SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; -#define is_top_domain_module(m) ((m >= SLEEP_RETENTION_MODULE_CLOCK_SYSTEM) && ((m <= SLEEP_RETENTION_MODULE_JPEG))) +#define is_top_domain_module(m) ((m >= SLEEP_RETENTION_MODULE_CLOCK_SYSTEM) && ((m <= SLEEP_RETENTION_MODULE_PPA))) #ifdef __cplusplus } diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index 6f0da9db1a7..f0080b6df35 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -67,9 +67,13 @@ extern "C" { #define REGDMA_EMAC_LINK(_pri) ((0x27 << 8) | _pri) #define REGDMA_JPEG_LINK(_pri) ((0x28 << 8) | _pri) #define REGDMA_LCDCAM_LINK(_pri) ((0x29 << 8) | _pri) -#define REGDMA_H264_LINK(_pri) ((0x2a << 8) | _pri) +#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_POWER_LINK(_pri) ((0xFD << 8) | _pri) #define REGDMA_CLOCK_ICG_LINK(_pri) ((0xFE << 8) | _pri) + #define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) #define REGDMA_LINK_PRI_SYS_CLK REGDMA_LINK_PRI_0 @@ -102,6 +106,8 @@ extern "C" { #define REGDMA_LINK_PRI_JPEG REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_LCDCAM REGDMA_LINK_PRI_GENERAL_PERIPH #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 typedef enum { REGDMA_LINK_PRI_0 = 0,