1 /**
2  * @file lv_draw_dma2d.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_draw_dma2d_private.h"
11 #if LV_USE_DRAW_DMA2D
12 
13 #include "../sw/lv_draw_sw.h"
14 #include "../../misc/lv_area_private.h"
15 
16 #if !LV_DRAW_DMA2D_ASYNC && LV_USE_DRAW_DMA2D_INTERRUPT
17     #warning LV_USE_DRAW_DMA2D_INTERRUPT is 1 but has no effect because LV_USE_OS is LV_OS_NONE
18 #endif
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 
24 #define DRAW_UNIT_ID_DMA2D 5
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 
30 /**********************
31  *  STATIC PROTOTYPES
32  **********************/
33 
34 static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
35 static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
36 static int32_t delete_cb(lv_draw_unit_t * draw_unit);
37 #if LV_DRAW_DMA2D_ASYNC
38     static void thread_cb(void * arg);
39 #endif
40 #if !LV_DRAW_DMA2D_ASYNC
41     static bool check_transfer_completion(void);
42 #endif
43 static void post_transfer_tasks(lv_draw_dma2d_unit_t * u);
44 
45 /**********************
46  *  STATIC VARIABLES
47  **********************/
48 
49 #if LV_DRAW_DMA2D_ASYNC
50     static lv_draw_dma2d_unit_t * g_unit;
51 #endif
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
lv_draw_dma2d_init(void)61 void lv_draw_dma2d_init(void)
62 {
63     lv_draw_dma2d_unit_t * draw_dma2d_unit = lv_draw_create_unit(sizeof(lv_draw_dma2d_unit_t));
64     draw_dma2d_unit->base_unit.evaluate_cb = evaluate_cb;
65     draw_dma2d_unit->base_unit.dispatch_cb = dispatch_cb;
66     draw_dma2d_unit->base_unit.delete_cb = delete_cb;
67     draw_dma2d_unit->base_unit.name = "DMA2D";
68 
69 #if LV_DRAW_DMA2D_ASYNC
70     g_unit = draw_dma2d_unit;
71 
72     lv_result_t res = lv_thread_init(&draw_dma2d_unit->thread, "dma2d", LV_THREAD_PRIO_HIGH, thread_cb, 2 * 1024,
73                                      draw_dma2d_unit);
74     LV_ASSERT(res == LV_RESULT_OK);
75 #endif
76 
77     /* enable the DMA2D clock */
78 #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5)
79     RCC->AHB1ENR |= RCC_AHB1ENR_DMA2DEN;
80 #elif defined(STM32H7)
81     RCC->AHB3ENR |= RCC_AHB3ENR_DMA2DEN;
82 #else
83 #warning "LVGL can't enable the clock for DMA2D"
84 #endif
85 
86     /* disable dead time */
87     DMA2D->AMTCR = 0;
88 
89     /* enable the interrupt */
90     NVIC_EnableIRQ(DMA2D_IRQn);
91 }
92 
lv_draw_dma2d_deinit(void)93 void lv_draw_dma2d_deinit(void)
94 {
95     /* disable the interrupt */
96     NVIC_DisableIRQ(DMA2D_IRQn);
97 
98     /* disable the DMA2D clock */
99 #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) || defined(STM32L4)
100     RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA2DEN;
101 #elif defined(STM32H7)
102     RCC->AHB3ENR &= ~RCC_AHB3ENR_DMA2DEN;
103 #endif
104 
105 #if LV_DRAW_DMA2D_ASYNC
106     lv_result_t res = lv_thread_delete(&g_unit->thread);
107     LV_ASSERT(res == LV_RESULT_OK);
108 
109     res = lv_thread_sync_delete(&g_unit->interrupt_signal);
110     LV_ASSERT(res == LV_RESULT_OK);
111 
112     g_unit = NULL;
113 #endif
114 }
115 
116 #if LV_USE_DRAW_DMA2D_INTERRUPT
lv_draw_dma2d_transfer_complete_interrupt_handler(void)117 void lv_draw_dma2d_transfer_complete_interrupt_handler(void)
118 {
119 #if LV_DRAW_DMA2D_ASYNC
120     lv_thread_sync_signal_isr(&g_unit->interrupt_signal);
121 #endif
122 }
123 #endif
124 
lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf)125 lv_draw_dma2d_output_cf_t lv_draw_dma2d_cf_to_dma2d_output_cf(lv_color_format_t cf)
126 {
127     switch(cf) {
128         case LV_COLOR_FORMAT_ARGB8888:
129         case LV_COLOR_FORMAT_XRGB8888:
130             return LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888;
131         case LV_COLOR_FORMAT_RGB888:
132             return LV_DRAW_DMA2D_OUTPUT_CF_RGB888;
133         case LV_COLOR_FORMAT_RGB565:
134             return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
135         case LV_COLOR_FORMAT_ARGB1555:
136             return LV_DRAW_DMA2D_OUTPUT_CF_ARGB1555;
137         default:
138             LV_ASSERT_MSG(false, "unsupported output color format");
139     }
140     return LV_DRAW_DMA2D_OUTPUT_CF_RGB565;
141 }
142 
lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf,lv_color_t color)143 uint32_t lv_draw_dma2d_color_to_dma2d_color(lv_draw_dma2d_output_cf_t cf, lv_color_t color)
144 {
145     switch(cf) {
146         case LV_DRAW_DMA2D_OUTPUT_CF_ARGB8888:
147         case LV_DRAW_DMA2D_OUTPUT_CF_RGB888:
148             return lv_color_to_u32(color);
149         case LV_DRAW_DMA2D_OUTPUT_CF_RGB565:
150             return lv_color_to_u16(color);
151         default:
152             LV_ASSERT_MSG(false, "unsupported output color format");
153     }
154     return 0;
155 }
156 
lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf)157 void lv_draw_dma2d_configure_and_start_transfer(const lv_draw_dma2d_configuration_t * conf)
158 {
159     /* number of lines register */
160     DMA2D->NLR = (conf->w << DMA2D_NLR_PL_Pos) | (conf->h << DMA2D_NLR_NL_Pos);
161 
162     /* output */
163 
164     /* output memory address register */
165     DMA2D->OMAR = (uint32_t)(uintptr_t) conf->output_address;
166     /* output offset register */
167     DMA2D->OOR = conf->output_offset;
168     /* output pixel format converter control register */
169     DMA2D->OPFCCR = ((uint32_t) conf->output_cf) << DMA2D_OPFCCR_CM_Pos;
170 
171     /* Fill color. Only for mode LV_DRAW_DMA2D_MODE_REGISTER_TO_MEMORY */
172     DMA2D->OCOLR = conf->reg_to_mem_mode_color;
173 
174     /* foreground */
175 
176     /* foreground memory address register */
177     DMA2D->FGMAR = (uint32_t)(uintptr_t) conf->fg_address;
178     /* foreground offset register */
179     DMA2D->FGOR = conf->fg_offset;
180     /* foreground color. only for mem-to-mem with blending and fixed-color foreground */
181     DMA2D->FGCOLR = conf->fg_color;
182     /* foreground pixel format converter control register */
183     DMA2D->FGPFCCR = (((uint32_t) conf->fg_cf) << DMA2D_FGPFCCR_CM_Pos)
184                      | (conf->fg_alpha << DMA2D_FGPFCCR_ALPHA_Pos)
185                      | (conf->fg_alpha_mode << DMA2D_FGPFCCR_AM_Pos);
186 
187     /* background */
188 
189     DMA2D->BGMAR = (uint32_t)(uintptr_t) conf->bg_address;
190     DMA2D->BGOR = conf->bg_offset;
191     DMA2D->BGCOLR = conf->bg_color;
192     DMA2D->BGPFCCR = (((uint32_t) conf->bg_cf) << DMA2D_BGPFCCR_CM_Pos)
193                      | (conf->bg_alpha << DMA2D_BGPFCCR_ALPHA_Pos)
194                      | (conf->bg_alpha_mode << DMA2D_BGPFCCR_AM_Pos);
195 
196     /* ensure the DMA2D register values are observed before the start transfer bit is set */
197     __DSB();
198 
199     /* start the transfer (also set mode and enable transfer complete interrupt) */
200     DMA2D->CR = DMA2D_CR_START | (((uint32_t) conf->mode) << DMA2D_CR_MODE_Pos)
201 #if LV_USE_DRAW_DMA2D_INTERRUPT
202                 | DMA2D_CR_TCIE
203 #endif
204                 ;
205 }
206 
207 #if LV_DRAW_DMA2D_CACHE
lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area)208 void lv_draw_dma2d_invalidate_cache(const lv_draw_dma2d_cache_area_t * mem_area)
209 {
210     if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return; /* data cache is disabled */
211 
212     uint32_t rows_remaining = mem_area->height;
213     uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
214     uint32_t row_end_addr = 0;
215 
216     __DSB();
217 
218     while(rows_remaining) {
219         uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
220         uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
221                                 __SCB_DCACHE_LINE_SIZE) + 1;
222 
223         if(addr == row_end_addr) {
224             addr += __SCB_DCACHE_LINE_SIZE;
225             cache_lines--;
226         }
227 
228         while(cache_lines) {
229             SCB->DCIMVAC = addr;
230             addr += __SCB_DCACHE_LINE_SIZE;
231             cache_lines--;
232         }
233 
234         row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
235         row_addr += mem_area->stride;
236         rows_remaining--;
237     };
238 
239     __DSB();
240     __ISB();
241 }
242 
lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area)243 void lv_draw_dma2d_clean_cache(const lv_draw_dma2d_cache_area_t * mem_area)
244 {
245     if((SCB->CCR & SCB_CCR_DC_Msk) == 0) return;  /* data cache is disabled */
246 
247     uint32_t rows_remaining = mem_area->height;
248     uint32_t row_addr = (uint32_t)(uintptr_t) mem_area->first_byte;
249     uint32_t row_end_addr = 0;
250 
251     __DSB();
252 
253     while(rows_remaining) {
254         uint32_t addr = row_addr & ~(__SCB_DCACHE_LINE_SIZE - 1U);
255         uint32_t cache_lines = ((((row_addr + mem_area->width_bytes - 1) & ~(__SCB_DCACHE_LINE_SIZE - 1U)) - addr) /
256                                 __SCB_DCACHE_LINE_SIZE) + 1;
257 
258         if(addr == row_end_addr) {
259             addr += __SCB_DCACHE_LINE_SIZE;
260             cache_lines--;
261         }
262 
263         while(cache_lines) {
264             SCB->DCCMVAC = addr;
265             addr += __SCB_DCACHE_LINE_SIZE;
266             cache_lines--;
267         }
268 
269         row_end_addr = addr - __SCB_DCACHE_LINE_SIZE;
270         row_addr += mem_area->stride;
271         rows_remaining--;
272     };
273 
274     __DSB();
275     __ISB();
276 }
277 #endif
278 
279 /**********************
280  *   STATIC FUNCTIONS
281  **********************/
282 
evaluate_cb(lv_draw_unit_t * draw_unit,lv_draw_task_t * task)283 static int32_t evaluate_cb(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
284 {
285     switch(task->type) {
286         case LV_DRAW_TASK_TYPE_FILL: {
287                 lv_draw_fill_dsc_t * dsc = task->draw_dsc;
288                 if(!(dsc->radius == 0
289                      && dsc->grad.dir == LV_GRAD_DIR_NONE
290                      && (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
291                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
292                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
293                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
294                     return 0;
295                 }
296             }
297             break;
298         case LV_DRAW_TASK_TYPE_IMAGE: {
299                 lv_draw_image_dsc_t * dsc = task->draw_dsc;
300                 if(!(dsc->header.cf < LV_COLOR_FORMAT_PROPRIETARY_START
301                      && dsc->clip_radius == 0
302                      && dsc->bitmap_mask_src == NULL
303                      && dsc->sup == NULL
304                      && dsc->tile == 0
305                      && dsc->blend_mode == LV_BLEND_MODE_NORMAL
306                      && dsc->recolor_opa <= LV_OPA_MIN
307                      && dsc->skew_y == 0
308                      && dsc->skew_x == 0
309                      && dsc->scale_x == 256
310                      && dsc->scale_y == 256
311                      && dsc->rotation == 0
312                      && lv_image_src_get_type(dsc->src) == LV_IMAGE_SRC_VARIABLE
313                      && (dsc->header.cf == LV_COLOR_FORMAT_ARGB8888
314                          || dsc->header.cf == LV_COLOR_FORMAT_XRGB8888
315                          || dsc->header.cf == LV_COLOR_FORMAT_RGB888
316                          || dsc->header.cf == LV_COLOR_FORMAT_RGB565
317                          || dsc->header.cf == LV_COLOR_FORMAT_ARGB1555)
318                      && (dsc->base.layer->color_format == LV_COLOR_FORMAT_ARGB8888
319                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_XRGB8888
320                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB888
321                          || dsc->base.layer->color_format == LV_COLOR_FORMAT_RGB565))) {
322                     return 0;
323                 }
324             }
325             break;
326         default:
327             return 0;
328     }
329 
330     task->preferred_draw_unit_id = DRAW_UNIT_ID_DMA2D;
331     task->preference_score = 0;
332 
333     return 0;
334 }
335 
dispatch_cb(lv_draw_unit_t * draw_unit,lv_layer_t * layer)336 static int32_t dispatch_cb(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
337 {
338     lv_draw_dma2d_unit_t * draw_dma2d_unit = (lv_draw_dma2d_unit_t *) draw_unit;
339 
340     if(draw_dma2d_unit->task_act) {
341 #if LV_DRAW_DMA2D_ASYNC
342         /*Return immediately if it's busy with draw task*/
343         return 0;
344 #else
345         if(!check_transfer_completion()) {
346             return LV_DRAW_UNIT_IDLE;
347         }
348         post_transfer_tasks(draw_dma2d_unit);
349 #endif
350     }
351 
352     lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_DMA2D);
353     if(t == NULL) {
354         return LV_DRAW_UNIT_IDLE;
355     }
356 
357     void * buf = lv_draw_layer_alloc_buf(layer);
358     if(buf == NULL) {
359         return LV_DRAW_UNIT_IDLE;
360     }
361 
362     t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
363     draw_dma2d_unit->base_unit.target_layer = layer;
364     draw_dma2d_unit->base_unit.clip_area = &t->clip_area;
365     draw_dma2d_unit->task_act = t;
366 
367     if(t->type == LV_DRAW_TASK_TYPE_FILL) {
368         lv_draw_fill_dsc_t * dsc = t->draw_dsc;
369         const lv_area_t * coords = &t->area;
370         lv_area_t clipped_coords;
371         if(!lv_area_intersect(&clipped_coords, coords, draw_dma2d_unit->base_unit.clip_area)) {
372             return LV_DRAW_UNIT_IDLE;
373         }
374 
375         void * dest = lv_draw_layer_go_to_xy(layer,
376                                              clipped_coords.x1 - layer->buf_area.x1,
377                                              clipped_coords.y1 - layer->buf_area.y1);
378 
379         if(dsc->opa >= LV_OPA_MAX) {
380             lv_draw_dma2d_opaque_fill(draw_dma2d_unit,
381                                       dest,
382                                       lv_area_get_width(&clipped_coords),
383                                       lv_area_get_height(&clipped_coords),
384                                       lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
385         }
386         else {
387             lv_draw_dma2d_fill(draw_dma2d_unit,
388                                dest,
389                                lv_area_get_width(&clipped_coords),
390                                lv_area_get_height(&clipped_coords),
391                                lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
392         }
393     }
394     else if(t->type == LV_DRAW_TASK_TYPE_IMAGE) {
395         lv_draw_image_dsc_t * dsc = t->draw_dsc;
396         const lv_area_t * coords = &t->area;
397         lv_area_t clipped_coords;
398         if(!lv_area_intersect(&clipped_coords, coords, draw_dma2d_unit->base_unit.clip_area)) {
399             return LV_DRAW_UNIT_IDLE;
400         }
401 
402         void * dest = lv_draw_layer_go_to_xy(layer,
403                                              clipped_coords.x1 - layer->buf_area.x1,
404                                              clipped_coords.y1 - layer->buf_area.y1);
405 
406         if(dsc->opa >= LV_OPA_MAX) {
407             lv_draw_dma2d_opaque_image(
408                 draw_dma2d_unit,
409                 dest,
410                 &clipped_coords,
411                 lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
412         }
413         else {
414             lv_draw_dma2d_image(
415                 draw_dma2d_unit,
416                 dest,
417                 &clipped_coords,
418                 lv_draw_buf_width_to_stride(lv_area_get_width(&layer->buf_area), dsc->base.layer->color_format));
419         }
420     }
421 
422 #if !LV_DRAW_DMA2D_ASYNC
423     lv_draw_dispatch_request();
424 #endif
425 
426     return 1;
427 }
428 
delete_cb(lv_draw_unit_t * draw_unit)429 static int32_t delete_cb(lv_draw_unit_t * draw_unit)
430 {
431     return 0;
432 }
433 
434 #if LV_DRAW_DMA2D_ASYNC
thread_cb(void * arg)435 static void thread_cb(void * arg)
436 {
437     lv_draw_dma2d_unit_t * u = arg;
438 
439     lv_thread_sync_init(&u->interrupt_signal);
440 
441     while(1) {
442 
443         do {
444             lv_thread_sync_wait(&u->interrupt_signal);
445         } while(u->task_act != NULL);
446 
447         post_transfer_tasks(u);
448         lv_draw_dispatch_request();
449     }
450 }
451 #endif
452 
453 #if !LV_DRAW_DMA2D_ASYNC
check_transfer_completion(void)454 static bool check_transfer_completion(void)
455 {
456     return !(DMA2D->CR & DMA2D_CR_START);
457 }
458 #endif
459 
post_transfer_tasks(lv_draw_dma2d_unit_t * u)460 static void post_transfer_tasks(lv_draw_dma2d_unit_t * u)
461 {
462 #if LV_DRAW_DMA2D_CACHE
463     lv_draw_dma2d_invalidate_cache(&u->writing_area);
464 #endif
465     u->task_act->state = LV_DRAW_TASK_STATE_READY;
466     u->task_act = NULL;
467 }
468 
469 #endif /*LV_USE_DRAW_DMA2D*/
470