Merge branch 'feat/support_lcd_on_s31' into 'master'

feat(lcd): support rgb and i80 lcd on s31

Closes IDF-14750, IDF-14751, IDF-14753, and IDF-14754

See merge request espressif/esp-idf!47312
This commit is contained in:
Chen Ji Chang
2026-04-22 00:40:26 +08:00
38 changed files with 1619 additions and 113 deletions
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -17,6 +17,7 @@
#include "soc/hp_sys_clkrst_struct.h" #include "soc/hp_sys_clkrst_struct.h"
#define LCD_LL_GET(_attr) LCD_LL_ ## _attr #define LCD_LL_GET(_attr) LCD_LL_ ## _attr
#define LCD_LL_SUPPORT(_feat) LCD_LL_SUPPORT_ ## _feat
#define LCD_LL_RGB_BUS_WIDTH 24 #define LCD_LL_RGB_BUS_WIDTH 24
#define LCD_LL_RGB_PANEL_NUM 1 #define LCD_LL_RGB_PANEL_NUM 1
#define LCD_LL_I80_BUS_WIDTH 24 #define LCD_LL_I80_BUS_WIDTH 24
@@ -45,7 +46,10 @@ extern "C" {
#define LCD_LL_CLK_FRAC_DIV_N_MAX 256 // LCD_CLK = LCD_CLK_S / (N + b/a), the N register is 8 bit-width #define LCD_LL_CLK_FRAC_DIV_N_MAX 256 // LCD_CLK = LCD_CLK_S / (N + b/a), the N register is 8 bit-width
#define LCD_LL_CLK_FRAC_DIV_AB_MAX 64 // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width #define LCD_LL_CLK_FRAC_DIV_AB_MAX 64 // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
#define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width #define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
#define LCD_LL_FIFO_DEPTH 8 // Async FIFO depth
typedef enum {
LCD_LL_MEM_LP_MODE_SHUT_DOWN,
} lcd_ll_mem_lp_mode_t;
/** /**
* @brief LCD data byte swizzle mode * @brief LCD data byte swizzle mode
@@ -172,6 +176,65 @@ static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num,
lcd_ll_set_group_clock_coeff(__VA_ARGS__); \ lcd_ll_set_group_clock_coeff(__VA_ARGS__); \
} while(0) } while(0)
/**
* @brief Force power on the LCD memory block, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_power_on(lcd_cam_dev_t *dev)
{
(void)dev;
// P4 does not have transfer buffer
}
/**
* @brief Force the LCD memory block into low power mode, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_low_power(lcd_cam_dev_t *dev)
{
(void)dev;
// P4 does not have transfer buffer
}
/**
* @brief Power control the LCD memory block by the outside PMU logic
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_power_by_pmu(lcd_cam_dev_t *dev)
{
(void)dev;
// P4 does not have transfer buffer
}
/**
* @brief Set low power mode for LCD memory block
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_set_low_power_mode(lcd_cam_dev_t *dev, lcd_ll_mem_lp_mode_t mode)
{
(void)dev;
HAL_ASSERT(mode == LCD_LL_MEM_LP_MODE_SHUT_DOWN);
}
/**
* @brief Enable the transfer buffer(memory block) for LCD module
*
* @param dev Peripheral instance address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_trans_buffer(lcd_cam_dev_t *dev, bool en)
{
(void)dev;
(void)en;
// P4 does not have transfer buffer
}
/** /**
* @brief Set the PCLK clock level state when there's no transaction undergoing * @brief Set the PCLK clock level state when there's no transaction undergoing
* *
+3 -5
View File
@@ -85,18 +85,16 @@ const soc_lcd_rgb_signal_desc_t soc_lcd_rgb_signals[1] = {
/** /**
* LCD_CAM Registers to be saved during sleep retention * LCD_CAM Registers to be saved during sleep retention
* - LCD Clock Configuration registers: LCDCAM_LCD_CLOCK_REG (0x0) * - 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 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 Control registers: LCDCAM_LCD_CTRL_REG (0x1c) (Note: lcd_rgb_mode_en is in this register)
* - 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 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) * - LCD DMA Interrupt Enable register: LCDCAM_LC_DMA_INT_ENA_REG (0x64)
* *
* NOTE: Only I80 LCD supports sleep retention, not RGB LCD. * NOTE: Only I80 LCD supports sleep retention, not RGB LCD.
*/ */
#define LCD_RETENTION_REGS_CNT 12 #define LCD_RETENTION_REGS_CNT 7
#define LCD_RETENTION_REGS_BASE (DR_REG_LCDCAM_BASE + 0x0) #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 uint32_t lcd_cam_regs_map[4] = {0x20050e1, 0x0, 0x0, 0x0};
static const regdma_entries_config_t lcd_regs_retention[] = { static const regdma_entries_config_t lcd_regs_retention[] = {
// backup stage: save configuration registers // backup stage: save configuration registers
// restore stage: restore the configuration registers // restore stage: restore the configuration registers
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,6 +16,7 @@
#include "soc/system_struct.h" #include "soc/system_struct.h"
#define LCD_LL_GET(_attr) LCD_LL_ ## _attr #define LCD_LL_GET(_attr) LCD_LL_ ## _attr
#define LCD_LL_SUPPORT(_feat) LCD_LL_SUPPORT_ ## _feat
#define LCD_LL_RGB_BUS_WIDTH 16 #define LCD_LL_RGB_BUS_WIDTH 16
#define LCD_LL_RGB_PANEL_NUM 1 #define LCD_LL_RGB_PANEL_NUM 1
#define LCD_LL_I80_BUS_WIDTH 16 #define LCD_LL_I80_BUS_WIDTH 16
@@ -39,6 +40,10 @@ extern "C" {
#define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width #define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
#define LCD_LL_FIFO_DEPTH 16 // Async FIFO depth #define LCD_LL_FIFO_DEPTH 16 // Async FIFO depth
typedef enum {
LCD_LL_MEM_LP_MODE_SHUT_DOWN,
} lcd_ll_mem_lp_mode_t;
/** /**
* @brief LCD data byte swizzle mode * @brief LCD data byte swizzle mode
*/ */
@@ -143,6 +148,65 @@ static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num,
dev->lcd_clock.lcd_clkm_div_b = div_b; dev->lcd_clock.lcd_clkm_div_b = div_b;
} }
/**
* @brief Force power on the LCD memory block, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_power_on(lcd_cam_dev_t *dev)
{
(void)dev;
// S3 does not have transfer buffer
}
/**
* @brief Force the LCD memory block into low power mode, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_low_power(lcd_cam_dev_t *dev)
{
(void)dev;
// S3 does not have transfer buffer
}
/**
* @brief Power control the LCD memory block by the outside PMU logic
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_power_by_pmu(lcd_cam_dev_t *dev)
{
(void)dev;
// S3 does not have transfer buffer
}
/**
* @brief Set low power mode for LCD memory block
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_set_low_power_mode(lcd_cam_dev_t *dev, lcd_ll_mem_lp_mode_t mode)
{
(void)dev;
HAL_ASSERT(mode == LCD_LL_MEM_LP_MODE_SHUT_DOWN);
}
/**
* @brief Enable the transfer buffer(memory block) for LCD module
*
* @param dev Peripheral instance address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_trans_buffer(lcd_cam_dev_t *dev, bool en)
{
(void)dev;
(void)en;
// S3 does not have transfer buffer
}
/** /**
* @brief Set the PCLK clock level state when there's no transaction undergoing * @brief Set the PCLK clock level state when there's no transaction undergoing
* *
@@ -0,0 +1,854 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h> /* For NULL declaration */
#include <stdint.h>
#include <stdbool.h>
#include "hal/misc.h"
#include "soc/lcd_cam_reg.h"
#include "soc/lcd_cam_struct.h"
#include "hal/assert.h"
#include "hal/lcd_types.h"
#include "soc/hp_sys_clkrst_struct.h"
#include "soc/hp_system_struct.h"
#define LCD_LL_GET(_attr) LCD_LL_ ## _attr
#define LCD_LL_SUPPORT(_feat) LCD_LL_SUPPORT_ ## _feat
#define LCD_LL_RGB_BUS_WIDTH 24
#define LCD_LL_RGB_PANEL_NUM 1
#define LCD_LL_I80_BUS_WIDTH 24
#define LCD_LL_I80_BUS_NUM 1
#define LCD_LL_SUPPORT_IOMUX 1
#ifdef __cplusplus
extern "C" {
#endif
#define LCD_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL)
// Interrupt event, bit mask
#define LCD_LL_EVENT_VSYNC_END (1 << 0)
#define LCD_LL_EVENT_TRANS_DONE (1 << 1)
#define LCD_LL_EVENT_UNDERRUN (1 << 4)
#define LCD_LL_EVENT_I80 (LCD_LL_EVENT_TRANS_DONE | LCD_LL_EVENT_UNDERRUN)
#define LCD_LL_EVENT_RGB (LCD_LL_EVENT_VSYNC_END | LCD_LL_EVENT_UNDERRUN)
#define LCD_LL_CLK_FRAC_DIV_N_MAX 256 // LCD_CLK = LCD_CLK_S / (N + b/a), the N register is 8 bit-width
#define LCD_LL_CLK_FRAC_DIV_AB_MAX 64 // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
#define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
typedef enum {
LCD_LL_MEM_LP_MODE_DEEP_SLEEP, // memory will enter deep sleep during low power stage, keep memory data
LCD_LL_MEM_LP_MODE_LIGHT_SLEEP, // memory will enter light sleep during low power stage, keep memory data
LCD_LL_MEM_LP_MODE_SHUT_DOWN, // memory will be powered down during low power stage
LCD_LL_MEM_LP_MODE_DISABLE, // disable the low power stage
} lcd_ll_mem_lp_mode_t;
/**
* @brief LCD data byte swizzle mode
*/
typedef enum {
LCD_LL_SWIZZLE_AB2BA, /*!< AB -> BA */
LCD_LL_SWIZZLE_ABC2ACB, /*!< ABC -> ACB */
LCD_LL_SWIZZLE_ABC2BAC, /*!< ABC -> BAC */
LCD_LL_SWIZZLE_ABC2BCA, /*!< ABC -> BCA */
LCD_LL_SWIZZLE_ABC2CAB, /*!< ABC -> CAB */
LCD_LL_SWIZZLE_ABC2CBA, /*!< ABC -> CBA */
} lcd_ll_swizzle_mode_t;
/**
* @brief Enable the bus clock for LCD module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void lcd_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_clk_en = enable;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_sys_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
#define lcd_ll_enable_bus_clock(...) do { \
(void)__DECLARE_RCC_RC_ATOMIC_ENV; \
lcd_ll_enable_bus_clock(__VA_ARGS__); \
} while(0)
/**
* @brief Reset the LCD module
*
* @param group_id Group ID
*/
static inline void _lcd_ll_reset_register(int group_id)
{
(void)group_id;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_rst_en = 1;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_rst_en = 0;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_rst_en = 1;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_rst_en = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
#define lcd_ll_reset_register(...) do { \
(void)__DECLARE_RCC_RC_ATOMIC_ENV; \
_lcd_ll_reset_register(__VA_ARGS__); \
} while(0)
/**
* @brief Enable clock gating
*
* @param dev LCD register base address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_clock(lcd_cam_dev_t *dev, bool en)
{
(void)dev;
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_en = en;
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_en = en;
}
/**
* @brief Select clock source for LCD peripheral
*
* @param dev LCD register base address
* @param src Clock source
*/
static inline void lcd_ll_select_clk_src(lcd_cam_dev_t *dev, lcd_clock_source_t src)
{
(void)dev;
switch (src) {
case LCD_CLK_SRC_XTAL:
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_src_sel = 0;
break;
case LCD_CLK_SRC_PLL160M:
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_src_sel = 1;
break;
case LCD_CLK_SRC_APLL:
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_src_sel = 2;
break;
default:
// disable the clock
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_src_sel = 3;
break;
}
}
/**
* @brief Set clock coefficient of LCD peripheral
*
* @param dev LCD register base address
* @param div_num Integer part of the divider
* @param div_a denominator of the divider
* @param div_b numerator of the divider
*/
__attribute__((always_inline))
static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num, int div_a, int div_b)
{
(void)dev;
// lcd_clk = module_clock_src / (div_num + div_b / div_a)
HAL_ASSERT(div_num > 0 && div_num <= LCD_LL_CLK_FRAC_DIV_N_MAX);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_lcd_ctrl0, reg_lcd_clk_div_num, div_num - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_lcd_ctrl0, reg_lcd_clk_div_denominator, div_a);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_lcd_ctrl0, reg_lcd_clk_div_numerator, div_b);
}
/**
* @brief Force power on the LCD memory block, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_power_on(lcd_cam_dev_t *dev)
{
(void)dev;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_force_ctrl = 1;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_en = 0;
}
/**
* @brief Force the LCD memory block into low power mode, regardless of the outside PMU logic
*
* @param dev Peripheral instance address
*/
static inline void lcd_ll_mem_force_low_power(lcd_cam_dev_t *dev)
{
(void)dev;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_force_ctrl = 1;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_en = 1;
}
/**
* @brief Power control the LCD memory block by the outside PMU logic
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_power_by_pmu(lcd_cam_dev_t *dev)
{
(void)dev;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_force_ctrl = 0;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_en = 0;
}
/**
* @brief Set low power mode for LCD memory block
*
* @param dev Peripheral instance address
* @param mode LCD memory low power mode in low power stage
*/
static inline void lcd_ll_mem_set_low_power_mode(lcd_cam_dev_t *dev, lcd_ll_mem_lp_mode_t mode)
{
(void)dev;
HP_SYSTEM.sys_lcdcam_mem_lp_ctrl.sys_lcdcam_mem_lp_mode = mode;
}
/**
* @brief Enable the transfer buffer(memory block) for LCD module
*
* @note Only for RGB mode
* @param dev Peripheral instance address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_trans_buffer(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_trans_buff_cfg.lcd_trans_buffer_ena = en;
}
/**
* @brief Set the PCLK clock level state when there's no transaction undergoing
*
* @param dev LCD register base address
* @param level 1 is high level, 0 is low level
*/
__attribute__((always_inline))
static inline void lcd_ll_set_clock_idle_level(lcd_cam_dev_t *dev, bool level)
{
dev->lcd_clock.lcd_ck_idle_edge = level;
}
/**
* @brief Set the PCLK sample edge
*
* @param dev LCD register base address
* @param active_on_neg True: sample on negedge, False: sample on posedge
*/
__attribute__((always_inline))
static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_on_neg)
{
dev->lcd_clock.lcd_ck_out_edge = active_on_neg;
}
/**
* @brief Set PCLK prescale
*
* @param dev LCD register base address
* @param prescale Prescale value, PCLK = LCD_CLK / prescale
*/
__attribute__((always_inline))
static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale)
{
HAL_ASSERT(prescale > 0 && prescale <= LCD_LL_PCLK_DIV_MAX);
// Formula: pixel_clk = lcd_clk / (1 + clkcnt_n)
// clkcnt_n can't be zero
uint32_t scale = 1;
if (prescale == 1) {
dev->lcd_clock.lcd_clk_equ_sysclk = 1;
} else {
dev->lcd_clock.lcd_clk_equ_sysclk = 0;
scale = prescale - 1;
}
dev->lcd_clock.lcd_clkcnt_n = scale;
}
/**
* @brief Enable YUV-RGB converter
*
* @param dev LCD register base address
* @param en True to enable converter, False to disable converter
*/
static inline void lcd_ll_enable_color_convert(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_rgb_yuv.lcd_conv_enable = en;
}
/**
* @brief Set convert data line width
*
* @param dev LCD register base address
* @param width data line width
*/
static inline void lcd_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width)
{
HAL_ASSERT(width == 8 || width == 16);
dev->lcd_rgb_yuv.lcd_conv_mode_8bits_on = (width == 8) ? 1 : 0;
}
/**
* @brief Set the color range of input data
*
* @param dev LCD register base address
* @param range Color range
*/
static inline void lcd_ll_set_input_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
{
if (range == LCD_COLOR_RANGE_LIMIT) {
dev->lcd_rgb_yuv.lcd_conv_data_in_mode = 0;
} else if (range == LCD_COLOR_RANGE_FULL) {
dev->lcd_rgb_yuv.lcd_conv_data_in_mode = 1;
}
}
/**
* @brief Set the color range of output data
*
* @param dev LCD register base address
* @param range Color range
*/
static inline void lcd_ll_set_output_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
{
if (range == LCD_COLOR_RANGE_LIMIT) {
dev->lcd_rgb_yuv.lcd_conv_data_out_mode = 0;
} else if (range == LCD_COLOR_RANGE_FULL) {
dev->lcd_rgb_yuv.lcd_conv_data_out_mode = 1;
}
}
/**
* @brief Set YUV conversion standard
*
* @param dev LCD register base address
* @param std YUV conversion standard
*/
static inline void lcd_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, lcd_yuv_conv_std_t std)
{
if (std == LCD_YUV_CONV_STD_BT601) {
dev->lcd_rgb_yuv.lcd_conv_protocol_mode = 0;
} else if (std == LCD_YUV_CONV_STD_BT709) {
dev->lcd_rgb_yuv.lcd_conv_protocol_mode = 1;
}
}
/**
* @brief Set the converter mode: RGB to YUV
*
* @param dev LCD register base address
* @param in_color_format Input color format
* @param out_color_format Output color format
*/
static inline void lcd_ll_set_rgb2yuv_convert_mode(lcd_cam_dev_t *dev, lcd_color_format_t in_color_format, lcd_color_format_t out_color_format)
{
(void)in_color_format;
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
switch (out_color_format) {
case LCD_COLOR_FMT_YUV422_UYVY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
break;
case LCD_COLOR_FMT_YUV420_OUYY_EVYY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
break;
default:
abort();
}
}
/**
* @brief Set the converter mode: YUV to RGB
*
* @param dev LCD register base address
* @param in_color_format Input color format
* @param out_color_format Output color format
*/
static inline void lcd_ll_set_yuv2rgb_convert_mode(lcd_cam_dev_t *dev, lcd_color_format_t in_color_format, lcd_color_format_t out_color_format)
{
(void)out_color_format;
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 0;
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
switch (in_color_format) {
case LCD_COLOR_FMT_YUV422_UYVY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
break;
case LCD_COLOR_FMT_YUV420_OUYY_EVYY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
break;
default:
abort();
}
}
/**
* @brief Set the converter mode: YUV to YUV
*
* @param dev LCD register base address
* @param in_color_format Input color format
* @param out_color_format Output color format
*/
static inline void lcd_ll_set_yuv2yuv_convert_mode(lcd_cam_dev_t *dev, lcd_color_format_t in_color_format, lcd_color_format_t out_color_format)
{
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
switch (in_color_format) {
case LCD_COLOR_FMT_YUV422_UYVY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
break;
case LCD_COLOR_FMT_YUV420_OUYY_EVYY:
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
break;
default:
abort();
}
switch (out_color_format) {
case LCD_COLOR_FMT_YUV422_UYVY:
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 0;
break;
case LCD_COLOR_FMT_YUV420_OUYY_EVYY:
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 1;
break;
default:
abort();
}
}
/**
* @brief Set clock cycles of each transaction phases
*
* @param dev LCD register base address
* @param cmd_cycles Clock cycles of CMD phase
* @param dummy_cycles Clock cycles of DUMMY phase
* @param data_cycles Clock cycles of DATA phase
*/
__attribute__((always_inline))
static inline void lcd_ll_set_phase_cycles(lcd_cam_dev_t *dev, uint32_t cmd_cycles, uint32_t dummy_cycles, uint32_t data_cycles)
{
HAL_ASSERT(cmd_cycles <= 2);
dev->lcd_user.lcd_cmd = (cmd_cycles > 0);
dev->lcd_user.lcd_dummy = (dummy_cycles > 0);
dev->lcd_user.lcd_dout = (data_cycles > 0);
dev->lcd_user.lcd_cmd_2_cycle_en = cmd_cycles > 1;
if (dummy_cycles > 0) {
dev->lcd_user.lcd_dummy_cyclelen = dummy_cycles - 1;
}
if (data_cycles > 0) {
dev->lcd_user.lcd_dout_cyclelen = data_cycles - 1;
}
}
/**
* @brief Set clock cycles of blank phases
*
* @param dev LCD register base address
* @param fk_cycles Clock cycles of front blank
* @param bk_cycles Clock cycles of back blank
*/
static inline void lcd_ll_set_blank_cycles(lcd_cam_dev_t *dev, uint32_t fk_cycles, uint32_t bk_cycles)
{
dev->lcd_misc.lcd_bk_en = (fk_cycles || bk_cycles);
if (fk_cycles > 0) {
dev->lcd_misc.lcd_vfk_cyclelen = fk_cycles - 1;
}
if (bk_cycles > 0) {
dev->lcd_misc.lcd_vbk_cyclelen = bk_cycles - 1;
}
}
/**
* @brief Set data read stride, i.e., number of bytes the LCD reads from the DMA in each step
*
* @param dev LCD register base address
* @param stride data stride size, in bits
*/
static inline void lcd_ll_set_dma_read_stride(lcd_cam_dev_t *dev, uint32_t stride)
{
switch (stride) {
case 8:
dev->lcd_user.lcd_byte_mode = 0;
break;
case 16:
dev->lcd_user.lcd_byte_mode = 1;
break;
case 24:
dev->lcd_user.lcd_byte_mode = 2;
break;
case 32:
dev->lcd_user.lcd_byte_mode = 3;
break;
default:
abort();
}
}
/**
* @brief Set the wire width of LCD output
*
* @param dev LCD register base address
* @param width LCD output wire width
*/
static inline void lcd_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width)
{
switch (width) {
case 8:
dev->lcd_misc.lcd_wire_mode = 0;
break;
case 16:
dev->lcd_misc.lcd_wire_mode = 1;
break;
case 24:
dev->lcd_misc.lcd_wire_mode = 2;
break;
default:
abort();
}
}
/**
* @brief Whether to continue the data phase when the DMA has content to send
*
* @param dev LCD register base address
* @param en True: The number of data cycles will be controller by DMA buffer size, instead of lcd_dout_cyclelen
* False: The number of data cycles will be controlled by lcd_dout_cyclelen
*/
static inline void lcd_ll_enable_output_always_on(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_always_out_en = en;
}
/**
* @brief Start the LCD transaction
*
* @param dev LCD register base address
*/
__attribute__((always_inline))
static inline void lcd_ll_start(lcd_cam_dev_t *dev)
{
dev->lcd_user.lcd_update_reg = 1; // update parameters before start transaction
dev->lcd_user.lcd_start = 1;
}
/**
* @brief Stop the LCD transaction
*
* @param dev LCD register base address
*/
__attribute__((always_inline))
static inline void lcd_ll_stop(lcd_cam_dev_t *dev)
{
dev->lcd_user.lcd_start = 0;
dev->lcd_user.lcd_update_reg = 1; // self clear
}
/**
* @brief Reset LCD TX controller and RGB/YUV converter
*
* @param dev LCD register base address
*/
static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
{
dev->lcd_user.lcd_reset = 1; // self clear
}
/**
* @brief Whether to reverse the data bit order
*
* @note It acts before the YUV-RGB converter
*
* @param dev LCD register base address
* @param en True to reverse, False to not reverse
*/
__attribute__((always_inline))
static inline void lcd_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_bit_order = en;
}
/**
* @brief Whether to reverse the output data bit order
*
* @note It acts after the YUV-RGB converter
*
* @param dev LCD register base address
* @param en True to reverse, False to not reverse
*/
static inline void lcd_ll_reverse_wire_bit_order(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_dout_bit_order = en;
}
/**
* @brief Whether to swap adjacent two bytes
*
* @note This acts before the YUV-RGB converter, mainly to change the data endian.
* e.g. {B1,B0},{B3,B2} => {B0,B1}{B2,B3}.
* Only valid when `lcd_ll_set_dma_read_stride` set the DMA read stride >= 16 bits
*
* @param dev LCD register base address
* @param en True to swap the byte order, False to not swap
*/
__attribute__((always_inline))
static inline void lcd_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_byte_order = en;
}
/**
* @brief Enable the byte swizzle
*
* @note The swizzle module acts after the YUV-RGB converter, used to reorder the data bytes before the data output line
*
* @param dev LCD register base address
* @param en True to enable, False to disable
*/
__attribute__((always_inline))
static inline void lcd_ll_enable_swizzle(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_dout_byte_swizzle_enable = en;
}
/**
* @brief Set data byte swizzle mode
*
* @param dev LCD register base address
* @param mode Swizzle mode
*/
static inline void lcd_ll_set_swizzle_mode(lcd_cam_dev_t *dev, lcd_ll_swizzle_mode_t mode)
{
dev->lcd_user.lcd_dout_byte_swizzle_mode = mode;
}
/**
* @brief Reset Async TX FIFO
*
* @param dev LCD register base address
*/
__attribute__((always_inline))
static inline void lcd_ll_fifo_reset(lcd_cam_dev_t *dev)
{
dev->lcd_misc.lcd_afifo_reset = 1; // self clear
}
/**
* @brief Set the level state of DC line, on different transaction phases
*
* @param dev LCD register base address
* @param idle_phase Level state of DC line on IDLE phase
* @param cmd_phase Level state of DC line on CMD phase
* @param dummy_phase Level state of DC line on DUMMY phase
* @param data_phase Level state of DC line on DATA phase
*/
__attribute__((always_inline))
static inline void lcd_ll_set_dc_level(lcd_cam_dev_t *dev, bool idle_phase, bool cmd_phase, bool dummy_phase, bool data_phase)
{
dev->lcd_misc.lcd_cd_idle_edge = idle_phase;
dev->lcd_misc.lcd_cd_cmd_set = (cmd_phase != idle_phase);
dev->lcd_misc.lcd_cd_dummy_set = (dummy_phase != idle_phase);
dev->lcd_misc.lcd_cd_data_set = (data_phase != idle_phase);
}
/**
* @brief Set cycle of delay for DC line
*
* @param dev LCD register base address
* @param delay Ticks of delay
*/
static inline void lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
{
dev->lcd_dly_mode_cfg1.lcd_cd_mode = delay;
}
/**
* @brief Set the LCD command (the data at CMD phase)
*
* @param dev LCD register base address
* @param data_width Data line width
* @param command command value
*/
__attribute__((always_inline))
static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, uint32_t command)
{
// i80 interface only supports 8-bit or 16-bit data width
HAL_ASSERT(data_width == 8 || data_width == 16);
// if command phase has two cycles, in the first cycle we use lcd_first_cmd_val
// in the second cycle, we use lcd_latter_cmd_val
if (data_width == 8) {
dev->lcd_first_cmd_val.val = command & 0xFF;
dev->lcd_latter_cmd_val.val = (command >> 8) & 0xFF;
} else if (data_width == 16) {
dev->lcd_first_cmd_val.val = command;
}
}
/**
* @brief Whether to enable RGB interface
*
* @param dev LCD register base address
* @param en True to enable RGB interface, False to disable RGB interface
*/
static inline void lcd_ll_enable_rgb_mode(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_misc.lcd_rgb_mode_en = en;
}
/**
* @brief Whether to send the next frame automatically
*
* @param dev LCD register base address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
{
// in RGB mode, enabling "next frame" means LCD controller keeps sending frame data
dev->lcd_misc.lcd_next_frame_en = en;
}
/**
* @brief Whether to output HSYNC signal in porch resion
*
* @param dev LCD register base address
* @param en True to enable, False to disable
*/
static inline void lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_rgb_ctrl.lcd_hs_blank_en = en;
}
/**
* @brief Set HSYNC signal offset in the line
*
* @param dev LCD register base address
* @param offset_in_line Offset value
*/
static inline void lcd_ll_set_hsync_position(lcd_cam_dev_t *dev, uint32_t offset_in_line)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_ctrl, lcd_hsync_position, offset_in_line);
}
/**
* @brief Set RGB LCD horizontal timing
*
* @param dev LCD register base address
* @param hsw Horizontal sync width
* @param hbp Horizontal back porch
* @param active_width Horizontal active width
* @param hfp Horizontal front porch
*/
static inline void lcd_ll_set_horizontal_timing(lcd_cam_dev_t *dev, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp)
{
dev->lcd_rgb_ctrl.lcd_hsync_width = hsw - 1;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_blank, lcd_hb_front, hbp + hsw - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_horizontal, lcd_ha_width, active_width - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_horizontal, lcd_ht_width, hsw + hbp + active_width + hfp - 1);
}
/**
* @brief Set RGB vertical timing
*
* @param dev LCD register base address
* @param vsw Vertical sync width
* @param vbp Vertical back porch
* @param active_height Vertical active height
* @param vfp Vertical front porch
*/
static inline void lcd_ll_set_vertical_timing(lcd_cam_dev_t *dev, uint32_t vsw, uint32_t vbp, uint32_t active_height, uint32_t vfp)
{
dev->lcd_rgb_ctrl.lcd_vsync_width = vsw - 1;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_blank, lcd_vb_front, vbp + vsw - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_vertical, lcd_va_height, active_height - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_rgb_vertical, lcd_vt_height, vsw + vbp + active_height + vfp - 1);
}
/**
* @brief Set level state for hsync, vsync, de at IDLE phase
*
* @param dev LCD register base address
* @param hsync_idle_level HSYNC level on IDLE phase
* @param vsync_idle_level VSYNC level on IDLE phase
* @param de_idle_level DE level on IDLE phase
*/
static inline void lcd_ll_set_idle_level(lcd_cam_dev_t *dev, bool hsync_idle_level, bool vsync_idle_level, bool de_idle_level)
{
dev->lcd_rgb_ctrl.lcd_hsync_idle_pol = hsync_idle_level;
dev->lcd_rgb_ctrl.lcd_vsync_idle_pol = vsync_idle_level;
dev->lcd_rgb_ctrl.lcd_de_idle_pol = de_idle_level;
}
/**
* @brief Set extra delay for HSYNC, VSYNC, and DE signals
*
* @param dev LCD register base address
* @param hsync_delay HSYNC delay
* @param vsync_delay VSYNC delay
* @param de_delay DE delay
*/
static inline void lcd_ll_set_delay_ticks(lcd_cam_dev_t *dev, uint32_t hsync_delay, uint32_t vsync_delay, uint32_t de_delay)
{
dev->lcd_dly_mode_cfg1.lcd_hsync_mode = hsync_delay;
dev->lcd_dly_mode_cfg1.lcd_vsync_mode = vsync_delay;
dev->lcd_dly_mode_cfg1.lcd_de_mode = de_delay;
}
/**
* @brief Enable/disable interrupt by mask
*
* @param dev LCD register base address
* @param mask Interrupt mask
* @param en True to enable interrupt, False to disable interrupt
*/
static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en)
{
if (en) {
dev->lc_dma_int_ena.val |= mask & 0x13;
} else {
dev->lc_dma_int_ena.val &= ~(mask & 0x13);
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define lcd_ll_enable_interrupt(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
lcd_ll_enable_interrupt(__VA_ARGS__); \
} while(0)
/**
* @brief Get interrupt status value
*
* @param dev LCD register base address
* @return Interrupt status value
*/
__attribute__((always_inline))
static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
{
return dev->lc_dma_int_st.val & 0x13;
}
/**
* @brief Clear interrupt status by mask
*
* @param dev LCD register base address
* @param mask Interrupt status mask
*/
__attribute__((always_inline))
static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)
{
dev->lc_dma_int_clr.val = mask & 0x13;
}
/**
* @brief Get address of interrupt status register address
*
* @param dev LCD register base address
* @return Interrupt status register address
*/
static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
{
return &dev->lc_dma_int_st;
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,153 @@
/*
* SPDX-FileCopyrightText: 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/io_mux_reg.h"
#include "soc/gpio_num.h"
const soc_lcd_i80_signal_desc_t soc_lcd_i80_signals[1] = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,
.irq_id = ETS_LCD_CAM_INTR_SOURCE,
.data_sigs = {
LCD_DATA_OUT_PAD_OUT0_IDX,
LCD_DATA_OUT_PAD_OUT1_IDX,
LCD_DATA_OUT_PAD_OUT2_IDX,
LCD_DATA_OUT_PAD_OUT3_IDX,
LCD_DATA_OUT_PAD_OUT4_IDX,
LCD_DATA_OUT_PAD_OUT5_IDX,
LCD_DATA_OUT_PAD_OUT6_IDX,
LCD_DATA_OUT_PAD_OUT7_IDX,
LCD_DATA_OUT_PAD_OUT8_IDX,
LCD_DATA_OUT_PAD_OUT9_IDX,
LCD_DATA_OUT_PAD_OUT10_IDX,
LCD_DATA_OUT_PAD_OUT11_IDX,
LCD_DATA_OUT_PAD_OUT12_IDX,
LCD_DATA_OUT_PAD_OUT13_IDX,
LCD_DATA_OUT_PAD_OUT14_IDX,
LCD_DATA_OUT_PAD_OUT15_IDX,
LCD_DATA_OUT_PAD_OUT16_IDX,
LCD_DATA_OUT_PAD_OUT17_IDX,
LCD_DATA_OUT_PAD_OUT18_IDX,
LCD_DATA_OUT_PAD_OUT19_IDX,
LCD_DATA_OUT_PAD_OUT20_IDX,
LCD_DATA_OUT_PAD_OUT21_IDX,
LCD_DATA_OUT_PAD_OUT22_IDX,
LCD_DATA_OUT_PAD_OUT23_IDX,
},
.cs_sig = LCD_CS_PAD_OUT_IDX,
.dc_sig = LCD_DC_PAD_OUT_IDX,
.wr_sig = LCD_PCLK_PAD_OUT_IDX,
}
};
const soc_lcd_rgb_signal_desc_t soc_lcd_rgb_signals[1] = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,
.irq_id = ETS_LCD_CAM_INTR_SOURCE,
.data_sigs = {
LCD_DATA_OUT_PAD_OUT0_IDX,
LCD_DATA_OUT_PAD_OUT1_IDX,
LCD_DATA_OUT_PAD_OUT2_IDX,
LCD_DATA_OUT_PAD_OUT3_IDX,
LCD_DATA_OUT_PAD_OUT4_IDX,
LCD_DATA_OUT_PAD_OUT5_IDX,
LCD_DATA_OUT_PAD_OUT6_IDX,
LCD_DATA_OUT_PAD_OUT7_IDX,
LCD_DATA_OUT_PAD_OUT8_IDX,
LCD_DATA_OUT_PAD_OUT9_IDX,
LCD_DATA_OUT_PAD_OUT10_IDX,
LCD_DATA_OUT_PAD_OUT11_IDX,
LCD_DATA_OUT_PAD_OUT12_IDX,
LCD_DATA_OUT_PAD_OUT13_IDX,
LCD_DATA_OUT_PAD_OUT14_IDX,
LCD_DATA_OUT_PAD_OUT15_IDX,
LCD_DATA_OUT_PAD_OUT16_IDX,
LCD_DATA_OUT_PAD_OUT17_IDX,
LCD_DATA_OUT_PAD_OUT18_IDX,
LCD_DATA_OUT_PAD_OUT19_IDX,
LCD_DATA_OUT_PAD_OUT20_IDX,
LCD_DATA_OUT_PAD_OUT21_IDX,
LCD_DATA_OUT_PAD_OUT22_IDX,
LCD_DATA_OUT_PAD_OUT23_IDX,
},
.hsync_sig = LCD_H_SYNC_PAD_OUT_IDX,
.vsync_sig = LCD_V_SYNC_PAD_OUT_IDX,
.pclk_sig = LCD_PCLK_PAD_OUT_IDX,
.de_sig = LCD_H_ENABLE_PAD_OUT_IDX,
.disp_sig = SIG_GPIO_OUT_IDX,
}
};
const soc_lcd_rgb_iomux_desc_t soc_lcd_rgb_iomux_descs[1] = {
[0] = {
.data_pins = {
{ .gpio_num = GPIO_NUM_8, .func = FUNC_GPIO8_LCD_DATA0_OUT_PAD },
{ .gpio_num = GPIO_NUM_9, .func = FUNC_GPIO9_LCD_DATA1_OUT_PAD },
{ .gpio_num = GPIO_NUM_10, .func = FUNC_GPIO10_LCD_DATA2_OUT_PAD },
{ .gpio_num = GPIO_NUM_11, .func = FUNC_GPIO11_LCD_DATA3_OUT_PAD },
{ .gpio_num = GPIO_NUM_12, .func = FUNC_GPIO12_LCD_DATA4_OUT_PAD },
{ .gpio_num = GPIO_NUM_13, .func = FUNC_GPIO13_LCD_DATA5_OUT_PAD },
{ .gpio_num = GPIO_NUM_14, .func = FUNC_GPIO14_LCD_DATA6_OUT_PAD },
{ .gpio_num = GPIO_NUM_15, .func = FUNC_GPIO15_LCD_DATA7_OUT_PAD },
{ .gpio_num = GPIO_NUM_16, .func = FUNC_GPIO16_LCD_DATA8_OUT_PAD },
{ .gpio_num = GPIO_NUM_17, .func = FUNC_GPIO17_LCD_DATA9_OUT_PAD },
{ .gpio_num = GPIO_NUM_18, .func = FUNC_GPIO18_LCD_DATA10_OUT_PAD },
{ .gpio_num = GPIO_NUM_19, .func = FUNC_GPIO19_LCD_DATA11_OUT_PAD },
{ .gpio_num = GPIO_NUM_33, .func = FUNC_GPIO33_LCD_DATA12_OUT_PAD },
{ .gpio_num = GPIO_NUM_34, .func = FUNC_GPIO34_LCD_DATA13_OUT_PAD },
{ .gpio_num = GPIO_NUM_35, .func = FUNC_GPIO35_LCD_DATA14_OUT_PAD },
{ .gpio_num = GPIO_NUM_36, .func = FUNC_GPIO36_LCD_DATA15_OUT_PAD },
{ .gpio_num = GPIO_NUM_37, .func = FUNC_GPIO37_LCD_DATA16_OUT_PAD },
{ .gpio_num = GPIO_NUM_38, .func = FUNC_GPIO38_LCD_DATA17_OUT_PAD },
{ .gpio_num = GPIO_NUM_39, .func = FUNC_GPIO39_LCD_DATA18_OUT_PAD },
{ .gpio_num = GPIO_NUM_2, .func = FUNC_GPIO2_LCD_DATA19_OUT_PAD },
{ .gpio_num = GPIO_NUM_3, .func = FUNC_GPIO3_LCD_DATA20_OUT_PAD },
{ .gpio_num = GPIO_NUM_4, .func = FUNC_GPIO4_LCD_DATA21_OUT_PAD },
{ .gpio_num = GPIO_NUM_5, .func = FUNC_GPIO5_LCD_DATA22_OUT_PAD },
{ .gpio_num = GPIO_NUM_7, .func = FUNC_GPIO7_LCD_DATA23_OUT_PAD },
},
.hsync_pin = { .gpio_num = GPIO_NUM_44, .func = FUNC_GPIO44_LCD_H_SYNC_PAD },
.vsync_pin = { .gpio_num = GPIO_NUM_45, .func = FUNC_GPIO45_LCD_V_SYNC_PAD },
.pclk_pin = { .gpio_num = GPIO_NUM_40, .func = FUNC_GPIO40_LCD_PCLK_PAD },
.de_pin = { .gpio_num = GPIO_NUM_43, .func = FUNC_GPIO43_LCD_H_ENABLE_PAD },
},
};
/**
* LCD_CAM Registers to be saved during sleep retention
* - LCD Clock Configuration registers: LCDCAM_LCD_CLOCK_REG (0x0)
* - LCD User Configuration registers: LCDCAM_LCD_USER_REG (0x14), LCDCAM_LCD_MISC_REG (0x18)
* - LCD Delay Mode Configuration registers: LCDCAM_LCD_DLY_MODE_CFG1_REG (0x34), LCDCAM_LCD_DLY_MODE_CFG2_REG (0x38)
* - LCD Transfer Buffer Configuration registers: LCDCAM_LCD_TRANS_BUFF_CFG_REG(0x3C)
* - 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 7
#define LCD_RETENTION_REGS_BASE (DR_REG_LCDCAM_BASE + 0x0)
static const uint32_t lcd_cam_regs_map[4] = {0x200e061, 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) | ENTRY(2),
},
};
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
},
};
@@ -6,6 +6,7 @@
#pragma once #pragma once
#include <stddef.h>
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/periph_defs.h" #include "soc/periph_defs.h"
#include "soc/regdma.h" #include "soc/regdma.h"
@@ -36,6 +37,24 @@ typedef struct {
} soc_lcd_rgb_signal_desc_t; } soc_lcd_rgb_signal_desc_t;
extern const soc_lcd_rgb_signal_desc_t soc_lcd_rgb_signals[LCD_LL_GET(RGB_PANEL_NUM)]; extern const soc_lcd_rgb_signal_desc_t soc_lcd_rgb_signals[LCD_LL_GET(RGB_PANEL_NUM)];
#if LCD_LL_SUPPORT(IOMUX)
typedef struct {
const int gpio_num;
const int func;
} soc_lcd_iomux_pin_desc_t;
typedef struct {
const soc_lcd_iomux_pin_desc_t data_pins[LCD_LL_GET(RGB_BUS_WIDTH)];
const soc_lcd_iomux_pin_desc_t hsync_pin;
const soc_lcd_iomux_pin_desc_t vsync_pin;
const soc_lcd_iomux_pin_desc_t pclk_pin;
const soc_lcd_iomux_pin_desc_t de_pin;
} soc_lcd_rgb_iomux_desc_t;
extern const soc_lcd_rgb_iomux_desc_t soc_lcd_rgb_iomux_descs[LCD_LL_GET(RGB_PANEL_NUM)];
#endif // LCD_LL_SUPPORT(IOMUX)
#endif // SOC_HAS(LCDCAM_RGB_LCD) #endif // SOC_HAS(LCDCAM_RGB_LCD)
#if SOC_HAS(LCDCAM_I80_LCD) #if SOC_HAS(LCDCAM_I80_LCD)
@@ -67,6 +67,7 @@ static inline void spimem_flash_ll_reset(spi_mem_dev_t *dev)
* *
* @return true if last command is done, otherwise false. * @return true if last command is done, otherwise false.
*/ */
__attribute__((always_inline))
static inline bool spimem_flash_ll_cmd_is_done(const spi_mem_dev_t *dev) static inline bool spimem_flash_ll_cmd_is_done(const spi_mem_dev_t *dev)
{ {
return (dev->cmd.val == 0); return (dev->cmd.val == 0);
@@ -421,6 +422,7 @@ static inline void spimem_flash_ll_program_page(spi_mem_dev_t *dev, const void *
* @param dev Beginning address of the peripheral registers. * @param dev Beginning address of the peripheral registers.
* @param pe_ops Is page program/erase operation or not. * @param pe_ops Is page program/erase operation or not.
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev, bool pe_ops) static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev, bool pe_ops)
{ {
uint32_t usr_pe = (pe_ops ? 0x60000 : 0x40000); uint32_t usr_pe = (pe_ops ? 0x60000 : 0x40000);
@@ -434,6 +436,7 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev, bool pe_ops)
* *
* @return true if the host is idle, otherwise false * @return true if the host is idle, otherwise false
*/ */
__attribute__((always_inline))
static inline bool spimem_flash_ll_host_idle(const spi_mem_dev_t *dev) static inline bool spimem_flash_ll_host_idle(const spi_mem_dev_t *dev)
{ {
return dev->cmd.mst_st == 0; return dev->cmd.mst_st == 0;
@@ -522,6 +525,7 @@ static inline void spimem_flash_ll_set_clock(spi_mem_dev_t *dev, spimem_flash_ll
* @param dev Beginning address of the peripheral registers. * @param dev Beginning address of the peripheral registers.
* @param bitlen Length of input, in bits. * @param bitlen Length of input, in bits.
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_miso_bitlen(spi_mem_dev_t *dev, uint32_t bitlen) static inline void spimem_flash_ll_set_miso_bitlen(spi_mem_dev_t *dev, uint32_t bitlen)
{ {
dev->user.usr_miso = bitlen > 0; dev->user.usr_miso = bitlen > 0;
@@ -549,6 +553,7 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
* @param command Command to send * @param command Command to send
* @param bitlen Length of the command * @param bitlen Length of the command
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen) static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{ {
dev->user.usr_command = 1; dev->user.usr_command = 1;
@@ -602,6 +607,7 @@ static inline void spimem_flash_ll_set_extra_address(spi_mem_dev_t *dev, uint32_
* @param dev Beginning address of the peripheral registers. * @param dev Beginning address of the peripheral registers.
* @param addr Address to send * @param addr Address to send
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_address(spi_mem_dev_t *dev, uint32_t addr) static inline void spimem_flash_ll_set_address(spi_mem_dev_t *dev, uint32_t addr)
{ {
dev->addr = addr; dev->addr = addr;
@@ -626,6 +632,7 @@ static inline void spimem_flash_ll_set_usr_address(spi_mem_dev_t *dev, uint32_t
* @param dev Beginning address of the peripheral registers. * @param dev Beginning address of the peripheral registers.
* @param dummy_n Cycles of dummy phases * @param dummy_n Cycles of dummy phases
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_dummy(spi_mem_dev_t *dev, uint32_t dummy_n) static inline void spimem_flash_ll_set_dummy(spi_mem_dev_t *dev, uint32_t dummy_n)
{ {
dev->user.usr_dummy = dummy_n ? 1 : 0; dev->user.usr_dummy = dummy_n ? 1 : 0;
@@ -720,6 +727,7 @@ static inline uint32_t spimem_flash_ll_calculate_clock_reg(uint8_t clkdiv)
* @param level 1: 1: output high, 0: output low * @param level 1: 1: output high, 0: output low
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_wp_level(spi_mem_dev_t *dev, bool level) static inline void spimem_flash_ll_set_wp_level(spi_mem_dev_t *dev, bool level)
{ {
dev->ctrl.wp_reg = level; dev->ctrl.wp_reg = level;
@@ -767,6 +775,7 @@ static inline void spimem_ctrlr_ll_unset_clock(uint8_t mspi_id)
/** /**
* @brief Reset whole memory spi * @brief Reset whole memory spi
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_sync_reset(void) static inline void spimem_flash_ll_sync_reset(void)
{ {
SPIMEM1.ctrl2.sync_reset = 0; SPIMEM1.ctrl2.sync_reset = 0;
@@ -785,6 +794,7 @@ static inline void spimem_flash_ll_sync_reset(void)
* @param user1_reg user1_reg * @param user1_reg user1_reg
* @param user2_reg user2_reg * @param user2_reg user2_reg
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_get_common_command_register_info(spi_mem_dev_t *dev, uint32_t *ctrl_reg, uint32_t *user_reg, uint32_t *user1_reg, uint32_t *user2_reg) static inline void spimem_flash_ll_get_common_command_register_info(spi_mem_dev_t *dev, uint32_t *ctrl_reg, uint32_t *user_reg, uint32_t *user1_reg, uint32_t *user2_reg)
{ {
*ctrl_reg = dev->ctrl.val; *ctrl_reg = dev->ctrl.val;
@@ -801,6 +811,7 @@ static inline void spimem_flash_ll_get_common_command_register_info(spi_mem_dev_
* @param user1_reg user1_reg * @param user1_reg user1_reg
* @param user2_reg user2_reg * @param user2_reg user2_reg
*/ */
__attribute__((always_inline))
static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_t *dev, uint32_t ctrl_reg, uint32_t user_reg, uint32_t user1_reg, uint32_t user2_reg) static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_t *dev, uint32_t ctrl_reg, uint32_t user_reg, uint32_t user1_reg, uint32_t user2_reg)
{ {
dev->ctrl.val = ctrl_reg; dev->ctrl.val = ctrl_reg;
@@ -2,11 +2,14 @@ set(srcs "test_app_main.c"
"test_dport.c" "test_dport.c"
"test_fp.c" "test_fp.c"
"test_dport_xt_highint5.S" "test_dport_xt_highint5.S"
"test_random.c"
"test_intr_alloc.c" "test_intr_alloc.c"
"test_rtc_wdt.c" "test_rtc_wdt.c"
) )
if(CONFIG_SOC_RNG_SUPPORTED)
list(APPEND srcs "test_random.c")
endif()
if(CONFIG_SOC_GP_LDO_SUPPORTED) if(CONFIG_SOC_GP_LDO_SUPPORTED)
list(APPEND srcs "test_ldo.c") list(APPEND srcs "test_ldo.c")
endif() endif()
+1 -1
View File
@@ -9,7 +9,7 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#if CONFIG_LCD_ENABLE_DEBUG_LOG #if CONFIG_LCD_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h // The local log level must be defined before including esp_log.h
// Set the maximum log level for gptimer driver // Set the maximum log level for dsi lcd driver
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#endif #endif
#include "hal/mipi_dsi_periph.h" #include "hal/mipi_dsi_periph.h"
@@ -273,6 +273,9 @@ 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(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"); 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; int bus_id = bus->bus_id;
PERIPH_RCC_ATOMIC() {
lcd_ll_enable_clock(bus->hal.dev, false);
}
#if I80_USE_RETENTION_LINK #if I80_USE_RETENTION_LINK
const periph_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module; const periph_retention_module_t module_id = soc_i80_lcd_retention_info[bus_id].retention_module;
if (sleep_retention_is_module_created(module_id)) { if (sleep_retention_is_module_created(module_id)) {
@@ -737,7 +740,7 @@ static void lcd_periph_trigger_quick_trans_done_event(esp_lcd_i80_bus_handle_t b
.length = 4, .length = 4,
.flags = { .flags = {
.mark_eof = true, // mark the "EOF" flag to trigger LCD EOF interrupt .mark_eof = true, // mark the "EOF" flag to trigger LCD EOF interrupt
.mark_final = true, // singly link list, mark final descriptor .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor
} }
}; };
gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL);
@@ -759,6 +762,7 @@ static void lcd_start_transaction(esp_lcd_i80_bus_t *bus, lcd_i80_trans_descript
lcd_ll_set_phase_cycles(bus->hal.dev, cmd_cycles, dummy_cycles, data_cycles); lcd_ll_set_phase_cycles(bus->hal.dev, cmd_cycles, dummy_cycles, data_cycles);
lcd_ll_set_blank_cycles(bus->hal.dev, 1, 1); lcd_ll_set_blank_cycles(bus->hal.dev, 1, 1);
lcd_ll_reset(bus->hal.dev);
// reset FIFO before starting a new transaction, in case there remains some dirty data in the FIFO because of the "fake trigger". // reset FIFO before starting a new transaction, in case there remains some dirty data in the FIFO because of the "fake trigger".
lcd_ll_fifo_reset(bus->hal.dev); lcd_ll_fifo_reset(bus->hal.dev);
+1 -1
View File
@@ -13,7 +13,7 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#if CONFIG_LCD_ENABLE_DEBUG_LOG #if CONFIG_LCD_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h // The local log level must be defined before including esp_log.h
// Set the maximum log level for gptimer driver // Set the maximum log level for i80 lcd driver
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#endif #endif
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
+117 -34
View File
@@ -12,7 +12,7 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#if CONFIG_LCD_ENABLE_DEBUG_LOG #if CONFIG_LCD_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h // The local log level must be defined before including esp_log.h
// Set the maximum log level for gptimer driver // Set the maximum log level for rgb lcd driver
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#endif #endif
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@@ -41,7 +41,6 @@
#include "esp_cache.h" #include "esp_cache.h"
#include "esp_memory_utils.h" #include "esp_memory_utils.h"
#include "hal/lcd_periph.h" #include "hal/lcd_periph.h"
#include "soc/io_mux_reg.h"
#include "hal/lcd_hal.h" #include "hal/lcd_hal.h"
#include "hal/lcd_ll.h" #include "hal/lcd_ll.h"
#include "hal/cache_hal.h" #include "hal/cache_hal.h"
@@ -95,6 +94,11 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *rgb_panel, const
static void lcd_rgb_panel_release_gpio(esp_rgb_panel_t *rgb_panel); static void lcd_rgb_panel_release_gpio(esp_rgb_panel_t *rgb_panel);
static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel); static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel);
static void rgb_lcd_default_isr_handler(void *args); static void rgb_lcd_default_isr_handler(void *args);
static esp_err_t lcd_rgb_panel_configure_gpio_matrix(const esp_lcd_rgb_panel_config_t *panel_config, int panel_id, uint64_t *gpio_reserve_mask);
#if LCD_LL_SUPPORT(IOMUX)
static bool lcd_rgb_panel_can_use_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc);
static esp_err_t lcd_rgb_panel_configure_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc, uint64_t *gpio_reserve_mask);
#endif // LCD_LL_SUPPORT(IOMUX)
struct esp_rgb_panel_t { struct esp_rgb_panel_t {
esp_lcd_panel_t base; // Base class of generic lcd panel esp_lcd_panel_t base; // Base class of generic lcd panel
@@ -207,6 +211,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
// flush data from cache to the physical memory // flush data from cache to the physical memory
if (rgb_panel->flags.fb_behind_cache) { if (rgb_panel->flags.fb_behind_cache) {
ESP_LOGD(TAG, "frame buffer %d at %p is behind the cache", i, rgb_panel->fbs[i]);
esp_cache_msync(rgb_panel->fbs[i], rgb_panel->fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); esp_cache_msync(rgb_panel->fbs[i], rgb_panel->fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
} }
} }
@@ -245,6 +250,10 @@ static esp_err_t lcd_rgb_panel_destroy(esp_rgb_panel_t *rgb_panel)
if (rgb_panel->clk_src) { if (rgb_panel->clk_src) {
esp_clk_tree_enable_src(rgb_panel->clk_src, false); esp_clk_tree_enable_src(rgb_panel->clk_src, false);
} }
// force power off LCD trans buffer power
lcd_ll_mem_force_low_power(rgb_panel->hal.dev);
// disable the LCD trans buffer
lcd_ll_enable_trans_buffer(rgb_panel->hal.dev, false);
if (rgb_panel->panel_id >= 0) { if (rgb_panel->panel_id >= 0) {
PERIPH_RCC_RELEASE_ATOMIC(soc_lcd_rgb_signals[rgb_panel->panel_id].module, ref_count) { PERIPH_RCC_RELEASE_ATOMIC(soc_lcd_rgb_signals[rgb_panel->panel_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
@@ -389,13 +398,19 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
PERIPH_RCC_ATOMIC() { PERIPH_RCC_ATOMIC() {
lcd_ll_enable_clock(hal->dev, true); lcd_ll_enable_clock(hal->dev, true);
} }
// power down memory during low power stage
lcd_ll_mem_set_low_power_mode(hal->dev, LCD_LL_MEM_LP_MODE_SHUT_DOWN);
// let PMU control the LCD trans buffer power
lcd_ll_mem_power_by_pmu(hal->dev);
// enable the LCD trans buffer to improve the performance and prevent underrun
lcd_ll_enable_trans_buffer(hal->dev, true);
// set clock source // set clock source
ret = lcd_rgb_panel_select_clock_src(rgb_panel, rgb_panel_config->clk_src); 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"); ESP_GOTO_ON_ERROR(ret, err, TAG, "set source clock failed");
// reset peripheral and FIFO after we select a correct clock source // reset peripheral and FIFO after we select a correct clock source
lcd_ll_fifo_reset(hal->dev);
lcd_ll_reset(hal->dev); lcd_ll_reset(hal->dev);
lcd_ll_fifo_reset(hal->dev);
// install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask) // install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask)
int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED; int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(soc_lcd_rgb_signals[panel_id].irq_id, isr_flags, ret = esp_intr_alloc_intrstatus(soc_lcd_rgb_signals[panel_id].irq_id, isr_flags,
@@ -592,8 +607,8 @@ static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel)
static esp_err_t rgb_panel_reset(esp_lcd_panel_t *panel) static esp_err_t rgb_panel_reset(esp_lcd_panel_t *panel)
{ {
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
lcd_ll_fifo_reset(rgb_panel->hal.dev);
lcd_ll_reset(rgb_panel->hal.dev); lcd_ll_reset(rgb_panel->hal.dev);
lcd_ll_fifo_reset(rgb_panel->hal.dev);
return ESP_OK; return ESP_OK;
} }
@@ -809,35 +824,16 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *rgb_panel, const
int panel_id = rgb_panel->panel_id; int panel_id = rgb_panel->panel_id;
// Set the number of output data lines // Set the number of output data lines
lcd_ll_set_data_wire_width(rgb_panel->hal.dev, panel_config->data_width); lcd_ll_set_data_wire_width(rgb_panel->hal.dev, panel_config->data_width);
// connect peripheral signals via GPIO matrix #if LCD_LL_SUPPORT(IOMUX)
for (size_t i = 0; i < panel_config->data_width; i++) { const soc_lcd_rgb_iomux_desc_t *iomux_desc = &soc_lcd_rgb_iomux_descs[panel_id];
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->data_gpio_nums[i])) { if (lcd_rgb_panel_can_use_iomux(panel_config, iomux_desc)) {
gpio_matrix_output(panel_config->data_gpio_nums[i], ESP_RETURN_ON_ERROR(lcd_rgb_panel_configure_iomux(panel_config, iomux_desc, &gpio_reserve_mask), TAG, "configure RGB iomux failed");
soc_lcd_rgb_signals[panel_id].data_sigs[i], false, false); ESP_LOGD(TAG, "RGB panel uses IOMUX (data_width=%zu)", panel_config->data_width);
gpio_reserve_mask |= (1ULL << panel_config->data_gpio_nums[i]); } else
} #endif // LCD_LL_SUPPORT(IOMUX)
} {
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->hsync_gpio_num)) { ESP_RETURN_ON_ERROR(lcd_rgb_panel_configure_gpio_matrix(panel_config, panel_id, &gpio_reserve_mask), TAG, "configure RGB GPIO matrix failed");
gpio_matrix_output(panel_config->hsync_gpio_num, ESP_LOGD(TAG, "RGB panel uses GPIO matrix (data_width=%zu)", panel_config->data_width);
soc_lcd_rgb_signals[panel_id].hsync_sig, false, false);
gpio_reserve_mask |= (1ULL << panel_config->hsync_gpio_num);
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->vsync_gpio_num)) {
gpio_matrix_output(panel_config->vsync_gpio_num,
soc_lcd_rgb_signals[panel_id].vsync_sig, false, false);
gpio_reserve_mask |= (1ULL << panel_config->vsync_gpio_num);
}
// PCLK may not be necessary in some cases (i.e. VGA output)
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->pclk_gpio_num)) {
gpio_matrix_output(panel_config->pclk_gpio_num,
soc_lcd_rgb_signals[panel_id].pclk_sig, false, false);
gpio_reserve_mask |= (1ULL << panel_config->pclk_gpio_num);
}
// DE signal might not be necessary for some RGB LCD
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->de_gpio_num)) {
gpio_matrix_output(panel_config->de_gpio_num,
soc_lcd_rgb_signals[panel_id].de_sig, false, false);
gpio_reserve_mask |= (1ULL << panel_config->de_gpio_num);
} }
// disp enable GPIO is optional, it is a general purpose output GPIO // disp enable GPIO is optional, it is a general purpose output GPIO
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->disp_gpio_num)) { if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->disp_gpio_num)) {
@@ -859,6 +855,93 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *rgb_panel, const
return ESP_OK; return ESP_OK;
} }
#if LCD_LL_SUPPORT(IOMUX)
static bool lcd_rgb_panel_can_use_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc)
{
for (size_t i = 0; i < panel_config->data_width; i++) {
if (panel_config->data_gpio_nums[i] != iomux_desc->data_pins[i].gpio_num) {
return false;
}
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->hsync_gpio_num) && panel_config->hsync_gpio_num != iomux_desc->hsync_pin.gpio_num) {
return false;
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->vsync_gpio_num) && panel_config->vsync_gpio_num != iomux_desc->vsync_pin.gpio_num) {
return false;
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->pclk_gpio_num) && panel_config->pclk_gpio_num != iomux_desc->pclk_pin.gpio_num) {
return false;
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->de_gpio_num) && panel_config->de_gpio_num != iomux_desc->de_pin.gpio_num) {
return false;
}
return true;
}
static esp_err_t lcd_rgb_panel_configure_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc, uint64_t *gpio_reserve_mask)
{
for (size_t i = 0; i < panel_config->data_width; i++) {
const soc_lcd_iomux_pin_desc_t *pin = &iomux_desc->data_pins[i];
ESP_RETURN_ON_ERROR(gpio_iomux_output(pin->gpio_num, pin->func), TAG, "set data[%zu] iomux failed", i);
*gpio_reserve_mask |= (1ULL << pin->gpio_num);
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->hsync_gpio_num)) {
ESP_RETURN_ON_ERROR(gpio_iomux_output(iomux_desc->hsync_pin.gpio_num, iomux_desc->hsync_pin.func), TAG, "set hsync iomux failed");
*gpio_reserve_mask |= (1ULL << panel_config->hsync_gpio_num);
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->vsync_gpio_num)) {
ESP_RETURN_ON_ERROR(gpio_iomux_output(iomux_desc->vsync_pin.gpio_num, iomux_desc->vsync_pin.func), TAG, "set vsync iomux failed");
*gpio_reserve_mask |= (1ULL << panel_config->vsync_gpio_num);
}
// PCLK may not be necessary in some cases (i.e. VGA output)
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->pclk_gpio_num)) {
ESP_RETURN_ON_ERROR(gpio_iomux_output(iomux_desc->pclk_pin.gpio_num, iomux_desc->pclk_pin.func), TAG, "set pclk iomux failed");
*gpio_reserve_mask |= (1ULL << panel_config->pclk_gpio_num);
}
// DE signal might not be necessary for some RGB LCD
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->de_gpio_num)) {
ESP_RETURN_ON_ERROR(gpio_iomux_output(iomux_desc->de_pin.gpio_num, iomux_desc->de_pin.func), TAG, "set de iomux failed");
*gpio_reserve_mask |= (1ULL << panel_config->de_gpio_num);
}
return ESP_OK;
}
#endif // LCD_LL_SUPPORT(IOMUX)
static esp_err_t lcd_rgb_panel_configure_gpio_matrix(const esp_lcd_rgb_panel_config_t *panel_config, int panel_id, uint64_t *gpio_reserve_mask)
{
// connect peripheral signals via GPIO matrix
for (size_t i = 0; i < panel_config->data_width; i++) {
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->data_gpio_nums[i])) {
gpio_matrix_output(panel_config->data_gpio_nums[i],
soc_lcd_rgb_signals[panel_id].data_sigs[i], false, false);
*gpio_reserve_mask |= (1ULL << panel_config->data_gpio_nums[i]);
}
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->hsync_gpio_num)) {
gpio_matrix_output(panel_config->hsync_gpio_num,
soc_lcd_rgb_signals[panel_id].hsync_sig, false, false);
*gpio_reserve_mask |= (1ULL << panel_config->hsync_gpio_num);
}
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->vsync_gpio_num)) {
gpio_matrix_output(panel_config->vsync_gpio_num,
soc_lcd_rgb_signals[panel_id].vsync_sig, false, false);
*gpio_reserve_mask |= (1ULL << panel_config->vsync_gpio_num);
}
// PCLK may not be necessary in some cases (i.e. VGA output)
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->pclk_gpio_num)) {
gpio_matrix_output(panel_config->pclk_gpio_num,
soc_lcd_rgb_signals[panel_id].pclk_sig, false, false);
*gpio_reserve_mask |= (1ULL << panel_config->pclk_gpio_num);
}
// DE signal might not be necessary for some RGB LCD
if (GPIO_IS_VALID_OUTPUT_GPIO(panel_config->de_gpio_num)) {
gpio_matrix_output(panel_config->de_gpio_num,
soc_lcd_rgb_signals[panel_id].de_sig, false, false);
*gpio_reserve_mask |= (1ULL << panel_config->de_gpio_num);
}
return ESP_OK;
}
static void lcd_rgb_panel_release_gpio(esp_rgb_panel_t *rgb_panel) static void lcd_rgb_panel_release_gpio(esp_rgb_panel_t *rgb_panel)
{ {
if (rgb_panel->gpio_reserve_mask) { if (rgb_panel->gpio_reserve_mask) {
@@ -1197,10 +1280,10 @@ static IRAM_ATTR void lcd_rgb_panel_try_restart_transmission(esp_rgb_panel_t *pa
static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel) static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel)
{ {
// reset FIFO of DMA and LCD, in case there remains old frame data // reset FIFO of DMA and LCD, in case there remains old frame data
gdma_reset(rgb_panel->dma_chan);
lcd_ll_stop(rgb_panel->hal.dev); lcd_ll_stop(rgb_panel->hal.dev);
lcd_ll_reset(rgb_panel->hal.dev); lcd_ll_reset(rgb_panel->hal.dev);
lcd_ll_fifo_reset(rgb_panel->hal.dev); lcd_ll_fifo_reset(rgb_panel->hal.dev);
gdma_reset(rgb_panel->dma_chan);
// pre-fill bounce buffers if needed // pre-fill bounce buffers if needed
if (rgb_panel->bb_size) { if (rgb_panel->bb_size) {
@@ -1,4 +1,4 @@
| Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | --------- |
This test app is used to test LCDs with intel 8080 interface. This test app is used to test LCDs with intel 8080 interface.
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -100,6 +100,28 @@ extern "C" {
#define TEST_LCD_DATA13_GPIO (25) #define TEST_LCD_DATA13_GPIO (25)
#define TEST_LCD_DATA14_GPIO (16) #define TEST_LCD_DATA14_GPIO (16)
#define TEST_LCD_DATA15_GPIO (17) #define TEST_LCD_DATA15_GPIO (17)
#elif CONFIG_IDF_TARGET_ESP32S31
#define TEST_LCD_BK_LIGHT_GPIO (1)
#define TEST_LCD_RST_GPIO (56)
#define TEST_LCD_CS_GPIO (2)
#define TEST_LCD_DC_GPIO (54)
#define TEST_LCD_PCLK_GPIO (5)
#define TEST_LCD_DATA0_GPIO (11)
#define TEST_LCD_DATA1_GPIO (52)
#define TEST_LCD_DATA2_GPIO (8)
#define TEST_LCD_DATA3_GPIO (50)
#define TEST_LCD_DATA4_GPIO (6)
#define TEST_LCD_DATA5_GPIO (48)
#define TEST_LCD_DATA6_GPIO (13)
#define TEST_LCD_DATA7_GPIO (46)
#define TEST_LCD_DATA8_GPIO (16)
#define TEST_LCD_DATA9_GPIO (9)
#define TEST_LCD_DATA10_GPIO (10)
#define TEST_LCD_DATA11_GPIO (17)
#define TEST_LCD_DATA12_GPIO (12)
#define TEST_LCD_DATA13_GPIO (18)
#define TEST_LCD_DATA14_GPIO (14)
#define TEST_LCD_DATA15_GPIO (15)
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: CC0-1.0 * SPDX-License-Identifier: CC0-1.0
*/ */
@@ -9,7 +9,6 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "unity.h" #include "unity.h"
#include "esp_random.h"
#include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_vendor.h"
#include "esp_lcd_nt35510.h" #include "esp_lcd_nt35510.h"
@@ -432,9 +431,9 @@ TEST_CASE("lcd_panel_with_i80_interface (st7789, 8bits)", "[lcd]")
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1); gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1);
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
} }
@@ -460,7 +459,7 @@ TEST_CASE("i80_lcd_send_colors_to_fixed_region", "[lcd]")
size_t color_size = (x_end - x_start) * (y_end - y_start) * 2; size_t color_size = (x_end - x_start) * (y_end - y_start) * 2;
void *color_data = malloc(color_size); void *color_data = malloc(color_size);
TEST_ASSERT_NOT_NULL(color_data); TEST_ASSERT_NOT_NULL(color_data);
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
memset(color_data, color_byte, color_size); memset(color_data, color_byte, color_size);
printf("creating i80 bus and IO driver\r\n"); printf("creating i80 bus and IO driver\r\n");
@@ -545,7 +544,7 @@ TEST_CASE("i80_lcd_send_colors_to_fixed_region", "[lcd]")
} }
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
// change to another color // change to another color
color_byte = esp_random() & 0xFF; color_byte = rand() & 0xFF;
memset(color_data, color_byte, color_size); memset(color_data, color_byte, color_size);
for (int i = 0; i < steps; i++) { for (int i = 0; i < steps; i++) {
TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step)); TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step));
@@ -3,6 +3,7 @@
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic @pytest.mark.generic
@@ -13,7 +14,7 @@ from pytest_embedded_idf.utils import idf_parametrize
], ],
indirect=True, indirect=True,
) )
@idf_parametrize('target', ['esp32', 'esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) @idf_parametrize('target', soc_filtered_targets('SOC_LCD_I80_SUPPORTED == 1'), indirect=['target'])
def test_i80_lcd(dut: Dut) -> None: def test_i80_lcd(dut: Dut) -> None:
dut.run_all_single_board_cases() dut.run_all_single_board_cases()
@@ -26,7 +27,11 @@ def test_i80_lcd(dut: Dut) -> None:
], ],
indirect=True, indirect=True,
) )
@idf_parametrize('target', ['esp32', 'esp32s2', 'esp32s3', 'esp32p4'], indirect=['target']) @idf_parametrize(
'target',
soc_filtered_targets('SOC_LCD_I80_SUPPORTED == 1 and SOC_FLASH_ENC_SUPPORTED == 1'),
indirect=['target'],
)
def test_i80_lcd_with_virt_flash_enc(dut: Dut) -> None: def test_i80_lcd_with_virt_flash_enc(dut: Dut) -> None:
print(' - Erase flash') print(' - Erase flash')
dut.serial.erase_flash() dut.serial.erase_flash()
@@ -1,4 +1,4 @@
| Supported Targets | ESP32-P4 | ESP32-S3 | | Supported Targets | ESP32-P4 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | -------- | | ----------------- | -------- | -------- | --------- |
This test app is used to test RGB565 interfaced LCDs. This test app is used to test RGB565 interfaced LCDs.
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -68,6 +68,30 @@ extern "C" {
#define TEST_LCD_DATA15_GPIO 19 // R4 #define TEST_LCD_DATA15_GPIO 19 // R4
#define TEST_LCD_DISP_EN_GPIO -1 #define TEST_LCD_DISP_EN_GPIO -1
#elif CONFIG_IDF_TARGET_ESP32S31
#define TEST_LCD_VSYNC_GPIO 45
#define TEST_LCD_HSYNC_GPIO 44
#define TEST_LCD_DE_GPIO 43
#define TEST_LCD_PCLK_GPIO 40
#define TEST_LCD_DATA0_GPIO 11 // B3
#define TEST_LCD_DATA1_GPIO 12 // B4
#define TEST_LCD_DATA2_GPIO 13 // B5
#define TEST_LCD_DATA3_GPIO 14 // B6
#define TEST_LCD_DATA4_GPIO 15 // B7
#define TEST_LCD_DATA5_GPIO 18 // G2
#define TEST_LCD_DATA6_GPIO 19 // G3
#define TEST_LCD_DATA7_GPIO 33 // G4
#define TEST_LCD_DATA8_GPIO 34 // G5
#define TEST_LCD_DATA9_GPIO 35 // G6
#define TEST_LCD_DATA10_GPIO 36 // G7
#define TEST_LCD_DATA11_GPIO 2 // R3
#define TEST_LCD_DATA12_GPIO 3 // R4
#define TEST_LCD_DATA13_GPIO 4 // R5
#define TEST_LCD_DATA14_GPIO 5 // R6
#define TEST_LCD_DATA15_GPIO 7 // R7
#define TEST_LCD_DISP_EN_GPIO -1
#else #else
#error "Unsupported target" #error "Unsupported target"
#endif #endif
@@ -11,7 +11,6 @@
#include "unity.h" #include "unity.h"
#include "esp_lcd_panel_rgb.h" #include "esp_lcd_panel_rgb.h"
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.h"
#include "esp_random.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "test_rgb_board.h" #include "test_rgb_board.h"
@@ -101,9 +100,9 @@ TEST_CASE("lcd_rgb_panel_stream_mode", "[lcd]")
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL);
printf("flush random color block\r\n"); printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
@@ -121,10 +120,10 @@ TEST_CASE("lcd_rgb_panel_8bit_interface", "[lcd]")
printf("initialize RGB panel with stream mode\r\n"); printf("initialize RGB panel with stream mode\r\n");
// bpp for RGB888 is 24 // bpp for RGB888 is 24
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(8, LCD_COLOR_FMT_RGB888, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(8, LCD_COLOR_FMT_RGB888, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL);
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
printf("flush random color block 0x%x\r\n", color_byte); printf("flush random color block 0x%x\r\n", color_byte);
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, 100 * 100 * 3); memset(img, color_byte, 100 * 100 * 3);
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
vTaskDelay(pdMS_TO_TICKS(2000)); vTaskDelay(pdMS_TO_TICKS(2000));
@@ -151,9 +150,9 @@ TEST_CASE("lcd_rgb_panel_refresh_on_demand", "[lcd]")
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, true, false, test_rgb_panel_trans_done, cur_task); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, true, false, test_rgb_panel_trans_done, cur_task);
printf("flush random color block\r\n"); printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
esp_lcd_rgb_panel_refresh(panel_handle); esp_lcd_rgb_panel_refresh(panel_handle);
@@ -176,9 +175,9 @@ TEST_CASE("lcd_rgb_panel_bounce_buffer", "[lcd]")
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 20 * TEST_LCD_H_RES, LCD_CLK_SRC_DEFAULT, false, false, test_rgb_panel_trans_done, cur_task); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 20 * TEST_LCD_H_RES, LCD_CLK_SRC_DEFAULT, false, false, test_rgb_panel_trans_done, cur_task);
printf("flush random color block\r\n"); printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
// wait for flush done // wait for flush done
@@ -198,9 +197,9 @@ TEST_CASE("lcd_rgb_panel_update_pclk", "[lcd]")
printf("initialize RGB panel with stream mode\r\n"); printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL);
printf("flush one clock block to the LCD\r\n"); printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n"); printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n");
@@ -226,9 +225,9 @@ TEST_CASE("lcd_rgb_panel_restart", "[lcd]")
printf("initialize RGB panel with stream mode\r\n"); printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, NULL, NULL);
printf("flush one clock block to the LCD\r\n"); printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n"); printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n");
@@ -250,7 +249,7 @@ TEST_CASE("lcd_rgb_panel_rotate", "[lcd]")
uint64_t t = 0; uint64_t t = 0;
uint8_t *img = malloc(w * h * sizeof(uint16_t)); uint8_t *img = malloc(w * h * sizeof(uint16_t));
TEST_ASSERT_NOT_NULL(img); TEST_ASSERT_NOT_NULL(img);
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
memset(img, color_byte, w * h * sizeof(uint16_t)); memset(img, color_byte, w * h * sizeof(uint16_t));
printf("initialize RGB panel with stream mode\r\n"); printf("initialize RGB panel with stream mode\r\n");
@@ -282,9 +281,9 @@ TEST_CASE("lcd_rgb_panel_user_frame_buffer", "[lcd]")
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, true, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, true, NULL, NULL);
printf("flush one clock block to the LCD\r\n"); printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n"); printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n");
@@ -316,9 +315,9 @@ TEST_CASE("lcd_rgb_panel_use_apll", "[lcd]")
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_APLL, false, false, NULL, NULL); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_APLL, false, false, NULL, NULL);
printf("flush random color block\r\n"); printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
@@ -356,9 +355,9 @@ TEST_CASE("lcd_rgb_panel_iram_safe", "[lcd]")
printf("initialize RGB panel with stream mode\r\n"); printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, test_rgb_panel_count_in_callback, &callback_calls); esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 0, LCD_CLK_SRC_DEFAULT, false, false, test_rgb_panel_count_in_callback, &callback_calls);
printf("flush one clock block to the LCD\r\n"); printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF; uint8_t color_byte = rand() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100); int x_start = rand() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100); int y_start = rand() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE); 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); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n"); printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n");
@@ -3,6 +3,7 @@
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.octal_psram @pytest.mark.octal_psram
@@ -50,8 +51,8 @@ def test_rgb_lcd_esp32s3_with_virt_flash_enc(dut: Dut) -> None:
], ],
indirect=True, indirect=True,
) )
@idf_parametrize('target', ['esp32p4'], indirect=['target']) @idf_parametrize('target', ['esp32p4', 'esp32s31'], indirect=['target'])
def test_rgb_lcd_esp32p4(dut: Dut) -> None: def test_rgb_lcd(dut: Dut) -> None:
dut.run_all_single_board_cases() dut.run_all_single_board_cases()
@@ -63,8 +64,12 @@ def test_rgb_lcd_esp32p4(dut: Dut) -> None:
], ],
indirect=True, indirect=True,
) )
@idf_parametrize('target', ['esp32p4'], indirect=['target']) @idf_parametrize(
def test_rgb_lcd_esp32p4_with_virt_flash_enc(dut: Dut) -> None: 'target',
soc_filtered_targets('IDF_TARGET in ["esp32p4", "esp32s31"] and SOC_FLASH_ENC_SUPPORTED == 1'),
indirect=['target'],
)
def test_rgb_lcd_with_virt_flash_enc(dut: Dut) -> None:
print(' - Erase flash') print(' - Erase flash')
dut.serial.erase_flash() dut.serial.erase_flash()
@@ -2,5 +2,5 @@ CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y CONFIG_SPIRAM_SPEED_80M=y
# Enable the XIP-PSRAM feature, so the ext-mem cache won't be disabled when SPI1 is operating the main flash # Enable the XIP-PSRAM feature, to increase PSRAM bandwidth
CONFIG_SPIRAM_XIP_FROM_PSRAM=y CONFIG_SPIRAM_XIP_FROM_PSRAM=y
@@ -0,0 +1,6 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_250M=y
# Enable the XIP-PSRAM feature, to increase PSRAM bandwidth
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
@@ -34,6 +34,7 @@ extern "C" {
* *
* @return linear address * @return linear address
*/ */
__attribute__((always_inline))
static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr) static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr)
{ {
return vaddr & SOC_MMU_LINEAR_ADDR_MASK; return vaddr & SOC_MMU_LINEAR_ADDR_MASK;
@@ -39,6 +39,26 @@ config SOC_GPTIMER_SUPPORTED
bool bool
default y default y
config SOC_LCDCAM_SUPPORTED
bool
default y
config SOC_LCDCAM_I80_LCD_SUPPORTED
bool
default y
config SOC_LCDCAM_RGB_LCD_SUPPORTED
bool
default y
config SOC_LCD_I80_SUPPORTED
bool
default y
config SOC_LCD_RGB_SUPPORTED
bool
default y
config SOC_MCPWM_SUPPORTED config SOC_MCPWM_SUPPORTED
bool bool
default y default y
@@ -161,6 +161,23 @@ typedef enum {
SOC_MOD_CLK_INVALID, /*!< Indication of the end of the available module clock sources */ SOC_MOD_CLK_INVALID, /*!< Indication of the end of the available module clock sources */
} soc_module_clk_t; } soc_module_clk_t;
//////////////////////////////////////////////////LCD///////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of LCD
*/
#define SOC_LCD_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, SOC_MOD_CLK_APLL}
/**
* @brief Type of LCD clock source
*/
typedef enum {
LCD_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
LCD_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
LCD_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
} soc_periph_lcd_clk_src_t;
//////////////////////////////////////////////////SYSTIMER////////////////////////////////////////////////////////////// //////////////////////////////////////////////////SYSTIMER//////////////////////////////////////////////////////////////
/** /**
@@ -14,6 +14,7 @@ extern "C" {
typedef enum { typedef enum {
/* HP peripherals */ /* HP peripherals */
PERIPH_LCD_CAM_MODULE,
PERIPH_TIMG0_MODULE, PERIPH_TIMG0_MODULE,
PERIPH_TIMG1_MODULE, PERIPH_TIMG1_MODULE,
PERIPH_UHCI0_MODULE, PERIPH_UHCI0_MODULE,
@@ -61,10 +61,9 @@ typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MCPWM1 = 36, SLEEP_RETENTION_MODULE_MCPWM1 = 36,
SLEEP_RETENTION_MODULE_MCPWM2 = 37, SLEEP_RETENTION_MODULE_MCPWM2 = 37,
SLEEP_RETENTION_MODULE_MCPWM3 = 38, SLEEP_RETENTION_MODULE_MCPWM3 = 38,
SLEEP_RETENTION_MODULE_PCNT0 = 39, SLEEP_RETENTION_MODULE_SDM0 = 39,
SLEEP_RETENTION_MODULE_PCNT1 = 40, SLEEP_RETENTION_MODULE_LCDCAM = 40,
SLEEP_RETENTION_MODULE_SDM0 = 41, SLEEP_RETENTION_MODULE_JPEG = 41,
SLEEP_RETENTION_MODULE_JPEG = 42,
/* Modem module, which includes WiFi, BLE and 802.15.4 */ /* Modem module, which includes WiFi, BLE and 802.15.4 */
SLEEP_RETENTION_MODULE_WIFI_MAC = 43, SLEEP_RETENTION_MODULE_WIFI_MAC = 43,
@@ -35,10 +35,12 @@
#define SOC_DMA2D_SUPPORTED 1 #define SOC_DMA2D_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1
// #define SOC_PCNT_SUPPORTED 1 // TODO: [ESP32S31] IDF-14699 // #define SOC_PCNT_SUPPORTED 1 // TODO: [ESP32S31] IDF-14699
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 #define SOC_LCDCAM_SUPPORTED 1
// #define SOC_LCDCAM_CAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 // #define SOC_LCDCAM_CAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722
// #define SOC_LCDCAM_I80_LCD_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 #define SOC_LCDCAM_I80_LCD_SUPPORTED 1
// #define SOC_LCDCAM_RGB_LCD_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722 #define SOC_LCDCAM_RGB_LCD_SUPPORTED 1
#define SOC_LCD_I80_SUPPORTED 1
#define SOC_LCD_RGB_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1 #define SOC_TWAI_SUPPORTED 1
#define SOC_TWAI_FD_SUPPORTED 1 #define SOC_TWAI_FD_SUPPORTED 1
@@ -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 OR MIT * SPDX-License-Identifier: Apache-2.0 OR MIT
*/ */
@@ -953,7 +953,7 @@ typedef union {
} lcdcam_lc_reg_date_reg_t; } lcdcam_lc_reg_date_reg_t;
typedef struct { typedef struct lcd_cam_dev_t {
volatile lcdcam_lcd_clock_reg_t lcd_clock; volatile lcdcam_lcd_clock_reg_t lcd_clock;
volatile lcdcam_cam_ctrl_reg_t cam_ctrl; volatile lcdcam_cam_ctrl_reg_t cam_ctrl;
volatile lcdcam_cam_ctrl1_reg_t cam_ctrl1; volatile lcdcam_cam_ctrl1_reg_t cam_ctrl1;
@@ -980,11 +980,12 @@ typedef struct {
volatile lcdcam_lc_dma_int_clr_reg_t lc_dma_int_clr; volatile lcdcam_lc_dma_int_clr_reg_t lc_dma_int_clr;
uint32_t reserved_074[34]; uint32_t reserved_074[34];
volatile lcdcam_lc_reg_date_reg_t lc_reg_date; volatile lcdcam_lc_reg_date_reg_t lc_reg_date;
} lcdcam_dev_t; } lcd_cam_dev_t;
extern lcd_cam_dev_t LCD_CAM;
#ifndef __cplusplus #ifndef __cplusplus
_Static_assert(sizeof(lcdcam_dev_t) == 0x100, "Invalid size of lcdcam_dev_t structure"); _Static_assert(sizeof(lcd_cam_dev_t) == 0x100, "Invalid size of lcdcam_dev_t structure");
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
@@ -40,7 +40,7 @@
#define DR_REG_SOC_ETM_BASE 0x20393000 #define DR_REG_SOC_ETM_BASE 0x20393000
#define DR_REG_TWAIFD0_BASE 0x20394000 #define DR_REG_TWAIFD0_BASE 0x20394000
#define DR_REG_TWAIFD1_BASE 0x20395000 #define DR_REG_TWAIFD1_BASE 0x20395000
#define DR_REG_LCD_CAM_BASE 0x20396000 #define DR_REG_LCDCAM_BASE 0x20396000
#define DR_REG_UHCI0_BASE 0x20398000 #define DR_REG_UHCI0_BASE 0x20398000
#define DR_REG_SYSTIMER_BASE 0x20399000 #define DR_REG_SYSTIMER_BASE 0x20399000
#define DR_REG_ZERO_DET_BASE 0x2039A000 #define DR_REG_ZERO_DET_BASE 0x2039A000
+1
View File
@@ -1,5 +1,6 @@
INPUT += \ INPUT += \
$(PROJECT_PATH)/components/esp_hal_dma/esp32s31/include/hal/bitscrambler_peri_select.h \ $(PROJECT_PATH)/components/esp_hal_dma/esp32s31/include/hal/bitscrambler_peri_select.h \
$(PROJECT_PATH)/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_encode.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_encode.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \ $(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \
@@ -16,6 +16,49 @@ RGB LCD panel is created by :cpp:func:`esp_lcd_new_rgb_panel`, with various conf
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` specifies how many frame buffers the driver should allocate. For backward compatibility, setting this to ``0`` will allocate a single frame buffer. If you don't want to allocate any frame buffer, use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` instead. - :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` specifies how many frame buffers the driver should allocate. For backward compatibility, setting this to ``0`` will allocate a single frame buffer. If you don't want to allocate any frame buffer, use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` instead.
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` determines whether frame buffer will be allocated. When it is set, no frame buffer will be allocated. This is also called the :ref:`bounce_buffer_only` mode. - :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` determines whether frame buffer will be allocated. When it is set, no frame buffer will be allocated. This is also called the :ref:`bounce_buffer_only` mode.
GPIO Matrix and IOMUX Pins
--------------------------
.. only:: esp32s31
On {IDF_TARGET_NAME}, the RGB LCD driver can use IOMUX automatically when all required signals are mapped to dedicated pins:
- If ``data_gpio_nums``, ``hsync_gpio_num``, ``vsync_gpio_num``, ``pclk_gpio_num``, and ``de_gpio_num`` all match their dedicated IOMUX pins, the driver bypasses the GPIO matrix automatically.
- If any one of these signals does not match the dedicated IOMUX pin, the driver falls back to GPIO matrix routing automatically.
For higher pixel clock configurations, using IOMUX pins is recommended for better timing robustness.
Dedicated RGB IOMUX pins on {IDF_TARGET_NAME} are listed below:
- When ``data_width = 8``, ``DATA[0:7]`` is used
- When ``data_width = 16``, ``DATA[0:15]`` is used
- When ``data_width = 24``, ``DATA[0:23]`` is used
.. list-table::
:widths: 35 65
:header-rows: 1
* - Signal
- GPIO
* - DATA[0:7]
- 8, 9, 10, 11, 12, 13, 14, 15
* - DATA[8:15]
- 16, 17, 18, 19, 33, 34, 35, 36
* - DATA[16:23]
- 37, 38, 39, 2, 3, 4, 5, 7
* - HSYNC
- 44
* - VSYNC
- 45
* - PCLK
- 40
* - DE
- 43
.. only:: not esp32s31
On {IDF_TARGET_NAME}, RGB signals are routed through the GPIO matrix.
RGB LCD Frame Buffer Operation Modes RGB LCD Frame Buffer Operation Modes
------------------------------------ ------------------------------------
@@ -16,6 +16,49 @@ RGB LCD 面板的分配步骤只需一步,即调用函数 :cpp:func:`esp_lcd_n
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` 设置由驱动程序分配的 frame buffer 的数量。为了向后兼容,``0`` 表示分配 ``一个`` frame buffer。如果不想分配任何 frame buffer,请设置 :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` - :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` 设置由驱动程序分配的 frame buffer 的数量。为了向后兼容,``0`` 表示分配 ``一个`` frame buffer。如果不想分配任何 frame buffer,请设置 :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb`
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` 可决定是否分配 frame buffer。设置该字段后将不分配 frame buffer。这也被称为 :ref:`bounce_buffer_only` 模式。 - :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` 可决定是否分配 frame buffer。设置该字段后将不分配 frame buffer。这也被称为 :ref:`bounce_buffer_only` 模式。
GPIO 矩阵与 IOMUX 管脚
----------------------
.. only:: esp32s31
{IDF_TARGET_NAME} 上,RGB LCD 驱动支持在满足专用管脚条件时自动使用 IOMUX,无需额外配置开关:
- 当 ``data_gpio_nums````hsync_gpio_num````vsync_gpio_num````pclk_gpio_num````de_gpio_num`` 都配置为 RGB 外设的专用 IOMUX 管脚时,驱动会自动绕过 GPIO 矩阵。
- 只要上述任一信号未使用专用 IOMUX 管脚,驱动就会自动回退为 GPIO 矩阵路由。
在较高像素时钟下,建议优先使用 IOMUX 管脚以获得更稳定的时序表现。
{IDF_TARGET_NAME} 上 RGB 接口可用的专用 IOMUX 管脚如下:
- 当 ``data_width = 8`` 时,使用 ``DATA[0:7]``
- 当 ``data_width = 16`` 时,使用 ``DATA[0:15]``
- 当 ``data_width = 24`` 时,使用 ``DATA[0:23]``
.. list-table::
:widths: 35 65
:header-rows: 1
* - 信号
- GPIO
* - DATA[0:7]
- 8, 9, 10, 11, 12, 13, 14, 15
* - DATA[8:15]
- 16, 17, 18, 19, 33, 34, 35, 36
* - DATA[16:23]
- 37, 38, 39, 2, 3, 4, 5, 7
* - HSYNC
- 44
* - VSYNC
- 45
* - PCLK
- 40
* - DE
- 43
.. only:: not esp32s31
在 {IDF_TARGET_NAME} 上,RGB 信号通过 GPIO 矩阵路由。
RGB LCD frame buffer 操作模式 RGB LCD frame buffer 操作模式
------------------------------ ------------------------------
@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | --------- |
# i80 LCD LVGL porting example # i80 LCD LVGL porting example
@@ -0,0 +1,20 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_250M=y
# Enabling the following configurations can help increase the PCLK frequency in the case when
# the Frame Buffer is allocated from the PSRAM and fetched by EDMA
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_EXAMPLE_PIN_NUM_BK_LIGHT=1
CONFIG_EXAMPLE_PIN_NUM_RST=56
CONFIG_EXAMPLE_PIN_NUM_CS=2
CONFIG_EXAMPLE_PIN_NUM_DC=54
CONFIG_EXAMPLE_PIN_NUM_PCLK=5
CONFIG_EXAMPLE_PIN_NUM_DATA0=11
CONFIG_EXAMPLE_PIN_NUM_DATA1=52
CONFIG_EXAMPLE_PIN_NUM_DATA2=8
CONFIG_EXAMPLE_PIN_NUM_DATA3=50
CONFIG_EXAMPLE_PIN_NUM_DATA4=6
CONFIG_EXAMPLE_PIN_NUM_DATA5=48
CONFIG_EXAMPLE_PIN_NUM_DATA6=13
CONFIG_EXAMPLE_PIN_NUM_DATA7=46
+2 -2
View File
@@ -1,5 +1,5 @@
| Supported Targets | ESP32-P4 | ESP32-S3 | | Supported Targets | ESP32-P4 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | -------- | | ----------------- | -------- | -------- | --------- |
# RGB LCD Panel Example # RGB LCD Panel Example
@@ -37,8 +37,8 @@ def test_rgb_lcd_lvgl_esp32s3(dut: Dut) -> None:
], ],
indirect=True, indirect=True,
) )
@idf_parametrize('target', ['esp32p4'], indirect=['target']) @idf_parametrize('target', ['esp32p4', 'esp32s31'], indirect=['target'])
def test_rgb_lcd_lvgl_esp32p4(dut: Dut) -> None: def test_rgb_lcd_lvgl(dut: Dut) -> None:
dut.expect_exact('example: Turn off LCD backlight') dut.expect_exact('example: Turn off LCD backlight')
dut.expect_exact('example: Install RGB LCD panel driver') dut.expect_exact('example: Install RGB LCD panel driver')
dut.expect_exact('example: Initialize RGB LCD panel') dut.expect_exact('example: Initialize RGB LCD panel')
@@ -0,0 +1,46 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_250M=y
# LCD_CAM support 24 data lines at most
CONFIG_EXAMPLE_LCD_DATA_LINES_24=y
CONFIG_LV_COLOR_DEPTH_24=y
# Enabling the following configurations can help increase the PCLK frequency in the case when
# the Frame Buffer is allocated from the PSRAM and fetched by EDMA
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
# Default GPIO assignment
CONFIG_EXAMPLE_LCD_VSYNC_GPIO=45
CONFIG_EXAMPLE_LCD_HSYNC_GPIO=44
CONFIG_EXAMPLE_LCD_DE_GPIO=43
CONFIG_EXAMPLE_LCD_PCLK_GPIO=40
# B0:B7 <=> DATA0:DATA7
CONFIG_EXAMPLE_LCD_DATA0_GPIO=8
CONFIG_EXAMPLE_LCD_DATA1_GPIO=9
CONFIG_EXAMPLE_LCD_DATA2_GPIO=10
CONFIG_EXAMPLE_LCD_DATA3_GPIO=11
CONFIG_EXAMPLE_LCD_DATA4_GPIO=12
CONFIG_EXAMPLE_LCD_DATA5_GPIO=13
CONFIG_EXAMPLE_LCD_DATA6_GPIO=14
CONFIG_EXAMPLE_LCD_DATA7_GPIO=15
# G0:G7 <=> DATA8:DATA15
CONFIG_EXAMPLE_LCD_DATA8_GPIO=16
CONFIG_EXAMPLE_LCD_DATA9_GPIO=17
CONFIG_EXAMPLE_LCD_DATA10_GPIO=18
CONFIG_EXAMPLE_LCD_DATA11_GPIO=19
CONFIG_EXAMPLE_LCD_DATA12_GPIO=33
CONFIG_EXAMPLE_LCD_DATA13_GPIO=34
CONFIG_EXAMPLE_LCD_DATA14_GPIO=35
CONFIG_EXAMPLE_LCD_DATA15_GPIO=36
# R0:R7 <=> DATA16:DATA23
CONFIG_EXAMPLE_LCD_DATA16_GPIO=37
CONFIG_EXAMPLE_LCD_DATA17_GPIO=38
CONFIG_EXAMPLE_LCD_DATA18_GPIO=39
CONFIG_EXAMPLE_LCD_DATA19_GPIO=2
CONFIG_EXAMPLE_LCD_DATA20_GPIO=3
CONFIG_EXAMPLE_LCD_DATA21_GPIO=4
CONFIG_EXAMPLE_LCD_DATA22_GPIO=5
CONFIG_EXAMPLE_LCD_DATA23_GPIO=7