|  | /* | 
|  | * Copyright (c) 2022 Byte-Lab d.o.o. <dev@byte-lab.com> | 
|  | * Copyright 2023 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT st_stm32_ltdc | 
|  |  | 
|  | #include <string.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/devicetree.h> | 
|  | #include <stm32_ll_rcc.h> | 
|  | #include <zephyr/drivers/display.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/drivers/clock_control/stm32_clock_control.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/pm/device.h> | 
|  | #include <zephyr/sys/barrier.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(display_stm32_ltdc, CONFIG_DISPLAY_LOG_LEVEL); | 
|  |  | 
|  | /* Horizontal synchronization pulse polarity */ | 
|  | #define LTDC_HSPOL_ACTIVE_LOW     0x00000000 | 
|  | #define LTDC_HSPOL_ACTIVE_HIGH    0x80000000 | 
|  |  | 
|  | /* Vertical synchronization pulse polarity */ | 
|  | #define LTDC_VSPOL_ACTIVE_LOW     0x00000000 | 
|  | #define LTDC_VSPOL_ACTIVE_HIGH    0x40000000 | 
|  |  | 
|  | /* Data enable pulse polarity */ | 
|  | #define LTDC_DEPOL_ACTIVE_LOW     0x00000000 | 
|  | #define LTDC_DEPOL_ACTIVE_HIGH    0x20000000 | 
|  |  | 
|  | /* Pixel clock polarity */ | 
|  | #define LTDC_PCPOL_ACTIVE_LOW     0x00000000 | 
|  | #define LTDC_PCPOL_ACTIVE_HIGH    0x10000000 | 
|  |  | 
|  | #if CONFIG_STM32_LTDC_ARGB8888 | 
|  | #define STM32_LTDC_INIT_PIXEL_SIZE	4u | 
|  | #define STM32_LTDC_INIT_PIXEL_FORMAT	LTDC_PIXEL_FORMAT_ARGB8888 | 
|  | #define DISPLAY_INIT_PIXEL_FORMAT	PIXEL_FORMAT_ARGB_8888 | 
|  | #elif CONFIG_STM32_LTDC_RGB888 | 
|  | #define STM32_LTDC_INIT_PIXEL_SIZE	3u | 
|  | #define STM32_LTDC_INIT_PIXEL_FORMAT	LTDC_PIXEL_FORMAT_RGB888 | 
|  | #define DISPLAY_INIT_PIXEL_FORMAT	PIXEL_FORMAT_RGB_888 | 
|  | #elif CONFIG_STM32_LTDC_RGB565 | 
|  | #define STM32_LTDC_INIT_PIXEL_SIZE	2u | 
|  | #define STM32_LTDC_INIT_PIXEL_FORMAT	LTDC_PIXEL_FORMAT_RGB565 | 
|  | #define DISPLAY_INIT_PIXEL_FORMAT	PIXEL_FORMAT_RGB_565 | 
|  | #else | 
|  | #error "Invalid LTDC pixel format chosen" | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_HAS_CMSIS_CORE_M) | 
|  | #include <cmsis_core.h> | 
|  |  | 
|  | #if __DCACHE_PRESENT == 1 | 
|  | #define CACHE_INVALIDATE(addr, size)	SCB_InvalidateDCache_by_Addr((addr), (size)) | 
|  | #define CACHE_CLEAN(addr, size)		SCB_CleanDCache_by_Addr((addr), (size)) | 
|  | #else | 
|  | #define CACHE_INVALIDATE(addr, size) | 
|  | #define CACHE_CLEAN(addr, size)		barrier_dsync_fence_full(); | 
|  | #endif /* __DCACHE_PRESENT == 1 */ | 
|  |  | 
|  | #else | 
|  | #define CACHE_INVALIDATE(addr, size) | 
|  | #define CACHE_CLEAN(addr, size) | 
|  | #endif /* CONFIG_HAS_CMSIS_CORE_M */ | 
|  |  | 
|  | struct display_stm32_ltdc_data { | 
|  | LTDC_HandleTypeDef hltdc; | 
|  | enum display_pixel_format current_pixel_format; | 
|  | uint8_t current_pixel_size; | 
|  | uint8_t *frame_buffer; | 
|  | }; | 
|  |  | 
|  | struct display_stm32_ltdc_config { | 
|  | uint32_t width; | 
|  | uint32_t height; | 
|  | struct gpio_dt_spec disp_on_gpio; | 
|  | struct gpio_dt_spec bl_ctrl_gpio; | 
|  | struct stm32_pclken pclken; | 
|  | const struct pinctrl_dev_config *pctrl; | 
|  | }; | 
|  |  | 
|  | static int stm32_ltdc_blanking_on(const struct device *dev) | 
|  | { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_blanking_off(const struct device *dev) | 
|  | { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | static void *stm32_ltdc_get_framebuffer(const struct device *dev) | 
|  | { | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  |  | 
|  | return (void *) data->frame_buffer; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_set_brightness(const struct device *dev, | 
|  | const uint8_t brightness) | 
|  | { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_set_contrast(const struct device *dev, | 
|  | const uint8_t contrast) | 
|  | { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_set_pixel_format(const struct device *dev, | 
|  | const enum display_pixel_format format) | 
|  | { | 
|  | int err; | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  |  | 
|  | switch (format) { | 
|  | case PIXEL_FORMAT_RGB_565: | 
|  | err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_RGB565, 0); | 
|  | data->current_pixel_format = PIXEL_FORMAT_RGB_565; | 
|  | data->current_pixel_size = 2u; | 
|  | break; | 
|  | case PIXEL_FORMAT_RGB_888: | 
|  | err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_RGB888, 0); | 
|  | data->current_pixel_format = PIXEL_FORMAT_RGB_888; | 
|  | data->current_pixel_size = 3u; | 
|  | break; | 
|  | case PIXEL_FORMAT_ARGB_8888: | 
|  | err = HAL_LTDC_SetPixelFormat(&data->hltdc, LTDC_PIXEL_FORMAT_ARGB8888, 0); | 
|  | data->current_pixel_format = PIXEL_FORMAT_ARGB_8888; | 
|  | data->current_pixel_size = 4u; | 
|  | default: | 
|  | err = -ENOTSUP; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_set_orientation(const struct device *dev, | 
|  | const enum display_orientation orientation) | 
|  | { | 
|  | ARG_UNUSED(dev); | 
|  |  | 
|  | if (orientation == DISPLAY_ORIENTATION_NORMAL) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | static void stm32_ltdc_get_capabilities(const struct device *dev, | 
|  | struct display_capabilities *capabilities) | 
|  | { | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  |  | 
|  | memset(capabilities, 0, sizeof(struct display_capabilities)); | 
|  |  | 
|  | capabilities->x_resolution = data->hltdc.LayerCfg[0].WindowX1 - | 
|  | data->hltdc.LayerCfg[0].WindowX0; | 
|  | capabilities->y_resolution = data->hltdc.LayerCfg[0].WindowY1 - | 
|  | data->hltdc.LayerCfg[0].WindowY0; | 
|  | capabilities->supported_pixel_formats = PIXEL_FORMAT_ARGB_8888 | | 
|  | PIXEL_FORMAT_RGB_888 | | 
|  | PIXEL_FORMAT_RGB_565; | 
|  | capabilities->screen_info = 0; | 
|  |  | 
|  | capabilities->current_pixel_format = data->current_pixel_format; | 
|  | capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_write(const struct device *dev, const uint16_t x, | 
|  | const uint16_t y, | 
|  | const struct display_buffer_descriptor *desc, | 
|  | const void *buf) | 
|  | { | 
|  | const struct display_stm32_ltdc_config *config = dev->config; | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  | uint8_t *dst = data->frame_buffer; | 
|  | const uint8_t *src = buf; | 
|  | uint16_t row; | 
|  |  | 
|  | /* dst = pointer to upper left pixel of the rectangle to be updated in frame buffer */ | 
|  | dst += (x * data->current_pixel_size); | 
|  | dst += (y * config->width * data->current_pixel_size); | 
|  |  | 
|  | for (row = 0; row < desc->height; row++) { | 
|  | (void) memcpy(dst, src, desc->width * data->current_pixel_size); | 
|  | CACHE_CLEAN(dst, desc->width * data->current_pixel_size); | 
|  | dst += (config->width * data->current_pixel_size); | 
|  | src += (desc->pitch * data->current_pixel_size); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_read(const struct device *dev, const uint16_t x, | 
|  | const uint16_t y, | 
|  | const struct display_buffer_descriptor *desc, | 
|  | void *buf) | 
|  | { | 
|  | const struct display_stm32_ltdc_config *config = dev->config; | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  | uint8_t *dst = buf; | 
|  | const uint8_t *src = data->frame_buffer; | 
|  | uint16_t row; | 
|  |  | 
|  | /* src = pointer to upper left pixel of the rectangle to be read from frame buffer */ | 
|  | src += (x * data->current_pixel_size); | 
|  | src += (y * config->width * data->current_pixel_size); | 
|  |  | 
|  | for (row = 0; row < desc->height; row++) { | 
|  | (void) memcpy(dst, src, desc->width * data->current_pixel_size); | 
|  | CACHE_CLEAN(dst, desc->width * data->current_pixel_size); | 
|  | src += (config->width * data->current_pixel_size); | 
|  | dst += (desc->pitch * data->current_pixel_size); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_init(const struct device *dev) | 
|  | { | 
|  | int err; | 
|  | const struct display_stm32_ltdc_config *config = dev->config; | 
|  | struct display_stm32_ltdc_data *data = dev->data; | 
|  |  | 
|  | /* Configure and set display on/off GPIO */ | 
|  | if (config->disp_on_gpio.port) { | 
|  | err = gpio_pin_configure_dt(&config->disp_on_gpio, GPIO_OUTPUT_ACTIVE); | 
|  | if (err < 0) { | 
|  | LOG_ERR("Configuration of display on/off control GPIO failed"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Configure and set display backlight control GPIO */ | 
|  | if (config->bl_ctrl_gpio.port) { | 
|  | err = gpio_pin_configure_dt(&config->bl_ctrl_gpio, GPIO_OUTPUT_ACTIVE); | 
|  | if (err < 0) { | 
|  | LOG_ERR("Configuration of display backlight control GPIO failed"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Configure DT provided pins */ | 
|  | if (!IS_ENABLED(CONFIG_MIPI_DSI)) { | 
|  | err = pinctrl_apply_state(config->pctrl, PINCTRL_STATE_DEFAULT); | 
|  | if (err < 0) { | 
|  | LOG_ERR("LTDC pinctrl setup failed"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) { | 
|  | LOG_ERR("clock control device not ready"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | /* Turn on LTDC peripheral clock */ | 
|  | err = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | 
|  | (clock_control_subsys_t) &config->pclken); | 
|  | if (err < 0) { | 
|  | LOG_ERR("Could not enable LTDC peripheral clock"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_SOC_SERIES_STM32F4X) | 
|  | LL_RCC_PLLSAI_Disable(); | 
|  | LL_RCC_PLLSAI_ConfigDomain_LTDC(LL_RCC_PLLSOURCE_HSE, | 
|  | LL_RCC_PLLSAIM_DIV_8, | 
|  | 192, | 
|  | LL_RCC_PLLSAIR_DIV_4, | 
|  | LL_RCC_PLLSAIDIVR_DIV_8); | 
|  |  | 
|  | LL_RCC_PLLSAI_Enable(); | 
|  | while (LL_RCC_PLLSAI_IsReady() != 1) { | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_SOC_SERIES_STM32F7X) | 
|  | LL_RCC_PLLSAI_Disable(); | 
|  | LL_RCC_PLLSAI_ConfigDomain_LTDC(LL_RCC_PLLSOURCE_HSE, | 
|  | LL_RCC_PLLM_DIV_25, | 
|  | 384, | 
|  | LL_RCC_PLLSAIR_DIV_5, | 
|  | LL_RCC_PLLSAIDIVR_DIV_8); | 
|  |  | 
|  | LL_RCC_PLLSAI_Enable(); | 
|  | while (LL_RCC_PLLSAI_IsReady() != 1) { | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* reset LTDC peripheral */ | 
|  | __HAL_RCC_LTDC_FORCE_RESET(); | 
|  | __HAL_RCC_LTDC_RELEASE_RESET(); | 
|  |  | 
|  | data->current_pixel_format = DISPLAY_INIT_PIXEL_FORMAT; | 
|  | data->current_pixel_size = STM32_LTDC_INIT_PIXEL_SIZE; | 
|  |  | 
|  | /* Initialise the LTDC peripheral */ | 
|  | err = HAL_LTDC_Init(&data->hltdc); | 
|  | if (err != HAL_OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* Configure layer 0 (only one layer is used) */ | 
|  | /* LTDC starts fetching pixels and sending them to display after this call */ | 
|  | err = HAL_LTDC_ConfigLayer(&data->hltdc, &data->hltdc.LayerCfg[0], 0); | 
|  | if (err != HAL_OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PM_DEVICE | 
|  | static int stm32_ltdc_suspend(const struct device *dev) | 
|  | { | 
|  | const struct display_stm32_ltdc_config *config = dev->config; | 
|  | int err; | 
|  |  | 
|  | /* Turn off disp_en (if its GPIO is defined in device tree) */ | 
|  | if (config->disp_on_gpio.port) { | 
|  | err = gpio_pin_set_dt(&config->disp_on_gpio, 0); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Turn off backlight (if its GPIO is defined in device tree) */ | 
|  | if (config->bl_ctrl_gpio.port) { | 
|  | err = gpio_pin_set_dt(&config->bl_ctrl_gpio, 0); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Reset LTDC peripheral registers */ | 
|  | __HAL_RCC_LTDC_FORCE_RESET(); | 
|  | __HAL_RCC_LTDC_RELEASE_RESET(); | 
|  |  | 
|  | /* Turn off LTDC peripheral clock */ | 
|  | err = clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | 
|  | (clock_control_subsys_t) &config->pclken); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int stm32_ltdc_pm_action(const struct device *dev, | 
|  | enum pm_device_action action) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | switch (action) { | 
|  | case PM_DEVICE_ACTION_RESUME: | 
|  | err = stm32_ltdc_init(dev); | 
|  | break; | 
|  | case PM_DEVICE_ACTION_SUSPEND: | 
|  | err = stm32_ltdc_suspend(dev); | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if (err < 0) { | 
|  | LOG_ERR("%s: failed to set power mode", dev->name); | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  | #endif /* CONFIG_PM_DEVICE */ | 
|  |  | 
|  | static const struct display_driver_api stm32_ltdc_display_api = { | 
|  | .blanking_on = stm32_ltdc_blanking_on, | 
|  | .blanking_off = stm32_ltdc_blanking_off, | 
|  | .write = stm32_ltdc_write, | 
|  | .read = stm32_ltdc_read, | 
|  | .get_framebuffer = stm32_ltdc_get_framebuffer, | 
|  | .set_brightness = stm32_ltdc_set_brightness, | 
|  | .set_contrast = stm32_ltdc_set_contrast, | 
|  | .get_capabilities = stm32_ltdc_get_capabilities, | 
|  | .set_pixel_format = stm32_ltdc_set_pixel_format, | 
|  | .set_orientation = stm32_ltdc_set_orientation | 
|  | }; | 
|  |  | 
|  | #if DT_INST_NODE_HAS_PROP(0, ext_sdram) | 
|  |  | 
|  | #if DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram1)) | 
|  | #define FRAME_BUFFER_SECTION __stm32_sdram1_section | 
|  | #elif DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram2)) | 
|  | #define FRAME_BUFFER_SECTION __stm32_sdram2_section | 
|  | #else | 
|  | #error "LTDC ext-sdram property in device tree does not reference SDRAM1 or SDRAM2 node" | 
|  | #define FRAME_BUFFER_SECTION | 
|  | #endif /* DT_SAME_NODE(DT_INST_PHANDLE(0, ext_sdram), DT_NODELABEL(sdram1)) */ | 
|  |  | 
|  | #else | 
|  | #define FRAME_BUFFER_SECTION | 
|  | #endif /* DT_INST_NODE_HAS_PROP(0, ext_sdram) */ | 
|  |  | 
|  | #ifdef CONFIG_MIPI_DSI | 
|  | #define STM32_LTDC_DEVICE_PINCTRL_INIT(n) | 
|  | #define STM32_LTDC_DEVICE_PINCTRL_GET(n) (NULL) | 
|  | #else | 
|  | #define STM32_LTDC_DEVICE_PINCTRL_INIT(n) PINCTRL_DT_INST_DEFINE(n) | 
|  | #define STM32_LTDC_DEVICE_PINCTRL_GET(n) PINCTRL_DT_INST_DEV_CONFIG_GET(n) | 
|  | #endif | 
|  |  | 
|  | #define STM32_LTDC_DEVICE(inst)									\ | 
|  | STM32_LTDC_DEVICE_PINCTRL_INIT(inst);							\ | 
|  | PM_DEVICE_DT_INST_DEFINE(inst, stm32_ltdc_pm_action);					\ | 
|  | /* frame buffer aligned to cache line width for optimal cache flushing */		\ | 
|  | FRAME_BUFFER_SECTION static uint8_t __aligned(32)					\ | 
|  | frame_buffer_##inst[STM32_LTDC_INIT_PIXEL_SIZE *		\ | 
|  | DT_INST_PROP(inst, height) *			\ | 
|  | DT_INST_PROP(inst, width)];			\ | 
|  | static struct display_stm32_ltdc_data stm32_ltdc_data_##inst = {			\ | 
|  | .frame_buffer = frame_buffer_##inst,						\ | 
|  | .hltdc = {									\ | 
|  | .Instance = (LTDC_TypeDef *) DT_INST_REG_ADDR(inst),			\ | 
|  | .Init = {								\ | 
|  | .HSPolarity = (DT_PROP(DT_INST_CHILD(inst, display_timings),	\ | 
|  | hsync_active) ?					\ | 
|  | LTDC_HSPOL_ACTIVE_HIGH : LTDC_HSPOL_ACTIVE_LOW),\ | 
|  | .VSPolarity = (DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vsync_active) ?		\ | 
|  | LTDC_VSPOL_ACTIVE_HIGH : LTDC_VSPOL_ACTIVE_LOW),\ | 
|  | .DEPolarity = (DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), de_active) ?			\ | 
|  | LTDC_DEPOL_ACTIVE_HIGH : LTDC_DEPOL_ACTIVE_LOW),\ | 
|  | .PCPolarity = (DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), pixelclk_active) ?		\ | 
|  | LTDC_PCPOL_ACTIVE_HIGH : LTDC_PCPOL_ACTIVE_LOW),\ | 
|  | .HorizontalSync = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), hsync_len) - 1,	\ | 
|  | .VerticalSync = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vsync_len) - 1,	\ | 
|  | .AccumulatedHBP = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), hback_porch) +	\ | 
|  | DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), hsync_len) - 1,	\ | 
|  | .AccumulatedVBP = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vback_porch) +	\ | 
|  | DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), vsync_len) - 1,	\ | 
|  | .AccumulatedActiveW = DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), hback_porch) +	\ | 
|  | DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), hsync_len) +		\ | 
|  | DT_INST_PROP(inst, width) - 1,		\ | 
|  | .AccumulatedActiveH = DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), vback_porch) +	\ | 
|  | DT_PROP(DT_INST_CHILD(inst,		\ | 
|  | display_timings), vsync_len) +		\ | 
|  | DT_INST_PROP(inst, height) - 1,		\ | 
|  | .TotalWidth = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), hback_porch) +		\ | 
|  | DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), hsync_len) +			\ | 
|  | DT_INST_PROP(inst, width) +			\ | 
|  | DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), hfront_porch) - 1,		\ | 
|  | .TotalHeigh = DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vback_porch) +		\ | 
|  | DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vsync_len) +			\ | 
|  | DT_INST_PROP(inst, height) +			\ | 
|  | DT_PROP(DT_INST_CHILD(inst,			\ | 
|  | display_timings), vfront_porch) - 1,		\ | 
|  | .Backcolor.Red =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_red, 0xFF),	\ | 
|  | .Backcolor.Green =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_green, 0xFF),	\ | 
|  | .Backcolor.Blue =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_blue, 0xFF),	\ | 
|  | },									\ | 
|  | .LayerCfg[0] = {							\ | 
|  | .WindowX0 = DT_INST_PROP_OR(inst, window0_x0, 0),		\ | 
|  | .WindowX1 = DT_INST_PROP_OR(inst, window0_x1,			\ | 
|  | DT_INST_PROP(inst, width)),	\ | 
|  | .WindowY0 = DT_INST_PROP_OR(inst, window0_y0, 0),		\ | 
|  | .WindowY1 = DT_INST_PROP_OR(inst, window0_y1,			\ | 
|  | DT_INST_PROP(inst, height)),	\ | 
|  | .PixelFormat = STM32_LTDC_INIT_PIXEL_FORMAT,			\ | 
|  | .Alpha = 255,							\ | 
|  | .Alpha0 = 0,							\ | 
|  | .BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA,			\ | 
|  | .BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA,			\ | 
|  | .FBStartAdress = (uint32_t) frame_buffer_##inst,		\ | 
|  | .ImageWidth = DT_INST_PROP(inst, width),			\ | 
|  | .ImageHeight = DT_INST_PROP(inst, height),			\ | 
|  | .Backcolor.Red =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_red, 0xFF),	\ | 
|  | .Backcolor.Green =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_green, 0xFF),	\ | 
|  | .Backcolor.Blue =						\ | 
|  | DT_INST_PROP_OR(inst, def_back_color_blue, 0xFF),	\ | 
|  | },									\ | 
|  | },										\ | 
|  | };											\ | 
|  | static const struct display_stm32_ltdc_config stm32_ltdc_config_##inst = {		\ | 
|  | .width = DT_INST_PROP(inst, width),						\ | 
|  | .height = DT_INST_PROP(inst, height),						\ | 
|  | .disp_on_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, disp_on_gpios),		\ | 
|  | (GPIO_DT_SPEC_INST_GET(inst, disp_on_gpios)), ({ 0 })),		\ | 
|  | .bl_ctrl_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, bl_ctrl_gpios),		\ | 
|  | (GPIO_DT_SPEC_INST_GET(inst, bl_ctrl_gpios)), ({ 0 })),		\ | 
|  | .pclken = {									\ | 
|  | .enr = DT_INST_CLOCKS_CELL(inst, bits),					\ | 
|  | .bus = DT_INST_CLOCKS_CELL(inst, bus)					\ | 
|  | },										\ | 
|  | .pctrl = STM32_LTDC_DEVICE_PINCTRL_GET(inst),					\ | 
|  | };											\ | 
|  | DEVICE_DT_INST_DEFINE(inst,								\ | 
|  | &stm32_ltdc_init,							\ | 
|  | PM_DEVICE_DT_INST_GET(inst),						\ | 
|  | &stm32_ltdc_data_##inst,						\ | 
|  | &stm32_ltdc_config_##inst,						\ | 
|  | POST_KERNEL,								\ | 
|  | CONFIG_DISPLAY_INIT_PRIORITY,						\ | 
|  | &stm32_ltdc_display_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(STM32_LTDC_DEVICE) |