mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/lcd_sleep_retention_p4' into 'master'
feat(lcd): support i80 lcd sleep retention on p4 Closes IDF-9925, IDF-10965, and IDF-10703 See merge request espressif/esp-idf!45549
This commit is contained in:
@@ -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
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
一些外设尚未支持睡眠上下文恢复,或者寄存器丢失后根本无法恢复。即使外设下电功能被启用,它们也会阻止外设下电的发生:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user