Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/esp_lvgl_port/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features
- Scaling feature in touch
- Added support for PPA rotation in LVGL9 (available for ESP32-P4)

## 2.5.0

Expand Down
15 changes: 12 additions & 3 deletions components/esp_lvgl_port/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "4.4")
return()
endif()

set(ADD_SRCS "")
set(ADD_LIBS "")
set(PRIV_REQ "")
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "esp32p4")
list(APPEND ADD_SRCS "src/common/ppa/lcd_ppa.c")
list(APPEND ADD_LIBS idf::esp_driver_ppa)
list(APPEND PRIV_REQ esp_driver_ppa)
endif()

# This component uses a CMake workaround, so we can compile esp_lvgl_port for both LVGL8.x and LVGL9.x
# 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)
# 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
idf_component_register(
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "priv_include"
REQUIRES "esp_lcd")
REQUIRES "esp_lcd"
PRIV_REQUIRES "${PRIV_REQ}")

# Get LVGL version
idf_build_get_property(build_components BUILD_COMPONENTS)
Expand Down Expand Up @@ -39,8 +50,6 @@ endif()

# Add LVGL port extensions
set(PORT_PATH "src/${PORT_FOLDER}")
set(ADD_SRCS "")
set(ADD_LIBS "")

idf_build_get_property(build_components BUILD_COMPONENTS)
if("espressif__button" IN_LIST build_components)
Expand Down
10 changes: 10 additions & 0 deletions components/esp_lvgl_port/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
menu "ESP LVGL PORT"

config LVGL_PORT_ENABLE_PPA
depends on SOC_PPA_SUPPORTED
bool "Enable PPA for screen rotation"
default n
help
Enables using PPA for screen rotation.

