feat(drivers): add NXP ELCDIF initial display support (#8349)
Arduino Lint / lint (push) Has been cancelled
Build Examples with C++ Compiler / build-examples (push) Has been cancelled
MicroPython CI / Build esp32 port (push) Has been cancelled
MicroPython CI / Build rp2 port (push) Has been cancelled
MicroPython CI / Build stm32 port (push) Has been cancelled
MicroPython CI / Build unix port (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_NORMAL_8BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_SDL - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_VG_LITE - gcc - Windows (push) Has been cancelled
C/C++ CI / Build ESP IDF ESP32S3 (push) Has been cancelled
C/C++ CI / Run tests with 32bit build (push) Has been cancelled
C/C++ CI / Run tests with 64bit build (push) Has been cancelled
BOM Check / bom-check (push) Has been cancelled
Verify that lv_conf_internal.h matches repository state / verify-conf-internal (push) Has been cancelled
Verify the widget property name / verify-property-name (push) Has been cancelled
Verify code formatting / verify-formatting (push) Has been cancelled
Build docs / build-and-deploy (push) Has been cancelled
Test API JSON generator / Test API JSON (push) Has been cancelled
Check Makefile / Build using Makefile (push) Has been cancelled
Check Makefile for UEFI / Build using Makefile for UEFI (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_32B - Ubuntu (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_64B - Ubuntu (push) Has been cancelled
Port repo release update / run-release-branch-updater (push) Has been cancelled
Verify Font License / verify-font-license (push) Has been cancelled
Verify Kconfig / verify-kconfig (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled

Signed-off-by: Felipe Neves <felipe@lvgl.io>
Co-authored-by: Attila Kiss <kissattila96@gmail.com>
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Felipe Neves
2025-06-11 14:47:15 -03:00
committed by GitHub
parent 57e93bae02
commit a051ee4e79
7 changed files with 352 additions and 0 deletions
+4
View File
@@ -2077,6 +2077,10 @@ menu "LVGL configuration"
bool "Generic MIPI driver"
default y if LV_USE_ST7735 || LV_USE_ST7789 || LV_USE_ST7796 || LV_USE_ILI9341
config LV_USE_NXP_ELCDIF
bool "Use NXP ELCDIF to generate display"
default n
config LV_USE_RENESAS_GLCDC
bool "Use Renesas GLCDC driver"
default n
@@ -10,6 +10,7 @@ Display
gen_mipi
ili9341
lcd_stm32_guide
nxp_elcdif
renesas_glcdc
st_ltdc
st7735
@@ -0,0 +1,66 @@
.. _nxp_elcdif:
=============
NXP eLCDIF
=============
Overview
--------
eLCDIF is a peripheral that is provided on some of the NXP devices capable to drive display panels through
the RGB interface, it supports different color depths and, on MIPI-DSI capable devices, its output ca be
directed to the MIPI display physical interface. The LVGL's NXP eLCDIF driver is responsible to bind the
NXP MCUx SDK low-level driver to the LVGL display subsystem.
Prerequisites
-------------
- This driver relies on the presence of the MCUx SDK from NXP in the same project
- Activate the diver by setting :c:macro:`LV_USE_NXP_ELCDIF` to ``1`` in your *"lv_conf.h"*.
Usage
-----
The LVGL driver for eLCDIF assumes the platform already configured the display low-level driver,
set the pin-mux, clocks, etc. It also requires the base address of the peripheral and configuration
structure already set.
The following code demonstrates using the diver in :cpp:enumerator:`LV_DISPLAY_RENDER_MODE_DIRECT` mode,
please notice in this mode of operation the application is responsible to allocate the framebuffers space
and pass them to the display, in the example below `buffer1` and `buffer2` are the current and the next
buffers that will be copied to the display screen, being swapped at each flush operation (managed
internally by the display driver). Also observe, in direct mode, each buffer should have the space at
least to hold at least the size of the screen, that is it, the heigh times the width times the bytes
for a pixel (which is application dependent or display supported), on the code below this size is represented
by `buf_size`.
.. code-block:: c
elcdif_rgb_mode_config_t config;
ELCDIF_RgbModeGetDefaultConfig(&config);
lv_display_t * g_disp = lv_nxp_display_elcdif_create_direct(LCDIF, config, buffer1, buffer2, buf_size);
lv_display_set_default(g_disp);
To use the driver in :cpp:enumerator:`LV_DISPLAY_RENDER_MODE_PARTIAL` mode, an extra buffer must be allocated,
preferably in the fastest available memory region.
Buffer swapping can be activated by passing a second buffer of same size instead of the :cpp:expr:`NULL` argument.
please notice in this case the `BUF_SIZE` needs to have, at least, space to hold data of 1/10 of the actual
display dimensions.
.. code-block:: c
#define BUF_SIZE (DISPLAY_HEIGHT * DISPLAY_WIDTH / 10 * 2) /*1/10 screen size for RGB565 format*/
static uint8_t partial_draw_buf[BUF_SIZE];
lv_display_t * g_disp = lv_nxp_display_elcdif_create_partial(LCDIF, config, partial_draw_buf, NULL, BUF_SIZE);
In runtime, the event handler function from the eLCDIF driver should be called inside of the eLCDIF interrupt handler
This function is responsible for notify the LVGL display subsystem about a finished flush operation:
.. code-block:: c
void eLCDIF_IRQ_Handler(void)
{
lv_nxp_display_elcdif_event_handler(g_disp);
}
+3
View File
@@ -1298,6 +1298,9 @@
#define LV_ST_LTDC_USE_DMA2D_FLUSH 0
#endif
/** Driver for NXP ELCDIF */
#define LV_USE_NXP_ELCDIF 0
/** LVGL Windows backend */
#define LV_USE_WINDOWS 0
@@ -0,0 +1,185 @@
/**
* @file lv_nxp_elcdif.c
*
* Driver for NXP's ELCD
*/
#include "lv_nxp_elcdif.h"
#if LV_USE_NXP_ELCDIF == 1
/*********************
* INCLUDES
*********************/
#include "../../../display/lv_display_private.h"
#include "fsl_video_common.h"
#include "fsl_elcdif.h"
#include "fsl_cache.h"
/*********************
* DEFINES
*********************/
#if (defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && (0 != FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET))
#define ELCDIF_ADDR_IP_2_CPU(addr) (MEMORY_ConvertMemoryMapAddress((uint32_t)(addr), kMEMORY_DMA2Local))
#else
#define ELCDIF_ADDR_IP_2_CPU(addr) (addr)
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p);
static void flush_partial_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p);
static lv_color_format_t lv_nxp_elcdif_to_lvgl_color_converter(elcdif_rgb_mode_config_t * config);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_display_t * lv_nxp_display_elcdif_create_direct(LCDIF_Type * base, const elcdif_rgb_mode_config_t * config,
void * frame_buffer1,
void * frame_buffer2, size_t buf_size)
{
LV_ASSERT(base);
LV_ASSERT(config);
lv_display_t * disp = lv_display_create(config->panelWidth, config->panelHeight);
LV_ASSERT(disp);
lv_color_format_t color_format = lv_nxp_elcdif_to_lvgl_color_converter((elcdif_rgb_mode_config_t *)config);
lv_display_set_color_format(disp, color_format);
lv_display_set_buffers(disp, frame_buffer1, frame_buffer2, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT);
lv_display_set_user_data(disp, base);
ELCDIF_EnableInterrupts(base, kELCDIF_CurFrameDoneInterruptEnable);
NVIC_EnableIRQ(eLCDIF_IRQn);
return disp;
}
lv_display_t * lv_nxp_display_elcdif_create_partial(LCDIF_Type * base, const elcdif_rgb_mode_config_t * config,
void * frame_buffer1,
void * frame_buffer2, size_t buf_size)
{
LV_ASSERT(base);
LV_ASSERT(config);
/* Create a direct mode display and then update the buffers to be set in partial mode */
lv_display_t * disp = lv_nxp_display_elcdif_create_direct(base, config, frame_buffer1, frame_buffer2, buf_size);
ELCDIF_DisableInterrupts(base, kELCDIF_CurFrameDoneInterruptEnable);
NVIC_DisableIRQ(eLCDIF_IRQn);
lv_display_set_flush_cb(disp, flush_partial_cb);
lv_display_set_buffers(disp, frame_buffer1, frame_buffer2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
ELCDIF_EnableInterrupts(base, kELCDIF_CurFrameDoneInterruptEnable);
NVIC_EnableIRQ(eLCDIF_IRQn);
return disp;
}
void lv_nxp_display_elcdif_event_handler(const lv_display_t * disp)
{
if(disp == NULL) {
/* Just return since no valid display has been set yet */
return;
}
LCDIF_Type * base = (LCDIF_Type *)lv_display_get_user_data((lv_display_t *)disp);
uint32_t intStatus = ELCDIF_GetInterruptStatus(base);
ELCDIF_ClearInterruptStatus(base, intStatus);
if(intStatus & kELCDIF_CurFrameDone) {
/* flush ready is ISR safe and atomic, so calling inside of the
* framebuffer interrupt is safe and makes the flush chain
* non blocking even in bare metal systems.
*/
lv_disp_flush_ready((lv_display_t *)disp);
}
SDK_ISR_EXIT_BARRIER;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p)
{
LCDIF_Type * base = (LCDIF_Type *)lv_display_get_user_data(disp);
DCACHE_CleanInvalidateByRange((uint32_t)color_p, lv_display_get_draw_buf_size(disp));
if(!lv_display_flush_is_last(disp)) {
lv_disp_flush_ready(disp);
return;
}
ELCDIF_SetNextBufferAddr(base, (uint32_t)color_p);
}
static void flush_partial_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p)
{
LCDIF_Type * base = (LCDIF_Type *)lv_display_get_user_data(disp);
DCACHE_CleanInvalidateByRange((uint32_t)color_p, lv_display_get_draw_buf_size(disp));
uint8_t * fb = (uint8_t *)ELCDIF_ADDR_IP_2_CPU(base->CUR_BUF);
int32_t w = lv_area_get_width(area);
int32_t h = lv_area_get_height(area);
int32_t disp_w = lv_display_get_horizontal_resolution(disp);
int32_t disp_h = lv_display_get_vertical_resolution(disp);
int32_t bytes_per_pixel = LV_COLOR_FORMAT_GET_SIZE(lv_display_get_color_format(disp));
int32_t i;
fb = fb + area->y1 * disp_h;
fb = fb + area->x1;
for(i = 0; i < h; i++) {
lv_memcpy(fb, color_p, w * bytes_per_pixel);
fb += disp_h;
color_p += w;
}
}
static lv_color_format_t lv_nxp_elcdif_to_lvgl_color_converter(elcdif_rgb_mode_config_t * config)
{
/*Handle color format conversion*/
lv_color_format_t color_format;
switch(config->pixelFormat) {
case kELCDIF_PixelFormatRAW8 :
color_format = LV_COLOR_FORMAT_L8;
break;
case kELCDIF_PixelFormatRGB565 :
color_format = LV_COLOR_FORMAT_RGB565;
break;
case kELCDIF_PixelFormatXRGB8888 :
color_format = LV_COLOR_FORMAT_XRGB8888;
break;
case kELCDIF_PixelFormatRGB888 :
color_format = LV_COLOR_FORMAT_RGB888;
break;
/*
There are some color formats in ELCDIF which LVGL does not support.
For these, use unknown format and drop a msg for the user
*/
default :
color_format = LV_COLOR_FORMAT_UNKNOWN;
LV_LOG_WARN("Not supported color format in ELCDIF. Using LV_UNKNOWN!");
}
return color_format;
}
#endif /*LV_USE_NXP_ELCDIF*/
@@ -0,0 +1,84 @@
/**
* @file lv_nxp_elcdif.h
* Driver for NXP's ELCD
*/
#ifndef LV_NXP_ELCDIF_DRIVER_H
#define LV_NXP_ELCDIF_DRIVER_H
#include "../../../lvgl.h"
#include "../../../display/lv_display.h"
#if LV_USE_NXP_ELCDIF == 1
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "fsl_elcdif.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Attach LVGL to ELCDIF using DIRECT rendering mode.
* ELCDIF should be already initialized.
* @param base The NXP eLCD controller base address
* @param config NXP eLCD config object
* @param frame_buffer1 pointer the first frame buffers
* @param frame_buffer2 pointer the second frame buffers
* @param buf_size size of a buffer in bytes (must be at least as large as the screen)
* @return a display object initialized and registerd on the LVGL runtime
*/
lv_display_t * lv_nxp_display_elcdif_create_direct(LCDIF_Type * base, const elcdif_rgb_mode_config_t * config,
void * frame_buffer1,
void * frame_buffer2, size_t buf_size);
/**
* Attach LVGL to ELCDIF using PARTIAL rendering mode.
* ELCDIF should be already initialized.
* @param base The NXP eLCD controller base address
* @param config NXP eLCD config object
* @param frame_buffer1 pointer the first frame buffers
* @param frame_buffer2 pointer the second frame buffers
* @param buf_size size of a buffer in bytes
* @return a display object initialized and registerd on the LVGL runtime
*/
lv_display_t * lv_nxp_display_elcdif_create_partial(LCDIF_Type * base, const elcdif_rgb_mode_config_t * config,
void * frame_buffer1,
void * frame_buffer2, size_t buf_size);
/**
* Call this function on the LCD Interrupt Service Routine
* It tells to LVGL what to do when a framebuffer is transmitted
* to the LCD panel
* @param disp The display instance that contains the eLCD related data
*
* @note: the parameter disp is tipycally the return value after
* `lv_nxp_display_elcdif_create_direct` has been sucessfully executed
*/
void lv_nxp_display_elcdif_event_handler(const lv_display_t * disp);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_NXP_ELCDIF*/
#endif /* LV_NXP_ELCDIF_DRIVER_H */
+9
View File
@@ -4209,6 +4209,15 @@
#endif
#endif
/** Driver for NXP ELCDIF */
#ifndef LV_USE_NXP_ELCDIF
#ifdef CONFIG_LV_USE_NXP_ELCDIF
#define LV_USE_NXP_ELCDIF CONFIG_LV_USE_NXP_ELCDIF
#else
#define LV_USE_NXP_ELCDIF 0
#endif
#endif
/** LVGL Windows backend */
#ifndef LV_USE_WINDOWS
#ifdef CONFIG_LV_USE_WINDOWS