Skip to content

Commit b2fce4d

Browse files
committed
feat(lvgl_port): Used PPA for rotation on ESP32-P4 and added SW rotation
1 parent d8c0bdc commit b2fce4d

File tree

7 files changed

+400
-6
lines changed

7 files changed

+400
-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+
## 2.6.0
4+
5+
## Features
6+
- Added support for PPA rotation in LVGL9 (available for ESP32P4)
7+
38
## 2.5.0
49

510
### Features (Functional change for button v4 users)

components/esp_lvgl_port/CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,24 @@ if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "4.4")
44
return()
55
endif()
66

7+
set(ADD_SRCS "")
8+
set(ADD_LIBS "")
9+
set(PRIV_REQ "")
10+
idf_build_get_property(target IDF_TARGET)
11+
if(${target} STREQUAL "esp32p4")
12+
list(APPEND ADD_SRCS "src/common/ppa/lcd_ppa.c")
13+
list(APPEND ADD_LIBS idf::esp_driver_ppa)
14+
list(APPEND PRIV_REQ esp_driver_ppa)
15+
endif()
16+
717
# This component uses a CMake workaround, so we can compile esp_lvgl_port for both LVGL8.x and LVGL9.x
818
# At the time of idf_component_register() we don't know which LVGL version is used, so we only register an INTERFACE component (with no sources)
919
# Later, when we know the LVGL version, we create another CMake library called 'lvgl_port_lib' and link it to the 'esp_lvgl_port' INTERFACE component
1020
idf_component_register(
1121
INCLUDE_DIRS "include"
1222
PRIV_INCLUDE_DIRS "priv_include"
13-
REQUIRES "esp_lcd")
23+
REQUIRES "esp_lcd"
24+
PRIV_REQUIRES "${PRIV_REQ}")
1425

1526
# Get LVGL version
1627
idf_build_get_property(build_components BUILD_COMPONENTS)
@@ -39,8 +50,6 @@ endif()
3950

4051
# Add LVGL port extensions
4152
set(PORT_PATH "src/${PORT_FOLDER}")
42-
set(ADD_SRCS "")
43-
set(ADD_LIBS "")
4453

4554
idf_build_get_property(build_components BUILD_COMPONENTS)
4655
if("espressif__button" IN_LIST build_components)

