boards/risc-v: LCD support on esp32p4-tab5

Adds support for st7121 and st7123 variants for LCD support
on the esp32p4-tab5 board. Includes power management though IO
expander under 'hmi_power' and new board KConfig options.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
Filipe Cavalcanti 2026-08-05 22:32:12 -03:00 committed by Matteo Golin
parent 8b73477c5e
commit 2bf531b3f4
13 changed files with 1990 additions and 7 deletions

View file

@ -5,6 +5,10 @@
if ARCH_BOARD_ESP32P4_TAB5
config ESP32P4_TAB5_HMI_POWER
bool
default n
config ESP32P4_TAB5_IOEXPANDER
bool "Enable the Tab5 board IO Expander"
default n
@ -31,4 +35,67 @@ if ESP32P4_TAB5_IOEXPANDER
Enable the second IO Expander.
endif
config ESP32P4_TAB5_LCD_POWER
bool "LCD / MIPI PHY power and reset path"
default n
select ESP32P4_TAB5_IOEXPANDER
select ESP32P4_TAB5_HMI_POWER
---help---
At board bring-up (after I2C0 init), enable on-chip LDO channel 3
at 2500 mV for VDD_MIPI_DPHY and raise LCD_EN on expander pin P4.
This does NOT initialize the MIPI-DSI host, the LCD panel, or
/dev/fb0 by itself. Enable ESP32P4_TAB5_MIPI_DSI (and
ESPRESSIF_MIPI_DSI) to register the host after this power path.
Enable ESP32P4_TAB5_LCD for panel initialization + /dev/fb0.
config ESP32P4_TAB5_MIPI_DSI
bool "Register MIPI-DSI host at bring-up"
default n
depends on ESP32P4_TAB5_LCD_POWER && ESPRESSIF_MIPI_DSI
---help---
After the LCD power path succeeds, call
esp_mipi_dsi_initialize() so the generic MIPI-DSI host is
registered (LP DCS/generic transfer ready).
Does NOT run panel initialization or create /dev/fb0 by itself;
enable ESP32P4_TAB5_LCD for that.
config ESP32P4_TAB5_LCD
bool "LCD panel and /dev/fb0"
default n
depends on ESP32P4_TAB5_MIPI_DSI
select DRIVERS_VIDEO
select VIDEO_FB
select FB_UPDATE
---help---
Bring up the selected Tab5 MIPI-DSI panel (M5Stack init table and
720×1280 RGB565 timings), allocate a PSRAM framebuffer, bind it
to the DSI bridge via DW-GDMA, and register /dev/fb0.
config ESP32P4_TAB5_LCD_APO_TEST
bool "All-Pixels-On flash after FB bring-up"
default y
depends on ESP32P4_TAB5_LCD
---help---
After /dev/fb0 is ready, force All-Pixels-On for ~1 s, then
All-Pixels-Off and restore the solid red test fill.
choice
prompt "Tab5 LCD panel controller"
default ESP32P4_TAB5_LCD_ST7121
depends on ESP32P4_TAB5_LCD
config ESP32P4_TAB5_LCD_ST7121
bool "ST7121"
---help---
Use the ST7121 initialization table and display timings.
config ESP32P4_TAB5_LCD_ST7123
bool "ST7123"
---help---
Use the ST7123 initialization table and display timings.
endchoice
endif # ARCH_BOARD_ESP32P4_TAB5

View file

@ -55,18 +55,46 @@
#define BUTTON_BOOT 35
/* Display - MIPI-DSI ILI9881C (NOT implemented; pins documented only)
/* Display - MIPI-DSI ST7121/ST7123 (M5Stack Tab5 reference timings)
*
* The DSI data/clock lanes are dedicated MIPI pins (not GPIO-muxed) powered
* by VDD_MIPI_DPHY; only the backlight enable is a GPIO. Panel reset is the
* board power-on reset (BSP_LCD_RST = NC) driven by IO expander 0x43 P4.
* by VDD_MIPI_DPHY (LDO ch3 @ 2500 mV). Backlight enable is GPIO22.
* BSP_LCD_RST is NC; LCD_EN / reset control is PI4IOE5V6408 @0x43 P4.
*
* Enable /dev/fb0 with CONFIG_ESP32P4_TAB5_LCD (see configs/lcd), which
* selects power + MIPI host + VIDEO_FB. Select the matching panel with
* CONFIG_ESP32P4_TAB5_LCD_ST7121 or CONFIG_ESP32P4_TAB5_LCD_ST7123.
*/
#define TAB5_GPIO_LCD_BL_EN 22 /* Backlight enable -> ME2212 boost EN */
/* Touch - GT911 (NOT implemented; pins documented only) */
#define TAB5_MIPI_DSI_H_RES 720
#define TAB5_MIPI_DSI_V_RES 1280
#define TAB5_MIPI_DSI_DPI_CLK_MHZ 70
#define TAB5_MIPI_DSI_HSYNC_PULSE_WIDTH 2
#define TAB5_MIPI_DSI_HSYNC_BACK_PORCH 40
#define TAB5_MIPI_DSI_HSYNC_FRONT_PORCH 40
#define TAB5_MIPI_DSI_LANES 2
#define TAB5_GPIO_TP_INT 23 /* GT911 touch interrupt */
#ifdef CONFIG_ESP32P4_TAB5_LCD_ST7123
# define TAB5_LCD_PANEL_NAME "ST7123"
# define TAB5_MIPI_DSI_VSYNC_PULSE_WIDTH 2
# define TAB5_MIPI_DSI_VSYNC_BACK_PORCH 8
# define TAB5_MIPI_DSI_VSYNC_FRONT_PORCH 220
# define TAB5_MIPI_DSI_LANE_BITRATE_MBPS 1000
#else
# define TAB5_LCD_PANEL_NAME "ST7121"
# define TAB5_MIPI_DSI_VSYNC_PULSE_WIDTH 20
# define TAB5_MIPI_DSI_VSYNC_BACK_PORCH 24
# define TAB5_MIPI_DSI_VSYNC_FRONT_PORCH 200
# define TAB5_MIPI_DSI_LANE_BITRATE_MBPS 965
#endif
/* Touch - ST7121/ST7123 TDDI @0x55 (NOT implemented; INT GPIO23). Older
* ILI9881 units used GT911 @0x14 on the same INT pin.
*/
#define TAB5_GPIO_TP_INT 23 /* Touch interrupt */
/* Audio - ES8388 codec (out) + ES7210 mic array (in), I2S (pins only) */

View file

@ -24,7 +24,20 @@ set(SRCS esp32p4_boot.c esp32p4_bringup.c)
if(CONFIG_ESP32P4_TAB5_IOEXPANDER)
list(APPEND SRCS esp32p4_ioexpander.c)
endif
endif()
if(CONFIG_ESP32P4_TAB5_HMI_POWER)
list(APPEND SRCS esp32p4_hmi_power.c)
endif()
if(CONFIG_ESP32P4_TAB5_LCD)
list(APPEND SRCS esp32p4_display.c esp32p4_mipi_dpi.c)
if(CONFIG_ESP32P4_TAB5_LCD_ST7121)
list(APPEND SRCS esp32p4_lcd_st7121.c)
elseif(CONFIG_ESP32P4_TAB5_LCD_ST7123)
list(APPEND SRCS esp32p4_lcd_st7123.c)
endif()
endif()
if(CONFIG_BOARDCTL)
if(CONFIG_BOARDCTL_RESET)

