1 /*
2 * Copyright (c) 2022 Byte-Lab d.o.o. <dev@byte-lab.com>
3 * Copyright 2023 NXP
4 * Copyright (c) 2024 STMicroelectronics
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #define DT_DRV_COMPAT st_stm32_ltdc
10
11 #include <string.h>
12 #include <zephyr/device.h>
13 #include <zephyr/devicetree.h>
14 #include <stm32_ll_rcc.h>
15 #include <zephyr/drivers/display.h>
16 #include <zephyr/drivers/gpio.h>
17 #include <zephyr/drivers/pinctrl.h>
18 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
19 #include <zephyr/drivers/clock_control.h>
20 #include <zephyr/drivers/reset.h>
21 #include <zephyr/pm/device.h>
22 #include <zephyr/sys/barrier.h>
23 #include <zephyr/cache.h>
24
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(display_stm32_ltdc, CONFIG_DISPLAY_LOG_LEVEL);
27
28 /* Horizontal synchronization pulse polarity */
29 #define LTDC_HSPOL_ACTIVE_LOW 0x00000000
30 #define LTDC_HSPOL_ACTIVE_HIGH 0x80000000
31
32 /* Vertical synchronization pulse polarity */
33 #define LTDC_VSPOL_ACTIVE_LOW 0x00000000
34 #define LTDC_VSPOL_ACTIVE_HIGH 0x40000000
35
36 /* Data enable pulse polarity */
37 #define LTDC_DEPOL_ACTIVE_LOW 0x00000000
38 #define LTDC_DEPOL_ACTIVE_HIGH 0x20000000
39
40 /* Pixel clock polarity */
41 #define LTDC_PCPOL_ACTIVE_LOW 0x00000000
42 #define LTDC_PCPOL_ACTIVE_HIGH 0x10000000
43
44 #if CONFIG_STM32_LTDC_ARGB8888
45 #define STM32_LTDC_INIT_PIXEL_SIZE 4u
46 #define STM32_LTDC_INIT_PIXEL_FORMAT LTDC_PIXEL_FORMAT_ARGB8888
47 #define DISPLAY_INIT_PIXEL_FORMAT PIXEL_FORMAT_ARGB_8888
48 #elif CONFIG_STM32_LTDC_RGB888
49 #define STM32_LTDC_INIT_PIXEL_SIZE 3u
50 #define STM32_LTDC_INIT_PIXEL_FORMAT LTDC_PIXEL_FORMAT_RGB888
51 #define DISPLAY_INIT_PIXEL_FORMAT PIXEL_FORMAT_RGB_888
52 #elif CONFIG_STM32_LTDC_RGB565
53 #define STM32_LTDC_INIT_PIXEL_SIZE 2u
54 #define STM32_LTDC_INIT_PIXEL_FORMAT LTDC_PIXEL_FORMAT_RGB565
55 #define DISPLAY_INIT_PIXEL_FORMAT PIXEL_FORMAT_RGB_565
56 #else
57 #error "Invalid LTDC pixel format chosen"
58 #endif
59
60 struct display_stm32_ltdc_data {
61 LTDC_HandleTypeDef hltdc;
62 enum display_pixel_format current_pixel_format;
63 uint8_t current_pixel_size;
64 uint8_t *frame_buffer;
65 uint32_t frame_buffer_len;
66 const uint8_t *pend_buf;
67 const uint8_t *front_buf;
68 struct k_sem sem;
69 };
70
71 struct display_stm32_ltdc_config {
72 uint32_t width;
73 uint32_t height;
74 struct gpio_dt_spec disp_on_gpio;
75 struct gpio_dt_spec bl_ctrl_gpio;
76 struct stm32_pclken pclken;
77 const struct reset_dt_spec reset;
78 const struct pinctrl_dev_config *pctrl;
79 void (*irq_config_func)(const struct device *dev);
80 const struct device *display_controller;
81 };
82
stm32_ltdc_global_isr(const struct device * dev)83 static void stm32_ltdc_global_isr(const struct device *dev)
84 {
85 struct display_stm32_ltdc_data *data = dev->data;
86
87 if (__HAL_LTDC_GET_FLAG(&data->hltdc, LTDC_FLAG_LI) &&
88 __HAL_LTDC_GET_IT_SOURCE(&data->hltdc, LTDC_IT_LI)) {
89 if (data->front_buf != data->pend_buf) {
90 data->front_buf = data->pend_buf;
91
92 LTDC_LAYER(&data->hltdc, LTDC_LAYER_1)->CFBAR = (uint32_t)data->front_buf;
93 __HAL_LTDC_RELOAD_CONFIG(&data->hltdc);
94
95 k_sem_give(&data->sem);
96 }
97
98 __HAL_LTDC_CLEAR_FLAG(&data->hltdc, LTDC_FLAG_LI);
99 }
100 }
101
stm32_ltdc_set_pixel_format(const struct device * dev,const enum display_pixel_format format)102 static int stm32_ltdc_set_pixel_format(const struct device *dev,
103 const enum display_pixel_format format)
104 {
105 int err;
106 struct display_stm32_ltdc_data *data = dev->data;
107
108 switch (format) {
109 case PIXEL_FORMAT_RGB_565:
110 err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_RGB565, 0);
111 data->current_pixel_format = PIXEL_FORMAT_RGB_565;
112 data->current_pixel_size = 2u;
113 break;
114 case PIXEL_FORMAT_RGB_888:
115 err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_RGB888, 0);
116 data->current_pixel_format = PIXEL_FORMAT_RGB_888;
117 data->current_pixel_size = 3u;
118 break;
119 case PIXEL_FORMAT_ARGB_8888:
120 err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_ARGB8888, 0);
121 data->current_pixel_format = PIXEL_FORMAT_ARGB_8888;
122 data->current_pixel_size = 4u;
123 default:
124 err = -ENOTSUP;
125 break;
126 }
127
128 return err;
129 }
130
stm32_ltdc_set_orientation(const struct device * dev,const enum display_orientation orientation)131 static int stm32_ltdc_set_orientation(const struct device *dev,
132 const enum display_orientation orientation)
133 {
134 ARG_UNUSED(dev);
135
136 if (orientation == DISPLAY_ORIENTATION_NORMAL) {
137 return 0;
138 }
139
140 return -ENOTSUP;
141 }
142
stm32_ltdc_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)143 static void stm32_ltdc_get_capabilities(const struct device *dev,
144 struct display_capabilities *capabilities)
145 {
146 struct display_stm32_ltdc_data *data = dev->data;
147
148 memset(capabilities, 0, sizeof(struct display_capabilities));
149
150 capabilities->x_resolution = data->hltdc.LayerCfg[0].WindowX1 -
151 data->hltdc.LayerCfg[0].WindowX0;
152 capabilities->y_resolution = data->hltdc.LayerCfg[0].WindowY1 -
153 data->hltdc.LayerCfg[0].WindowY0;
154 capabilities->supported_pixel_formats = PIXEL_FORMAT_ARGB_8888 |
155 PIXEL_FORMAT_RGB_888 |
156 PIXEL_FORMAT_RGB_565;
157 capabilities->screen_info = 0;
158
159 capabilities->current_pixel_format = data->current_pixel_format;
160 capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL;
161 }
162
stm32_ltdc_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)163 static int stm32_ltdc_write(const struct device *dev, const uint16_t x,
164 const uint16_t y,
165 const struct display_buffer_descriptor *desc,
166 const void *buf)
167 {
168 const struct display_stm32_ltdc_config *config = dev->config;
169 struct display_stm32_ltdc_data *data = dev->data;
170 uint8_t *dst = NULL;
171 const uint8_t *pend_buf = NULL;
172 const uint8_t *src = buf;
173 uint16_t row;
174
175 if ((x == 0) && (y == 0) &&
176 (desc->width == config->width) &&
177 (desc->height == config->height) &&
178 (desc->pitch == desc->width)) {
179 /* Use buf as ltdc frame buffer directly if it length same as ltdc frame buffer. */
180 pend_buf = buf;
181 } else {
182 if (CONFIG_STM32_LTDC_FB_NUM == 0) {
183 LOG_ERR("Partial write requires internal frame buffer");
184 return -ENOTSUP;
185 }
186
187 dst = data->frame_buffer;
188
189 if (CONFIG_STM32_LTDC_FB_NUM == 2) {
190 if (data->front_buf == data->frame_buffer) {
191 dst = data->frame_buffer + data->frame_buffer_len;
192 }
193
194 memcpy(dst, data->front_buf, data->frame_buffer_len);
195 }
196
197 pend_buf = dst;
198
199 /* dst = pointer to upper left pixel of the rectangle
200 * to be updated in frame buffer.
201 */
202 dst += (x * data->current_pixel_size);
203 dst += (y * config->width * data->current_pixel_size);
204
205 for (row = 0; row < desc->height; row++) {
206 (void) memcpy(dst, src, desc->width * data->current_pixel_size);
207 sys_cache_data_flush_range(dst, desc->width * data->current_pixel_size);
208 dst += (config->width * data->current_pixel_size);
209 src += (desc->pitch * data->current_pixel_size);
210 }
211
212 }
213
214 if (data->front_buf == pend_buf) {
215 return 0;
216 }
217
218 k_sem_reset(&data->sem);
219
220 data->pend_buf = pend_buf;
221
222 k_sem_take(&data->sem, K_FOREVER);
223
224 return 0;
225 }
226
stm32_ltdc_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)227 static int stm32_ltdc_read(const struct device *dev, const uint16_t x,
228 const uint16_t y,
229 const struct display_buffer_descriptor *desc,
230 void *buf)
231 {
232 const struct display_stm32_ltdc_config *config = dev->config;
233 struct display_stm32_ltdc_data *data = dev->data;
234 uint8_t *dst = buf;
235 const uint8_t *src = data->front_buf;
236 uint16_t row;
237
238 /* src = pointer to upper left pixel of the rectangle to be read from frame buffer */
239 src += (x * data->current_pixel_size);
240 src += (y * config->width * data->current_pixel_size);
241
242 for (row = 0; row < desc->height; row++) {
243 (void) memcpy(dst, src, desc->width * data->current_pixel_size);
244 sys_cache_data_flush_range(dst, desc->width * data->current_pixel_size);
245 src += (config->width * data->current_pixel_size);
246 dst += (desc->pitch * data->current_pixel_size);
247 }
248
249 return 0;
250 }
251
stm32_ltdc_get_framebuffer(const struct device * dev)252 static void *stm32_ltdc_get_framebuffer(const struct device *dev)
253 {
254 struct display_stm32_ltdc_data *data = dev->data;
255
256 return ((void *)data->front_buf);
257 }
258
stm32_ltdc_display_blanking_off(const struct device * dev)259 static int stm32_ltdc_display_blanking_off(const struct device *dev)
260 {
261 const struct display_stm32_ltdc_config *config = dev->config;
262 const struct device *display_dev = config->display_controller;
263
264 /* Panel controller's phandle is not passed to LTDC in devicetree */
265 if (display_dev == NULL) {
266 LOG_ERR("There is no panel controller to forward blanking_off call to");
267 return -ENOSYS;
268 }
269
270 if (!device_is_ready(display_dev)) {
271 LOG_ERR("Display device %s not ready", display_dev->name);
272 return -ENODEV;
273 }
274
275 return display_blanking_off(display_dev);
276 }
277
stm32_ltdc_display_blanking_on(const struct device * dev)278 static int stm32_ltdc_display_blanking_on(const struct device *dev)
279 {
280 const struct display_stm32_ltdc_config *config = dev->config;
281 const struct device *display_dev = config->display_controller;
282
283 /* Panel controller's phandle is not passed to LTDC in devicetree */
284 if (display_dev == NULL) {
285 LOG_ERR("There is no panel controller to forward blanking_on call to");
286 return -ENOSYS;
287 }
288
289 if (!device_is_ready(display_dev)) {
290 LOG_ERR("Display device %s not ready", display_dev->name);
291 return -ENODEV;
292 }
293
294 return display_blanking_on(display_dev);
295 }
296
stm32_ltdc_init(const struct device * dev)297 static int stm32_ltdc_init(const struct device *dev)
298 {
299 int err;
300 const struct display_stm32_ltdc_config *config = dev->config;
301 struct display_stm32_ltdc_data *data = dev->data;
302
303 /* Configure and set display on/off GPIO */
304 if (config->disp_on_gpio.port) {
305 err = gpio_pin_configure_dt(&config->disp_on_gpio, GPIO_OUTPUT_ACTIVE);
306 if (err < 0) {
307 LOG_ERR("Configuration of display on/off control GPIO failed");
308 return err;
309 }
310 }
311
312 /* Configure and set display backlight control GPIO */
313 if (config->bl_ctrl_gpio.port) {
314 err = gpio_pin_configure_dt(&config->bl_ctrl_gpio, GPIO_OUTPUT_ACTIVE);
315 if (err < 0) {
316 LOG_ERR("Configuration of display backlight control GPIO failed");
317 return err;
318 }
319 }
320
321 /* Configure DT provided pins */
322 if (!IS_ENABLED(CONFIG_MIPI_DSI)) {
323 err = pinctrl_apply_state(config->pctrl, PINCTRL_STATE_DEFAULT);
324 if (err < 0) {
325 LOG_ERR("LTDC pinctrl setup failed");
326 return err;
327 }
328 }
329
330 if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) {
331 LOG_ERR("clock control device not ready");
332 return -ENODEV;
333 }
334
335 /* Turn on LTDC peripheral clock */
336 err = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
337 (clock_control_subsys_t) &config->pclken);
338 if (err < 0) {
339 LOG_ERR("Could not enable LTDC peripheral clock");
340 return err;
341 }
342
343 #if defined(CONFIG_SOC_SERIES_STM32F4X)
344 LL_RCC_PLLSAI_Disable();
345 LL_RCC_PLLSAI_ConfigDomain_LTDC(LL_RCC_PLLSOURCE_HSE,
346 LL_RCC_PLLSAIM_DIV_8,
347 192,
348 LL_RCC_PLLSAIR_DIV_4,
349 LL_RCC_PLLSAIDIVR_DIV_8);
350
351 LL_RCC_PLLSAI_Enable();
352 while (LL_RCC_PLLSAI_IsReady() != 1) {
353 }
354 #endif
355
356 #if defined(CONFIG_SOC_SERIES_STM32F7X)
357 LL_RCC_PLLSAI_Disable();
358 LL_RCC_PLLSAI_ConfigDomain_LTDC(LL_RCC_PLLSOURCE_HSE,
359 LL_RCC_PLLM_DIV_25,
360 384,
361 LL_RCC_PLLSAIR_DIV_5,
362 LL_RCC_PLLSAIDIVR_DIV_8);
363
364 LL_RCC_PLLSAI_Enable();
365 while (LL_RCC_PLLSAI_IsReady() != 1) {
366 }
367 #endif
368
369 /* reset LTDC peripheral */
370 (void)reset_line_toggle_dt(&config->reset);
371
372 data->current_pixel_format = DISPLAY_INIT_PIXEL_FORMAT;
373 data->current_pixel_size = STM32_LTDC_INIT_PIXEL_SIZE;
374
375 k_sem_init(&data->sem, 0, 1);
376
377 config->irq_config_func(dev);
378
379 #ifdef CONFIG_STM32_LTDC_DISABLE_FMC_BANK1
380 /* Clear MBKEN and MTYP[1:0] bits. */
381 #ifdef CONFIG_SOC_SERIES_STM32F7X
382 FMC_Bank1->BTCR[0] &= ~(0x0000000D);
383 #else /* CONFIG_SOC_SERIES_STM32H7X */
384 FMC_Bank1_R->BTCR[0] &= ~(0x0000000D);
385 #endif
386 #endif /* CONFIG_STM32_LTDC_DISABLE_FMC_BANK1 */
387
388 /* Initialise the LTDC peripheral */
389 err = HAL_LTDC_Init(&data->hltdc);
390 if (err != HAL_OK) {
391 return err;
392 }
393
394 /* Configure layer 1 (only one layer is used) */
395 /* LTDC starts fetching pixels and sending them to display after this call */
396 err = HAL_LTDC_ConfigLayer(&data->hltdc, &data->hltdc.LayerCfg[0], LTDC_LAYER_1);
397 if (err != HAL_OK) {
398 return err;
399 }
400
401 /* Disable layer 2, since it not used */
402 __HAL_LTDC_LAYER_DISABLE(&data->hltdc, LTDC_LAYER_2);
403
404 /* Set the line interrupt position */
405 LTDC->LIPCR = 0U;
406
407 __HAL_LTDC_CLEAR_FLAG(&data->hltdc, LTDC_FLAG_LI);
408 __HAL_LTDC_ENABLE_IT(&data->hltdc, LTDC_IT_LI);
409
410 return 0;
411 }
412
413 #ifdef CONFIG_PM_DEVICE
stm32_ltdc_suspend(const struct device * dev)414 static int stm32_ltdc_suspend(const struct device *dev)
415 {
416 const struct display_stm32_ltdc_config *config = dev->config;
417 int err;
418
419 /* Turn off disp_en (if its GPIO is defined in device tree) */
420 if (config->disp_on_gpio.port) {
421 err = gpio_pin_set_dt(&config->disp_on_gpio, 0);
422 if (err < 0) {
423 return err;
424 }
425 }
426
427 /* Turn off backlight (if its GPIO is defined in device tree) */
428 if (config->bl_ctrl_gpio.port) {
429 err = gpio_pin_set_dt(&config->bl_ctrl_gpio, 0);
430 if (err < 0) {
431 return err;
432 }
433 }
434
435 /* Reset LTDC peripheral registers */
436 (void)reset_line_toggle_dt(&config->reset);
437
438 /* Turn off LTDC peripheral clock */
439 err = clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
440 (clock_control_subsys_t) &config->pclken);
441
442 return err;
443 }
444
stm32_ltdc_pm_action(const struct device * dev,enum pm_device_action action)445 static int stm32_ltdc_pm_action(const struct device *dev,
446 enum pm_device_action action)
447 {
448 int err;
449
450 switch (action) {
451 case PM_DEVICE_ACTION_RESUME:
452 err = stm32_ltdc_init(dev);
453 break;
454 case PM_DEVICE_ACTION_SUSPEND:
455 err = stm32_ltdc_suspend(dev);
456 break;
457 default:
458 return -ENOTSUP;
459 }
460
461 if (err < 0) {
462 LOG_ERR("%s: failed to set power mode", dev->name);
463 }
464
465 return err;
466 }
467 #endif /* CONFIG_PM_DEVICE */
468
469 static DEVICE_API(display, stm32_ltdc_display_api) = {
470 .write = stm32_ltdc_write,
471 .read = stm32_ltdc_read,
472 .get_framebuffer = stm32_ltdc_get_framebuffer,
473 .get_capabilities = stm32_ltdc_get_capabilities,
474 .set_pixel_format = stm32_ltdc_set_pixel_format,
475 .set_orientation = stm32_ltdc_set_orientation,
476 .blanking_off = stm32_ltdc_display_blanking_off,
477 .blanking_on = stm32_ltdc_display_blanking_on,
478 };
479
480 #if DT_INST_NODE_HAS_PROP(0, ext_sdram)
481
482 #if DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram1))
483 #define FRAME_BUFFER_SECTION __stm32_sdram1_section
484 #elif DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram2))
485 #define FRAME_BUFFER_SECTION __stm32_sdram2_section
486 #else
487 #error "LTDC ext-sdram property in device tree does not reference SDRAM1 or SDRAM2 node"
488 #define FRAME_BUFFER_SECTION
489 #endif /* DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram1)) */
490
491 #else
492 #define FRAME_BUFFER_SECTION
493 #endif /* DT_INST_NODE_HAS_PROP(0, ext_sdram) */
494
495 #ifdef CONFIG_MIPI_DSI
496 #define STM32_LTDC_DEVICE_PINCTRL_INIT(n)
497 #define STM32_LTDC_DEVICE_PINCTRL_GET(n) (NULL)
498 #else
499 #define STM32_LTDC_DEVICE_PINCTRL_INIT(n) PINCTRL_DT_INST_DEFINE(n)
500 #define STM32_LTDC_DEVICE_PINCTRL_GET(n) PINCTRL_DT_INST_DEV_CONFIG_GET(n)
501 #endif
502
503 #define STM32_LTDC_FRAME_BUFFER_LEN(inst) \
504 (STM32_LTDC_INIT_PIXEL_SIZE * DT_INST_PROP(inst, height) * DT_INST_PROP(inst, width)) \
505
506 #define STM32_LTDC_DEVICE(inst) \
507 STM32_LTDC_DEVICE_PINCTRL_INIT(inst); \
508 PM_DEVICE_DT_INST_DEFINE(inst, stm32_ltdc_pm_action); \
509 static void stm32_ltdc_irq_config_func_##inst(const struct device *dev) \
510 { \
511 IRQ_CONNECT(DT_INST_IRQN(inst), \
512 DT_INST_IRQ(inst, priority), \
513 stm32_ltdc_global_isr, \
514 DEVICE_DT_INST_GET(inst), \
515 0); \
516 irq_enable(DT_INST_IRQN(inst)); \
517 } \
518 /* frame buffer aligned to cache line width for optimal cache flushing */ \
519 FRAME_BUFFER_SECTION static uint8_t __aligned(32) \
520 frame_buffer_##inst[CONFIG_STM32_LTDC_FB_NUM * \
521 STM32_LTDC_FRAME_BUFFER_LEN(inst)]; \
522 static struct display_stm32_ltdc_data stm32_ltdc_data_##inst = { \
523 .frame_buffer = frame_buffer_##inst, \
524 .frame_buffer_len = STM32_LTDC_FRAME_BUFFER_LEN(inst), \
525 .front_buf = frame_buffer_##inst, \
526 .pend_buf = frame_buffer_##inst, \
527 .hltdc = { \
528 .Instance = (LTDC_TypeDef *) DT_INST_REG_ADDR(inst), \
529 .Init = { \
530 .HSPolarity = (DT_PROP(DT_INST_CHILD(inst, display_timings), \
531 hsync_active) ? \
532 LTDC_HSPOL_ACTIVE_HIGH : LTDC_HSPOL_ACTIVE_LOW),\
533 .VSPolarity = (DT_PROP(DT_INST_CHILD(inst, \
534 display_timings), vsync_active) ? \
535 LTDC_VSPOL_ACTIVE_HIGH : LTDC_VSPOL_ACTIVE_LOW),\
536 .DEPolarity = (DT_PROP(DT_INST_CHILD(inst, \
537 display_timings), de_active) ? \
538 LTDC_DEPOL_ACTIVE_HIGH : LTDC_DEPOL_ACTIVE_LOW),\
539 .PCPolarity = (DT_PROP(DT_INST_CHILD(inst, \
540 display_timings), pixelclk_active) ? \
541 LTDC_PCPOL_ACTIVE_HIGH : LTDC_PCPOL_ACTIVE_LOW),\
542 .HorizontalSync = DT_PROP(DT_INST_CHILD(inst, \
543 display_timings), hsync_len) - 1, \
544 .VerticalSync = DT_PROP(DT_INST_CHILD(inst, \
545 display_timings), vsync_len) - 1, \
546 .AccumulatedHBP = DT_PROP(DT_INST_CHILD(inst, \
547 display_timings), hback_porch) + \
548 DT_PROP(DT_INST_CHILD(inst, \
549 display_timings), hsync_len) - 1, \
550 .AccumulatedVBP = DT_PROP(DT_INST_CHILD(inst, \
551 display_timings), vback_porch) + \
552 DT_PROP(DT_INST_CHILD(inst, \
553 display_timings), vsync_len) - 1, \
554 .AccumulatedActiveW = DT_PROP(DT_INST_CHILD(inst, \
555 display_timings), hback_porch) + \
556 DT_PROP(DT_INST_CHILD(inst, \
557 display_timings), hsync_len) + \
558 DT_INST_PROP(inst, width) - 1, \
559 .AccumulatedActiveH = DT_PROP(DT_INST_CHILD(inst, \
560 display_timings), vback_porch) + \
561 DT_PROP(DT_INST_CHILD(inst, \
562 display_timings), vsync_len) + \
563 DT_INST_PROP(inst, height) - 1, \
564 .TotalWidth = DT_PROP(DT_INST_CHILD(inst, \
565 display_timings), hback_porch) + \
566 DT_PROP(DT_INST_CHILD(inst, \
567 display_timings), hsync_len) + \
568 DT_INST_PROP(inst, width) + \
569 DT_PROP(DT_INST_CHILD(inst, \
570 display_timings), hfront_porch) - 1, \
571 .TotalHeigh = DT_PROP(DT_INST_CHILD(inst, \
572 display_timings), vback_porch) + \
573 DT_PROP(DT_INST_CHILD(inst, \
574 display_timings), vsync_len) + \
575 DT_INST_PROP(inst, height) + \
576 DT_PROP(DT_INST_CHILD(inst, \
577 display_timings), vfront_porch) - 1, \
578 .Backcolor.Red = \
579 DT_INST_PROP_OR(inst, def_back_color_red, 0xFF), \
580 .Backcolor.Green = \
581 DT_INST_PROP_OR(inst, def_back_color_green, 0xFF), \
582 .Backcolor.Blue = \
583 DT_INST_PROP_OR(inst, def_back_color_blue, 0xFF), \
584 }, \
585 .LayerCfg[0] = { \
586 .WindowX0 = DT_INST_PROP_OR(inst, window0_x0, 0), \
587 .WindowX1 = DT_INST_PROP_OR(inst, window0_x1, \
588 DT_INST_PROP(inst, width)), \
589 .WindowY0 = DT_INST_PROP_OR(inst, window0_y0, 0), \
590 .WindowY1 = DT_INST_PROP_OR(inst, window0_y1, \
591 DT_INST_PROP(inst, height)), \
592 .PixelFormat = STM32_LTDC_INIT_PIXEL_FORMAT, \
593 .Alpha = 255, \
594 .Alpha0 = 0, \
595 .BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA, \
596 .BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA, \
597 .FBStartAdress = (uint32_t) frame_buffer_##inst, \
598 .ImageWidth = DT_INST_PROP(inst, width), \
599 .ImageHeight = DT_INST_PROP(inst, height), \
600 .Backcolor.Red = \
601 DT_INST_PROP_OR(inst, def_back_color_red, 0xFF), \
602 .Backcolor.Green = \
603 DT_INST_PROP_OR(inst, def_back_color_green, 0xFF), \
604 .Backcolor.Blue = \
605 DT_INST_PROP_OR(inst, def_back_color_blue, 0xFF), \
606 }, \
607 }, \
608 }; \
609 static const struct display_stm32_ltdc_config stm32_ltdc_config_##inst = { \
610 .width = DT_INST_PROP(inst, width), \
611 .height = DT_INST_PROP(inst, height), \
612 .disp_on_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, disp_on_gpios), \
613 (GPIO_DT_SPEC_INST_GET(inst, disp_on_gpios)), ({ 0 })), \
614 .bl_ctrl_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, bl_ctrl_gpios), \
615 (GPIO_DT_SPEC_INST_GET(inst, bl_ctrl_gpios)), ({ 0 })), \
616 .reset = RESET_DT_SPEC_INST_GET(0), \
617 .pclken = { \
618 .enr = DT_INST_CLOCKS_CELL(inst, bits), \
619 .bus = DT_INST_CLOCKS_CELL(inst, bus) \
620 }, \
621 .pctrl = STM32_LTDC_DEVICE_PINCTRL_GET(inst), \
622 .irq_config_func = stm32_ltdc_irq_config_func_##inst, \
623 .display_controller = DEVICE_DT_GET_OR_NULL( \
624 DT_INST_PHANDLE(inst, display_controller)), \
625 }; \
626 DEVICE_DT_INST_DEFINE(inst, \
627 &stm32_ltdc_init, \
628 PM_DEVICE_DT_INST_GET(inst), \
629 &stm32_ltdc_data_##inst, \
630 &stm32_ltdc_config_##inst, \
631 POST_KERNEL, \
632 CONFIG_DISPLAY_INIT_PRIORITY, \
633 &stm32_ltdc_display_api);
634
635 DT_INST_FOREACH_STATUS_OKAY(STM32_LTDC_DEVICE)
636