1 /**
2  * @file lv_gpu_nxp_vglite.c
3  *
4  */
5 
6 /**
7  * MIT License
8  *
9  * Copyright (c) 2020 NXP
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights to
14  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15  * the Software, and to permit persons to whom the Software is furnished to do so,
16  * subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice (including the next paragraph)
19  * shall be included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
26  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  *
28  */
29 
30 /*********************
31  *      INCLUDES
32  *********************/
33 
34 #include "lv_gpu_nxp_vglite.h"
35 
36 #if LV_USE_GPU_NXP_VG_LITE
37 
38 #include "lvgl.h"
39 #include "../misc/lv_log.h"
40 #include "fsl_cache.h"
41 #include "vg_lite.h"
42 #include "fsl_debug_console.h"
43 
44 /*********************
45  *      DEFINES
46  *********************/
47 
48 #if LV_COLOR_DEPTH==16
49     #define VGLITE_PX_FMT VG_LITE_RGB565
50 #else
51     #error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16.
52 #endif
53 
54 /* Enable BLIT quality degradation workaround for RT595 */
55 #define RT595_BLIT_WRKRND_ENABLED 1
56 
57 /* If LV_HOR_RES_MAX/LV_VER_RES_MAX is higher than this value, workaround will be enabled */
58 #define RT595_BLIT_WRKRND_THR 352
59 
60 /* Print detailed info to SDK console (NOT to LVGL log system) */
61 #define BLIT_DBG_VERBOSE 0
62 
63 /* Draw rectangles around BLIT tiles */
64 #define BLIT_DBG_AREAS   0
65 
66 /* Redirect PRINT to SDK PRINTF */
67 #define PRINT PRINTF
68 
69 /* Verbose debug print */
70 #if BLIT_DBG_VERBOSE
71     #define PRINT_BLT PRINTF
72 #else
73     #define PRINT_BLT(...)
74 #endif
75 
76 /* Internal compound symbol */
77 #if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \
78     defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \
79     ((LV_HOR_RES_MAX > RT595_BLIT_WRKRND_THR) || (LV_VER_RES_MAX > RT595_BLIT_WRKRND_THR)) && \
80     RT595_BLIT_WRKRND_ENABLED
81 #define _BLIT_SPLIT_ENABLED 1
82 #else
83 #define _BLIT_SPLIT_ENABLED 0
84 #endif
85 
86 /* BLIT split threshold - BLITs with width or height higher than this value will be done
87  * in multiple steps. Value must be 16-aligned. Don't change.
88  * */
89 #define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
90 
91 
92 /**********************
93  *      TYPEDEFS
94  **********************/
95 
96 /**********************
97  *  STATIC PROTOTYPES
98  **********************/
99 
100 static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
101                              const lv_color_t * ptr, bool source);
102 
103 static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit);
104 #if _BLIT_SPLIT_ENABLED
105 static void _align_x(lv_area_t * area, lv_color_t ** buf);
106 static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx);
107 static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
108 #if BLIT_DBG_AREAS
109 static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
110                             lv_area_t * fill_area, lv_color_t color);
111 #endif
112 static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit);
113 #endif
114 
115 /**********************
116  *  STATIC VARIABLES
117  **********************/
118 
119 /**********************
120  *      MACROS
121  **********************/
122 
123 #define CHECK(cond, txt)                \
124     do {                                \
125         if (cond) {                     \
126             PRINT("%s. STOP!\n", txt);  \
127             for ( ; ; );                \
128         }                               \
129     } while(0)
130 
131 /**********************
132  *   GLOBAL FUNCTIONS
133  **********************/
134 
135 /***
136  * Fills rectangular area in buffer.
137  * @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes)
138  * @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px)
139  * @param[in] dest_height Destination buffer height in pixels
140  * @param[in] fill_area Area to be filled
141  * @param[in] color Fill color
142  * @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill)
143  * @retval LV_RES_OK Fill completed
144  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
145  */
lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf,lv_coord_t dest_width,lv_coord_t dest_height,const lv_area_t * fill_area,lv_color_t color,lv_opa_t opa)146 lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
147                                 const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
148 {
149     vg_lite_buffer_t rt;
150     vg_lite_rectangle_t rect;
151     vg_lite_error_t err = VG_LITE_SUCCESS;
152     lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
153     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
154 
155     if(_init_vg_buf(&rt, (uint32_t) dest_width, (uint32_t) dest_height, (uint32_t) dest_width * sizeof(lv_color_t),
156                     (const lv_color_t *) dest_buf, false) != LV_RES_OK) {
157 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
158         LV_LOG_ERROR("init_vg_buf reported error. Fill failed.");
159 #endif
160         return LV_RES_INV;
161     }
162 
163     if(opa >= (lv_opa_t) LV_OPA_MAX) {   /*Opaque fill*/
164         rect.x = fill_area->x1;
165         rect.y = fill_area->y1;
166         rect.width = (int32_t) fill_area->x2 - (int32_t) fill_area->x1 + 1;
167         rect.height = (int32_t) fill_area->y2 - (int32_t) fill_area->y1 + 1;
168 
169         if(disp != NULL && disp->driver->clean_dcache_cb != NULL) {  /*Clean & invalidate cache*/
170             disp->driver->clean_dcache_cb(disp->driver);
171         }
172 
173         err = vg_lite_clear(&rt, &rect, col32.full);
174         if(err != VG_LITE_SUCCESS) {
175 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
176             LV_LOG_ERROR("vg_lite_clear reported error. Fill failed.");
177 #endif
178             return LV_RES_INV;
179         }
180         err = vg_lite_finish();
181         if(err != VG_LITE_SUCCESS) {
182 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
183             LV_LOG_ERROR("vg_lite_finish reported error. Fill failed.");
184 #endif
185             return LV_RES_INV;
186         }
187     }
188     else {   /*fill with transparency*/
189 
190         vg_lite_path_t path;
191         lv_color32_t colMix;
192         int16_t path_data[] = { /*VG rectangular path*/
193             VLC_OP_MOVE, fill_area->x1,  fill_area->y1,
194             VLC_OP_LINE, fill_area->x2 + 1,  fill_area->y1,
195             VLC_OP_LINE, fill_area->x2 + 1,  fill_area->y2 + 1,
196             VLC_OP_LINE, fill_area->x1,  fill_area->y2 + 1,
197             VLC_OP_LINE, fill_area->x1,  fill_area->y1,
198             VLC_OP_END
199         };
200 
201         err = vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data,
202                                 (vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, ((vg_lite_float_t) fill_area->x2) + 1.0f,
203                                 ((vg_lite_float_t) fill_area->y2) + 1.0f);
204         if(err != VG_LITE_SUCCESS)  {
205 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
206             LV_LOG_ERROR("vg_lite_init_path() failed.");
207 #endif
208             return LV_RES_INV;
209         }
210 
211         colMix.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); /*Pre-multiply color*/
212         colMix.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8);
213         colMix.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8);
214         colMix.ch.alpha = opa;
215 
216         if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) {  /*Clean & invalidate cache*/
217             disp->driver->clean_dcache_cb(disp->driver);
218         }
219 
220         vg_lite_matrix_t matrix;
221         vg_lite_identity(&matrix);
222 
223         /*Draw rectangle*/
224         err = vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full);
225         if(err != VG_LITE_SUCCESS) {
226 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
227             LV_LOG_ERROR("vg_lite_draw() failed.");
228 #endif
229             vg_lite_clear_path(&path);
230             return LV_RES_INV;
231         }
232 
233         err = vg_lite_finish();
234         if(err != VG_LITE_SUCCESS) {
235 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
236             LV_LOG_ERROR("vg_lite_finish() failed.");
237 #endif
238             return LV_RES_INV;
239         }
240 
241         err = vg_lite_clear_path(&path);
242         if(err != VG_LITE_SUCCESS) {
243 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
244             LV_LOG_ERROR("vg_lite_clear_path() failed.");
245 #endif
246             return LV_RES_INV;
247         }
248     }
249 
250     if(err == VG_LITE_SUCCESS) {
251         return LV_RES_OK;
252     }
253     else {
254 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
255         LV_LOG_ERROR("VG Lite Fill failed.");
256 #endif
257         return LV_RES_INV;
258     }
259 }
260 
261 /***
262  * BLock Image Transfer.
263  * @param[in] blit Description of the transfer
264  * @retval LV_RES_OK Transfer complete
265  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
266  */
lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit)267 lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
268 {
269 #if _BLIT_SPLIT_ENABLED
270 
271     lv_res_t rv = LV_RES_INV;
272 
273     if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) {
274         PRINT_BLT("Blit check failed\n");
275         return LV_RES_INV;
276     }
277 
278     PRINT_BLT("BLIT from: "
279               "Area: %03d,%03d - %03d,%03d "
280               "Addr: %d\n\n",
281               blit->src_area.x1, blit->src_area.y1,
282               blit->src_area.x2, blit->src_area.y2,
283               (uintptr_t) blit->src);
284 
285     PRINT_BLT("BLIT to: "
286               "Area: %03d,%03d - %03d,%03d "
287               "Addr: %d\n\n",
288               blit->dst_area.x1, blit->dst_area.y1,
289               blit->dst_area.x2, blit->dst_area.y2,
290               (uintptr_t) blit->src);
291 
292     /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible.  */
293     _align_x(&blit->src_area, (lv_color_t **)&blit->src);
294     _align_y(&blit->src_area, (lv_color_t **)&blit->src,  blit->src_stride / sizeof(lv_color_t));
295     _align_x(&blit->dst_area, (lv_color_t **)&blit->dst);
296     _align_y(&blit->dst_area, (lv_color_t **)&blit->dst,  blit->dst_stride / sizeof(lv_color_t));
297 
298     /* Stage 2: If we're in limit, do a single BLIT */
299     if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
300        (blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
301         PRINT_BLT("Simple blit!\n");
302         return _lv_gpu_nxp_vglite_blit_single(blit);
303     };
304 
305     /* Stage 3: Split the BLIT into multiple tiles */
306     PRINT_BLT("Split blit!\n");
307 
308     PRINT_BLT("Blit "
309               "([%03d,%03d], [%03d,%03d]) -> "
310               "([%03d,%03d], [%03d,%03d]) | "
311               "([%03dx%03d] -> [%03dx%03d]) | "
312               "A:(%d -> %d)\n",
313               blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2,
314               blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2,
315               lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area),
316               lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area),
317               (uintptr_t) blit->src, (uintptr_t) blit->dst);
318 
319 
320     uint32_t totalWidth = lv_area_get_width(&blit->src_area);
321     uint32_t totalHeight = lv_area_get_height(&blit->src_area);
322 
323     lv_gpu_nxp_vglite_blit_info_t tileBlit;
324 
325     /* Number of tiles needed */
326     int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
327                       LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
328     int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
329                       LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
330 
331     /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
332      * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
333      * different */
334     int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0;
335     int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0;
336 
337     PRINT_BLT("\n");
338     PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX);
339 
340     tileBlit = *blit;
341 
342     for(int tileY = 0; tileY < totalTilesY; tileY++) {
343 
344         tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */
345         tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
346         if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
347             tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
348         }
349         tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof(
350                            lv_color_t); /* stride in px! */
351 
352         tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */
353         tileBlit.dst_area.y2 = tileBlit.src_area.y2;
354 
355         tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof(
356                            lv_color_t); /* stride in px! */
357 
358         for(int tileX = 0; tileX < totalTilesX; tileX++) {
359 
360             if(tileX == 0) {
361                 /* 1st tile is special - there may be a gap between buffer start pointer
362                  * and area.x1 value, as the pointer has to be aligned.
363                  * tileBlit.src pointer - keep init value from Y-loop.
364                  * Also, 1st tile start is not shifted! shift is applied from 2nd tile */
365                 tileBlit.src_area.x1 = blit->src_area.x1;
366                 tileBlit.dst_area.x1 = blit->dst_area.x1;
367             }
368             else {
369                 /* subsequent tiles always starts from 0, but shifted*/
370                 tileBlit.src_area.x1 = 0 + shiftSrcX;
371                 tileBlit.dst_area.x1 = 0 + shiftDstX;
372                 /* and advance start pointer + 1 tile size */
373                 tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
374                 tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
375             }
376 
377             /* Clip tile end coordinates */
378             tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
379             if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
380                 tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
381             }
382 
383             tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
384             if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
385                 tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
386             }
387 
388             if(tileX < (totalTilesX - 1)) {
389                 /* And adjust end coords if shifted,  but not for last tile! */
390                 tileBlit.src_area.x2 += shiftSrcX;
391                 tileBlit.dst_area.x2 += shiftDstX;
392             }
393 
394             rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit);
395 
396 #if BLIT_DBG_AREAS
397             _draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, LV_COLOR_RED);
398             _draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area,
399                             LV_COLOR_GREEN);
400 #endif
401 
402             PRINT_BLT("Tile [%d, %d]: "
403                       "([%d,%d], [%d,%d]) -> "
404                       "([%d,%d], [%d,%d]) | "
405                       "([%dx%d] -> [%dx%d]) | "
406                       "A:(0x%8X -> 0x%8X) %s\n",
407                       tileX, tileY,
408                       tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2,
409                       tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2,
410                       lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area),
411                       lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area),
412                       (uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst,
413                       rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!");
414 
415             if(rv != LV_RES_OK) { /* if anything goes wrong... */
416 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
417                 LV_LOG_ERROR("Split BLIT failed. Trying SW BLIT instead.");
418 #endif
419                 _sw_blit(&tileBlit);
420                 rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */
421             }
422 
423         }
424         PRINT_BLT(" \n");
425     }
426 
427     return rv; /* should never fail */
428 
429 #else /* non RT595 */
430     /* Just pass down */
431     return _lv_gpu_nxp_vglite_blit_single(blit);
432 #endif
433 }
434 
435 /**********************
436  *   STATIC FUNCTIONS
437  **********************/
438 
439 /***
440  * BLock Image Transfer - single direct BLIT.
441  * @param[in] blit Description of the transfer
442  * @retval LV_RES_OK Transfer complete
443  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
444  */
_lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit)445 static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit)
446 {
447     vg_lite_buffer_t src_vgbuf, dst_vgbuf;
448     vg_lite_error_t err = VG_LITE_SUCCESS;
449     uint32_t rect[4];
450     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
451 
452     if(blit == NULL) {
453         /*Wrong parameter*/
454         return LV_RES_INV;
455     }
456 
457     if(blit->opa < (lv_opa_t) LV_OPA_MIN) {
458         return LV_RES_OK; /*Nothing to BLIT*/
459     }
460 
461     /*Wrap src/dst buffer into VG-Lite buffer*/
462     if(_init_vg_buf(&src_vgbuf, (uint32_t) blit->src_width, (uint32_t) blit->src_height, (uint32_t) blit->src_stride,
463                     blit->src, true) != LV_RES_OK) {
464 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
465         LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
466 #endif
467         return LV_RES_INV;
468     }
469 
470     if(_init_vg_buf(&dst_vgbuf, (uint32_t) blit->dst_width, (uint32_t) blit->dst_height, (uint32_t) blit->dst_stride,
471                     blit->dst, false) != LV_RES_OK) {
472 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
473         LV_LOG_ERROR("init_vg_buf reported error. BLIT failed.");
474 #endif
475         return LV_RES_INV;
476     }
477 
478     rect[0] = (uint32_t) blit->src_area.x1; /* start x */
479     rect[1] = (uint32_t) blit->src_area.y1; /* start y */
480     rect[2] = (uint32_t) blit->src_area.x2 - (uint32_t) blit->src_area.x1 + 1U; /* width */
481     rect[3] = (uint32_t) blit->src_area.y2 - (uint32_t) blit->src_area.y1 + 1U; /* height */
482 
483     vg_lite_matrix_t matrix;
484     vg_lite_identity(&matrix);
485     vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix);
486 
487     if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) {  /*Clean & invalidate cache*/
488         disp->driver->clean_dcache_cb(disp->driver);
489     }
490 
491     uint32_t color;
492     vg_lite_blend_t blend;
493     if(blit->opa >= (uint8_t) LV_OPA_MAX) {
494         color = 0x0;
495         blend = VG_LITE_BLEND_NONE;
496     }
497     else {
498         uint32_t opa = (uint32_t) blit->opa;
499         color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
500         blend = VG_LITE_BLEND_SRC_OVER;
501         src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
502     }
503 
504     err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT);
505     if(err != VG_LITE_SUCCESS) {
506 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
507         LV_LOG_ERROR("vg_lite_blit_rect() failed.");
508 #endif
509         return LV_RES_INV;
510     }
511 
512     err = vg_lite_finish();
513     if(err != VG_LITE_SUCCESS) {
514 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
515         LV_LOG_ERROR("vg_lite_finish() failed.");
516 #endif
517         return LV_RES_INV;
518     }
519 
520     if(err == VG_LITE_SUCCESS) {
521         return LV_RES_OK;
522     }
523     else {
524 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
525         LV_LOG_ERROR("vg_lite_blit_rect or vg_lite_finish reported error. BLIT failed.");
526 #endif
527         return LV_RES_INV;
528     }
529 }
530 
531 /***
532  * Fills vg_lite_buffer_t structure according given parameters.
533  * @param[out] dst Buffer structure to be filled
534  * @param[in] width Width of buffer in pixels
535  * @param[in] height Height of buffer in pixels
536  * @param[in] stride Stride of the buffer in bytes
537  * @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements)
538  */
_init_vg_buf(vg_lite_buffer_t * dst,uint32_t width,uint32_t height,uint32_t stride,const lv_color_t * ptr,bool source)539 static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride,
540                              const lv_color_t * ptr, bool source)
541 {
542     if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0U) {  /*Test for alignment*/
543 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
544         LV_LOG_ERROR("ptr (0x%X) not aligned to %d.", (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
545 #endif
546         return LV_RES_INV;
547     }
548 
549     if(source &&
550        (stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) {  /*Test for stride alignment*/
551 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
552         LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d bytes.", stride,
553                      LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
554 #endif
555         return LV_RES_INV;
556     }
557 
558     dst->format = VGLITE_PX_FMT;
559     dst->tiled = VG_LITE_LINEAR;
560     dst->image_mode = VG_LITE_NORMAL_IMAGE_MODE;
561     dst->transparency_mode = VG_LITE_IMAGE_OPAQUE;
562 
563     dst->width = (int32_t) width;
564     dst->height = (int32_t) height;
565     dst->stride = (int32_t) stride;
566 
567     void * r_ptr = memset(&dst->yuv, 0, sizeof(dst->yuv));
568     if(r_ptr == NULL) {
569         return LV_RES_INV;
570     }
571 
572     dst->memory = (void *)ptr;
573     dst->address = (uint32_t) dst->memory;
574     dst->handle = NULL;
575 
576     return LV_RES_OK;
577 }
578 
579 #if _BLIT_SPLIT_ENABLED
580 
581 /**
582  * Software BLIT as a fall-back scenario
583  * @param[in] blit BLIT configuration
584  */
_sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit)585 static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
586 {
587     int x, y;
588 
589     lv_coord_t w = lv_area_get_width(&blit->src_area);
590     lv_coord_t h = lv_area_get_height(&blit->src_area);
591 
592     uint32_t srcStridePx = blit->src_stride / sizeof(lv_color_t);
593     uint32_t dstStridePx = blit->dst_stride / sizeof(lv_color_t);
594 
595     lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1;
596     lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1;
597 
598     if(blit->opa >= LV_OPA_MAX) {
599         /* simple copy */
600         for(y = 0; y < h; y++) {
601             _lv_memcpy(dst, src, w * sizeof(lv_color_t));
602             src += srcStridePx;
603             dst += dstStridePx;
604         }
605     }
606     else if(blit->opa >= LV_OPA_MIN) {
607         /* alpha blending */
608         for(y = 0; y < h; y++) {
609             for(x = 0; x < w; x++) {
610                 dst[x] = lv_color_mix(src[x], dst[x], blit->opa);
611             }
612             src += srcStridePx;
613             dst += dstStridePx;
614         }
615     }
616 }
617 
618 /**
619  * Verify BLIT structure - widths, stride, pointer alignment
620  * @param[in] blit
621  * @return
622  */
_lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit)623 static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit)
624 {
625 
626     if(lv_area_get_width(&blit->src_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) {  /* Test for minimal width */
627 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
628         LV_LOG_ERROR("source area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area),
629                      LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
630 #endif
631         return LV_RES_INV;
632     }
633 
634     if(lv_area_get_width(&blit->dst_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) {  /* Test for minimal width */
635 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
636         LV_LOG_ERROR("destination area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area),
637                      LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
638 #endif
639         return LV_RES_INV;
640     }
641 
642     if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) {  /* Test for pointer alignment */
643 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
644         LV_LOG_ERROR("source buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
645 #endif
646         return LV_RES_INV;
647     }
648     /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
649 
650     if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
651        0x0) {  /* Test for stride alignment */
652 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
653         LV_LOG_ERROR("source buffer stride (%d px) not aligned to %d px.", blit->src_stride,
654                      LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
655 #endif
656         return LV_RES_INV;
657     }
658 
659     if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) !=
660        0x0) {  /* Test for stride alignment */
661 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
662         LV_LOG_ERROR("destination buffer stride (%d px) not aligned to %d px.", blit->dst_stride,
663                      LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
664 #endif
665         return LV_RES_INV;
666     }
667 
668     if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) ||
669        (lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) {
670 #if LV_GPU_NXP_VG_LITE_LOG_ERRORS
671         LV_LOG_ERROR("source and destination buffer areas are not equal.");
672 #endif
673         return LV_RES_INV;
674     }
675 
676     return LV_RES_OK;
677 }
678 
679 /***
680  * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
681  * @param[in,out] area Area to be updated
682  * @param[in,out] buf Pointer to be updated
683  */
_align_x(lv_area_t * area,lv_color_t ** buf)684 static void _align_x(lv_area_t * area, lv_color_t ** buf)
685 {
686 
687     int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH));
688     CHECK(alignedAreaStartPx < 0, "Should never happen.");
689 
690     area->x1 -= alignedAreaStartPx;
691     area->x2 -= alignedAreaStartPx;
692     *buf += alignedAreaStartPx;
693 }
694 
695 /***
696  * Move buffer pointer to the area start and update variables, Y-axis only.
697  * @param[in,out] area Area to be updated
698  * @param[in,out] buf Pointer to be updated
699  * @param[in] stridePx Buffer stride in pixels
700  */
_align_y(lv_area_t * area,lv_color_t ** buf,uint32_t stridePx)701 static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx)
702 {
703     int LineToAlignMem;
704     int alignedAreaStartPy;
705     /* find how many lines of pixels will respect memory alignment requirement */
706     if(stridePx % LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0) {
707         alignedAreaStartPy = area->y1;
708     }
709     else {
710         LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
711         CHECK(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0,
712               "Complex case: need gcd function.");
713         alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
714         CHECK(alignedAreaStartPy < 0, "Should never happen.");
715     }
716 
717     area->y1 -= alignedAreaStartPy;
718     area->y2 -= alignedAreaStartPy;
719     *buf += alignedAreaStartPy * stridePx;
720 }
721 
722 #if BLIT_DBG_AREAS
723 /***
724  * Draws a simple rectangle, 1 px line width.
725  * @param dest_buf Destination buffer
726  * @param dest_width Destination buffer width (must be aligned on 16px)
727  * @param dest_height Destination buffer height
728  * @param fill_area Rectangle coordinates
729  * @param color Rectangle color
730  */
_draw_rectangle(lv_color_t * dest_buf,lv_coord_t dest_width,lv_coord_t dest_height,lv_area_t * fill_area,lv_color_t color)731 static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height,
732                             lv_area_t * fill_area, lv_color_t color)
733 {
734 
735     lv_area_t a;
736 
737     /* top line */
738     a.x1 = fill_area->x1;
739     a.x2 = fill_area->x2;
740     a.y1 = fill_area->y1;
741     a.y2 = fill_area->y1;
742     lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
743 
744 
745     /* bottom line */
746     a.x1 = fill_area->x1;
747     a.x2 = fill_area->x2;
748     a.y1 = fill_area->y2;
749     a.y2 = fill_area->y2;
750     lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
751 
752     /* left line */
753     a.x1 = fill_area->x1;
754     a.x2 = fill_area->x1;
755     a.y1 = fill_area->y1;
756     a.y2 = fill_area->y2;
757     lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
758 
759     /* right line */
760     a.x1 = fill_area->x2;
761     a.x2 = fill_area->x2;
762     a.y1 = fill_area->y1;
763     a.y2 = fill_area->y2;
764     lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER);
765 }
766 #endif /* BLIT_DBG_AREAS */
767 
768 #endif /* _BLIT_SPLIT_ENABLED */
769 
770 #endif /*LV_USE_GPU_NXP_VG_LITE*/
771