View file

@ -28,6 +28,19 @@ ifeq ($(CONFIG_ESP32P4_TAB5_IOEXPANDER),y)
CSRCS += esp32p4_ioexpander.c
endif
ifeq ($(CONFIG_ESP32P4_TAB5_HMI_POWER),y)
CSRCS += esp32p4_hmi_power.c
endif
ifeq ($(CONFIG_ESP32P4_TAB5_LCD),y)
CSRCS += esp32p4_display.c esp32p4_mipi_dpi.c
ifeq ($(CONFIG_ESP32P4_TAB5_LCD_ST7121),y)
CSRCS += esp32p4_lcd_st7121.c
else ifeq ($(CONFIG_ESP32P4_TAB5_LCD_ST7123),y)
CSRCS += esp32p4_lcd_st7123.c
endif
endif
ifeq ($(CONFIG_BOARDCTL),y)
ifeq ($(CONFIG_BOARDCTL_RESET),y)
CSRCS += esp32p4_reset.c

View file

@ -28,6 +28,13 @@
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <stdbool.h>
#ifdef CONFIG_ESPRESSIF_MIPI_DSI
# include "espressif/esp_mipi_dsi.h"
#endif
/****************************************************************************
* Public Data
@ -56,6 +63,108 @@
int esp_bringup(void);
#ifdef CONFIG_ESP32P4_TAB5_LCD_POWER
/****************************************************************************
* Name: tab5_mipi_phy_power
*
* Description:
* Enable or disable LDO channel 3 (VDD_MIPI_DPHY @ 2500 mV).
*
* Input Parameters:
* on - True to enable the LDO channel, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_mipi_phy_power(bool on);
/****************************************************************************
* Name: tab5_lcd_enable
*
* Description:
* Drive PI4IOE P4 (BSP_LCD_EN). true = enable (high).
*
* Input Parameters:
* enable - True to enable the LCD rail, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_enable(bool enable);
/****************************************************************************
* Name: tab5_lcd_backlight
*
* Description:
* Drive backlight enable GPIO22 (ME2212 boost EN).
*
* Input Parameters:
* on - True to enable the backlight, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_backlight(bool on);
/****************************************************************************
* Name: tab5_lcd_power_init
*
* Description:
* Power MIPI PHY LDO, init PI4IOE, and enable LCD (P4 high).
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_power_init(void);
#endif /* CONFIG_ESP32P4_TAB5_LCD_POWER */
#ifdef CONFIG_ESP32P4_TAB5_LCD
/****************************************************************************
* Name: tab5_fb_reload_test_pattern
*
* Description:
* Re-fill the plane with the bring-up test color and cache write-back.
* Call after fb_register(): the generic FB driver memset()s the plane
* and would otherwise leave a black DMA buffer.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_fb_reload_test_pattern(void);
/****************************************************************************
* Name: tab5_mipi_dsi_dpi_config
*
* Description:
* Fill DPI timing for the selected Tab5 ST712x panel. Arch
* host has no panel defaults board code supplies this to
* esp_mipi_dsi_configure_dpi().
*
* Input Parameters:
* cfg - Pointer to the DPI configuration structure.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
void tab5_mipi_dsi_dpi_config(FAR struct esp_mipi_dsi_dpi_config_s *cfg);
#endif /* CONFIG_ESP32P4_TAB5_LCD */
#ifdef CONFIG_ESP32P4_TAB5_IOEXPANDER
/****************************************************************************
* Name: tab5_pi4ioe_init

View file

@ -42,6 +42,14 @@
# include "esp_board_i2c.h"
#endif
#ifdef CONFIG_ESP32P4_TAB5_MIPI_DSI
# include "espressif/esp_mipi_dsi.h"
#endif
#ifdef CONFIG_ESP32P4_TAB5_LCD
# include <nuttx/video/fb.h>
#endif
#include <arch/board/board.h>
#include "esp32p4-tab5.h"
@ -103,11 +111,66 @@ int esp_bringup(void)
ret = tab5_pi4ioe_init();
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize Tab5 IO expanders: %d\n", ret);
syslog(LOG_ERR, "ERROR: failed to init IO expanders: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_ESP32P4_TAB5_LCD_POWER
/* MIPI PHY LDO + PI4IOE LCD_EN (before optional DSI host) */
ret = tab5_lcd_power_init();
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to init LCD power path: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_ESP32P4_TAB5_MIPI_DSI
/* Tab5 ST712x bus: 2 lanes @ board.h bitrate. */
struct esp_mipi_dsi_bus_config_s bus_cfg =
{
.num_data_lanes = TAB5_MIPI_DSI_LANES,
.lane_bit_rate_mbps = TAB5_MIPI_DSI_LANE_BITRATE_MBPS,
};
ret = esp_mipi_dsi_initialize(&bus_cfg);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to init MIPI-DSI host: %d\n",
ret);
return ret;
}
syslog(LOG_INFO, "Tab5: MIPI-DSI host registered\n");
#endif
#ifdef CONFIG_ESP32P4_TAB5_LCD
/* Selected panel DCS + DPI + DW-GDMA FB → /dev/fb0 */
ret = fb_register(0, 0);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to register /dev/fb0: %d\n", ret);
}
else
{
/* fb_register() clears the plane; restore test red for DMA. */
ret = tab5_fb_reload_test_pattern();
if (ret < 0)
{
syslog(LOG_ERR,
"ERROR: failed to reload FB test pattern: %d\n", ret);
}
syslog(LOG_INFO, "/dev/fb0 registered (%s)\n",
TAB5_LCD_PANEL_NAME);
}
#endif
#ifdef CONFIG_FS_TMPFS
/* Mount the tmpfs file system */

View file

