* [PATCH v3 0/7] Add display support for STM32MP25
@ 2025-09-04 12:53 Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
` (6 more replies)
0 siblings, 7 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
This series aims to add basic LVDS display support for STM32MP257F-EV1
board.
It introduces:
- the STM32 LVDS Display Interface Transmitter driver.
- a new version of the STM32 LTDC driver.
- the support of "panel-lvds" compatible.
It adds and enables driver support for stm32mp257f-ev1-u-boot
It also brings fixes to the ofnode driver.
This work is based on the Linux device-tree sent recently [1].
[1] https://lore.kernel.org/lkml/20250822-drm-misc-next-v5-0-9c825e28f733@foss.st.com/
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
Changes in v3:
- Add Yannick's trailers where relevant
- Change stm32_lvds license for 'GPL-2.0-or-later OR BSD-3-Clause'
- Link to v2: https://lore.kernel.org/r/20250903-master-v2-0-5cdf73bff42c@foss.st.com
Changes in v2:
- Add Patrice's trailers where relevant.
- Add Yannick and Philippe as reviewers.
- Drop patch [7/8] - "configs: stm32mp25: enable LVDS display support"
- s/cirle/circle in commit log [5/8] - "video: stm32: ltdc: properly search the first available panel"
- Rebase on latest master branch
- Align on latest Linux patchset (which adds a new LTDC compatible)
- stm32_lvds driver:
- Add 'st,stm32mp255-ltdc' compatible following above change
- stm32_lvds driver:
- Gather all registers and fields definitions together
- Uppercase registers on the following pattern: LVDS_<register_name>
- Uppercase register fields on the following pattern:
<reg_name>_<field_name>
- Rename priv struct to 'stm32_lvds_priv' and rename variable for more
explicit context (eg lvds -> priv)
- Create plat struct 'stm32_lvds_plat'
- Add .of_to_plat hook and move related content inside
- Change several function prototypes following above change for more
explicit context and readability.
- Correct 'clkin_khz' error condition
- Link to v1: https://lore.kernel.org/r/20250820-master-v1-0-fc76f18ab2fd@foss.st.com
---
Raphael Gallais-Pou (7):
ofnode: support panel-timings in ofnode_decode_display_timing
video: simple_panel: add support for "panel-lvds" display
video: stm32: STM32 driver support for LVDS
video: stm32: ltdc: support new hardware version for STM32MP25 SoC
video: stm32: ltdc: properly search the first available panel
ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
configs: stm32mp25: enable LVDS display support
MAINTAINERS | 1 +
arch/arm/dts/stm32mp25-u-boot.dtsi | 8 +
configs/stm32mp25_defconfig | 3 +
doc/board/st/st-dt.rst | 1 +
drivers/core/ofnode.c | 17 +-
drivers/video/simple_panel.c | 1 +
drivers/video/stm32/Kconfig | 9 +
drivers/video/stm32/Makefile | 1 +
drivers/video/stm32/stm32_ltdc.c | 158 ++++++++-
drivers/video/stm32/stm32_lvds.c | 693 +++++++++++++++++++++++++++++++++++++
10 files changed, 872 insertions(+), 20 deletions(-)
---
base-commit: d367be2b13a388fdb0dc2720f66c30ddcec8a4fd
change-id: 20250813-master-23ce98cadee0
Best regards,
--
Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-10-30 7:35 ` Yannick FERTRE
2025-11-01 9:03 ` Simon Glass
2025-09-04 12:53 ` [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display Raphael Gallais-Pou
` (5 subsequent siblings)
6 siblings, 2 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
The "Display Timings" in panel-common.yaml can be provided by 2 properties
- panel-timing: when display panels are restricted to a single resolution
the "panel-timing" node expresses the required timings.
- display-timings: several resolutions with different timings are supported
with several timing subnode of "display-timings" node
This patch update the parsing function to handle this 2 possibility
when index = 0.
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
drivers/core/ofnode.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode parent, int index,
int ret = 0;
timings = ofnode_find_subnode(parent, "display-timings");
- if (!ofnode_valid(timings))
- return -EINVAL;
-
- i = 0;
- ofnode_for_each_subnode(node, timings) {
- if (i++ == index)
- break;
+ if (ofnode_valid(timings)) {
+ i = 0;
+ ofnode_for_each_subnode(node, timings) {
+ if (i++ == index)
+ break;
+ }
+ } else {
+ if (index != 0)
+ return -EINVAL;
+ node = ofnode_find_subnode(parent, "panel-timing");
}
if (!ofnode_valid(node))
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-10-30 7:41 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS Raphael Gallais-Pou
` (4 subsequent siblings)
6 siblings, 2 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
Add the compatible "panel-lvds" for simple-panel driver in U-Boot. In
Linux this compatible is managed by the driver
drivers/gpu/drm/panel/panel-lvds.c but in U-Boot the specific LVDS
features (bus_format/bus_flags) are not supported.
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
drivers/video/simple_panel.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index b6c5b058b2e967bbbd47ab3c3ce5ca52c7804409..0f23df701bc3c40ea49380bbfa3743ee592d8bd3 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -191,6 +191,7 @@ static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
static const struct udevice_id simple_panel_ids[] = {
{ .compatible = "simple-panel" },
+ { .compatible = "panel-lvds" },
{ .compatible = "auo,b133xtn01" },
{ .compatible = "auo,b116xw03" },
{ .compatible = "auo,b133htn01" },
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-10-30 7:42 ` Yannick FERTRE
2025-09-04 12:53 ` [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC Raphael Gallais-Pou
` (3 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
The LVDS Display Interface Transmitter handles the LVDS protocol:
it maps the pixels received from the upstream Pixel-DMA (LTDC)
onto the LVDS PHY.
The LVDS controller driver supports the following high-level features:
• FDP-Link-I and OpenLDI (v0.95) protocols
• Single-Link or Dual-Link operation
• Single-Display or Double-Display (with the same content
duplicated on both)
• Flexible Bit-Mapping, including JEIDA and VESA
• RGB888 or RGB666 output
• Synchronous design, with one input pixel per clock cycle
• No resolution limitation.
Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
MAINTAINERS | 1 +
doc/board/st/st-dt.rst | 1 +
drivers/video/stm32/Kconfig | 9 +
drivers/video/stm32/Makefile | 1 +
drivers/video/stm32/stm32_lvds.c | 693 +++++++++++++++++++++++++++++++++++++++
5 files changed, 705 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3fb163aa1db1dba249e60b40ab3e785db4fde5d3..604ba4ca04c1079b14cd8f4c1fff4c12da0e278d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -728,6 +728,7 @@ F: drivers/serial/serial_stm32.*
F: drivers/spi/stm32_qspi.c
F: drivers/spi/stm32_spi.c
F: drivers/video/stm32/stm32_ltdc.c
+F: drivers/video/stm32/stm32_lvds.c
F: drivers/watchdog/stm32mp_wdt.c
F: include/dt-bindings/clock/stm32fx-clock.h
F: include/dt-bindings/clock/stm32mp*
diff --git a/doc/board/st/st-dt.rst b/doc/board/st/st-dt.rst
index 2a285c81807ee26924948d8c2497f11dfe45b6e6..28cada97e72da711408a1aaaa928ad3faa51162e 100644
--- a/doc/board/st/st-dt.rst
+++ b/doc/board/st/st-dt.rst
@@ -25,6 +25,7 @@ kernel binding directory = Documentation/devicetree/bindings/
* display
- display/st,stm32-dsi.yaml
- display/st,stm32-ltdc.yaml
+ - display/st,stm32mp25-lvds.yaml
* gpio
- pinctrl/st,stm32-pinctrl.yaml
* hwlock
diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
index c354c402c288b4c004bd2b5cc74a4d00beef2773..4cb8a841caf51aaba5115b9fcddb967f73a3d9b4 100644
--- a/drivers/video/stm32/Kconfig
+++ b/drivers/video/stm32/Kconfig
@@ -23,6 +23,15 @@ config VIDEO_STM32_DSI
This option enables support DSI internal bridge which can be used on
devices which have DSI devices connected.
+config VIDEO_STM32_LVDS
+ bool "Enable STM32 LVDS video support"
+ depends on VIDEO_STM32
+ select VIDEO_BRIDGE
+ select VIDEO_DW_MIPI_DSI
+ help
+ This enables Low Voltage Differential Signaling (LVDS) display
+ support.
+
config VIDEO_STM32_MAX_XRES
int "Maximum horizontal resolution (for memory allocation purposes)"
depends on VIDEO_STM32
diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
index f8b42d1a4d126a31aa11ce820e829b133ca3bb2d..059d9000c1d7762d06bff36d5408143a34848227 100644
--- a/drivers/video/stm32/Makefile
+++ b/drivers/video/stm32/Makefile
@@ -7,3 +7,4 @@
obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
+obj-${CONFIG_VIDEO_STM32_LVDS} += stm32_lvds.o
diff --git a/drivers/video/stm32/stm32_lvds.c b/drivers/video/stm32/stm32_lvds.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf1393c9e8724ed099aeed63344ebdc0004507b6
--- /dev/null
+++ b/drivers/video/stm32/stm32_lvds.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 STMicroelectronics - All Rights Reserved
+ * Author(s): Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
+ *
+ * This Low Voltage Differential Signal controller driver is based on the Linux Kernel driver from
+ * drivers/gpu/drm/stm/ltdc.c
+ */
+
+#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
+
+#include <clk.h>
+#include <dm.h>
+#include <log.h>
+#include <media_bus_format.h>
+#include <panel.h>
+#include <reset.h>
+#include <video.h>
+#include <video_bridge.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <linux/iopoll.h>
+
+/* LVDS Host registers */
+#define LVDS_CR 0x0000 /* configuration register */
+#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */
+#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */
+#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */
+#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */
+#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */
+#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */
+#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */
+#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */
+#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */
+#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */
+#define LVDS_DMLCR(id) (LVDS_DMLCR0 + 8U * (id))
+#define LVDS_DMMCR(id) (LVDS_DMMCR0 + 8U * (id))
+#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */
+#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */
+
+#define CDL1CR_DEFAULT 0x4321
+#define CDL2CR_DEFAULT 0x59876
+
+/* LVDS Host registers */
+#define LVDS_PHY_MASTER 0x0
+#define LVDS_PHY_SLAVE 0x100
+
+/* phy parameter can only be one of those two above */
+#define LVDS_PXGCR(phy) ((phy) + 0x1000) /* Global Control Register */
+#define LVDS_PXCMCR1(phy) ((phy) + 0x100C) /* Current Mode Control Register 1 */
+#define LVDS_PXCMCR2(phy) ((phy) + 0x1010) /* Current Mode Control Register 2 */
+#define LVDS_PXSCR(phy) ((phy) + 0x1020) /* Serial Control Register */
+#define LVDS_PXBCR1(phy) ((phy) + 0x102C) /* Bias Control Register 1 */
+#define LVDS_PXBCR2(phy) ((phy) + 0x1030) /* Bias Control Register 2 */
+#define LVDS_PXBCR3(phy) ((phy) + 0x1034) /* Bias Control Register 3 */
+#define LVDS_PXMPLCR(phy) ((phy) + 0x1064) /* Monitor PLL Lock Control Register */
+#define LVDS_PXDCR(phy) ((phy) + 0x1084) /* Debug Control Register */
+#define LVDS_PXSSR1(phy) ((phy) + 0x1088) /* Spare Status Register 1 */
+#define LVDS_PXCFGCR(phy) ((phy) + 0x10A0) /* Configuration Control Register */
+#define LVDS_PXPLLCR1(phy) ((phy) + 0x10C0) /* PLL_MODE 1 Control Register */
+#define LVDS_PXPLLCR2(phy) ((phy) + 0x10C4) /* PLL_MODE 2 Control Register */
+#define LVDS_PXPLLSR(phy) ((phy) + 0x10C8) /* PLL Status Register */
+#define LVDS_PXPLLSDCR1(phy) ((phy) + 0x10CC) /* PLL_SD_1 Control Register */
+#define LVDS_PXPLLSDCR2(phy) ((phy) + 0x10D0) /* PLL_SD_2 Control Register */
+#define LVDS_PXPLLTWGCR1(phy) ((phy) + 0x10D4) /* PLL_TWG_1 Control Register */
+#define LVDS_PXPLLTWGCR2(phy) ((phy) + 0x10D8) /* PLL_TWG_2 Control Register */
+#define LVDS_PXPLLCPCR(phy) ((phy) + 0x10E0) /* PLL_CP Control Register */
+#define LVDS_PXPLLTESTCR(phy) ((phy) + 0x10E8) /* PLL_TEST Control Register */
+
+/* LVDS Wrapper registers */
+#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */
+#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */
+#define LVDS_VERR 0x1FF4 /* Version register */
+#define LVDS_IPIDR 0x1FF8 /* Identification register */
+#define LVDS_SIDR 0x1FFC /* Size Identification register */
+
+#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */
+#define CR_HSPOL BIT(1) /* HS Polarity (horizontal sync) */
+#define CR_VSPOL BIT(2) /* VS Polarity (vertical sync) */
+#define CR_DEPOL BIT(3) /* DE Polarity (data enable) */
+#define CR_CI BIT(4) /* Control Internal (software controlled bit) */
+#define CR_LKMOD BIT(5) /* Link Mode, for both Links */
+#define CR_LKPHA BIT(6) /* Link Phase, for both Links */
+#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */
+#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */
+
+#define DMMCRX_MAP0 GENMASK(4, 0)
+#define DMMCRX_MAP1 GENMASK(9, 5)
+#define DMMCRX_MAP2 GENMASK(14, 10)
+#define DMMCRX_MAP3 GENMASK(19, 15)
+#define DMLCRX_MAP4 GENMASK(4, 0)
+#define DMLCRX_MAP5 GENMASK(9, 5)
+#define DMLCRX_MAP6 GENMASK(14, 10)
+
+#define CDLCRX_DISTR0 GENMASK(3, 0)
+#define CDLCRX_DISTR1 GENMASK(7, 4)
+#define CDLCRX_DISTR2 GENMASK(11, 8)
+#define CDLCRX_DISTR3 GENMASK(15, 12)
+#define CDLCRX_DISTR4 GENMASK(19, 16)
+
+#define FREF_INDEX 0
+#define NDIV_INDEX 1
+#define FPFD_INDEX 2
+#define MDIV_INDEX 3
+#define FVCO_INDEX 4
+#define BDIV_INDEX 5
+#define FBIT_INDEX 6
+#define FLS_INDEX 7
+#define FDP_INDEX 8
+
+#define PXGCR_BIT_CLK_OUT BIT(0)
+#define PXGCR_LS_CLK_OUT BIT(4)
+#define PXGCR_DP_CLK_OUT BIT(8)
+#define PXGCR_RSTZ BIT(24)
+#define PXGCR_DIV_RSTN BIT(25)
+
+#define PXCMCR1_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4))
+#define PXCMCR2_CM_EN_DL4 BIT(4)
+#define PXSCR_SER_DATA_OK BIT(16)
+#define PXBCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
+#define PXBCR2_BIAS_EN BIT(28)
+#define PXBCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
+#define PXDCR_POWER_OK BIT(12)
+#define PXCFGCR_EN_DIG_DL GENMASK(4, 0)
+
+#define PXPLLCR1_PLL_EN BIT(0)
+#define PxPLLCR1_SD_EN BIT(1)
+#define PXPLLCR1_TWG_EN BIT(2)
+#define PXPLLCR1_PLL_DIVIDERS_EN BIT(8)
+#define PXPLLCR2_NDIV GENMASK(25, 16)
+#define PXPLLCR2_BDIV GENMASK(9, 0)
+#define PXPLLSR_PLL_LOCK BIT(0)
+#define PXPLLSDCR1_MDIV GENMASK(9, 0)
+#define PXPLLCPCR_CPCTRL_DEFAULT 0x1
+#define PXPLLTESTCR_PLL_TEST_CLK_EN BIT(0)
+#define PXPLLTESTCR_PLL_TDIV_EN BIT(8)
+#define PXPLLTESTCR_TDIV GENMASK(25, 16)
+#define PXPLLTESTCR_TDIV_VALUE 70
+
+#define WCLKCR_SLV_CLKPIX_SEL BIT(0)
+#define WCLKCR_SRCSEL BIT(8)
+
+/* Sleep & timeout for pll lock/unlock */
+#define SLEEP_US 1000
+#define TIMEOUT_US 20000000
+
+#define PHY_SLV_OFS 0x100
+
+/* PLL parameters */
+#define NDIV_MIN 2
+#define NDIV_MAX 6
+#define BDIV_MIN 2
+#define BDIV_MAX 6
+#define MDIV_MIN 1
+#define MDIV_MAX 1023
+
+struct stm32_lvds_plat {
+ void __iomem *base;
+ struct udevice *panel;
+ struct reset_ctl rst;
+ struct clk pclk;
+ struct clk refclk;
+};
+
+struct stm32_lvds_priv {
+ struct display_timing timings;
+ u32 refclk_rate;
+ int dual_link;
+ int bus_format;
+};
+
+/*
+ * enum lvds_pixels_order - Pixel order of an LVDS connection
+ * @LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
+ * from the first port, odd pixels from the second port
+ * @LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated
+ * from the first port, even pixels from the second port
+ */
+enum lvds_pixels_order {
+ LVDS_DUAL_LINK_EVEN_ODD_PIXELS = BIT(0),
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS = BIT(1),
+};
+
+enum lvds_pixel {
+ PIX_R_0 = 0x00,
+ PIX_R_1 = 0x01,
+ PIX_R_2 = 0x02,
+ PIX_R_3 = 0x03,
+ PIX_R_4 = 0x04,
+ PIX_R_5 = 0x05,
+ PIX_R_6 = 0x06,
+ PIX_R_7 = 0x07,
+ PIX_G_0 = 0x08,
+ PIX_G_1 = 0x09,
+ PIX_G_2 = 0x0A,
+ PIX_G_3 = 0x0B,
+ PIX_G_4 = 0x0C,
+ PIX_G_5 = 0x0D,
+ PIX_G_6 = 0x0E,
+ PIX_G_7 = 0x0F,
+ PIX_B_0 = 0x10,
+ PIX_B_1 = 0x11,
+ PIX_B_2 = 0x12,
+ PIX_B_3 = 0x13,
+ PIX_B_4 = 0x14,
+ PIX_B_5 = 0x15,
+ PIX_B_6 = 0x16,
+ PIX_B_7 = 0x17,
+ PIX_H_S = 0x18,
+ PIX_V_S = 0x19,
+ PIX_D_E = 0x1A,
+ PIX_C_E = 0x1B,
+ PIX_C_I = 0x1C,
+ PIX_TOG = 0x1D,
+ PIX_ONE = 0x1E,
+ PIX_ZER = 0x1F,
+};
+
+/*
+ * Expected JEIDA-RGB888 data to be sent in LSB format
+ * bit6 ............................bit0
+ */
+const enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
+ { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
+ { PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
+ { PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
+ { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
+ { PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
+};
+
+/*
+ * Expected VESA-RGB888 data to be sent in LSB format
+ * bit6 ............................bit0
+ */
+const enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
+ { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
+ { PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
+ { PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
+ { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
+ { PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
+};
+
+static inline void lvds_writel(void __iomem *base, u32 reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+static inline u32 lvds_readl(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void lvds_set(void __iomem *base, u32 reg, u32 mask)
+{
+ lvds_writel(base, reg, lvds_readl(base, reg) | mask);
+}
+
+static inline void lvds_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ lvds_writel(base, reg, lvds_readl(base, reg) & ~mask);
+}
+
+static u32 pll_get_clkout_khz(u32 clkin_khz, u32 bdiv, u32 mdiv, u32 ndiv)
+{
+ int divisor = ndiv * bdiv;
+
+ /* Prevents from division by 0 */
+ if (!divisor)
+ return 0;
+
+ return clkin_khz * mdiv / divisor;
+}
+
+static int lvds_pll_get_params(u32 clkin_khz, u32 clkout_khz,
+ u32 *bdiv, u32 *mdiv, u32 *ndiv)
+{
+ u32 i, o, n;
+ u32 delta, best_delta; /* all in khz */
+
+ /* Early checks preventing division by 0 & odd results */
+ if (clkin_khz == 0 || clkout_khz == 0)
+ return -EINVAL;
+
+ best_delta = 1000000; /* big started value (1000000khz) */
+
+ for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
+ for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
+ n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
+ /* Check ndiv according to vco range */
+ if (n < MDIV_MIN || n > MDIV_MAX)
+ continue;
+ /* Check if new delta is better & saves parameters */
+ delta = abs(pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz);
+ if (delta < best_delta) {
+ *ndiv = i;
+ *mdiv = n;
+ *bdiv = o;
+ best_delta = delta;
+ }
+ /* fast return in case of "perfect result" */
+ if (!delta)
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_lvds_pll_enable(struct udevice *dev,
+ int phy)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ struct display_timing timings = priv->timings;
+ u32 pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
+ int ret, val, multiplier;
+
+ /* Release PHY from reset */
+ lvds_set(plat->base, LVDS_PXGCR(phy), PXGCR_DIV_RSTN | PXGCR_RSTZ);
+
+ /* lvds_pll_config */
+ /* Set PLL Slv & Mst configs and timings */
+ pll_in_khz = priv->refclk_rate / 1000;
+
+ if (priv->dual_link)
+ multiplier = 2;
+ else
+ multiplier = 1;
+
+ ret = lvds_pll_get_params(pll_in_khz, timings.pixelclock.typ * 7 / 1000 / multiplier,
+ &bdiv, &mdiv, &ndiv);
+ if (ret)
+ return ret;
+
+ /* Set PLL parameters */
+ lvds_writel(plat->base, LVDS_PXPLLCR2(phy), (ndiv << 16) | bdiv);
+ lvds_writel(plat->base, LVDS_PXPLLSDCR1(phy), mdiv);
+ lvds_writel(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_TDIV_VALUE << 16);
+
+ /* Disable TWG and SD: for now, PLL just need to be in integer mode */
+ lvds_clear(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_TWG_EN | PxPLLCR1_SD_EN);
+
+ /* Power up bias and PLL dividers */
+ lvds_set(plat->base, LVDS_PXDCR(phy), PXDCR_POWER_OK);
+
+ lvds_set(plat->base, LVDS_PXCMCR1(phy), PXCMCR1_CM_EN_DL);
+ lvds_set(plat->base, LVDS_PXCMCR2(phy), PXCMCR2_CM_EN_DL4);
+
+ lvds_set(plat->base, LVDS_PXPLLCPCR(phy), PXPLLCPCR_CPCTRL_DEFAULT);
+ lvds_set(plat->base, LVDS_PXBCR3(phy), PXBCR3_VM_EN_DL);
+ lvds_set(plat->base, LVDS_PXBCR1(phy), PXBCR1_EN_BIAS_DL);
+ lvds_set(plat->base, LVDS_PXCFGCR(phy), PXCFGCR_EN_DIG_DL);
+
+ /* lvds_pll_enable */
+ /* PLL lock timing control for the monitor unmask after startup (pll_en) */
+ /* Adjust the value so that the masking window is opened at start-up */
+ /* MST_MON_PLL_LOCK_UNMASK_TUNE */
+ lvds_writel(plat->base, LVDS_PXMPLCR(phy), (0x200 - 0x160) << 16);
+
+ lvds_writel(plat->base, LVDS_PXBCR2(phy), PXBCR2_BIAS_EN);
+
+ lvds_set(plat->base, LVDS_PXGCR(phy),
+ PXGCR_DP_CLK_OUT | PXGCR_LS_CLK_OUT | PXGCR_BIT_CLK_OUT);
+
+ lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TDIV_EN);
+ lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_DIVIDERS_EN);
+ lvds_set(plat->base, LVDS_PXSCR(phy), PXSCR_SER_DATA_OK);
+
+ /* Enable the LVDS PLL & wait for its lock */
+ lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_EN);
+ ret = readl_poll_sleep_timeout(plat->base + LVDS_PXPLLSR(phy),
+ val, val & PXPLLSR_PLL_LOCK, SLEEP_US, TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /* Select MST PHY clock as pixel clock for the LDITX instead of FREF */
+ /* WCLKCR_SLV_CLKPIX_SEL is for dual link */
+ lvds_writel(plat->base, LVDS_WCLKCR, WCLKCR_SLV_CLKPIX_SEL);
+
+ lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TEST_CLK_EN);
+
+ return 0;
+}
+
+static int stm32_lvds_enable(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ struct display_timing timings = priv->timings;
+ u32 lvds_cdl1cr = 0;
+ u32 lvds_cdl2cr = 0;
+ u32 lvds_dmlcr = 0;
+ u32 lvds_dmmcr = 0;
+ u32 lvds_cr = 0;
+ int i;
+
+ lvds_clear(plat->base, LVDS_CDL1CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
+ | CDLCRX_DISTR3 | CDLCRX_DISTR4);
+ lvds_clear(plat->base, LVDS_CDL2CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
+ | CDLCRX_DISTR3 | CDLCRX_DISTR4);
+
+ /* Set channel distribution */
+ lvds_cr &= ~CR_LKMOD;
+ lvds_cdl1cr = CDL1CR_DEFAULT;
+
+ if (priv->dual_link) {
+ lvds_cr |= CR_LKMOD;
+ lvds_cdl2cr = CDL2CR_DEFAULT;
+ }
+
+ /* Set signal polarity */
+ if (timings.flags & DISPLAY_FLAGS_DE_LOW)
+ lvds_cr |= CR_DEPOL;
+
+ if (timings.flags & DISPLAY_FLAGS_HSYNC_LOW)
+ lvds_cr |= CR_HSPOL;
+
+ if (timings.flags & DISPLAY_FLAGS_VSYNC_LOW)
+ lvds_cr |= CR_VSPOL;
+
+ /* Set link phase */
+ switch (priv->dual_link) {
+ case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
+ lvds_cr &= ~CR_LKPHA;
+ break;
+ case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
+ lvds_cr |= CR_LKPHA;
+ break;
+ default:
+ dev_dbg(dev, "No phase precised, setting default\n");
+ lvds_cr &= ~CR_LKPHA;
+ break;
+ }
+
+ /* Set Data Mapping */
+ switch (priv->bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
+ for (i = 0; i < 5; i++) {
+ lvds_dmlcr = ((lvds_bitmap_vesa_rgb888[i][0])
+ + (lvds_bitmap_vesa_rgb888[i][1] << 5)
+ + (lvds_bitmap_vesa_rgb888[i][2] << 10)
+ + (lvds_bitmap_vesa_rgb888[i][3] << 15));
+ lvds_dmmcr = ((lvds_bitmap_vesa_rgb888[i][4])
+ + (lvds_bitmap_vesa_rgb888[i][5] << 5)
+ + (lvds_bitmap_vesa_rgb888[i][6] << 10));
+
+ /* Write registers at the end of computations */
+ lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
+ lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
+ }
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
+ for (i = 0; i < 5; i++) {
+ lvds_dmlcr = ((lvds_bitmap_jeida_rgb888[i][0])
+ + (lvds_bitmap_jeida_rgb888[i][1] << 5)
+ + (lvds_bitmap_jeida_rgb888[i][2] << 10)
+ + (lvds_bitmap_jeida_rgb888[i][3] << 15));
+ lvds_dmmcr = ((lvds_bitmap_jeida_rgb888[i][4])
+ + (lvds_bitmap_jeida_rgb888[i][5] << 5)
+ + (lvds_bitmap_jeida_rgb888[i][6] << 10));
+
+ /* Write registers at the end of computations */
+ lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
+ lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
+ }
+ break;
+ default:
+ dev_dbg(dev, "Unsupported LVDS bus format 0x%04x\n", priv->bus_format);
+ }
+
+ /* Turn the output on */
+ lvds_cr |= CR_LVDSEN;
+
+ /* Commit config to registers */
+ lvds_set(plat->base, LVDS_CR, lvds_cr);
+ lvds_writel(plat->base, LVDS_CDL1CR, lvds_cdl1cr);
+ lvds_writel(plat->base, LVDS_CDL2CR, lvds_cdl2cr);
+
+ return 0;
+}
+
+static int stm32_lvds_attach(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = panel_get_display_timing(plat->panel, &priv->timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
+ 0, &priv->timings);
+ if (ret) {
+ dev_err(dev, "decode display timing error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = stm32_lvds_enable(dev);
+
+ return ret;
+}
+
+static int stm32_lvds_set_backlight(struct udevice *dev, int percent)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ int ret;
+
+ ret = panel_enable_backlight(plat->panel);
+ if (ret) {
+ dev_err(dev, "panel %s enable backlight error %d\n",
+ plat->panel->name, ret);
+ }
+
+ return ret;
+}
+
+static int lvds_handle_pixel_order(struct stm32_lvds_plat *plat)
+{
+ ofnode parent, panel_port0, panel_port1;
+ bool even_pixels, odd_pixels;
+ int port0, port1;
+
+ /*
+ * In case we are operating in single link,
+ * there is only one port linked to the LVDS.
+ * Check whether we are in this case and exit if yes.
+ */
+ parent = ofnode_find_subnode(dev_ofnode(plat->panel), "ports");
+ if (!ofnode_valid(parent))
+ return 0;
+
+ panel_port0 = ofnode_first_subnode(parent);
+ if (!ofnode_valid(panel_port0))
+ return -EPIPE;
+
+ even_pixels = ofnode_read_bool(panel_port0, "dual-lvds-even-pixels");
+ odd_pixels = ofnode_read_bool(panel_port0, "dual-lvds-odd-pixels");
+ if (even_pixels && odd_pixels)
+ return -EINVAL;
+
+ port0 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+ panel_port1 = ofnode_next_subnode(panel_port0);
+ if (!ofnode_valid(panel_port1))
+ return -EPIPE;
+
+ even_pixels = ofnode_read_bool(panel_port1, "dual-lvds-even-pixels");
+ odd_pixels = ofnode_read_bool(panel_port1, "dual-lvds-odd-pixels");
+ if (even_pixels && odd_pixels)
+ return -EINVAL;
+
+ port1 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+ /*
+ * A valid dual-LVDS bus is found when one port is marked with
+ * "dual-lvds-even-pixels", and the other port is marked with
+ * "dual-lvds-odd-pixels", bail out if the markers are not right.
+ */
+ if (port0 + port1 != LVDS_DUAL_LINK_EVEN_ODD_PIXELS + LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
+ return -EINVAL;
+
+ return port0;
+}
+
+static int stm32_lvds_of_to_plat(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ const char *data_mapping;
+ int ret;
+
+ plat->base = dev_read_addr_ptr(dev);
+ if ((fdt_addr_t)plat->base == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Unable to read LVDS base address\n");
+ return -EINVAL;
+ }
+
+ ret = clk_get_by_name(dev, "pclk", &plat->pclk);
+ if (ret) {
+ dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "ref", &plat->refclk);
+ if (ret) {
+ dev_err(dev, "Unable to get reference clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_index(dev, 0, &plat->rst);
+ if (ret) {
+ dev_err(dev, "Failed to get LVDS reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_driver(UCLASS_PANEL,
+ DM_DRIVER_GET(simple_panel), &plat->panel);
+ if (ret) {
+ dev_err(dev, "panel device error %d\n", ret);
+ return ret;
+ }
+
+ ret = panel_get_display_timing(plat->panel, &priv->timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
+ 0, &priv->timings);
+ if (ret) {
+ dev_err(dev, "decode display timing error %d\n", ret);
+ return ret;
+ }
+ }
+
+ data_mapping = ofnode_read_string(dev_ofnode(plat->panel), "data-mapping");
+ if (!strcmp(data_mapping, "vesa-24"))
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+ else if (!strcmp(data_mapping, "jeida-24"))
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
+ else
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+
+ return 0;
+}
+
+static int stm32_lvds_probe(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = clk_enable(&plat->pclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&plat->refclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable reference clock: %d\n", ret);
+ goto err_clk;
+ }
+
+ priv->refclk_rate = (unsigned int)clk_get_rate(&plat->refclk);
+
+ reset_deassert(&plat->rst);
+
+ /* Handle dual link config */
+ priv->dual_link = lvds_handle_pixel_order(plat);
+ if (priv->dual_link < 0)
+ goto err_rst;
+
+ if (priv->dual_link > 0) {
+ ret = stm32_lvds_pll_enable(dev, LVDS_PHY_SLAVE);
+ if (ret)
+ goto err_rst;
+ }
+
+ ret = stm32_lvds_pll_enable(dev, LVDS_PHY_MASTER);
+ if (ret)
+ goto err_rst;
+
+ return 0;
+
+err_rst:
+ clk_disable(&plat->refclk);
+err_clk:
+ clk_disable(&plat->pclk);
+
+ return ret;
+}
+
+static const struct video_bridge_ops stm32_lvds_ops = {
+ .attach = stm32_lvds_attach,
+ .set_backlight = stm32_lvds_set_backlight,
+};
+
+static const struct udevice_id stm32_lvds_ids[] = {
+ {.compatible = "st,stm32mp25-lvds"},
+ {}
+};
+
+U_BOOT_DRIVER(stm32_lvds) = {
+ .name = "stm32-display-lvds",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = stm32_lvds_ids,
+ .ops = &stm32_lvds_ops,
+ .of_to_plat = stm32_lvds_of_to_plat,
+ .probe = stm32_lvds_probe,
+ .plat_auto = sizeof(struct stm32_lvds_plat),
+ .priv_auto = sizeof(struct stm32_lvds_priv),
+};
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
` (2 preceding siblings ...)
2025-09-04 12:53 ` [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-09-12 16:41 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-09-04 12:53 ` [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel Raphael Gallais-Pou
` (2 subsequent siblings)
6 siblings, 2 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
STM32MP2 SoCs feature a new version of the LTDC IP. This new version
features a bus clock, as well as a 150MHz pad frequency. Add its
compatible to the list of device to probe and handle quirks. The new
hardware version features a bus clock.
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
drivers/video/stm32/stm32_ltdc.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
index 0a062c8939dbe49b11aa50f5ca9701bdbe5c5b0b..efe9a00996eca0301d2a2b82074ba9690a967a73 100644
--- a/drivers/video/stm32/stm32_ltdc.c
+++ b/drivers/video/stm32/stm32_ltdc.c
@@ -262,6 +262,7 @@ static const u32 layer_regs_a2[] = {
#define HWVER_10300 0x010300
#define HWVER_20101 0x020101
#define HWVER_40100 0x040100
+#define HWVER_40101 0x040101
enum stm32_ltdc_pix_fmt {
PF_ARGB8888 = 0, /* ARGB [32 bits] */
@@ -529,7 +530,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
struct udevice *bridge = NULL;
struct udevice *panel = NULL;
struct display_timing timings;
- struct clk pclk;
+ struct clk pclk, bclk;
struct reset_ctl rst;
ulong rate;
int ret;
@@ -540,7 +541,21 @@ static int stm32_ltdc_probe(struct udevice *dev)
return -EINVAL;
}
- ret = clk_get_by_index(dev, 0, &pclk);
+ ret = clk_get_by_name(dev, "bus", &bclk);
+ if (ret) {
+ if (ret != -ENODATA) {
+ dev_err(dev, "bus clock get error %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = clk_enable(&bclk);
+ if (ret) {
+ dev_err(dev, "bus clock enable error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = clk_get_by_name(dev, "lcd", &pclk);
if (ret) {
dev_err(dev, "peripheral clock get error %d\n", ret);
return ret;
@@ -566,6 +581,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
priv->pix_fmt_hw = pix_fmt_a1;
break;
case HWVER_40100:
+ case HWVER_40101:
priv->layer_regs = layer_regs_a2;
priv->pix_fmt_hw = pix_fmt_a2;
break;
@@ -688,6 +704,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
static const struct udevice_id stm32_ltdc_ids[] = {
{ .compatible = "st,stm32-ltdc" },
+ { .compatible = "st,stm32mp251-ltdc" },
+ { .compatible = "st,stm32mp255-ltdc" },
{ }
};
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
` (3 preceding siblings ...)
2025-09-04 12:53 ` [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-09-04 12:53 ` [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
6 siblings, 2 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
Initially there was only one DSI bridge with one panel attached to this
device. This explained the call to uclass_first_device_err(UCLASS_PANEL,
...) which worked fine at the time.
Now that multiple bridges and panels, with different technologies, can
be plugged onto the board this way to get the panel device is outdated.
The lookup is done is two steps. First we circle through the
UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
endpoints until we get a UCLASS_PANEL device available.
Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
drivers/video/stm32/stm32_ltdc.c | 136 +++++++++++++++++++++++++++++++++++----
1 file changed, 125 insertions(+), 11 deletions(-)
diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
index efe9a00996eca0301d2a2b82074ba9690a967a73..834bfb625d2d34a44bd8edff1c92af6dec344c20 100644
--- a/drivers/video/stm32/stm32_ltdc.c
+++ b/drivers/video/stm32/stm32_ltdc.c
@@ -17,6 +17,7 @@
#include <video_bridge.h>
#include <asm/io.h>
#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/printk.h>
@@ -495,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr)
setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN);
}
+static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node,
+ enum uclass_id id, struct udevice **remote_dev)
+{
+ u32 remote_phandle;
+ ofnode remote;
+ int ret = 0;
+
+ ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
+ if (ret) {
+ dev_err(dev, "%s(%s): Could not find remote-endpoint property\n",
+ __func__, dev_read_name(dev));
+ return ret;
+ }
+
+ remote = ofnode_get_by_phandle(remote_phandle);
+ if (!ofnode_valid(remote))
+ return -EINVAL;
+
+ while (ofnode_valid(remote)) {
+ remote = ofnode_get_parent(remote);
+ if (!ofnode_valid(remote)) {
+ dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n",
+ __func__, dev_read_name(dev), id);
+ continue;
+ }
+
+ ret = uclass_find_device_by_ofnode(id, remote, remote_dev);
+ if (*remote_dev && !ret) {
+ ret = uclass_get_device_by_ofnode(id, remote, remote_dev);
+ if (ret)
+ dev_dbg(dev, "%s(%s): failed to get remote device %s\n",
+ __func__, dev_read_name(dev), dev_read_name(*remote_dev));
+ break;
+ }
+ };
+
+ return ret;
+}
+
+static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel)
+{
+ ofnode ep_node, node, ports;
+ int ret = 0;
+
+ if (!dev)
+ return -EINVAL;
+
+ ports = ofnode_find_subnode(dev_ofnode(dev), "ports");
+ if (!ofnode_valid(ports)) {
+ dev_err(dev, "Remote bridge subnode\n");
+ return ret;
+ }
+
+ for (node = ofnode_first_subnode(ports);
+ ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ ep_node = ofnode_first_subnode(node);
+ if (!ofnode_valid(ep_node))
+ continue;
+
+ ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel);
+ }
+
+ /* Sanity check, we can get out of the loop without having a clean ofnode */
+ if (!(*panel))
+ ret = -EINVAL;
+ else
+ if (!ofnode_valid(dev_ofnode(*panel)))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node,
+ struct udevice **panel, struct udevice **bridge)
+{
+ int ret;
+
+ if (*panel)
+ return -EINVAL;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
+ ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge);
+ if (ret)
+ return ret;
+
+ ret = stm32_ltdc_get_panel(*bridge, panel);
+ } else {
+ /* no bridge, search a panel from display controller node */
+ ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel);
+ }
+
+ return ret;
+}
+
#if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY)
static int stm32_ltdc_alloc_fb(struct udevice *dev)
{
@@ -532,6 +628,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
struct display_timing timings;
struct clk pclk, bclk;
struct reset_ctl rst;
+ ofnode node, port;
ulong rate;
int ret;
@@ -568,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
}
priv->hw_version = readl(priv->regs + LTDC_IDR);
- debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
+ dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
switch (priv->hw_version) {
case HWVER_10200:
@@ -589,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev)
return -ENODEV;
}
- ret = uclass_first_device_err(UCLASS_PANEL, &panel);
- if (ret) {
- if (ret != -ENODEV)
- dev_err(dev, "panel device error %d\n", ret);
- return ret;
+ /*
+ * Try all the ports until one working.
+ *
+ * This is done in two times. First is checks for the
+ * UCLASS_VIDEO_BRIDGE available, and then for this bridge
+ * it scans for a UCLASS_PANEL.
+ */
+
+ port = dev_read_subnode(dev, "port");
+ if (!ofnode_valid(port)) {
+ dev_err(dev, "%s(%s): 'port' subnode not found\n",
+ __func__, dev_read_name(dev));
+ return -EINVAL;
}
+ for (node = ofnode_first_subnode(port);
+ ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge);
+ if (ret)
+ dev_dbg(dev, "Device failed ret=%d\n", ret);
+ else
+ break;
+ }
+
+ /* Sanity check */
+ if (ret)
+ return ret;
+
ret = panel_get_display_timing(panel, &timings);
if (ret) {
ret = ofnode_decode_display_timing(dev_ofnode(panel),
@@ -624,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev)
reset_deassert(&rst);
if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
- ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
- if (ret)
- dev_dbg(dev,
- "No video bridge, or no backlight on bridge\n");
-
if (bridge) {
ret = video_bridge_attach(bridge);
if (ret) {
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
` (4 preceding siblings ...)
2025-09-04 12:53 ` [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
6 siblings, 2 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
Use LTDC and LVDS nodes in all boot phases. This is specially useful
before U-Boot relocation.
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
--- a/arch/arm/dts/stm32mp25-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
@@ -93,6 +93,14 @@
bootph-all;
};
+<dc {
+ bootph-all;
+};
+
+&lvds {
+ bootph-all;
+};
+
&pinctrl {
bootph-all;
};
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
` (5 preceding siblings ...)
2025-09-04 12:53 ` [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot Raphael Gallais-Pou
@ 2025-09-04 12:53 ` Raphael Gallais-Pou
2025-09-12 16:46 ` Patrice CHOTARD
` (2 more replies)
6 siblings, 3 replies; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-04 12:53 UTC (permalink / raw)
To: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
Compile VIDEO_STM32 and VIDEO_STM32_LVDS by default.
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
---
configs/stm32mp25_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/configs/stm32mp25_defconfig b/configs/stm32mp25_defconfig
index 2b02cd86d6134497151e398eb54230f08d4e6272..e84df4a2a8f52890376f7689b01d3dcab5079ed9 100644
--- a/configs/stm32mp25_defconfig
+++ b/configs/stm32mp25_defconfig
@@ -78,6 +78,9 @@ CONFIG_SERIAL_RX_BUFFER=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
# CONFIG_OPTEE_TA_AVB is not set
+CONFIG_VIDEO=y
+CONFIG_VIDEO_STM32=y
+CONFIG_VIDEO_STM32_LVDS=y
CONFIG_WDT=y
CONFIG_WDT_STM32MP=y
CONFIG_WDT_ARM_SMC=y
--
2.25.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* Re: [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-09-04 12:53 ` [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC Raphael Gallais-Pou
@ 2025-09-12 16:41 ` Patrice CHOTARD
2025-09-15 13:05 ` Raphael Gallais-Pou
2025-10-30 7:43 ` Yannick FERTRE
1 sibling, 1 reply; 39+ messages in thread
From: Patrice CHOTARD @ 2025-09-12 16:41 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> STM32MP2 SoCs feature a new version of the LTDC IP. This new version
> features a bus clock, as well as a 150MHz pad frequency. Add its
> compatible to the list of device to probe and handle quirks. The new
> hardware version features a bus clock.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/stm32/stm32_ltdc.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index 0a062c8939dbe49b11aa50f5ca9701bdbe5c5b0b..efe9a00996eca0301d2a2b82074ba9690a967a73 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -262,6 +262,7 @@ static const u32 layer_regs_a2[] = {
> #define HWVER_10300 0x010300
> #define HWVER_20101 0x020101
> #define HWVER_40100 0x040100
> +#define HWVER_40101 0x040101
>
> enum stm32_ltdc_pix_fmt {
> PF_ARGB8888 = 0, /* ARGB [32 bits] */
> @@ -529,7 +530,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> struct udevice *bridge = NULL;
> struct udevice *panel = NULL;
> struct display_timing timings;
> - struct clk pclk;
> + struct clk pclk, bclk;
> struct reset_ctl rst;
> ulong rate;
> int ret;
> @@ -540,7 +541,21 @@ static int stm32_ltdc_probe(struct udevice *dev)
> return -EINVAL;
> }
>
> - ret = clk_get_by_index(dev, 0, &pclk);
> + ret = clk_get_by_name(dev, "bus", &bclk);
> + if (ret) {
> + if (ret != -ENODATA) {
> + dev_err(dev, "bus clock get error %d\n", ret);
> + return ret;
> + }
> + } else {
> + ret = clk_enable(&bclk);
> + if (ret) {
> + dev_err(dev, "bus clock enable error %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = clk_get_by_name(dev, "lcd", &pclk);
> if (ret) {
> dev_err(dev, "peripheral clock get error %d\n", ret);
> return ret;
> @@ -566,6 +581,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> priv->pix_fmt_hw = pix_fmt_a1;
> break;
> case HWVER_40100:
> + case HWVER_40101:
> priv->layer_regs = layer_regs_a2;
> priv->pix_fmt_hw = pix_fmt_a2;
> break;
> @@ -688,6 +704,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
>
> static const struct udevice_id stm32_ltdc_ids[] = {
> { .compatible = "st,stm32-ltdc" },
> + { .compatible = "st,stm32mp251-ltdc" },
> + { .compatible = "st,stm32mp255-ltdc" },
in V1, only "st,stm32mp251-ltdc" compatible was introduced.
Why are you adding "st,stm32mp255-ltdc" compatible ?
For me only "st,stm32-ltdc" is needed, currently, nothing in drivers/video/stm32/stm32_ltdc.c
is justifying to add "st,stm32mp251-ltdc" or "st,stm32mp251-ltdc" new compatible.
Patrice
> { }
> };
>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel
2025-09-04 12:53 ` [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel Raphael Gallais-Pou
@ 2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
1 sibling, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-09-12 16:45 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> Initially there was only one DSI bridge with one panel attached to this
> device. This explained the call to uclass_first_device_err(UCLASS_PANEL,
> ...) which worked fine at the time.
>
> Now that multiple bridges and panels, with different technologies, can
> be plugged onto the board this way to get the panel device is outdated.
>
> The lookup is done is two steps. First we circle through the
> UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
> endpoints until we get a UCLASS_PANEL device available.
>
> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/stm32/stm32_ltdc.c | 136 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 125 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index efe9a00996eca0301d2a2b82074ba9690a967a73..834bfb625d2d34a44bd8edff1c92af6dec344c20 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -17,6 +17,7 @@
> #include <video_bridge.h>
> #include <asm/io.h>
> #include <dm/device-internal.h>
> +#include <dm/uclass-internal.h>
> #include <dm/device_compat.h>
> #include <linux/bitops.h>
> #include <linux/printk.h>
> @@ -495,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr)
> setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN);
> }
>
> +static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node,
> + enum uclass_id id, struct udevice **remote_dev)
> +{
> + u32 remote_phandle;
> + ofnode remote;
> + int ret = 0;
> +
> + ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> + if (ret) {
> + dev_err(dev, "%s(%s): Could not find remote-endpoint property\n",
> + __func__, dev_read_name(dev));
> + return ret;
> + }
> +
> + remote = ofnode_get_by_phandle(remote_phandle);
> + if (!ofnode_valid(remote))
> + return -EINVAL;
> +
> + while (ofnode_valid(remote)) {
> + remote = ofnode_get_parent(remote);
> + if (!ofnode_valid(remote)) {
> + dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n",
> + __func__, dev_read_name(dev), id);
> + continue;
> + }
> +
> + ret = uclass_find_device_by_ofnode(id, remote, remote_dev);
> + if (*remote_dev && !ret) {
> + ret = uclass_get_device_by_ofnode(id, remote, remote_dev);
> + if (ret)
> + dev_dbg(dev, "%s(%s): failed to get remote device %s\n",
> + __func__, dev_read_name(dev), dev_read_name(*remote_dev));
> + break;
> + }
> + };
> +
> + return ret;
> +}
> +
> +static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel)
> +{
> + ofnode ep_node, node, ports;
> + int ret = 0;
> +
> + if (!dev)
> + return -EINVAL;
> +
> + ports = ofnode_find_subnode(dev_ofnode(dev), "ports");
> + if (!ofnode_valid(ports)) {
> + dev_err(dev, "Remote bridge subnode\n");
> + return ret;
> + }
> +
> + for (node = ofnode_first_subnode(ports);
> + ofnode_valid(node);
> + node = dev_read_next_subnode(node)) {
> + ep_node = ofnode_first_subnode(node);
> + if (!ofnode_valid(ep_node))
> + continue;
> +
> + ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel);
> + }
> +
> + /* Sanity check, we can get out of the loop without having a clean ofnode */
> + if (!(*panel))
> + ret = -EINVAL;
> + else
> + if (!ofnode_valid(dev_ofnode(*panel)))
> + ret = -EINVAL;
> +
> + return ret;
> +}
> +
> +static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node,
> + struct udevice **panel, struct udevice **bridge)
> +{
> + int ret;
> +
> + if (*panel)
> + return -EINVAL;
> +
> + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge);
> + if (ret)
> + return ret;
> +
> + ret = stm32_ltdc_get_panel(*bridge, panel);
> + } else {
> + /* no bridge, search a panel from display controller node */
> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel);
> + }
> +
> + return ret;
> +}
> +
> #if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY)
> static int stm32_ltdc_alloc_fb(struct udevice *dev)
> {
> @@ -532,6 +628,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> struct display_timing timings;
> struct clk pclk, bclk;
> struct reset_ctl rst;
> + ofnode node, port;
> ulong rate;
> int ret;
>
> @@ -568,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> }
>
> priv->hw_version = readl(priv->regs + LTDC_IDR);
> - debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
> + dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
>
> switch (priv->hw_version) {
> case HWVER_10200:
> @@ -589,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev)
> return -ENODEV;
> }
>
> - ret = uclass_first_device_err(UCLASS_PANEL, &panel);
> - if (ret) {
> - if (ret != -ENODEV)
> - dev_err(dev, "panel device error %d\n", ret);
> - return ret;
> + /*
> + * Try all the ports until one working.
> + *
> + * This is done in two times. First is checks for the
> + * UCLASS_VIDEO_BRIDGE available, and then for this bridge
> + * it scans for a UCLASS_PANEL.
> + */
> +
> + port = dev_read_subnode(dev, "port");
> + if (!ofnode_valid(port)) {
> + dev_err(dev, "%s(%s): 'port' subnode not found\n",
> + __func__, dev_read_name(dev));
> + return -EINVAL;
> }
>
> + for (node = ofnode_first_subnode(port);
> + ofnode_valid(node);
> + node = dev_read_next_subnode(node)) {
> + ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge);
> + if (ret)
> + dev_dbg(dev, "Device failed ret=%d\n", ret);
> + else
> + break;
> + }
> +
> + /* Sanity check */
> + if (ret)
> + return ret;
> +
> ret = panel_get_display_timing(panel, &timings);
> if (ret) {
> ret = ofnode_decode_display_timing(dev_ofnode(panel),
> @@ -624,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev)
> reset_deassert(&rst);
>
> if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
> - ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
> - if (ret)
> - dev_dbg(dev,
> - "No video bridge, or no backlight on bridge\n");
> -
> if (bridge) {
> ret = video_bridge_attach(bridge);
> if (ret) {
>
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-09-04 12:53 ` [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot Raphael Gallais-Pou
@ 2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
1 sibling, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-09-12 16:45 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> Use LTDC and LVDS nodes in all boot phases. This is specially useful
> before U-Boot relocation.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
> index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
> --- a/arch/arm/dts/stm32mp25-u-boot.dtsi
> +++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
> @@ -93,6 +93,14 @@
> bootph-all;
> };
>
> +<dc {
> + bootph-all;
> +};
> +
> +&lvds {
> + bootph-all;
> +};
> +
> &pinctrl {
> bootph-all;
> };
>
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
@ 2025-09-12 16:46 ` Patrice CHOTARD
2025-10-30 7:44 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
2 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-09-12 16:46 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> Compile VIDEO_STM32 and VIDEO_STM32_LVDS by default.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> configs/stm32mp25_defconfig | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/configs/stm32mp25_defconfig b/configs/stm32mp25_defconfig
> index 2b02cd86d6134497151e398eb54230f08d4e6272..e84df4a2a8f52890376f7689b01d3dcab5079ed9 100644
> --- a/configs/stm32mp25_defconfig
> +++ b/configs/stm32mp25_defconfig
> @@ -78,6 +78,9 @@ CONFIG_SERIAL_RX_BUFFER=y
> CONFIG_SPI=y
> CONFIG_DM_SPI=y
> # CONFIG_OPTEE_TA_AVB is not set
> +CONFIG_VIDEO=y
> +CONFIG_VIDEO_STM32=y
> +CONFIG_VIDEO_STM32_LVDS=y
> CONFIG_WDT=y
> CONFIG_WDT_STM32MP=y
> CONFIG_WDT_ARM_SMC=y
>
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Tahnks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-09-12 16:41 ` Patrice CHOTARD
@ 2025-09-15 13:05 ` Raphael Gallais-Pou
2025-09-17 15:10 ` Patrice CHOTARD
0 siblings, 1 reply; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-09-15 13:05 UTC (permalink / raw)
To: Patrice CHOTARD, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/12/25 18:41, Patrice CHOTARD wrote:
>
> On 9/4/25 14:53, Raphael Gallais-Pou wrote:
>> ...
>> static const struct udevice_id stm32_ltdc_ids[] = {
>> { .compatible = "st,stm32-ltdc" },
>> + { .compatible = "st,stm32mp251-ltdc" },
>> + { .compatible = "st,stm32mp255-ltdc" },
> in V1, only "st,stm32mp251-ltdc" compatible was introduced.
> Why are you adding "st,stm32mp255-ltdc" compatible ?
>
> For me only "st,stm32-ltdc" is needed, currently, nothing in drivers/video/stm32/stm32_ltdc.c
> is justifying to add "st,stm32mp251-ltdc" or "st,stm32mp251-ltdc" new compatible.
>
> Patrice
Hi Patrice,
It has been poorly explained in the cover letter. Between the v3 and v4 versions
of the Linux series:
https://lore.kernel.org/all/20250822-drm-misc-next-v5-0-9c825e28f733@foss.st.com/
Since U-Boot is based on the Linux device-tree, I figured it might follow the
added compatible.
Now the reason why the "st,stm32mp255-ltdc" has been added is because on the
STM32MP255 (and bigger) the LTDC needs 4 clocks, whether on the STM32MP251 and
smaller SoCs it only needs two. Thus the two new compatibles. Furthermore there
is features depending on the SoC version which will be added in the near future.
Best regards, Raphaël
>
>> { }
>> };
>>
>>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-09-15 13:05 ` Raphael Gallais-Pou
@ 2025-09-17 15:10 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-09-17 15:10 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/15/25 15:05, Raphael Gallais-Pou wrote:
>
>
> On 9/12/25 18:41, Patrice CHOTARD wrote:
>>
>> On 9/4/25 14:53, Raphael Gallais-Pou wrote:
>>> ...
>
>>> static const struct udevice_id stm32_ltdc_ids[] = {
>>> { .compatible = "st,stm32-ltdc" },
>>> + { .compatible = "st,stm32mp251-ltdc" },
>>> + { .compatible = "st,stm32mp255-ltdc" },
>> in V1, only "st,stm32mp251-ltdc" compatible was introduced.
>> Why are you adding "st,stm32mp255-ltdc" compatible ?
>>
>> For me only "st,stm32-ltdc" is needed, currently, nothing in drivers/video/stm32/stm32_ltdc.c
>> is justifying to add "st,stm32mp251-ltdc" or "st,stm32mp251-ltdc" new compatible.
>>
>> Patrice
>
> Hi Patrice,
>
> It has been poorly explained in the cover letter. Between the v3 and v4 versions
> of the Linux series:
> https://lore.kernel.org/all/20250822-drm-misc-next-v5-0-9c825e28f733@foss.st.com/
>
> Since U-Boot is based on the Linux device-tree, I figured it might follow the
> added compatible.
> Now the reason why the "st,stm32mp255-ltdc" has been added is because on the
> STM32MP255 (and bigger) the LTDC needs 4 clocks, whether on the STM32MP251 and
> smaller SoCs it only needs two. Thus the two new compatibles. Furthermore there
> is features depending on the SoC version which will be added in the near future.
> Best regards, Raphaël
>>
>>> { }
>>> };
>>>
>>>
>
Thanks for clarification
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
@ 2025-10-30 7:35 ` Yannick FERTRE
2025-11-01 9:03 ` Simon Glass
1 sibling, 0 replies; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:35 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> The "Display Timings" in panel-common.yaml can be provided by 2 properties
> - panel-timing: when display panels are restricted to a single resolution
> the "panel-timing" node expresses the required timings.
> - display-timings: several resolutions with different timings are supported
> with several timing subnode of "display-timings" node
>
> This patch update the parsing function to handle this 2 possibility
> when index = 0.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/core/ofnode.c | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> index e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9 100644
> --- a/drivers/core/ofnode.c
> +++ b/drivers/core/ofnode.c
> @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode parent, int index,
> int ret = 0;
>
> timings = ofnode_find_subnode(parent, "display-timings");
> - if (!ofnode_valid(timings))
> - return -EINVAL;
> -
> - i = 0;
> - ofnode_for_each_subnode(node, timings) {
> - if (i++ == index)
> - break;
> + if (ofnode_valid(timings)) {
> + i = 0;
> + ofnode_for_each_subnode(node, timings) {
> + if (i++ == index)
> + break;
> + }
> + } else {
> + if (index != 0)
> + return -EINVAL;
> + node = ofnode_find_subnode(parent, "panel-timing");
> }
>
> if (!ofnode_valid(node))
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display
2025-09-04 12:53 ` [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display Raphael Gallais-Pou
@ 2025-10-30 7:41 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
1 sibling, 0 replies; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:41 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> Add the compatible "panel-lvds" for simple-panel driver in U-Boot. In
> Linux this compatible is managed by the driver
> drivers/gpu/drm/panel/panel-lvds.c but in U-Boot the specific LVDS
> features (bus_format/bus_flags) are not supported.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/simple_panel.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
> index b6c5b058b2e967bbbd47ab3c3ce5ca52c7804409..0f23df701bc3c40ea49380bbfa3743ee592d8bd3 100644
> --- a/drivers/video/simple_panel.c
> +++ b/drivers/video/simple_panel.c
> @@ -191,6 +191,7 @@ static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
>
> static const struct udevice_id simple_panel_ids[] = {
> { .compatible = "simple-panel" },
> + { .compatible = "panel-lvds" },
> { .compatible = "auo,b133xtn01" },
> { .compatible = "auo,b116xw03" },
> { .compatible = "auo,b133htn01" },
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS
2025-09-04 12:53 ` [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS Raphael Gallais-Pou
@ 2025-10-30 7:42 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
0 siblings, 1 reply; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:42 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> The LVDS Display Interface Transmitter handles the LVDS protocol:
> it maps the pixels received from the upstream Pixel-DMA (LTDC)
> onto the LVDS PHY.
>
> The LVDS controller driver supports the following high-level features:
> • FDP-Link-I and OpenLDI (v0.95) protocols
> • Single-Link or Dual-Link operation
> • Single-Display or Double-Display (with the same content
> duplicated on both)
> • Flexible Bit-Mapping, including JEIDA and VESA
> • RGB888 or RGB666 output
> • Synchronous design, with one input pixel per clock cycle
> • No resolution limitation.
>
> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> MAINTAINERS | 1 +
> doc/board/st/st-dt.rst | 1 +
> drivers/video/stm32/Kconfig | 9 +
> drivers/video/stm32/Makefile | 1 +
> drivers/video/stm32/stm32_lvds.c | 693 +++++++++++++++++++++++++++++++++++++++
> 5 files changed, 705 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3fb163aa1db1dba249e60b40ab3e785db4fde5d3..604ba4ca04c1079b14cd8f4c1fff4c12da0e278d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -728,6 +728,7 @@ F: drivers/serial/serial_stm32.*
> F: drivers/spi/stm32_qspi.c
> F: drivers/spi/stm32_spi.c
> F: drivers/video/stm32/stm32_ltdc.c
> +F: drivers/video/stm32/stm32_lvds.c
> F: drivers/watchdog/stm32mp_wdt.c
> F: include/dt-bindings/clock/stm32fx-clock.h
> F: include/dt-bindings/clock/stm32mp*
> diff --git a/doc/board/st/st-dt.rst b/doc/board/st/st-dt.rst
> index 2a285c81807ee26924948d8c2497f11dfe45b6e6..28cada97e72da711408a1aaaa928ad3faa51162e 100644
> --- a/doc/board/st/st-dt.rst
> +++ b/doc/board/st/st-dt.rst
> @@ -25,6 +25,7 @@ kernel binding directory = Documentation/devicetree/bindings/
> * display
> - display/st,stm32-dsi.yaml
> - display/st,stm32-ltdc.yaml
> + - display/st,stm32mp25-lvds.yaml
> * gpio
> - pinctrl/st,stm32-pinctrl.yaml
> * hwlock
> diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
> index c354c402c288b4c004bd2b5cc74a4d00beef2773..4cb8a841caf51aaba5115b9fcddb967f73a3d9b4 100644
> --- a/drivers/video/stm32/Kconfig
> +++ b/drivers/video/stm32/Kconfig
> @@ -23,6 +23,15 @@ config VIDEO_STM32_DSI
> This option enables support DSI internal bridge which can be used on
> devices which have DSI devices connected.
>
> +config VIDEO_STM32_LVDS
> + bool "Enable STM32 LVDS video support"
> + depends on VIDEO_STM32
> + select VIDEO_BRIDGE
> + select VIDEO_DW_MIPI_DSI
> + help
> + This enables Low Voltage Differential Signaling (LVDS) display
> + support.
> +
> config VIDEO_STM32_MAX_XRES
> int "Maximum horizontal resolution (for memory allocation purposes)"
> depends on VIDEO_STM32
> diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
> index f8b42d1a4d126a31aa11ce820e829b133ca3bb2d..059d9000c1d7762d06bff36d5408143a34848227 100644
> --- a/drivers/video/stm32/Makefile
> +++ b/drivers/video/stm32/Makefile
> @@ -7,3 +7,4 @@
>
> obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
> obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
> +obj-${CONFIG_VIDEO_STM32_LVDS} += stm32_lvds.o
> diff --git a/drivers/video/stm32/stm32_lvds.c b/drivers/video/stm32/stm32_lvds.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..bf1393c9e8724ed099aeed63344ebdc0004507b6
> --- /dev/null
> +++ b/drivers/video/stm32/stm32_lvds.c
> @@ -0,0 +1,693 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
> +/*
> + * Copyright (C) 2025 STMicroelectronics - All Rights Reserved
> + * Author(s): Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
> + *
> + * This Low Voltage Differential Signal controller driver is based on the Linux Kernel driver from
> + * drivers/gpu/drm/stm/ltdc.c
> + */
> +
> +#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
> +
> +#include <clk.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <media_bus_format.h>
> +#include <panel.h>
> +#include <reset.h>
> +#include <video.h>
> +#include <video_bridge.h>
> +#include <asm/io.h>
> +#include <dm/device_compat.h>
> +#include <dm/ofnode.h>
> +#include <linux/iopoll.h>
> +
> +/* LVDS Host registers */
> +#define LVDS_CR 0x0000 /* configuration register */
> +#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */
> +#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */
> +#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */
> +#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */
> +#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */
> +#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */
> +#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */
> +#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */
> +#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */
> +#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */
> +#define LVDS_DMLCR(id) (LVDS_DMLCR0 + 8U * (id))
> +#define LVDS_DMMCR(id) (LVDS_DMMCR0 + 8U * (id))
> +#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */
> +#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */
> +
> +#define CDL1CR_DEFAULT 0x4321
> +#define CDL2CR_DEFAULT 0x59876
> +
> +/* LVDS Host registers */
> +#define LVDS_PHY_MASTER 0x0
> +#define LVDS_PHY_SLAVE 0x100
> +
> +/* phy parameter can only be one of those two above */
> +#define LVDS_PXGCR(phy) ((phy) + 0x1000) /* Global Control Register */
> +#define LVDS_PXCMCR1(phy) ((phy) + 0x100C) /* Current Mode Control Register 1 */
> +#define LVDS_PXCMCR2(phy) ((phy) + 0x1010) /* Current Mode Control Register 2 */
> +#define LVDS_PXSCR(phy) ((phy) + 0x1020) /* Serial Control Register */
> +#define LVDS_PXBCR1(phy) ((phy) + 0x102C) /* Bias Control Register 1 */
> +#define LVDS_PXBCR2(phy) ((phy) + 0x1030) /* Bias Control Register 2 */
> +#define LVDS_PXBCR3(phy) ((phy) + 0x1034) /* Bias Control Register 3 */
> +#define LVDS_PXMPLCR(phy) ((phy) + 0x1064) /* Monitor PLL Lock Control Register */
> +#define LVDS_PXDCR(phy) ((phy) + 0x1084) /* Debug Control Register */
> +#define LVDS_PXSSR1(phy) ((phy) + 0x1088) /* Spare Status Register 1 */
> +#define LVDS_PXCFGCR(phy) ((phy) + 0x10A0) /* Configuration Control Register */
> +#define LVDS_PXPLLCR1(phy) ((phy) + 0x10C0) /* PLL_MODE 1 Control Register */
> +#define LVDS_PXPLLCR2(phy) ((phy) + 0x10C4) /* PLL_MODE 2 Control Register */
> +#define LVDS_PXPLLSR(phy) ((phy) + 0x10C8) /* PLL Status Register */
> +#define LVDS_PXPLLSDCR1(phy) ((phy) + 0x10CC) /* PLL_SD_1 Control Register */
> +#define LVDS_PXPLLSDCR2(phy) ((phy) + 0x10D0) /* PLL_SD_2 Control Register */
> +#define LVDS_PXPLLTWGCR1(phy) ((phy) + 0x10D4) /* PLL_TWG_1 Control Register */
> +#define LVDS_PXPLLTWGCR2(phy) ((phy) + 0x10D8) /* PLL_TWG_2 Control Register */
> +#define LVDS_PXPLLCPCR(phy) ((phy) + 0x10E0) /* PLL_CP Control Register */
> +#define LVDS_PXPLLTESTCR(phy) ((phy) + 0x10E8) /* PLL_TEST Control Register */
> +
> +/* LVDS Wrapper registers */
> +#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */
> +#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */
> +#define LVDS_VERR 0x1FF4 /* Version register */
> +#define LVDS_IPIDR 0x1FF8 /* Identification register */
> +#define LVDS_SIDR 0x1FFC /* Size Identification register */
> +
> +#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */
> +#define CR_HSPOL BIT(1) /* HS Polarity (horizontal sync) */
> +#define CR_VSPOL BIT(2) /* VS Polarity (vertical sync) */
> +#define CR_DEPOL BIT(3) /* DE Polarity (data enable) */
> +#define CR_CI BIT(4) /* Control Internal (software controlled bit) */
> +#define CR_LKMOD BIT(5) /* Link Mode, for both Links */
> +#define CR_LKPHA BIT(6) /* Link Phase, for both Links */
> +#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */
> +#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */
> +
> +#define DMMCRX_MAP0 GENMASK(4, 0)
> +#define DMMCRX_MAP1 GENMASK(9, 5)
> +#define DMMCRX_MAP2 GENMASK(14, 10)
> +#define DMMCRX_MAP3 GENMASK(19, 15)
> +#define DMLCRX_MAP4 GENMASK(4, 0)
> +#define DMLCRX_MAP5 GENMASK(9, 5)
> +#define DMLCRX_MAP6 GENMASK(14, 10)
> +
> +#define CDLCRX_DISTR0 GENMASK(3, 0)
> +#define CDLCRX_DISTR1 GENMASK(7, 4)
> +#define CDLCRX_DISTR2 GENMASK(11, 8)
> +#define CDLCRX_DISTR3 GENMASK(15, 12)
> +#define CDLCRX_DISTR4 GENMASK(19, 16)
> +
> +#define FREF_INDEX 0
> +#define NDIV_INDEX 1
> +#define FPFD_INDEX 2
> +#define MDIV_INDEX 3
> +#define FVCO_INDEX 4
> +#define BDIV_INDEX 5
> +#define FBIT_INDEX 6
> +#define FLS_INDEX 7
> +#define FDP_INDEX 8
> +
> +#define PXGCR_BIT_CLK_OUT BIT(0)
> +#define PXGCR_LS_CLK_OUT BIT(4)
> +#define PXGCR_DP_CLK_OUT BIT(8)
> +#define PXGCR_RSTZ BIT(24)
> +#define PXGCR_DIV_RSTN BIT(25)
> +
> +#define PXCMCR1_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4))
> +#define PXCMCR2_CM_EN_DL4 BIT(4)
> +#define PXSCR_SER_DATA_OK BIT(16)
> +#define PXBCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
> +#define PXBCR2_BIAS_EN BIT(28)
> +#define PXBCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
> +#define PXDCR_POWER_OK BIT(12)
> +#define PXCFGCR_EN_DIG_DL GENMASK(4, 0)
> +
> +#define PXPLLCR1_PLL_EN BIT(0)
> +#define PxPLLCR1_SD_EN BIT(1)
> +#define PXPLLCR1_TWG_EN BIT(2)
> +#define PXPLLCR1_PLL_DIVIDERS_EN BIT(8)
> +#define PXPLLCR2_NDIV GENMASK(25, 16)
> +#define PXPLLCR2_BDIV GENMASK(9, 0)
> +#define PXPLLSR_PLL_LOCK BIT(0)
> +#define PXPLLSDCR1_MDIV GENMASK(9, 0)
> +#define PXPLLCPCR_CPCTRL_DEFAULT 0x1
> +#define PXPLLTESTCR_PLL_TEST_CLK_EN BIT(0)
> +#define PXPLLTESTCR_PLL_TDIV_EN BIT(8)
> +#define PXPLLTESTCR_TDIV GENMASK(25, 16)
> +#define PXPLLTESTCR_TDIV_VALUE 70
> +
> +#define WCLKCR_SLV_CLKPIX_SEL BIT(0)
> +#define WCLKCR_SRCSEL BIT(8)
> +
> +/* Sleep & timeout for pll lock/unlock */
> +#define SLEEP_US 1000
> +#define TIMEOUT_US 20000000
> +
> +#define PHY_SLV_OFS 0x100
> +
> +/* PLL parameters */
> +#define NDIV_MIN 2
> +#define NDIV_MAX 6
> +#define BDIV_MIN 2
> +#define BDIV_MAX 6
> +#define MDIV_MIN 1
> +#define MDIV_MAX 1023
> +
> +struct stm32_lvds_plat {
> + void __iomem *base;
> + struct udevice *panel;
> + struct reset_ctl rst;
> + struct clk pclk;
> + struct clk refclk;
> +};
> +
> +struct stm32_lvds_priv {
> + struct display_timing timings;
> + u32 refclk_rate;
> + int dual_link;
> + int bus_format;
> +};
> +
> +/*
> + * enum lvds_pixels_order - Pixel order of an LVDS connection
> + * @LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
> + * from the first port, odd pixels from the second port
> + * @LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated
> + * from the first port, even pixels from the second port
> + */
> +enum lvds_pixels_order {
> + LVDS_DUAL_LINK_EVEN_ODD_PIXELS = BIT(0),
> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS = BIT(1),
> +};
> +
> +enum lvds_pixel {
> + PIX_R_0 = 0x00,
> + PIX_R_1 = 0x01,
> + PIX_R_2 = 0x02,
> + PIX_R_3 = 0x03,
> + PIX_R_4 = 0x04,
> + PIX_R_5 = 0x05,
> + PIX_R_6 = 0x06,
> + PIX_R_7 = 0x07,
> + PIX_G_0 = 0x08,
> + PIX_G_1 = 0x09,
> + PIX_G_2 = 0x0A,
> + PIX_G_3 = 0x0B,
> + PIX_G_4 = 0x0C,
> + PIX_G_5 = 0x0D,
> + PIX_G_6 = 0x0E,
> + PIX_G_7 = 0x0F,
> + PIX_B_0 = 0x10,
> + PIX_B_1 = 0x11,
> + PIX_B_2 = 0x12,
> + PIX_B_3 = 0x13,
> + PIX_B_4 = 0x14,
> + PIX_B_5 = 0x15,
> + PIX_B_6 = 0x16,
> + PIX_B_7 = 0x17,
> + PIX_H_S = 0x18,
> + PIX_V_S = 0x19,
> + PIX_D_E = 0x1A,
> + PIX_C_E = 0x1B,
> + PIX_C_I = 0x1C,
> + PIX_TOG = 0x1D,
> + PIX_ONE = 0x1E,
> + PIX_ZER = 0x1F,
> +};
> +
> +/*
> + * Expected JEIDA-RGB888 data to be sent in LSB format
> + * bit6 ............................bit0
> + */
> +const enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
> + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
> + { PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
> + { PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
> + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
> + { PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
> +};
> +
> +/*
> + * Expected VESA-RGB888 data to be sent in LSB format
> + * bit6 ............................bit0
> + */
> +const enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
> + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
> + { PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
> + { PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
> + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
> + { PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
> +};
> +
> +static inline void lvds_writel(void __iomem *base, u32 reg, u32 val)
> +{
> + writel(val, base + reg);
> +}
> +
> +static inline u32 lvds_readl(void __iomem *base, u32 reg)
> +{
> + return readl(base + reg);
> +}
> +
> +static inline void lvds_set(void __iomem *base, u32 reg, u32 mask)
> +{
> + lvds_writel(base, reg, lvds_readl(base, reg) | mask);
> +}
> +
> +static inline void lvds_clear(void __iomem *base, u32 reg, u32 mask)
> +{
> + lvds_writel(base, reg, lvds_readl(base, reg) & ~mask);
> +}
> +
> +static u32 pll_get_clkout_khz(u32 clkin_khz, u32 bdiv, u32 mdiv, u32 ndiv)
> +{
> + int divisor = ndiv * bdiv;
> +
> + /* Prevents from division by 0 */
> + if (!divisor)
> + return 0;
> +
> + return clkin_khz * mdiv / divisor;
> +}
> +
> +static int lvds_pll_get_params(u32 clkin_khz, u32 clkout_khz,
> + u32 *bdiv, u32 *mdiv, u32 *ndiv)
> +{
> + u32 i, o, n;
> + u32 delta, best_delta; /* all in khz */
> +
> + /* Early checks preventing division by 0 & odd results */
> + if (clkin_khz == 0 || clkout_khz == 0)
> + return -EINVAL;
> +
> + best_delta = 1000000; /* big started value (1000000khz) */
> +
> + for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
> + for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
> + n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
> + /* Check ndiv according to vco range */
> + if (n < MDIV_MIN || n > MDIV_MAX)
> + continue;
> + /* Check if new delta is better & saves parameters */
> + delta = abs(pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz);
> + if (delta < best_delta) {
> + *ndiv = i;
> + *mdiv = n;
> + *bdiv = o;
> + best_delta = delta;
> + }
> + /* fast return in case of "perfect result" */
> + if (!delta)
> + return 0;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int stm32_lvds_pll_enable(struct udevice *dev,
> + int phy)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
> + struct display_timing timings = priv->timings;
> + u32 pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
> + int ret, val, multiplier;
> +
> + /* Release PHY from reset */
> + lvds_set(plat->base, LVDS_PXGCR(phy), PXGCR_DIV_RSTN | PXGCR_RSTZ);
> +
> + /* lvds_pll_config */
> + /* Set PLL Slv & Mst configs and timings */
> + pll_in_khz = priv->refclk_rate / 1000;
> +
> + if (priv->dual_link)
> + multiplier = 2;
> + else
> + multiplier = 1;
> +
> + ret = lvds_pll_get_params(pll_in_khz, timings.pixelclock.typ * 7 / 1000 / multiplier,
> + &bdiv, &mdiv, &ndiv);
> + if (ret)
> + return ret;
> +
> + /* Set PLL parameters */
> + lvds_writel(plat->base, LVDS_PXPLLCR2(phy), (ndiv << 16) | bdiv);
> + lvds_writel(plat->base, LVDS_PXPLLSDCR1(phy), mdiv);
> + lvds_writel(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_TDIV_VALUE << 16);
> +
> + /* Disable TWG and SD: for now, PLL just need to be in integer mode */
> + lvds_clear(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_TWG_EN | PxPLLCR1_SD_EN);
> +
> + /* Power up bias and PLL dividers */
> + lvds_set(plat->base, LVDS_PXDCR(phy), PXDCR_POWER_OK);
> +
> + lvds_set(plat->base, LVDS_PXCMCR1(phy), PXCMCR1_CM_EN_DL);
> + lvds_set(plat->base, LVDS_PXCMCR2(phy), PXCMCR2_CM_EN_DL4);
> +
> + lvds_set(plat->base, LVDS_PXPLLCPCR(phy), PXPLLCPCR_CPCTRL_DEFAULT);
> + lvds_set(plat->base, LVDS_PXBCR3(phy), PXBCR3_VM_EN_DL);
> + lvds_set(plat->base, LVDS_PXBCR1(phy), PXBCR1_EN_BIAS_DL);
> + lvds_set(plat->base, LVDS_PXCFGCR(phy), PXCFGCR_EN_DIG_DL);
> +
> + /* lvds_pll_enable */
> + /* PLL lock timing control for the monitor unmask after startup (pll_en) */
> + /* Adjust the value so that the masking window is opened at start-up */
> + /* MST_MON_PLL_LOCK_UNMASK_TUNE */
> + lvds_writel(plat->base, LVDS_PXMPLCR(phy), (0x200 - 0x160) << 16);
> +
> + lvds_writel(plat->base, LVDS_PXBCR2(phy), PXBCR2_BIAS_EN);
> +
> + lvds_set(plat->base, LVDS_PXGCR(phy),
> + PXGCR_DP_CLK_OUT | PXGCR_LS_CLK_OUT | PXGCR_BIT_CLK_OUT);
> +
> + lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TDIV_EN);
> + lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_DIVIDERS_EN);
> + lvds_set(plat->base, LVDS_PXSCR(phy), PXSCR_SER_DATA_OK);
> +
> + /* Enable the LVDS PLL & wait for its lock */
> + lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_EN);
> + ret = readl_poll_sleep_timeout(plat->base + LVDS_PXPLLSR(phy),
> + val, val & PXPLLSR_PLL_LOCK, SLEEP_US, TIMEOUT_US);
> + if (ret)
> + return ret;
> +
> + /* Select MST PHY clock as pixel clock for the LDITX instead of FREF */
> + /* WCLKCR_SLV_CLKPIX_SEL is for dual link */
> + lvds_writel(plat->base, LVDS_WCLKCR, WCLKCR_SLV_CLKPIX_SEL);
> +
> + lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TEST_CLK_EN);
> +
> + return 0;
> +}
> +
> +static int stm32_lvds_enable(struct udevice *dev)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
> + struct display_timing timings = priv->timings;
> + u32 lvds_cdl1cr = 0;
> + u32 lvds_cdl2cr = 0;
> + u32 lvds_dmlcr = 0;
> + u32 lvds_dmmcr = 0;
> + u32 lvds_cr = 0;
> + int i;
> +
> + lvds_clear(plat->base, LVDS_CDL1CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
> + | CDLCRX_DISTR3 | CDLCRX_DISTR4);
> + lvds_clear(plat->base, LVDS_CDL2CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
> + | CDLCRX_DISTR3 | CDLCRX_DISTR4);
> +
> + /* Set channel distribution */
> + lvds_cr &= ~CR_LKMOD;
> + lvds_cdl1cr = CDL1CR_DEFAULT;
> +
> + if (priv->dual_link) {
> + lvds_cr |= CR_LKMOD;
> + lvds_cdl2cr = CDL2CR_DEFAULT;
> + }
> +
> + /* Set signal polarity */
> + if (timings.flags & DISPLAY_FLAGS_DE_LOW)
> + lvds_cr |= CR_DEPOL;
> +
> + if (timings.flags & DISPLAY_FLAGS_HSYNC_LOW)
> + lvds_cr |= CR_HSPOL;
> +
> + if (timings.flags & DISPLAY_FLAGS_VSYNC_LOW)
> + lvds_cr |= CR_VSPOL;
> +
> + /* Set link phase */
> + switch (priv->dual_link) {
> + case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
> + lvds_cr &= ~CR_LKPHA;
> + break;
> + case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
> + lvds_cr |= CR_LKPHA;
> + break;
> + default:
> + dev_dbg(dev, "No phase precised, setting default\n");
> + lvds_cr &= ~CR_LKPHA;
> + break;
> + }
> +
> + /* Set Data Mapping */
> + switch (priv->bus_format) {
> + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
> + for (i = 0; i < 5; i++) {
> + lvds_dmlcr = ((lvds_bitmap_vesa_rgb888[i][0])
> + + (lvds_bitmap_vesa_rgb888[i][1] << 5)
> + + (lvds_bitmap_vesa_rgb888[i][2] << 10)
> + + (lvds_bitmap_vesa_rgb888[i][3] << 15));
> + lvds_dmmcr = ((lvds_bitmap_vesa_rgb888[i][4])
> + + (lvds_bitmap_vesa_rgb888[i][5] << 5)
> + + (lvds_bitmap_vesa_rgb888[i][6] << 10));
> +
> + /* Write registers at the end of computations */
> + lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
> + lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
> + }
> + break;
> + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
> + for (i = 0; i < 5; i++) {
> + lvds_dmlcr = ((lvds_bitmap_jeida_rgb888[i][0])
> + + (lvds_bitmap_jeida_rgb888[i][1] << 5)
> + + (lvds_bitmap_jeida_rgb888[i][2] << 10)
> + + (lvds_bitmap_jeida_rgb888[i][3] << 15));
> + lvds_dmmcr = ((lvds_bitmap_jeida_rgb888[i][4])
> + + (lvds_bitmap_jeida_rgb888[i][5] << 5)
> + + (lvds_bitmap_jeida_rgb888[i][6] << 10));
> +
> + /* Write registers at the end of computations */
> + lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
> + lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
> + }
> + break;
> + default:
> + dev_dbg(dev, "Unsupported LVDS bus format 0x%04x\n", priv->bus_format);
> + }
> +
> + /* Turn the output on */
> + lvds_cr |= CR_LVDSEN;
> +
> + /* Commit config to registers */
> + lvds_set(plat->base, LVDS_CR, lvds_cr);
> + lvds_writel(plat->base, LVDS_CDL1CR, lvds_cdl1cr);
> + lvds_writel(plat->base, LVDS_CDL2CR, lvds_cdl2cr);
> +
> + return 0;
> +}
> +
> +static int stm32_lvds_attach(struct udevice *dev)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + ret = panel_get_display_timing(plat->panel, &priv->timings);
> + if (ret) {
> + ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
> + 0, &priv->timings);
> + if (ret) {
> + dev_err(dev, "decode display timing error %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = stm32_lvds_enable(dev);
> +
> + return ret;
> +}
> +
> +static int stm32_lvds_set_backlight(struct udevice *dev, int percent)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + int ret;
> +
> + ret = panel_enable_backlight(plat->panel);
> + if (ret) {
> + dev_err(dev, "panel %s enable backlight error %d\n",
> + plat->panel->name, ret);
> + }
> +
> + return ret;
> +}
> +
> +static int lvds_handle_pixel_order(struct stm32_lvds_plat *plat)
> +{
> + ofnode parent, panel_port0, panel_port1;
> + bool even_pixels, odd_pixels;
> + int port0, port1;
> +
> + /*
> + * In case we are operating in single link,
> + * there is only one port linked to the LVDS.
> + * Check whether we are in this case and exit if yes.
> + */
> + parent = ofnode_find_subnode(dev_ofnode(plat->panel), "ports");
> + if (!ofnode_valid(parent))
> + return 0;
> +
> + panel_port0 = ofnode_first_subnode(parent);
> + if (!ofnode_valid(panel_port0))
> + return -EPIPE;
> +
> + even_pixels = ofnode_read_bool(panel_port0, "dual-lvds-even-pixels");
> + odd_pixels = ofnode_read_bool(panel_port0, "dual-lvds-odd-pixels");
> + if (even_pixels && odd_pixels)
> + return -EINVAL;
> +
> + port0 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
> +
> + panel_port1 = ofnode_next_subnode(panel_port0);
> + if (!ofnode_valid(panel_port1))
> + return -EPIPE;
> +
> + even_pixels = ofnode_read_bool(panel_port1, "dual-lvds-even-pixels");
> + odd_pixels = ofnode_read_bool(panel_port1, "dual-lvds-odd-pixels");
> + if (even_pixels && odd_pixels)
> + return -EINVAL;
> +
> + port1 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
> +
> + /*
> + * A valid dual-LVDS bus is found when one port is marked with
> + * "dual-lvds-even-pixels", and the other port is marked with
> + * "dual-lvds-odd-pixels", bail out if the markers are not right.
> + */
> + if (port0 + port1 != LVDS_DUAL_LINK_EVEN_ODD_PIXELS + LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
> + return -EINVAL;
> +
> + return port0;
> +}
> +
> +static int stm32_lvds_of_to_plat(struct udevice *dev)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
> + const char *data_mapping;
> + int ret;
> +
> + plat->base = dev_read_addr_ptr(dev);
> + if ((fdt_addr_t)plat->base == FDT_ADDR_T_NONE) {
> + dev_err(dev, "Unable to read LVDS base address\n");
> + return -EINVAL;
> + }
> +
> + ret = clk_get_by_name(dev, "pclk", &plat->pclk);
> + if (ret) {
> + dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_get_by_name(dev, "ref", &plat->refclk);
> + if (ret) {
> + dev_err(dev, "Unable to get reference clock: %d\n", ret);
> + return ret;
> + }
> +
> + ret = reset_get_by_index(dev, 0, &plat->rst);
> + if (ret) {
> + dev_err(dev, "Failed to get LVDS reset: %d\n", ret);
> + return ret;
> + }
> +
> + ret = uclass_get_device_by_driver(UCLASS_PANEL,
> + DM_DRIVER_GET(simple_panel), &plat->panel);
> + if (ret) {
> + dev_err(dev, "panel device error %d\n", ret);
> + return ret;
> + }
> +
> + ret = panel_get_display_timing(plat->panel, &priv->timings);
> + if (ret) {
> + ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
> + 0, &priv->timings);
> + if (ret) {
> + dev_err(dev, "decode display timing error %d\n", ret);
> + return ret;
> + }
> + }
> +
> + data_mapping = ofnode_read_string(dev_ofnode(plat->panel), "data-mapping");
> + if (!strcmp(data_mapping, "vesa-24"))
> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
> + else if (!strcmp(data_mapping, "jeida-24"))
> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
> + else
> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
> +
> + return 0;
> +}
> +
> +static int stm32_lvds_probe(struct udevice *dev)
> +{
> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + ret = clk_enable(&plat->pclk);
> + if (ret) {
> + dev_err(dev, "Failed to enable peripheral clock: %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_enable(&plat->refclk);
> + if (ret) {
> + dev_err(dev, "Failed to enable reference clock: %d\n", ret);
> + goto err_clk;
> + }
> +
> + priv->refclk_rate = (unsigned int)clk_get_rate(&plat->refclk);
> +
> + reset_deassert(&plat->rst);
> +
> + /* Handle dual link config */
> + priv->dual_link = lvds_handle_pixel_order(plat);
> + if (priv->dual_link < 0)
> + goto err_rst;
> +
> + if (priv->dual_link > 0) {
> + ret = stm32_lvds_pll_enable(dev, LVDS_PHY_SLAVE);
> + if (ret)
> + goto err_rst;
> + }
> +
> + ret = stm32_lvds_pll_enable(dev, LVDS_PHY_MASTER);
> + if (ret)
> + goto err_rst;
> +
> + return 0;
> +
> +err_rst:
> + clk_disable(&plat->refclk);
> +err_clk:
> + clk_disable(&plat->pclk);
> +
> + return ret;
> +}
> +
> +static const struct video_bridge_ops stm32_lvds_ops = {
> + .attach = stm32_lvds_attach,
> + .set_backlight = stm32_lvds_set_backlight,
> +};
> +
> +static const struct udevice_id stm32_lvds_ids[] = {
> + {.compatible = "st,stm32mp25-lvds"},
> + {}
> +};
> +
> +U_BOOT_DRIVER(stm32_lvds) = {
> + .name = "stm32-display-lvds",
> + .id = UCLASS_VIDEO_BRIDGE,
> + .of_match = stm32_lvds_ids,
> + .ops = &stm32_lvds_ops,
> + .of_to_plat = stm32_lvds_of_to_plat,
> + .probe = stm32_lvds_probe,
> + .plat_auto = sizeof(struct stm32_lvds_plat),
> + .priv_auto = sizeof(struct stm32_lvds_priv),
> +};
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-09-04 12:53 ` [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC Raphael Gallais-Pou
2025-09-12 16:41 ` Patrice CHOTARD
@ 2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
1 sibling, 1 reply; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:43 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> STM32MP2 SoCs feature a new version of the LTDC IP. This new version
> features a bus clock, as well as a 150MHz pad frequency. Add its
> compatible to the list of device to probe and handle quirks. The new
> hardware version features a bus clock.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/stm32/stm32_ltdc.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index 0a062c8939dbe49b11aa50f5ca9701bdbe5c5b0b..efe9a00996eca0301d2a2b82074ba9690a967a73 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -262,6 +262,7 @@ static const u32 layer_regs_a2[] = {
> #define HWVER_10300 0x010300
> #define HWVER_20101 0x020101
> #define HWVER_40100 0x040100
> +#define HWVER_40101 0x040101
>
> enum stm32_ltdc_pix_fmt {
> PF_ARGB8888 = 0, /* ARGB [32 bits] */
> @@ -529,7 +530,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> struct udevice *bridge = NULL;
> struct udevice *panel = NULL;
> struct display_timing timings;
> - struct clk pclk;
> + struct clk pclk, bclk;
> struct reset_ctl rst;
> ulong rate;
> int ret;
> @@ -540,7 +541,21 @@ static int stm32_ltdc_probe(struct udevice *dev)
> return -EINVAL;
> }
>
> - ret = clk_get_by_index(dev, 0, &pclk);
> + ret = clk_get_by_name(dev, "bus", &bclk);
> + if (ret) {
> + if (ret != -ENODATA) {
> + dev_err(dev, "bus clock get error %d\n", ret);
> + return ret;
> + }
> + } else {
> + ret = clk_enable(&bclk);
> + if (ret) {
> + dev_err(dev, "bus clock enable error %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = clk_get_by_name(dev, "lcd", &pclk);
> if (ret) {
> dev_err(dev, "peripheral clock get error %d\n", ret);
> return ret;
> @@ -566,6 +581,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> priv->pix_fmt_hw = pix_fmt_a1;
> break;
> case HWVER_40100:
> + case HWVER_40101:
> priv->layer_regs = layer_regs_a2;
> priv->pix_fmt_hw = pix_fmt_a2;
> break;
> @@ -688,6 +704,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
>
> static const struct udevice_id stm32_ltdc_ids[] = {
> { .compatible = "st,stm32-ltdc" },
> + { .compatible = "st,stm32mp251-ltdc" },
> + { .compatible = "st,stm32mp255-ltdc" },
> { }
> };
>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel
2025-09-04 12:53 ` [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
@ 2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
1 sibling, 1 reply; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:43 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> Initially there was only one DSI bridge with one panel attached to this
> device. This explained the call to uclass_first_device_err(UCLASS_PANEL,
> ...) which worked fine at the time.
>
> Now that multiple bridges and panels, with different technologies, can
> be plugged onto the board this way to get the panel device is outdated.
>
> The lookup is done is two steps. First we circle through the
> UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
> endpoints until we get a UCLASS_PANEL device available.
>
> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/stm32/stm32_ltdc.c | 136 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 125 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
> index efe9a00996eca0301d2a2b82074ba9690a967a73..834bfb625d2d34a44bd8edff1c92af6dec344c20 100644
> --- a/drivers/video/stm32/stm32_ltdc.c
> +++ b/drivers/video/stm32/stm32_ltdc.c
> @@ -17,6 +17,7 @@
> #include <video_bridge.h>
> #include <asm/io.h>
> #include <dm/device-internal.h>
> +#include <dm/uclass-internal.h>
> #include <dm/device_compat.h>
> #include <linux/bitops.h>
> #include <linux/printk.h>
> @@ -495,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr)
> setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN);
> }
>
> +static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node,
> + enum uclass_id id, struct udevice **remote_dev)
> +{
> + u32 remote_phandle;
> + ofnode remote;
> + int ret = 0;
> +
> + ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> + if (ret) {
> + dev_err(dev, "%s(%s): Could not find remote-endpoint property\n",
> + __func__, dev_read_name(dev));
> + return ret;
> + }
> +
> + remote = ofnode_get_by_phandle(remote_phandle);
> + if (!ofnode_valid(remote))
> + return -EINVAL;
> +
> + while (ofnode_valid(remote)) {
> + remote = ofnode_get_parent(remote);
> + if (!ofnode_valid(remote)) {
> + dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n",
> + __func__, dev_read_name(dev), id);
> + continue;
> + }
> +
> + ret = uclass_find_device_by_ofnode(id, remote, remote_dev);
> + if (*remote_dev && !ret) {
> + ret = uclass_get_device_by_ofnode(id, remote, remote_dev);
> + if (ret)
> + dev_dbg(dev, "%s(%s): failed to get remote device %s\n",
> + __func__, dev_read_name(dev), dev_read_name(*remote_dev));
> + break;
> + }
> + };
> +
> + return ret;
> +}
> +
> +static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel)
> +{
> + ofnode ep_node, node, ports;
> + int ret = 0;
> +
> + if (!dev)
> + return -EINVAL;
> +
> + ports = ofnode_find_subnode(dev_ofnode(dev), "ports");
> + if (!ofnode_valid(ports)) {
> + dev_err(dev, "Remote bridge subnode\n");
> + return ret;
> + }
> +
> + for (node = ofnode_first_subnode(ports);
> + ofnode_valid(node);
> + node = dev_read_next_subnode(node)) {
> + ep_node = ofnode_first_subnode(node);
> + if (!ofnode_valid(ep_node))
> + continue;
> +
> + ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel);
> + }
> +
> + /* Sanity check, we can get out of the loop without having a clean ofnode */
> + if (!(*panel))
> + ret = -EINVAL;
> + else
> + if (!ofnode_valid(dev_ofnode(*panel)))
> + ret = -EINVAL;
> +
> + return ret;
> +}
> +
> +static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node,
> + struct udevice **panel, struct udevice **bridge)
> +{
> + int ret;
> +
> + if (*panel)
> + return -EINVAL;
> +
> + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge);
> + if (ret)
> + return ret;
> +
> + ret = stm32_ltdc_get_panel(*bridge, panel);
> + } else {
> + /* no bridge, search a panel from display controller node */
> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel);
> + }
> +
> + return ret;
> +}
> +
> #if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY)
> static int stm32_ltdc_alloc_fb(struct udevice *dev)
> {
> @@ -532,6 +628,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> struct display_timing timings;
> struct clk pclk, bclk;
> struct reset_ctl rst;
> + ofnode node, port;
> ulong rate;
> int ret;
>
> @@ -568,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
> }
>
> priv->hw_version = readl(priv->regs + LTDC_IDR);
> - debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
> + dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
>
> switch (priv->hw_version) {
> case HWVER_10200:
> @@ -589,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev)
> return -ENODEV;
> }
>
> - ret = uclass_first_device_err(UCLASS_PANEL, &panel);
> - if (ret) {
> - if (ret != -ENODEV)
> - dev_err(dev, "panel device error %d\n", ret);
> - return ret;
> + /*
> + * Try all the ports until one working.
> + *
> + * This is done in two times. First is checks for the
> + * UCLASS_VIDEO_BRIDGE available, and then for this bridge
> + * it scans for a UCLASS_PANEL.
> + */
> +
> + port = dev_read_subnode(dev, "port");
> + if (!ofnode_valid(port)) {
> + dev_err(dev, "%s(%s): 'port' subnode not found\n",
> + __func__, dev_read_name(dev));
> + return -EINVAL;
> }
>
> + for (node = ofnode_first_subnode(port);
> + ofnode_valid(node);
> + node = dev_read_next_subnode(node)) {
> + ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge);
> + if (ret)
> + dev_dbg(dev, "Device failed ret=%d\n", ret);
> + else
> + break;
> + }
> +
> + /* Sanity check */
> + if (ret)
> + return ret;
> +
> ret = panel_get_display_timing(panel, &timings);
> if (ret) {
> ret = ofnode_decode_display_timing(dev_ofnode(panel),
> @@ -624,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev)
> reset_deassert(&rst);
>
> if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
> - ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
> - if (ret)
> - dev_dbg(dev,
> - "No video bridge, or no backlight on bridge\n");
> -
> if (bridge) {
> ret = video_bridge_attach(bridge);
> if (ret) {
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-09-04 12:53 ` [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
@ 2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
1 sibling, 1 reply; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:43 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> Use LTDC and LVDS nodes in all boot phases. This is specially useful
> before U-Boot relocation.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
> index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
> --- a/arch/arm/dts/stm32mp25-u-boot.dtsi
> +++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
> @@ -93,6 +93,14 @@
> bootph-all;
> };
>
> +<dc {
> + bootph-all;
> +};
> +
> +&lvds {
> + bootph-all;
> +};
> +
> &pinctrl {
> bootph-all;
> };
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
2025-09-12 16:46 ` Patrice CHOTARD
@ 2025-10-30 7:44 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
2 siblings, 0 replies; 39+ messages in thread
From: Yannick FERTRE @ 2025-10-30 7:44 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Patrice Chotard,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
Hi Raphael,
Thanks for the patch.
Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
> Compile VIDEO_STM32 and VIDEO_STM32_LVDS by default.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> configs/stm32mp25_defconfig | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/configs/stm32mp25_defconfig b/configs/stm32mp25_defconfig
> index 2b02cd86d6134497151e398eb54230f08d4e6272..e84df4a2a8f52890376f7689b01d3dcab5079ed9 100644
> --- a/configs/stm32mp25_defconfig
> +++ b/configs/stm32mp25_defconfig
> @@ -78,6 +78,9 @@ CONFIG_SERIAL_RX_BUFFER=y
> CONFIG_SPI=y
> CONFIG_DM_SPI=y
> # CONFIG_OPTEE_TA_AVB is not set
> +CONFIG_VIDEO=y
> +CONFIG_VIDEO_STM32=y
> +CONFIG_VIDEO_STM32_LVDS=y
> CONFIG_WDT=y
> CONFIG_WDT_STM32MP=y
> CONFIG_WDT_ARM_SMC=y
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
2025-10-30 7:35 ` Yannick FERTRE
@ 2025-11-01 9:03 ` Simon Glass
2025-11-02 1:09 ` [Uboot-stm32] " Raphaël Gallais-Pou
1 sibling, 1 reply; 39+ messages in thread
From: Simon Glass @ 2025-11-01 9:03 UTC (permalink / raw)
To: Raphael Gallais-Pou
Cc: Tom Rini, Kamil Lulko, Patrice Chotard, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Sumit Garg, Philippe Cornu,
Yannick Fertre, u-boot, uboot-stm32
Hi Raphael,
On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
<raphael.gallais-pou@foss.st.com> wrote:
>
> The "Display Timings" in panel-common.yaml can be provided by 2 properties
> - panel-timing: when display panels are restricted to a single resolution
> the "panel-timing" node expresses the required timings.
> - display-timings: several resolutions with different timings are supported
> with several timing subnode of "display-timings" node
>
> This patch update the parsing function to handle this 2 possibility
> when index = 0.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/core/ofnode.c | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> index e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9 100644
> --- a/drivers/core/ofnode.c
> +++ b/drivers/core/ofnode.c
> @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode parent, int index,
> int ret = 0;
>
> timings = ofnode_find_subnode(parent, "display-timings");
> - if (!ofnode_valid(timings))
> - return -EINVAL;
> -
> - i = 0;
> - ofnode_for_each_subnode(node, timings) {
> - if (i++ == index)
> - break;
> + if (ofnode_valid(timings)) {
> + i = 0;
> + ofnode_for_each_subnode(node, timings) {
> + if (i++ == index)
> + break;
> + }
> + } else {
> + if (index != 0)
> + return -EINVAL;
> + node = ofnode_find_subnode(parent, "panel-timing");
> }
>
> if (!ofnode_valid(node))
>
> --
> 2.25.1
>
Please add a test for this in test/dm/ofnode.c
Regards,
Simon
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-01 9:03 ` Simon Glass
@ 2025-11-02 1:09 ` Raphaël Gallais-Pou
2025-11-02 19:53 ` Simon Glass
0 siblings, 1 reply; 39+ messages in thread
From: Raphaël Gallais-Pou @ 2025-11-02 1:09 UTC (permalink / raw)
To: Simon Glass
Cc: Raphael Gallais-Pou, Tom Rini, Yannick Fertre, u-boot,
uboot-stm32, Sumit Garg, Patrick Delaunay, Kamil Lulko,
Dillon Min, Anatolij Gustschin
Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> Hi Raphael,
>
> On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> <raphael.gallais-pou@foss.st.com> wrote:
> >
> > The "Display Timings" in panel-common.yaml can be provided by 2 properties
> > - panel-timing: when display panels are restricted to a single resolution
> > the "panel-timing" node expresses the required timings.
> > - display-timings: several resolutions with different timings are supported
> > with several timing subnode of "display-timings" node
> >
> > This patch update the parsing function to handle this 2 possibility
> > when index = 0.
> >
> > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > ---
> > drivers/core/ofnode.c | 17 ++++++++++-------
> > 1 file changed, 10 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > index e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9 100644
> > --- a/drivers/core/ofnode.c
> > +++ b/drivers/core/ofnode.c
> > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode parent, int index,
> > int ret = 0;
> >
> > timings = ofnode_find_subnode(parent, "display-timings");
> > - if (!ofnode_valid(timings))
> > - return -EINVAL;
> > -
> > - i = 0;
> > - ofnode_for_each_subnode(node, timings) {
> > - if (i++ == index)
> > - break;
> > + if (ofnode_valid(timings)) {
> > + i = 0;
> > + ofnode_for_each_subnode(node, timings) {
> > + if (i++ == index)
> > + break;
> > + }
> > + } else {
> > + if (index != 0)
> > + return -EINVAL;
> > + node = ofnode_find_subnode(parent, "panel-timing");
> > }
> >
> > if (!ofnode_valid(node))
> >
> > --
> > 2.25.1
> >
>
> Please add a test for this in test/dm/ofnode.c
Hi Simon,
I'll gladly do that, but I haven't write and use any test in U-Boot. So
it is a bit foggy how to implement it.
Do we want to create a fake device-tree and test each configuration or
do we want to test in the _current_ device-tree if timings are correctly
set according to the index value ?
Best regards,
Raphaël
>
> Regards,
> Simon
> _______________________________________________
> Uboot-stm32 mailing list
> Uboot-stm32@st-md-mailman.stormreply.com
> https://st-md-mailman.stormreply.com/mailman/listinfo/uboot-stm32
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-02 1:09 ` [Uboot-stm32] " Raphaël Gallais-Pou
@ 2025-11-02 19:53 ` Simon Glass
2025-11-03 14:17 ` Tom Rini
0 siblings, 1 reply; 39+ messages in thread
From: Simon Glass @ 2025-11-02 19:53 UTC (permalink / raw)
To: Raphaël Gallais-Pou
Cc: Raphael Gallais-Pou, Tom Rini, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
Hi Raphael,
On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
wrote:
>
> Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> > Hi Raphael,
> >
> > On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> > <raphael.gallais-pou@foss.st.com> wrote:
> > >
> > > The "Display Timings" in panel-common.yaml can be provided by 2
properties
> > > - panel-timing: when display panels are restricted to a single
resolution
> > > the "panel-timing" node expresses the required
timings.
> > > - display-timings: several resolutions with different timings are
supported
> > > with several timing subnode of "display-timings"
node
> > >
> > > This patch update the parsing function to handle this 2 possibility
> > > when index = 0.
> > >
> > > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > > ---
> > > drivers/core/ofnode.c | 17 ++++++++++-------
> > > 1 file changed, 10 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > > index
e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9
100644
> > > --- a/drivers/core/ofnode.c
> > > +++ b/drivers/core/ofnode.c
> > > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode
parent, int index,
> > > int ret = 0;
> > >
> > > timings = ofnode_find_subnode(parent, "display-timings");
> > > - if (!ofnode_valid(timings))
> > > - return -EINVAL;
> > > -
> > > - i = 0;
> > > - ofnode_for_each_subnode(node, timings) {
> > > - if (i++ == index)
> > > - break;
> > > + if (ofnode_valid(timings)) {
> > > + i = 0;
> > > + ofnode_for_each_subnode(node, timings) {
> > > + if (i++ == index)
> > > + break;
> > > + }
> > > + } else {
> > > + if (index != 0)
> > > + return -EINVAL;
> > > + node = ofnode_find_subnode(parent, "panel-timing");
> > > }
> > >
> > > if (!ofnode_valid(node))
> > >
> > > --
> > > 2.25.1
> > >
> >
> > Please add a test for this in test/dm/ofnode.c
>
> Hi Simon,
>
> I'll gladly do that, but I haven't write and use any test in U-Boot. So
> it is a bit foggy how to implement it.
There is some info here:
https://docs.u-boot.org/en/latest/develop/testing.html
>
> Do we want to create a fake device-tree and test each configuration or
> do we want to test in the _current_ device-tree if timings are correctly
> set according to the index value ?
It looks like there is a 'display-timings' node in test.dts, with three
subnodes, so you should just be able to get an ofnode for that and then
read out one of them and check it.
Regards,
Simon
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-02 19:53 ` Simon Glass
@ 2025-11-03 14:17 ` Tom Rini
2025-11-04 14:01 ` Raphael Gallais-Pou
2025-11-04 16:31 ` Simon Glass
0 siblings, 2 replies; 39+ messages in thread
From: Tom Rini @ 2025-11-03 14:17 UTC (permalink / raw)
To: Simon Glass
Cc: Raphaël Gallais-Pou, Raphael Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
[-- Attachment #1: Type: text/plain, Size: 3671 bytes --]
On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
> Hi Raphael,
>
> On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
> wrote:
> >
> > Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> > > Hi Raphael,
> > >
> > > On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> > > <raphael.gallais-pou@foss.st.com> wrote:
> > > >
> > > > The "Display Timings" in panel-common.yaml can be provided by 2
> properties
> > > > - panel-timing: when display panels are restricted to a single
> resolution
> > > > the "panel-timing" node expresses the required
> timings.
> > > > - display-timings: several resolutions with different timings are
> supported
> > > > with several timing subnode of "display-timings"
> node
> > > >
> > > > This patch update the parsing function to handle this 2 possibility
> > > > when index = 0.
> > > >
> > > > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > > > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > > > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > > > ---
> > > > drivers/core/ofnode.c | 17 ++++++++++-------
> > > > 1 file changed, 10 insertions(+), 7 deletions(-)
> > > >
> > > > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > > > index
> e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9
> 100644
> > > > --- a/drivers/core/ofnode.c
> > > > +++ b/drivers/core/ofnode.c
> > > > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode
> parent, int index,
> > > > int ret = 0;
> > > >
> > > > timings = ofnode_find_subnode(parent, "display-timings");
> > > > - if (!ofnode_valid(timings))
> > > > - return -EINVAL;
> > > > -
> > > > - i = 0;
> > > > - ofnode_for_each_subnode(node, timings) {
> > > > - if (i++ == index)
> > > > - break;
> > > > + if (ofnode_valid(timings)) {
> > > > + i = 0;
> > > > + ofnode_for_each_subnode(node, timings) {
> > > > + if (i++ == index)
> > > > + break;
> > > > + }
> > > > + } else {
> > > > + if (index != 0)
> > > > + return -EINVAL;
> > > > + node = ofnode_find_subnode(parent, "panel-timing");
> > > > }
> > > >
> > > > if (!ofnode_valid(node))
> > > >
> > > > --
> > > > 2.25.1
> > > >
> > >
> > > Please add a test for this in test/dm/ofnode.c
> >
> > Hi Simon,
> >
> > I'll gladly do that, but I haven't write and use any test in U-Boot. So
> > it is a bit foggy how to implement it.
>
> There is some info here:
>
> https://docs.u-boot.org/en/latest/develop/testing.html
>
> >
> > Do we want to create a fake device-tree and test each configuration or
> > do we want to test in the _current_ device-tree if timings are correctly
> > set according to the index value ?
>
> It looks like there is a 'display-timings' node in test.dts, with three
> subnodes, so you should just be able to get an ofnode for that and then
> read out one of them and check it.
OK, but what is the utility in doing that? We don't, and aren't, going
to have tests for every valid possible DT node, and this isn't
introducing new library parsing functionality (the most recent patch to
test/dm/ofnode.c was for ofnode_graph and that is important to test). We
don't have display-timing tests to start with, so we're fine not adding
something more here.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-03 14:17 ` Tom Rini
@ 2025-11-04 14:01 ` Raphael Gallais-Pou
2025-11-04 16:21 ` Tom Rini
2025-11-04 16:31 ` Simon Glass
1 sibling, 1 reply; 39+ messages in thread
From: Raphael Gallais-Pou @ 2025-11-04 14:01 UTC (permalink / raw)
To: Tom Rini, Simon Glass
Cc: Raphaël Gallais-Pou, Yannick Fertre, U-Boot Mailing List,
U-Boot STM32, Sumit Garg, Patrick Delaunay, Kamil Lulko,
Dillon Min, Anatolij Gustschin, Patrice CHOTARD
On 11/3/25 15:17, Tom Rini wrote:
> On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
>> Hi Raphael,
>>
>> On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
>> wrote:
>>> Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
>>>> Hi Raphael,
>>>>
>>>> On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
>>>> <raphael.gallais-pou@foss.st.com> wrote:
...
>>>> Please add a test for this in test/dm/ofnode.c
>>> Hi Simon,
>>>
>>> I'll gladly do that, but I haven't write and use any test in U-Boot. So
>>> it is a bit foggy how to implement it.
>> There is some info here:
>>
>> https://docs.u-boot.org/en/latest/develop/testing.html
>>
>>> Do we want to create a fake device-tree and test each configuration or
>>> do we want to test in the _current_ device-tree if timings are correctly
>>> set according to the index value ?
>> It looks like there is a 'display-timings' node in test.dts, with three
>> subnodes, so you should just be able to get an ofnode for that and then
>> read out one of them and check it.
> OK, but what is the utility in doing that? We don't, and aren't, going
> to have tests for every valid possible DT node, and this isn't
> introducing new library parsing functionality (the most recent patch to
> test/dm/ofnode.c was for ofnode_graph and that is important to test). We
> don't have display-timing tests to start with, so we're fine not adding
> something more here.
>
(Adding back Patrice, whom had been removed from CC.)
Got it, so nothing to do on my side.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-04 14:01 ` Raphael Gallais-Pou
@ 2025-11-04 16:21 ` Tom Rini
2025-11-14 16:48 ` Patrice CHOTARD
0 siblings, 1 reply; 39+ messages in thread
From: Tom Rini @ 2025-11-04 16:21 UTC (permalink / raw)
To: Raphael Gallais-Pou
Cc: Simon Glass, Raphaël Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin, Patrice CHOTARD
[-- Attachment #1: Type: text/plain, Size: 1803 bytes --]
On Tue, Nov 04, 2025 at 03:01:31PM +0100, Raphael Gallais-Pou wrote:
>
>
> On 11/3/25 15:17, Tom Rini wrote:
> > On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
> >> Hi Raphael,
> >>
> >> On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
> >> wrote:
> >>> Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> >>>> Hi Raphael,
> >>>>
> >>>> On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> >>>> <raphael.gallais-pou@foss.st.com> wrote:
> ...
> >>>> Please add a test for this in test/dm/ofnode.c
> >>> Hi Simon,
> >>>
> >>> I'll gladly do that, but I haven't write and use any test in U-Boot. So
> >>> it is a bit foggy how to implement it.
> >> There is some info here:
> >>
> >> https://docs.u-boot.org/en/latest/develop/testing.html
> >>
> >>> Do we want to create a fake device-tree and test each configuration or
> >>> do we want to test in the _current_ device-tree if timings are correctly
> >>> set according to the index value ?
> >> It looks like there is a 'display-timings' node in test.dts, with three
> >> subnodes, so you should just be able to get an ofnode for that and then
> >> read out one of them and check it.
> > OK, but what is the utility in doing that? We don't, and aren't, going
> > to have tests for every valid possible DT node, and this isn't
> > introducing new library parsing functionality (the most recent patch to
> > test/dm/ofnode.c was for ofnode_graph and that is important to test). We
> > don't have display-timing tests to start with, so we're fine not adding
> > something more here.
> >
>
> (Adding back Patrice, whom had been removed from CC.)
>
> Got it, so nothing to do on my side.
For this specific part of the series, yes, this seems fine.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-03 14:17 ` Tom Rini
2025-11-04 14:01 ` Raphael Gallais-Pou
@ 2025-11-04 16:31 ` Simon Glass
2025-11-04 16:55 ` Tom Rini
1 sibling, 1 reply; 39+ messages in thread
From: Simon Glass @ 2025-11-04 16:31 UTC (permalink / raw)
To: Tom Rini
Cc: Raphaël Gallais-Pou, Raphael Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
Hi Tom,
On Mon, 3 Nov 2025 at 15:17, Tom Rini <trini@konsulko.com> wrote:
>
> On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
> > Hi Raphael,
> >
> > On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
> > wrote:
> > >
> > > Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> > > > Hi Raphael,
> > > >
> > > > On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> > > > <raphael.gallais-pou@foss.st.com> wrote:
> > > > >
> > > > > The "Display Timings" in panel-common.yaml can be provided by 2
> > properties
> > > > > - panel-timing: when display panels are restricted to a single
> > resolution
> > > > > the "panel-timing" node expresses the required
> > timings.
> > > > > - display-timings: several resolutions with different timings are
> > supported
> > > > > with several timing subnode of "display-timings"
> > node
> > > > >
> > > > > This patch update the parsing function to handle this 2 possibility
> > > > > when index = 0.
> > > > >
> > > > > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > > > > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > > > > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > > > > ---
> > > > > drivers/core/ofnode.c | 17 ++++++++++-------
> > > > > 1 file changed, 10 insertions(+), 7 deletions(-)
> > > > >
> > > > > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > > > > index
> > e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9
> > 100644
> > > > > --- a/drivers/core/ofnode.c
> > > > > +++ b/drivers/core/ofnode.c
> > > > > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode
> > parent, int index,
> > > > > int ret = 0;
> > > > >
> > > > > timings = ofnode_find_subnode(parent, "display-timings");
> > > > > - if (!ofnode_valid(timings))
> > > > > - return -EINVAL;
> > > > > -
> > > > > - i = 0;
> > > > > - ofnode_for_each_subnode(node, timings) {
> > > > > - if (i++ == index)
> > > > > - break;
> > > > > + if (ofnode_valid(timings)) {
> > > > > + i = 0;
> > > > > + ofnode_for_each_subnode(node, timings) {
> > > > > + if (i++ == index)
> > > > > + break;
> > > > > + }
> > > > > + } else {
> > > > > + if (index != 0)
> > > > > + return -EINVAL;
> > > > > + node = ofnode_find_subnode(parent, "panel-timing");
> > > > > }
> > > > >
> > > > > if (!ofnode_valid(node))
> > > > >
> > > > > --
> > > > > 2.25.1
> > > > >
> > > >
> > > > Please add a test for this in test/dm/ofnode.c
> > >
> > > Hi Simon,
> > >
> > > I'll gladly do that, but I haven't write and use any test in U-Boot. So
> > > it is a bit foggy how to implement it.
> >
> > There is some info here:
> >
> > https://docs.u-boot.org/en/latest/develop/testing.html
> >
> > >
> > > Do we want to create a fake device-tree and test each configuration or
> > > do we want to test in the _current_ device-tree if timings are correctly
> > > set according to the index value ?
> >
> > It looks like there is a 'display-timings' node in test.dts, with three
> > subnodes, so you should just be able to get an ofnode for that and then
> > read out one of them and check it.
>
> OK, but what is the utility in doing that? We don't, and aren't, going
> to have tests for every valid possible DT node, and this isn't
> introducing new library parsing functionality (the most recent patch to
> test/dm/ofnode.c was for ofnode_graph and that is important to test). We
> don't have display-timing tests to start with, so we're fine not adding
> something more here.
The utility is that code is tested, so it works now and doesn't break
later. For ofnode we do have tests - see test/dm/ofnode.c
Regards,
Simon
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-04 16:31 ` Simon Glass
@ 2025-11-04 16:55 ` Tom Rini
2025-11-07 12:23 ` Simon Glass
0 siblings, 1 reply; 39+ messages in thread
From: Tom Rini @ 2025-11-04 16:55 UTC (permalink / raw)
To: Simon Glass
Cc: Raphaël Gallais-Pou, Raphael Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
[-- Attachment #1: Type: text/plain, Size: 4744 bytes --]
On Tue, Nov 04, 2025 at 05:31:21PM +0100, Simon Glass wrote:
> Hi Tom,
>
> On Mon, 3 Nov 2025 at 15:17, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
> > > Hi Raphael,
> > >
> > > On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
> > > wrote:
> > > >
> > > > Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> > > > > Hi Raphael,
> > > > >
> > > > > On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> > > > > <raphael.gallais-pou@foss.st.com> wrote:
> > > > > >
> > > > > > The "Display Timings" in panel-common.yaml can be provided by 2
> > > properties
> > > > > > - panel-timing: when display panels are restricted to a single
> > > resolution
> > > > > > the "panel-timing" node expresses the required
> > > timings.
> > > > > > - display-timings: several resolutions with different timings are
> > > supported
> > > > > > with several timing subnode of "display-timings"
> > > node
> > > > > >
> > > > > > This patch update the parsing function to handle this 2 possibility
> > > > > > when index = 0.
> > > > > >
> > > > > > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > > > > > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > > > > > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > > > > > ---
> > > > > > drivers/core/ofnode.c | 17 ++++++++++-------
> > > > > > 1 file changed, 10 insertions(+), 7 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > > > > > index
> > > e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9
> > > 100644
> > > > > > --- a/drivers/core/ofnode.c
> > > > > > +++ b/drivers/core/ofnode.c
> > > > > > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode
> > > parent, int index,
> > > > > > int ret = 0;
> > > > > >
> > > > > > timings = ofnode_find_subnode(parent, "display-timings");
> > > > > > - if (!ofnode_valid(timings))
> > > > > > - return -EINVAL;
> > > > > > -
> > > > > > - i = 0;
> > > > > > - ofnode_for_each_subnode(node, timings) {
> > > > > > - if (i++ == index)
> > > > > > - break;
> > > > > > + if (ofnode_valid(timings)) {
> > > > > > + i = 0;
> > > > > > + ofnode_for_each_subnode(node, timings) {
> > > > > > + if (i++ == index)
> > > > > > + break;
> > > > > > + }
> > > > > > + } else {
> > > > > > + if (index != 0)
> > > > > > + return -EINVAL;
> > > > > > + node = ofnode_find_subnode(parent, "panel-timing");
> > > > > > }
> > > > > >
> > > > > > if (!ofnode_valid(node))
> > > > > >
> > > > > > --
> > > > > > 2.25.1
> > > > > >
> > > > >
> > > > > Please add a test for this in test/dm/ofnode.c
> > > >
> > > > Hi Simon,
> > > >
> > > > I'll gladly do that, but I haven't write and use any test in U-Boot. So
> > > > it is a bit foggy how to implement it.
> > >
> > > There is some info here:
> > >
> > > https://docs.u-boot.org/en/latest/develop/testing.html
> > >
> > > >
> > > > Do we want to create a fake device-tree and test each configuration or
> > > > do we want to test in the _current_ device-tree if timings are correctly
> > > > set according to the index value ?
> > >
> > > It looks like there is a 'display-timings' node in test.dts, with three
> > > subnodes, so you should just be able to get an ofnode for that and then
> > > read out one of them and check it.
> >
> > OK, but what is the utility in doing that? We don't, and aren't, going
> > to have tests for every valid possible DT node, and this isn't
> > introducing new library parsing functionality (the most recent patch to
> > test/dm/ofnode.c was for ofnode_graph and that is important to test). We
> > don't have display-timing tests to start with, so we're fine not adding
> > something more here.
>
> The utility is that code is tested, so it works now and doesn't break
> later. For ofnode we do have tests - see test/dm/ofnode.c
That is a circular and unsatisfying answer to what I said. I did read
test/dm/ofnode.c and then re-read the patch and don't see any value in
adding nodes and then reading nodes, but gave an example of what kind of
changes do make sense to add tests for because they add value. And in
the interest of not having yet another seemingly infinite thread with
you, this is all I'm going to further add here.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-04 16:55 ` Tom Rini
@ 2025-11-07 12:23 ` Simon Glass
0 siblings, 0 replies; 39+ messages in thread
From: Simon Glass @ 2025-11-07 12:23 UTC (permalink / raw)
To: Tom Rini
Cc: Raphaël Gallais-Pou, Raphael Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
Hi Raphale,
On Tue, 4 Nov 2025 at 17:55, Tom Rini <trini@konsulko.com> wrote:
>
> On Tue, Nov 04, 2025 at 05:31:21PM +0100, Simon Glass wrote:
> > Hi Tom,
> >
> > On Mon, 3 Nov 2025 at 15:17, Tom Rini <trini@konsulko.com> wrote:
> > >
> > > On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
> > > > Hi Raphael,
> > > >
> > > > On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
> > > > wrote:
> > > > >
> > > > > Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
> > > > > > Hi Raphael,
> > > > > >
> > > > > > On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
> > > > > > <raphael.gallais-pou@foss.st.com> wrote:
> > > > > > >
> > > > > > > The "Display Timings" in panel-common.yaml can be provided by 2
> > > > properties
> > > > > > > - panel-timing: when display panels are restricted to a single
> > > > resolution
> > > > > > > the "panel-timing" node expresses the required
> > > > timings.
> > > > > > > - display-timings: several resolutions with different timings are
> > > > supported
> > > > > > > with several timing subnode of "display-timings"
> > > > node
> > > > > > >
> > > > > > > This patch update the parsing function to handle this 2 possibility
> > > > > > > when index = 0.
> > > > > > >
> > > > > > > Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> > > > > > > Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> > > > > > > Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> > > > > > > ---
> > > > > > > drivers/core/ofnode.c | 17 ++++++++++-------
> > > > > > > 1 file changed, 10 insertions(+), 7 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
> > > > > > > index
> > > > e040e3f2806ffe74c58dcd82f36307351acd5a99..5a721b46e5a3214e7bd437739776362c2d22a3c9
> > > > 100644
> > > > > > > --- a/drivers/core/ofnode.c
> > > > > > > +++ b/drivers/core/ofnode.c
> > > > > > > @@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode
> > > > parent, int index,
> > > > > > > int ret = 0;
> > > > > > >
> > > > > > > timings = ofnode_find_subnode(parent, "display-timings");
> > > > > > > - if (!ofnode_valid(timings))
> > > > > > > - return -EINVAL;
> > > > > > > -
> > > > > > > - i = 0;
> > > > > > > - ofnode_for_each_subnode(node, timings) {
> > > > > > > - if (i++ == index)
> > > > > > > - break;
> > > > > > > + if (ofnode_valid(timings)) {
> > > > > > > + i = 0;
> > > > > > > + ofnode_for_each_subnode(node, timings) {
> > > > > > > + if (i++ == index)
> > > > > > > + break;
> > > > > > > + }
> > > > > > > + } else {
> > > > > > > + if (index != 0)
> > > > > > > + return -EINVAL;
> > > > > > > + node = ofnode_find_subnode(parent, "panel-timing");
> > > > > > > }
> > > > > > >
> > > > > > > if (!ofnode_valid(node))
> > > > > > >
> > > > > > > --
> > > > > > > 2.25.1
> > > > > > >
> > > > > >
> > > > > > Please add a test for this in test/dm/ofnode.c
> > > > >
> > > > > Hi Simon,
> > > > >
> > > > > I'll gladly do that, but I haven't write and use any test in U-Boot. So
> > > > > it is a bit foggy how to implement it.
> > > >
> > > > There is some info here:
> > > >
> > > > https://docs.u-boot.org/en/latest/develop/testing.html
> > > >
> > > > >
> > > > > Do we want to create a fake device-tree and test each configuration or
> > > > > do we want to test in the _current_ device-tree if timings are correctly
> > > > > set according to the index value ?
> > > >
> > > > It looks like there is a 'display-timings' node in test.dts, with three
> > > > subnodes, so you should just be able to get an ofnode for that and then
> > > > read out one of them and check it.
> > >
> > > OK, but what is the utility in doing that? We don't, and aren't, going
> > > to have tests for every valid possible DT node, and this isn't
> > > introducing new library parsing functionality (the most recent patch to
> > > test/dm/ofnode.c was for ofnode_graph and that is important to test). We
> > > don't have display-timing tests to start with, so we're fine not adding
> > > something more here.
> >
> > The utility is that code is tested, so it works now and doesn't break
> > later. For ofnode we do have tests - see test/dm/ofnode.c
>
> That is a circular and unsatisfying answer to what I said. I did read
> test/dm/ofnode.c and then re-read the patch and don't see any value in
> adding nodes and then reading nodes, but gave an example of what kind of
> changes do make sense to add tests for because they add value. And in
> the interest of not having yet another seemingly infinite thread with
> you, this is all I'm going to further add here.
The nodes exist in test.dts so it should just be a case of calling
ofnode_decode_display_timing() with a given index and checking what is
returned. That would be enough to test your code, if you'd like to.
Regards,
SImon
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing
2025-11-04 16:21 ` Tom Rini
@ 2025-11-14 16:48 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:48 UTC (permalink / raw)
To: Tom Rini, Raphael Gallais-Pou
Cc: Simon Glass, Raphaël Gallais-Pou, Yannick Fertre,
U-Boot Mailing List, U-Boot STM32, Sumit Garg, Patrick Delaunay,
Kamil Lulko, Dillon Min, Anatolij Gustschin
On 11/4/25 17:21, Tom Rini wrote:
> On Tue, Nov 04, 2025 at 03:01:31PM +0100, Raphael Gallais-Pou wrote:
>>
>>
>> On 11/3/25 15:17, Tom Rini wrote:
>>> On Sun, Nov 02, 2025 at 08:53:43PM +0100, Simon Glass wrote:
>>>> Hi Raphael,
>>>>
>>>> On Sun, 2 Nov 2025 at 02:10, Raphaël Gallais-Pou <rgallaispou@gmail.com>
>>>> wrote:
>>>>> Le Sat, Nov 01, 2025 at 10:03:59AM +0100, Simon Glass a écrit :
>>>>>> Hi Raphael,
>>>>>>
>>>>>> On Thu, 4 Sept 2025 at 14:53, Raphael Gallais-Pou
>>>>>> <raphael.gallais-pou@foss.st.com> wrote:
>> ...
>>>>>> Please add a test for this in test/dm/ofnode.c
>>>>> Hi Simon,
>>>>>
>>>>> I'll gladly do that, but I haven't write and use any test in U-Boot. So
>>>>> it is a bit foggy how to implement it.
>>>> There is some info here:
>>>>
>>>> https://docs.u-boot.org/en/latest/develop/testing.html
>>>>
>>>>> Do we want to create a fake device-tree and test each configuration or
>>>>> do we want to test in the _current_ device-tree if timings are correctly
>>>>> set according to the index value ?
>>>> It looks like there is a 'display-timings' node in test.dts, with three
>>>> subnodes, so you should just be able to get an ofnode for that and then
>>>> read out one of them and check it.
>>> OK, but what is the utility in doing that? We don't, and aren't, going
>>> to have tests for every valid possible DT node, and this isn't
>>> introducing new library parsing functionality (the most recent patch to
>>> test/dm/ofnode.c was for ofnode_graph and that is important to test). We
>>> don't have display-timing tests to start with, so we're fine not adding
>>> something more here.
>>>
>>
>> (Adding back Patrice, whom had been removed from CC.)
>>
>> Got it, so nothing to do on my side.
>
> For this specific part of the series, yes, this seems fine.
>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display
2025-09-04 12:53 ` [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display Raphael Gallais-Pou
2025-10-30 7:41 ` Yannick FERTRE
@ 2025-11-14 16:48 ` Patrice CHOTARD
1 sibling, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:48 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> Add the compatible "panel-lvds" for simple-panel driver in U-Boot. In
> Linux this compatible is managed by the driver
> drivers/gpu/drm/panel/panel-lvds.c but in U-Boot the specific LVDS
> features (bus_format/bus_flags) are not supported.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> drivers/video/simple_panel.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
> index b6c5b058b2e967bbbd47ab3c3ce5ca52c7804409..0f23df701bc3c40ea49380bbfa3743ee592d8bd3 100644
> --- a/drivers/video/simple_panel.c
> +++ b/drivers/video/simple_panel.c
> @@ -191,6 +191,7 @@ static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
>
> static const struct udevice_id simple_panel_ids[] = {
> { .compatible = "simple-panel" },
> + { .compatible = "panel-lvds" },
> { .compatible = "auo,b133xtn01" },
> { .compatible = "auo,b116xw03" },
> { .compatible = "auo,b133htn01" },
>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
2025-09-12 16:46 ` Patrice CHOTARD
2025-10-30 7:44 ` Yannick FERTRE
@ 2025-11-14 16:48 ` Patrice CHOTARD
2 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:48 UTC (permalink / raw)
To: Raphael Gallais-Pou, Tom Rini, Kamil Lulko, Dillon Min,
Patrick Delaunay, Anatolij Gustschin, Simon Glass, Sumit Garg,
Philippe Cornu, Yannick Fertre
Cc: u-boot, uboot-stm32
On 9/4/25 14:53, Raphael Gallais-Pou wrote:
> Compile VIDEO_STM32 and VIDEO_STM32_LVDS by default.
>
> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
> ---
> configs/stm32mp25_defconfig | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/configs/stm32mp25_defconfig b/configs/stm32mp25_defconfig
> index 2b02cd86d6134497151e398eb54230f08d4e6272..e84df4a2a8f52890376f7689b01d3dcab5079ed9 100644
> --- a/configs/stm32mp25_defconfig
> +++ b/configs/stm32mp25_defconfig
> @@ -78,6 +78,9 @@ CONFIG_SERIAL_RX_BUFFER=y
> CONFIG_SPI=y
> CONFIG_DM_SPI=y
> # CONFIG_OPTEE_TA_AVB is not set
> +CONFIG_VIDEO=y
> +CONFIG_VIDEO_STM32=y
> +CONFIG_VIDEO_STM32_LVDS=y
> CONFIG_WDT=y
> CONFIG_WDT_STM32MP=y
> CONFIG_WDT_ARM_SMC=y
>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS
2025-10-30 7:42 ` Yannick FERTRE
@ 2025-11-14 16:48 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:48 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
On 10/30/25 08:42, Yannick FERTRE wrote:
> Hi Raphael,
>
> Thanks for the patch.
>
> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>
> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>> The LVDS Display Interface Transmitter handles the LVDS protocol:
>> it maps the pixels received from the upstream Pixel-DMA (LTDC)
>> onto the LVDS PHY.
>>
>> The LVDS controller driver supports the following high-level features:
>> • FDP-Link-I and OpenLDI (v0.95) protocols
>> • Single-Link or Dual-Link operation
>> • Single-Display or Double-Display (with the same content
>> duplicated on both)
>> • Flexible Bit-Mapping, including JEIDA and VESA
>> • RGB888 or RGB666 output
>> • Synchronous design, with one input pixel per clock cycle
>> • No resolution limitation.
>>
>> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>> ---
>> MAINTAINERS | 1 +
>> doc/board/st/st-dt.rst | 1 +
>> drivers/video/stm32/Kconfig | 9 +
>> drivers/video/stm32/Makefile | 1 +
>> drivers/video/stm32/stm32_lvds.c | 693 +++++++++++++++++++++++++++++++++++++++
>> 5 files changed, 705 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 3fb163aa1db1dba249e60b40ab3e785db4fde5d3..604ba4ca04c1079b14cd8f4c1fff4c12da0e278d 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -728,6 +728,7 @@ F: drivers/serial/serial_stm32.*
>> F: drivers/spi/stm32_qspi.c
>> F: drivers/spi/stm32_spi.c
>> F: drivers/video/stm32/stm32_ltdc.c
>> +F: drivers/video/stm32/stm32_lvds.c
>> F: drivers/watchdog/stm32mp_wdt.c
>> F: include/dt-bindings/clock/stm32fx-clock.h
>> F: include/dt-bindings/clock/stm32mp*
>> diff --git a/doc/board/st/st-dt.rst b/doc/board/st/st-dt.rst
>> index 2a285c81807ee26924948d8c2497f11dfe45b6e6..28cada97e72da711408a1aaaa928ad3faa51162e 100644
>> --- a/doc/board/st/st-dt.rst
>> +++ b/doc/board/st/st-dt.rst
>> @@ -25,6 +25,7 @@ kernel binding directory = Documentation/devicetree/bindings/
>> * display
>> - display/st,stm32-dsi.yaml
>> - display/st,stm32-ltdc.yaml
>> + - display/st,stm32mp25-lvds.yaml
>> * gpio
>> - pinctrl/st,stm32-pinctrl.yaml
>> * hwlock
>> diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
>> index c354c402c288b4c004bd2b5cc74a4d00beef2773..4cb8a841caf51aaba5115b9fcddb967f73a3d9b4 100644
>> --- a/drivers/video/stm32/Kconfig
>> +++ b/drivers/video/stm32/Kconfig
>> @@ -23,6 +23,15 @@ config VIDEO_STM32_DSI
>> This option enables support DSI internal bridge which can be used on
>> devices which have DSI devices connected.
>> +config VIDEO_STM32_LVDS
>> + bool "Enable STM32 LVDS video support"
>> + depends on VIDEO_STM32
>> + select VIDEO_BRIDGE
>> + select VIDEO_DW_MIPI_DSI
>> + help
>> + This enables Low Voltage Differential Signaling (LVDS) display
>> + support.
>> +
>> config VIDEO_STM32_MAX_XRES
>> int "Maximum horizontal resolution (for memory allocation purposes)"
>> depends on VIDEO_STM32
>> diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
>> index f8b42d1a4d126a31aa11ce820e829b133ca3bb2d..059d9000c1d7762d06bff36d5408143a34848227 100644
>> --- a/drivers/video/stm32/Makefile
>> +++ b/drivers/video/stm32/Makefile
>> @@ -7,3 +7,4 @@
>> obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
>> obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
>> +obj-${CONFIG_VIDEO_STM32_LVDS} += stm32_lvds.o
>> diff --git a/drivers/video/stm32/stm32_lvds.c b/drivers/video/stm32/stm32_lvds.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..bf1393c9e8724ed099aeed63344ebdc0004507b6
>> --- /dev/null
>> +++ b/drivers/video/stm32/stm32_lvds.c
>> @@ -0,0 +1,693 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
>> +/*
>> + * Copyright (C) 2025 STMicroelectronics - All Rights Reserved
>> + * Author(s): Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
>> + *
>> + * This Low Voltage Differential Signal controller driver is based on the Linux Kernel driver from
>> + * drivers/gpu/drm/stm/ltdc.c
>> + */
>> +
>> +#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
>> +
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <log.h>
>> +#include <media_bus_format.h>
>> +#include <panel.h>
>> +#include <reset.h>
>> +#include <video.h>
>> +#include <video_bridge.h>
>> +#include <asm/io.h>
>> +#include <dm/device_compat.h>
>> +#include <dm/ofnode.h>
>> +#include <linux/iopoll.h>
>> +
>> +/* LVDS Host registers */
>> +#define LVDS_CR 0x0000 /* configuration register */
>> +#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */
>> +#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */
>> +#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */
>> +#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */
>> +#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */
>> +#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */
>> +#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */
>> +#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */
>> +#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */
>> +#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */
>> +#define LVDS_DMLCR(id) (LVDS_DMLCR0 + 8U * (id))
>> +#define LVDS_DMMCR(id) (LVDS_DMMCR0 + 8U * (id))
>> +#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */
>> +#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */
>> +
>> +#define CDL1CR_DEFAULT 0x4321
>> +#define CDL2CR_DEFAULT 0x59876
>> +
>> +/* LVDS Host registers */
>> +#define LVDS_PHY_MASTER 0x0
>> +#define LVDS_PHY_SLAVE 0x100
>> +
>> +/* phy parameter can only be one of those two above */
>> +#define LVDS_PXGCR(phy) ((phy) + 0x1000) /* Global Control Register */
>> +#define LVDS_PXCMCR1(phy) ((phy) + 0x100C) /* Current Mode Control Register 1 */
>> +#define LVDS_PXCMCR2(phy) ((phy) + 0x1010) /* Current Mode Control Register 2 */
>> +#define LVDS_PXSCR(phy) ((phy) + 0x1020) /* Serial Control Register */
>> +#define LVDS_PXBCR1(phy) ((phy) + 0x102C) /* Bias Control Register 1 */
>> +#define LVDS_PXBCR2(phy) ((phy) + 0x1030) /* Bias Control Register 2 */
>> +#define LVDS_PXBCR3(phy) ((phy) + 0x1034) /* Bias Control Register 3 */
>> +#define LVDS_PXMPLCR(phy) ((phy) + 0x1064) /* Monitor PLL Lock Control Register */
>> +#define LVDS_PXDCR(phy) ((phy) + 0x1084) /* Debug Control Register */
>> +#define LVDS_PXSSR1(phy) ((phy) + 0x1088) /* Spare Status Register 1 */
>> +#define LVDS_PXCFGCR(phy) ((phy) + 0x10A0) /* Configuration Control Register */
>> +#define LVDS_PXPLLCR1(phy) ((phy) + 0x10C0) /* PLL_MODE 1 Control Register */
>> +#define LVDS_PXPLLCR2(phy) ((phy) + 0x10C4) /* PLL_MODE 2 Control Register */
>> +#define LVDS_PXPLLSR(phy) ((phy) + 0x10C8) /* PLL Status Register */
>> +#define LVDS_PXPLLSDCR1(phy) ((phy) + 0x10CC) /* PLL_SD_1 Control Register */
>> +#define LVDS_PXPLLSDCR2(phy) ((phy) + 0x10D0) /* PLL_SD_2 Control Register */
>> +#define LVDS_PXPLLTWGCR1(phy) ((phy) + 0x10D4) /* PLL_TWG_1 Control Register */
>> +#define LVDS_PXPLLTWGCR2(phy) ((phy) + 0x10D8) /* PLL_TWG_2 Control Register */
>> +#define LVDS_PXPLLCPCR(phy) ((phy) + 0x10E0) /* PLL_CP Control Register */
>> +#define LVDS_PXPLLTESTCR(phy) ((phy) + 0x10E8) /* PLL_TEST Control Register */
>> +
>> +/* LVDS Wrapper registers */
>> +#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */
>> +#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */
>> +#define LVDS_VERR 0x1FF4 /* Version register */
>> +#define LVDS_IPIDR 0x1FF8 /* Identification register */
>> +#define LVDS_SIDR 0x1FFC /* Size Identification register */
>> +
>> +#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */
>> +#define CR_HSPOL BIT(1) /* HS Polarity (horizontal sync) */
>> +#define CR_VSPOL BIT(2) /* VS Polarity (vertical sync) */
>> +#define CR_DEPOL BIT(3) /* DE Polarity (data enable) */
>> +#define CR_CI BIT(4) /* Control Internal (software controlled bit) */
>> +#define CR_LKMOD BIT(5) /* Link Mode, for both Links */
>> +#define CR_LKPHA BIT(6) /* Link Phase, for both Links */
>> +#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */
>> +#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */
>> +
>> +#define DMMCRX_MAP0 GENMASK(4, 0)
>> +#define DMMCRX_MAP1 GENMASK(9, 5)
>> +#define DMMCRX_MAP2 GENMASK(14, 10)
>> +#define DMMCRX_MAP3 GENMASK(19, 15)
>> +#define DMLCRX_MAP4 GENMASK(4, 0)
>> +#define DMLCRX_MAP5 GENMASK(9, 5)
>> +#define DMLCRX_MAP6 GENMASK(14, 10)
>> +
>> +#define CDLCRX_DISTR0 GENMASK(3, 0)
>> +#define CDLCRX_DISTR1 GENMASK(7, 4)
>> +#define CDLCRX_DISTR2 GENMASK(11, 8)
>> +#define CDLCRX_DISTR3 GENMASK(15, 12)
>> +#define CDLCRX_DISTR4 GENMASK(19, 16)
>> +
>> +#define FREF_INDEX 0
>> +#define NDIV_INDEX 1
>> +#define FPFD_INDEX 2
>> +#define MDIV_INDEX 3
>> +#define FVCO_INDEX 4
>> +#define BDIV_INDEX 5
>> +#define FBIT_INDEX 6
>> +#define FLS_INDEX 7
>> +#define FDP_INDEX 8
>> +
>> +#define PXGCR_BIT_CLK_OUT BIT(0)
>> +#define PXGCR_LS_CLK_OUT BIT(4)
>> +#define PXGCR_DP_CLK_OUT BIT(8)
>> +#define PXGCR_RSTZ BIT(24)
>> +#define PXGCR_DIV_RSTN BIT(25)
>> +
>> +#define PXCMCR1_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4))
>> +#define PXCMCR2_CM_EN_DL4 BIT(4)
>> +#define PXSCR_SER_DATA_OK BIT(16)
>> +#define PXBCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
>> +#define PXBCR2_BIAS_EN BIT(28)
>> +#define PXBCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
>> +#define PXDCR_POWER_OK BIT(12)
>> +#define PXCFGCR_EN_DIG_DL GENMASK(4, 0)
>> +
>> +#define PXPLLCR1_PLL_EN BIT(0)
>> +#define PxPLLCR1_SD_EN BIT(1)
>> +#define PXPLLCR1_TWG_EN BIT(2)
>> +#define PXPLLCR1_PLL_DIVIDERS_EN BIT(8)
>> +#define PXPLLCR2_NDIV GENMASK(25, 16)
>> +#define PXPLLCR2_BDIV GENMASK(9, 0)
>> +#define PXPLLSR_PLL_LOCK BIT(0)
>> +#define PXPLLSDCR1_MDIV GENMASK(9, 0)
>> +#define PXPLLCPCR_CPCTRL_DEFAULT 0x1
>> +#define PXPLLTESTCR_PLL_TEST_CLK_EN BIT(0)
>> +#define PXPLLTESTCR_PLL_TDIV_EN BIT(8)
>> +#define PXPLLTESTCR_TDIV GENMASK(25, 16)
>> +#define PXPLLTESTCR_TDIV_VALUE 70
>> +
>> +#define WCLKCR_SLV_CLKPIX_SEL BIT(0)
>> +#define WCLKCR_SRCSEL BIT(8)
>> +
>> +/* Sleep & timeout for pll lock/unlock */
>> +#define SLEEP_US 1000
>> +#define TIMEOUT_US 20000000
>> +
>> +#define PHY_SLV_OFS 0x100
>> +
>> +/* PLL parameters */
>> +#define NDIV_MIN 2
>> +#define NDIV_MAX 6
>> +#define BDIV_MIN 2
>> +#define BDIV_MAX 6
>> +#define MDIV_MIN 1
>> +#define MDIV_MAX 1023
>> +
>> +struct stm32_lvds_plat {
>> + void __iomem *base;
>> + struct udevice *panel;
>> + struct reset_ctl rst;
>> + struct clk pclk;
>> + struct clk refclk;
>> +};
>> +
>> +struct stm32_lvds_priv {
>> + struct display_timing timings;
>> + u32 refclk_rate;
>> + int dual_link;
>> + int bus_format;
>> +};
>> +
>> +/*
>> + * enum lvds_pixels_order - Pixel order of an LVDS connection
>> + * @LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
>> + * from the first port, odd pixels from the second port
>> + * @LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated
>> + * from the first port, even pixels from the second port
>> + */
>> +enum lvds_pixels_order {
>> + LVDS_DUAL_LINK_EVEN_ODD_PIXELS = BIT(0),
>> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS = BIT(1),
>> +};
>> +
>> +enum lvds_pixel {
>> + PIX_R_0 = 0x00,
>> + PIX_R_1 = 0x01,
>> + PIX_R_2 = 0x02,
>> + PIX_R_3 = 0x03,
>> + PIX_R_4 = 0x04,
>> + PIX_R_5 = 0x05,
>> + PIX_R_6 = 0x06,
>> + PIX_R_7 = 0x07,
>> + PIX_G_0 = 0x08,
>> + PIX_G_1 = 0x09,
>> + PIX_G_2 = 0x0A,
>> + PIX_G_3 = 0x0B,
>> + PIX_G_4 = 0x0C,
>> + PIX_G_5 = 0x0D,
>> + PIX_G_6 = 0x0E,
>> + PIX_G_7 = 0x0F,
>> + PIX_B_0 = 0x10,
>> + PIX_B_1 = 0x11,
>> + PIX_B_2 = 0x12,
>> + PIX_B_3 = 0x13,
>> + PIX_B_4 = 0x14,
>> + PIX_B_5 = 0x15,
>> + PIX_B_6 = 0x16,
>> + PIX_B_7 = 0x17,
>> + PIX_H_S = 0x18,
>> + PIX_V_S = 0x19,
>> + PIX_D_E = 0x1A,
>> + PIX_C_E = 0x1B,
>> + PIX_C_I = 0x1C,
>> + PIX_TOG = 0x1D,
>> + PIX_ONE = 0x1E,
>> + PIX_ZER = 0x1F,
>> +};
>> +
>> +/*
>> + * Expected JEIDA-RGB888 data to be sent in LSB format
>> + * bit6 ............................bit0
>> + */
>> +const enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
>> + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
>> + { PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
>> + { PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
>> + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
>> + { PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
>> +};
>> +
>> +/*
>> + * Expected VESA-RGB888 data to be sent in LSB format
>> + * bit6 ............................bit0
>> + */
>> +const enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
>> + { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
>> + { PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
>> + { PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
>> + { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
>> + { PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
>> +};
>> +
>> +static inline void lvds_writel(void __iomem *base, u32 reg, u32 val)
>> +{
>> + writel(val, base + reg);
>> +}
>> +
>> +static inline u32 lvds_readl(void __iomem *base, u32 reg)
>> +{
>> + return readl(base + reg);
>> +}
>> +
>> +static inline void lvds_set(void __iomem *base, u32 reg, u32 mask)
>> +{
>> + lvds_writel(base, reg, lvds_readl(base, reg) | mask);
>> +}
>> +
>> +static inline void lvds_clear(void __iomem *base, u32 reg, u32 mask)
>> +{
>> + lvds_writel(base, reg, lvds_readl(base, reg) & ~mask);
>> +}
>> +
>> +static u32 pll_get_clkout_khz(u32 clkin_khz, u32 bdiv, u32 mdiv, u32 ndiv)
>> +{
>> + int divisor = ndiv * bdiv;
>> +
>> + /* Prevents from division by 0 */
>> + if (!divisor)
>> + return 0;
>> +
>> + return clkin_khz * mdiv / divisor;
>> +}
>> +
>> +static int lvds_pll_get_params(u32 clkin_khz, u32 clkout_khz,
>> + u32 *bdiv, u32 *mdiv, u32 *ndiv)
>> +{
>> + u32 i, o, n;
>> + u32 delta, best_delta; /* all in khz */
>> +
>> + /* Early checks preventing division by 0 & odd results */
>> + if (clkin_khz == 0 || clkout_khz == 0)
>> + return -EINVAL;
>> +
>> + best_delta = 1000000; /* big started value (1000000khz) */
>> +
>> + for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
>> + for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
>> + n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
>> + /* Check ndiv according to vco range */
>> + if (n < MDIV_MIN || n > MDIV_MAX)
>> + continue;
>> + /* Check if new delta is better & saves parameters */
>> + delta = abs(pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz);
>> + if (delta < best_delta) {
>> + *ndiv = i;
>> + *mdiv = n;
>> + *bdiv = o;
>> + best_delta = delta;
>> + }
>> + /* fast return in case of "perfect result" */
>> + if (!delta)
>> + return 0;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_lvds_pll_enable(struct udevice *dev,
>> + int phy)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
>> + struct display_timing timings = priv->timings;
>> + u32 pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
>> + int ret, val, multiplier;
>> +
>> + /* Release PHY from reset */
>> + lvds_set(plat->base, LVDS_PXGCR(phy), PXGCR_DIV_RSTN | PXGCR_RSTZ);
>> +
>> + /* lvds_pll_config */
>> + /* Set PLL Slv & Mst configs and timings */
>> + pll_in_khz = priv->refclk_rate / 1000;
>> +
>> + if (priv->dual_link)
>> + multiplier = 2;
>> + else
>> + multiplier = 1;
>> +
>> + ret = lvds_pll_get_params(pll_in_khz, timings.pixelclock.typ * 7 / 1000 / multiplier,
>> + &bdiv, &mdiv, &ndiv);
>> + if (ret)
>> + return ret;
>> +
>> + /* Set PLL parameters */
>> + lvds_writel(plat->base, LVDS_PXPLLCR2(phy), (ndiv << 16) | bdiv);
>> + lvds_writel(plat->base, LVDS_PXPLLSDCR1(phy), mdiv);
>> + lvds_writel(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_TDIV_VALUE << 16);
>> +
>> + /* Disable TWG and SD: for now, PLL just need to be in integer mode */
>> + lvds_clear(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_TWG_EN | PxPLLCR1_SD_EN);
>> +
>> + /* Power up bias and PLL dividers */
>> + lvds_set(plat->base, LVDS_PXDCR(phy), PXDCR_POWER_OK);
>> +
>> + lvds_set(plat->base, LVDS_PXCMCR1(phy), PXCMCR1_CM_EN_DL);
>> + lvds_set(plat->base, LVDS_PXCMCR2(phy), PXCMCR2_CM_EN_DL4);
>> +
>> + lvds_set(plat->base, LVDS_PXPLLCPCR(phy), PXPLLCPCR_CPCTRL_DEFAULT);
>> + lvds_set(plat->base, LVDS_PXBCR3(phy), PXBCR3_VM_EN_DL);
>> + lvds_set(plat->base, LVDS_PXBCR1(phy), PXBCR1_EN_BIAS_DL);
>> + lvds_set(plat->base, LVDS_PXCFGCR(phy), PXCFGCR_EN_DIG_DL);
>> +
>> + /* lvds_pll_enable */
>> + /* PLL lock timing control for the monitor unmask after startup (pll_en) */
>> + /* Adjust the value so that the masking window is opened at start-up */
>> + /* MST_MON_PLL_LOCK_UNMASK_TUNE */
>> + lvds_writel(plat->base, LVDS_PXMPLCR(phy), (0x200 - 0x160) << 16);
>> +
>> + lvds_writel(plat->base, LVDS_PXBCR2(phy), PXBCR2_BIAS_EN);
>> +
>> + lvds_set(plat->base, LVDS_PXGCR(phy),
>> + PXGCR_DP_CLK_OUT | PXGCR_LS_CLK_OUT | PXGCR_BIT_CLK_OUT);
>> +
>> + lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TDIV_EN);
>> + lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_DIVIDERS_EN);
>> + lvds_set(plat->base, LVDS_PXSCR(phy), PXSCR_SER_DATA_OK);
>> +
>> + /* Enable the LVDS PLL & wait for its lock */
>> + lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_EN);
>> + ret = readl_poll_sleep_timeout(plat->base + LVDS_PXPLLSR(phy),
>> + val, val & PXPLLSR_PLL_LOCK, SLEEP_US, TIMEOUT_US);
>> + if (ret)
>> + return ret;
>> +
>> + /* Select MST PHY clock as pixel clock for the LDITX instead of FREF */
>> + /* WCLKCR_SLV_CLKPIX_SEL is for dual link */
>> + lvds_writel(plat->base, LVDS_WCLKCR, WCLKCR_SLV_CLKPIX_SEL);
>> +
>> + lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TEST_CLK_EN);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_lvds_enable(struct udevice *dev)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
>> + struct display_timing timings = priv->timings;
>> + u32 lvds_cdl1cr = 0;
>> + u32 lvds_cdl2cr = 0;
>> + u32 lvds_dmlcr = 0;
>> + u32 lvds_dmmcr = 0;
>> + u32 lvds_cr = 0;
>> + int i;
>> +
>> + lvds_clear(plat->base, LVDS_CDL1CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
>> + | CDLCRX_DISTR3 | CDLCRX_DISTR4);
>> + lvds_clear(plat->base, LVDS_CDL2CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
>> + | CDLCRX_DISTR3 | CDLCRX_DISTR4);
>> +
>> + /* Set channel distribution */
>> + lvds_cr &= ~CR_LKMOD;
>> + lvds_cdl1cr = CDL1CR_DEFAULT;
>> +
>> + if (priv->dual_link) {
>> + lvds_cr |= CR_LKMOD;
>> + lvds_cdl2cr = CDL2CR_DEFAULT;
>> + }
>> +
>> + /* Set signal polarity */
>> + if (timings.flags & DISPLAY_FLAGS_DE_LOW)
>> + lvds_cr |= CR_DEPOL;
>> +
>> + if (timings.flags & DISPLAY_FLAGS_HSYNC_LOW)
>> + lvds_cr |= CR_HSPOL;
>> +
>> + if (timings.flags & DISPLAY_FLAGS_VSYNC_LOW)
>> + lvds_cr |= CR_VSPOL;
>> +
>> + /* Set link phase */
>> + switch (priv->dual_link) {
>> + case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
>> + lvds_cr &= ~CR_LKPHA;
>> + break;
>> + case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
>> + lvds_cr |= CR_LKPHA;
>> + break;
>> + default:
>> + dev_dbg(dev, "No phase precised, setting default\n");
>> + lvds_cr &= ~CR_LKPHA;
>> + break;
>> + }
>> +
>> + /* Set Data Mapping */
>> + switch (priv->bus_format) {
>> + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
>> + for (i = 0; i < 5; i++) {
>> + lvds_dmlcr = ((lvds_bitmap_vesa_rgb888[i][0])
>> + + (lvds_bitmap_vesa_rgb888[i][1] << 5)
>> + + (lvds_bitmap_vesa_rgb888[i][2] << 10)
>> + + (lvds_bitmap_vesa_rgb888[i][3] << 15));
>> + lvds_dmmcr = ((lvds_bitmap_vesa_rgb888[i][4])
>> + + (lvds_bitmap_vesa_rgb888[i][5] << 5)
>> + + (lvds_bitmap_vesa_rgb888[i][6] << 10));
>> +
>> + /* Write registers at the end of computations */
>> + lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
>> + lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
>> + }
>> + break;
>> + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
>> + for (i = 0; i < 5; i++) {
>> + lvds_dmlcr = ((lvds_bitmap_jeida_rgb888[i][0])
>> + + (lvds_bitmap_jeida_rgb888[i][1] << 5)
>> + + (lvds_bitmap_jeida_rgb888[i][2] << 10)
>> + + (lvds_bitmap_jeida_rgb888[i][3] << 15));
>> + lvds_dmmcr = ((lvds_bitmap_jeida_rgb888[i][4])
>> + + (lvds_bitmap_jeida_rgb888[i][5] << 5)
>> + + (lvds_bitmap_jeida_rgb888[i][6] << 10));
>> +
>> + /* Write registers at the end of computations */
>> + lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
>> + lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
>> + }
>> + break;
>> + default:
>> + dev_dbg(dev, "Unsupported LVDS bus format 0x%04x\n", priv->bus_format);
>> + }
>> +
>> + /* Turn the output on */
>> + lvds_cr |= CR_LVDSEN;
>> +
>> + /* Commit config to registers */
>> + lvds_set(plat->base, LVDS_CR, lvds_cr);
>> + lvds_writel(plat->base, LVDS_CDL1CR, lvds_cdl1cr);
>> + lvds_writel(plat->base, LVDS_CDL2CR, lvds_cdl2cr);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_lvds_attach(struct udevice *dev)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
>> + int ret;
>> +
>> + ret = panel_get_display_timing(plat->panel, &priv->timings);
>> + if (ret) {
>> + ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
>> + 0, &priv->timings);
>> + if (ret) {
>> + dev_err(dev, "decode display timing error %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + ret = stm32_lvds_enable(dev);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_lvds_set_backlight(struct udevice *dev, int percent)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + int ret;
>> +
>> + ret = panel_enable_backlight(plat->panel);
>> + if (ret) {
>> + dev_err(dev, "panel %s enable backlight error %d\n",
>> + plat->panel->name, ret);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int lvds_handle_pixel_order(struct stm32_lvds_plat *plat)
>> +{
>> + ofnode parent, panel_port0, panel_port1;
>> + bool even_pixels, odd_pixels;
>> + int port0, port1;
>> +
>> + /*
>> + * In case we are operating in single link,
>> + * there is only one port linked to the LVDS.
>> + * Check whether we are in this case and exit if yes.
>> + */
>> + parent = ofnode_find_subnode(dev_ofnode(plat->panel), "ports");
>> + if (!ofnode_valid(parent))
>> + return 0;
>> +
>> + panel_port0 = ofnode_first_subnode(parent);
>> + if (!ofnode_valid(panel_port0))
>> + return -EPIPE;
>> +
>> + even_pixels = ofnode_read_bool(panel_port0, "dual-lvds-even-pixels");
>> + odd_pixels = ofnode_read_bool(panel_port0, "dual-lvds-odd-pixels");
>> + if (even_pixels && odd_pixels)
>> + return -EINVAL;
>> +
>> + port0 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
>> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
>> +
>> + panel_port1 = ofnode_next_subnode(panel_port0);
>> + if (!ofnode_valid(panel_port1))
>> + return -EPIPE;
>> +
>> + even_pixels = ofnode_read_bool(panel_port1, "dual-lvds-even-pixels");
>> + odd_pixels = ofnode_read_bool(panel_port1, "dual-lvds-odd-pixels");
>> + if (even_pixels && odd_pixels)
>> + return -EINVAL;
>> +
>> + port1 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
>> + LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
>> +
>> + /*
>> + * A valid dual-LVDS bus is found when one port is marked with
>> + * "dual-lvds-even-pixels", and the other port is marked with
>> + * "dual-lvds-odd-pixels", bail out if the markers are not right.
>> + */
>> + if (port0 + port1 != LVDS_DUAL_LINK_EVEN_ODD_PIXELS + LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
>> + return -EINVAL;
>> +
>> + return port0;
>> +}
>> +
>> +static int stm32_lvds_of_to_plat(struct udevice *dev)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
>> + const char *data_mapping;
>> + int ret;
>> +
>> + plat->base = dev_read_addr_ptr(dev);
>> + if ((fdt_addr_t)plat->base == FDT_ADDR_T_NONE) {
>> + dev_err(dev, "Unable to read LVDS base address\n");
>> + return -EINVAL;
>> + }
>> +
>> + ret = clk_get_by_name(dev, "pclk", &plat->pclk);
>> + if (ret) {
>> + dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = clk_get_by_name(dev, "ref", &plat->refclk);
>> + if (ret) {
>> + dev_err(dev, "Unable to get reference clock: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = reset_get_by_index(dev, 0, &plat->rst);
>> + if (ret) {
>> + dev_err(dev, "Failed to get LVDS reset: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = uclass_get_device_by_driver(UCLASS_PANEL,
>> + DM_DRIVER_GET(simple_panel), &plat->panel);
>> + if (ret) {
>> + dev_err(dev, "panel device error %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = panel_get_display_timing(plat->panel, &priv->timings);
>> + if (ret) {
>> + ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
>> + 0, &priv->timings);
>> + if (ret) {
>> + dev_err(dev, "decode display timing error %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + data_mapping = ofnode_read_string(dev_ofnode(plat->panel), "data-mapping");
>> + if (!strcmp(data_mapping, "vesa-24"))
>> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
>> + else if (!strcmp(data_mapping, "jeida-24"))
>> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
>> + else
>> + priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_lvds_probe(struct udevice *dev)
>> +{
>> + struct stm32_lvds_plat *plat = dev_get_plat(dev);
>> + struct stm32_lvds_priv *priv = dev_get_priv(dev);
>> + int ret;
>> +
>> + ret = clk_enable(&plat->pclk);
>> + if (ret) {
>> + dev_err(dev, "Failed to enable peripheral clock: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = clk_enable(&plat->refclk);
>> + if (ret) {
>> + dev_err(dev, "Failed to enable reference clock: %d\n", ret);
>> + goto err_clk;
>> + }
>> +
>> + priv->refclk_rate = (unsigned int)clk_get_rate(&plat->refclk);
>> +
>> + reset_deassert(&plat->rst);
>> +
>> + /* Handle dual link config */
>> + priv->dual_link = lvds_handle_pixel_order(plat);
>> + if (priv->dual_link < 0)
>> + goto err_rst;
>> +
>> + if (priv->dual_link > 0) {
>> + ret = stm32_lvds_pll_enable(dev, LVDS_PHY_SLAVE);
>> + if (ret)
>> + goto err_rst;
>> + }
>> +
>> + ret = stm32_lvds_pll_enable(dev, LVDS_PHY_MASTER);
>> + if (ret)
>> + goto err_rst;
>> +
>> + return 0;
>> +
>> +err_rst:
>> + clk_disable(&plat->refclk);
>> +err_clk:
>> + clk_disable(&plat->pclk);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct video_bridge_ops stm32_lvds_ops = {
>> + .attach = stm32_lvds_attach,
>> + .set_backlight = stm32_lvds_set_backlight,
>> +};
>> +
>> +static const struct udevice_id stm32_lvds_ids[] = {
>> + {.compatible = "st,stm32mp25-lvds"},
>> + {}
>> +};
>> +
>> +U_BOOT_DRIVER(stm32_lvds) = {
>> + .name = "stm32-display-lvds",
>> + .id = UCLASS_VIDEO_BRIDGE,
>> + .of_match = stm32_lvds_ids,
>> + .ops = &stm32_lvds_ops,
>> + .of_to_plat = stm32_lvds_of_to_plat,
>> + .probe = stm32_lvds_probe,
>> + .plat_auto = sizeof(struct stm32_lvds_plat),
>> + .priv_auto = sizeof(struct stm32_lvds_priv),
>> +};
>>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC
2025-10-30 7:43 ` Yannick FERTRE
@ 2025-11-14 16:49 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:49 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
On 10/30/25 08:43, Yannick FERTRE wrote:
> Hi Raphael,
>
> Thanks for the patch.
>
> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>
> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>> STM32MP2 SoCs feature a new version of the LTDC IP. This new version
>> features a bus clock, as well as a 150MHz pad frequency. Add its
>> compatible to the list of device to probe and handle quirks. The new
>> hardware version features a bus clock.
>>
>> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
>> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>> ---
>> drivers/video/stm32/stm32_ltdc.c | 22 ++++++++++++++++++++--
>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
>> index 0a062c8939dbe49b11aa50f5ca9701bdbe5c5b0b..efe9a00996eca0301d2a2b82074ba9690a967a73 100644
>> --- a/drivers/video/stm32/stm32_ltdc.c
>> +++ b/drivers/video/stm32/stm32_ltdc.c
>> @@ -262,6 +262,7 @@ static const u32 layer_regs_a2[] = {
>> #define HWVER_10300 0x010300
>> #define HWVER_20101 0x020101
>> #define HWVER_40100 0x040100
>> +#define HWVER_40101 0x040101
>> enum stm32_ltdc_pix_fmt {
>> PF_ARGB8888 = 0, /* ARGB [32 bits] */
>> @@ -529,7 +530,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> struct udevice *bridge = NULL;
>> struct udevice *panel = NULL;
>> struct display_timing timings;
>> - struct clk pclk;
>> + struct clk pclk, bclk;
>> struct reset_ctl rst;
>> ulong rate;
>> int ret;
>> @@ -540,7 +541,21 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> return -EINVAL;
>> }
>> - ret = clk_get_by_index(dev, 0, &pclk);
>> + ret = clk_get_by_name(dev, "bus", &bclk);
>> + if (ret) {
>> + if (ret != -ENODATA) {
>> + dev_err(dev, "bus clock get error %d\n", ret);
>> + return ret;
>> + }
>> + } else {
>> + ret = clk_enable(&bclk);
>> + if (ret) {
>> + dev_err(dev, "bus clock enable error %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + ret = clk_get_by_name(dev, "lcd", &pclk);
>> if (ret) {
>> dev_err(dev, "peripheral clock get error %d\n", ret);
>> return ret;
>> @@ -566,6 +581,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> priv->pix_fmt_hw = pix_fmt_a1;
>> break;
>> case HWVER_40100:
>> + case HWVER_40101:
>> priv->layer_regs = layer_regs_a2;
>> priv->pix_fmt_hw = pix_fmt_a2;
>> break;
>> @@ -688,6 +704,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
>> static const struct udevice_id stm32_ltdc_ids[] = {
>> { .compatible = "st,stm32-ltdc" },
>> + { .compatible = "st,stm32mp251-ltdc" },
>> + { .compatible = "st,stm32mp255-ltdc" },
>> { }
>> };
>>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel
2025-10-30 7:43 ` Yannick FERTRE
@ 2025-11-14 16:49 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:49 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
On 10/30/25 08:43, Yannick FERTRE wrote:
> Hi Raphael,
>
> Thanks for the patch.
>
> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>
> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>> Initially there was only one DSI bridge with one panel attached to this
>> device. This explained the call to uclass_first_device_err(UCLASS_PANEL,
>> ...) which worked fine at the time.
>>
>> Now that multiple bridges and panels, with different technologies, can
>> be plugged onto the board this way to get the panel device is outdated.
>>
>> The lookup is done is two steps. First we circle through the
>> UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
>> endpoints until we get a UCLASS_PANEL device available.
>>
>> Acked-by: Yannick Fertre <yannick.fertre@foss.st.com>
>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>> ---
>> drivers/video/stm32/stm32_ltdc.c | 136 +++++++++++++++++++++++++++++++++++----
>> 1 file changed, 125 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
>> index efe9a00996eca0301d2a2b82074ba9690a967a73..834bfb625d2d34a44bd8edff1c92af6dec344c20 100644
>> --- a/drivers/video/stm32/stm32_ltdc.c
>> +++ b/drivers/video/stm32/stm32_ltdc.c
>> @@ -17,6 +17,7 @@
>> #include <video_bridge.h>
>> #include <asm/io.h>
>> #include <dm/device-internal.h>
>> +#include <dm/uclass-internal.h>
>> #include <dm/device_compat.h>
>> #include <linux/bitops.h>
>> #include <linux/printk.h>
>> @@ -495,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr)
>> setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN);
>> }
>> +static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node,
>> + enum uclass_id id, struct udevice **remote_dev)
>> +{
>> + u32 remote_phandle;
>> + ofnode remote;
>> + int ret = 0;
>> +
>> + ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
>> + if (ret) {
>> + dev_err(dev, "%s(%s): Could not find remote-endpoint property\n",
>> + __func__, dev_read_name(dev));
>> + return ret;
>> + }
>> +
>> + remote = ofnode_get_by_phandle(remote_phandle);
>> + if (!ofnode_valid(remote))
>> + return -EINVAL;
>> +
>> + while (ofnode_valid(remote)) {
>> + remote = ofnode_get_parent(remote);
>> + if (!ofnode_valid(remote)) {
>> + dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n",
>> + __func__, dev_read_name(dev), id);
>> + continue;
>> + }
>> +
>> + ret = uclass_find_device_by_ofnode(id, remote, remote_dev);
>> + if (*remote_dev && !ret) {
>> + ret = uclass_get_device_by_ofnode(id, remote, remote_dev);
>> + if (ret)
>> + dev_dbg(dev, "%s(%s): failed to get remote device %s\n",
>> + __func__, dev_read_name(dev), dev_read_name(*remote_dev));
>> + break;
>> + }
>> + };
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel)
>> +{
>> + ofnode ep_node, node, ports;
>> + int ret = 0;
>> +
>> + if (!dev)
>> + return -EINVAL;
>> +
>> + ports = ofnode_find_subnode(dev_ofnode(dev), "ports");
>> + if (!ofnode_valid(ports)) {
>> + dev_err(dev, "Remote bridge subnode\n");
>> + return ret;
>> + }
>> +
>> + for (node = ofnode_first_subnode(ports);
>> + ofnode_valid(node);
>> + node = dev_read_next_subnode(node)) {
>> + ep_node = ofnode_first_subnode(node);
>> + if (!ofnode_valid(ep_node))
>> + continue;
>> +
>> + ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel);
>> + }
>> +
>> + /* Sanity check, we can get out of the loop without having a clean ofnode */
>> + if (!(*panel))
>> + ret = -EINVAL;
>> + else
>> + if (!ofnode_valid(dev_ofnode(*panel)))
>> + ret = -EINVAL;
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node,
>> + struct udevice **panel, struct udevice **bridge)
>> +{
>> + int ret;
>> +
>> + if (*panel)
>> + return -EINVAL;
>> +
>> + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
>> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge);
>> + if (ret)
>> + return ret;
>> +
>> + ret = stm32_ltdc_get_panel(*bridge, panel);
>> + } else {
>> + /* no bridge, search a panel from display controller node */
>> + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> #if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY)
>> static int stm32_ltdc_alloc_fb(struct udevice *dev)
>> {
>> @@ -532,6 +628,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> struct display_timing timings;
>> struct clk pclk, bclk;
>> struct reset_ctl rst;
>> + ofnode node, port;
>> ulong rate;
>> int ret;
>> @@ -568,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> }
>> priv->hw_version = readl(priv->regs + LTDC_IDR);
>> - debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
>> + dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
>> switch (priv->hw_version) {
>> case HWVER_10200:
>> @@ -589,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> return -ENODEV;
>> }
>> - ret = uclass_first_device_err(UCLASS_PANEL, &panel);
>> - if (ret) {
>> - if (ret != -ENODEV)
>> - dev_err(dev, "panel device error %d\n", ret);
>> - return ret;
>> + /*
>> + * Try all the ports until one working.
>> + *
>> + * This is done in two times. First is checks for the
>> + * UCLASS_VIDEO_BRIDGE available, and then for this bridge
>> + * it scans for a UCLASS_PANEL.
>> + */
>> +
>> + port = dev_read_subnode(dev, "port");
>> + if (!ofnode_valid(port)) {
>> + dev_err(dev, "%s(%s): 'port' subnode not found\n",
>> + __func__, dev_read_name(dev));
>> + return -EINVAL;
>> }
>> + for (node = ofnode_first_subnode(port);
>> + ofnode_valid(node);
>> + node = dev_read_next_subnode(node)) {
>> + ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge);
>> + if (ret)
>> + dev_dbg(dev, "Device failed ret=%d\n", ret);
>> + else
>> + break;
>> + }
>> +
>> + /* Sanity check */
>> + if (ret)
>> + return ret;
>> +
>> ret = panel_get_display_timing(panel, &timings);
>> if (ret) {
>> ret = ofnode_decode_display_timing(dev_ofnode(panel),
>> @@ -624,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev)
>> reset_deassert(&rst);
>> if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
>> - ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
>> - if (ret)
>> - dev_dbg(dev,
>> - "No video bridge, or no backlight on bridge\n");
>> -
>> if (bridge) {
>> ret = video_bridge_attach(bridge);
>> if (ret) {
>>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-10-30 7:43 ` Yannick FERTRE
@ 2025-11-14 16:49 ` Patrice CHOTARD
2025-11-14 16:54 ` Patrice CHOTARD
0 siblings, 1 reply; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:49 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
On 10/30/25 08:43, Yannick FERTRE wrote:
> Hi Raphael,
>
> Thanks for the patch.
>
> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>
> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>> Use LTDC and LVDS nodes in all boot phases. This is specially useful
>> before U-Boot relocation.
>>
>> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
>> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>> ---
>> arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
>> 1 file changed, 8 insertions(+)
>>
>> diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
>> index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
>> --- a/arch/arm/dts/stm32mp25-u-boot.dtsi
>> +++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
>> @@ -93,6 +93,14 @@
>> bootph-all;
>> };
>> +<dc {
>> + bootph-all;
>> +};
>> +
>> +&lvds {
>> + bootph-all;
>> +};
>> +
>> &pinctrl {
>> bootph-all;
>> };
>>
Applied to u-boot-stm32/master
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-11-14 16:49 ` Patrice CHOTARD
@ 2025-11-14 16:54 ` Patrice CHOTARD
2025-11-14 17:30 ` [Uboot-stm32] " Patrice CHOTARD
0 siblings, 1 reply; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 16:54 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: u-boot, uboot-stm32
On 11/14/25 17:49, Patrice CHOTARD wrote:
>
>
> On 10/30/25 08:43, Yannick FERTRE wrote:
>> Hi Raphael,
>>
>> Thanks for the patch.
>>
>> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>>
>> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>>> Use LTDC and LVDS nodes in all boot phases. This is specially useful
>>> before U-Boot relocation.
>>>
>>> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
>>> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
>>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>>> ---
>>> arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
>>> 1 file changed, 8 insertions(+)
>>>
>>> diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
>>> index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
>>> --- a/arch/arm/dts/stm32mp25-u-boot.dtsi
>>> +++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
>>> @@ -93,6 +93,14 @@
>>> bootph-all;
>>> };
>>> +<dc {
>>> + bootph-all;
>>> +};
>>> +
>>> +&lvds {
>>> + bootph-all;
>>> +};
>>> +
>>> &pinctrl {
>>> bootph-all;
>>> };
>>>
> Applied to u-boot-stm32/master
>
> Thanks
> Patrice
This patch will be dropped and submitted in kernel DT side instead.
Thanks
Patrice
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [Uboot-stm32] [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot
2025-11-14 16:54 ` Patrice CHOTARD
@ 2025-11-14 17:30 ` Patrice CHOTARD
0 siblings, 0 replies; 39+ messages in thread
From: Patrice CHOTARD @ 2025-11-14 17:30 UTC (permalink / raw)
To: Yannick FERTRE, Raphael Gallais-Pou, Tom Rini, Kamil Lulko,
Dillon Min, Patrick Delaunay, Anatolij Gustschin, Simon Glass,
Sumit Garg, Philippe Cornu
Cc: uboot-stm32, u-boot
On 11/14/25 17:54, Patrice CHOTARD wrote:
>
>
> On 11/14/25 17:49, Patrice CHOTARD wrote:
>>
>>
>> On 10/30/25 08:43, Yannick FERTRE wrote:
>>> Hi Raphael,
>>>
>>> Thanks for the patch.
>>>
>>> Acked-by: Yannick Fertre<yannick.fertre@foss.st.com>
>>>
>>> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>>>> Use LTDC and LVDS nodes in all boot phases. This is specially useful
>>>> before U-Boot relocation.
>>>>
>>>> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
>>>> Reviewed-by: Yannick Fertre <yannick.fertre@foss.st.com>
>>>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
>>>> ---
>>>> arch/arm/dts/stm32mp25-u-boot.dtsi | 8 ++++++++
>>>> 1 file changed, 8 insertions(+)
>>>>
>>>> diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
>>>> index d9aeeb6d510df9dea9700148cf8a8ad5efcfd4f9..3ac35c4a6bc27c72eeeb532742fd4412b7aa1b85 100644
>>>> --- a/arch/arm/dts/stm32mp25-u-boot.dtsi
>>>> +++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
>>>> @@ -93,6 +93,14 @@
>>>> bootph-all;
>>>> };
>>>> +<dc {
>>>> + bootph-all;
>>>> +};
>>>> +
>>>> +&lvds {
>>>> + bootph-all;
>>>> +};
>>>> +
>>>> &pinctrl {
>>>> bootph-all;
>>>> };
>>>>
>> Applied to u-boot-stm32/master
>>
>> Thanks
>> Patrice
>
> This patch will be dropped and submitted in kernel DT side instead.
see https://patchwork.kernel.org/project/linux-arm-kernel/list/?series=1023673
Patrice
>
> Thanks
> Patrice
>
> _______________________________________________
> Uboot-stm32 mailing list
> Uboot-stm32@st-md-mailman.stormreply.com
> https://st-md-mailman.stormreply.com/mailman/listinfo/uboot-stm32
^ permalink raw reply [flat|nested] 39+ messages in thread
end of thread, other threads:[~2025-11-14 17:30 UTC | newest]
Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-04 12:53 [PATCH v3 0/7] Add display support for STM32MP25 Raphael Gallais-Pou
2025-09-04 12:53 ` [PATCH v3 1/7] ofnode: support panel-timings in ofnode_decode_display_timing Raphael Gallais-Pou
2025-10-30 7:35 ` Yannick FERTRE
2025-11-01 9:03 ` Simon Glass
2025-11-02 1:09 ` [Uboot-stm32] " Raphaël Gallais-Pou
2025-11-02 19:53 ` Simon Glass
2025-11-03 14:17 ` Tom Rini
2025-11-04 14:01 ` Raphael Gallais-Pou
2025-11-04 16:21 ` Tom Rini
2025-11-14 16:48 ` Patrice CHOTARD
2025-11-04 16:31 ` Simon Glass
2025-11-04 16:55 ` Tom Rini
2025-11-07 12:23 ` Simon Glass
2025-09-04 12:53 ` [PATCH v3 2/7] video: simple_panel: add support for "panel-lvds" display Raphael Gallais-Pou
2025-10-30 7:41 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 3/7] video: stm32: STM32 driver support for LVDS Raphael Gallais-Pou
2025-10-30 7:42 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 4/7] video: stm32: ltdc: support new hardware version for STM32MP25 SoC Raphael Gallais-Pou
2025-09-12 16:41 ` Patrice CHOTARD
2025-09-15 13:05 ` Raphael Gallais-Pou
2025-09-17 15:10 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 6/7] ARM: dts: stm32: use LTDC and LVDS nodes before relocation in stm32mp25-u-boot Raphael Gallais-Pou
2025-09-12 16:45 ` Patrice CHOTARD
2025-10-30 7:43 ` Yannick FERTRE
2025-11-14 16:49 ` Patrice CHOTARD
2025-11-14 16:54 ` Patrice CHOTARD
2025-11-14 17:30 ` [Uboot-stm32] " Patrice CHOTARD
2025-09-04 12:53 ` [PATCH v3 7/7] configs: stm32mp25: enable LVDS display support Raphael Gallais-Pou
2025-09-12 16:46 ` Patrice CHOTARD
2025-10-30 7:44 ` Yannick FERTRE
2025-11-14 16:48 ` Patrice CHOTARD
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.