1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(dma2d): add basic support (#6691)

This commit is contained in:
Liam 2024-09-11 03:48:43 -04:00 committed by GitHub
parent c0af741b45
commit bdb5806fdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1116 additions and 1 deletions

View File

@ -179,6 +179,18 @@
#endif
/* Accelerate blends, fills, etc. with STM32 DMA2D */
#define LV_USE_DRAW_DMA2D 0
#if LV_USE_DRAW_DMA2D
#define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h"
/* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler`
* upon receiving the DMA2D global interrupt
*/
#define LV_USE_DRAW_DMA2D_INTERRUPT 0
#endif
/*=======================
* FEATURE CONFIGURATION
*=======================*/

20
Kconfig
View File

@ -437,6 +437,26 @@ menu "LVGL configuration"
select LV_USE_MATRIX
help
Enable drawing support vector graphic APIs.
config LV_USE_DRAW_DMA2D
bool "Use DMA2D on the supporting STM32 platforms"
default n
help
Accelerate blends, fills, image decoding, etc. with STM32 DMA2D.
config LV_DRAW_DMA2D_HAL_INCLUDE
int "the header file for LVGL to include for DMA2D"
default "stm32h7xx_hal.h"
depends on LV_USE_DRAW_DMA2D
config LV_USE_DRAW_DMA2D_INTERRUPT
int "use the DMA2D transfer complete interrupt"
default n
depends on LV_USE_DRAW_DMA2D
help
if enabled, the user is required to call
`lv_draw_dma2d_transfer_complete_interrupt_handler`
upon receiving the DMA2D global interrupt
endmenu
menu "Feature Configuration"

View File

@ -264,3 +264,23 @@ variables:
* Inform the graphics library that you are ready with the flushing*/
lv_display_flush_ready(display);
}
DMA2D Support
-------------
LVGL supports DMA2D - a feature of some STM32 MCUs which can improve performance
when blending fills and images. Some STM32 product lines such as STM32F4 STM32F7, STM32L4,
STM32U5, and STM32H7 include models with DMA2D support.
LVGL's integration with DMA2D can be enabled by setting ``LV_USE_DRAW_DMA2D``
to ``1`` in ``lv_conf.h``
With ``LV_USE_DRAW_DMA2D_INTERRUPT`` set to ``0`` and ``LV_USE_OS`` set to ``LV_OS_NONE``,
DMA2D will draw some fills and images concurrently with the software render where
possible. If ``LV_USE_DRAW_DMA2D_INTERRUPT`` is set to ``1`` and ``LV_USE_OS`` set to
``LV_OS_FREERTOS`` (or another OS) the main difference will be that the core will idle
instead of "busywait" while waiting for a DMA2D transfer to complete.
If ``LV_USE_DRAW_DMA2D_INTERRUPT`` is enabled then you are required to call
:cpp:expr:`lv_draw_dma2d_transfer_complete_interrupt_handler` whenever the DMA2D
"transfer complete" global interrupt is received.

View File

@ -242,6 +242,18 @@
#endif
/* Accelerate blends, fills, etc. with STM32 DMA2D */
#define LV_USE_DRAW_DMA2D 0
#if LV_USE_DRAW_DMA2D
#define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h"
/* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler`
* upon receiving the DMA2D global interrupt
*/
#define LV_USE_DRAW_DMA2D_INTERRUPT 0
#endif
/*=======================
* FEATURE CONFIGURATION
*=======================*/

View File

@ -264,6 +264,18 @@
#define LV_VG_LITE_STROKE_CACHE_CNT 32
#endif
/* Accelerate blends, fills, etc. with STM32 DMA2D */
#define LV_USE_DRAW_DMA2D 0
#if LV_USE_DRAW_DMA2D
#define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h"
/* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler`
* upon receiving the DMA2D global interrupt
*/
#define LV_USE_DRAW_DMA2D_INTERRUPT 0
#endif
/*=======================
* FEATURE CONFIGURATION
*=======================*/

View File