@ -0,0 +1,485 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_display.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Tab5 display / framebuffer glue (PinePhone-style up_fbinitialize split).
*
* Sequence (ESP-IDF DPI panel + selected ST712x controller):
* 1. Allocate RGB565 FB (PSRAM)
* 2. Host already registered by bringup
* 3. configure_dpi -> panel DCS -> bind FB -> video_start -> display_on
* 4. Backlight on
* 5. Optional All-Pixels-On bring-up flash
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <syslog.h>
#include <nuttx/arch.h>
#include <nuttx/compiler.h>
#include <nuttx/kmalloc.h>
#include <nuttx/video/fb.h>
#include <nuttx/video/mipi_display.h>
#include <nuttx/video/mipi_dsi.h>
#include <arch/board/board.h>
#include "espressif/esp_mipi_dsi.h"
#include "esp32p4-tab5.h"
#ifdef CONFIG_ESP32P4_TAB5_LCD_ST7123
# include "esp32p4_lcd_st7123.h"
# define tab5_panel_initialize tab5_st7123_initialize
#else
# include "esp32p4_lcd_st7121.h"
# define tab5_panel_initialize tab5_st7121_initialize
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define TAB5_FB_BPP 16
#define TAB5_FB_WIDTH TAB5_MIPI_DSI_H_RES
#define TAB5_FB_HEIGHT TAB5_MIPI_DSI_V_RES
#define TAB5_FB_STRIDE (TAB5_FB_WIDTH * (TAB5_FB_BPP / 8))
#define TAB5_FB_SIZE (TAB5_FB_STRIDE * TAB5_FB_HEIGHT)
#define TAB5_FB_TEST_COLOR 0xf800
#define TAB5_DCS_ALL_PIXELS_ON 0x23
#define TAB5_DCS_ALL_PIXELS_OFF 0x22
#define TAB5_APO_TEST_MS 1000
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int tab5_getvideoinfo(FAR struct fb_vtable_s *vtable,
FAR struct fb_videoinfo_s *vinfo);
static int tab5_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
FAR struct fb_planeinfo_s *pinfo);
#ifdef CONFIG_FB_UPDATE
static int tab5_updatearea(FAR struct fb_vtable_s *vtable,
FAR const struct fb_area_s *area);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static struct fb_vtable_s g_tab5_vtable =
{
.getvideoinfo = tab5_getvideoinfo,
.getplaneinfo = tab5_getplaneinfo,
#ifdef CONFIG_FB_UPDATE
.updatearea = tab5_updatearea,
#endif
};
static struct fb_videoinfo_s g_tab5_video =
{
.fmt = FB_FMT_RGB16_565,
.xres = TAB5_FB_WIDTH,
.yres = TAB5_FB_HEIGHT,
.nplanes = 1,
};
static struct fb_planeinfo_s g_tab5_plane =
{
.fbmem = NULL,
.fblen = TAB5_FB_SIZE,
.stride = TAB5_FB_STRIDE,
.display = 0,
.bpp = TAB5_FB_BPP,
.xres_virtual = TAB5_FB_WIDTH,
.yres_virtual = TAB5_FB_HEIGHT,
.xoffset = 0,
.yoffset = 0,
};
static FAR uint16_t *g_tab5_fb;
static bool g_tab5_fb_ready;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tab5_getvideoinfo
*
* Description:
* Return the video information.
*
* Input Parameters:
* vtable - The vtable.
* vinfo - The video information.
*
* Returned Value:
* Zero on success.
*
****************************************************************************/
static int tab5_getvideoinfo(FAR struct fb_vtable_s *vtable,
FAR struct fb_videoinfo_s *vinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_tab5_vtable && vinfo != NULL);
memcpy(vinfo, &g_tab5_video, sizeof(*vinfo));
return OK;
}
/****************************************************************************
* Name: tab5_getplaneinfo
*
* Description:
* Return the plane information.
*
* Input Parameters:
* vtable - The vtable.
* planeno - The plane number.
* pinfo - The plane information.
*
* Returned Value:
* Zero on success, -EINVAL if the plane number is invalid.
*
****************************************************************************/
static int tab5_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
FAR struct fb_planeinfo_s *pinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_tab5_vtable && pinfo != NULL);
if (planeno != 0 || g_tab5_plane.fbmem == NULL)
{
return -EINVAL;
}
memcpy(pinfo, &g_tab5_plane, sizeof(*pinfo));
return OK;
}
#ifdef CONFIG_FB_UPDATE
/****************************************************************************
* Name: tab5_updatearea
*
* Description:
* Update a region of the framebuffer.
*
* Input Parameters:
* vtable - The vtable.
* area - The area to update.
*
* Returned Value:
* Zero on success, -EAGAIN if the framebuffer is not ready,
* -EINVAL if the area is out of bounds.
****************************************************************************/
static int tab5_updatearea(FAR struct fb_vtable_s *vtable,
FAR const struct fb_area_s *area)
{
size_t offset;
size_t len;
DEBUGASSERT(vtable != NULL && vtable == &g_tab5_vtable);
if (g_tab5_fb == NULL)
{
return -EAGAIN;
}
if (area == NULL)
{
return esp_mipi_dsi_flush_framebuffer(g_tab5_fb, TAB5_FB_SIZE);
}
if (area->y >= TAB5_FB_HEIGHT || area->x >= TAB5_FB_WIDTH)
{
return OK;
}
offset = (size_t)area->y * TAB5_FB_STRIDE +
(size_t)area->x * (TAB5_FB_BPP / 8);
len = (size_t)area->h * TAB5_FB_STRIDE;
if (offset + len > TAB5_FB_SIZE)
{
len = TAB5_FB_SIZE - offset;
}
return esp_mipi_dsi_flush_framebuffer((FAR uint8_t *)g_tab5_fb + offset,
len);
}
#endif /* CONFIG_FB_UPDATE */
/****************************************************************************
* Name: tab5_fb_fill
*
* Description:
* Fill the framebuffer with a given color.
*
* Input Parameters:
* color - The color to fill the framebuffer with.
*
* Returned Value:
* None.
*
****************************************************************************/
static void tab5_fb_fill(uint16_t color)
{
size_t i;
size_t npix = (size_t)TAB5_FB_WIDTH * TAB5_FB_HEIGHT;
int ret;
for (i = 0; i < npix; i++)
{
g_tab5_fb[i] = color;
}
ret = esp_mipi_dsi_flush_framebuffer(g_tab5_fb, TAB5_FB_SIZE);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: FB cache C2M sync failed: %d\n", ret);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_fbinitialize
*
* Description:
* Initialize the framebuffer.
*
* Input Parameters:
* display - The display number.
*
* Returned Value:
* Zero on success, -ENODEV on failure.
*
****************************************************************************/
int up_fbinitialize(int display)
{
FAR struct mipi_dsi_host *host;
FAR struct mipi_dsi_device *device;
struct esp_mipi_dsi_dpi_config_s dpi;
int ret;
if (display != 0)
{
return -ENODEV;
}
if (g_tab5_fb_ready)
{
return OK;
}
g_tab5_fb = kumm_memalign(64, TAB5_FB_SIZE);
if (g_tab5_fb == NULL)
{
syslog(LOG_ERR,
"ERROR: FB alloc failed (%u bytes; enable PSRAM)\n",
(unsigned int)TAB5_FB_SIZE);
return -ENOMEM;
}
memset(g_tab5_fb, 0, TAB5_FB_SIZE);
g_tab5_plane.fbmem = g_tab5_fb;
host = esp_mipi_dsi_host_get();
if (host == NULL)
{
syslog(LOG_ERR,
"ERROR: MIPI-DSI host not ready "
"(enable ESP32P4_TAB5_MIPI_DSI)\n");
ret = -EAGAIN;
goto errout_fb;
}
/* IDF order: DPI config before panel DCS; display_on after video_start. */
syslog(LOG_INFO, "Configuring DPI...\n");
tab5_mipi_dsi_dpi_config(&dpi);
ret = esp_mipi_dsi_configure_dpi(&dpi);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: configure_dpi failed: %d\n", ret);
goto errout_fb;
}
syslog(LOG_INFO, "%s panel init...\n",
TAB5_LCD_PANEL_NAME);
device = tab5_panel_initialize(host);
if (device == NULL)
{
ret = -EIO;
goto errout_fb;
}
ret = esp_mipi_dsi_bind_framebuffer(g_tab5_fb, TAB5_FB_SIZE,
TAB5_FB_WIDTH, TAB5_FB_HEIGHT,
TAB5_FB_BPP);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: bind_framebuffer failed: %d\n", ret);
goto errout_fb;
}
tab5_fb_fill(TAB5_FB_TEST_COLOR);
ret = mipi_dsi_dcs_exit_sleep_mode(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: pre-video sleep_out failed: %d\n",
ret);
goto errout_fb;
}
up_mdelay(120);
syslog(LOG_INFO, "Starting video...\n");
ret = esp_mipi_dsi_video_start();
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: video_start failed: %d\n", ret);
goto errout_fb;
}
ret = mipi_dsi_dcs_set_display_on(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: post-video display_on failed: %d\n",
ret);
goto errout_fb;
}
tab5_fb_fill(TAB5_FB_TEST_COLOR);
ret = tab5_lcd_backlight(true);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: backlight failed: %d\n", ret);
}
g_tab5_fb_ready = true;
syslog(LOG_INFO,
"/dev/fb0 ready %ux%u RGB565 @ %p\n",
TAB5_FB_WIDTH, TAB5_FB_HEIGHT, g_tab5_fb);
#ifdef CONFIG_ESP32P4_TAB5_LCD_APO_TEST
/* White flash proves DCS/BL; then restore normal mode + red FB. */
mipi_dsi_dcs_write(device, TAB5_DCS_ALL_PIXELS_ON, NULL, 0);
up_mdelay(TAB5_APO_TEST_MS);
mipi_dsi_dcs_write(device, TAB5_DCS_ALL_PIXELS_OFF, NULL, 0);
mipi_dsi_dcs_write(device, MIPI_DCS_ENTER_NORMAL_MODE, NULL, 0);
mipi_dsi_dcs_set_display_on(device);
tab5_fb_fill(TAB5_FB_TEST_COLOR);
#endif
return OK;
errout_fb:
kumm_free(g_tab5_fb);
g_tab5_fb = NULL;
g_tab5_plane.fbmem = NULL;
return ret;
}
/****************************************************************************
* Name: up_fbgetvplane
*
* Description:
* Return the framebuffer vtable.
*
* Input Parameters:
* display - The display number.
* vplane - The vplane number.
*
* Returned Value:
* Pointer to the vtable on success, NULL on failure.
*
****************************************************************************/
FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane)
{
if (display != 0 || vplane != 0 || !g_tab5_fb_ready)
{
return NULL;
}
return &g_tab5_vtable;
}
/****************************************************************************
* Name: up_fbuninitialize
*
* Description:
* Uninitialize the framebuffer.
*
* Input Parameters:
* display - The display number.
*
* Returned Value:
* None.
*
****************************************************************************/
void up_fbuninitialize(int display)
{
UNUSED(display);
}
/****************************************************************************
* Name: tab5_fb_reload_test_pattern
*
* Description:
* Call after fb_register(): the generic FB driver memset()s the
* framebuffer and would otherwise leave a black DMA buffer.
* This sets the framebuffer to the test color (red).
*
* Returned Value:
* Zero on success, -EAGAIN on failure.
*
****************************************************************************/
int tab5_fb_reload_test_pattern(void)
{
if (!g_tab5_fb_ready || g_tab5_fb == NULL)
{
return -EAGAIN;
}
tab5_fb_fill(TAB5_FB_TEST_COLOR);
return OK;
}

