diff --git a/Kconfig b/Kconfig index 65afe06b2..51eabf11c 100644 --- a/Kconfig +++ b/Kconfig @@ -409,6 +409,26 @@ menu "LVGL configuration" config LV_USE_PXP_ASSERT bool "Enable PXP asserts" depends on LV_USE_DRAW_PXP + + config LV_USE_DRAW_G2D + bool "Use NXP's G2D on MPU platforms" + default n + + config LV_G2D_HASH_TABLE_SIZE + bool "Maximum number of buffers that can be stored for G2D draw unit." + default 50 + depends on LV_USE_DRAW_G2D + help + Includes the frame buffers and assets. + + config LV_USE_G2D_DRAW_THREAD + bool "Use additional draw thread for G2D processing" + depends on LV_USE_DRAW_G2D && LV_USE_OS > 0 + default y + + config LV_USE_G2D_ASSERT + bool "Enable G2D asserts" + depends on LV_USE_DRAW_G2D default n config LV_USE_DRAW_DAVE2D diff --git a/docs/details/integration/chip/nxp.rst b/docs/details/integration/chip/nxp.rst index 43bf440eb..ad9a4d58d 100644 --- a/docs/details/integration/chip/nxp.rst +++ b/docs/details/integration/chip/nxp.rst @@ -20,7 +20,7 @@ Creating new project with LVGL `Download an SDK for a supported board `__ today and get started with your next GUI application. It comes fully configured -with LVGL (and with PXP/VGLite support if the modules are present), no +with LVGL (and with PXP/VGLite/G2D support if the modules are present), no additional integration work is required. HW acceleration for NXP iMX RT platforms @@ -31,6 +31,13 @@ Depending on the RT platform used, the acceleration can be done by NXP PXP accelerator has its own context that allows them to be used individually as well simultaneously (in LVGL multithreading mode). +HW acceleration for NXP iMX platforms +---------------------------------------- + +On MPU platforms, the acceleration can be done (hardware independent) by NXP G2D +library. This accelerator has its own context that allows them to be used +individually as well simultaneously with the CPU (in LVGL multithreading mode). + PXP accelerator ~~~~~~~~~~~~~~~ Basic configuration: @@ -327,7 +334,7 @@ is available for other operations while the GPU is running. RTOS is required to block the LVGL drawing thread and switch to another task or suspend the CPU for power savings. -Supported draw tasks are available in "src/draw/nxp/pxp/lv_draw_vglite.c": +Supported draw tasks are available in "src/draw/nxp/vglite/lv_draw_vglite.c": .. code-block:: c @@ -408,3 +415,122 @@ Project setup: - "src/draw/nxp/vglite/lv_vglite_matrix.c": set vglite matrix - "src/draw/nxp/vglite/lv_vglite_path.c": create vglite path data - "src/draw/nxp/vglite/lv_vglite_utils.c": function helpers + +G2D accelerator +~~~~~~~~~~~~~~~ +Basic configuration: +^^^^^^^^^^^^^^^^^^^^ + +- Select NXP G2D engine in "lv_conf.h": Set :c:macro:`LV_USE_G2D` to `1`. +- In order to use G2D as a draw unit, select in "lv_conf.h": Set :c:macro:`LV_USE_DRAW_G2D` to `1`. +- Enable G2D asserts in "lv_conf.h": Set :c:macro: `LV_USE_G2D_ASSERT` to `1`. + There are few G2D assertions that can stop the program execution in case the + c:macro: `LV_ASSERT_HANDLER` is set to `while(1);` (Halt by default). Else, + there will be logged just an error message via `LV_LOG_ERROR`. + +Basic initialization: +^^^^^^^^^^^^^^^^^^^^^ + +G2D draw initialization is done automatically in :cpp:func:`lv_init()` once the +G2D is enabled as a draw unit , no user code is required: + +.. code:: c + + #if LV_USE_DRAW_G2D + lv_draw_g2d_init(); + #endif + +During G2D initialization, a new draw unit `lv_draw_g2d_unit_t` will be created +with the additional callbacks, if :c:macro:`LV_USE_DRAW_G2D` is set to `1`: + +.. code:: c + + lv_draw_g2d_unit_t * draw_g2d_unit = lv_draw_create_unit(sizeof(lv_draw_g2d_unit_t)); + draw_g2d_unit->base_unit.evaluate_cb = _g2d_evaluate; + draw_g2d_unit->base_unit.dispatch_cb = _g2d_dispatch; + draw_g2d_unit->base_unit.delete_cb = _g2d_delete; + +and an addition thread `_g2d_render_thread_cb()` will be spawned in order to +handle the supported draw tasks. + +.. code:: c + + #if LV_USE_G2D_DRAW_THREAD + lv_thread_init(&draw_g2d_unit->thread, LV_THREAD_PRIO_HIGH, _g2d_render_thread_cb, 2 * 1024, draw_g2d_unit); + #endif + +If `LV_USE_G2D_DRAW_THREAD` is not defined, then no additional draw thread will be created +and the G2D drawing task will get executed on the same LVGL main thread. + +`_g2d_evaluate()` will get called after each task is being created and will +analyze if the task is supported by G2D or not. If it is supported, then an +preferred score and the draw unit id will be set to the task. An `score` equal +to `100` is the default CPU score. Smaller score means that G2D is capable of +drawing it faster. + +`_g2d_dispatch()` is the G2D dispatcher callback, it will take a ready to draw +task (having the `DRAW_UNIT_ID_G2D` set) and will pass the task to the G2D draw +unit for processing. + +`_g2d_delete()` will cleanup the G2D draw unit. + +Features supported: +^^^^^^^^^^^^^^^^^^^ + +Several drawing features in LVGL can be offloaded to the G2D engine. The CPU is +available for other operations while the G2D is running. Linux OS is required to +block the LVGL drawing thread and switch to another task or suspend the CPU for +power savings. + +Supported draw tasks are available in "src/draw/nx/g2d/lv_draw_g2d.c": + +.. code:: c + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: + lv_draw_g2d_fill(u, t->draw_dsc, &t->area); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_draw_g2d_img(u, t->draw_dsc, &t->area); + break; + default: + break; + } + +- Fill area with color (w/o radius, w/o gradient) + optional opacity. +- Blit source image ARGB8888 over destination. + ARGB8888 + optional opacity. +- Scale source image ARGB8888. + +Known limitations: +^^^^^^^^^^^^^^^^^^ + +- G2D/PXP can only rotate at 90x angles. +- Rotation is not supported for images unaligned to blocks of 16x16 pixels. G2D/PXP + is set to process 16x16 blocks to optimize the system for memory bandwidth and + image processing time. The output engine essentially truncates any output + pixels after the desired number of pixels has been written. When rotating a + source image and the output is not divisible by the block size, the incorrect + pixels could be truncated and the final output image can look shifted. +- Recolor or transformation for images w/ opacity or alpha channel can't be + obtained in a single G2D/PXP pipeline configuration. Two or multiple steps would + be required. +- Buffer address must be aligned to 64 bytes: set :c:macro:`LV_DRAW_BUF_ALIGN` + to `64` in "lv_conf.h". + No stride alignment is required: set :c:macro:`LV_DRAW_BUF_STRIDE_ALIGN` to + `1` in "lv_conf.h". + +Project setup: +^^^^^^^^^^^^^^ + +- Add G2D related source files (and corresponding headers if available) to + project: + + - "src/draw/nxp/g2d/lv_draw_buf_g2d.c": draw buffer callbacks + - "src/draw/nxp/g2d/lv_draw_g2d_fill.c": fill area + - "src/draw/nxp/g2d/lv_draw_g2d_img.c": blit image (w/ optional recolor or + transformation) + - "src/draw/nxp/g2d/lv_draw_g2d.c": draw unit initialization + - "src/draw/nxp/g2d/lv_draw_g2d_buf_map.c": hash map for g2d buffers + - "src/draw/nxp/g2d/lv_g2d_utils.c": function helpers + diff --git a/docs/details/integration/renderers/index.rst b/docs/details/integration/renderers/index.rst index 94301dd87..53788cd68 100644 --- a/docs/details/integration/renderers/index.rst +++ b/docs/details/integration/renderers/index.rst @@ -10,6 +10,7 @@ Renderers and GPUs nema_gfx nxp_pxp nxp_vglite_gpu + nxp_g2d sdl stm32_dma2d vg_lite diff --git a/docs/details/integration/renderers/nxp_g2d.rst b/docs/details/integration/renderers/nxp_g2d.rst new file mode 100644 index 000000000..4a2d7454c --- /dev/null +++ b/docs/details/integration/renderers/nxp_g2d.rst @@ -0,0 +1,8 @@ +=========== +NXP G2D GPU +=========== + +API +--- + +:ref:`lv_draw_g2d_h` diff --git a/docs/details/integration/renderers/nxp_pxp.rst b/docs/details/integration/renderers/nxp_pxp.rst index 02b0a0c64..c95fd4e76 100644 --- a/docs/details/integration/renderers/nxp_pxp.rst +++ b/docs/details/integration/renderers/nxp_pxp.rst @@ -8,8 +8,3 @@ API :ref:`lv_draw_pxp_h` :ref:`lv_pxp_cfg_h` - -:ref:`lv_pxp_osa_h` - -:ref:`lv_pxp_utils_h` - diff --git a/docs/details/integration/renderers/nxp_vglite_gpu.rst b/docs/details/integration/renderers/nxp_vglite_gpu.rst index a99ca7bf7..9e3567e46 100644 --- a/docs/details/integration/renderers/nxp_vglite_gpu.rst +++ b/docs/details/integration/renderers/nxp_vglite_gpu.rst @@ -6,11 +6,3 @@ API --- :ref:`lv_draw_vglite_h` - -:ref:`lv_vglite_buf_h` - -:ref:`lv_vglite_matrix_h` - -:ref:`lv_vglite_path_h` - -:ref:`lv_vglite_utils_h` diff --git a/env_support/cmsis-pack/README.md b/env_support/cmsis-pack/README.md index 72d065605..6b98762be 100644 --- a/env_support/cmsis-pack/README.md +++ b/env_support/cmsis-pack/README.md @@ -59,6 +59,7 @@ remove the misleading guide above this code segment. - LV_USE_DRAW_VGLITE - LV_USE_DRAW_VG_LITE - LV_USE_PXP + - LV_USE_DRAW_G2D - LV_USE_DRAW_SDL - LV_USE_DRAW_ARM2D_SYNC - LV_USE_DRAW_ARM2D_ASYNC diff --git a/lv_conf_template.h b/lv_conf_template.h index 441595a1c..c090e9305 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -278,6 +278,23 @@ #define LV_USE_PXP_ASSERT 0 #endif +/** Use NXP's G2D on MPU platforms. */ +#define LV_USE_DRAW_G2D 0 + +#if LV_USE_DRAW_G2D + /** Maximum number of buffers that can be stored for G2D draw unit. + * Includes the frame buffers and assets. */ + #define LV_G2D_HASH_TABLE_SIZE 50 + + #if LV_USE_OS + /** Use additional draw thread for G2D processing.*/ + #define LV_USE_G2D_DRAW_THREAD 1 + #endif + + /** Enable G2D asserts. */ + #define LV_USE_G2D_ASSERT 0 +#endif + /** Use Renesas Dave2D on RA platforms. */ #define LV_USE_DRAW_DAVE2D 0 diff --git a/src/draw/nxp/g2d/lv_draw_buf_g2d.c b/src/draw/nxp/g2d/lv_draw_buf_g2d.c new file mode 100644 index 000000000..99e6dd190 --- /dev/null +++ b/src/draw/nxp/g2d/lv_draw_buf_g2d.c @@ -0,0 +1,93 @@ +/** + * @file lv_draw_buf_g2d.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_g2d.h" + +#if LV_USE_DRAW_G2D +#include "../../lv_draw_buf_private.h" +#include "g2d.h" +#include "lv_g2d_buf_map.h" +#include "lv_g2d_utils.h" + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format); + +static void _buf_free(void * buf); + +static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_buf_g2d_init_handlers(void) +{ + lv_draw_buf_handlers_t * handlers = lv_draw_buf_get_handlers(); + + handlers->buf_malloc_cb = _buf_malloc; + handlers->buf_free_cb = _buf_free; + handlers->invalidate_cache_cb = _invalidate_cache; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void * _buf_malloc(size_t size_bytes, lv_color_format_t color_format) +{ + LV_UNUSED(color_format); + + size_bytes += LV_DRAW_BUF_ALIGN - 1; + struct g2d_buf * buf = g2d_alloc(size_bytes, 1); + G2D_ASSERT_MSG(buf, "Failed to alloc buffer."); + g2d_insert_buf_map(buf->buf_vaddr, buf); + return buf->buf_vaddr; +} + +static void _buf_free(void * buf) +{ + g2d_free_item(buf); +} + +static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area) +{ + + LV_UNUSED(area); + struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data); + G2D_ASSERT_MSG(buf, "Failed to find buffer in map."); + g2d_cache_op(buf, G2D_CACHE_FLUSH); +} + +#endif /*LV_USE_DRAW_G2D*/ diff --git a/src/draw/nxp/g2d/lv_draw_g2d.c b/src/draw/nxp/g2d/lv_draw_g2d.c new file mode 100644 index 000000000..9fa489fdb --- /dev/null +++ b/src/draw/nxp/g2d/lv_draw_g2d.c @@ -0,0 +1,317 @@ +/** + * @file lv_draw_g2d.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +#include "lv_draw_g2d.h" + +#if LV_USE_DRAW_G2D +#include "../../../misc/lv_area_private.h" +#include "g2d.h" +#include "lv_g2d_buf_map.h" + +/********************* + * DEFINES + *********************/ + +#define DRAW_UNIT_ID_G2D 8 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Evaluate a task and set the score and preferred G2D unit. + * Return 1 if task is preferred, 0 otherwise (task is not supported). + */ +static int32_t _g2d_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task); + +/** + * Dispatch a task to the G2D unit. + * Return 1 if task was dispatched, 0 otherwise (task not supported). + */ +static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer); + +/** + * Delete the G2D draw unit. + */ +static int32_t _g2d_delete(lv_draw_unit_t * draw_unit); + +#if LV_USE_G2D_DRAW_THREAD + static void _g2d_render_thread_cb(void * ptr); +#endif + +static void _g2d_execute_drawing(lv_draw_g2d_unit_t * u); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_g2d_init(void) +{ + lv_draw_buf_g2d_init_handlers(); + + lv_draw_g2d_unit_t * draw_g2d_unit = lv_draw_create_unit(sizeof(lv_draw_g2d_unit_t)); + draw_g2d_unit->base_unit.evaluate_cb = _g2d_evaluate; + draw_g2d_unit->base_unit.dispatch_cb = _g2d_dispatch; + draw_g2d_unit->base_unit.delete_cb = _g2d_delete; + g2d_create_buf_map(); + if(g2d_open(&draw_g2d_unit->g2d_handle)) { + LV_LOG_ERROR("g2d_open fail.\n"); + } +#if LV_USE_G2D_DRAW_THREAD + lv_thread_init(&draw_g2d_unit->thread, "g2ddraw", LV_THREAD_PRIO_HIGH, _g2d_render_thread_cb, 2 * 1024, draw_g2d_unit); +#endif +} + +void lv_draw_g2d_deinit(void) +{ + g2d_free_buf_map(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static inline bool _g2d_dest_cf_supported(lv_color_format_t cf) +{ + bool is_cf_supported = false; + + switch(cf) { + case LV_COLOR_FORMAT_ARGB8888: + is_cf_supported = true; + break; + default: + break; + } + + return is_cf_supported; +} + +static inline bool _g2d_src_cf_supported(lv_color_format_t cf) +{ + bool is_cf_supported = false; + + switch(cf) { + case LV_COLOR_FORMAT_ARGB8888: + is_cf_supported = true; + break; + default: + break; + } + + return is_cf_supported; +} + +static bool _g2d_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc) +{ + const lv_image_dsc_t * img_dsc = draw_dsc->src; + + bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN); + bool has_rotation = (draw_dsc->rotation != 0); + + /* Recolor or rotation are not supported. */ + if(has_recolor || has_rotation) + return false; + + return true; +} + +static int32_t _g2d_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t) +{ + LV_UNUSED(u); + + const lv_draw_dsc_base_t * draw_dsc_base = (lv_draw_dsc_base_t *) t->draw_dsc; + lv_draw_buf_t * draw_buf = draw_dsc_base->layer->draw_buf; + + if(!_g2d_dest_cf_supported(draw_dsc_base->layer->color_format)) + return 0; + + if(draw_buf && !g2d_search_buf_map(draw_buf->data)) + return 0; + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: { + const lv_draw_fill_dsc_t * draw_dsc = (lv_draw_fill_dsc_t *) t->draw_dsc; + + /* Most simple case: just a plain rectangle (no radius, no gradient). */ + if((draw_dsc->radius != 0) || (draw_dsc->grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_G2D; + } + return 1; + } + case LV_DRAW_TASK_TYPE_IMAGE: { + const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; + + if(!_g2d_src_cf_supported(draw_dsc->header.cf)) + return 0; + + if(!_g2d_draw_img_supported(draw_dsc)) + return 0; + + if(t->preference_score > 70) { + t->preference_score = 70; + t->preferred_draw_unit_id = DRAW_UNIT_ID_G2D; + } + return 1; + } + default: + return 0; + } + + return 0; + +} + +static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) +{ + lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit; + + /* Return immediately if it's busy with draw task. */ + if(draw_g2d_unit->task_act) + return 0; + + /* Try to get an ready to draw. */ + lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_G2D); + + if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_G2D) + return LV_DRAW_UNIT_IDLE; + + if(lv_draw_layer_alloc_buf(layer) == NULL) + return LV_DRAW_UNIT_IDLE; + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_g2d_unit->task_act = t; + +#if LV_USE_G2D_DRAW_THREAD + /* Let the render thread work. */ + if(draw_g2d_unit->inited) + lv_thread_sync_signal(&draw_g2d_unit->sync); +#else + _g2d_execute_drawing(draw_g2d_unit); + + draw_g2d_unit->task_act->state = LV_DRAW_TASK_STATE_READY; + draw_g2d_unit->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); +#endif + + return 1; +} + +static int32_t _g2d_delete(lv_draw_unit_t * draw_unit) +{ + lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit; + lv_result_t res = LV_RESULT_OK; + +#if LV_USE_G2D_DRAW_THREAD + LV_LOG_INFO("Cancel G2D draw thread."); + draw_g2d_unit->exit_status = true; + + if(draw_g2d_unit->inited) + lv_thread_sync_signal(&draw_g2d_unit->sync); + + res = lv_thread_delete(&draw_g2d_unit->thread); +#endif + g2d_close(draw_g2d_unit->g2d_handle); + + return res; +} + +static void _g2d_execute_drawing(lv_draw_g2d_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + lv_layer_t * layer = t->target_layer; + lv_draw_buf_t * draw_buf = layer->draw_buf; + + t->draw_unit = (lv_draw_unit_t *)u; + + lv_area_t draw_area; + if(!lv_area_intersect(&draw_area, &t->area, &t->clip_area)) + return; /*Fully clipped, nothing to do*/ + + /* Make area relative to the buffer */ + lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1); + + /* Invalidate only the drawing area */ + lv_draw_buf_invalidate_cache(draw_buf, &draw_area); + + switch(t->type) { + case LV_DRAW_TASK_TYPE_FILL: + lv_draw_g2d_fill(t); + break; + case LV_DRAW_TASK_TYPE_IMAGE: + lv_draw_g2d_img(t); + break; + default: + break; + } + + /* Invalidate only the drawing area */ + lv_draw_buf_invalidate_cache(draw_buf, &draw_area); +} + +#if LV_USE_G2D_DRAW_THREAD +static void _g2d_render_thread_cb(void * ptr) +{ + lv_draw_g2d_unit_t * u = ptr; + + lv_thread_sync_init(&u->sync); + u->inited = true; + + while(1) { + /* Wait for sync if there is no task set. */ + while(u->task_act == NULL) { + if(u->exit_status) + break; + + lv_thread_sync_wait(&u->sync); + } + + if(u->exit_status) { + LV_LOG_INFO("Ready to exit G2D draw thread."); + break; + } + + _g2d_execute_drawing(u); + + /* Signal the ready state to dispatcher. */ + u->task_act->state = LV_DRAW_TASK_STATE_READY; + + /* Cleanup. */ + u->task_act = NULL; + + /* The draw unit is free now. Request a new dispatching as it can get a new task. */ + lv_draw_dispatch_request(); + } + + u->inited = false; + lv_thread_sync_delete(&u->sync); + LV_LOG_INFO("Exit G2D draw thread."); +} +#endif + +#endif /*LV_USE_DRAW_G2D*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_draw_g2d.h b/src/draw/nxp/g2d/lv_draw_g2d.h new file mode 100644 index 000000000..151a16d03 --- /dev/null +++ b/src/draw/nxp/g2d/lv_draw_g2d.h @@ -0,0 +1,66 @@ +/** + * @file lv_draw_g2d.h + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LV_DRAW_G2D_H +#define LV_DRAW_G2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_G2D +#include "../../sw/lv_draw_sw_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct lv_draw_g2d_unit { + lv_draw_sw_unit_t; + + void * g2d_handle; +} lv_draw_g2d_unit_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_g2d_init(void); + +void lv_draw_g2d_deinit(void); + +void lv_draw_buf_g2d_init_handlers(void); + +void lv_draw_g2d_fill(lv_draw_task_t * t); + +void lv_draw_g2d_img(lv_draw_task_t * t); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_G2D*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_G2D_H*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_draw_g2d_fill.c b/src/draw/nxp/g2d/lv_draw_g2d_fill.c new file mode 100644 index 000000000..a4e647c8e --- /dev/null +++ b/src/draw/nxp/g2d/lv_draw_g2d_fill.c @@ -0,0 +1,189 @@ +/** + * @file lv_draw_g2d_fill.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_g2d.h" + +#if LV_USE_DRAW_G2D +#include +#include "g2d.h" +#include "../../../misc/lv_area_private.h" +#include "lv_g2d_buf_map.h" +#include "lv_g2d_utils.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/* Blit simple w/ opa and alpha channel */ +static void _g2d_fill(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf); +static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, + struct g2d_buf * src_buf, struct g2d_surface * src_surf); + +static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_t color, lv_opa_t opa); + +static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_t color); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_g2d_fill(lv_draw_task_t * t) +{ + lv_draw_fill_dsc_t * dsc = t->draw_dsc; + + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit; + lv_layer_t * layer = t->target_layer; + lv_draw_buf_t * draw_buf = layer->draw_buf; + lv_area_t * coords = &t->area; + + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1); + + lv_area_t rel_clip_area; + lv_area_copy(&rel_clip_area, &t->clip_area); + lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1); + + lv_area_t blend_area; + if(!lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area)) + return; /*Fully clipped, nothing to do*/ + + /* G2D takes stride in pixels. */ + int32_t stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8); + + /* Destination buffer */ + struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data); + + bool has_opa = (dsc->opa < (lv_opa_t)LV_OPA_MAX); + struct g2d_surface * dst_surf = lv_malloc(sizeof(struct g2d_surface)); + G2D_ASSERT_MSG(dst_surf, "Failed to alloc destination surface."); + _g2d_set_dst_surf(dst_surf, dst_buf, &blend_area, stride, dsc->color); + + if(has_opa) { + struct g2d_buf * tmp_buf = g2d_alloc(lv_area_get_width(&blend_area) * lv_area_get_height(&blend_area) * sizeof( + lv_color32_t), 1); + G2D_ASSERT_MSG(tmp_buf, "Failed to alloc temporary buffer."); + struct g2d_surface * src_surf = lv_malloc(sizeof(struct g2d_surface)); + G2D_ASSERT_MSG(src_surf, "Failed to alloc source surface."); + _g2d_set_src_surf(src_surf, tmp_buf, &blend_area, stride, dsc->color, dsc->opa); + _g2d_fill_with_opa(u->g2d_handle, dst_buf, dst_surf, tmp_buf, src_surf); + g2d_free(tmp_buf); + lv_free(src_surf); + } + else { + _g2d_fill(u->g2d_handle, dst_buf, dst_surf); + } + + lv_free(dst_surf); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_t color, lv_opa_t opa) +{ + int32_t width = lv_area_get_width(area); + int32_t height = lv_area_get_height(area); + + src_surf->format = G2D_RGBA8888; + + src_surf->left = 0; + src_surf->top = 0; + src_surf->right = width; + src_surf->bottom = height; + src_surf->stride = stride; + src_surf->width = width; + src_surf->height = height; + + src_surf->planes[0] = buf->buf_paddr; + src_surf->rot = G2D_ROTATION_0; + + src_surf->clrcolor = g2d_rgba_to_u32(color); + src_surf->global_alpha = opa; + src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA; +} + +static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_t color) +{ + int32_t width = lv_area_get_width(area); + int32_t height = lv_area_get_height(area); + + dst_surf->format = G2D_RGBA8888; + + dst_surf->left = area->x1; + dst_surf->top = area->y1; + dst_surf->right = area->x1 + width; + dst_surf->bottom = area->y1 + height; + dst_surf->stride = stride; + dst_surf->width = width; + dst_surf->height = height; + + dst_surf->planes[0] = buf->buf_paddr; + dst_surf->rot = G2D_ROTATION_0; + + dst_surf->clrcolor = g2d_rgba_to_u32(color); + dst_surf->global_alpha = 0xff; + dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA; +} + +static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, + struct g2d_buf * src_buf, struct g2d_surface * src_surf) +{ + g2d_cache_op(src_buf, G2D_CACHE_FLUSH); + + g2d_clear(g2d_handle, src_surf); + g2d_flush(g2d_handle); + + g2d_enable(g2d_handle, G2D_BLEND); + g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA); + g2d_blit(g2d_handle, src_surf, dst_surf); + g2d_finish(g2d_handle); + g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA); + g2d_disable(g2d_handle, G2D_BLEND); +} + +static void _g2d_fill(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf) +{ + g2d_clear(g2d_handle, dst_surf); + g2d_flush(g2d_handle); + + g2d_finish(g2d_handle); +} + +#endif /*LV_USE_DRAW_G2D*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_draw_g2d_img.c b/src/draw/nxp/g2d/lv_draw_g2d_img.c new file mode 100644 index 000000000..b1d0df459 --- /dev/null +++ b/src/draw/nxp/g2d/lv_draw_g2d_img.c @@ -0,0 +1,222 @@ +/** + * @file lv_draw_g2d_img.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_g2d.h" + +#if LV_USE_DRAW_G2D +#include +#include +#include "g2d.h" +#include "../../../misc/lv_area_private.h" +#include "lv_g2d_utils.h" +#include "lv_g2d_buf_map.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * data); + +static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_format_t cf, lv_opa_t opa); + +static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc); + +/* Blit simple w/ opa and alpha channel */ +static void _g2d_blit(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, + struct g2d_buf * src_buf, struct g2d_surface * src_surf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_g2d_img(lv_draw_task_t * t) +{ + lv_draw_image_dsc_t * dsc = t->draw_dsc; + + if(dsc->opa <= (lv_opa_t)LV_OPA_MIN) + return; + + lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)t->draw_unit; + lv_layer_t * layer = t->target_layer; + lv_draw_buf_t * draw_buf = layer->draw_buf; + const lv_image_dsc_t * img_dsc = dsc->src; + lv_area_t * coords = &t->area; + + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1); + + lv_area_t clip_area; + lv_area_copy(&clip_area, &t->clip_area); + lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1); + + lv_area_t blend_area; + bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE); + if(has_transform) + lv_area_copy(&blend_area, &rel_coords); + else if(!lv_area_intersect(&blend_area, &rel_coords, &clip_area)) + return; /*Fully clipped, nothing to do*/ + + lv_area_t src_area; + src_area.x1 = blend_area.x1 - (coords->x1 - layer->buf_area.x1); + src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1); + src_area.x2 = src_area.x1 + lv_area_get_width(&blend_area); + src_area.y2 = src_area.y1 + lv_area_get_height(&blend_area); + int32_t src_stride = img_dsc->header.stride / (lv_color_format_get_bpp(img_dsc->header.cf) / 8); + lv_color_format_t src_cf = img_dsc->header.cf; + + /* Source image */ + struct g2d_buf * src_buf = _g2d_handle_src_buf(img_dsc); + + /* Destination buffer */ + struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data); + + /* G2D takes stride in pixels. */ + int32_t dest_stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8); + lv_color_format_t dest_cf = draw_buf->header.cf; + + struct g2d_surface * src_surf = lv_malloc(sizeof(struct g2d_surface)); + G2D_ASSERT_MSG(src_surf, "Failed to alloc source surface."); + struct g2d_surface * dst_surf = lv_malloc(sizeof(struct g2d_surface)); + G2D_ASSERT_MSG(dst_surf, "Failed to alloc destination surface."); + + _g2d_set_src_surf(src_surf, src_buf, &src_area, src_stride, src_cf, dsc->opa); + _g2d_set_dst_surf(dst_surf, dst_buf, &blend_area, dest_stride, dest_cf, dsc); + + _g2d_blit(u->g2d_handle, dst_buf, dst_surf, src_buf, src_surf); + + lv_free(src_surf); + lv_free(dst_surf); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * img_dsc) +{ + struct g2d_buf * src_buf = g2d_search_buf_map((void *)img_dsc->data); + + if(src_buf == NULL) { + src_buf = g2d_alloc(img_dsc->data_size, 1); + G2D_ASSERT_MSG(src_buf, "Failed to alloc source buffer."); + memcpy((int *)src_buf->buf_vaddr, img_dsc->data, img_dsc->data_size); + g2d_insert_buf_map((void *)img_dsc->data, src_buf); + } + + return src_buf; +} + +static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_format_t cf, lv_opa_t opa) +{ + src_surf->format = g2d_get_buf_format(cf); + + src_surf->left = area->x1; + src_surf->top = area->y1; + src_surf->right = area->x2; + src_surf->bottom = area->y2; + src_surf->stride = stride; + src_surf->width = area->x2 - area->x1; + src_surf->height = area->y2 - area->y1; + + src_surf->planes[0] = buf->buf_paddr; + src_surf->rot = G2D_ROTATION_0; + + src_surf->clrcolor = g2d_rgba_to_u32(lv_color_black()); + src_surf->global_alpha = opa; + src_surf->blendfunc = G2D_ONE | G2D_PRE_MULTIPLIED_ALPHA; +} + +static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area, + int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc) +{ + int32_t width = lv_area_get_width(area); + int32_t height = lv_area_get_height(area); + + lv_point_t pivot = dsc->pivot; + /*The offsets are now relative to the transformation result with pivot ULC*/ + int32_t piv_offset_x = 0; + int32_t piv_offset_y = 0; + int32_t trim_x = 0; + int32_t trim_y = 0; + int32_t dest_w; + int32_t dest_h; + + float fp_scale_x = (float)dsc->scale_x / LV_SCALE_NONE; + float fp_scale_y = (float)dsc->scale_y / LV_SCALE_NONE; + int32_t int_scale_x = (int32_t)fp_scale_x; + int32_t int_scale_y = (int32_t)fp_scale_y; + + /*Any scale_factor in (k, k + 1] will result in a trim equal to k*/ + trim_x = (fp_scale_x == int_scale_x) ? int_scale_x - 1 : int_scale_x; + trim_y = (fp_scale_y == int_scale_y) ? int_scale_y - 1 : int_scale_y; + + dest_w = width * fp_scale_x + trim_x; + dest_h = height * fp_scale_y + trim_y; + + /*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/ + piv_offset_x = floor(fp_scale_x * piv_offset_x) - floor((fp_scale_x - 1) * pivot.x); + piv_offset_y = floor(fp_scale_y * piv_offset_y) - floor((fp_scale_y - 1) * pivot.y); + + dst_surf->format = g2d_get_buf_format(cf); + + dst_surf->left = area->x1 + piv_offset_x; + dst_surf->top = area->y1 + piv_offset_y; + dst_surf->right = area->x1 + piv_offset_x + dest_w - trim_x; + dst_surf->bottom = area->y1 + piv_offset_y + dest_h - trim_y; + dst_surf->stride = stride; + dst_surf->width = dest_w - trim_x; + dst_surf->height = dest_h - trim_y; + + dst_surf->planes[0] = buf->buf_paddr; + dst_surf->rot = G2D_ROTATION_0; + + dst_surf->clrcolor = g2d_rgba_to_u32(lv_color_black()); + dst_surf->global_alpha = 0xff; + dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA; +} + +static void _g2d_blit(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, + struct g2d_buf * src_buf, struct g2d_surface * src_surf) +{ + g2d_cache_op(src_buf, G2D_CACHE_FLUSH); + + g2d_enable(g2d_handle, G2D_BLEND); + g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA); + g2d_blit(g2d_handle, src_surf, dst_surf); + g2d_finish(g2d_handle); + g2d_disable(g2d_handle, G2D_GLOBAL_ALPHA); + g2d_disable(g2d_handle, G2D_BLEND); +} +#endif /*LV_USE_DRAW_G2D*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_g2d_buf_map.c b/src/draw/nxp/g2d/lv_g2d_buf_map.c new file mode 100644 index 000000000..1537d7408 --- /dev/null +++ b/src/draw/nxp/g2d/lv_g2d_buf_map.c @@ -0,0 +1,254 @@ +/** + * @file lv_g2d_buf_map.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +#include "lv_g2d_buf_map.h" + +#if LV_USE_DRAW_G2D +#include +#include "lv_g2d_utils.h" +#include "g2d.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static unsigned long _map_hash_function(void * ptr); + +static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value); + +static void _map_free_item(lv_map_item_t * item); + +static void _handle_collision(unsigned long index, lv_map_item_t * item); + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_buf_map_t * table; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void g2d_create_buf_map(void) +{ + table = (lv_buf_map_t *) lv_malloc(sizeof(lv_buf_map_t)); + table->size = LV_G2D_HASH_TABLE_SIZE; + table->count = 0; + table->items = (lv_map_item_t **) lv_malloc_zeroed(table->size * sizeof(lv_map_item_t *)); + table->overflow_list = (lv_array_t **) lv_malloc_zeroed(table->size * sizeof(lv_array_t *)); + + for(int i = 0; i < table->size; i++) { + table->items[i] = NULL; + table->overflow_list[i] = NULL; + } +} + +void g2d_free_buf_map(void) +{ + for(int i = 0; i < table->size; i++) { + if(table->overflow_list[i]) { + lv_array_deinit(table->overflow_list[i]); + lv_free(table->overflow_list[i]); + } + + lv_map_item_t * item = table->items[i]; + if(item != NULL) + _map_free_item(item); + } + + lv_free(table->items); + lv_free(table->overflow_list); + lv_free(table); +} + +void g2d_insert_buf_map(void * key, struct g2d_buf * value) +{ + lv_map_item_t * item = _map_create_item(key, value); + int index = _map_hash_function(key); + lv_map_item_t * curr_item = table->items[index]; + + if(table->items[index] == NULL) { + /* Key not found. */ + if(table->count == table->size) { + /* Table is full. */ + _map_free_item(item); + G2D_ASSERT_MSG(false, "Hash table is full."); + return; + } + } + else { + if(table->items[index]->key == key) { + /* Key already exists, update value. */ + table->items[index]->value = value; + return; + } + else { + /* Handle the collision */ + _handle_collision(index, item); + return; + } + } + + /* Insert item. */ + table->items[index] = item; + table->count++; +} + +struct g2d_buf * g2d_search_buf_map(void * key) +{ + int index = _map_hash_function(key); + lv_map_item_t * item = table->items[index]; + lv_array_t * list = (lv_array_t *)table->overflow_list[index]; + + if(item == NULL) + return NULL; + + if(item->key == key) + return item->value; + + if(list == NULL) + return NULL; + + for(uint32_t i = 0; i < lv_array_size(list); i++) { + item = (lv_map_item_t *)lv_array_at(list, i); + if(item->key == key) + return item->value; + } + + return NULL; +} + +void g2d_free_item(void * key) +{ + /* Delete an item from the table. */ + int index = _map_hash_function(key); + lv_map_item_t * item = table->items[index]; + lv_array_t * list = (lv_array_t *)table->overflow_list[index]; + + if(item == NULL) { + return; + } + else if(list == NULL && item->key == key) { + /* No collision chain, just remove item. */ + table->items[index] = NULL; + _map_free_item(item); + table->count--; + return; + } + else if(list != NULL) { + /* Collision chain exists. */ + for(uint32_t i = 0; i < lv_array_size(list); i++) { + item = (lv_map_item_t *)lv_array_at(list, i); + if(item->key == key) { + lv_array_remove(list, i); + return; + } + } + } +} + +void g2d_print_table(void) +{ + LV_LOG("\nHash Table\n-------------------\n"); + + for(int i = 0; i < table->size; i++) { + if(table->items[i]) { + LV_LOG("Index:%d, Key:%p, Value:%p\n", i, table->items[i] -> key, table->items[i]->value); + if(table->overflow_list[i]) { + for(int j = 0 ; j < lv_array_size(table->overflow_list[i]); j++) { + lv_map_item_t * item = (lv_map_item_t *)lv_array_at(table->overflow_list[i], j); + LV_LOG("Index:%d, Key:%p, Value:%p\n", i, item -> key, item->value); + } + + } + } + } + + LV_LOG("-------------------\n\n"); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static unsigned long _map_hash_function(void * ptr) +{ + unsigned long i = 0; + char str[64]; + G2D_ASSERT_MSG(ptr, "Key is null."); + sprintf(str, "%p", ptr); + + for(int j = 0; str[j]; j++) + i += str[j]; + + return i % LV_G2D_HASH_TABLE_SIZE; +} + +static void _handle_collision(unsigned long index, lv_map_item_t * item) +{ + if(table->overflow_list[index] == NULL) { + /* Create the list. */ + lv_array_t * list = (lv_array_t *) lv_malloc(sizeof(lv_array_t));; + lv_array_init(list, LV_ARRAY_DEFAULT_CAPACITY, sizeof(lv_map_item_t)); + lv_array_push_back(list, item); + table->overflow_list[index] = list; + return; + } + else { + lv_array_t * list = (lv_array_t *)table->overflow_list[index]; + for(int i = 0; i < lv_array_size(list); i++) { + lv_map_item_t * it = (lv_map_item_t *)lv_array_at(list, i); + if(it->key == item->key) { + /* Key exists, update value. */ + it->value = item->value; + return; + } + } + /* Insert to the list. */ + lv_array_push_back(table->overflow_list[index], item); + return; + } +} + +static lv_map_item_t * _map_create_item(void * key, struct g2d_buf * value) +{ + lv_map_item_t * item = (lv_map_item_t *) lv_malloc(sizeof(lv_map_item_t)); + G2D_ASSERT_MSG(item, "Failed to alloc item."); + item->key = key; + item->value = value; + return item; +} + +static void _map_free_item(lv_map_item_t * item) +{ + /* Also free the g2d_buf. */ + lv_free(item->key); + g2d_free(item->value); + item->key = NULL; + item->value = NULL; + lv_free(item); + item = NULL; +} + +#endif /*LV_USE_DRAW_G2D*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_g2d_buf_map.h b/src/draw/nxp/g2d/lv_g2d_buf_map.h new file mode 100644 index 000000000..6e2196d30 --- /dev/null +++ b/src/draw/nxp/g2d/lv_g2d_buf_map.h @@ -0,0 +1,81 @@ + +/** + * @file lv_g2d_buf_map.h + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LV_G2D_BUF_MAP_H +#define LV_G2D_BUF_MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_G2D + +#include "../../../misc/lv_array.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* Map item definition. */ +typedef struct lv_map_item { + /* Virtual address buffer. */ + void * key; + struct g2d_buf * value; +} lv_map_item_t; + +/*Buf map definition. */ +typedef struct lv_buf_map { + lv_map_item_t ** items; + lv_array_t ** overflow_list; + + int size; + int count; +} lv_buf_map_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void g2d_create_buf_map(void); + +void g2d_free_buf_map(void); + +void g2d_insert_buf_map(void * key, struct g2d_buf * value); + +struct g2d_buf * g2d_search_buf_map(void * key); + +void g2d_free_item(void * key); + +void g2d_print_table(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_G2D*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /* LV_G2D_BUF_MAP_H */ diff --git a/src/draw/nxp/g2d/lv_g2d_utils.c b/src/draw/nxp/g2d/lv_g2d_utils.c new file mode 100644 index 000000000..006a80958 --- /dev/null +++ b/src/draw/nxp/g2d/lv_g2d_utils.c @@ -0,0 +1,90 @@ +/** + * @file lv_g2d_utils.c + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_g2d_utils.h" + +#if LV_USE_DRAW_G2D +#include"g2d.h" +#include "lv_draw_g2d.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** +* MACROS +**********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +enum g2d_format g2d_get_buf_format(lv_color_format_t cf) +{ + enum g2d_format color_f = G2D_RGB565; + + switch(cf) { + case LV_COLOR_FORMAT_RGB565: + color_f = G2D_RGB565; + break; + case LV_COLOR_FORMAT_ARGB8888: + color_f = G2D_BGRA8888; + break; + case LV_COLOR_FORMAT_XRGB8888: + color_f = G2D_BGRX8888; + break; + case LV_COLOR_FORMAT_RGB888: + color_f = G2D_BGR888; + break; + case LV_COLOR_FORMAT_NV12: + color_f = G2D_NV12; + break; + case LV_COLOR_FORMAT_I420: + color_f = G2D_I420; + break; + case LV_COLOR_FORMAT_NV21: + color_f = G2D_NV21; + break; + case LV_COLOR_FORMAT_YUY2: + color_f = G2D_YUYV; + break; + case LV_COLOR_FORMAT_UYVY: + color_f = G2D_UYVY; + break; + default: + G2D_ASSERT_MSG(false, "Unsupported color format."); + break; + } + return color_f; +} + +uint32_t g2d_rgba_to_u32(lv_color_t color) +{ + return (uint32_t)((color.red) + (color.green << 8) + (color.blue << 16) + ((uint32_t)0xff << 24)); +} + +/********************** +* STATIC FUNCTIONS +**********************/ + +#endif /*LV_USE_DRAW_G2D*/ \ No newline at end of file diff --git a/src/draw/nxp/g2d/lv_g2d_utils.h b/src/draw/nxp/g2d/lv_g2d_utils.h new file mode 100644 index 000000000..f4bb172df --- /dev/null +++ b/src/draw/nxp/g2d/lv_g2d_utils.h @@ -0,0 +1,67 @@ +/** + * @file lv_g2d_utils.h + * + */ + +/** + * Copyright 2024 NXP + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LV_G2D_UTILS_H +#define LV_G2D_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_DRAW_G2D +#include "../../sw/lv_draw_sw_private.h" + +/********************* + * DEFINES + *********************/ + +#if LV_USE_G2D_ASSERT +#define G2D_ASSERT(expr) LV_ASSERT(expr) +#else +#define G2D_ASSERT(expr) +#endif + +#define G2D_ASSERT_MSG(expr, msg) \ + do { \ + if(!(expr)) { \ + LV_LOG_ERROR(msg); \ + G2D_ASSERT(false); \ + } \ + } while(0) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +enum g2d_format g2d_get_buf_format(lv_color_format_t cf); + +uint32_t g2d_rgba_to_u32(lv_color_t color); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_G2D*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_G2D_UTILS_H*/ \ No newline at end of file diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index f37685adf..a49b45c70 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -779,6 +779,51 @@ #endif #endif +/** Use NXP's G2D on MPU platforms. */ +#ifndef LV_USE_DRAW_G2D + #ifdef CONFIG_LV_USE_DRAW_G2D + #define LV_USE_DRAW_G2D CONFIG_LV_USE_DRAW_G2D + #else + #define LV_USE_DRAW_G2D 0 + #endif +#endif + +#if LV_USE_DRAW_G2D + /** Maximum number of buffers that can be stored for G2D draw unit. + * Includes the frame buffers and assets. */ + #ifndef LV_G2D_HASH_TABLE_SIZE + #ifdef CONFIG_LV_G2D_HASH_TABLE_SIZE + #define LV_G2D_HASH_TABLE_SIZE CONFIG_LV_G2D_HASH_TABLE_SIZE + #else + #define LV_G2D_HASH_TABLE_SIZE 50 + #endif + #endif + + #if LV_USE_OS + /** Use additional draw thread for G2D processing.*/ + #ifndef LV_USE_G2D_DRAW_THREAD + #ifdef LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_G2D_DRAW_THREAD + #define LV_USE_G2D_DRAW_THREAD CONFIG_LV_USE_G2D_DRAW_THREAD + #else + #define LV_USE_G2D_DRAW_THREAD 0 + #endif + #else + #define LV_USE_G2D_DRAW_THREAD 1 + #endif + #endif + #endif + + /** Enable G2D asserts. */ + #ifndef LV_USE_G2D_ASSERT + #ifdef CONFIG_LV_USE_G2D_ASSERT + #define LV_USE_G2D_ASSERT CONFIG_LV_USE_G2D_ASSERT + #else + #define LV_USE_G2D_ASSERT 0 + #endif + #endif +#endif + /** Use Renesas Dave2D on RA platforms. */ #ifndef LV_USE_DRAW_DAVE2D #ifdef CONFIG_LV_USE_DRAW_DAVE2D diff --git a/src/lv_init.c b/src/lv_init.c index c0797421b..c486b016c 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -56,6 +56,9 @@ #include "draw/nxp/pxp/lv_draw_pxp.h" #endif #endif +#if LV_USE_DRAW_G2D + #include "draw/nxp/g2d/lv_draw_g2d.h" +#endif #if LV_USE_DRAW_DAVE2D #include "draw/renesas/dave2d/lv_draw_dave2d.h" #endif @@ -237,6 +240,10 @@ void lv_init(void) #endif #endif +#if LV_USE_DRAW_G2D + lv_draw_g2d_init(); +#endif + #if LV_USE_DRAW_DAVE2D lv_draw_dave2d_init(); #endif @@ -453,6 +460,10 @@ void lv_deinit(void) lv_draw_vglite_deinit(); #endif +#if LV_USE_DRAW_G2D + lv_draw_g2d_deinit(); +#endif + #if LV_USE_DRAW_VG_LITE lv_draw_vg_lite_deinit(); #endif