@ -0,0 +1,461 @@
/**
* @file lv_draw_dma2d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
#include "../sw/lv_draw_sw.h"
#include "../../misc/lv_area_private.h"
#if !LV_DRAW_DMA2D_ASYNC && LV_USE_DRAW_DMA2D_INTERRUPT
#warning LV_USE_DRAW_DMA2D_INTERRUPT is 1 but has no effect because LV_USE_OS is LV_OS_NONE
#endif
/*********************
* DEFINES
*********************/
#define DRAW_UNIT_ID_DMA2D 5
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
static int32_t delete_cb(lv_draw_unit_t * draw_unit);
#if LV_DRAW_DMA2D_ASYNC
static void thread_cb(void * arg);
#endif
#if !LV_DRAW_DMA2D_ASYNC
static bool check_transfer_completion(void);
#endif
static void post_transfer_tasks(lv_draw_dma2d_unit_t * u);
/**********************
* STATIC VARIABLES
**********************/
#if LV_DRAW_DMA2D_ASYNC
static lv_draw_dma2d_unit_t * g_unit;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_init(void)
{
lv_draw_dma2d_unit_t * draw_dma2d_unit = lv_draw_create_unit(sizeof(lv_draw_dma2d_unit_t));
draw_dma2d_unit->base_unit.evaluate_cb = evaluate_cb;
draw_dma2d_unit->base_unit.dispatch_cb = dispatch_cb;
draw_dma2d_unit->base_unit.delete_cb = delete_cb;
#if LV_DRAW_DMA2D_ASYNC
g_unit = draw_dma2d_unit;
lv_result_t res = lv_thread_init(&draw_dma2d_unit->thread, LV_THREAD_PRIO_HIGH, thread_cb, 2 * 1024, draw_dma2d_unit);
LV_ASSERT(res == LV_RESULT_OK);
#endif
/* enable the DMA2D clock */
#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5)
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
#elif defined(STM32H7)
RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
#else
#warning "LVGL can't enable the clock for DMA2D"
#endif
/* disable dead time */
DMA2D->AMTCR = 0;
/* enable the interrupt */
NVIC_EnableIRQ(DMA2D_IRQn);
}
void lv_draw_dma2d_deinit(void)
{
/* disable the interrupt */
NVIC_DisableIRQ(DMA2D_IRQn);
/* disable the DMA2D clock */
#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) || defined(STM32L4)
RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA2DEN;
#elif defined(STM32H7)
RCC->AHB3ENR &= ~RCC_AHB3ENR_DMA2DEN;
#endif
#if LV_DRAW_DMA2D_ASYNC
lv_result_t res = lv_thread_delete(&g_unit->thread);
LV_ASSERT(res == LV_RESULT_OK);
res = lv_thread_sync_delete(&g_unit->interrupt_signal);
LV_ASSERT(res == LV_RESULT_OK);
g_unit = NULL;
#endif
}
#if LV_USE_DRAW_DMA2D_INTERRUPT
void lv_draw_dma2d_transfer_complete_interrupt_handler(void)
{
#if LV_DRAW_DMA2D_ASYNC
lv_thread_sync_signal_isr(&g_unit->interrupt_signal);
#endif
}
#endif
lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf)
{
switch(cf) {
case LV_COLOR_FORMAT_ARGB8888:
case LV_COLOR_FORMAT_XRGB8888:
return LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888;
case LV_COLOR_FORMAT_RGB888:
return LV_DRAW_DMA2D_OUTPUT_CF_RGB888;
case LV_COLOR_FORMAT_RGB565:
return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
default:
LV_ASSERT_MSG(false, "unsupported output color format");
}
}
uint32_t lv_draw_dma2d_color_to_dma2d_ocolr(lv_draw_dma2d_output_cf_t cf, lv_color_t color)
{
switch(cf) {
case LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888:
case LV_DRAW_DMA2D_OUTPUT_CF_RGB888:
return lv_color_to_u32(color);
case LV_DRAW_DMA2D_OUTPUT_CF_RGB565:
return lv_color_to_u16(color);
default:
LV_ASSERT_MSG(false, "unsupported output color format");
}
}
void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf)
{
/* number of lines register */
DMA2D->NLR = (conf->w << DMA2D_NLR_PL_Pos) | (conf->h << DMA2D_NLR_NL_Pos);
/* output */
/* output memory address register */
DMA2D->OMAR = (uint32_t)(uintptr_t) conf->output_address;
/* output offset register */
DMA2D->OOR = conf->output_offset;
/* output pixel format converter control register */
DMA2D->OPFCCR = ((uint32_t) conf->output_cf) << DMA2D_OPFCCR_CM_Pos;
/* Fill color. Only for mode LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY */
DMA2D->OCOLR = conf->reg_to_mem_mode_color;
/* foreground */
/* foreground memory address register */
DMA2D->FGMAR = (uint32_t)(uintptr_t) conf->fg_address;
/* foreground offset register */
DMA2D->FGOR = conf->fg_offset;
/* foreground color. only for mem-to-mem with blending and fixed-color foreground */
DMA2D->FGCOLR = conf->fg_color;
/* foreground pixel format converter control register */
DMA2D->FGPFCCR = (((uint32_t) conf->fg_cf) << DMA2D_FGPFCCR_CM_Pos)
| (conf->fg_alpha << DMA2D_FGPFCCR_ALPHA_Pos)
| (conf->fg_alpha_mode << DMA2D_FGPFCCR_AM_Pos);
/* background */
DMA2D->BGMAR = (uint32_t)(uintptr_t) conf->bg_address;
DMA2D->BGOR = conf->bg_offset;
DMA2D->BGCOLR = conf->bg_color;
DMA2D->BGPFCCR = (((uint32_t) conf->bg_cf) << DMA2D_BGPFCCR_CM_Pos)
| (conf->bg_alpha << DMA2D_BGPFCCR_ALPHA_Pos)
| (conf->bg_alpha_mode << DMA2D_BGPFCCR_AM_Pos);
/* ensure the DMA2D register values are observed before the start transfer bit is set */
__DSB();
/* start the transfer (also set mode and enable transfer complete interrupt) */
DMA2D->CR = DMA2D_CR_START | (((uint32_t) conf->mode) << DMA2D_CR_MODE_Pos)
#if LV_USE_DRAW_DMA2D_INTERRUPT
| DMA2D_CR_TCIE
#endif
;
}
#if LV_DRAW_DMA2D_CACHE
void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area)
{
if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return; /* data cache is disabled */
uint32_t rows_remaining = mem_area->height;
uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
uint32_t row_end_addr = 0;
__DSB();
while(rows_remaining) {
uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
__SCB_DCACHE_LINE_SIZE) + 1;
if(addr == row_end_addr) {
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
while(cache_lines) {
SCB->DCIMVAC = addr;
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
row_addr += mem_area->stride;
rows_remaining--;
};
__DSB();
__ISB();
}
void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area)
{
if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return; /* data cache is disabled */
uint32_t rows_remaining = mem_area->height;
uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
uint32_t row_end_addr = 0;
__DSB();
while(rows_remaining) {
uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
__SCB_DCACHE_LINE_SIZE) + 1;
if(addr == row_end_addr) {
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
while(cache_lines) {
SCB->DCCMVAC = addr;
addr += __SCB_DCACHE_LINE_SIZE;
cache_lines--;
}
row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
row_addr += mem_area->stride;
rows_remaining--;
};
__DSB();
__ISB();
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
{
switch(task->type) {
case LV_DRAW_TASK_TYPE_FILL: {
lv_draw_fill_dsc_t * dsc = task->draw_dsc;
if(!(dsc->radius == 0
&& dsc->grad.dir == LV_GRAD_DIR_NONE
&& (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
return 0;
}
}
break;
case LV_DRAW_TASK_TYPE_IMAGE: {
lv_draw_image_dsc_t * dsc = task->draw_dsc;
if(!(dsc->clip_radius == 0
&& dsc->bitmap_mask_src == NULL
&& dsc->sup == NULL
&& dsc->tile == 0
&& dsc->blend_mode == LV_BLEND_MODE_NORMAL
&& dsc->recolor_opa <= LV_OPA_MIN
&& dsc->skew_y == 0
&& dsc->skew_x == 0
&& dsc->scale_x == 256
&& dsc->scale_y == 256
&& dsc->rotation == 0
&& lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE
&& (dsc->header.cf == LV_COLOR_FORMAT_ARGB8888
|| dsc->header.cf == LV_COLOR_FORMAT_XRGB8888
|| dsc->header.cf == LV_COLOR_FORMAT_RGB888
|| dsc->header.cf == LV_COLOR_FORMAT_RGB565)
&& (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
|| dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
return 0;
}
}
break;
default:
return 0;
}
task->preferred_draw_unit_id = DRAW_UNIT_ID_DMA2D;
task->preference_score = 0;
return 0;
}
static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
{
lv_draw_dma2d_unit_t * draw_dma2d_unit = (lv_draw_dma2d_unit_t *) draw_unit;
if(draw_dma2d_unit->task_act) {
#if LV_DRAW_DMA2D_ASYNC
/*Return immediately if it's busy with draw task*/
return 0;
#else
if(!check_transfer_completion()) {
return LV_DRAW_UNIT_IDLE;
}
post_transfer_tasks(draw_dma2d_unit);
#endif
}
lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_DMA2D);
if(t == NULL) {
return LV_DRAW_UNIT_IDLE;
}
void * buf = lv_draw_layer_alloc_buf(layer);
if(buf == NULL) {
return LV_DRAW_UNIT_IDLE;
}
t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
draw_dma2d_unit->base_unit.target_layer = layer;
draw_dma2d_unit->base_unit.clip_area = &t->clip_area;
draw_dma2d_unit->task_act = t;
if(t->type == LV_DRAW_TASK_TYPE_FILL) {
lv_draw_fill_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, coords, draw_dma2d_unit->base_unit.clip_area)) {
return LV_DRAW_UNIT_IDLE;
}
void * dest = lv_draw_layer_go_to_xy(layer,
clipped_coords.x1 - layer->buf_area.x1,
clipped_coords.y1 - layer->buf_area.y1);
if(dsc->opa >= LV_OPA_MAX) {
lv_draw_dma2d_opaque_fill(draw_dma2d_unit,
dest,
lv_area_get_width(&clipped_coords),
lv_area_get_height(&clipped_coords),
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
else {
lv_draw_dma2d_fill(draw_dma2d_unit,
dest,
lv_area_get_width(&clipped_coords),
lv_area_get_height(&clipped_coords),
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
}
else if(t->type == LV_DRAW_TASK_TYPE_IMAGE) {
lv_draw_image_dsc_t * dsc = t->draw_dsc;
const lv_area_t * coords = &t->area;
lv_area_t clipped_coords;
if(!lv_area_intersect(&clipped_coords, coords, draw_dma2d_unit->base_unit.clip_area)) {
return LV_DRAW_UNIT_IDLE;
}
void * dest = lv_draw_layer_go_to_xy(layer,
clipped_coords.x1 - layer->buf_area.x1,
clipped_coords.y1 - layer->buf_area.y1);
if(dsc->opa >= LV_OPA_MAX) {
lv_draw_dma2d_opaque_image(
draw_dma2d_unit,
dest,
&clipped_coords,
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
else {
lv_draw_dma2d_image(
draw_dma2d_unit,
dest,
&clipped_coords,
lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
}
}
#if !LV_DRAW_DMA2D_ASYNC
lv_draw_dispatch_request();
#endif
return 1;
}
static int32_t delete_cb(lv_draw_unit_t * draw_unit)
{
return 0;
}
#if LV_DRAW_DMA2D_ASYNC
static void thread_cb(void * arg)
{
lv_draw_dma2d_unit_t * u = arg;
lv_thread_sync_init(&u->interrupt_signal);
while(1) {
do {
lv_thread_sync_wait(&u->interrupt_signal);
} while(u->task_act != NULL);
post_transfer_tasks(u);
lv_draw_dispatch_request();
}
}
#endif
#if !LV_DRAW_DMA2D_ASYNC
static bool check_transfer_completion(void)
{
return !(DMA2D->CR & DMA2D_CR_START);
}
#endif
static void post_transfer_tasks(lv_draw_dma2d_unit_t * u)
{
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_invalidate_cache(&u->writing_area);
#endif
u->task_act->state = LV_DRAW_TASK_STATE_READY;
u->task_act = NULL;
}
#endif /*LV_USE_DRAW_DMA2D*/

View File

@ -0,0 +1,49 @@
/**
* @file lv_draw_dma2d.h
*
*/
#ifndef LV_DRAW_DMA2D_H
#define LV_DRAW_DMA2D_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_dma2d_init(void);
void lv_draw_dma2d_deinit(void);
#if LV_USE_DRAW_DMA2D_INTERRUPT
void lv_draw_dma2d_transfer_complete_interrupt_handler(void);
#endif
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_DMA2D_H*/

View File

@ -0,0 +1,125 @@
/**
* @file lv_draw_dma2d_fill.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_opaque_fill(lv_draw_dma2d_unit_t * u, void * first_pixel, int32_t w, int32_t h, int32_t stride)
{
lv_draw_fill_dsc_t * dsc = u->task_act->draw_dsc;
lv_color_format_t cf = dsc->base.layer->color_format;
lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
uint32_t reg_to_mem_color = lv_draw_dma2d_color_to_dma2d_ocolr(output_cf, dsc->color);
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t cache_area = {
.first_byte = first_pixel,
.width_bytes = w * cf_size,
.height = h,
.stride = stride
};
lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
#endif
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
.w = w,
.h = h,
.output_address = first_pixel,
.output_offset = (stride / cf_size) - w,
.output_cf = output_cf,
.reg_to_mem_mode_color = reg_to_mem_color
};
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
void lv_draw_dma2d_fill(lv_draw_dma2d_unit_t * u, void * first_pixel, int32_t w, int32_t h, int32_t stride)
{
lv_draw_fill_dsc_t * dsc = u->task_act->draw_dsc;
lv_color_t color = dsc->color;
lv_color_format_t cf = dsc->base.layer->color_format;
lv_opa_t opa = dsc->opa;
lv_draw_dma2d_output_cf_t output_cf = lv_draw_dma2d_cf_to_dma2d_output_cf(cf);
uint32_t cf_size = LV_COLOR_FORMAT_GET_SIZE(cf);
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t cache_area = {
.first_byte = first_pixel,
.width_bytes = w * cf_size,
.height = h,
.stride = stride
};
lv_memcpy(&u->writing_area, &cache_area, sizeof(lv_draw_dma2d_cache_area_t));
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&cache_area);
#endif
uint32_t output_offset = (stride / cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_FG,
.w = w,
.h = h,
.output_address = first_pixel,
.output_offset = output_offset,
.output_cf = output_cf,
.fg_color = lv_color_to_u32(color),
.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
.fg_alpha = opa,
.bg_address = first_pixel,
.bg_offset = output_offset,
.bg_cf = (lv_draw_dma2d_fgbg_cf_t) output_cf
};
/* Background alpha channel should be treated as 0xFF if the cf is XRGB */
if(cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/

View File

@ -0,0 +1,208 @@
/**
* @file lv_draw_dma2d_image.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d_private.h"
#if LV_USE_DRAW_DMA2D
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_dma2d_opaque_image(lv_draw_dma2d_unit_t * u, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride)
{
int32_t w = lv_area_get_width(clipped_coords);
int32_t h = lv_area_get_height(clipped_coords);
lv_draw_image_dsc_t * dsc = u->task_act->draw_dsc;
lv_color_format_t output_cf = dsc->base.layer->color_format;
lv_color_format_t image_cf = dsc->header.cf;
lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
uint32_t output_cf_size = LV_COLOR_FORMAT_GET_SIZE(output_cf);
lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
const lv_image_dsc_t * img_dsc = dsc->src;
uint32_t image_stride = img_dsc->header.stride;
if(image_stride == 0) image_stride = image_cf_size * img_dsc->header.w;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t dest_area = {
.first_byte = dest_first_pixel,
.width_bytes = w * output_cf_size,
.height = h,
.stride = dest_stride
};
lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
if(lv_color_format_has_alpha(image_cf)) {
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&dest_area);
}
#endif
const void * image_first_byte = img_dsc->data
+ (image_stride * (clipped_coords->y1 - dsc->image_area.y1))
+ (image_cf_size * (clipped_coords->x1 - dsc->image_area.x1));
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t src_area = {
.first_byte = image_first_byte,
.width_bytes = w * image_cf_size,
.height = h,
.stride = image_stride
};
/* make sure the image area is up-to-date in main memory for DMA2D */
lv_draw_dma2d_clean_cache(&src_area);
#endif
uint32_t output_offset = (dest_stride / output_cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
.w = w,
.h = h,
.output_address = dest_first_pixel,
.output_offset = output_offset,
.output_cf = output_cf_dma2d,
.fg_address = image_first_byte,
.fg_offset = (image_stride / image_cf_size) - w,
.fg_cf = image_cf_dma2d
};
/* only process the background if the image might be transparent */
if(lv_color_format_has_alpha(image_cf)) {
conf.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING;
conf.bg_address = dest_first_pixel;
conf.bg_offset = output_offset;
conf.bg_cf = output_cf_dma2d;
}
/* Alpha channel should be treated as 0xFF if the cf is XRGB */
if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.fg_alpha = 0xff;
}
if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
void lv_draw_dma2d_image(lv_draw_dma2d_unit_t * u, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride)
{
int32_t w = lv_area_get_width(clipped_coords);
int32_t h = lv_area_get_height(clipped_coords);
lv_draw_image_dsc_t * dsc = u->task_act->draw_dsc;
lv_color_format_t output_cf = dsc->base.layer->color_format;
lv_color_format_t image_cf = dsc->header.cf;
lv_opa_t opa = dsc->opa;
lv_draw_dma2d_output_cf_t output_cf_dma2d = lv_draw_dma2d_cf_to_dma2d_output_cf(output_cf);
uint32_t output_cf_size = LV_COLOR_FORMAT_GET_SIZE(output_cf);
lv_draw_dma2d_fgbg_cf_t image_cf_dma2d = (lv_draw_dma2d_fgbg_cf_t) lv_draw_dma2d_cf_to_dma2d_output_cf(image_cf);
uint32_t image_cf_size = LV_COLOR_FORMAT_GET_SIZE(image_cf);
const lv_image_dsc_t * img_dsc = dsc->src;
uint32_t image_stride = img_dsc->header.stride;
if(image_stride == 0) image_stride = image_cf_size * img_dsc->header.w;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t dest_area = {
.first_byte = dest_first_pixel,
.width_bytes = w * output_cf_size,
.height = h,
.stride = dest_stride
};
lv_memcpy(&u->writing_area, &dest_area, sizeof(lv_draw_dma2d_cache_area_t));
/* make sure the background area DMA2D is blending is up-to-date in main memory */
lv_draw_dma2d_clean_cache(&dest_area);
#endif
const void * image_first_byte = img_dsc->data
+ (image_stride * (clipped_coords->y1 - dsc->image_area.y1))
+ (image_cf_size * (clipped_coords->x1 - dsc->image_area.x1));
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t src_area = {
.first_byte = image_first_byte,
.width_bytes = w * image_cf_size,
.height = h,
.stride = image_stride
};
/* make sure the image area is up-to-date in main memory for DMA2D */
lv_draw_dma2d_clean_cache(&src_area);
#endif
uint32_t output_offset = (dest_stride / output_cf_size) - w;
lv_draw_dma2d_configuration_t conf = {
.mode = LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
.w = w,
.h = h,
.output_address = dest_first_pixel,
.output_offset = output_offset,
.output_cf = output_cf_dma2d,
.fg_address = image_first_byte,
.fg_offset = (image_stride / image_cf_size) - w,
.fg_cf = image_cf_dma2d,
.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL,
.fg_alpha = opa,
.bg_address = dest_first_pixel,
.bg_offset = output_offset,
.bg_cf = output_cf_dma2d,
};
/* Alpha channel should be treated as 0xFF if the cf is XRGB */
if(image_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.fg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
}
if(output_cf == LV_COLOR_FORMAT_XRGB8888) {
conf.bg_alpha_mode = LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL;
conf.bg_alpha = 0xff;
}
lv_draw_dma2d_configure_and_start_transfer(&conf);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/

View File

@ -0,0 +1,155 @@
/**
* @file lv_draw_dma2d_private.h
*
*/
#ifndef LV_DRAW_DMA2D_PRIVATE_H
#define LV_DRAW_DMA2D_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw_dma2d.h"
#if LV_USE_DRAW_DMA2D
#include "../lv_draw_private.h"
#include LV_DRAW_DMA2D_HAL_INCLUDE
/*********************
* DEFINES
*********************/
#if LV_USE_DRAW_DMA2D_INTERRUPT && LV_USE_OS
#define LV_DRAW_DMA2D_ASYNC 1
#else
#define LV_DRAW_DMA2D_ASYNC 0
#endif
#if defined(__CORTEX_M) && (__CORTEX_M == 7)
#define LV_DRAW_DMA2D_CACHE 1
#else
#define LV_DRAW_DMA2D_CACHE 0
#endif
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888 = 0,
LV_DRAW_DMA2D_OUTPUT_CF_RGB888,
LV_DRAW_DMA2D_OUTPUT_CF_RGB565,
LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555,
LV_DRAW_DMA2D_OUTPUT_CF_ARGB4444
} lv_draw_dma2d_output_cf_t;
typedef enum {
LV_DRAW_DMA2D_FGBG_CF_ARGB8888 = 0,
LV_DRAW_DMA2D_FGBG_CF_RGB888,
LV_DRAW_DMA2D_FGBG_CF_RGB565,
LV_DRAW_DMA2D_FGBG_CF_ARGB1555,
LV_DRAW_DMA2D_FGBG_CF_ARGB4444,
LV_DRAW_DMA2D_FGBG_CF_L8,
LV_DRAW_DMA2D_FGBG_CF_AL44,
LV_DRAW_DMA2D_FGBG_CF_AL88,
LV_DRAW_DMA2D_FGBG_CF_L4,
LV_DRAW_DMA2D_FGBG_CF_A8,
LV_DRAW_DMA2D_FGBG_CF_A4,
LV_DRAW_DMA2D_FGBG_CF_YCBCR
} lv_draw_dma2d_fgbg_cf_t;
typedef enum {
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY = 0,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_PFC,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING,
LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_FG,
LV_DRAW_DMA2D_MODE_MEMORY_TO_MEMORY_WITH_BLENDING_AND_FIXED_COLOR_BG
} lv_draw_dma2d_mode_t;
typedef enum {
LV_DRAW_DMA2D_ALPHA_MODE_NO_MODIFY_IMAGE_ALPHA_CHANNEL = 0,
LV_DRAW_DMA2D_ALPHA_MODE_REPLACE_ALPHA_CHANNEL,
LV_DRAW_DMA2D_ALPHA_MODE_MULTIPLY_IMAGE_ALPHA_CHANNEL
} lv_draw_dma2d_alpha_mode_t;
typedef struct {
lv_draw_dma2d_mode_t mode;
uint32_t w;
uint32_t h;
void * output_address;
uint32_t output_offset;
lv_draw_dma2d_output_cf_t output_cf;
uint32_t reg_to_mem_mode_color;
const void * fg_address;
uint32_t fg_offset;
lv_draw_dma2d_fgbg_cf_t fg_cf;
uint32_t fg_color;
uint32_t fg_alpha_mode;
uint32_t fg_alpha;
const void * bg_address;
uint32_t bg_offset;
lv_draw_dma2d_fgbg_cf_t bg_cf;
uint32_t bg_color;
uint32_t bg_alpha_mode;
uint32_t bg_alpha;
} lv_draw_dma2d_configuration_t;
typedef struct {
const void * first_byte;
uint32_t width_bytes;
uint32_t height;
uint32_t stride;
} lv_draw_dma2d_cache_area_t;
typedef struct {
lv_draw_unit_t base_unit;
lv_draw_task_t * volatile task_act;
#if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_cache_area_t writing_area;
#endif
#if LV_DRAW_DMA2D_ASYNC
lv_thread_t thread;
lv_thread_sync_t interrupt_signal;
#endif
} lv_draw_dma2d_unit_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_dma2d_opaque_fill(lv_draw_dma2d_unit_t * u, void * first_pixel, int32_t w, int32_t h, int32_t stride);
void lv_draw_dma2d_fill(lv_draw_dma2d_unit_t * u, void * first_pixel, int32_t w, int32_t h, int32_t stride);
void lv_draw_dma2d_opaque_image(lv_draw_dma2d_unit_t * u, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride);
void lv_draw_dma2d_image(lv_draw_dma2d_unit_t * u, void * dest_first_pixel, lv_area_t * clipped_coords,
int32_t dest_stride);
lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf);
uint32_t lv_draw_dma2d_color_to_dma2d_ocolr(lv_draw_dma2d_output_cf_t cf, lv_color_t color);
void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf);
#if LV_DRAW_DMA2D_CACHE
void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area);
void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area);
#endif
/**********************
* MACROS
**********************/
#endif /*LV_USE_DRAW_DMA2D*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DRAW_DMA2D_PRIVATE_H*/

View File

@ -175,7 +175,7 @@ typedef struct {
#if LV_USE_OS
lv_thread_sync_t sync;
#else
int dispatch_req;
volatile int dispatch_req;
#endif
lv_mutex_t circle_cache_mutex;
bool task_running;

View File

@ -745,6 +745,36 @@
#endif
#endif
/* Accelerate blends, fills, etc. with STM32 DMA2D */
#ifndef LV_USE_DRAW_DMA2D
#ifdef CONFIG_LV_USE_DRAW_DMA2D
#define LV_USE_DRAW_DMA2D CONFIG_LV_USE_DRAW_DMA2D
#else
#define LV_USE_DRAW_DMA2D 0
#endif
#endif
#if LV_USE_DRAW_DMA2D
#ifndef LV_DRAW_DMA2D_HAL_INCLUDE
#ifdef CONFIG_LV_DRAW_DMA2D_HAL_INCLUDE
#define LV_DRAW_DMA2D_HAL_INCLUDE CONFIG_LV_DRAW_DMA2D_HAL_INCLUDE
#else
#define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h"
#endif
#endif
/* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler`
* upon receiving the DMA2D global interrupt
*/
#ifndef LV_USE_DRAW_DMA2D_INTERRUPT
#ifdef CONFIG_LV_USE_DRAW_DMA2D_INTERRUPT
#define LV_USE_DRAW_DMA2D_INTERRUPT CONFIG_LV_USE_DRAW_DMA2D_INTERRUPT
#else
#define LV_USE_DRAW_DMA2D_INTERRUPT 0
#endif
#endif
#endif
/*=======================
* FEATURE CONFIGURATION
*=======================*/

View File

@ -55,6 +55,9 @@
#if LV_USE_DRAW_VG_LITE
#include "draw/vg_lite/lv_draw_vg_lite.h"
#endif
#if LV_USE_DRAW_DMA2D
#include "draw/dma2d/lv_draw_dma2d.h"
#endif
#if LV_USE_WINDOWS
#include "drivers/windows/lv_windows_context.h"
#endif
@ -210,6 +213,10 @@ void lv_init(void)
lv_draw_sdl_init();
#endif
#if LV_USE_DRAW_DMA2D
lv_draw_dma2d_init();
#endif
#if LV_USE_WINDOWS
lv_windows_platform_init();
#endif
@ -397,6 +404,10 @@ void lv_deinit(void)
lv_draw_vg_lite_deinit();
#endif
#if LV_USE_DRAW_DMA2D
lv_draw_dma2d_deinit();
#endif
#if LV_USE_DRAW_SW
lv_draw_sw_deinit();
#endif