View file

@ -0,0 +1,187 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_hmi_power.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <stdint.h>
#include <syslog.h>
#include <errno.h>
#include <nuttx/sched.h>
#include "espressif/esp_ldo.h"
#include "espressif/esp_gpio.h"
#include "esp32p4-tab5.h"
#include <arch/board/board.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* MIPI PHY LDO channel and voltage */
#define ESP_LDO_MIPI_PHY_CHAN 3
#define ESP_LDO_MIPI_PHY_VOLTAGE_MV 2500
/****************************************************************************
* Private Data
****************************************************************************/
static struct esp_ldo_config_t g_mipi_phy_ldo_config =
{
.chan_id = ESP_LDO_MIPI_PHY_CHAN,
.voltage_mv = ESP_LDO_MIPI_PHY_VOLTAGE_MV,
.handler = NULL,
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tab5_mipi_phy_power
*
* Description:
* Enable or disable the MIPI PHY LDO.
*
* Input Parameters:
* on - True to enable the MIPI PHY LDO, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_mipi_phy_power(bool on)
{
int ret = OK;
if (on)
{
ret = esp_ldo_channel_acquire(&g_mipi_phy_ldo_config);
}
else
{
ret = esp_ldo_channel_release(&g_mipi_phy_ldo_config);
}
return ret;
}
/****************************************************************************
* Name: tab5_lcd_enable
*
* Description:
* Enable or disable the LCD rail via IO Expander (low).
*
* Input Parameters:
* enable - True to enable the LCD rail, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_enable(bool enable)
{
int ret;
ret = tab5_pi4ioe_low_write_pin(TAB5_LCD_EN_PIN, enable);
if (ret < 0)
{
syslog(LOG_ERR,
"ERROR: failed to write LCD_EN pin: %d\n", ret);
return ret;
}
syslog(LOG_INFO, "LCD %s\n", enable ? "enabled" : "disabled");
return OK;
}
/****************************************************************************
* Name: tab5_lcd_backlight
*
* Description:
* Configure GPIO22 as output and drive the backlight enable.
*
* Input Parameters:
* on - True to enable the backlight, false to disable it.
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_backlight(bool on)
{
int ret;
ret = esp_configgpio(TAB5_GPIO_LCD_BL_EN, OUTPUT);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to configure backlight GPIO%d: %d\n",
TAB5_GPIO_LCD_BL_EN, ret);
return ret;
}
esp_gpiowrite(TAB5_GPIO_LCD_BL_EN, on);
syslog(LOG_INFO, "Backlight %s\n", on ? "enabled" : "disabled");
return OK;
}
/****************************************************************************
* Name: tab5_lcd_power_init
*
* Description:
* Power MIPI PHY LDO, init PI4IOE, and enable LCD (P4 high).
*
* Returned Value:
* Zero on success, -1 on failure.
*
****************************************************************************/
int tab5_lcd_power_init(void)
{
int ret;
ret = tab5_mipi_phy_power(true);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to power on MIPI PHY: %d\n", ret);
return ret;
}
ret = tab5_lcd_enable(true);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: failed to enable LCD rail: %d\n", ret);
return ret;
}
syslog(LOG_INFO, "LCD power/reset path ready\n");
return OK;
}

View file