components/esp_lvgl_port/idf_component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: "2.5.0"
1+
version: "2.6.0"
22
description: ESP LVGL port
33
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
44
dependencies:
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
8+
#include <string.h>
9+
#include "esp_err.h"
10+
#include "esp_check.h"
11+
#include "esp_heap_caps.h"
12+
#include "soc/soc_caps.h"
13+
#include "lcd_ppa.h"
14+
15+
#define PPA_LCD_ENABLE_CB 0
16+
17+
#if SOC_PPA_SUPPORTED
18+
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
19+
20+
struct lvgl_port_ppa_t {
21+
uint8_t *buffer;
22+
uint32_t buffer_size;
23+
ppa_client_handle_t srm_handle;
24+
uint32_t color_type_id;
25+
};
26+
27+
static const char *TAG = "PPA";
28+
/*******************************************************************************
29+
* Function definitions
30+
*******************************************************************************/
31+
#if PPA_LCD_ENABLE_CB
32+
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data);
33+
#endif
34+
/*******************************************************************************
35+
* Public API functions
36+
*******************************************************************************/
37+
38+
lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg)
39+
{
40+
esp_err_t ret = ESP_OK;
41+
assert(cfg != NULL);
42+
43+
lvgl_port_ppa_t *ppa_ctx = malloc(sizeof(lvgl_port_ppa_t));
44+
ESP_GOTO_ON_FALSE(ppa_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for PPA context allocation!");
45+
memset(ppa_ctx, 0, sizeof(lvgl_port_ppa_t));
46+
47+
uint32_t buffer_caps = 0;
48+
if (cfg->flags.buff_dma) {
49+
buffer_caps |= MALLOC_CAP_DMA;
50+
}
51+
if (cfg->flags.buff_spiram) {
52+
buffer_caps |= MALLOC_CAP_SPIRAM;
53+
}
54+
if (buffer_caps == 0) {
55+
buffer_caps |= MALLOC_CAP_DEFAULT;
56+
}
57+
58+
ppa_ctx->buffer_size = ALIGN_UP(cfg->buffer_size, CONFIG_CACHE_L2_CACHE_LINE_SIZE);
59+
ppa_ctx->buffer = heap_caps_aligned_calloc(CONFIG_CACHE_L2_CACHE_LINE_SIZE, ppa_ctx->buffer_size, sizeof(uint8_t), buffer_caps);
60+
assert(ppa_ctx->buffer != NULL);
61+
62+
ppa_client_config_t ppa_client_config = {
63+
.oper_type = PPA_OPERATION_SRM,
64+
};
65+
ESP_GOTO_ON_ERROR(ppa_register_client(&ppa_client_config, &ppa_ctx->srm_handle), err, TAG, "Error when registering PPA client!");
66+
67+
#if PPA_LCD_ENABLE_CB
68+
ppa_event_callbacks_t ppa_cbs = {
69+
.on_trans_done = _lvgl_port_ppa_callback,
70+
};
71+
ESP_GOTO_ON_ERROR(ppa_client_register_event_callbacks(ppa_ctx->srm_handle, &ppa_cbs), err, TAG, "Error when registering PPA callbacks!");
72+
#endif
73+
74+
ppa_ctx->color_type_id = COLOR_TYPE_ID(cfg->color_space, cfg->pixel_format);
75+
76+
err:
77+
if (ret != ESP_OK) {
78+
if (ppa_ctx->buffer) {
79+
free(ppa_ctx->buffer);
80+
}
81+
if (ppa_ctx) {
82+
free(ppa_ctx);
83+
}
84+
}
85+
86+
return ppa_ctx;
87+
}
88+
89+
void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle)
90+
{
91+
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
92+
assert(ppa_ctx != NULL);
93+
94+
if (ppa_ctx->buffer) {
95+
free(ppa_ctx->buffer);
96+
}
97+
98+
ppa_unregister_client(ppa_ctx->srm_handle);
99+
100+
free(ppa_ctx);
101+
}
102+
103+
uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle)
104+
{
105+
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
106+
assert(ppa_ctx != NULL);
107+
return ppa_ctx->buffer;
108+
}
109+
110+
esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg)
111+
{
112+
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
113+
assert(ppa_ctx != NULL);
114+
assert(rotate_cfg != NULL);
115+
const int w = rotate_cfg->area.x2 - rotate_cfg->area.x1 + 1;
116+
const int h = rotate_cfg->area.y2 - rotate_cfg->area.y1 + 1;
117+
118+
/* Set dimension by screen size and rotation */
119+
int out_w = w;
120+
int out_h = h;
121+
122+
int x1 = rotate_cfg->area.x1;
123+
int x2 = rotate_cfg->area.x2;
124+
int y1 = rotate_cfg->area.y1;
125+
int y2 = rotate_cfg->area.y2;
126+
127+
/* Rotate coordinates */
128+
switch (rotate_cfg->rotation) {
129+
case PPA_SRM_ROTATION_ANGLE_0:
130+
break;
131+
case PPA_SRM_ROTATION_ANGLE_90:
132+
out_w = h;
133+
out_h = w;
134+
x1 = rotate_cfg->area.y1;
135+
x2 = rotate_cfg->area.y2;
136+
y1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2;
137+
y2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1;
138+
break;
139+
case PPA_SRM_ROTATION_ANGLE_180:
140+
x1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2 - 1;
141+
x2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1 - 1;
142+
y1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2;
143+
y2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1;
144+
break;
145+
case PPA_SRM_ROTATION_ANGLE_270:
146+
out_w = h;
147+
out_h = w;
148+
x1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2 - 1;
149+
x2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1 - 1;
150+
y1 = rotate_cfg->area.x1;
151+
y2 = rotate_cfg->area.x2;
152+
break;
153+
}
154+
/* Return new coordinates */
155+
rotate_cfg->area.x1 = x1;
156+
rotate_cfg->area.x2 = x2;
157+
rotate_cfg->area.y1 = y1;
158+
rotate_cfg->area.y2 = y2;
159+
160+
/* Prepare Operation */
161+
ppa_srm_oper_config_t srm_oper_config = {
162+
.in.buffer = rotate_cfg->in_buff,
163+
.in.pic_w = w,
164+
.in.pic_h = h,
165+
.in.block_w = w,
166+
.in.block_h = h,
167+
.in.block_offset_x = 0,
168+
.in.block_offset_y = 0,
169+
.in.srm_cm = ppa_ctx->color_type_id,
170+
171+
.out.buffer = ppa_ctx->buffer,
172+
.out.buffer_size = ppa_ctx->buffer_size,
173+
.out.pic_w = out_w,
174+
.out.pic_h = out_h,
175+
.out.block_offset_x = 0,
176+
.out.block_offset_y = 0,
177+
.out.srm_cm = ppa_ctx->color_type_id,
178+
179+
.rotation_angle = rotate_cfg->rotation,
180+
.scale_x = 1.0,
181+
.scale_y = 1.0,
182+
183+
.byte_swap = rotate_cfg->swap_bytes,
184+
185+
.mode = rotate_cfg->ppa_mode,
186+
.user_data = rotate_cfg->user_data,
187+
};
188+
189+
return ppa_do_scale_rotate_mirror(ppa_ctx->srm_handle, &srm_oper_config);
190+
}
191+
192+
#if PPA_LCD_ENABLE_CB
193+
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data)
194+
{
195+
return false;
196+
}
197+
#endif
198+
199+
#endif
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief LCD PPA
10+
*/
11+
12+
#pragma once
13+
#include "driver/ppa.h"
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
typedef struct lvgl_port_ppa_t lvgl_port_ppa_t;
20+
typedef lvgl_port_ppa_t *lvgl_port_ppa_handle_t;
21+
22+
/**
23+
* @brief Init configuration structure
24+
*/
25+
typedef struct {
26+
uint32_t buffer_size; /*!< Size of the buffer for the PPA */
27+
color_space_t color_space; /*!< Color space of input/output data */
28+
uint32_t pixel_format; /*!< Pixel format of input/output data */
29+
struct {
30+
unsigned int buff_dma: 1; /*!< Allocated buffer will be DMA capable */
31+
unsigned int buff_spiram: 1; /*!< Allocated buffer will be in PSRAM */
32+
} flags;
33+
} lvgl_port_ppa_cfg_t;
34+
35+
/**
36+
* @brief Display area structure
37+
*/
38+
typedef struct {
39+
uint16_t x1;
40+
uint16_t x2;
41+
uint16_t y1;
42+
uint16_t y2;
43+
} lvgl_port_ppa_disp_area_t;
44+
45+
/**
46+
* @brief Display size structure
47+
*/
48+
typedef struct {
49+
uint32_t hres;
50+
uint32_t vres;
51+
} lvgl_port_ppa_disp_size_t;
52+
53+
/**
54+
* @brief Rotation configuration
55+
*/
56+
typedef struct {
57+
uint8_t *in_buff; /*!< Input buffer for rotation */
58+
lvgl_port_ppa_disp_area_t area; /*!< Coordinates of area */
59+
lvgl_port_ppa_disp_size_t disp_size; /*!< Display size */
60+
ppa_srm_rotation_angle_t rotation; /*!< Output rotation */
61+
ppa_trans_mode_t ppa_mode; /*!< Blocking or non-blocking mode */
62+
bool swap_bytes; /*!< SWAP bytes */
63+
void *user_data;
64+
} lvgl_port_ppa_disp_rotate_t;
65+
66+
67+
/**
68+
* @brief Initialize PPA
69+
*
70+
* @note This function initialize PPA SRM Client and create buffer for process.
71+
*
72+
* @param cfg Configuration structure
73+
*
74+
* @return
75+
* - PPA LCD handle
76+
*/
77+
lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg);
78+
79+
/**
80+
* @brief Remove PPA
81+
*
82+
* @param handle PPA LCD handle
83+
*
84+
* @note This function free buffer and deinitialize PPA.
85+
*/
86+
void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle);
87+
88+
/**
89+
* @brief Get output buffer
90+
*
91+
* @param handle PPA LCD handle
92+
*
93+
* @note This function get allocated buffer for output of PPA operation.
94+
*/
95+
uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle);
96+
97+
/**
98+
* @brief Do rotation
99+
*
100+
* @param handle PPA LCD handle
101+
* @param rotate_cfg Rotation settings
102+
*
103+
* @return
104+
* - ESP_OK on success
105+
* - ESP_ERR_NO_MEM if memory allocation fails
106+
*/
107+
esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg);
108+
109+
#ifdef __cplusplus
110+
}
111+
#endif

0 commit comments

Comments
 (0)