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