@ -0,0 +1,375 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_lcd_st7121.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* ST7121 panel bring-up for M5Stack Tab5.
*
* Init init table sourced from https://github.com/espressif/esp-iot-solution
* under components/display/lcd/esp_lcd_st7121/esp_lcd_st7121.c.
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <syslog.h>
#include <nuttx/arch.h>
#include <nuttx/video/mipi_dsi.h>
#include <arch/board/board.h>
#include "esp32p4-tab5.h"
#include "esp32p4_lcd_st7121.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ST7121_NAME "st7121"
#define ST7121_COMMAND_DELAY_MS 5
/****************************************************************************
* Private Types
****************************************************************************/
struct st7121_init_cmd_s
{
uint8_t cmd;
FAR const uint8_t *data;
uint8_t len;
uint16_t delay_ms;
};
/****************************************************************************
* Private Data
****************************************************************************/
static const uint8_t g_st7121_cmd_60a[] =
{
0x71, 0x21, 0xa2
};
static const uint8_t g_st7121_cmd_60b[] =
{
0x71, 0x21, 0xa3
};
static const uint8_t g_st7121_cmd_60c[] =
{
0x71, 0x21, 0xa4
};
static const uint8_t g_st7121_cmd_78[] =
{
0x21
};
static const uint8_t g_st7121_cmd_79[] =
{
0xef
};
static const uint8_t g_st7121_cmd_a4[] =
{
0x31
};
static const uint8_t g_st7121_cmd_b7[] =
{
0x00, 0x00, 0x5f, 0x5f, 0x44, 0x1a
};
static const uint8_t g_st7121_cmd_b0[] =
{
0x22, 0x6b, 0x11, 0x89, 0x25, 0x43, 0x43
};
static const uint8_t g_st7121_cmd_bf[] =
{
0xa7, 0xa7
};
static const uint8_t g_st7121_cmd_a5[] =
{
0xf0, 0x03
};
static const uint8_t g_st7121_cmd_d7[] =
{
0x10, 0x2c, 0x14, 0x2a, 0x80, 0x80
};
static const uint8_t g_st7121_cmd_90[] =
{
0x71, 0x23, 0x5a, 0x20, 0x24, 0x11, 0x21
};
static const uint8_t g_st7121_cmd_a3[] =
{
0x80, 0x01, 0x8c, 0xff, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00,
0x00, 0x1e, 0x5c, 0x1e, 0x80, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x46, 0x00, 0x00, 0x1e, 0x5c, 0x1e, 0x80, 0x10, 0xef, 0x58, 0x00,
0x00, 0x00, 0xff
};
static const uint8_t g_st7121_cmd_a6[] =
{
0x0a, 0x00, 0x24, 0x71, 0x36, 0x00, 0x00, 0x00, 0x68, 0x68, 0x91, 0xff,
0x00, 0x24, 0x71, 0x37, 0x00, 0x00, 0x00, 0x68, 0x68, 0x91, 0xff, 0x00,
0x24, 0x71, 0x00, 0x00, 0x00, 0x00, 0x68, 0x68, 0x91, 0xff, 0x00, 0x2c,
0x71, 0x00, 0x01, 0x00, 0x00, 0x68, 0x68, 0xff, 0xff, 0x00, 0x08, 0x80,
0x08, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t g_st7121_cmd_a7[] =
{
0x1a, 0x1a, 0xc0, 0x64, 0x40, 0x04, 0x15, 0x40, 0x00, 0x40, 0x00, 0x68,
0x68, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0x26, 0x37, 0x40, 0x00, 0x00,
0x00, 0x68, 0x68, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0x8c, 0x9d, 0x40,
0x00, 0x00, 0x00, 0x68, 0x68, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0xae,
0xbf, 0x00, 0x00, 0x20, 0x00, 0x68, 0x68, 0x91, 0xff, 0x08, 0x80, 0x79
};
static const uint8_t g_st7121_cmd_ac[] =
{
0x1d, 0x18, 0x19, 0x1d, 0x18, 0x19, 0x04, 0x1c, 0x1d, 0x08, 0x0a, 0x10,
0x12, 0x0c, 0x0e, 0x14, 0x16, 0x00, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x18,
0x19, 0x1d, 0x18, 0x19, 0x06, 0x1c, 0x1d, 0x09, 0x0b, 0x11, 0x13, 0x0d,
0x0f, 0x15, 0x17, 0x02, 0x1d, 0x1d, 0x1d, 0x1d
};
static const uint8_t g_st7121_cmd_ad[] =
{
0x0c, 0x40, 0x46, 0x00, 0x07, 0x4b, 0x4b, 0xff, 0xff, 0xf0, 0x40, 0x0e,
0x01, 0x07, 0x42, 0x42, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff
};
static const uint8_t g_st7121_cmd_ae[] =
{
0xf0, 0xff, 0x03, 0xf0, 0xff, 0x03, 0x00
};
static const uint8_t g_st7121_cmd_b2[] =
{
0x15, 0x19, 0x05, 0x23, 0x49, 0x2d, 0x03, 0x2e, 0x5c, 0xd2, 0xff, 0x10,
0x60, 0xfd, 0x20, 0xc0, 0x00
};
static const uint8_t g_st7121_cmd_e8[] =
{
0x20, 0x60, 0x04, 0x8e, 0x8e, 0x3e, 0x04, 0xdc, 0xdc, 0x3e, 0x06, 0xfa,
0x26, 0x3e
};
static const uint8_t g_st7121_cmd_75[] =
{
0x03, 0x04
};
static const uint8_t g_st7121_cmd_e7[] =
{
0x4b, 0x00, 0x00, 0xbe, 0x4b, 0x8c, 0x20, 0x1a, 0xf0, 0x7d, 0x14, 0x7d,
0x14, 0x7d, 0x14, 0x7d, 0x14, 0xff, 0x00, 0x32, 0x30, 0x73, 0x00, 0x00,
0xc8, 0x6a, 0xff, 0x5a, 0x64, 0x38, 0x88, 0x15, 0xb1, 0x01, 0x01, 0x64,
0x01, 0x01, 0x7c, 0xff, 0x1a, 0x51
};
static const uint8_t g_st7121_cmd_e1[] =
{
0x0c, 0x0c
};
static const uint8_t g_st7121_cmd_ea[] =
{
0x15, 0x00, 0x01
};
static const uint8_t g_st7121_cmd_c8[] =
{
0x00, 0x00, 0x04, 0x08, 0x10, 0x00, 0x1f, 0x01, 0x39, 0x3e, 0x00, 0x78,
0x06, 0xe2, 0x02, 0x11, 0x33, 0x01, 0x7a, 0x0d, 0x21, 0xc4, 0x0b, 0x19,
0x08, 0x32, 0xa0, 0x08, 0x1a, 0x0a, 0xf3, 0x7f, 0x0e, 0xc5, 0xe8, 0x03,
0xff
};
static const uint8_t g_st7121_cmd_c9[] =
{
0x00, 0x00, 0x04, 0x08, 0x10, 0x00, 0x1f, 0x01, 0x39, 0x3e, 0x00, 0x78,
0x06, 0xe2, 0x02, 0x11, 0x33, 0x01, 0x7a, 0x0d, 0x21, 0xc4, 0x0b, 0x19,
0x08, 0x32, 0xa0, 0x08, 0x1a, 0x0a, 0xf3, 0x7f, 0x0e, 0xc5, 0xe8, 0x03,
0xff
};
static const uint8_t g_st7121_cmd_60d[] =
{
0x71, 0x21, 0x00
};
static const uint8_t g_st7121_cmd_35[] =
{
0x00
};
static const struct st7121_init_cmd_s g_st7121_init[] =
{
{0x60, g_st7121_cmd_60a, sizeof(g_st7121_cmd_60a), 0},
{0x60, g_st7121_cmd_60b, sizeof(g_st7121_cmd_60b), 0},
{0x60, g_st7121_cmd_60c, sizeof(g_st7121_cmd_60c), 0},
{0x78, g_st7121_cmd_78, sizeof(g_st7121_cmd_78), 0},
{0x79, g_st7121_cmd_79, sizeof(g_st7121_cmd_79), 0},
{0xa4, g_st7121_cmd_a4, sizeof(g_st7121_cmd_a4), 0},
{0xb7, g_st7121_cmd_b7, sizeof(g_st7121_cmd_b7), 0},
{0xb0, g_st7121_cmd_b0, sizeof(g_st7121_cmd_b0), 0},
{0xbf, g_st7121_cmd_bf, sizeof(g_st7121_cmd_bf), 0},
{0xa5, g_st7121_cmd_a5, sizeof(g_st7121_cmd_a5), 0},
{0xd7, g_st7121_cmd_d7, sizeof(g_st7121_cmd_d7), 0},
{0x90, g_st7121_cmd_90, sizeof(g_st7121_cmd_90), 0},
{0xa3, g_st7121_cmd_a3, sizeof(g_st7121_cmd_a3), 0},
{0xa6, g_st7121_cmd_a6, sizeof(g_st7121_cmd_a6), 0},
{0xa7, g_st7121_cmd_a7, sizeof(g_st7121_cmd_a7), 0},
{0xac, g_st7121_cmd_ac, sizeof(g_st7121_cmd_ac), 0},
{0xad, g_st7121_cmd_ad, sizeof(g_st7121_cmd_ad), 0},
{0xae, g_st7121_cmd_ae, sizeof(g_st7121_cmd_ae), 0},
{0xb2, g_st7121_cmd_b2, sizeof(g_st7121_cmd_b2), 0},
{0xe8, g_st7121_cmd_e8, sizeof(g_st7121_cmd_e8), 0},
{0x75, g_st7121_cmd_75, sizeof(g_st7121_cmd_75), 0},
{0xe7, g_st7121_cmd_e7, sizeof(g_st7121_cmd_e7), 0},
{0xe1, g_st7121_cmd_e1, sizeof(g_st7121_cmd_e1), 0},
{0xea, g_st7121_cmd_ea, sizeof(g_st7121_cmd_ea), 0},
{0xc8, g_st7121_cmd_c8, sizeof(g_st7121_cmd_c8), 0},
{0xc9, g_st7121_cmd_c9, sizeof(g_st7121_cmd_c9), 0},
{0x60, g_st7121_cmd_60d, sizeof(g_st7121_cmd_60d), 0},
{0x11, NULL, 0, 80},
{0x29, NULL, 0, 800},
{0x35, g_st7121_cmd_35, sizeof(g_st7121_cmd_35), 0},
};
#define ST7121_INIT_COUNT (sizeof(g_st7121_init) / sizeof(g_st7121_init[0]))
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: st7121_send_init
****************************************************************************/
static int st7121_send_init(FAR struct mipi_dsi_device *device)
{
unsigned int i;
ssize_t n;
for (i = 0; i < ST7121_INIT_COUNT; i++)
{
FAR const struct st7121_init_cmd_s *cmd = &g_st7121_init[i];
n = mipi_dsi_dcs_write(device, cmd->cmd, cmd->data, cmd->len);
if (n < 0)
{
syslog(LOG_ERR,
"ERROR: DCS 0x%02x failed: %zd (cmd %u/%u)\n",
cmd->cmd, n, i + 1, ST7121_INIT_COUNT);
return (int)n;
}
if (cmd->delay_ms > 0)
{
up_mdelay(cmd->delay_ms);
}
up_mdelay(ST7121_COMMAND_DELAY_MS);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tab5_st7121_initialize
****************************************************************************/
FAR struct mipi_dsi_device *tab5_st7121_initialize(
FAR struct mipi_dsi_host *host)
{
FAR struct mipi_dsi_device *device;
int ret;
if (host == NULL)
{
return NULL;
}
tab5_lcd_backlight(false);
ret = tab5_lcd_enable(true);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: LCD_EN enable failed: %d\n", ret);
return NULL;
}
device = mipi_dsi_device_register(host, ST7121_NAME, 0);
if (device == NULL)
{
syslog(LOG_ERR, "ERROR: mipi_dsi_device_register failed\n");
return NULL;
}
device->lanes = TAB5_MIPI_DSI_LANES;
device->format = MIPI_DSI_FMT_RGB565;
device->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
device->hs_rate = TAB5_MIPI_DSI_LANE_BITRATE_MBPS * 1000000UL;
device->lp_rate = 0;
ret = mipi_dsi_attach(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: attach failed: %d\n", ret);
return NULL;
}
ret = mipi_dsi_dcs_soft_reset(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: soft reset failed: %d\n", ret);
return NULL;
}
up_mdelay(120);
ret = st7121_send_init(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: DCS init failed: %d\n", ret);
return NULL;
}
return device;
}

View file

@ -0,0 +1,75 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_lcd_st7121.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7121_H
#define __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7121_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/video/mipi_dsi.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifndef __ASSEMBLY__
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: tab5_st7121_initialize
*
* Description:
* Register the ST7121 as a mipi_dsi_device, attach to the Espressif host,
* power-cycle LCD_EN (BSP_LCD_RST is NC), issue DCS soft-reset, and send
* the M5Stack Tab5 ST7121 vendor DCS initialization table. Does not start
* video; board code calls display_on after video_start.
*
* Input Parameters:
* host - Registered MIPI-DSI host (esp_mipi_dsi_host_get())
*
* Returned Value:
* Pointer to the registered device on success; NULL on failure.
*
****************************************************************************/
#ifdef CONFIG_ESP32P4_TAB5_LCD_ST7121
FAR struct mipi_dsi_device *tab5_st7121_initialize(
FAR struct mipi_dsi_host *host);
#endif
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7121_H */

View file

@ -0,0 +1,425 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_lcd_st7123.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* ST7123 panel bring-up for M5Stack Tab5.
*
* Init init table sourced from https://github.com/espressif/esp-iot-solution
* under components/display/lcd/esp_lcd_st7123/esp_lcd_st7123.c.
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <nuttx/arch.h>
#include <nuttx/video/mipi_dsi.h>
#include <arch/board/board.h>
#include "esp32p4-tab5.h"
#include "esp32p4_lcd_st7123.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ST7123_NAME "st7123"
/****************************************************************************
* Private Types
****************************************************************************/
struct st7123_init_cmd_s
{
uint8_t cmd;
FAR const uint8_t *data;
uint8_t len;
uint16_t delay_ms;
};
/****************************************************************************
* Private Data
****************************************************************************/
/* ESP-BSP Tab5 ST7123 vendor table (disp_init_data_st7123). */
static const uint8_t g_st7123_cmd_60a[] =
{
0x71, 0x23, 0xa2
};
static const uint8_t g_st7123_cmd_60b[] =
{
0x71, 0x23, 0xa3
};
static const uint8_t g_st7123_cmd_60c[] =
{
0x71, 0x23, 0xa4
};
static const uint8_t g_st7123_cmd_a4[] =
{
0x31
};
static const uint8_t g_st7123_cmd_d7[] =
{
0x10, 0x0a, 0x10, 0x2a, 0x80, 0x80
};
static const uint8_t g_st7123_cmd_90[] =
{
0x71, 0x23, 0x5a, 0x20, 0x24, 0x09, 0x09
};
static const uint8_t g_st7123_cmd_a3[] =
{
0x80, 0x01, 0x88, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00,
0x00, 0x1e, 0x5c, 0x1e, 0x80, 0x00, 0x4f, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x46, 0x00, 0x00, 0x1e, 0x5c, 0x1e, 0x80, 0x00, 0x6f, 0x58, 0x00,
0x00, 0x00, 0xff
};
static const uint8_t g_st7123_cmd_a6[] =
{
0x03, 0x00, 0x24, 0x55, 0x36, 0x00, 0x39, 0x00, 0x6e, 0x6e, 0x91, 0xff,
0x00, 0x24, 0x55, 0x38, 0x00, 0x37, 0x00, 0x6e, 0x6e, 0x91, 0xff, 0x00,
0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6e, 0x91, 0xff, 0x00, 0xec,
0x11, 0x00, 0x03, 0x00, 0x03, 0x6e, 0x6e, 0xff, 0xff, 0x00, 0x08, 0x80,
0x08, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t g_st7123_cmd_a7[] =
{
0x19, 0x19, 0x80, 0x64, 0x40, 0x07, 0x16, 0x40, 0x00, 0x44, 0x03, 0x6e,
0x6e, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0x25, 0x34, 0x40, 0x00, 0x02,
0x01, 0x6e, 0x6e, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x6e, 0x6e, 0x91, 0xff, 0x08, 0x80, 0x64, 0x40, 0x00,
0x00, 0x00, 0x00, 0x20, 0x00, 0x6e, 0x6e, 0x84, 0xff, 0x08, 0x80, 0x44
};
static const uint8_t g_st7123_cmd_ac[] =
{
0x03, 0x19, 0x19, 0x18, 0x18, 0x06, 0x13, 0x13, 0x11, 0x11, 0x08, 0x08,
0x0a, 0x0a, 0x1c, 0x1c, 0x07, 0x07, 0x00, 0x00, 0x02, 0x02, 0x01, 0x19,
0x19, 0x18, 0x18, 0x06, 0x12, 0x12, 0x10, 0x10, 0x09, 0x09, 0x0b, 0x0b,
0x1c, 0x1c, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01
};
static const uint8_t g_st7123_cmd_ad[] =
{
0xf0, 0x00, 0x46, 0x00, 0x03, 0x50, 0x50, 0xff, 0xff, 0xf0, 0x40, 0x06,
0x01, 0x07, 0x42, 0x42, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff
};
static const uint8_t g_st7123_cmd_ae[] =
{
0xfe, 0x3f, 0x3f, 0xfe, 0x3f, 0x3f, 0x00
};
static const uint8_t g_st7123_cmd_b2[] =
{
0x15, 0x19, 0x05, 0x23, 0x49, 0xaf, 0x03, 0x2e, 0x5c, 0xd2, 0xff, 0x10,
0x20, 0xfd, 0x20, 0xc0, 0x00
};
static const uint8_t g_st7123_cmd_e8[] =
{
0x20, 0x6f, 0x04, 0x97, 0x97, 0x3e, 0x04, 0xdc, 0xdc, 0x3e, 0x06, 0xfa,
0x26, 0x3e
};
static const uint8_t g_st7123_cmd_75[] =
{
0x03, 0x04
};
static const uint8_t g_st7123_cmd_e7[] =
{
0x3b, 0x00, 0x00, 0x7c, 0xa1, 0x8c, 0x20, 0x1a, 0xf0, 0xb1, 0x50, 0x00,
0x50, 0xb1, 0x50, 0xb1, 0x50, 0xd8, 0x00, 0x55, 0x00, 0xb1, 0x00, 0x45,
0xc9, 0x6a, 0xff, 0x5a, 0xd8, 0x18, 0x88, 0x15, 0xb1, 0x01, 0x01, 0x77
};
static const uint8_t g_st7123_cmd_ea[] =
{
0x13, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2c
};
static const uint8_t g_st7123_cmd_b0[] =
{
0x22, 0x43, 0x11, 0x61, 0x25, 0x43, 0x43
};
static const uint8_t g_st7123_cmd_b7[] =
{
0x00, 0x00, 0x73, 0x73
};
static const uint8_t g_st7123_cmd_bf[] =
{
0xa6, 0xaa
};
static const uint8_t g_st7123_cmd_a9[] =
{
0x00, 0x00, 0x73, 0xff, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03
};
static const uint8_t g_st7123_cmd_c8[] =
{
0x00, 0x00, 0x10, 0x1f, 0x36, 0x00, 0x5d, 0x04, 0x9d, 0x05, 0x10, 0xf2,
0x06, 0x60, 0x03, 0x11, 0xad, 0x00, 0xef, 0x01, 0x22, 0x2e, 0x0e, 0x74,
0x08, 0x32, 0xdc, 0x09, 0x33, 0x0f, 0xf3, 0x77, 0x0d, 0xb0, 0xdc, 0x03,
0xff
};
static const uint8_t g_st7123_cmd_c9[] =
{
0x00, 0x00, 0x10, 0x1f, 0x36, 0x00, 0x5d, 0x04, 0x9d, 0x05, 0x10, 0xf2,
0x06, 0x60, 0x03, 0x11, 0xad, 0x00, 0xef, 0x01, 0x22, 0x2e, 0x0e, 0x74,
0x08, 0x32, 0xdc, 0x09, 0x33, 0x0f, 0xf3, 0x77, 0x0d, 0xb0, 0xdc, 0x03,
0xff
};
static const uint8_t g_st7123_cmd_36[] =
{
0x03
};
static const uint8_t g_st7123_cmd_35[] =
{
0x00
};
static const struct st7123_init_cmd_s g_st7123_init[] =
{
{
0x60, g_st7123_cmd_60a, sizeof(g_st7123_cmd_60a), 0
},
{
0x60, g_st7123_cmd_60b, sizeof(g_st7123_cmd_60b), 0
},
{
0x60, g_st7123_cmd_60c, sizeof(g_st7123_cmd_60c), 0
},
{
0xa4, g_st7123_cmd_a4, sizeof(g_st7123_cmd_a4), 0
},
{
0xd7, g_st7123_cmd_d7, sizeof(g_st7123_cmd_d7), 0
},
{
0x90, g_st7123_cmd_90, sizeof(g_st7123_cmd_90), 0
},
{
0xa3, g_st7123_cmd_a3, sizeof(g_st7123_cmd_a3), 0
},
{
0xa6, g_st7123_cmd_a6, sizeof(g_st7123_cmd_a6), 0
},
{
0xa7, g_st7123_cmd_a7, sizeof(g_st7123_cmd_a7), 0
},
{
0xac, g_st7123_cmd_ac, sizeof(g_st7123_cmd_ac), 0
},
{
0xad, g_st7123_cmd_ad, sizeof(g_st7123_cmd_ad), 0
},
{
0xae, g_st7123_cmd_ae, sizeof(g_st7123_cmd_ae), 0
},
{
0xb2, g_st7123_cmd_b2, sizeof(g_st7123_cmd_b2), 0
},
{
0xe8, g_st7123_cmd_e8, sizeof(g_st7123_cmd_e8), 0
},
{
0x75, g_st7123_cmd_75, sizeof(g_st7123_cmd_75), 0
},
{
0xe7, g_st7123_cmd_e7, sizeof(g_st7123_cmd_e7), 0
},
{
0xea, g_st7123_cmd_ea, sizeof(g_st7123_cmd_ea), 0
},
{
0xb0, g_st7123_cmd_b0, sizeof(g_st7123_cmd_b0), 0
},
{
0xb7, g_st7123_cmd_b7, sizeof(g_st7123_cmd_b7), 0
},
{
0xbf, g_st7123_cmd_bf, sizeof(g_st7123_cmd_bf), 0
},
{
0xa9, g_st7123_cmd_a9, sizeof(g_st7123_cmd_a9), 0
},
{
0xc8, g_st7123_cmd_c8, sizeof(g_st7123_cmd_c8), 0
},
{
0xc9, g_st7123_cmd_c9, sizeof(g_st7123_cmd_c9), 0
},
{
0x36, g_st7123_cmd_36, sizeof(g_st7123_cmd_36), 0
},
/* 0x11 / 0x29: 0-param DCS (dummy 0x00 as 1-param broke sleep-out).
* Delays match ESP-BSP disp_init_data_st7123 (120 / 50).
*/
{
0x11, NULL, 0, 120
},
{
0x29, NULL, 0, 50
},
{
0x35, g_st7123_cmd_35, sizeof(g_st7123_cmd_35), 0
},
};
#define ST7123_INIT_COUNT (sizeof(g_st7123_init) / sizeof(g_st7123_init[0]))
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: st7123_send_init
*
* Description:
* Send the ST7123 vendor init table.
*
* Input Parameters:
* device - Pointer to the MIPI-DSI device structure.
*
* Returned Value:
* Zero on success, -1 on failure.
****************************************************************************/
static int st7123_send_init(FAR struct mipi_dsi_device *device)
{
unsigned int i;
ssize_t n;
for (i = 0; i < ST7123_INIT_COUNT; i++)
{
FAR const struct st7123_init_cmd_s *cmd = &g_st7123_init[i];
n = mipi_dsi_dcs_write(device, cmd->cmd, cmd->data, cmd->len);
if (n < 0)
{
syslog(LOG_ERR,
"ERROR: DCS 0x%02x failed: %zd (cmd %u/%u)\n",
cmd->cmd, n, i + 1, ST7123_INIT_COUNT);
return (int)n;
}
if (cmd->delay_ms > 0)
{
up_mdelay(cmd->delay_ms);
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tab5_st7123_initialize
*
* Description:
* Register the ST7123 as a mipi_dsi_device, attach to the Espressif host,
* pulse LCD reset via PI4IOE, and send the ESP-BSP Tab5 vendor DCS init
* table (disp_init_data_st7123[]). Does not start video; board code
* calls display_on after video_start.
*
* Input Parameters:
* host - Pointer to the MIPI-DSI host structure.
*
* Returned Value:
* Pointer to the MIPI-DSI device structure on success, NULL on failure.
****************************************************************************/
FAR struct mipi_dsi_device *tab5_st7123_initialize(
FAR struct mipi_dsi_host *host)
{
FAR struct mipi_dsi_device *device;
int ret;
if (host == NULL)
{
return NULL;
}
ret = tab5_lcd_enable(true);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: LCD_EN enable failed: %d\n", ret);
return NULL;
}
device = mipi_dsi_device_register(host, ST7123_NAME, 0);
if (device == NULL)
{
syslog(LOG_ERR, "ERROR: mipi_dsi_device_register failed\n");
return NULL;
}
device->lanes = TAB5_MIPI_DSI_LANES;
device->format = MIPI_DSI_FMT_RGB565;
device->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
device->hs_rate = TAB5_MIPI_DSI_LANE_BITRATE_MBPS * 1000000UL;
device->lp_rate = 0;
ret = mipi_dsi_attach(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: attach failed: %d\n", ret);
return NULL;
}
ret = st7123_send_init(device);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: DCS init failed: %d\n", ret);
return NULL;
}
return device;
}

View file

@ -0,0 +1,74 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_lcd_st7123.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7123_H
#define __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7123_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/video/mipi_dsi.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifndef __ASSEMBLY__
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: tab5_st7123_initialize
*
* Description:
* Register the ST7123 as a mipi_dsi_device, attach to the Espressif host,
* pulse LCD reset via PI4IOE, and send the ESP-BSP Tab5 vendor DCS init
* table (disp_init_data_st7123[]). Does not start video; board code
* calls display_on after video_start.
*
* Input Parameters:
* host - Registered MIPI-DSI host (esp_mipi_dsi_host_get())
*
* Returned Value:
* Pointer to the registered device on success; NULL on failure.
*
****************************************************************************/
#ifdef CONFIG_ESP32P4_TAB5_LCD_ST7123
FAR struct mipi_dsi_device *tab5_st7123_initialize(
FAR struct mipi_dsi_host *host);
#endif
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __BOARDS_RISCV_ESP32P4_ESP32P4_TAB5_SRC_ESP32P4_LCD_ST7123_H */

View file

@ -0,0 +1,69 @@
/****************************************************************************
* boards/risc-v/esp32p4/esp32p4-tab5/src/esp32p4_mipi_dpi.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <nuttx/video/mipi_dsi.h>
#include <arch/board/board.h>
#include "esp32p4-tab5.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tab5_mipi_dsi_dpi_config
*
* Description:
* Configure the MIPI-DSI DPI configuration.
*
* Input Parameters:
* cfg - Pointer to the MIPI-DSI DPI configuration structure.
*
* Returned Value:
* None.
****************************************************************************/
void tab5_mipi_dsi_dpi_config(FAR struct esp_mipi_dsi_dpi_config_s *cfg)
{
DEBUGASSERT(cfg != NULL);
cfg->h_res = TAB5_MIPI_DSI_H_RES;
cfg->v_res = TAB5_MIPI_DSI_V_RES;
cfg->hsync_pulse_width = TAB5_MIPI_DSI_HSYNC_PULSE_WIDTH;
cfg->hsync_back_porch = TAB5_MIPI_DSI_HSYNC_BACK_PORCH;
cfg->hsync_front_porch = TAB5_MIPI_DSI_HSYNC_FRONT_PORCH;
cfg->vsync_pulse_width = TAB5_MIPI_DSI_VSYNC_PULSE_WIDTH;
cfg->vsync_back_porch = TAB5_MIPI_DSI_VSYNC_BACK_PORCH;
cfg->vsync_front_porch = TAB5_MIPI_DSI_VSYNC_FRONT_PORCH;
cfg->dpi_clock_freq_mhz = TAB5_MIPI_DSI_DPI_CLK_MHZ;
cfg->virtual_channel = 0;
cfg->format = MIPI_DSI_FMT_RGB565;
}