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