1 /**
2  * @file lv_st_ltdc.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../../../lv_conf_internal.h"
11 #if LV_USE_ST_LTDC
12 
13 #include "lv_st_ltdc.h"
14 #include "../../../display/lv_display_private.h"
15 #include "../../../draw/sw/lv_draw_sw.h"
16 #include "ltdc.h"
17 
18 #if LV_ST_LTDC_USE_DMA2D_FLUSH
19     #if LV_USE_DRAW_DMA2D
20         #error cannot use LV_ST_LTDC_USE_DMA2D_FLUSH with LV_USE_DRAW_DMA2D
21     #endif /*LV_USE_DRAW_DMA2D*/
22 
23     #include "dma2d.h"
24 #endif /*LV_ST_LTDC_USE_DMA2D_FLUSH*/
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 
34 #if LV_USE_OS != LV_OS_NONE
35     typedef lv_thread_sync_t sync_t;
36 #else
37     typedef volatile bool sync_t;
38 #endif
39 
40 /**********************
41  *  STATIC PROTOTYPES
42  **********************/
43 
44 static lv_display_t * create(void * buf1, void * buf2, uint32_t buf_size, uint32_t layer_idx,
45                              lv_display_render_mode_t mode);
46 static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
47 static void flush_wait_cb(lv_display_t * disp);
48 static lv_color_format_t get_lv_cf_from_layer_cf(uint32_t cf);
49 static void reload_event_callback(LTDC_HandleTypeDef * hltdc);
50 
51 #if LV_ST_LTDC_USE_DMA2D_FLUSH
52     static void transfer_complete_callback(DMA2D_HandleTypeDef * hdma2d);
53     static uint32_t get_dma2d_output_cf_from_layer_cf(uint32_t cf);
54     static uint32_t get_dma2d_input_cf_from_lv_cf(uint32_t cf);
55 #endif
56 
57 /**********************
58  *  STATIC VARIABLES
59  **********************/
60 
61 static struct {
62     bool disp_flushed_in_flush_cb[MAX_LAYER];
63     sync_t sync[MAX_LAYER];
64     volatile bool layer_interrupt_is_owned[MAX_LAYER];
65 #if LV_ST_LTDC_USE_DMA2D_FLUSH
66     volatile uint32_t dma2d_interrupt_owner; /*layer_idx + 1, or 0 for none*/
67 #endif
68 } g_data;
69 
70 /**********************
71  *      MACROS
72  **********************/
73 
74 #if LV_USE_OS != LV_OS_NONE
75     #define SYNC_INIT(layer_idx) lv_thread_sync_init(&g_data.sync[layer_idx])
76     #define SYNC_WAIT(layer_idx) lv_thread_sync_wait(&g_data.sync[layer_idx])
77     #define SYNC_SIGNAL_ISR(layer_idx) lv_thread_sync_signal_isr(&g_data.sync[layer_idx])
78 #else
79     #define SYNC_INIT(layer_idx) do { g_data.sync[layer_idx] = false; } while(0)
80     #define SYNC_WAIT(layer_idx) do { while(!g_data.sync[layer_idx]); g_data.sync[layer_idx] = false; } while(0)
81     #define SYNC_SIGNAL_ISR(layer_idx) do { g_data.sync[layer_idx] = true; } while(0)
82 #endif
83 
84 /**********************
85  *   GLOBAL FUNCTIONS
86  **********************/
87 
lv_st_ltdc_create_direct(void * fb_adr_1,void * fb_adr_2,uint32_t layer_idx)88 lv_display_t * lv_st_ltdc_create_direct(void * fb_adr_1, void * fb_adr_2, uint32_t layer_idx)
89 {
90     return create(fb_adr_1, fb_adr_2, 0, layer_idx, LV_DISPLAY_RENDER_MODE_DIRECT);
91 }
92 
lv_st_ltdc_create_partial(void * render_buf_1,void * render_buf_2,uint32_t buf_size,uint32_t layer_idx)93 lv_display_t * lv_st_ltdc_create_partial(void * render_buf_1, void * render_buf_2, uint32_t buf_size,
94                                          uint32_t layer_idx)
95 {
96     return create(render_buf_1, render_buf_2, buf_size, layer_idx, LV_DISPLAY_RENDER_MODE_PARTIAL);
97 }
98 
99 /**********************
100  *   STATIC FUNCTIONS
101  **********************/
102 
create(void * buf1,void * buf2,uint32_t buf_size,uint32_t layer_idx,lv_display_render_mode_t mode)103 static lv_display_t * create(void * buf1, void * buf2, uint32_t buf_size, uint32_t layer_idx,
104                              lv_display_render_mode_t mode)
105 {
106     LTDC_LayerCfgTypeDef * layer_cfg = &hltdc.LayerCfg[layer_idx];
107     uint32_t layer_width = layer_cfg->ImageWidth;
108     uint32_t layer_height = layer_cfg->ImageHeight;
109     uint32_t layer_cf = layer_cfg->PixelFormat;
110     lv_color_format_t cf = get_lv_cf_from_layer_cf(layer_cf);
111 
112     lv_display_t * disp = lv_display_create(layer_width, layer_height);
113     lv_display_set_color_format(disp, cf);
114     lv_display_set_flush_cb(disp, flush_cb);
115     lv_display_set_flush_wait_cb(disp, flush_wait_cb);
116     lv_display_set_driver_data(disp, (void *)(uintptr_t)layer_idx);
117 
118     if(mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
119         uint32_t cf_size = lv_color_format_get_size(cf);
120         lv_display_set_buffers(disp, buf1, buf2, layer_width * layer_height * cf_size, LV_DISPLAY_RENDER_MODE_DIRECT);
121 
122         if(buf1 != NULL && buf2 != NULL) {
123             HAL_LTDC_RegisterCallback(&hltdc, HAL_LTDC_RELOAD_EVENT_CB_ID, reload_event_callback);
124             SYNC_INIT(layer_idx);
125         }
126     }
127     else {
128         lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
129 
130 #if LV_ST_LTDC_USE_DMA2D_FLUSH
131         hdma2d.XferCpltCallback = transfer_complete_callback;
132         SYNC_INIT(layer_idx);
133 #endif
134     }
135 
136     return disp;
137 }
138 
flush_cb(lv_display_t * disp,const lv_area_t * area,uint8_t * px_map)139 static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
140 {
141     uint32_t layer_idx = (uint32_t)(uintptr_t)lv_display_get_driver_data(disp);
142     g_data.disp_flushed_in_flush_cb[layer_idx] = false;
143 
144     if(disp->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
145         if(lv_display_is_double_buffered(disp) && lv_display_flush_is_last(disp)) {
146             HAL_LTDC_SetAddress_NoReload(&hltdc, (uint32_t)px_map, layer_idx);
147             g_data.layer_interrupt_is_owned[layer_idx] = true;
148             HAL_LTDC_Reload(&hltdc, LTDC_RELOAD_VERTICAL_BLANKING);
149         }
150         else {
151             g_data.disp_flushed_in_flush_cb[layer_idx] = true;
152         }
153     }
154     else {
155         LTDC_LayerCfgTypeDef * layer_cfg = &hltdc.LayerCfg[layer_idx];
156 
157         lv_color_format_t cf = lv_display_get_color_format(disp);
158         int32_t disp_width = disp->hor_res;
159 
160         uint8_t * fb = (uint8_t *) layer_cfg->FBStartAdress;
161         uint32_t px_size = lv_color_format_get_size(cf);
162         uint32_t fb_stride = px_size * disp_width;
163         lv_area_t rotated_area = *area;
164         lv_display_rotate_area(disp, &rotated_area);
165         uint8_t * first_pixel = fb + fb_stride * rotated_area.y1 + px_size * rotated_area.x1;
166 
167         int32_t area_width = lv_area_get_width(area);
168         int32_t area_height = lv_area_get_height(area);
169 
170         lv_display_rotation_t rotation = lv_display_get_rotation(disp);
171         if(rotation == LV_DISPLAY_ROTATION_0) {
172 #if LV_ST_LTDC_USE_DMA2D_FLUSH
173             uint32_t dma2d_input_cf = get_dma2d_input_cf_from_lv_cf(cf);
174             uint32_t dma2d_output_cf = get_dma2d_output_cf_from_layer_cf(layer_cfg->PixelFormat);
175 
176             while(DMA2D->CR & DMA2D_CR_START);
177             DMA2D->FGPFCCR = dma2d_input_cf;
178             DMA2D->FGMAR = (uint32_t)px_map;
179             DMA2D->FGOR = 0;
180             DMA2D->OPFCCR = dma2d_output_cf;
181             DMA2D->OMAR = (uint32_t)first_pixel;
182             DMA2D->OOR = disp_width - area_width;
183             DMA2D->NLR = (area_width << DMA2D_NLR_PL_Pos) | (area_height << DMA2D_NLR_NL_Pos);
184             g_data.dma2d_interrupt_owner = layer_idx + 1;
185             DMA2D->CR = DMA2D_CR_START | DMA2D_CR_TCIE | (0x1U << DMA2D_CR_MODE_Pos); /* memory-to-memory with PFC */
186 #else
187             uint32_t area_stride = px_size * area_width;
188             uint8_t * fb_p = first_pixel;
189             uint8_t * px_map_p = px_map;
190             for(int i = 0; i < area_height; i++) {
191                 lv_memcpy(fb_p, px_map_p, area_stride);
192                 fb_p += fb_stride;
193                 px_map_p += area_stride;
194             }
195             g_data.disp_flushed_in_flush_cb[layer_idx] = true;
196 #endif
197         }
198         else {
199             uint32_t area_stride = px_size * area_width;
200             lv_draw_sw_rotate(px_map, first_pixel, area_width, area_height, area_stride, fb_stride, rotation, cf);
201             g_data.disp_flushed_in_flush_cb[layer_idx] = true;
202         }
203     }
204 }
205 
flush_wait_cb(lv_display_t * disp)206 static void flush_wait_cb(lv_display_t * disp)
207 {
208     uint32_t layer_idx = (uint32_t)(uintptr_t)lv_display_get_driver_data(disp);
209     if(!g_data.disp_flushed_in_flush_cb[layer_idx]) {
210         SYNC_WAIT(layer_idx);
211     }
212 }
213 
get_lv_cf_from_layer_cf(uint32_t cf)214 static lv_color_format_t get_lv_cf_from_layer_cf(uint32_t cf)
215 {
216     switch(cf) {
217         case LTDC_PIXEL_FORMAT_ARGB8888:
218             return LV_COLOR_FORMAT_ARGB8888;
219         case LTDC_PIXEL_FORMAT_RGB888:
220             return LV_COLOR_FORMAT_RGB888;
221         case LTDC_PIXEL_FORMAT_RGB565:
222             return LV_COLOR_FORMAT_RGB565;
223         case LTDC_PIXEL_FORMAT_L8:
224             return LV_COLOR_FORMAT_L8;
225         case LTDC_PIXEL_FORMAT_AL88:
226             return LV_COLOR_FORMAT_AL88;
227         default:
228             LV_ASSERT_MSG(0, "the LTDC color format is not supported");
229     }
230 }
231 
reload_event_callback(LTDC_HandleTypeDef * hltdc)232 static void reload_event_callback(LTDC_HandleTypeDef * hltdc)
233 {
234     uint32_t i;
235     for(i = 0; i < MAX_LAYER; i++) {
236         if(g_data.layer_interrupt_is_owned[i]) {
237             g_data.layer_interrupt_is_owned[i] = false;
238             SYNC_SIGNAL_ISR(i);
239         }
240     }
241 }
242 
243 #if LV_ST_LTDC_USE_DMA2D_FLUSH
transfer_complete_callback(DMA2D_HandleTypeDef * hdma2d)244 static void transfer_complete_callback(DMA2D_HandleTypeDef * hdma2d)
245 {
246     DMA2D->IFCR = 0x3FU;
247     uint32_t owner = g_data.dma2d_interrupt_owner;
248     if(owner) {
249         g_data.dma2d_interrupt_owner = 0;
250         owner -= 1;
251         SYNC_SIGNAL_ISR(owner);
252     }
253 }
254 
get_dma2d_output_cf_from_layer_cf(uint32_t cf)255 static uint32_t get_dma2d_output_cf_from_layer_cf(uint32_t cf)
256 {
257     switch(cf) {
258         case LTDC_PIXEL_FORMAT_ARGB8888:
259             return DMA2D_OUTPUT_ARGB8888;
260         case LTDC_PIXEL_FORMAT_RGB888:
261             return DMA2D_OUTPUT_RGB888;
262         case LTDC_PIXEL_FORMAT_RGB565:
263             return DMA2D_OUTPUT_RGB565;
264         default:
265             LV_ASSERT_MSG(0, "DMA2D cannot output to the LTDC color format");
266     }
267 }
268 
get_dma2d_input_cf_from_lv_cf(uint32_t cf)269 static uint32_t get_dma2d_input_cf_from_lv_cf(uint32_t cf)
270 {
271     switch(cf) {
272         case LV_COLOR_FORMAT_ARGB8888:
273             return DMA2D_INPUT_ARGB8888;
274         case LV_COLOR_FORMAT_RGB888:
275             return DMA2D_INPUT_RGB888;
276         case LV_COLOR_FORMAT_RGB565:
277             return DMA2D_INPUT_RGB565;
278         case LV_COLOR_FORMAT_L8:
279             return DMA2D_INPUT_L8;
280         case LV_COLOR_FORMAT_AL88:
281             return DMA2D_INPUT_AL88;
282         case LV_COLOR_FORMAT_A8:
283             return DMA2D_INPUT_A8;
284         default:
285             LV_ASSERT_MSG(0, "the LVGL color format is not a DMA2D input color format");
286     }
287 }
288 #endif /*LV_ST_LTDC_USE_DMA2D_FLUSH*/
289 
290 #endif /*LV_USE_ST_LTDC*/
291