From bdb5806fdf7a6a62eb717c964bdfa406138c3073 Mon Sep 17 00:00:00 2001 From: Liam <30486941+liamHowatt@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:48:43 -0400 Subject: [PATCH] feat(dma2d): add basic support (#6691) --- .devcontainer/__lv_conf.h__ | 12 + Kconfig | 20 ++ docs/integration/chip/stm32.rst | 20 ++ env_support/cmsis-pack/lv_conf_cmsis.h | 12 + lv_conf_template.h | 12 + src/draw/dma2d/lv_draw_dma2d.c | 461 +++++++++++++++++++++++++ src/draw/dma2d/lv_draw_dma2d.h | 49 +++ src/draw/dma2d/lv_draw_dma2d_fill.c | 125 +++++++ src/draw/dma2d/lv_draw_dma2d_img.c | 208 +++++++++++ src/draw/dma2d/lv_draw_dma2d_private.h | 155 +++++++++ src/draw/lv_draw_private.h | 2 +- src/lv_conf_internal.h | 30 ++ src/lv_init.c | 11 + 13 files changed, 1116 insertions(+), 1 deletion(-) create mode 100644 src/draw/dma2d/lv_draw_dma2d.c create mode 100644 src/draw/dma2d/lv_draw_dma2d.h create mode 100644 src/draw/dma2d/lv_draw_dma2d_fill.c create mode 100644 src/draw/dma2d/lv_draw_dma2d_img.c create mode 100644 src/draw/dma2d/lv_draw_dma2d_private.h diff --git a/.devcontainer/__lv_conf.h__ b/.devcontainer/__lv_conf.h__ index 99cd2585a..3b022e1f9 100644 --- a/.devcontainer/__lv_conf.h__ +++ b/.devcontainer/__lv_conf.h__ @@ -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 *=======================*/ diff --git a/Kconfig b/Kconfig index c73bd41fb..6ca76d9e4 100644 --- a/Kconfig +++ b/Kconfig @@ -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" diff --git a/docs/integration/chip/stm32.rst b/docs/integration/chip/stm32.rst index ed630e2a3..2d12c6282 100644 --- a/docs/integration/chip/stm32.rst +++ b/docs/integration/chip/stm32.rst @@ -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. diff --git a/env_support/cmsis-pack/lv_conf_cmsis.h b/env_support/cmsis-pack/lv_conf_cmsis.h index 020b849c1..0cb7505af 100644 --- a/env_support/cmsis-pack/lv_conf_cmsis.h +++ b/env_support/cmsis-pack/lv_conf_cmsis.h @@ -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 *=======================*/ diff --git a/lv_conf_template.h b/lv_conf_template.h index 1ff84cb75..409bfa607 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -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 *=======================*/ diff --git a/src/draw/dma2d/lv_draw_dma2d.c b/src/draw/dma2d/lv_draw_dma2d.c new file mode 100644 index 000000000..83659c030 --- /dev/null +++ b/src/draw/dma2d/lv_draw_dma2d.c @@ -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*/ diff --git a/src/draw/dma2d/lv_draw_dma2d.h b/src/draw/dma2d/lv_draw_dma2d.h new file mode 100644 index 000000000..af175f1fc --- /dev/null +++ b/src/draw/dma2d/lv_draw_dma2d.h @@ -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*/ diff --git a/src/draw/dma2d/lv_draw_dma2d_fill.c b/src/draw/dma2d/lv_draw_dma2d_fill.c new file mode 100644 index 000000000..96396e1c3 --- /dev/null +++ b/src/draw/dma2d/lv_draw_dma2d_fill.c @@ -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*/ diff --git a/src/draw/dma2d/lv_draw_dma2d_img.c b/src/draw/dma2d/lv_draw_dma2d_img.c new file mode 100644 index 000000000..02e206894 --- /dev/null +++ b/src/draw/dma2d/lv_draw_dma2d_img.c @@ -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*/ diff --git a/src/draw/dma2d/lv_draw_dma2d_private.h b/src/draw/dma2d/lv_draw_dma2d_private.h new file mode 100644 index 000000000..051d02c13 --- /dev/null +++ b/src/draw/dma2d/lv_draw_dma2d_private.h @@ -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*/ diff --git a/src/draw/lv_draw_private.h b/src/draw/lv_draw_private.h index 2ebf5a1c9..e3fd03452 100644 --- a/src/draw/lv_draw_private.h +++ b/src/draw/lv_draw_private.h @@ -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; diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 5b7814b21..05185040e 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -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 *=======================*/ diff --git a/src/lv_init.c b/src/lv_init.c index 26897a7bc..b7eff16ea 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -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