Skip to content

Commit f2180be

Browse files
committed
feat(esp_lvgl_port): Use display flush wait callback with semaphore
In the current implementation LVGL spins in a busy loop until lv_disp_flush_ready() is called. This commit utilizes the flush wait callback mechanism is used instead. lvgl_port_flush_wait_smphr_callback() waits on a semaphore allowing other FreeRTOS tasks to run. CPU usage by LVGL task is reduced by 2-3%. This feature is not avalable for RGB interface - I don't know exactly how flushing works in this case. The task notification and waking up in lvgl_port_flush_gsync_ready_callback() is confusing. Also lvgl_port_flush_ready() is untouched.
1 parent 7917d41 commit f2180be

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
lines changed

components/esp_lvgl_port/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## [Unreleased]
4+
5+
### Features
6+
- Added display flush wait callback support
7+
38
## 2.6.2
49

510
- Changed minimum IDF version to IDF5.1

components/esp_lvgl_port/priv_include/esp_lvgl_port_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ typedef enum {
2929
*/
3030
typedef struct {
3131
unsigned int avoid_tearing: 1; /*!< Use internal RGB buffers as a LVGL draw buffers to avoid tearing effect */
32+
unsigned int use_flush_smphr: 1; /*!< Use semaphore to signal LCD panel flush done */
3233
} lvgl_port_disp_priv_cfg_t;
3334

3435
/**

components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -59,6 +59,7 @@ typedef struct {
5959
uint8_t *oled_buffer;
6060
lv_display_t *disp_drv; /* LVGL display driver */
6161
lv_display_rotation_t current_rotation;
62+
SemaphoreHandle_t flush_smphr; /* LCD panel Flush Done semaphore (NULL if lv_disp_flush_ready() is used) */
6263
SemaphoreHandle_t trans_sem; /* Idle transfer mutex */
6364
#if LVGL_PORT_PPA
6465
lvgl_port_ppa_handle_t ppa_handle;
@@ -85,6 +86,7 @@ static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t pane
8586
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
8687
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
8788
#endif
89+
static void lvgl_port_flush_wait_smphr_callback(lv_display_t *drv);
8890
#endif
8991
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map);
9092
static void lvgl_port_disp_size_update_callback(lv_event_t *e);
@@ -98,7 +100,14 @@ static void lvgl_port_display_invalidate_callback(lv_event_t *e);
98100
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
99101
{
100102
lvgl_port_lock(0);
101-
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL);
103+
const lvgl_port_disp_priv_cfg_t priv_cfg = {
104+
#if LVGL_PORT_HANDLE_FLUSH_READY
105+
.use_flush_smphr = 1, /* Always use semaphore for now */
106+
#else
107+
.use_flush_smphr = 0, /* Don't need semaphore if not handling flush ready */
108+
#endif
109+
};
110+
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
102111

103112
if (disp != NULL) {
104113
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp);
@@ -113,6 +122,11 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
113122
};
114123
/* Register done callback */
115124
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp);
125+
126+
/* Register wait callback if using semaphore */
127+
if (disp_ctx->flush_smphr) {
128+
lv_display_set_flush_wait_cb(disp, lvgl_port_flush_wait_smphr_callback);
129+
}
116130
#endif
117131

118132
/* Apply rotation from initial display configuration */
@@ -128,6 +142,7 @@ lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, co
128142
assert(dsi_cfg != NULL);
129143
const lvgl_port_disp_priv_cfg_t priv_cfg = {
130144
.avoid_tearing = dsi_cfg->flags.avoid_tearing,
145+
.use_flush_smphr = 1, /* Always use semaphore for now */
131146
};
132147
lvgl_port_lock(0);
133148
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
@@ -147,6 +162,11 @@ lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, co
147162
/* Register done callback */
148163
esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, disp);
149164

165+
/* Register wait callback if using semaphore */
166+
if (disp_ctx->flush_smphr) {
167+
lv_display_set_flush_wait_cb(disp, lvgl_port_flush_wait_smphr_callback);
168+
}
169+
150170
/* Apply rotation from initial display configuration */
151171
lvgl_port_disp_rotation_update(disp_ctx);
152172

