diff --git a/boards/shields/st_b_dsi_mb1314/Kconfig.shield b/boards/shields/st_b_dsi_mb1314/Kconfig.shield new file mode 100644 index 0000000000000..efdcb33a54b38 --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/Kconfig.shield @@ -0,0 +1,6 @@ +# Copyright (c) 2025 STMicroelectronics + +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_ST_B_DSI_MB1314 + def_bool $(shields_list_contains,st_b_dsi_mb1314) diff --git a/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.conf b/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.conf new file mode 100644 index 0000000000000..1370d0c479ae7 --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.conf @@ -0,0 +1,5 @@ +CONFIG_DISPLAY=y +CONFIG_MIPI_DSI=y +CONFIG_STM32_LTDC_RGB565=y +CONFIG_STM32_LTDC_FB_NUM=1 +CONFIG_GPIO_HOGS_INIT_PRIORITY=71 diff --git a/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.overlay b/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.overlay new file mode 100644 index 0000000000000..f962d51821128 --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/boards/stm32l4r9i_disco.overlay @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&clk_hse { + clock-frequency = ; /* 16MHz */ + status = "okay"; +}; + +<dc { + /* SRAM0 is too small to hold a framebuffer, use SRAM2 */ + ext-sdram = <&sram2>; +}; + +&mfx { + dsi_3v3_pwron { + gpio-hog; + gpios = <8 GPIO_ACTIVE_LOW>; + output-high; + line-name = "DSI_3V3_PWRON"; + }; + + dsi_1v8_pwron { + gpio-hog; + gpios = <18 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "DSI_1v8_PWRON"; + }; +}; + +&pllsai2 { + /* PLLSAI2 is here to generate the PCLK to feed the DSI + * We need to feed roughly 500Mbps. PCLK depends on format + * For 24bit (RGB888) format, we need roughly 20.8 MHz. (Actually target 15 MHz) + * div-m = 1, mul-n = 60, div-r = 4, div-divr = 4 + * + * PCLK = MSI (4MHz) * mul-n / (div-m * div-r * div-divr) + */ + status = "okay"; + div-m = <1>; + mul-n = <60>; + div-r = <4>; + div-divr = <4>; + clocks = <&clk_msi>; +}; diff --git a/boards/shields/st_b_dsi_mb1314/doc/index.rst b/boards/shields/st_b_dsi_mb1314/doc/index.rst new file mode 100644 index 0000000000000..af58390a70e51 --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/doc/index.rst @@ -0,0 +1,45 @@ +.. _st_b_dsi_mb1314: + +ST B-DSI-MB1314 +############### + +Overview +******** + +This shield provides a DSI display, based on a round AMOLED touch-sensitive panel +of 1.2 inches and 390x390 pixels. The display module reference is IEG1120TB103GF-001 +from Govisionox Optoelectronics. It displays up to 16M colors. + +.. figure:: mb1314.webp + :alt: B-DSI-MB1314 Image + :align: center + + B-DSI-MB1314 Image + +Requirements +************ + +Your board needs to have ``zephyr_mipi_dsi`` and ``zephyr_lcd_controller`` +device-tree labels to work with this shield. + +Usage +***** + +The shield can be used in any application by setting ``SHIELD`` to +``st_b_dsi_mb1314`` and adding the necessary board specific device tree +properties. + +Set ``--shield "st_b_dsi_mb1314"`` when you invoke ``west build``. For example: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/display + :board: stm32l4r9i_disco + :shield: st_b_dsi_mb1314 + :goals: build + +References +********** + +- `Product page `_ + +- `User manual `_ diff --git a/boards/shields/st_b_dsi_mb1314/doc/mb1314.webp b/boards/shields/st_b_dsi_mb1314/doc/mb1314.webp new file mode 100644 index 0000000000000..0f94609d0dcaf Binary files /dev/null and b/boards/shields/st_b_dsi_mb1314/doc/mb1314.webp differ diff --git a/boards/shields/st_b_dsi_mb1314/shield.yml b/boards/shields/st_b_dsi_mb1314/shield.yml new file mode 100644 index 0000000000000..c5cb35838a48b --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/shield.yml @@ -0,0 +1,6 @@ +shield: + name: st_b_dsi_mb1314 + full_name: ST B-DSI-MB1314 + vendor: st + supported_features: + - display diff --git a/boards/shields/st_b_dsi_mb1314/st_b_dsi_mb1314.overlay b/boards/shields/st_b_dsi_mb1314/st_b_dsi_mb1314.overlay new file mode 100644 index 0000000000000..771f1ab29541d --- /dev/null +++ b/boards/shields/st_b_dsi_mb1314/st_b_dsi_mb1314.overlay @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + chosen { + zephyr,display = &zephyr_lcd_controller; + }; +}; + +&zephyr_mipi_dsi { + status = "okay"; + + /* DSI HOST dedicated PLL to generate 62.5 MHz from 16MHz + * source clock, in order to generate 500Mbps on single lane + * + * F_VCO = CLK_IN / pll-idf * 2 * pll-ndiv + * PHI = F_VCO / 2 / (1 << pll-odf) = lane_byte_clk + * = 25 MHz / 5 * 2 * 100 / 2 / (1<<0) / 8 = 62.5 MHz + */ + pll-ndiv = <125>; + pll-idf = <4>; + pll-odf = <0>; + + phy-timings = <33 /* ClockLaneHS2LPTime */ + 30 /* ClockLaneLP2HSTime */ + 11 /* DataLaneHS2LPTime */ + 21 /* DataLaneLP2HSTime */ + 0 /* DataLaneMaxReadTime */ + 7 /* StopWaitTime */>; + + host-timeouts = <1 /* TimeoutCkdiv */ + 0 /* HighSpeedTransmissionTimeout */ + 0 /* LowPowerReceptionTimeout */ + 0 /* HighSpeedReadTimeout */ + 0 /* LowPowerReadTimeout */ + 0 /* HighSpeedWriteTimeout */ + 0 /* HighSpeedWritePrespMode */ + 0 /* LowPowerWriteTimeout */ + 0 /* BTATimeout */>; + + vs-active-high; + hs-active-high; + de-active-high; + + lp-rx-filter=<10000>; + + g1120tb101: panel@0 { + compatible = "gvo,g1120tb101"; + reg = <0x0>; + height = <390>; + width = <390>; + data-lanes = <1>; + reset-gpios = <&dsi_lcd_qsh_030 57 GPIO_ACTIVE_LOW>; + + pixel-format = ; + }; +}; + +&zephyr_lcd_controller { + status = "okay"; + + width = <390>; + height = <390>; + pixel-format = ; + + display-timings { + compatible = "zephyr,panel-timing"; + de-active = <0>; + pixelclk-active = <0>; + hsync-active = <0>; + vsync-active = <0>; + hsync-len = <1>; + vsync-len = <1>; + hback-porch = <1>; + vback-porch = <1>; + hfront-porch = <1>; + vfront-porch = <1>; + }; + def-back-color-red = <0x0>; + def-back-color-green = <0x0>; + def-back-color-blue = <0x0>; +}; diff --git a/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts b/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts index 06acc172d0e29..b43cfc8f8d9d3 100644 --- a/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts +++ b/boards/st/stm32l4r9i_disco/stm32l4r9i_disco.dts @@ -90,6 +90,24 @@ <27 0 &gpioc 6 0>; /* DCMI_D0 */ }; + dsi_lcd_qsh_030: connector_dsi_lcd { + compatible = "st,dsi-lcd-qsh-030"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = <4 0 &mfx 9 0>, /* TOUCH_INT */ + <22 0 &gpioh 14 0>, /* SPI_CS */ + <24 0 &gpiob 13 0>, /* SPI2_SCK */ + <26 0 &gpiob 15 0>, /* SPI2_MOSI */ + <28 0 &gpiob 14 0>, /* SPI_DCX */ + <40 0 &gpiog 13 0>, /* I2C1_SDA */ + <43 0 &gpioa 8 0>, /* DSI_SWIRE */ + <44 0 &gpiob 6 0>, /* I2C1_SCL */ + <49 0 &gpiof 11 0>, /* DSI_TE */ + <53 0 &gpiob 1 0>, /* LCD_BL_CTRL */ + <57 0 &mfx 10 0>; /* DSI/TOUCH RESET */ + }; + aliases { led0 = &green_led; led1 = &orange_led; @@ -347,3 +365,7 @@ st_cam_dvp: &dcmi { pinctrl-names = "default"; }; + +/* alias used by display shields */ +zephyr_mipi_dsi: &mipi_dsi {}; +zephyr_lcd_controller: <dc {}; diff --git a/drivers/display/CMakeLists.txt b/drivers/display/CMakeLists.txt index 037daf8876d9f..5897ddf25f281 100644 --- a/drivers/display/CMakeLists.txt +++ b/drivers/display/CMakeLists.txt @@ -41,6 +41,7 @@ zephyr_library_sources_ifdef(CONFIG_NT35510 display_nt35510.c) zephyr_library_sources_ifdef(CONFIG_RENESAS_RA_GLCDC display_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_ILI9806E_DSI display_ili9806e_dsi.c) zephyr_library_sources_ifdef(CONFIG_ST7701 display_st7701.c) +zephyr_library_sources_ifdef(CONFIG_G1120TB101 display_g1120tb101.c) zephyr_library_sources_ifdef(CONFIG_MICROBIT_DISPLAY mb_display.c diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig index b47f3bc7d477d..b7dae71e0884b 100644 --- a/drivers/display/Kconfig +++ b/drivers/display/Kconfig @@ -58,5 +58,6 @@ source "drivers/display/Kconfig.nt35510" source "drivers/display/Kconfig.renesas_ra" source "drivers/display/Kconfig.ili9806e_dsi" source "drivers/display/Kconfig.st7701" +source "drivers/display/Kconfig.g1120tb101" endif # DISPLAY diff --git a/drivers/display/Kconfig.g1120tb101 b/drivers/display/Kconfig.g1120tb101 new file mode 100644 index 0000000000000..4490121b7f8b7 --- /dev/null +++ b/drivers/display/Kconfig.g1120tb101 @@ -0,0 +1,20 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config G1120TB101 + bool "G1120TB101 display controller" + default y + depends on DT_HAS_GVO_G1120TB101_ENABLED + select MIPI_DSI + help + Enable the driver for GVO G1120TB101 display controller. + +if G1120TB101 + +config DISPLAY_G1120TB101_INIT_PRIORITY + int "Initialization priority" + default DISPLAY_INIT_PRIORITY + help + G1120TB101 display driver initialization priority. + +endif diff --git a/drivers/display/display_g1120tb101.c b/drivers/display/display_g1120tb101.c new file mode 100644 index 0000000000000..3911dac9b2974 --- /dev/null +++ b/drivers/display/display_g1120tb101.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * Inspired from display_otm8009a.c + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT gvo_g1120tb101 + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(g1120tb101, CONFIG_DISPLAY_LOG_LEVEL); + +struct g1120tb101_config { + const struct device *mipi_dsi; + const struct gpio_dt_spec reset; + uint8_t data_lanes; + uint16_t width; + uint16_t height; + uint8_t channel; + uint8_t dsi_pixel_format; +}; + +struct g1120tb101_cmd_data { + uint8_t cmd; + uint8_t param; +}; + +static const struct g1120tb101_cmd_data g1120tb101_init_data[] = { + /* Go to command 2 */ + { 0xfe, 0x01 }, + /* IC Frame rate control, set power, sw mapping, mux switch timing command */ + { 0x06, 0x62 }, { 0x0e, 0x80 }, { 0x0f, 0x80 }, { 0x10, 0x71 }, { 0x13, 0x81 }, + { 0x14, 0x81 }, { 0x15, 0x82 }, { 0x16, 0x82 }, { 0x18, 0x88 }, { 0x19, 0x55 }, + { 0x1a, 0x10 }, { 0x1c, 0x99 }, { 0x1d, 0x03 }, { 0x1e, 0x03 }, { 0x1f, 0x03 }, + { 0x20, 0x03 }, { 0x25, 0x03 }, { 0x26, 0x8d }, { 0x2a, 0x03 }, { 0x2b, 0x8d }, + { 0x36, 0x00 }, { 0x37, 0x10 }, { 0x3a, 0x00 }, { 0x3b, 0x00 }, { 0x3d, 0x20 }, + { 0x3f, 0x3a }, { 0x40, 0x30 }, { 0x41, 0x1a }, { 0x42, 0x33 }, { 0x43, 0x22 }, + { 0x44, 0x11 }, { 0x45, 0x66 }, { 0x46, 0x55 }, { 0x47, 0x44 }, { 0x4c, 0x33 }, + { 0x4d, 0x22 }, { 0x4e, 0x11 }, { 0x4f, 0x66 }, { 0x50, 0x55 }, { 0x51, 0x44 }, + { 0x57, 0x33 }, { 0x6b, 0x1b }, { 0x70, 0x55 }, { 0x74, 0x0c }, + /* Go to command 3 */ + { 0xfe, 0x02 }, + /* Set the VGMP/VGSP voltage control */ + { 0x9b, 0x40 }, { 0x9c, 0x00 }, { 0x9d, 0x20 }, + /* Go to command 4 */ + { 0xfe, 0x03 }, + /* Set the VGMP/VGSP voltage control */ + { 0x9b, 0x40 }, { 0x9c, 0x00 }, { 0x9d, 0x20 }, + /* Go to command 5 */ + { 0xfe, 0x04 }, + /* VSR command */ + { 0x5d, 0x10 }, + /* VSR1 timing set */ + { 0x00, 0x8d }, { 0x01, 0x00 }, { 0x02, 0x01 }, { 0x03, 0x01 }, { 0x04, 0x10 }, + { 0x05, 0x01 }, { 0x06, 0xa7 }, { 0x07, 0x20 }, { 0x08, 0x00 }, + /* VSR2 timing set */ + { 0x09, 0xc2 }, { 0x0a, 0x00 }, { 0x0b, 0x02 }, { 0x0c, 0x01 }, { 0x0d, 0x40 }, + { 0x0e, 0x06 }, { 0x0f, 0x01 }, { 0x10, 0xa7 }, { 0x11, 0x00 }, + /* VSR3 timing set */ + { 0x12, 0xc2 }, { 0x13, 0x00 }, { 0x14, 0x02 }, { 0x15, 0x01 }, { 0x16, 0x40 }, + { 0x17, 0x07 }, { 0x18, 0x01 }, { 0x19, 0xa7 }, { 0x1a, 0x00 }, + /* VSR4 timing set */ + { 0x1B, 0x82 }, { 0x1C, 0x00 }, { 0x1D, 0xFF }, { 0x1E, 0x05 }, { 0x1F, 0x60 }, + { 0x20, 0x02 }, { 0x21, 0x01 }, { 0x22, 0x7C }, { 0x23, 0x00 }, + /* VSR5 timing set */ + { 0x24, 0xC2 }, { 0x25, 0x00 }, { 0x26, 0x04 }, { 0x27, 0x02 }, { 0x28, 0x70 }, + { 0x29, 0x05 }, { 0x2A, 0x74 }, { 0x2B, 0x8D }, { 0x2D, 0x00 }, + /* VSR6 timing set */ + { 0x2F, 0xC2 }, { 0x30, 0x00 }, { 0x31, 0x04 }, { 0x32, 0x02 }, { 0x33, 0x70 }, + { 0x34, 0x07 }, { 0x35, 0x74 }, { 0x36, 0x8D }, { 0x37, 0x00 }, + /* VSR marping command */ + { 0x5E, 0x20 }, { 0x5F, 0x31 }, { 0x60, 0x54 }, { 0x61, 0x76 }, { 0x62, 0x98 }, + /* Go to command 6 */ + { 0xfe, 0x05 }, + /* Set the ELVSS voltage */ + { 0x05, 0x17 }, { 0x2A, 0x04 }, { 0x91, 0x00 }, + /* Go back in standard commands */ + { 0xfe, 0x00 }, + /* Set the Pixel format */ + { MIPI_DCS_SET_PIXEL_FORMAT, 0x07}, + /* Set tear off */ + { MIPI_DCS_SET_TEAR_OFF, 0x00 }, + /* Set DSI mode to internal timing added vs ORIGINAL for Command mode */ + { 0xc2, 0x00 }, +}; + +static inline int g1120tb101_dcs_write(const struct device *dev, uint8_t cmd, const void *buf, + size_t len) +{ + const struct g1120tb101_config *cfg = dev->config; + int ret; + + ret = mipi_dsi_dcs_write(cfg->mipi_dsi, cfg->channel, cmd, buf, len); + if (ret < 0) { + LOG_ERR("DCS 0x%x write failed! (%d)", cmd, ret); + return ret; + } + + return 0; +} + +static int g1120tb101_configure(const struct device *dev) +{ + uint8_t InitParam1[4] = {0x00, 0x04, 0x01, 0x89}; + uint8_t InitParam2[4] = {0x00, 0x00, 0x01, 0x85}; + int ret, i; + + /* Configure common commands */ + for (i = 0; i < ARRAY_SIZE(g1120tb101_init_data); i++) { + ret = g1120tb101_dcs_write(dev, g1120tb101_init_data[i].cmd, + &g1120tb101_init_data[i].param, 1); + if (ret) { + LOG_ERR("Failed to write cmd:0x%x, param:0x%x (ret:0x%x)", + g1120tb101_init_data[i].cmd, g1120tb101_init_data[i].param, ret); + return ret; + } + } + + ret = g1120tb101_dcs_write(dev, MIPI_DCS_SET_COLUMN_ADDRESS, InitParam1, 4); + if (ret) { + LOG_ERR("Failed to write COLUMN_ADDRESS (ret:0x%x)", ret); + return ret; + } + + ret = g1120tb101_dcs_write(dev, MIPI_DCS_SET_PAGE_ADDRESS, InitParam2, 4); + if (ret) { + LOG_ERR("Failed to write PAGE_ADDRESS (ret:0x%x)", ret); + return ret; + } + + ret = g1120tb101_dcs_write(dev, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); + if (ret) { + LOG_ERR("Failed to write EXIT_SLEEP_MODE (ret:0x%x)", ret); + return ret; + } + + k_msleep(120); + + return 0; +} + +static int g1120tb101_blanking_on(const struct device *dev) +{ + return g1120tb101_dcs_write(dev, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); +} + +static int g1120tb101_blanking_off(const struct device *dev) +{ + return g1120tb101_dcs_write(dev, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); +} + +static int g1120tb101_write(const struct device *dev, uint16_t x, uint16_t y, + const struct display_buffer_descriptor *desc, const void *buf) +{ + return -ENOTSUP; +} + +static int g1120tb101_set_brightness(const struct device *dev, uint8_t brightness) +{ + return g1120tb101_dcs_write(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, &brightness, 1); +} + +static void g1120tb101_get_capabilities(const struct device *dev, + struct display_capabilities *capabilities) +{ + const struct g1120tb101_config *cfg = dev->config; + + memset(capabilities, 0, sizeof(struct display_capabilities)); + capabilities->x_resolution = cfg->width; + capabilities->y_resolution = cfg->height; + capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; + capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; + capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +static const struct display_driver_api g1120tb101_api = { + .blanking_on = g1120tb101_blanking_on, + .blanking_off = g1120tb101_blanking_off, + .write = g1120tb101_write, + .set_brightness = g1120tb101_set_brightness, + .get_capabilities = g1120tb101_get_capabilities, +}; + +static int g1120tb101_init(const struct device *dev) +{ + const struct g1120tb101_config *cfg = dev->config; + struct mipi_dsi_device mdev = { 0 }; + int ret; + + if (cfg->dsi_pixel_format != MIPI_DSI_PIXFMT_RGB888) { + LOG_ERR("Unsupported pixel-format 0x%x\n", cfg->dsi_pixel_format); + return -EINVAL; + } + + if (cfg->data_lanes != 1) { + LOG_ERR("Only MIPI 1 lane is supported\n"); + return -EINVAL; + } + + if (cfg->reset.port) { + ret = gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_ACTIVE); + if (ret < 0) { + LOG_ERR("Reset configure failed! (%d)", ret); + return ret; + } + + k_msleep(100); + + ret = gpio_pin_set_dt(&cfg->reset, 0); + if (ret < 0) { + LOG_ERR("Disable reset failed! (%d)", ret); + return ret; + } + + k_msleep(120); + } + + /* attach to MIPI-DSI host */ + mdev.data_lanes = cfg->data_lanes; + mdev.pixfmt = cfg->dsi_pixel_format; + mdev.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM; + + mdev.timings.hactive = cfg->width; + mdev.timings.hbp = 1; + mdev.timings.hfp = 1; + mdev.timings.hsync = 1; + mdev.timings.vactive = cfg->height; + mdev.timings.vbp = 1; + mdev.timings.vfp = 1; + mdev.timings.vsync = 1; + + ret = mipi_dsi_attach(cfg->mipi_dsi, cfg->channel, &mdev); + if (ret < 0) { + LOG_ERR("MIPI-DSI attach failed! (%d)", ret); + return ret; + } + + ret = g1120tb101_configure(dev); + if (ret) { + LOG_ERR("DSI init sequence failed! (%d)", ret); + return ret; + } + + ret = g1120tb101_blanking_off(dev); + if (ret) { + LOG_ERR("Display blanking off failed! (%d)", ret); + return ret; + } + + return 0; +} + +#define G1120TB101_DEVICE(inst) \ + static const struct g1120tb101_config g1120tb101_config_##inst = { \ + .mipi_dsi = DEVICE_DT_GET(DT_INST_BUS(inst)), \ + .reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \ + .data_lanes = DT_INST_PROP_BY_IDX(inst, data_lanes, 0), \ + .width = DT_INST_PROP(inst, width), \ + .height = DT_INST_PROP(inst, height), \ + .channel = DT_INST_REG_ADDR(inst), \ + .dsi_pixel_format = DT_INST_PROP(inst, pixel_format), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, &g1120tb101_init, NULL, NULL, \ + &g1120tb101_config_##inst, POST_KERNEL, \ + CONFIG_DISPLAY_G1120TB101_INIT_PRIORITY, &g1120tb101_api); \ + +DT_INST_FOREACH_STATUS_OKAY(G1120TB101_DEVICE) diff --git a/drivers/display/display_stm32_ltdc.c b/drivers/display/display_stm32_ltdc.c index d950f5b9dbfb8..3c6abd6982c0c 100644 --- a/drivers/display/display_stm32_ltdc.c +++ b/drivers/display/display_stm32_ltdc.c @@ -45,20 +45,19 @@ LOG_MODULE_REGISTER(display_stm32_ltdc, CONFIG_DISPLAY_LOG_LEVEL); #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 +#define STM32_LTDC_INIT_PIXEL_SIZE DISPLAY_BITS_PER_PIXEL(DISPLAY_INIT_PIXEL_FORMAT) \ + / BITS_PER_BYTE struct display_stm32_ltdc_data { LTDC_HandleTypeDef hltdc; @@ -108,26 +107,23 @@ static int stm32_ltdc_set_pixel_format(const struct device *dev, { int err; struct display_stm32_ltdc_data *data = dev->data; + uint32_t ltdc_pix_fmt; + + if (format == PIXEL_FORMAT_RGB_565) { + ltdc_pix_fmt = LTDC_PIXEL_FORMAT_RGB565; + } else if (format == PIXEL_FORMAT_RGB_888) { + ltdc_pix_fmt = LTDC_PIXEL_FORMAT_RGB888; + } else if (format == PIXEL_FORMAT_ARGB_8888) { + ltdc_pix_fmt = LTDC_PIXEL_FORMAT_ARGB8888; + } else { + return -ENOTSUP; + } - 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; - break; - default: - err = -ENOTSUP; - break; + err = HAL_LTDC_SetPixelFormat(&data->hltdc, ltdc_pix_fmt, 0); + if (!err) { + data->current_pixel_format = format; + data->current_pixel_size = + DISPLAY_BITS_PER_PIXEL(data->current_pixel_format) / BITS_PER_BYTE; } return err; diff --git a/drivers/mipi_dsi/dsi_stm32.c b/drivers/mipi_dsi/dsi_stm32.c index 0d200862dddbe..6aaaf6deb5dd8 100644 --- a/drivers/mipi_dsi/dsi_stm32.c +++ b/drivers/mipi_dsi/dsi_stm32.c @@ -21,16 +21,6 @@ LOG_MODULE_REGISTER(dsi_stm32, CONFIG_MIPI_DSI_LOG_LEVEL); -#if defined(CONFIG_STM32_LTDC_ARGB8888) -#define STM32_DSI_INIT_PIXEL_FORMAT DSI_RGB888 -#elif defined(CONFIG_STM32_LTDC_RGB888) -#define STM32_DSI_INIT_PIXEL_FORMAT DSI_RGB888 -#elif defined(CONFIG_STM32_LTDC_RGB565) -#define STM32_DSI_INIT_PIXEL_FORMAT DSI_RGB565 -#else -#error "Invalid LTDC pixel format chosen" -#endif /* CONFIG_STM32_LTDC_ARGB8888 */ - #define MAX_TX_ESC_CLK_KHZ 20000 #define MAX_TX_ESC_CLK_DIV 8 @@ -244,7 +234,14 @@ static int mipi_dsi_stm32_attach(const struct device *dev, uint8_t channel, } vcfg->VirtualChannelID = channel; - vcfg->ColorCoding = STM32_DSI_INIT_PIXEL_FORMAT; + if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB888) { + vcfg->ColorCoding = DSI_RGB888; + } else if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB565) { + vcfg->ColorCoding = DSI_RGB565; + } else { + LOG_ERR("Unsupported pix format\n"); + return -ENOTSUP; + } if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { vcfg->Mode = DSI_VID_MODE_BURST; diff --git a/dts/arm/st/l4/stm32l4p5.dtsi b/dts/arm/st/l4/stm32l4p5.dtsi index 9b0aca624ed9f..3d8278a13c6a3 100644 --- a/dts/arm/st/l4/stm32l4p5.dtsi +++ b/dts/arm/st/l4/stm32l4p5.dtsi @@ -292,6 +292,16 @@ }; }; + ltdc: display-controller@40016800 { + compatible = "st,stm32-ltdc"; + reg = <0x40016800 0x200>; + interrupts = <91 0>, <92 0>; + interrupt-names = "ltdc", "ltdc_er"; + clocks = <&rcc STM32_CLOCK(APB2, 26U)>; + resets = <&rctl STM32_RESET(APB2, 26U)>; + status = "disabled"; + }; + can1: can@40006400 { compatible = "st,stm32-bxcan"; reg = <0x40006400 0x400>; diff --git a/dts/arm/st/l4/stm32l4r5.dtsi b/dts/arm/st/l4/stm32l4r5.dtsi index f5c716ac92888..e039ad666c46a 100644 --- a/dts/arm/st/l4/stm32l4r5.dtsi +++ b/dts/arm/st/l4/stm32l4r5.dtsi @@ -7,6 +7,7 @@ #include #include +/delete-node/ <dc; /delete-node/ &sdmmc2; /delete-node/ &sram2; /* different memory address */ diff --git a/dts/arm/st/l4/stm32l4r9.dtsi b/dts/arm/st/l4/stm32l4r9.dtsi index db067f754e200..e5ee7c3992784 100644 --- a/dts/arm/st/l4/stm32l4r9.dtsi +++ b/dts/arm/st/l4/stm32l4r9.dtsi @@ -21,5 +21,18 @@ resets = <&rctl STM32_RESET(APB2, 26U)>; status = "disabled"; }; + + mipi_dsi: dsihost@40016c00 { + compatible = "st,stm32-mipi-dsi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40016c00 0x800>; + clock-names = "dsiclk", "refclk", "pixelclk"; + clocks = <&rcc STM32_CLOCK(APB2, 27U)>, + <&rcc STM32_SRC_HSE NO_SEL>, + <&rcc STM32_SRC_PLLSAI2_DIVR NO_SEL>; + resets = <&rctl STM32_RESET(APB2, 27U)>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/display/gvo,g1120tb101.yaml b/dts/bindings/display/gvo,g1120tb101.yaml new file mode 100644 index 0000000000000..3ed8cb297efbd --- /dev/null +++ b/dts/bindings/display/gvo,g1120tb101.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: GVO G1120TB101 Panel + +compatible: "gvo,g1120tb101" + +include: [mipi-dsi-device.yaml, display-controller.yaml] + +properties: + reset-gpios: + type: phandle-array + description: | + The RESETn pin is asserted to disable the sensor causing a hard + reset. The sensor receives this as an active-low signal. diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index 29299d496eb17..03c03f497079b 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -281,6 +281,7 @@ grinn Grinn grmn Garmin Limited gss Gas Sensing Solutions Ltd. gumstix Gumstix, Inc. +gvo Kunshan Govisionox Optoelectronics Co., Ltd. hamamatsu Hamamatsu Photonics K.K. hannstar HannStar Display Corporation haoyu Haoyu Microelectronic Co. Ltd. diff --git a/include/zephyr/dt-bindings/clock/stm32l4_clock.h b/include/zephyr/dt-bindings/clock/stm32l4_clock.h index eabfffc9ed208..5e094f099d1ba 100644 --- a/include/zephyr/dt-bindings/clock/stm32l4_clock.h +++ b/include/zephyr/dt-bindings/clock/stm32l4_clock.h @@ -26,7 +26,8 @@ /* defined in stm32_common_clocks.h */ /** Fixed clocks */ /* Low speed clocks defined in stm32_common_clocks.h */ -#define STM32_SRC_HSI (STM32_SRC_LSI + 1) +#define STM32_SRC_HSE (STM32_SRC_LSI + 1) +#define STM32_SRC_HSI (STM32_SRC_HSE + 1) #define STM32_SRC_HSI48 (STM32_SRC_HSI + 1) #define STM32_SRC_MSI (STM32_SRC_HSI48 + 1) /** Bus clock */ diff --git a/tests/drivers/display/display_check/testcase.yaml b/tests/drivers/display/display_check/testcase.yaml index 596e3dde52778..05a3958153058 100644 --- a/tests/drivers/display/display_check/testcase.yaml +++ b/tests/drivers/display/display_check/testcase.yaml @@ -44,6 +44,14 @@ tests: fixture: fixture_display pytest_dut_scope: session display_capture_config: "${DISPLAY_TEST_DIR}/display_config.yaml" + tests.drivers.display.check.st_b_dsi_mb1314: + filter: dt_compat_enabled("gvo,g1120tb101") + platform_allow: stm32l4r9i_disco + extra_args: SHIELD=st_b_dsi_mb1314 + harness_config: + fixture: fixture_display + pytest_dut_scope: session + display_capture_config: "${DISPLAY_TEST_DIR}/display_config.yaml" tests.drivers.display.check.shield: # This test case is intended to verify support for shields on boards # known to support them. It is not intended to cover all combinations