endmenu
2 changes: 1 addition & 1 deletion components/esp_lvgl_port/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ Display rotation can be changed at runtime.
```

> [!NOTE]
> This feature consume more RAM.
> Software rotation consumes more RAM. Software rotation uses [PPA](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/ppa.html) if available on the chip (e.g. ESP32P4).

> [!NOTE]
> During the hardware rotating, the component call [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) API. When using software rotation, you cannot use neither `direct_mode` nor `full_refresh` in the driver. See [LVGL documentation](https://docs.lvgl.io/8.3/porting/display.html?highlight=sw_rotate) for more info.
Expand Down
2 changes: 1 addition & 1 deletion components/esp_lvgl_port/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "2.5.0"
version: "2.6.0"
description: ESP LVGL port
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
dependencies:
Expand Down
199 changes: 199 additions & 0 deletions components/esp_lvgl_port/src/common/ppa/lcd_ppa.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/


#include <string.h>
#include "esp_err.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#include "lcd_ppa.h"

#define PPA_LCD_ENABLE_CB 0

#if SOC_PPA_SUPPORTED
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))

struct lvgl_port_ppa_t {
uint8_t *buffer;
uint32_t buffer_size;
ppa_client_handle_t srm_handle;
uint32_t color_type_id;
};

static const char *TAG = "PPA";
/*******************************************************************************
* Function definitions
*******************************************************************************/
#if PPA_LCD_ENABLE_CB
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data);
#endif
/*******************************************************************************
* Public API functions
*******************************************************************************/

lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg)
{
esp_err_t ret = ESP_OK;
assert(cfg != NULL);

lvgl_port_ppa_t *ppa_ctx = malloc(sizeof(lvgl_port_ppa_t));
ESP_GOTO_ON_FALSE(ppa_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for PPA context allocation!");
memset(ppa_ctx, 0, sizeof(lvgl_port_ppa_t));

uint32_t buffer_caps = 0;
if (cfg->flags.buff_dma) {
buffer_caps |= MALLOC_CAP_DMA;
}
if (cfg->flags.buff_spiram) {
buffer_caps |= MALLOC_CAP_SPIRAM;
}
if (buffer_caps == 0) {
buffer_caps |= MALLOC_CAP_DEFAULT;
}

ppa_ctx->buffer_size = ALIGN_UP(cfg->buffer_size, CONFIG_CACHE_L2_CACHE_LINE_SIZE);
ppa_ctx->buffer = heap_caps_aligned_calloc(CONFIG_CACHE_L2_CACHE_LINE_SIZE, ppa_ctx->buffer_size, sizeof(uint8_t), buffer_caps);
assert(ppa_ctx->buffer != NULL);

ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
};
ESP_GOTO_ON_ERROR(ppa_register_client(&ppa_client_config, &ppa_ctx->srm_handle), err, TAG, "Error when registering PPA client!");

#if PPA_LCD_ENABLE_CB
ppa_event_callbacks_t ppa_cbs = {
.on_trans_done = _lvgl_port_ppa_callback,
};
ESP_GOTO_ON_ERROR(ppa_client_register_event_callbacks(ppa_ctx->srm_handle, &ppa_cbs), err, TAG, "Error when registering PPA callbacks!");
#endif

ppa_ctx->color_type_id = COLOR_TYPE_ID(cfg->color_space, cfg->pixel_format);

err:
if (ret != ESP_OK) {
if (ppa_ctx->buffer) {
free(ppa_ctx->buffer);
}
if (ppa_ctx) {
free(ppa_ctx);
}
}

return ppa_ctx;
}

void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle)
{
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
assert(ppa_ctx != NULL);

if (ppa_ctx->buffer) {
free(ppa_ctx->buffer);
}

ppa_unregister_client(ppa_ctx->srm_handle);

free(ppa_ctx);
}

uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle)
{
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
assert(ppa_ctx != NULL);
return ppa_ctx->buffer;
}

esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg)
{
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
assert(ppa_ctx != NULL);
assert(rotate_cfg != NULL);
const int w = rotate_cfg->area.x2 - rotate_cfg->area.x1 + 1;
const int h = rotate_cfg->area.y2 - rotate_cfg->area.y1 + 1;

/* Set dimension by screen size and rotation */
int out_w = w;
int out_h = h;

int x1 = rotate_cfg->area.x1;
int x2 = rotate_cfg->area.x2;
int y1 = rotate_cfg->area.y1;
int y2 = rotate_cfg->area.y2;

/* Rotate coordinates */
switch (rotate_cfg->rotation) {
case PPA_SRM_ROTATION_ANGLE_0:
break;
case PPA_SRM_ROTATION_ANGLE_90:
out_w = h;
out_h = w;
x1 = rotate_cfg->area.y1;
x2 = rotate_cfg->area.y2;
y1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2 - 1;
y2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1 - 1;
break;
case PPA_SRM_ROTATION_ANGLE_180:
x1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2 - 1;
x2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1 - 1;
y1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2 - 1;
y2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1 - 1;
break;
case PPA_SRM_ROTATION_ANGLE_270:
out_w = h;
out_h = w;
x1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2 - 1;
x2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1 - 1;
y1 = rotate_cfg->area.x1;
y2 = rotate_cfg->area.x2;
break;
}
/* Return new coordinates */
rotate_cfg->area.x1 = x1;
rotate_cfg->area.x2 = x2;
rotate_cfg->area.y1 = y1;
rotate_cfg->area.y2 = y2;

/* Prepare Operation */
ppa_srm_oper_config_t srm_oper_config = {
.in.buffer = rotate_cfg->in_buff,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = w,
.in.block_h = h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = ppa_ctx->color_type_id,

.out.buffer = ppa_ctx->buffer,
.out.buffer_size = ppa_ctx->buffer_size,
.out.pic_w = out_w,
.out.pic_h = out_h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = ppa_ctx->color_type_id,

.rotation_angle = rotate_cfg->rotation,
.scale_x = 1.0,
.scale_y = 1.0,

.byte_swap = rotate_cfg->swap_bytes,

.mode = rotate_cfg->ppa_mode,
.user_data = rotate_cfg->user_data,
};

return ppa_do_scale_rotate_mirror(ppa_ctx->srm_handle, &srm_oper_config);
}

#if PPA_LCD_ENABLE_CB
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data)
{
return false;
}
#endif

#endif
111 changes: 111 additions & 0 deletions components/esp_lvgl_port/src/common/ppa/lcd_ppa.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief LCD PPA
*/

#pragma once
#include "driver/ppa.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct lvgl_port_ppa_t lvgl_port_ppa_t;
typedef lvgl_port_ppa_t *lvgl_port_ppa_handle_t;

/**
* @brief Init configuration structure
*/
typedef struct {
uint32_t buffer_size; /*!< Size of the buffer for the PPA */
color_space_t color_space; /*!< Color space of input/output data */
uint32_t pixel_format; /*!< Pixel format of input/output data */
struct {
unsigned int buff_dma: 1; /*!< Allocated buffer will be DMA capable */
unsigned int buff_spiram: 1; /*!< Allocated buffer will be in PSRAM */
} flags;
} lvgl_port_ppa_cfg_t;

/**
* @brief Display area structure
*/
typedef struct {
uint16_t x1;
uint16_t x2;
uint16_t y1;
uint16_t y2;
} lvgl_port_ppa_disp_area_t;

/**
* @brief Display size structure
*/
typedef struct {
uint32_t hres;
uint32_t vres;
} lvgl_port_ppa_disp_size_t;

/**
* @brief Rotation configuration
*/
typedef struct {
uint8_t *in_buff; /*!< Input buffer for rotation */
lvgl_port_ppa_disp_area_t area; /*!< Coordinates of area */
lvgl_port_ppa_disp_size_t disp_size; /*!< Display size */
ppa_srm_rotation_angle_t rotation; /*!< Output rotation */
ppa_trans_mode_t ppa_mode; /*!< Blocking or non-blocking mode */
bool swap_bytes; /*!< SWAP bytes */
void *user_data;
} lvgl_port_ppa_disp_rotate_t;


/**
* @brief Initialize PPA
*
* @note This function initialize PPA SRM Client and create buffer for process.
*
* @param cfg Configuration structure
*
* @return
* - PPA LCD handle
*/
lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg);

/**
* @brief Remove PPA
*
* @param handle PPA LCD handle
*
* @note This function free buffer and deinitialize PPA.
*/
void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle);

/**
* @brief Get output buffer
*
* @param handle PPA LCD handle
*
* @note This function get allocated buffer for output of PPA operation.
*/
uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle);

/**
* @brief Do rotation
*
* @param handle PPA LCD handle
* @param rotate_cfg Rotation settings
*
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if memory allocation fails
*/
esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg);

#ifdef __cplusplus
}
#endif
Loading
Loading