@@ -232,6 +252,10 @@ esp_err_t lvgl_port_remove_disp(lv_display_t *disp)
232252
if (disp_ctx->trans_sem) {
233253
vSemaphoreDelete(disp_ctx->trans_sem);
234254
}
255+
256+
if (disp_ctx->flush_smphr) {
257+
vSemaphoreDelete(disp_ctx->flush_smphr);
258+
}
235259
#if LVGL_PORT_PPA
236260
if (disp_ctx->ppa_handle) {
237261
lvgl_port_ppa_delete(disp_ctx->ppa_handle);
@@ -261,6 +285,7 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp
261285
lv_color_t *buf2 = NULL;
262286
uint32_t buffer_size = 0;
263287
SemaphoreHandle_t trans_sem = NULL;
288+
SemaphoreHandle_t flush_sem = NULL;
264289
assert(disp_cfg != NULL);
265290
assert(disp_cfg->panel_handle != NULL);
266291
assert(disp_cfg->buffer_size > 0);
@@ -341,6 +366,13 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp
341366
disp_ctx->draw_buffs[1] = buf2;
342367
}
343368

369+
/* Use semaphore to signal LCD panel flush done */
370+
if (priv_cfg && priv_cfg->use_flush_smphr) {
371+
flush_sem = xSemaphoreCreateBinary();
372+
ESP_GOTO_ON_FALSE(flush_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create flush Semaphore");
373+
disp_ctx->flush_smphr = flush_sem;
374+
}
375+
344376
disp = lv_display_create(disp_cfg->hres, disp_cfg->vres);
345377

346378
/* Set display color format */
@@ -441,6 +473,9 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp
441473
if (trans_sem) {
442474
vSemaphoreDelete(trans_sem);
443475
}
476+
if (flush_sem) {
477+
vSemaphoreDelete(flush_sem);
478+
}
444479
}
445480

446481
return disp;
@@ -449,19 +484,47 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp
449484
#if LVGL_PORT_HANDLE_FLUSH_READY
450485
static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
451486
{
487+
BaseType_t need_yield = pdFALSE;
488+
452489
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
453490
assert(disp_drv != NULL);
454-
lv_disp_flush_ready(disp_drv);
455-
return false;
491+
lvgl_port_display_ctx_t *disp_ctx = lv_display_get_user_data(disp_drv);
492+
assert(disp_ctx != NULL);
493+
494+
if (disp_ctx->flush_smphr) {
495+
if (xPortInIsrContext() == pdTRUE) {
496+
need_yield = xSemaphoreGiveFromISR(disp_ctx->flush_smphr, &need_yield);
497+
} else {
498+
xSemaphoreGive(disp_ctx->flush_smphr);
499+
}
500+
} else {
501+
lv_disp_flush_ready(disp_drv);
502+
}
503+
504+
return (need_yield == pdTRUE);
456505
}
457506

458507
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
459508
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
460509
{
510+
BaseType_t need_yield = pdFALSE;
511+
461512
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
462513
assert(disp_drv != NULL);
463-
lv_disp_flush_ready(disp_drv);
464-
return false;
514+
lvgl_port_display_ctx_t *disp_ctx = lv_display_get_user_data(disp_drv);
515+
assert(disp_ctx != NULL);
516+
517+
if (disp_ctx->flush_smphr) {
518+
if (xPortInIsrContext() == pdTRUE) {
519+
need_yield = xSemaphoreGiveFromISR(disp_ctx->flush_smphr, &need_yield);
520+
} else {
521+
xSemaphoreGive(disp_ctx->flush_smphr);
522+
}
523+
} else {
524+
lv_disp_flush_ready(disp_drv);
525+
}
526+
527+
return (need_yield == pdTRUE);
465528
}
466529

467530
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
@@ -498,6 +561,16 @@ static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t pane
498561
return (need_yield == pdTRUE);
499562
}
500563
#endif
564+
565+
static void lvgl_port_flush_wait_smphr_callback(lv_display_t *drv)
566+
{
567+
assert(drv != NULL);
568+
569+
lvgl_port_display_ctx_t *disp_ctx = lv_display_get_driver_data(drv);
570+
assert(disp_ctx != NULL);
571+
assert(disp_ctx->flush_smphr != NULL); /* This callback should only be used with semaphore */
572+
xSemaphoreTake(disp_ctx->flush_smphr, portMAX_DELAY);
573+
}
501574
#endif
502575

503576
static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area_t *area, uint8_t **color_map)

0 commit comments

Comments
 (0)