diff --git a/components/esp_hal_lcd/esp32p4/lcd_periph.c b/components/esp_hal_lcd/esp32p4/lcd_periph.c index f5746af0a0..be1b4c4fa0 100644 --- a/components/esp_hal_lcd/esp32p4/lcd_periph.c +++ b/components/esp_hal_lcd/esp32p4/lcd_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "hal/lcd_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/lcd_cam_reg.h" const soc_lcd_i80_signal_desc_t soc_lcd_i80_signals[1] = { [0] = { @@ -80,3 +81,39 @@ const soc_lcd_rgb_signal_desc_t soc_lcd_rgb_signals[1] = { .disp_sig = SIG_GPIO_OUT_IDX, } }; + +/** + * LCD_CAM Registers to be saved during sleep retention + * - LCD Clock Configuration registers: LCDCAM_LCD_CLOCK_REG (0x0) + * - LCD RGB/YUV Configuration registers: LCDCAM_LCD_RGB_YUV_REG (0x10) + * - LCD User Configuration registers: LCDCAM_LCD_USER_REG (0x14), LCDCAM_LCD_MISC_REG (0x18) + * - LCD Control registers: LCDCAM_LCD_CTRL_REG (0x1c), LCDCAM_LCD_CTRL1_REG (0x20), LCDCAM_LCD_CTRL2_REG (0x24) + * - LCD Command Value registers: LCDCAM_LCD_FIRST_CMD_VAL_REG (0x28), LCDCAM_LCD_LATTER_CMD_VAL_REG (0x2c) + * - LCD Delay Mode Configuration registers: LCDCAM_LCD_DLY_MODE_CFG1_REG (0x30), LCDCAM_LCD_DLY_MODE_CFG2_REG (0x38) + * - LCD DMA Interrupt Enable register: LCDCAM_LC_DMA_INT_ENA_REG (0x64) + * + * NOTE: Only I80 LCD supports sleep retention, not RGB LCD. + */ +#define LCD_RETENTION_REGS_CNT 12 +#define LCD_RETENTION_REGS_BASE (DR_REG_LCDCAM_BASE + 0x0) +static const uint32_t lcd_cam_regs_map[4] = {0x2005ff1, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t lcd_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_LCDCAM_LINK(0x00), + LCD_RETENTION_REGS_BASE, LCD_RETENTION_REGS_BASE, + LCD_RETENTION_REGS_CNT, 0, 0, + lcd_cam_regs_map[0], lcd_cam_regs_map[1], + lcd_cam_regs_map[2], lcd_cam_regs_map[3]), + .owner = ENTRY(0), + }, +}; + +const soc_i80_lcd_retention_info_t soc_i80_lcd_retention_info[1] = { + [0] = { + .regdma_entry_array = lcd_regs_retention, + .array_size = ARRAY_SIZE(lcd_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_LCDCAM + }, +}; diff --git a/components/esp_hal_lcd/include/hal/lcd_periph.h b/components/esp_hal_lcd/include/hal/lcd_periph.h index 24ceb37167..c3624d26bb 100644 --- a/components/esp_hal_lcd/include/hal/lcd_periph.h +++ b/components/esp_hal_lcd/include/hal/lcd_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -61,6 +61,17 @@ typedef struct { extern const soc_lcd_i2s_signal_desc_t soc_lcd_i2s_signals[I2S_LL_GET(INST_NUM)]; #endif // SOC_HAS(I2S_I80_LCD) +#if SOC_HAS(PAU) && SOC_HAS(LCDCAM_I80_LCD) +// Only LCDCAM I80 LCD supports sleep retention +typedef struct { + const periph_retention_module_t retention_module; + const regdma_entries_config_t *regdma_entry_array; + uint32_t array_size; +} soc_i80_lcd_retention_info_t; + +extern const soc_i80_lcd_retention_info_t soc_i80_lcd_retention_info[LCD_LL_GET(I80_BUS_NUM)]; +#endif // SOC_HAS(PAU) && SOC_HAS(LCDCAM_I80_LCD) + #ifdef __cplusplus } #endif 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 773dfbcb73..d4766efde5 100644 --- a/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32p4/peripheral_domain_pd.c @@ -99,6 +99,9 @@ bool peripheral_domain_pd_allowed(void) // ESP32P4 supports JPEG sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_JPEG); + // ESP32P4 supports LCDCAM sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_LCDCAM); + 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); return sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules); diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index 3314a22787..d97019894e 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -10,6 +10,10 @@ #include "hal/lcd_hal.h" #include "hal/cache_ll.h" #include "hal/cache_hal.h" +#include "esp_private/sleep_retention.h" + +// Use retention link only when the target supports sleep retention is enabled +#define I80_USE_RETENTION_LINK (SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) #if defined(SOC_GDMA_TRIG_PERIPH_LCD0_BUS) && (SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB) #define LCD_GDMA_NEW_CHANNEL gdma_new_ahb_channel @@ -31,6 +35,11 @@ typedef struct esp_lcd_i80_bus_t esp_lcd_i80_bus_t; typedef struct lcd_panel_io_i80_t lcd_panel_io_i80_t; typedef struct lcd_i80_trans_descriptor_t lcd_i80_trans_descriptor_t; +#if I80_USE_RETENTION_LINK +static esp_err_t lcd_i80_create_sleep_retention_link_cb(void *arg); +static void lcd_i80_create_retention_module(esp_lcd_i80_bus_t *bus); +#endif // I80_USE_RETENTION_LINK + static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size); static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size); static esp_err_t panel_io_i80_del(esp_lcd_panel_io_t *io); @@ -117,6 +126,10 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc // although LCD_CAM can support up to 24 data lines, we restrict users to only use 8 or 16 bit width ESP_RETURN_ON_FALSE(bus_config->bus_width == 8 || bus_config->bus_width == 16, ESP_ERR_INVALID_ARG, TAG, "invalid bus width:%d", bus_config->bus_width); +#if !SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION + ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#endif // SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION + // allocate i80 bus memory bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t), LCD_I80_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus"); @@ -143,6 +156,26 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc lcd_ll_reset_register(bus_id); } } +#if I80_USE_RETENTION_LINK + // no need to acquire mutex, because the bus is exclusive + sleep_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = lcd_i80_create_sleep_retention_link_cb, + .arg = bus, + }, + }, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) + }; + if (sleep_retention_module_init(module_id, &init_param) != ESP_OK) { + // even though the sleep retention module init failed, LCD driver should still work, so just warning here + ESP_LOGW(TAG, "init sleep retention failed, power domain may be turned off during sleep"); + } + if (bus_config->flags.allow_pd) { + lcd_i80_create_retention_module(bus); + } +#endif // I80_USE_RETENTION_LINK // initialize HAL layer, so we can call LL APIs later lcd_hal_init(&bus->hal, bus_id); PERIPH_RCC_ATOMIC() { @@ -240,6 +273,16 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus) ESP_GOTO_ON_FALSE(bus, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty"); int bus_id = bus->bus_id; +#if I80_USE_RETENTION_LINK + const periph_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module; + if (sleep_retention_is_module_created(module_id)) { + assert(sleep_retention_is_module_inited(module_id)); + sleep_retention_module_free(module_id); + } + if (sleep_retention_is_module_inited(module_id)) { + sleep_retention_module_deinit(module_id); + } +#endif // I80_USE_RETENTION_LINK lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus_id); PERIPH_RCC_RELEASE_ATOMIC(soc_lcd_i80_signals[bus_id].module, ref_count) { if (ref_count == 0) { @@ -546,6 +589,33 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons return ESP_OK; } +#if I80_USE_RETENTION_LINK +static esp_err_t lcd_i80_create_sleep_retention_link_cb(void *arg) +{ + esp_lcd_i80_bus_t *bus = (esp_lcd_i80_bus_t *)arg; + int bus_id = bus->bus_id; + sleep_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module; + esp_err_t err = sleep_retention_entries_create(soc_i80_lcd_retention_info[bus_id].regdma_entry_array, + soc_i80_lcd_retention_info[bus_id].array_size, + REGDMA_LINK_PRI_LCDCAM, module_id); + ESP_RETURN_ON_ERROR(err, TAG, "create retention link failed"); + return ESP_OK; +} + +static void lcd_i80_create_retention_module(esp_lcd_i80_bus_t *bus) +{ + int bus_id = bus->bus_id; + sleep_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module; + + if (sleep_retention_is_module_inited(module_id) && !sleep_retention_is_module_created(module_id)) { + if (sleep_retention_module_allocate(module_id) != ESP_OK) { + // even though the sleep retention module create failed, LCD driver should still work, so just warning here + ESP_LOGW(TAG, "create retention module failed, power domain can't turn off"); + } + } +} +#endif // I80_USE_RETENTION_LINK + static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_clock_source_t clk_src) { // get clock source frequency diff --git a/components/esp_lcd/include/esp_lcd_io_i80.h b/components/esp_lcd/include/esp_lcd_io_i80.h index c9647a1764..1df265aca0 100644 --- a/components/esp_lcd/include/esp_lcd_io_i80.h +++ b/components/esp_lcd/include/esp_lcd_io_i80.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -30,6 +30,11 @@ typedef struct { size_t bus_width; /*!< Number of data lines, 8 or 16 */ size_t max_transfer_bytes; /*!< Maximum transfer size, this determines the length of internal DMA link */ size_t dma_burst_size; /*!< DMA burst size, in bytes */ + /// Extra configuration flags for I80 bus + struct extra_i80_bus_flags { + 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. */ + } flags; /*!< Extra bus config flags */ } esp_lcd_i80_bus_config_t; /** @@ -67,19 +72,21 @@ typedef struct { void *user_ctx; /*!< User private data, passed directly to on_color_trans_done's user_ctx */ int lcd_cmd_bits; /*!< Bit-width of LCD command */ int lcd_param_bits; /*!< Bit-width of LCD parameter */ - struct { + /// D/C line levels configuration + struct i80_panel_io_dc_levels { unsigned int dc_idle_level: 1; /*!< Level of DC line in IDLE phase */ unsigned int dc_cmd_level: 1; /*!< Level of DC line in CMD phase */ unsigned int dc_dummy_level: 1; /*!< Level of DC line in DUMMY phase */ unsigned int dc_data_level: 1; /*!< Level of DC line in DATA phase */ } dc_levels; /*!< Each i80 device might have its own D/C control logic */ - struct { + /// Extra configuration flags for I80 panel IO + struct extra_i80_panel_io_flags { unsigned int cs_active_high: 1; /*!< If set, a high level of CS line will select the device, otherwise, CS line is low level active */ unsigned int reverse_color_bits: 1; /*!< Reverse the data bits, D[N:0] -> D[0:N] */ unsigned int swap_color_bytes: 1; /*!< Swap adjacent two color bytes */ unsigned int pclk_active_neg: 1; /*!< The display will write data lines when there's a falling edge on WR signal (a.k.a the PCLK) */ unsigned int pclk_idle_low: 1; /*!< The WR signal (a.k.a the PCLK) stays at low level in IDLE phase */ - } flags; /*!< Panel IO config flags */ + } flags; /*!< Extra panel IO config flags */ } esp_lcd_panel_io_i80_config_t; /** diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index c34b4e1b70..24c204c322 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -48,6 +48,7 @@ #include "hal/cache_ll.h" #include "hal/color_hal.h" #include "rgb_lcd_rotation_sw.h" +#include "esp_private/sleep_retention.h" // hardware issue workaround #if CONFIG_IDF_TARGET_ESP32S3 @@ -284,6 +285,9 @@ static esp_err_t lcd_rgb_panel_destroy(esp_rgb_panel_t *rgb_panel) esp_pm_lock_release(rgb_panel->pm_lock); esp_pm_lock_delete(rgb_panel->pm_lock); } +#endif +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + sleep_retention_power_lock_release(); #endif free(rgb_panel); return ESP_OK; @@ -342,6 +346,11 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf expect_bb_eof_count = fb_size / bb_size; } +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + // acquire the retention power lock to prevent the power domain from being turned off during light sleep + sleep_retention_power_lock_acquire(); +#endif + // calculate the number of DMA descriptors size_t num_dma_nodes = 0; // allocate memory for rgb panel @@ -379,6 +388,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf // set clock source ret = lcd_rgb_panel_select_clock_src(rgb_panel, rgb_panel_config->clk_src); ESP_GOTO_ON_ERROR(ret, err, TAG, "set source clock failed"); + // reset peripheral and FIFO after we select a correct clock source lcd_ll_fifo_reset(hal->dev); lcd_ll_reset(hal->dev); diff --git a/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt b/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt index dbbd5aa92d..fff7eea82e 100644 --- a/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt +++ b/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt @@ -1,6 +1,10 @@ set(srcs "test_app_main.c" "test_i80_lcd_panel.c") +if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE) + list(APPEND srcs "test_i80_lcd_panel_sleep.c") +endif() + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register(SRCS ${srcs} diff --git a/components/esp_lcd/test_apps/i80_lcd/main/test_i80_lcd_panel_sleep.c b/components/esp_lcd/test_apps/i80_lcd/main/test_i80_lcd_panel_sleep.c new file mode 100644 index 0000000000..186d930616 --- /dev/null +++ b/components/esp_lcd/test_apps/i80_lcd/main/test_i80_lcd_panel_sleep.c @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "esp_random.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_commands.h" +#include "soc/soc_caps.h" +#include "driver/gpio.h" +#include "test_i80_board.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" + +static void test_i80_lcd_sleep_retention(bool allow_pd) +{ +#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t)) + uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(img); + + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + + esp_lcd_i80_bus_handle_t i80_bus = NULL; + esp_lcd_i80_bus_config_t bus_config = { + .dc_gpio_num = TEST_LCD_DC_GPIO, + .wr_gpio_num = TEST_LCD_PCLK_GPIO, + .clk_src = LCD_CLK_SRC_DEFAULT, + .data_gpio_nums = { + TEST_LCD_DATA0_GPIO, + TEST_LCD_DATA1_GPIO, + TEST_LCD_DATA2_GPIO, + TEST_LCD_DATA3_GPIO, + TEST_LCD_DATA4_GPIO, + TEST_LCD_DATA5_GPIO, + TEST_LCD_DATA6_GPIO, + TEST_LCD_DATA7_GPIO, + }, + .bus_width = 8, + .max_transfer_bytes = TEST_IMG_SIZE + 10, + .flags = { + .allow_pd = allow_pd, + }, + }; + TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus)); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_i80_config_t io_config = { + .cs_gpio_num = TEST_LCD_CS_GPIO, + .pclk_hz = 8000000, // 8MHz + .trans_queue_depth = 10, + .dc_levels = { + .dc_idle_level = 0, + .dc_cmd_level = 0, + .dc_dummy_level = 0, + .dc_data_level = 1, + }, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + TEST_ESP_OK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle)); + + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_LCD_RST_GPIO, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = 16, + }; + TEST_ESP_OK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle)); + + // turn off backlight + gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0); + esp_lcd_panel_reset(panel_handle); + esp_lcd_panel_init(panel_handle); + esp_lcd_panel_invert_color(panel_handle, true); + // the gap is LCD panel specific, even panels with the same driver IC, can have different gap value + esp_lcd_panel_set_gap(panel_handle, 0, 20); + // turn on display + esp_lcd_panel_disp_on_off(panel_handle, true); + // turn on backlight + gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1); + + for (int i = 0; i < 200; i++) { + uint8_t color_byte = esp_random() & 0xFF; + int x_start = esp_random() % (TEST_LCD_H_RES - 100); + int y_start = esp_random() % (TEST_LCD_V_RES - 100); + memset(img, color_byte, TEST_IMG_SIZE); + esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img); + } + // turn off screen + esp_lcd_panel_disp_on_off(panel_handle, false); + + // go to sleep + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + printf("go to light sleep for 2 seconds\r\n"); +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(true)); +#endif + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(2 * 1000 * 1000)); + TEST_ESP_OK(esp_light_sleep_start()); + + printf("Waked up! Let's see if I80 LCD driver can still work...\r\n"); +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(false)); +#endif + + printf("check if the sleep happened as expected\r\n"); + TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); +#if SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(allow_pd ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); + + // turn on screen + esp_lcd_panel_disp_on_off(panel_handle, true); + + printf("Testing I80 LCD after sleep...\n"); + for (int i = 0; i < 200; i++) { + uint8_t color_byte = esp_random() & 0xFF; + int x_start = esp_random() % (TEST_LCD_H_RES - 100); + int y_start = esp_random() % (TEST_LCD_V_RES - 100); + memset(img, color_byte, TEST_IMG_SIZE); + esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img); + } + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus)); + TEST_ESP_OK(gpio_reset_pin(TEST_LCD_BK_LIGHT_GPIO)); + free(img); +#undef TEST_IMG_SIZE +} + +TEST_CASE("i80_lcd light sleep", "[lcd]") +{ + test_i80_lcd_sleep_retention(false); +#if SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION + test_i80_lcd_sleep_retention(true); +#endif +} diff --git a/components/esp_lcd/test_apps/i80_lcd/sdkconfig.ci.release b/components/esp_lcd/test_apps/i80_lcd/sdkconfig.ci.release index 91d93f163e..eb643d4d06 100644 --- a/components/esp_lcd/test_apps/i80_lcd/sdkconfig.ci.release +++ b/components/esp_lcd/test_apps/i80_lcd/sdkconfig.ci.release @@ -1,4 +1,5 @@ CONFIG_PM_ENABLE=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y diff --git a/components/esp_lcd/test_apps/i80_lcd/sdkconfig.defaults b/components/esp_lcd/test_apps/i80_lcd/sdkconfig.defaults index ccc43c6fdf..15635373d9 100644 --- a/components/esp_lcd/test_apps/i80_lcd/sdkconfig.defaults +++ b/components/esp_lcd/test_apps/i80_lcd/sdkconfig.defaults @@ -3,3 +3,6 @@ # # CONFIG_ESP_TASK_WDT_INIT is not set CONFIG_FREERTOS_HZ=1000 + +# primitives for checking sleep internal state +CONFIG_ESP_SLEEP_DEBUG=y diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 7a226323dc..0ec3fb00ac 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1871,6 +1871,10 @@ config SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV bool default y +config SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_LP_CORE_SUPPORT_ETM bool default y diff --git a/components/soc/esp32p4/include/soc/retention_periph_defs.h b/components/soc/esp32p4/include/soc/retention_periph_defs.h index 3e40d9057d..1a6d3685a9 100644 --- a/components/soc/esp32p4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32p4/include/soc/retention_periph_defs.h @@ -62,6 +62,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_SDM0 = 36, SLEEP_RETENTION_MODULE_EMAC = 37, SLEEP_RETENTION_MODULE_JPEG = 38, + SLEEP_RETENTION_MODULE_LCDCAM = 39, SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; @@ -105,6 +106,7 @@ typedef enum periph_retention_module { : ((m) == SLEEP_RETENTION_MODULE_MCPWM1) ? true \ : ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \ : ((m) == SLEEP_RETENTION_MODULE_JPEG) ? true \ + : ((m) == SLEEP_RETENTION_MODULE_LCDCAM) ? true \ : false) #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index e2257b8448..f4b7b38f60 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -716,6 +716,9 @@ /*--------------------------- CAM ---------------------------------*/ #define SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV (1) +/*--------------------------- LCD ---------------------------------*/ +#define SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION (1) /*!< Support back up registers before sleep */ + /*------------------------------------- ULP CAPS -------------------------------------*/ #define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */ #define SOC_LP_CORE_SUPPORT_LP_ADC (1) /*!< LP ADC can be accessed from the LP-Core */ diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index 19f394e20b..219ebca147 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -67,6 +67,7 @@ extern "C" { #define REGDMA_SDM_LINK(_pri) ((0x26 << 8) | _pri) #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_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) @@ -96,6 +97,7 @@ extern "C" { #define REGDMA_LINK_PRI_SDM REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_EMAC REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_JPEG REGDMA_LINK_PRI_GENERAL_PERIPH +#define REGDMA_LINK_PRI_LCDCAM REGDMA_LINK_PRI_GENERAL_PERIPH typedef enum { REGDMA_LINK_PRI_0 = 0, diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index 3e1eb063c3..fca7343745 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -193,6 +193,7 @@ The following drivers hold the ``ESP_PM_APB_FREQ_MAX`` lock while the driver is :SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO :SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs :SOC_EMAC_SUPPORT_SLEEP_RETENTION: - EMAC + :SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION: - I80 LCD Some peripherals haven't support Light-sleep context retention, or it cannot survive from the register lose. They will prevent the power-down of peripherals even when the feature is enabled. diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 352e07993e..bf8b5dd989 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -193,6 +193,7 @@ ESP-IDF 使用预测性时间补偿机制来实现自动 Light-sleep。系统会 :SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO :SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs :SOC_EMAC_SUPPORT_SLEEP_RETENTION: - EMAC + :SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION: - I80 LCD 一些外设尚未支持睡眠上下文恢复,或者寄存器丢失后根本无法恢复。即使外设下电功能被启用,它们也会阻止外设下电的发生: