public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH v6 00/12] Rockchip VOP2 support
@ 2025-11-08  5:37 Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 01/12] video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE Dang Huynh via B4 Relay
                   ` (12 more replies)
  0 siblings, 13 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:37 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

This series adds VOP2 support to U-Boot and enable it for PineTab2.

HDMI should work flawlessly but there's still some timings issue with
DW MIPI DSI, perhaps that's something to look into later.

Tested-by: Piotr Zalewski <pZ010001011111@proton.me>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
Changes in v6:
- Switch to mainlining.org email
- Link to v5: https://lore.kernel.org/r/20250607-vop2-pt2-v5-0-1d52f7a2cf3c@riseup.net

Changes in v5:
- Add check for invalid win_id value
- Link to v4: https://lore.kernel.org/r/20250505-vop2-pt2-v4-0-3b7c3ab1a617@riseup.net

Changes in v4:
- Remove unnecessary clk_enable() as VOP clocks are enabled by TF-A
- Adjust I2C clock configuration to match 24 MHz clock
- Reorganize VOP node order
- Link to v3: https://lore.kernel.org/r/20250412-vop2-pt2-v3-0-7c796db335e9@riseup.net

Changes in v3:
- Renaming prefix from "rkvop" to "rkvop2" and similar
- Win planes are platform-configurable
- Link to v2: https://lore.kernel.org/r/20250223-vop2-pt2-v2-0-71350e482970@riseup.net

Changes in v2:
- Get display timings from video bridge (requires [1])
- Disable esmart and post before booting to OS
- Enable avdd-0v9 and avdd-1v8 when probing HDMI.
- Link to v1: https://lore.kernel.org/r/20250116-vop2-pt2-v1-0-c9d646b53930@riseup.net

---
Dang Huynh (10):
      video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE
      video: rockchip: dw-mipi-dsi: Add get_display_timing support
      video: Add BOE TH101MB31IG002-28A MIPI-DSI panel
      video: rockchip: Add VOP2 support
      video: rockchip: vop2: Add video bridge support
      arm: dts: rockchip: rk356x: Prerelocate VOP in U-Boot proper
      configs: quartz64: Enable vidconsole
      video: rockchip: Add HDMI support for RK3568
      configs: pinetab2-rk3566: Enable video and USB keyboard
      clk: rockchip: rk3568: Use assigned VPLL clock when possible

Ondrej Jirman (2):
      video: rockchip: dw_mipi_dsi: Improve pixel clock calculations
      video: rockchip: dw_mipi_dsi: Proceed when external PHY is not defined

 arch/arm/dts/rk356x-u-boot.dtsi                 |   4 +
 arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 ++++++++++++
 configs/pinetab2-rk3566_defconfig               |  10 +
 drivers/clk/rockchip/clk_rk3568.c               |   6 +-
 drivers/video/Kconfig                           |  10 +
 drivers/video/Makefile                          |   1 +
 drivers/video/boe-th101mb31ig002-28a.c          | 236 ++++++++++
 drivers/video/rockchip/Kconfig                  |   2 +-
 drivers/video/rockchip/Makefile                 |   4 +-
 drivers/video/rockchip/dw_mipi_dsi_rockchip.c   |  45 +-
 drivers/video/rockchip/rk3568_hdmi.c            |  71 +++
 drivers/video/rockchip/rk3568_vop.c             | 260 +++++++++++
 drivers/video/rockchip/rk_vop2.c                | 557 ++++++++++++++++++++++++
 drivers/video/rockchip/rk_vop2.h                |  76 ++++
 include/configs/quartz64_rk3566.h               |   5 +-
 15 files changed, 1544 insertions(+), 23 deletions(-)
---
base-commit: 928af44314a1a086e946ef3c0901d40bdb3e19a9
change-id: 20250114-vop2-pt2-755fb1991bb6

Best regards,
-- 
Dang Huynh <dang.huynh@mainlining.org>



^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v6 01/12] video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
@ 2025-11-08  5:37 ` Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 02/12] video: rockchip: dw_mipi_dsi: Improve pixel clock calculations Dang Huynh via B4 Relay
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:37 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The driver is in video bridge class, so we must depend on it or
the driver will fail to init.

Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig
index 0f4550a29e3..dfd08f1e5d1 100644
--- a/drivers/video/rockchip/Kconfig
+++ b/drivers/video/rockchip/Kconfig
@@ -72,7 +72,7 @@ config DISPLAY_ROCKCHIP_MIPI
 
 config DISPLAY_ROCKCHIP_DW_MIPI
 	bool "Rockchip Designware MIPI"
-	depends on VIDEO_ROCKCHIP
+	depends on VIDEO_ROCKCHIP && VIDEO_BRIDGE
 	select VIDEO_DW_MIPI_DSI
 	help
 	  Select the Designware MIPI DSI controller in use on some Rockchip

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 02/12] video: rockchip: dw_mipi_dsi: Improve pixel clock calculations
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 01/12] video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE Dang Huynh via B4 Relay
@ 2025-11-08  5:37 ` Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 03/12] video: rockchip: dw_mipi_dsi: Proceed when external PHY is not defined Dang Huynh via B4 Relay
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:37 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Ondrej Jirman <megi@xff.cz>

Calculate burst mode overhead in one place for both internal
and external PHY use case and exit if out of range, instead
of ignoring the wrong value.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/dw_mipi_dsi_rockchip.c | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
index fa512173510..c47992dfb13 100644
--- a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
+++ b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
@@ -526,8 +526,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct display_timing *timings,
 	struct udevice *dev = device->dev;
 	struct dw_rockchip_dsi_priv *dsi = dev_get_priv(dev);
 	int bpp;
-	unsigned long mpclk, tmp;
-	unsigned int target_mbps = 1000;
 	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
 	unsigned long best_freq = 0;
 	unsigned long fvco_min, fvco_max, fin, fout;
@@ -544,30 +542,28 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct display_timing *timings,
 		return bpp;
 	}
 
-	mpclk = DIV_ROUND_UP(timings->pixelclock.typ, 1000);
-	if (mpclk) {
-		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
-		tmp = (mpclk * (bpp / lanes) * 10 / 8) / 1000;
-		if (tmp < max_mbps)
-			target_mbps = tmp;
-		else
-			dev_err(dsi->dsi_host,
-				"DPHY clock frequency is out of range\n");
+	fout = timings->pixelclock.typ / MSEC_PER_SEC * bpp / lanes;
+	if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+		fout = fout * 12 / 10;
+	fout *= MSEC_PER_SEC;
+
+	if (fout > max_mbps * USEC_PER_SEC) {
+		dev_err(dsi->dsi_host, "DPHY clock frequency is out of range\n");
+		return -EINVAL;
 	}
 
 	/* for external phy only the mipi_dphy_config is necessary */
 	if (generic_phy_valid(&dsi->phy)) {
-		phy_mipi_dphy_get_default_config(timings->pixelclock.typ  * 10 / 8,
+		phy_mipi_dphy_get_default_config(fout / bpp * lanes,
 						 bpp, lanes,
 						 &dsi->phy_opts);
-		dsi->lane_mbps = target_mbps;
+		dsi->lane_mbps = DIV_ROUND_UP(fout, USEC_PER_SEC);
 		*lane_mbps = dsi->lane_mbps;
 
 		return 0;
 	}
 
 	fin = clk_get_rate(dsi->ref);
-	fout = target_mbps * USEC_PER_SEC;
 
 	/* constraint: 5Mhz <= Fref / N <= 40MHz */
 	min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 03/12] video: rockchip: dw_mipi_dsi: Proceed when external PHY is not defined
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 01/12] video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE Dang Huynh via B4 Relay
  2025-11-08  5:37 ` [PATCH v6 02/12] video: rockchip: dw_mipi_dsi: Improve pixel clock calculations Dang Huynh via B4 Relay
@ 2025-11-08  5:37 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 04/12] video: rockchip: dw-mipi-dsi: Add get_display_timing support Dang Huynh via B4 Relay
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:37 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Ondrej Jirman <megi@xff.cz>

In this case the DM returns ENOENT, not ENODATA.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/dw_mipi_dsi_rockchip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
index c47992dfb13..95e825eb3d6 100644
--- a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
+++ b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
@@ -839,7 +839,7 @@ static int dw_mipi_dsi_rockchip_probe(struct udevice *dev)
 	 * NULL if it's not initialized.
 	 */
 	ret = generic_phy_get_by_name(dev, "dphy", &priv->phy);
-	if (ret && ret != -ENODATA) {
+	if (ret && ret != -ENOENT) {
 		dev_err(dev, "failed to get mipi dphy: %d\n", ret);
 		return ret;
 	}

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 04/12] video: rockchip: dw-mipi-dsi: Add get_display_timing support
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (2 preceding siblings ...)
  2025-11-08  5:37 ` [PATCH v6 03/12] video: rockchip: dw_mipi_dsi: Proceed when external PHY is not defined Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 05/12] video: Add BOE TH101MB31IG002-28A MIPI-DSI panel Dang Huynh via B4 Relay
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

This allows video drivers to obtain display timings from the video
bridge.

Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/dw_mipi_dsi_rockchip.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
index 95e825eb3d6..d21ea7953a6 100644
--- a/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
+++ b/drivers/video/rockchip/dw_mipi_dsi_rockchip.c
@@ -224,6 +224,7 @@ struct dw_rockchip_dsi_priv {
 	struct mipi_dsi_device device;
 	void __iomem *base;
 	struct udevice *panel;
+	struct display_timing timings;
 	void __iomem *grf;
 
 	/* Optional external dphy */
@@ -709,7 +710,7 @@ static int dw_mipi_dsi_rockchip_attach(struct udevice *dev)
 	struct dw_rockchip_dsi_priv *priv = dev_get_priv(dev);
 	struct mipi_dsi_device *device = &priv->device;
 	struct mipi_dsi_panel_plat *mplat;
-	struct display_timing timings;
+	struct display_timing *timings = &priv->timings;
 	int ret;
 
 	ret = uclass_first_device_err(UCLASS_PANEL, &priv->panel);
@@ -724,10 +725,10 @@ static int dw_mipi_dsi_rockchip_attach(struct udevice *dev)
 	device->format = mplat->format;
 	device->mode_flags = mplat->mode_flags;
 
-	ret = panel_get_display_timing(priv->panel, &timings);
+	ret = panel_get_display_timing(priv->panel, timings);
 	if (ret) {
 		ret = ofnode_decode_display_timing(dev_ofnode(priv->panel),
-						   0, &timings);
+						   0, timings);
 		if (ret) {
 			dev_err(dev, "decode display timing error %d\n", ret);
 			return ret;
@@ -740,7 +741,7 @@ static int dw_mipi_dsi_rockchip_attach(struct udevice *dev)
 		return ret;
 	}
 
-	ret = dsi_host_init(priv->dsi_host, device, &timings, 4,
+	ret = dsi_host_init(priv->dsi_host, device, timings, 4,
 			    &dsi_rockchip_phy_ops);
 	if (ret) {
 		dev_err(dev, "failed to initialize mipi dsi host\n");
@@ -902,9 +903,19 @@ static int dw_mipi_dsi_rockchip_probe(struct udevice *dev)
 	return 0;
 }
 
+static int dw_mipi_dsi_rockchip_get_dt(struct udevice *dev,
+				       struct display_timing *timings)
+{
+	struct dw_rockchip_dsi_priv *priv = dev_get_priv(dev);
+
+	memcpy(timings, &priv->timings, sizeof(*timings));
+	return 0;
+}
+
 struct video_bridge_ops dw_mipi_dsi_rockchip_ops = {
 	.attach = dw_mipi_dsi_rockchip_attach,
 	.set_backlight = dw_mipi_dsi_rockchip_set_bl,
+	.get_display_timing = dw_mipi_dsi_rockchip_get_dt,
 };
 
 static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 05/12] video: Add BOE TH101MB31IG002-28A MIPI-DSI panel
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (3 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 04/12] video: rockchip: dw-mipi-dsi: Add get_display_timing support Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 06/12] video: rockchip: Add VOP2 support Dang Huynh via B4 Relay
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

BOE TH101MB31IG002-28A is a MIPI-DSI panel used in the Pine64
PineTab2.

Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/Kconfig                  |  10 ++
 drivers/video/Makefile                 |   1 +
 drivers/video/boe-th101mb31ig002-28a.c | 236 +++++++++++++++++++++++++++++++++
 3 files changed, 247 insertions(+)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b5777da5218..aeb3c56ec09 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -530,6 +530,16 @@ config VIDEO_BCM2835
 	  that same resolution (or as near as possible) and 32bpp depth, so
 	  that U-Boot can access it with full colour depth.
 
+config VIDEO_LCD_BOE_TH101MB31IG002_28A
+	bool "BOE TH101MB31IG002-28A DSI LCD panel support"
+	depends on PANEL && BACKLIGHT
+	select VIDEO_MIPI_DSI
+	help
+	  Say Y here if you want to enable support for BOE TH101MB31IG002-28A
+	  panel.
+
+	  This panel has a 800x1280 resolution and uses 24 bit RGB per pixel.
+
 config VIDEO_LCD_ENDEAVORU
 	tristate "Endeavoru 720x1280 DSI video mode panel"
 	depends on PANEL && BACKLIGHT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 96c7ce7bb09..e5c3b46b1fd 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_VIDEO_EFI) += efi.o
 obj-y += imx/
 obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
 obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
+obj-$(CONFIG_VIDEO_LCD_BOE_TH101MB31IG002_28A) += boe-th101mb31ig002-28a.o
 obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
 obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX10D07VM0BAA) += hitachi-tx10d07vm0baa.o
diff --git a/drivers/video/boe-th101mb31ig002-28a.c b/drivers/video/boe-th101mb31ig002-28a.c
new file mode 100644
index 00000000000..2c377a31669
--- /dev/null
+++ b/drivers/video/boe-th101mb31ig002-28a.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com>
+ * Copyright (c) 2023 Manuel Traut <manut@mecka.net>
+ * Copyright (c) 2023 - 2025 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <panel.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct th101mb31ig002_28a_panel_priv {
+	struct udevice *backlight;
+	struct udevice *reg_power;
+	struct gpio_desc enable;
+	struct gpio_desc reset;
+};
+
+static const struct display_timing boe_th101mb31ig002_default_timing = {
+	.pixelclock.typ		= 73500000,
+	.hactive.typ		= 800,
+	.hfront_porch.typ	= 64,
+	.hback_porch.typ	= 64,
+	.hsync_len.typ		= 16,
+	.vactive.typ		= 1280,
+	.vfront_porch.typ	= 2,
+	.vback_porch.typ	= 12,
+	.vsync_len.typ		= 4,
+};
+
+#define dsi_dcs_write_seq(device, seq...) do {					\
+		static const u8 d[] = { seq };					\
+		int ret;							\
+		ret = mipi_dsi_dcs_write_buffer(device, d, ARRAY_SIZE(d));	\
+		if (ret < 0)							\
+			return ret;						\
+	} while (0)
+
+static int th101mb31ig002_28a_init_sequence(struct udevice *dev)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+	struct mipi_dsi_device *device = plat->device;
+	int ret;
+
+	dsi_dcs_write_seq(device, 0xE0, 0xAB, 0xBA);
+	dsi_dcs_write_seq(device, 0xE1, 0xBA, 0xAB);
+	dsi_dcs_write_seq(device, 0xB1, 0x10, 0x01, 0x47, 0xFF);
+	dsi_dcs_write_seq(device, 0xB2, 0x0C, 0x14, 0x04, 0x50, 0x50, 0x14);
+	dsi_dcs_write_seq(device, 0xB3, 0x56, 0x53, 0x00);
+	dsi_dcs_write_seq(device, 0xB4, 0x33, 0x30, 0x04);
+	dsi_dcs_write_seq(device, 0xB6, 0xB0, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00);
+	dsi_dcs_write_seq(device, 0xB8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00, 0x00);
+	dsi_dcs_write_seq(device, 0xB9, 0x7C, 0x65, 0x55, 0x49, 0x46, 0x36, 0x3B, 0x24, 0x3D,
+			  0x3C, 0x3D, 0x5C, 0x4C, 0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7C, 0x65,
+			  0x55, 0x49, 0x46, 0x36, 0x3B, 0x24, 0x3D, 0x3C, 0x3D, 0x5C, 0x4C, 0x55,
+			  0x47, 0x46, 0x39, 0x26, 0x06);
+	dsi_dcs_write_seq(device, 0xC0, 0xFF, 0x87, 0x12, 0x34, 0x44, 0x44, 0x44,
+			  0x44, 0x98, 0x04, 0x98, 0x04, 0x0F, 0x00, 0x00, 0xC1);
+	dsi_dcs_write_seq(device, 0xC1, 0x54, 0x94, 0x02, 0x85, 0x9F, 0x00, 0x7F, 0x00, 0x54,
+			  0x00);
+	dsi_dcs_write_seq(device, 0xC2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11, 0x22, 0x20, 0x44,
+			  0xFF, 0x18, 0x00);
+	dsi_dcs_write_seq(device, 0xC3, 0x86, 0x46, 0x05, 0x05, 0x1C, 0x1C, 0x1D, 0x1D, 0x02,
+			  0x1F, 0x1F, 0x1E, 0x1E, 0x0F, 0x0F, 0x0D, 0x0D, 0x13, 0x13, 0x11, 0x11,
+			  0x00);
+	dsi_dcs_write_seq(device, 0xC4, 0x07, 0x07, 0x04, 0x04, 0x1C, 0x1C, 0x1D, 0x1D, 0x02,
+			  0x1F, 0x1F, 0x1E, 0x1E, 0x0E, 0x0E, 0x0C, 0x0C, 0x12, 0x12, 0x10, 0x10,
+			  0x00);
+	dsi_dcs_write_seq(device, 0xC6, 0x2A, 0x2A);
+	dsi_dcs_write_seq(device, 0xC8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
+	dsi_dcs_write_seq(device, 0xCA, 0xCB, 0x43);
+	dsi_dcs_write_seq(device, 0xCD, 0x0E, 0x4B, 0x4B, 0x20, 0x19, 0x6B, 0x06, 0xB3);
+	dsi_dcs_write_seq(device, 0xD2, 0xE3, 0x2B, 0x38, 0x00);
+	dsi_dcs_write_seq(device, 0xD4, 0x00, 0x01, 0x00, 0x0E, 0x04, 0x44, 0x08, 0x10, 0x00,
+			  0x00, 0x00);
+	dsi_dcs_write_seq(device, 0xE6, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
+	dsi_dcs_write_seq(device, 0xF0, 0x12, 0x03, 0x20, 0x00, 0xFF);
+	dsi_dcs_write_seq(device, 0xF3, 0x00);
+
+	ret = mipi_dsi_dcs_exit_sleep_mode(device);
+	if (ret)
+		return ret;
+
+	mdelay(120);
+
+	ret = mipi_dsi_dcs_set_display_on(device);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int th101mb31ig002_28a_panel_enable_backlight(struct udevice *dev)
+{
+	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+	struct mipi_dsi_device *device = plat->device;
+	struct th101mb31ig002_28a_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = mipi_dsi_attach(device);
+	if (ret < 0) {
+		dev_err(dev, "Failed to attach display: %d\n", ret);
+		return ret;
+	}
+
+	ret = th101mb31ig002_28a_init_sequence(dev);
+	if (ret) {
+		dev_err(dev, "Failed to init display: %d\n", ret);
+		return ret;
+	}
+
+	if (priv->backlight) {
+		ret = backlight_enable(priv->backlight);
+		if (ret) {
+			dev_err(dev, "Failed to enable backlight: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int th101mb31ig002_28a_panel_set_backlight(struct udevice *dev, int percent)
+{
+	struct th101mb31ig002_28a_panel_priv *priv = dev_get_priv(dev);
+
+	return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int th101mb31ig002_28a_panel_get_display_timing(struct udevice *dev,
+						       struct display_timing *timings)
+{
+	memcpy(timings, &boe_th101mb31ig002_default_timing, sizeof(*timings));
+
+	return 0;
+}
+
+static int th101mb31ig002_28a_panel_of_to_plat(struct udevice *dev)
+{
+	struct th101mb31ig002_28a_panel_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+		ret =  device_get_supply_regulator(dev, "power-supply",
+						   &priv->reg_power);
+		if (ret && ret != -ENOENT) {
+			dev_err(dev, "Warning: cannot get power supply\n");
+			return ret;
+		}
+	}
+
+	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+					   "backlight", &priv->backlight);
+	if (ret)
+		dev_warn(dev, "failed to get backlight\n");
+
+	ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
+				   GPIOD_IS_OUT);
+	if (ret) {
+		dev_err(dev, "Failed to get enable GPIO (%d)\n", ret);
+		if (ret != -ENOENT)
+			return ret;
+	}
+
+	ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
+				   GPIOD_IS_OUT);
+	if (ret) {
+		dev_err(dev, "Failed to get reset GPIO (%d)\n", ret);
+		if (ret != -ENOENT)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int th101mb31ig002_28a_panel_probe(struct udevice *dev)
+{
+	struct th101mb31ig002_28a_panel_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+	int ret;
+
+	if (CONFIG_IS_ENABLED(DM_REGULATOR) && priv->reg_power) {
+		ret = regulator_set_enable(priv->reg_power, true);
+		if (ret)
+			return ret;
+	}
+
+	/* enable panel */
+	dm_gpio_set_value(&priv->enable, 1);
+	mdelay(50);
+
+	/* reset panel */
+	dm_gpio_set_value(&priv->reset, 0);
+	udelay(10);
+	dm_gpio_set_value(&priv->reset, 1);
+	udelay(10);
+	dm_gpio_set_value(&priv->reset, 0);
+	mdelay(5);
+
+	plat->lanes = 4;
+	plat->format = MIPI_DSI_FMT_RGB888;
+	plat->mode_flags = MIPI_DSI_MODE_VIDEO |
+			   MIPI_DSI_MODE_VIDEO_BURST |
+			   MIPI_DSI_MODE_EOT_PACKET |
+			   MIPI_DSI_MODE_LPM;
+
+	return 0;
+}
+
+static const struct panel_ops th101mb31ig002_28a_panel_ops = {
+	.enable_backlight = th101mb31ig002_28a_panel_enable_backlight,
+	.set_backlight = th101mb31ig002_28a_panel_set_backlight,
+	.get_display_timing = th101mb31ig002_28a_panel_get_display_timing,
+};
+
+static const struct udevice_id th101mb31ig002_28a_ids[] = {
+	{ .compatible = "boe,th101mb31ig002-28a", },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(th101mb31ig002_28a_panel) = {
+	.name		= "th101mb31ig002_28a_panel",
+	.id			= UCLASS_PANEL,
+	.of_match	= th101mb31ig002_28a_ids,
+	.ops		= &th101mb31ig002_28a_panel_ops,
+	.of_to_plat	= th101mb31ig002_28a_panel_of_to_plat,
+	.probe		= th101mb31ig002_28a_panel_probe,
+	.plat_auto	= sizeof(struct mipi_dsi_panel_plat),
+	.priv_auto	= sizeof(struct th101mb31ig002_28a_panel_priv),
+};

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (4 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 05/12] video: Add BOE TH101MB31IG002-28A MIPI-DSI panel Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-10  3:24   ` Chaoyi Chen
  2025-11-08  5:38 ` [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support Dang Huynh via B4 Relay
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

VOP2 (Video Output Processor v2) is a display controller on Rockchip
SoCs. It can be found on RK3566/8 and RK3588.

This commit currently only supports RK3566/8.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
 drivers/video/rockchip/Makefile                 |   3 +-
 drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
 drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
 drivers/video/rockchip/rk_vop2.h                |  76 ++++
 5 files changed, 1138 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/arch-rockchip/vop_rk3568.h b/arch/arm/include/asm/arch-rockchip/vop_rk3568.h
new file mode 100644
index 00000000000..6388369ce8c
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/vop_rk3568.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
+ *
+ * Based on vop_rk3288.h:
+ *   Copyright (c) 2015 Google, Inc
+ *   Copyright 2014 Rockchip Inc.
+ */
+
+#ifndef _ASM_ARCH_VOP_RK3568_H
+#define _ASM_ARCH_VOP_RK3568_H
+
+struct rk3568_vop_sysctrl {
+	u32 reg_cfg_done;
+	u32 version_info;
+	u32 autogating_ctrl;
+	u32 win_reg_cfg_done;
+	u32 axi_ctrl0;
+	u32 axi_hurry_ctrl0;
+	u32 axi_hurry_ctrl1;
+	u32 axi_outstanding_ctrl0;
+	u32 axi_outstanding_ctrl1;
+	u32 axi_lut_ctrl;
+	u32 dsp_en;
+	u32 dsp_ctrl;
+	u32 dsp_pol;
+	u32 pwr_ctrl;
+	u32 var_freq_ctrl;
+	u32 mmu_raddr_range;
+	u32 wb_ctrl0;
+	u32 wb_xspd;
+	u32 wb_yrgb_mst;
+	u32 wb_cbr_mst;
+	u32 otp_win;
+	u32 otp_mirr_ctrl;
+	u32 lut_port_sel;
+	u32 pwr_stable_ctrl;
+	u32 status0;
+	u32 status1;
+	u32 status2;
+	u32 status3;
+	u32 line_flag0;
+	u32 line_flag1;
+	u32 line_flag2;
+	u32 line_flag3;
+	u32 sys0_intr_en;
+	u32 sys0_intr_clr;
+	u32 sys0_intr_status;
+	u32 sys0_intr_status_raw;
+	u32 sys1_intr_en;
+	u32 sys1_intr_clr;
+	u32 sys1_intr_status;
+	u32 sys1_intr_status_raw;
+	u32 port0_intr_en;
+	u32 port0_intr_clr;
+	u32 port0_intr_status;
+	u32 port0_intr_status_raw;
+	u32 port1_intr_en;
+	u32 port1_intr_clr;
+	u32 port1_intr_status;
+	u32 port1_intr_status_raw;
+	u32 port2_intr_en;
+	u32 port2_intr_clr;
+	u32 port2_intr_status;
+	u32 port2_intr_status_raw;
+	u32 port3_intr_en;
+	u32 port3_intr_clr;
+	u32 port3_intr_status;
+	u32 port3_intr_status_raw;
+};
+
+check_member(rk3568_vop_sysctrl, port3_intr_status_raw, 0x00DC);
+
+struct rk3568_vop_overlay {
+	u32 overlay_ctrl;
+	u32 layer_sel;
+	u32 port_sel;
+};
+
+check_member(rk3568_vop_overlay, port_sel, 0x0008);
+
+struct rk3568_vop_post {
+	u32 dsp_ctrl;
+	u32 mipi_ctrl;
+	u32 color_ctrl;
+	u32 reserved2;
+	u32 lut_reserved[4];
+	u32 reserved[3];
+	u32 dsp_bg;
+	u32 prescan_htimings;
+	u32 dsp_hact_info;
+	u32 dsp_vact_info;
+	u32 scl_factor_yrgb;
+	u32 scl_ctrl;
+	u32 dsp_vact_info_f1;
+	u32 dsp_htotal_hs_end;
+	u32 dsp_hact_st_end;
+	u32 dsp_vtotal_vs_end;
+	u32 dsp_vact_st_end;
+	u32 dsp_vs_st_end_f1;
+	u32 dsp_vact_st_end_f1;
+};
+
+check_member(rk3568_vop_post, dsp_vact_st_end_f1, 0x005C);
+
+struct rk3568_vop_cluster {
+	u32 win0_ctrl0;
+	u32 win0_ctrl1;
+	u32 win0_ctrl2;
+	u32 reserved0;
+	u32 win0_mst_yrgb;
+	u32 win0_mst_cbcr;
+	u32 win0_vir;
+	u32 reserved1;
+	u32 win0_act_info;
+	u32 win0_dsp_info;
+	u32 win0_dsp_st;
+	u32 win0_dsp_bg;
+	u32 win0_scl_factor_yrgb;
+	u32 reserved2;
+	u32 win0_scl_offset;
+	u32 win0_transformed_offset;
+	u32 reserved3[4];
+	u32 win0_afbcd_output_ctrl;
+	u32 win0_afbcd_mode;
+	u32 win0_afbcd_hdr_ptr;
+	u32 win0_afbcd_vir_width;
+	u32 win0_afbcd_size;
+	u32 win0_afbcd_pic_offset;
+	u32 win0_afbcd_dis_offset;
+	u32 win0_afbcd_ctrl;
+};
+
+check_member(rk3568_vop_cluster, win0_afbcd_ctrl, 0x006C);
+
+struct rk3568_vop_esmart {
+	u32 esmart_ctrl0;
+	u32 esmart_ctrl1;
+	u32 reserved0[2];
+	u32 esmart_region0_mst_ctl;
+	u32 esmart_region0_mst_yrgb;
+	u32 esmart_region0_mst_cbcr;
+	u32 esmart_region0_vir;
+	u32 esmart_region0_act_info;
+	u32 esmart_region0_dsp_info;
+	u32 esmart_region0_dsp_offset;
+	u32 reserved1[1];
+	u32 esmart_region0_scl_ctrl;
+	u32 esmart_region0_scl_factor_yrgb;
+	u32 esmart_region0_scl_factor_cbcr;
+	u32 esmart_region0_scl_offset;
+};
+
+check_member(rk3568_vop_esmart, esmart_region0_scl_offset, 0x003C);
+
+enum rockchip_fb_data_format_t {
+	ARGB8888 = 0,
+	RGB888 = 1,
+	RGB565 = 2,
+};
+
+enum vop_modes {
+	VOP_MODE_EDP = 0,
+	VOP_MODE_MIPI,
+	VOP_MODE_HDMI,
+	VOP_MODE_LVDS,
+	VOP_MODE_DP,
+};
+
+/* OFFSETS */
+#define VOP2_SYSREG_OFFSET 0x0
+#define VOP2_OVERLAY_OFFSET 0x0600
+#define VOP2_POST_OFFSET(n) 0x0c00 + ((n) * 0x100)
+#define VOP2_CLUSTER_OFFSET(n) 0x1000 + ((n) * 0x200)
+#define VOP2_ESMART_OFFSET(n) 0x1800 + ((n) * 0x200)
+
+/* System Registers */
+/* REG_CFG_DONE */
+#define M_GLOBAL_REGDONE (1 << 15)
+#define M_LOAD_GLOBAL(x) (1 << ((x) & 3))
+
+#define V_GLOBAL_REGDONE(x) (((x) & 1) << 15)
+#define V_LOAD_GLOBAL(x, y) (((y) & 1) << ((x) & 3))
+
+/* VERSION_INFO */
+#define M_FPGA_VERSION  (0xffff << 16)
+#define M_RTL_VERSION (0xffff)
+
+/* AUTO_GATING_CTRL */
+#define M_AUTO_GATING (1 << 31)
+#define V_AUTO_GATING(x) (((x) & 1) << 31)
+
+/* DSP_INFACE_POL */
+#define M_DSP_INFACE_REGDONE (1 << 28)
+#define V_DSP_INFACE_REGDONE(x) (((x) & 1) << 28)
+
+/* OTP_WIN_EN */
+#define M_OTP_WIN (1 << 0)
+#define V_OTP_WIN(x) (((x) & 1) << 0)
+
+/* Overlay */
+/* OVERLAY_CTRL */
+#define M_LAYER_SEL_REGDONE_SEL (3 << 30)
+#define M_LAYER_SEL_REGDONE_EN (1 << 28)
+#define M_VP_OVERLAY_MODE(vp) (1 << ((vp) & 3))
+
+#define V_LAYER_SEL_REGDONE_SEL(x) (((x) & 3) << 30)
+#define V_LAYER_SEL_REGDONE_EN(x) (((x) & 1) << 28)
+#define V_VP_OVERLAY_MODE(x, vp) (((x) & 1) << ((vp) & 3))
+
+/* LAYER_SEL */
+#define V_LAYER_SEL(x, port) (((port) & 7) << 4 * ((x) & 7))
+
+/* PORT_SEL */
+#define V_ESMART_SEL_PORT(x, vp) (((vp) & 3) << (24 + (2 * ((x) & 3))))
+#define V_CLUSTER_SEL_PORT(x, vp) (((vp) & 3) << (16 + (2 * ((x) & 3))))
+#define V_PORT_MUX(x, vp) (((x) & 0xf) << 4 * ((vp) & 3))
+
+/* Post processing */
+/* DSP_CTRL */
+#define M_POST_STANDBY (1 << 31)
+#define M_POST_FP_STANDBY (1 << 30)
+#define M_POST_BLACK (1 << 27)
+#define M_POST_OUT_ZERO (1 << 26)
+#define M_POST_LB_MODE (1 << 23)
+#define M_PRE_DITHER_DOWN (1 << 16)
+#define M_DSP_OUT_MODE (0xf)
+
+#define V_POST_STANDBY(x) (((x) & 1) << 31)
+#define V_POST_FP_STANDBY(x) (((x) & 1) << 30)
+#define V_POST_BLACK(x) (((x) & 1) << 27)
+#define V_POST_OUT_ZERO(x) (((x) & 1) << 26)
+#define V_POST_LB_MODE(x) (((x) & 1) << 23)
+#define V_PRE_DITHER_DOWN(x) (((x) & 1) << 16)
+#define V_DSP_OUT_MODE(x) ((x) & 0xf)
+
+/* COLOR_CTRL */
+#define M_POST_COLORBAR_MODE (1 << 1)
+#define M_POST_COLORBAR_EN (1 << 0)
+
+#define V_POST_COLORBAR_MODE(x) (((x) & 1) << 1)
+#define V_POST_COLORBAR_EN(x) (((x) & 1) << 0)
+
+/* DSP_BG */
+#define M_DSP_BG_RED     (0x3f << 20)
+#define M_DSP_BG_GREEN   (0x3f << 10)
+#define M_DSP_BG_BLUE    (0x3f << 0)
+
+#define V_DSP_BG_RED(x)     (((x) & 0x3f) << 20)
+#define V_DSP_BG_GREEN(x)   (((x) & 0x3f) << 10)
+#define V_DSP_BG_BLUE(x)    (((x) & 0x3f) << 0)
+
+/* ESMART */
+#define M_ESMART_REGION0_MST_EN (1 << 0)
+#define V_ESMART_REGION0_DATA_FMT(x)        (((x) & 0x16) << 1)
+
+/* VOP2_ESMART_REGION0_VIR */
+#define V_ARGB888_VIRWIDTH(x)	(((x) & 0xffff) << 0)
+#define V_RGB888_VIRWIDTH(x)	((((((x) * 3) >> 2) + ((x) % 3)) & 0xffff) << 0)
+#define V_RGB565_VIRWIDTH(x)	((((x) / 2) & 0xffff) << 0)
+#define YUV_VIRWIDTH(x)		((((x) / 4) & 0xffff) << 0)
+
+#define V_ACT_HEIGHT(x)         (((x) & 0x1fff) << 16)
+#define V_ACT_WIDTH(x)          ((x) & 0x1fff)
+#define V_DSP_HEIGHT(x)         (((x) & 0x1fff) << 16)
+#define V_DSP_WIDTH(x)          ((x) & 0x1fff)
+#define V_DSP_YST(x)         (((x) & 0x1fff) << 16)
+#define V_DSP_XST(x)          ((x) & 0x1fff)
+
+#define V_HSYNC(x)		(((x) & 0x1fff) << 0)   /* hsync pulse width */
+#define V_HORPRD(x)		(((x) & 0x1fff) << 16)   /* horizontal period */
+#define V_VSYNC(x)		(((x) & 0x1fff) << 0)
+#define V_VERPRD(x)		(((x) & 0x1fff) << 16)
+
+#define V_HEAP(x)		(((x) & 0x1fff) << 0)/* horizontal active end */
+#define V_HASP(x)		(((x) & 0x1fff) << 16)/* horizontal active start */
+#define V_VAEP(x)		(((x) & 0x1fff) << 0)
+#define V_VASP(x)		(((x) & 0x1fff) << 16)
+
+#endif
diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile
index f55beceebf1..2f89a979a28 100644
--- a/drivers/video/rockchip/Makefile
+++ b/drivers/video/rockchip/Makefile
@@ -4,10 +4,11 @@
 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 
 ifdef CONFIG_VIDEO_ROCKCHIP
-obj-y += rk_vop.o
+obj-y += rk_vop.o rk_vop2.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288_vop.o
 obj-$(CONFIG_ROCKCHIP_RK3328) += rk3328_vop.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399_vop.o
+obj-$(CONFIG_ROCKCHIP_RK3568) += rk3568_vop.o
 obj-$(CONFIG_DISPLAY_ROCKCHIP_EDP) += rk_edp.o
 obj-$(CONFIG_DISPLAY_ROCKCHIP_LVDS) += rk_lvds.o
 obj-hdmi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_hdmi.o
diff --git a/drivers/video/rockchip/rk3568_vop.c b/drivers/video/rockchip/rk3568_vop.c
new file mode 100644
index 00000000000..787060c1d80
--- /dev/null
+++ b/drivers/video/rockchip/rk3568_vop.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
+ *
+ * Based on rk3399_vop.c:
+ *   Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
+ *   Copyright (c) 2015 Google, Inc
+ *   Copyright 2014 Rockchip Inc.
+ */
+
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <regmap.h>
+#include <video.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <asm/global_data.h>
+#include <linux/bitfield.h>
+#include "rk_vop2.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define M_MIPI1_INFACE_MUX (3 << 21)
+#define M_LVDS_INFACE_MUX (3 << 18)
+#define M_MIPI_INFACE_MUX (3 << 16)
+#define M_EDP_INFACE_MUX (3 << 14)
+#define M_HDMI_INFACE_MUX (3 << 10)
+#define M_RGB_INFACE_MUX (3 << 8)
+
+#define V_MIPI1_INFACE_MUX(x) (((x) & 3) << 21)
+#define V_LVDS_INFACE_MUX(x) (((x) & 3) << 18)
+#define V_MIPI_INFACE_MUX(x) (((x) & 3) << 16)
+#define V_EDP_INFACE_MUX(x) (((x) & 3) << 14)
+#define V_HDMI_INFACE_MUX(x) (((x) & 3) << 10)
+#define V_RGB_INFACE_MUX(x) (((x) & 3) << 8)
+
+#define M_MIPI_POL (0xf << 16)
+#define M_EDP_POL (0xf << 12)
+#define M_HDMI_POL (0xf << 4)
+#define M_RGB_LVDS_POL (0xf << 0)
+
+#define V_MIPI_POL(x) (((x) & 0xf) << 16)
+#define V_EDP_POL(x) (((x) & 0xf) << 12)
+#define V_HDMI_POL(x) (((x) & 0xf) << 4)
+#define V_RGB_LVDS_POL(x) (((x) & 0xf) << 0)
+
+#define M_MIPI1_OUT_EN (1 << 20)
+#define M_BT656_OUT_EN (1 << 7)
+#define M_BT1120_OUT_EN (1 << 6)
+#define M_LVDS_OUT_EN (1 << 5)
+#define M_MIPI_OUT_EN (1 << 4)
+#define M_EDP_OUT_EN (1 << 3)
+#define M_HDMI_OUT_EN (1 << 1)
+#define M_RGB_OUT_EN (1 << 0)
+
+#define M_ALL_OUT_EN (M_MIPI1_OUT_EN | M_BT656_OUT_EN | M_BT1120_OUT_EN | M_LVDS_OUT_EN | \
+			M_MIPI_OUT_EN | M_EDP_OUT_EN | M_HDMI_OUT_EN | M_RGB_OUT_EN)
+
+#define V_MIPI1_OUT_EN(x) (((x) & 1) << 20)
+#define V_BT656_OUT_EN(x) (((x) & 1) << 7)
+#define V_BT1120_OUT_EN(x) (((x) & 1) << 6)
+#define V_LVDS_OUT_EN(x) (((x) & 1) << 5)
+#define V_MIPI_OUT_EN(x) (((x) & 1) << 4)
+#define V_EDP_OUT_EN(x) (((x) & 1) << 3)
+#define V_HDMI_OUT_EN(x) (((x) & 1) << 1)
+#define V_RGB_OUT_EN(x) (((x) & 1) << 0)
+
+static void rk3568_enable_output(struct udevice *dev,
+				 enum vop_modes mode, u32 port)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
+	u32 reg;
+
+	switch (mode) {
+	case VOP_MODE_EDP:
+		reg |= M_EDP_OUT_EN | V_EDP_INFACE_MUX(port);
+		break;
+
+	case VOP_MODE_HDMI:
+		reg |= M_HDMI_OUT_EN | V_HDMI_INFACE_MUX(port);
+		break;
+
+	case VOP_MODE_MIPI:
+		reg |= M_MIPI_OUT_EN | V_MIPI_INFACE_MUX(port);
+		break;
+
+	case VOP_MODE_LVDS:
+		reg |= M_LVDS_OUT_EN | V_LVDS_INFACE_MUX(port);
+		break;
+
+	default:
+		debug("%s: unsupported output mode %x\n", __func__, mode);
+		return;
+	}
+
+	debug("%s: vop output 0x%08x\n", __func__, reg);
+	writel(reg, &sysctrl->dsp_en);
+}
+
+static void rk3568_set_pin_polarity(struct udevice *dev,
+				    enum vop_modes mode, u32 polarity)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
+	u32 reg;
+
+	reg = M_DSP_INFACE_REGDONE;
+
+	switch (mode) {
+	case VOP_MODE_EDP:
+		reg |= V_EDP_POL(polarity);
+		break;
+
+	case VOP_MODE_HDMI:
+		reg |= V_HDMI_POL(polarity);
+		break;
+
+	case VOP_MODE_MIPI:
+		reg |= V_MIPI_POL(polarity);
+		break;
+
+	/* RGB and LVDS shares the same polarity */
+	case VOP_MODE_LVDS:
+		reg |= V_RGB_LVDS_POL(polarity);
+		break;
+
+	default:
+		debug("%s: unsupported output mode %x\n", __func__, mode);
+		return;
+	}
+
+	debug("%s: vop polarity 0x%08x\n", __func__, reg);
+	writel(reg, &sysctrl->dsp_pol);
+}
+
+static int rkvop2_initialize(struct udevice *dev)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
+
+	/* Enable OTP function */
+	clrsetbits_le32(&sysctrl->otp_win, M_OTP_WIN, V_OTP_WIN(1));
+
+	writel(M_GLOBAL_REGDONE, &sysctrl->reg_cfg_done);
+
+	/* Disable auto gating */
+	clrsetbits_le32(&sysctrl->autogating_ctrl, M_AUTO_GATING, V_AUTO_GATING(0));
+
+	return 0;
+}
+
+/*
+ * FIXME: Booting into Linux with a window plane enabled causes VOP IOMMU
+ * to fail.
+ *
+ * This can be removed when there's a better way to handle MMU under Linux.
+ */
+static int rk3568_vop_remove(struct udevice *dev)
+{
+	if (CONFIG_IS_ENABLED(VIDEO_REMOVE)) {
+		struct rk_vop2_priv *priv = dev_get_priv(dev);
+		struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
+		struct rk3568_vop_esmart *esmart = priv->regs + VOP2_ESMART_OFFSET(priv->layer - 4);
+
+		debug("Removing VOP2 driver (vp=%d, layer=%d)\n", priv->vp, priv->layer);
+
+		/* RK356X appears to only make use of (E)SMARTs */
+		writel(0, &esmart->esmart_region0_mst_ctl);
+
+		/* On RK3568, we don't need to shift the bit by 16. */
+		writel(M_GLOBAL_REGDONE | M_LOAD_GLOBAL(priv->vp),
+		       &sysctrl->reg_cfg_done);
+	}
+
+	return 0;
+}
+
+static int rk3568_vop_probe(struct udevice *dev)
+{
+	int ret;
+
+	/* Before relocation we don't need to do anything */
+	if (!(gd->flags & GD_FLG_RELOC))
+		return 0;
+
+	ret = rkvop2_initialize(dev);
+	if (ret)
+		return ret;
+
+	return rk_vop2_probe(dev);
+}
+
+/*
+ * RK3566 datasheet omits the VP2, even though it exist in the hardware
+ * so let's not use it.
+ */
+struct rkvop2_platdata rk3566_platdata = {
+	.delay = 20,
+	.bg_dly = {42, 40, -1},
+	/* SMART0, ESMART0 */
+	.vp_lyr = {3, 2, -1},
+	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
+		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
+		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
+		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
+};
+
+struct rkvop2_platdata rk3568_platdata = {
+	.delay = 20,
+	.bg_dly = {42, 40, 40},
+	/* SMART0, SMART1, ESMART1 */
+	.vp_lyr = {3, 7, 6},
+	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
+		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
+		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
+		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
+};
+
+struct rkvop2_driverdata rk3566_driverdata = {
+	.features = VOP_FEATURE_OUTPUT_10BIT,
+	.set_pin_polarity = rk3568_set_pin_polarity,
+	.enable_output = rk3568_enable_output,
+	.platdata = &rk3566_platdata,
+};
+
+struct rkvop2_driverdata rk3568_driverdata = {
+	.features = VOP_FEATURE_OUTPUT_10BIT,
+	.set_pin_polarity = rk3568_set_pin_polarity,
+	.enable_output = rk3568_enable_output,
+	.platdata = &rk3568_platdata,
+};
+
+static const struct udevice_id rk3568_vop_ids[] = {
+	{ .compatible = "rockchip,rk3566-vop",
+	  .data = (ulong)&rk3566_driverdata },
+	{ .compatible = "rockchip,rk3568-vop",
+	  .data = (ulong)&rk3568_driverdata },
+	{ }
+};
+
+static const struct video_ops rk3568_vop_ops = {
+};
+
+U_BOOT_DRIVER(rk3568_vop) = {
+	.name	= "rk3568_vop",
+	.id	= UCLASS_VIDEO,
+	.of_match = rk3568_vop_ids,
+	.ops	= &rk3568_vop_ops,
+	.bind	= rk_vop2_bind,
+	.probe	= rk3568_vop_probe,
+	.remove = rk3568_vop_remove,
+	.priv_auto = sizeof(struct rk_vop2_priv),
+#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
+	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
+#else
+	.flags = DM_FLAG_PRE_RELOC,
+#endif
+};
diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
new file mode 100644
index 00000000000..992f215d416
--- /dev/null
+++ b/drivers/video/rockchip/rk_vop2.c
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
+ *
+ * Based on rk_vop.c:
+ *   Copyright (c) 2015 Google, Inc
+ *   Copyright 2014 Rockchip Inc.
+ */
+
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <edid.h>
+#include <log.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <video.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/vop_rk3568.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <efi.h>
+#include <efi_loader.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <power/regulator.h>
+
+#include "rk_vop2.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
+static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
+{
+	u32 reg;
+
+	reg = M_GLOBAL_REGDONE;
+
+	/*
+	 * For RK3588, changes will only take effect when the same bit is
+	 * leftshifted by 16.
+	 */
+	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
+
+	writel(reg, &sysctrl->reg_cfg_done);
+}
+
+static int rkvop2_enable(struct udevice *dev, ulong fbbase,
+			 int fb_bits_per_pixel, const struct display_timing *edid,
+			 int port, int win_id, struct rkvop2_platdata *platdata)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
+	struct rk3568_vop_esmart *esmart;
+	bool is_cluster = false;
+	u8 layer;
+	u32 reg;
+	u32 rgb_mode;
+	u32 hactive = edid->hactive.typ;
+	u32 vactive = edid->vactive.typ;
+
+	if (platdata->layers[win_id] < 0)
+		return -EINVAL;
+
+	switch (platdata->layers[win_id]) {
+	case ROCKCHIP_VOP2_CLUSTER0:
+	case ROCKCHIP_VOP2_CLUSTER1:
+	case ROCKCHIP_VOP2_CLUSTER2:
+	case ROCKCHIP_VOP2_CLUSTER3:
+		is_cluster = true;
+		break;
+	default:
+		break;
+	}
+
+	layer = platdata->layers[win_id];
+
+	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
+	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
+
+	/* TODO: Support VOP2 CLUSTER */
+	if (is_cluster) {
+		dev_err(dev, "win_id is a cluster, not supported.\n");
+		return -ENOSYS;
+	}
+
+	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
+
+	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
+
+	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
+	       &esmart->esmart_region0_act_info);
+
+	/* Set offset to 0,0 */
+	writel(0, &esmart->esmart_region0_dsp_offset);
+
+	writel(V_DSP_WIDTH(hactive - 1) |
+			V_DSP_HEIGHT(vactive - 1),
+			&esmart->esmart_region0_dsp_info);
+
+	switch (fb_bits_per_pixel) {
+	case 16:
+		rgb_mode = RGB565;
+		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
+		break;
+	case 24:
+		rgb_mode = RGB888;
+		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
+		break;
+	case 32:
+	default:
+		rgb_mode = ARGB8888;
+		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
+		break;
+	}
+
+	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
+
+	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
+	       &esmart->esmart_region0_mst_ctl);
+
+	/* Set esmart to the destination video port */
+	reg = V_ESMART_SEL_PORT(layer - 4, port);
+
+	/*
+	 * VOP2 requires every port mux to be configured.
+	 *
+	 * As U-Boot only supports singledisplay, we'll set all
+	 * unused ports to set layer to 8 (disabled).
+	 */
+	for (int i = 0; i < 4; i++) {
+		if (i != port)
+			reg |= V_PORT_MUX(8, i);
+	}
+
+	writel(reg, &overlay->port_sel);
+
+	/* Set layer 0 to win_id */
+	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
+
+	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
+	writel(reg, &overlay->overlay_ctrl);
+
+	priv->layer = layer;
+
+	return 0;
+}
+
+static void rkvop2_set_pin_polarity(struct udevice *dev,
+				    enum vop_modes mode, u32 polarity)
+{
+	struct rkvop2_driverdata *ops =
+		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
+
+	if (ops->set_pin_polarity)
+		ops->set_pin_polarity(dev, mode, polarity);
+}
+
+static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
+{
+	struct rkvop2_driverdata *ops =
+		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
+
+	if (ops->enable_output)
+		ops->enable_output(dev, mode, port);
+}
+
+static void rkvop2_mode_set(struct udevice *dev,
+			    const struct display_timing *edid,
+		enum vop_modes mode, int port,
+		struct rkvop2_platdata *platdata)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
+	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
+	struct rkvop2_driverdata *data =
+		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
+
+	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
+
+	u32 hactive = edid->hactive.typ;
+	u32 vactive = edid->vactive.typ;
+	u32 hsync_len = edid->hsync_len.typ;
+	u32 hback_porch = edid->hback_porch.typ;
+	u32 vsync_len = edid->vsync_len.typ;
+	u32 vback_porch = edid->vback_porch.typ;
+	u32 hfront_porch = edid->hfront_porch.typ;
+	u32 vfront_porch = edid->vfront_porch.typ;
+	int mode_flags;
+	u32 pin_polarity;
+	u32 reg;
+
+	pin_polarity = BIT(DCLK_INVERT);
+	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		pin_polarity |= BIT(HSYNC_POSITIVE);
+	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		pin_polarity |= BIT(VSYNC_POSITIVE);
+
+	rkvop2_enable_output(dev, mode, port);
+	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
+
+	mode_flags = 0;  /* RGB888 */
+	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
+	    mode == VOP_MODE_HDMI)
+		mode_flags = 15;  /* RGBaaa */
+
+	reg = V_DSP_OUT_MODE(mode_flags);
+
+	debug("(%s, %s): bg_dly: %d\n",
+	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
+
+	if (platdata->bg_dly[port] < 0) {
+		dev_err(dev, "bg_dly is zero for vp%d\n", port);
+		return;
+	}
+
+	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
+	       &post->prescan_htimings);
+
+	writel(V_HSYNC(hsync_len) |
+			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
+			&post->dsp_htotal_hs_end);
+
+	writel(V_HEAP(hsync_len + hback_porch + hactive) |
+			V_HASP(hsync_len + hback_porch),
+			&post->dsp_hact_st_end);
+
+	writel(V_VAEP(vsync_len + vback_porch + vactive) |
+			V_VASP(vsync_len + vback_porch),
+			&post->dsp_vact_st_end);
+
+	writel(V_VSYNC(vsync_len) |
+			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
+			&post->dsp_vtotal_vs_end);
+
+	writel(V_HEAP(hsync_len + hback_porch + hactive) |
+			V_HASP(hsync_len + hback_porch),
+			&post->dsp_hact_info);
+
+	writel(V_VAEP(vsync_len + vback_porch + vactive) |
+			V_VASP(vsync_len + vback_porch),
+			&post->dsp_vact_info);
+
+	/* No scaling */
+	writel(0x10001000, &post->scl_factor_yrgb);
+
+	writel(reg, &post->dsp_ctrl);
+
+	rkvop2_cfg_regdone(sysctrl, port);
+}
+
+/**
+ * rk_display_init() - Try to enable the given display device
+ *
+ * This function performs many steps:
+ * - Finds the display device being referenced by @ep_node
+ * - Puts the VOP's ID into its uclass platform data
+ * - Probes the device to set it up
+ * - Reads the timing information (from EDID or panel)
+ * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
+ * - Enables the display (the display device handles this and will do different
+ *     things depending on the display type)
+ * - Tells the uclass about the display resolution so that the console will
+ *     appear correctly
+ *
+ * @dev:	VOP device that we want to connect to the display
+ * @fbbase:	Frame buffer address
+ * @vp_node:	Device tree node to process
+ * Return: 0 if OK, -ve if something went wrong
+ */
+static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
+{
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct rkvop2_driverdata *drvdata =
+		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
+	struct rkvop2_platdata *platdata =
+		(struct rkvop2_platdata *)drvdata->platdata;
+	ofnode ep_node;
+	int vop_id, port_id, win_id;
+	struct display_timing timing;
+	struct udevice *disp;
+	int ret;
+	u32 remote_phandle;
+	struct display_plat *disp_uc_plat;
+	enum video_log2_bpp l2bpp;
+	ofnode remote;
+	const char *compat;
+	char dclk_name[9];
+	struct clk dclk;
+
+	debug("%s(%s, 0x%lx, %s)\n", __func__,
+	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
+
+	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
+	if (port_id < 0) {
+		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
+		return port_id;
+	}
+
+	ep_node = ofnode_first_subnode(vp_node);
+	if (!ofnode_valid(ep_node)) {
+		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
+		return -EINVAL;
+	}
+
+	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
+	if (ret) {
+		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
+		return ret;
+	}
+
+	remote = ofnode_get_by_phandle(remote_phandle);
+	if (!ofnode_valid(remote))
+		return -EINVAL;
+
+	remote = ofnode_get_parent(remote);
+	if (!ofnode_valid(remote))
+		return -EINVAL;
+
+	/*
+	 * The remote-endpoint references into a subnode of the encoder
+	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
+	 * the following:
+	 *
+	 * hdmi: hdmi@fe0a0000 {
+	 *   ports {
+	 *     hdmi_in: port {
+	 *       hdmi_in_vp0: endpoint { ... };
+	 *     }
+	 *   }
+	 * }
+	 *
+	 * This isn't any different from how VOP1 works, so we'll adapt
+	 * the same method of finding the display from the original code
+	 * (find the enclosing device of "UCLASS_DISPLAY")
+	 *
+	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
+	 * DW MIPI DSI driver for Rockchip.
+	 */
+	while (ofnode_valid(remote)) {
+		remote = ofnode_get_parent(remote);
+		if (!ofnode_valid(remote)) {
+			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
+			      __func__, dev_read_name(dev));
+			return -EINVAL;
+		}
+
+		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
+		if (disp)
+			break;
+	};
+	compat = ofnode_get_property(remote, "compatible", NULL);
+	if (!compat) {
+		debug("%s(%s): Failed to find compatible property\n",
+		      __func__, dev_read_name(dev));
+		return -EINVAL;
+	}
+	if (strstr(compat, "edp")) {
+		vop_id = VOP_MODE_EDP;
+	} else if (strstr(compat, "mipi")) {
+		vop_id = VOP_MODE_MIPI;
+	} else if (strstr(compat, "hdmi")) {
+		vop_id = VOP_MODE_HDMI;
+	} else if (strstr(compat, "rk3588-dp")) {
+		vop_id = VOP_MODE_DP;
+	} else if (strstr(compat, "lvds")) {
+		vop_id = VOP_MODE_LVDS;
+	} else {
+		debug("%s(%s): Failed to find vop mode for %s\n",
+		      __func__, dev_read_name(dev), compat);
+		return -EINVAL;
+	}
+	debug("vop_id=%d - port=%d\n", vop_id, port_id);
+
+	/* Get the video port clock and enable it */
+	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
+	ret = clk_get_by_name(dev, dclk_name, &dclk);
+	if (ret < 0)
+		return ret;
+
+	disp_uc_plat = dev_get_uclass_plat(disp);
+	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
+	if (display_in_use(disp)) {
+		debug("   - device in use\n");
+		return -EBUSY;
+	}
+
+	disp_uc_plat->source_id = vop_id;
+	disp_uc_plat->src_dev = dev;
+
+	ret = device_probe(disp);
+	if (ret) {
+		debug("%s: device '%s' display won't probe (ret=%d)\n",
+		      __func__, dev->name, ret);
+		return ret;
+	}
+
+	ret = display_read_timing(disp, &timing);
+	if (ret) {
+		debug("%s: Failed to read timings\n", __func__);
+		return ret;
+	}
+
+	/* Set clock rate on video port to display timings */
+	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set clock rate: %d\n", ret);
+		return ret;
+	}
+
+	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
+	      dclk_name, clk_get_rate(&dclk));
+
+	/* Set bitwidth for vop display according to vop mode */
+	switch (vop_id) {
+	case VOP_MODE_EDP:
+	case VOP_MODE_MIPI:
+	case VOP_MODE_HDMI:
+	case VOP_MODE_DP:
+	case VOP_MODE_LVDS:
+		l2bpp = VIDEO_BPP32;
+		break;
+	default:
+		l2bpp = VIDEO_BPP16;
+	}
+
+	/*
+	 * We'll use the default platform-specific win_id from Linux
+	 * so that Linux can take over U-Boot plane when Linux reconfigures
+	 * VOP2.
+	 */
+	win_id = platdata->vp_lyr[port_id];
+	if (win_id < 0) {
+		dev_err(dev, "win_id is null, don't setup\n");
+		return -EINVAL;
+	}
+
+	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
+	if (ret < 0)
+		return ret;
+
+	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
+
+	ret = display_enable(disp, 1 << l2bpp, &timing);
+	if (ret)
+		return ret;
+
+	uc_priv->xsize = timing.hactive.typ;
+	uc_priv->ysize = timing.vactive.typ;
+	uc_priv->bpix = l2bpp;
+
+	priv->vp = port_id;
+
+	debug("fb=%lx, size=%d %d\n", fbbase,
+	      uc_priv->xsize, uc_priv->ysize);
+
+	return 0;
+}
+
+int rk_vop2_probe(struct udevice *dev)
+{
+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+	struct rk_vop2_priv *priv = dev_get_priv(dev);
+	int ret = 0;
+	ofnode port, node;
+
+	/* Before relocation we don't need to do anything */
+	if (!(gd->flags & GD_FLG_RELOC))
+		return 0;
+
+	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
+		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
+		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
+	}
+
+	priv->regs = dev_read_addr_ptr(dev);
+
+	/* Try all the ports until we find one that works. */
+	port = dev_read_subnode(dev, "ports");
+	if (!ofnode_valid(port)) {
+		debug("%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 = rk_display_init(dev, plat->base, node);
+		if (ret)
+			debug("Device failed: ret=%d\n", ret);
+		if (!ret)
+			break;
+	}
+	video_set_flush_dcache(dev, true);
+
+	return ret;
+}
+
+int rk_vop2_bind(struct udevice *dev)
+{
+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
+			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
+
+	return 0;
+}
diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
new file mode 100644
index 00000000000..5d668070014
--- /dev/null
+++ b/drivers/video/rockchip/rk_vop2.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
+ */
+
+#ifndef __RK_VOP2_H__
+#define __RK_VOP2_H__
+
+#include <asm/arch-rockchip/vop_rk3568.h>
+
+struct rk_vop2_priv {
+	void *grf;
+	void *regs;
+	int vp;
+	int layer;
+};
+
+enum vop2_features {
+	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
+};
+
+enum vop2_layer {
+	ROCKCHIP_VOP2_CLUSTER0 = 0,
+	ROCKCHIP_VOP2_CLUSTER1,
+	ROCKCHIP_VOP2_CLUSTER2,
+	ROCKCHIP_VOP2_CLUSTER3,
+	ROCKCHIP_VOP2_ESMART0,
+	ROCKCHIP_VOP2_ESMART1,
+	ROCKCHIP_VOP2_ESMART2,
+	ROCKCHIP_VOP2_ESMART3,
+	ROCKCHIP_VOP2_SMART0 = 6,
+	ROCKCHIP_VOP2_SMART1,
+	ROCKCHIP_VOP2_RESERVED = -1,
+};
+
+struct rkvop2_platdata {
+	const u8 delay;
+	const s8 bg_dly[4]; /* VOP2 supports up to 4 video ports (0-3) */
+	const s8 vp_lyr[4];
+	const s8 layers[8];
+};
+
+struct rkvop2_driverdata {
+	/* configuration */
+	u32 features;
+	void (*platdata);
+	/* block-specific setters/getters */
+	void (*enable_output)(struct udevice *dev, enum vop_modes mode, u32 port);
+	void (*set_pin_polarity)(struct udevice *dev, enum vop_modes mode, u32 port);
+};
+
+/**
+ * rk_vop2_probe() - common probe implementation
+ *
+ * Performs the rk_display_init on each port-subnode until finding a
+ * working port (or returning an error if none of the ports could be
+ * successfully initialised).
+ *
+ * @dev:	device
+ * Return: 0 if OK, -ve if something went wrong
+ */
+int rk_vop2_probe(struct udevice *dev);
+
+/**
+ * rk_vop2_bind() - common bind implementation
+ *
+ * Sets the plat->size field to the amount of memory to be reserved for
+ * the framebuffer: this is always
+ *     (32 BPP) x VIDEO_ROCKCHIP_MAX_XRES x VIDEO_ROCKCHIP_MAX_YRES
+ *
+ * @dev:	device
+ * Return: 0 (always OK)
+ */
+int rk_vop2_bind(struct udevice *dev);
+
+#endif

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (5 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 06/12] video: rockchip: Add VOP2 support Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2026-03-04 12:29   ` Peter Robinson
  2025-11-08  5:38 ` [PATCH v6 08/12] arm: dts: rockchip: rk356x: Prerelocate VOP in U-Boot proper Dang Huynh via B4 Relay
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add support for video bridge to VOP2 so we can use the MIPI DSI
bridge driver that we have.

Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/rk_vop2.c | 81 +++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 22 deletions(-)

diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
index 992f215d416..156daafa0c3 100644
--- a/drivers/video/rockchip/rk_vop2.c
+++ b/drivers/video/rockchip/rk_vop2.c
@@ -13,10 +13,12 @@
 #include <dm/device_compat.h>
 #include <edid.h>
 #include <log.h>
+#include <panel.h>
 #include <regmap.h>
 #include <reset.h>
 #include <syscon.h>
 #include <video.h>
+#include <video_bridge.h>
 #include <asm/global_data.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
@@ -291,6 +293,7 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
 	int vop_id, port_id, win_id;
 	struct display_timing timing;
 	struct udevice *disp;
+	struct udevice *bridge;
 	int ret;
 	u32 remote_phandle;
 	struct display_plat *disp_uc_plat;
@@ -357,8 +360,11 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
 			return -EINVAL;
 		}
 
+		if (IS_ENABLED(CONFIG_VIDEO_BRIDGE))
+			uclass_find_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &bridge);
+
 		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
-		if (disp)
+		if (disp || bridge)
 			break;
 	};
 	compat = ofnode_get_property(remote, "compatible", NULL);
@@ -390,27 +396,49 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
 	if (ret < 0)
 		return ret;
 
-	disp_uc_plat = dev_get_uclass_plat(disp);
-	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
-	if (display_in_use(disp)) {
-		debug("   - device in use\n");
-		return -EBUSY;
-	}
+	if (bridge) {
+		/* video bridge detected, probe it */
+		ret = device_probe(bridge);
+		if (ret) {
+			dev_err(dev, "Failed to probe video bridge: %d\n", ret);
+			return ret;
+		}
 
-	disp_uc_plat->source_id = vop_id;
-	disp_uc_plat->src_dev = dev;
+		/* Attach the DSI controller and the display to the bridge. */
+		ret = video_bridge_attach(bridge);
+		if (ret) {
+			dev_err(dev, "Failed to attach video bridge: %d\n", ret);
+			return ret;
+		}
 
-	ret = device_probe(disp);
-	if (ret) {
-		debug("%s: device '%s' display won't probe (ret=%d)\n",
-		      __func__, dev->name, ret);
-		return ret;
-	}
+		ret = video_bridge_get_display_timing(bridge, &timing);
+		if (ret) {
+			dev_err(dev, "Failed to read timings: %d\n", ret);
+			return ret;
+		}
+	} else {
+		disp_uc_plat = dev_get_uclass_plat(disp);
+		debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
+		if (display_in_use(disp)) {
+			debug("   - device in use\n");
+			return -EBUSY;
+		}
 
-	ret = display_read_timing(disp, &timing);
-	if (ret) {
-		debug("%s: Failed to read timings\n", __func__);
-		return ret;
+		disp_uc_plat->source_id = vop_id;
+		disp_uc_plat->src_dev = dev;
+
+		ret = device_probe(disp);
+		if (ret) {
+			debug("%s: device '%s' display won't probe (ret=%d)\n",
+			      __func__, dev->name, ret);
+			return ret;
+		}
+
+		ret = display_read_timing(disp, &timing);
+		if (ret) {
+			debug("%s: Failed to read timings\n", __func__);
+			return ret;
+		}
 	}
 
 	/* Set clock rate on video port to display timings */
@@ -453,9 +481,18 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
 
 	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
 
-	ret = display_enable(disp, 1 << l2bpp, &timing);
-	if (ret)
-		return ret;
+	if (bridge) {
+		/* Attach the DSI controller and the display to the bridge. */
+		ret = video_bridge_set_backlight(bridge, 60);
+		if (ret) {
+			dev_err(dev, "Failed to start the video bridge: %d\n", ret);
+			return ret;
+		}
+	} else {
+		ret = display_enable(disp, 1 << l2bpp, &timing);
+		if (ret)
+			return ret;
+	}
 
 	uc_priv->xsize = timing.hactive.typ;
 	uc_priv->ysize = timing.vactive.typ;

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 08/12] arm: dts: rockchip: rk356x: Prerelocate VOP in U-Boot proper
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (6 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 09/12] configs: quartz64: Enable vidconsole Dang Huynh via B4 Relay
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

We need to prerelocate VOP2 memory so the driver can work. This
will only support U-Boot proper.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/dts/rk356x-u-boot.dtsi | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/dts/rk356x-u-boot.dtsi b/arch/arm/dts/rk356x-u-boot.dtsi
index 738b9673d35..1ed06239f05 100644
--- a/arch/arm/dts/rk356x-u-boot.dtsi
+++ b/arch/arm/dts/rk356x-u-boot.dtsi
@@ -170,6 +170,10 @@
 	bootph-all;
 };
 
+&vop {
+	bootph-some-ram;
+};
+
 &xin24m {
 	bootph-all;
 };

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 09/12] configs: quartz64: Enable vidconsole
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (7 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 08/12] arm: dts: rockchip: rk356x: Prerelocate VOP in U-Boot proper Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 10/12] video: rockchip: Add HDMI support for RK3568 Dang Huynh via B4 Relay
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Now that we have VOP2 support, we should enable vidconsole.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 include/configs/quartz64_rk3566.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/include/configs/quartz64_rk3566.h b/include/configs/quartz64_rk3566.h
index dfe0fee94cd..bf391458219 100644
--- a/include/configs/quartz64_rk3566.h
+++ b/include/configs/quartz64_rk3566.h
@@ -3,7 +3,10 @@
 #ifndef __QUARTZ64_RK3566_H
 #define __QUARTZ64_RK3566_H
 
-#define ROCKCHIP_DEVICE_SETTINGS
+#define ROCKCHIP_DEVICE_SETTINGS \
+		"stdin=serial,usbkbd\0" \
+		"stdout=serial,vidconsole\0" \
+		"stderr=serial,vidconsole\0"
 
 #include <configs/rk3568_common.h>
 

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 10/12] video: rockchip: Add HDMI support for RK3568
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (8 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 09/12] configs: quartz64: Enable vidconsole Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 11/12] configs: pinetab2-rk3566: Enable video and USB keyboard Dang Huynh via B4 Relay
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

HDMI on RK3568 is mostly simplified, all this does is enabling
DDC for display timings and HPD.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/video/rockchip/Makefile      |  1 +
 drivers/video/rockchip/rk3568_hdmi.c | 71 ++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)

diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile
index 2f89a979a28..b751c969b46 100644
--- a/drivers/video/rockchip/Makefile
+++ b/drivers/video/rockchip/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_DISPLAY_ROCKCHIP_LVDS) += rk_lvds.o
 obj-hdmi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_hdmi.o
 obj-hdmi-$(CONFIG_ROCKCHIP_RK3328) += rk3328_hdmi.o
 obj-hdmi-$(CONFIG_ROCKCHIP_RK3399) += rk3399_hdmi.o
+obj-hdmi-$(CONFIG_ROCKCHIP_RK3568) += rk3568_hdmi.o
 obj-$(CONFIG_DISPLAY_ROCKCHIP_HDMI) += rk_hdmi.o $(obj-hdmi-y)
 obj-mipi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_mipi.o
 obj-mipi-$(CONFIG_ROCKCHIP_RK3399) += rk3399_mipi.o
diff --git a/drivers/video/rockchip/rk3568_hdmi.c b/drivers/video/rockchip/rk3568_hdmi.c
new file mode 100644
index 00000000000..c3f4504ca8f
--- /dev/null
+++ b/drivers/video/rockchip/rk3568_hdmi.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <display.h>
+#include <dm.h>
+#include <dw_hdmi.h>
+#include <edid.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <asm/arch-rockchip/grf_rk3568.h>
+#include "rk_hdmi.h"
+
+#define RK3568_IO_DDC_IN_MSK             ((3 << 14) | (3 << (14 + 16)))
+
+static int rk3568_hdmi_enable(struct udevice *dev, int panel_bpp,
+			      const struct display_timing *edid)
+{
+	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+
+	return dw_hdmi_enable(&priv->hdmi, edid);
+}
+
+static int rk3568_hdmi_of_to_plat(struct udevice *dev)
+{
+	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+	struct dw_hdmi *hdmi = &priv->hdmi;
+
+	hdmi->i2c_clk_high = 0x6c;
+	hdmi->i2c_clk_low = 0x7d;
+
+	return rk_hdmi_of_to_plat(dev);
+}
+
+static const char * const rk3568_hdmi_reg_names[] = {
+	"avdd-0v9",
+	"avdd-1v8"
+};
+
+static int rk3568_hdmi_probe(struct udevice *dev)
+{
+	struct rk_hdmi_priv *priv = dev_get_priv(dev);
+	struct rk3568_grf *grf = priv->grf;
+
+	rk_hdmi_probe_regulators(dev, rk3568_hdmi_reg_names,
+				 ARRAY_SIZE(rk3568_hdmi_reg_names));
+
+	writel(RK3568_IO_DDC_IN_MSK, &grf->vo_con1);
+
+	return rk_hdmi_probe(dev);
+}
+
+static const struct dm_display_ops rk3568_hdmi_ops = {
+	.read_edid = rk_hdmi_read_edid,
+	.enable = rk3568_hdmi_enable,
+};
+
+static const struct udevice_id rk3568_hdmi_ids[] = {
+	{ .compatible = "rockchip,rk3568-dw-hdmi" },
+	{ }
+};
+
+U_BOOT_DRIVER(rk3568_hdmi_rockchip) = {
+	.name = "rk3568_hdmi_rockchip",
+	.id = UCLASS_DISPLAY,
+	.of_match = rk3568_hdmi_ids,
+	.ops = &rk3568_hdmi_ops,
+	.of_to_plat = rk3568_hdmi_of_to_plat,
+	.probe = rk3568_hdmi_probe,
+	.priv_auto = sizeof(struct rk_hdmi_priv),
+};

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 11/12] configs: pinetab2-rk3566: Enable video and USB keyboard
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (9 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 10/12] video: rockchip: Add HDMI support for RK3568 Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2025-11-08  5:38 ` [PATCH v6 12/12] clk: rockchip: rk3568: Use assigned VPLL clock when possible Dang Huynh via B4 Relay
  2026-01-08 22:14 ` [PATCH v6 00/12] Rockchip VOP2 support Stefan Monnier
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Now that we have VOP2 support, let's enable it and support the
built in USB keyboard.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 configs/pinetab2-rk3566_defconfig | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/configs/pinetab2-rk3566_defconfig b/configs/pinetab2-rk3566_defconfig
index 6e5fde591d0..3b8441e3552 100644
--- a/configs/pinetab2-rk3566_defconfig
+++ b/configs/pinetab2-rk3566_defconfig
@@ -68,6 +68,7 @@ CONFIG_MMC_SDHCI_SDMA=y
 CONFIG_MMC_SDHCI_ROCKCHIP=y
 CONFIG_SPI_FLASH_SFDP_SUPPORT=y
 CONFIG_SPI_FLASH_SILICONKAISER=y
+CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y
 CONFIG_PHY_ROCKCHIP_INNO_USB2=y
 CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
 CONFIG_SPL_PINCTRL=y
@@ -93,6 +94,7 @@ CONFIG_USB_OHCI_HCD=y
 CONFIG_USB_OHCI_GENERIC=y
 CONFIG_USB_DWC3=y
 CONFIG_USB_DWC3_GENERIC=y
+CONFIG_USB_KEYBOARD=y
 CONFIG_USB_HOST_ETHER=y
 CONFIG_USB_ETHER_ASIX=y
 CONFIG_USB_ETHER_ASIX88179=y
@@ -100,4 +102,12 @@ CONFIG_USB_ETHER_RTL8152=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DOWNLOAD=y
 CONFIG_USB_FUNCTION_ROCKUSB=y
+CONFIG_VIDEO=y
+CONFIG_VIDEO_LCD_BOE_TH101MB31IG002_28A=y
+CONFIG_DISPLAY=y
+CONFIG_VIDEO_ROCKCHIP=y
+CONFIG_DISPLAY_ROCKCHIP_HDMI=y
+CONFIG_DISPLAY_ROCKCHIP_DW_MIPI=y
+CONFIG_VIDEO_BRIDGE=y
+CONFIG_VIDEO_REMOVE=y
 CONFIG_ERRNO_STR=y

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH v6 12/12] clk: rockchip: rk3568: Use assigned VPLL clock when possible
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (10 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 11/12] configs: pinetab2-rk3566: Enable video and USB keyboard Dang Huynh via B4 Relay
@ 2025-11-08  5:38 ` Dang Huynh via B4 Relay
  2026-01-08 22:14 ` [PATCH v6 00/12] Rockchip VOP2 support Stefan Monnier
  12 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-11-08  5:38 UTC (permalink / raw)
  To: u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

This matches how VPLL is configured under Linux and avoid weird
behaviors when VPLL is reconfigured under Linux.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/clk/rockchip/clk_rk3568.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk_rk3568.c b/drivers/clk/rockchip/clk_rk3568.c
index 533031caead..cd0e24d77dc 100644
--- a/drivers/clk/rockchip/clk_rk3568.c
+++ b/drivers/clk/rockchip/clk_rk3568.c
@@ -1820,7 +1820,11 @@ static ulong rk3568_dclk_vop_set_clk(struct rk3568_clk_priv *priv,
 			     ((div - 1) << DCLK0_VOP_DIV_SHIFT));
 		rk3568_pmu_pll_set_rate(priv, HPLL, div * rate);
 	} else if (sel == DCLK_VOP_SEL_VPLL) {
-		div = DIV_ROUND_UP(RK3568_VOP_PLL_LIMIT_FREQ, rate);
+		if (priv->vpll_hz)
+			div = DIV_ROUND_UP(priv->vpll_hz, rate);
+		else
+			div = DIV_ROUND_UP(RK3568_VOP_PLL_LIMIT_FREQ, rate);
+
 		rk_clrsetreg(&cru->clksel_con[conid],
 			     DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK,
 			     (DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT) |

-- 
2.51.2



^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-11-08  5:38 ` [PATCH v6 06/12] video: rockchip: Add VOP2 support Dang Huynh via B4 Relay
@ 2025-11-10  3:24   ` Chaoyi Chen
  2025-12-18  4:47     ` Dang Huynh
  0 siblings, 1 reply; 25+ messages in thread
From: Chaoyi Chen @ 2025-11-10  3:24 UTC (permalink / raw)
  To: dang.huynh, u-boot
  Cc: Anatolij Gustschin, Simon Glass, Philipp Tomsich, Kever Yang,
	Tom Rini, Svyatoslav Ryhel, Alexander Graf, Alper Nebi Yasak,
	Ondrej Jirman, Ion Agorria, Dario Binacchi, Dragan Simic,
	Patrice Chotard, Peter Robinson, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:

> From: Dang Huynh <dang.huynh@mainlining.org>
>
> VOP2 (Video Output Processor v2) is a display controller on Rockchip
> SoCs. It can be found on RK3566/8 and RK3588.
>
> This commit currently only supports RK3566/8.
>
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
>   drivers/video/rockchip/Makefile                 |   3 +-
>   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
>   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
>   drivers/video/rockchip/rk_vop2.h                |  76 ++++
>   5 files changed, 1138 insertions(+), 1 deletion(-)

[...]

> +
> +/*
> + * RK3566 datasheet omits the VP2, even though it exist in the hardware
> + * so let's not use it.
> + */
> +struct rkvop2_platdata rk3566_platdata = {
> +	.delay = 20,
> +	.bg_dly = {42, 40, -1},
> +	/* SMART0, ESMART0 */
> +	.vp_lyr = {3, 2, -1},

For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".

You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.


> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
> +};
> +
> +struct rkvop2_platdata rk3568_platdata = {
> +	.delay = 20,
> +	.bg_dly = {42, 40, 40},
> +	/* SMART0, SMART1, ESMART1 */
> +	.vp_lyr = {3, 7, 6},
> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
> +};
> +
> +struct rkvop2_driverdata rk3566_driverdata = {
> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> +	.set_pin_polarity = rk3568_set_pin_polarity,
> +	.enable_output = rk3568_enable_output,
> +	.platdata = &rk3566_platdata,
> +};
> +
> +struct rkvop2_driverdata rk3568_driverdata = {
> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> +	.set_pin_polarity = rk3568_set_pin_polarity,
> +	.enable_output = rk3568_enable_output,
> +	.platdata = &rk3568_platdata,
> +};
> +
> +static const struct udevice_id rk3568_vop_ids[] = {
> +	{ .compatible = "rockchip,rk3566-vop",
> +	  .data = (ulong)&rk3566_driverdata },
> +	{ .compatible = "rockchip,rk3568-vop",
> +	  .data = (ulong)&rk3568_driverdata },
> +	{ }
> +};
> +
> +static const struct video_ops rk3568_vop_ops = {
> +};
> +
> +U_BOOT_DRIVER(rk3568_vop) = {
> +	.name	= "rk3568_vop",
> +	.id	= UCLASS_VIDEO,
> +	.of_match = rk3568_vop_ids,
> +	.ops	= &rk3568_vop_ops,
> +	.bind	= rk_vop2_bind,
> +	.probe	= rk3568_vop_probe,
> +	.remove = rk3568_vop_remove,
> +	.priv_auto = sizeof(struct rk_vop2_priv),
> +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
> +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
> +#else
> +	.flags = DM_FLAG_PRE_RELOC,
> +#endif
> +};
> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> new file mode 100644
> index 00000000000..992f215d416
> --- /dev/null
> +++ b/drivers/video/rockchip/rk_vop2.c
> @@ -0,0 +1,520 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
> + *
> + * Based on rk_vop.c:
> + *   Copyright (c) 2015 Google, Inc
> + *   Copyright 2014 Rockchip Inc.
> + */
> +
> +#include <clk.h>
> +#include <display.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <edid.h>
> +#include <log.h>
> +#include <regmap.h>
> +#include <reset.h>
> +#include <syscon.h>
> +#include <video.h>
> +#include <asm/global_data.h>
> +#include <asm/gpio.h>
> +#include <asm/io.h>
> +#include <asm/arch-rockchip/clock.h>
> +#include <asm/arch-rockchip/vop_rk3568.h>
> +#include <dm/device-internal.h>
> +#include <dm/uclass-internal.h>
> +#include <efi.h>
> +#include <efi_loader.h>
> +#include <linux/bitops.h>
> +#include <linux/err.h>
> +#include <power/regulator.h>
> +
> +#include "rk_vop2.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +enum vop_pol {
> +	HSYNC_POSITIVE = 0,
> +	VSYNC_POSITIVE = 1,
> +	DEN_NEGATIVE   = 2,
> +	DCLK_INVERT    = 3
> +};
> +
> +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
> +{
> +	u32 reg;
> +
> +	reg = M_GLOBAL_REGDONE;
> +
> +	/*
> +	 * For RK3588, changes will only take effect when the same bit is
> +	 * leftshifted by 16.
> +	 */
> +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
> +
> +	writel(reg, &sysctrl->reg_cfg_done);
> +}
> +
> +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
> +			 int fb_bits_per_pixel, const struct display_timing *edid,
> +			 int port, int win_id, struct rkvop2_platdata *platdata)
> +{
> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
> +	struct rk3568_vop_esmart *esmart;
> +	bool is_cluster = false;
> +	u8 layer;
> +	u32 reg;
> +	u32 rgb_mode;
> +	u32 hactive = edid->hactive.typ;
> +	u32 vactive = edid->vactive.typ;
> +
> +	if (platdata->layers[win_id] < 0)
> +		return -EINVAL;
> +
> +	switch (platdata->layers[win_id]) {
> +	case ROCKCHIP_VOP2_CLUSTER0:
> +	case ROCKCHIP_VOP2_CLUSTER1:
> +	case ROCKCHIP_VOP2_CLUSTER2:
> +	case ROCKCHIP_VOP2_CLUSTER3:
> +		is_cluster = true;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	layer = platdata->layers[win_id];
> +
> +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
> +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
> +
> +	/* TODO: Support VOP2 CLUSTER */
> +	if (is_cluster) {
> +		dev_err(dev, "win_id is a cluster, not supported.\n");
> +		return -ENOSYS;
> +	}
> +
> +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
> +
> +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
> +
> +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
> +	       &esmart->esmart_region0_act_info);
> +
> +	/* Set offset to 0,0 */
> +	writel(0, &esmart->esmart_region0_dsp_offset);
> +
> +	writel(V_DSP_WIDTH(hactive - 1) |
> +			V_DSP_HEIGHT(vactive - 1),
> +			&esmart->esmart_region0_dsp_info);
> +
> +	switch (fb_bits_per_pixel) {
> +	case 16:
> +		rgb_mode = RGB565;
> +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> +		break;
> +	case 24:
> +		rgb_mode = RGB888;
> +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> +		break;
> +	case 32:
> +	default:
> +		rgb_mode = ARGB8888;
> +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> +		break;
> +	}
> +
> +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
> +
> +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
> +	       &esmart->esmart_region0_mst_ctl);
> +
> +	/* Set esmart to the destination video port */
> +	reg = V_ESMART_SEL_PORT(layer - 4, port);
> +
> +	/*
> +	 * VOP2 requires every port mux to be configured.
> +	 *
> +	 * As U-Boot only supports singledisplay, we'll set all
> +	 * unused ports to set layer to 8 (disabled).
> +	 */
> +	for (int i = 0; i < 4; i++) {
> +		if (i != port)
> +			reg |= V_PORT_MUX(8, i);
> +	}
> +
> +	writel(reg, &overlay->port_sel);
> +
> +	/* Set layer 0 to win_id */
> +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
> +
> +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
> +	writel(reg, &overlay->overlay_ctrl);
> +
> +	priv->layer = layer;
> +
> +	return 0;
> +}
> +
> +static void rkvop2_set_pin_polarity(struct udevice *dev,
> +				    enum vop_modes mode, u32 polarity)
> +{
> +	struct rkvop2_driverdata *ops =
> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> +
> +	if (ops->set_pin_polarity)
> +		ops->set_pin_polarity(dev, mode, polarity);
> +}
> +
> +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
> +{
> +	struct rkvop2_driverdata *ops =
> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> +
> +	if (ops->enable_output)
> +		ops->enable_output(dev, mode, port);
> +}
> +
> +static void rkvop2_mode_set(struct udevice *dev,
> +			    const struct display_timing *edid,
> +		enum vop_modes mode, int port,
> +		struct rkvop2_platdata *platdata)
> +{
> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
> +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
> +	struct rkvop2_driverdata *data =
> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> +
> +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
> +
> +	u32 hactive = edid->hactive.typ;
> +	u32 vactive = edid->vactive.typ;
> +	u32 hsync_len = edid->hsync_len.typ;
> +	u32 hback_porch = edid->hback_porch.typ;
> +	u32 vsync_len = edid->vsync_len.typ;
> +	u32 vback_porch = edid->vback_porch.typ;
> +	u32 hfront_porch = edid->hfront_porch.typ;
> +	u32 vfront_porch = edid->vfront_porch.typ;
> +	int mode_flags;
> +	u32 pin_polarity;
> +	u32 reg;
> +
> +	pin_polarity = BIT(DCLK_INVERT);
> +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
> +		pin_polarity |= BIT(HSYNC_POSITIVE);
> +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> +		pin_polarity |= BIT(VSYNC_POSITIVE);
> +
> +	rkvop2_enable_output(dev, mode, port);
> +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
> +
> +	mode_flags = 0;  /* RGB888 */
> +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
> +	    mode == VOP_MODE_HDMI)
> +		mode_flags = 15;  /* RGBaaa */

You should also set RGBaaa for VOP_MODE_EDP.


> +
> +	reg = V_DSP_OUT_MODE(mode_flags);
> +
> +	debug("(%s, %s): bg_dly: %d\n",
> +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
> +
> +	if (platdata->bg_dly[port] < 0) {
> +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
> +		return;
> +	}
> +
> +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
> +	       &post->prescan_htimings);
> +
> +	writel(V_HSYNC(hsync_len) |
> +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
> +			&post->dsp_htotal_hs_end);
> +
> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> +			V_HASP(hsync_len + hback_porch),
> +			&post->dsp_hact_st_end);
> +
> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> +			V_VASP(vsync_len + vback_porch),
> +			&post->dsp_vact_st_end);
> +
> +	writel(V_VSYNC(vsync_len) |
> +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
> +			&post->dsp_vtotal_vs_end);
> +
> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> +			V_HASP(hsync_len + hback_porch),
> +			&post->dsp_hact_info);
> +
> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> +			V_VASP(vsync_len + vback_porch),
> +			&post->dsp_vact_info);
> +
> +	/* No scaling */
> +	writel(0x10001000, &post->scl_factor_yrgb);
> +
> +	writel(reg, &post->dsp_ctrl);
> +
> +	rkvop2_cfg_regdone(sysctrl, port);
> +}
> +
> +/**
> + * rk_display_init() - Try to enable the given display device
> + *
> + * This function performs many steps:
> + * - Finds the display device being referenced by @ep_node
> + * - Puts the VOP's ID into its uclass platform data
> + * - Probes the device to set it up
> + * - Reads the timing information (from EDID or panel)
> + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
> + * - Enables the display (the display device handles this and will do different
> + *     things depending on the display type)
> + * - Tells the uclass about the display resolution so that the console will
> + *     appear correctly
> + *
> + * @dev:	VOP device that we want to connect to the display
> + * @fbbase:	Frame buffer address
> + * @vp_node:	Device tree node to process
> + * Return: 0 if OK, -ve if something went wrong
> + */
> +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> +{
> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> +	struct rkvop2_driverdata *drvdata =
> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> +	struct rkvop2_platdata *platdata =
> +		(struct rkvop2_platdata *)drvdata->platdata;
> +	ofnode ep_node;
> +	int vop_id, port_id, win_id;
> +	struct display_timing timing;
> +	struct udevice *disp;
> +	int ret;
> +	u32 remote_phandle;
> +	struct display_plat *disp_uc_plat;
> +	enum video_log2_bpp l2bpp;
> +	ofnode remote;
> +	const char *compat;
> +	char dclk_name[9];
> +	struct clk dclk;
> +
> +	debug("%s(%s, 0x%lx, %s)\n", __func__,
> +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
> +
> +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
> +	if (port_id < 0) {
> +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
> +		return port_id;
> +	}
> +
> +	ep_node = ofnode_first_subnode(vp_node);
> +	if (!ofnode_valid(ep_node)) {
> +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
> +		return -EINVAL;
> +	}
> +
> +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> +	if (ret) {
> +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
> +		return ret;
> +	}
> +
> +	remote = ofnode_get_by_phandle(remote_phandle);
> +	if (!ofnode_valid(remote))
> +		return -EINVAL;
> +
> +	remote = ofnode_get_parent(remote);
> +	if (!ofnode_valid(remote))
> +		return -EINVAL;
> +
> +	/*
> +	 * The remote-endpoint references into a subnode of the encoder
> +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
> +	 * the following:
> +	 *
> +	 * hdmi: hdmi@fe0a0000 {
> +	 *   ports {
> +	 *     hdmi_in: port {
> +	 *       hdmi_in_vp0: endpoint { ... };
> +	 *     }
> +	 *   }
> +	 * }
> +	 *
> +	 * This isn't any different from how VOP1 works, so we'll adapt
> +	 * the same method of finding the display from the original code
> +	 * (find the enclosing device of "UCLASS_DISPLAY")
> +	 *
> +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
> +	 * DW MIPI DSI driver for Rockchip.
> +	 */
> +	while (ofnode_valid(remote)) {
> +		remote = ofnode_get_parent(remote);
> +		if (!ofnode_valid(remote)) {
> +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
> +			      __func__, dev_read_name(dev));
> +			return -EINVAL;
> +		}
> +
> +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> +		if (disp)
> +			break;
> +	};
> +	compat = ofnode_get_property(remote, "compatible", NULL);
> +	if (!compat) {
> +		debug("%s(%s): Failed to find compatible property\n",
> +		      __func__, dev_read_name(dev));
> +		return -EINVAL;
> +	}
> +	if (strstr(compat, "edp")) {
> +		vop_id = VOP_MODE_EDP;
> +	} else if (strstr(compat, "mipi")) {
> +		vop_id = VOP_MODE_MIPI;
> +	} else if (strstr(compat, "hdmi")) {
> +		vop_id = VOP_MODE_HDMI;
> +	} else if (strstr(compat, "rk3588-dp")) {

Can we directly use "dp" ?


> +		vop_id = VOP_MODE_DP;
> +	} else if (strstr(compat, "lvds")) {
> +		vop_id = VOP_MODE_LVDS;
> +	} else {
> +		debug("%s(%s): Failed to find vop mode for %s\n",
> +		      __func__, dev_read_name(dev), compat);
> +		return -EINVAL;
> +	}
> +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
> +
> +	/* Get the video port clock and enable it */
> +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
> +	ret = clk_get_by_name(dev, dclk_name, &dclk);
> +	if (ret < 0)
> +		return ret;
> +
> +	disp_uc_plat = dev_get_uclass_plat(disp);
> +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> +	if (display_in_use(disp)) {
> +		debug("   - device in use\n");
> +		return -EBUSY;
> +	}
> +
> +	disp_uc_plat->source_id = vop_id;
> +	disp_uc_plat->src_dev = dev;
> +
> +	ret = device_probe(disp);
> +	if (ret) {
> +		debug("%s: device '%s' display won't probe (ret=%d)\n",
> +		      __func__, dev->name, ret);
> +		return ret;
> +	}
> +
> +	ret = display_read_timing(disp, &timing);
> +	if (ret) {
> +		debug("%s: Failed to read timings\n", __func__);
> +		return ret;
> +	}
> +
> +	/* Set clock rate on video port to display timings */
> +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
> +		return ret;
> +	}
> +
> +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
> +	      dclk_name, clk_get_rate(&dclk));
> +
> +	/* Set bitwidth for vop display according to vop mode */
> +	switch (vop_id) {
> +	case VOP_MODE_EDP:
> +	case VOP_MODE_MIPI:
> +	case VOP_MODE_HDMI:
> +	case VOP_MODE_DP:
> +	case VOP_MODE_LVDS:
> +		l2bpp = VIDEO_BPP32;
> +		break;
> +	default:
> +		l2bpp = VIDEO_BPP16;

It seems that we always end up with VIDEO_BPP32?


> +	}
> +
> +	/*
> +	 * We'll use the default platform-specific win_id from Linux
> +	 * so that Linux can take over U-Boot plane when Linux reconfigures
> +	 * VOP2.
> +	 */
> +	win_id = platdata->vp_lyr[port_id];
> +	if (win_id < 0) {
> +		dev_err(dev, "win_id is null, don't setup\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
> +	if (ret < 0)
> +		return ret;
> +
> +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> +
> +	ret = display_enable(disp, 1 << l2bpp, &timing);
> +	if (ret)
> +		return ret;
> +
> +	uc_priv->xsize = timing.hactive.typ;
> +	uc_priv->ysize = timing.vactive.typ;
> +	uc_priv->bpix = l2bpp;
> +
> +	priv->vp = port_id;
> +
> +	debug("fb=%lx, size=%d %d\n", fbbase,
> +	      uc_priv->xsize, uc_priv->ysize);
> +
> +	return 0;
> +}
> +
> +int rk_vop2_probe(struct udevice *dev)
> +{
> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> +	int ret = 0;
> +	ofnode port, node;
> +
> +	/* Before relocation we don't need to do anything */
> +	if (!(gd->flags & GD_FLG_RELOC))
> +		return 0;
> +
> +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
> +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
> +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
> +	}
> +
> +	priv->regs = dev_read_addr_ptr(dev);
> +
> +	/* Try all the ports until we find one that works. */
> +	port = dev_read_subnode(dev, "ports");
> +	if (!ofnode_valid(port)) {
> +		debug("%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 = rk_display_init(dev, plat->base, node);
> +		if (ret)
> +			debug("Device failed: ret=%d\n", ret);
> +		if (!ret)
> +			break;
> +	}
> +	video_set_flush_dcache(dev, true);
> +
> +	return ret;
> +}
> +
> +int rk_vop2_bind(struct udevice *dev)
> +{
> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> +
> +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
> +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
> +
> +	return 0;
> +}
> diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
> new file mode 100644
> index 00000000000..5d668070014
> --- /dev/null
> +++ b/drivers/video/rockchip/rk_vop2.h
> @@ -0,0 +1,76 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> + */
> +
> +#ifndef __RK_VOP2_H__
> +#define __RK_VOP2_H__
> +
> +#include <asm/arch-rockchip/vop_rk3568.h>
> +
> +struct rk_vop2_priv {
> +	void *grf;
> +	void *regs;
> +	int vp;
> +	int layer;
> +};
> +
> +enum vop2_features {
> +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
> +};
> +
> +enum vop2_layer {
> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> +	ROCKCHIP_VOP2_CLUSTER1,
> +	ROCKCHIP_VOP2_CLUSTER2,
> +	ROCKCHIP_VOP2_CLUSTER3,
> +	ROCKCHIP_VOP2_ESMART0,
> +	ROCKCHIP_VOP2_ESMART1,
> +	ROCKCHIP_VOP2_ESMART2,
> +	ROCKCHIP_VOP2_ESMART3,
> +	ROCKCHIP_VOP2_SMART0 = 6,

Why is it necessary to assign a value?


> +	ROCKCHIP_VOP2_SMART1,
> +	ROCKCHIP_VOP2_RESERVED = -1,
> +};
> +
>
-- 
Best,
Chaoyi


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-11-10  3:24   ` Chaoyi Chen
@ 2025-12-18  4:47     ` Dang Huynh
  2025-12-23  1:34       ` Chaoyi Chen
  0 siblings, 1 reply; 25+ messages in thread
From: Dang Huynh @ 2025-12-18  4:47 UTC (permalink / raw)
  To: Chaoyi Chen
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Peter Robinson, Miquel Raynal,
	Jonas Karlman, Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

On Mon, Nov 10, 2025 at 11:24:01AM +0800, Chaoyi Chen wrote:
> On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:
> 
> > From: Dang Huynh <dang.huynh@mainlining.org>
> > 
> > VOP2 (Video Output Processor v2) is a display controller on Rockchip
> > SoCs. It can be found on RK3566/8 and RK3588.
> > 
> > This commit currently only supports RK3566/8.
> > 
> > Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> > ---
> >   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
> >   drivers/video/rockchip/Makefile                 |   3 +-
> >   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
> >   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
> >   drivers/video/rockchip/rk_vop2.h                |  76 ++++
> >   5 files changed, 1138 insertions(+), 1 deletion(-)
> 
> [...]
> 
> > +
> > +/*
> > + * RK3566 datasheet omits the VP2, even though it exist in the hardware
> > + * so let's not use it.
> > + */
> > +struct rkvop2_platdata rk3566_platdata = {
> > +	.delay = 20,
> > +	.bg_dly = {42, 40, -1},
> > +	/* SMART0, ESMART0 */
> > +	.vp_lyr = {3, 2, -1},
> 
> For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".
> 
> You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.
> 
You can use ESMART0 and SMART0 at the same time? That's how Linux 6.18 would configure VOP for both VP0 and VP1.

> 
> > +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
> > +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> > +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> > +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
> > +};
> > +
> > +struct rkvop2_platdata rk3568_platdata = {
> > +	.delay = 20,
> > +	.bg_dly = {42, 40, 40},
> > +	/* SMART0, SMART1, ESMART1 */
> > +	.vp_lyr = {3, 7, 6},
> > +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
> > +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> > +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> > +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
> > +};
> > +
> > +struct rkvop2_driverdata rk3566_driverdata = {
> > +	.features = VOP_FEATURE_OUTPUT_10BIT,
> > +	.set_pin_polarity = rk3568_set_pin_polarity,
> > +	.enable_output = rk3568_enable_output,
> > +	.platdata = &rk3566_platdata,
> > +};
> > +
> > +struct rkvop2_driverdata rk3568_driverdata = {
> > +	.features = VOP_FEATURE_OUTPUT_10BIT,
> > +	.set_pin_polarity = rk3568_set_pin_polarity,
> > +	.enable_output = rk3568_enable_output,
> > +	.platdata = &rk3568_platdata,
> > +};
> > +
> > +static const struct udevice_id rk3568_vop_ids[] = {
> > +	{ .compatible = "rockchip,rk3566-vop",
> > +	  .data = (ulong)&rk3566_driverdata },
> > +	{ .compatible = "rockchip,rk3568-vop",
> > +	  .data = (ulong)&rk3568_driverdata },
> > +	{ }
> > +};
> > +
> > +static const struct video_ops rk3568_vop_ops = {
> > +};
> > +
> > +U_BOOT_DRIVER(rk3568_vop) = {
> > +	.name	= "rk3568_vop",
> > +	.id	= UCLASS_VIDEO,
> > +	.of_match = rk3568_vop_ids,
> > +	.ops	= &rk3568_vop_ops,
> > +	.bind	= rk_vop2_bind,
> > +	.probe	= rk3568_vop_probe,
> > +	.remove = rk3568_vop_remove,
> > +	.priv_auto = sizeof(struct rk_vop2_priv),
> > +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
> > +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
> > +#else
> > +	.flags = DM_FLAG_PRE_RELOC,
> > +#endif
> > +};
> > diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> > new file mode 100644
> > index 00000000000..992f215d416
> > --- /dev/null
> > +++ b/drivers/video/rockchip/rk_vop2.c
> > @@ -0,0 +1,520 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
> > + *
> > + * Based on rk_vop.c:
> > + *   Copyright (c) 2015 Google, Inc
> > + *   Copyright 2014 Rockchip Inc.
> > + */
> > +
> > +#include <clk.h>
> > +#include <display.h>
> > +#include <dm.h>
> > +#include <dm/device_compat.h>
> > +#include <edid.h>
> > +#include <log.h>
> > +#include <regmap.h>
> > +#include <reset.h>
> > +#include <syscon.h>
> > +#include <video.h>
> > +#include <asm/global_data.h>
> > +#include <asm/gpio.h>
> > +#include <asm/io.h>
> > +#include <asm/arch-rockchip/clock.h>
> > +#include <asm/arch-rockchip/vop_rk3568.h>
> > +#include <dm/device-internal.h>
> > +#include <dm/uclass-internal.h>
> > +#include <efi.h>
> > +#include <efi_loader.h>
> > +#include <linux/bitops.h>
> > +#include <linux/err.h>
> > +#include <power/regulator.h>
> > +
> > +#include "rk_vop2.h"
> > +
> > +DECLARE_GLOBAL_DATA_PTR;
> > +
> > +enum vop_pol {
> > +	HSYNC_POSITIVE = 0,
> > +	VSYNC_POSITIVE = 1,
> > +	DEN_NEGATIVE   = 2,
> > +	DCLK_INVERT    = 3
> > +};
> > +
> > +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
> > +{
> > +	u32 reg;
> > +
> > +	reg = M_GLOBAL_REGDONE;
> > +
> > +	/*
> > +	 * For RK3588, changes will only take effect when the same bit is
> > +	 * leftshifted by 16.
> > +	 */
> > +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
> > +
> > +	writel(reg, &sysctrl->reg_cfg_done);
> > +}
> > +
> > +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
> > +			 int fb_bits_per_pixel, const struct display_timing *edid,
> > +			 int port, int win_id, struct rkvop2_platdata *platdata)
> > +{
> > +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> > +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
> > +	struct rk3568_vop_esmart *esmart;
> > +	bool is_cluster = false;
> > +	u8 layer;
> > +	u32 reg;
> > +	u32 rgb_mode;
> > +	u32 hactive = edid->hactive.typ;
> > +	u32 vactive = edid->vactive.typ;
> > +
> > +	if (platdata->layers[win_id] < 0)
> > +		return -EINVAL;
> > +
> > +	switch (platdata->layers[win_id]) {
> > +	case ROCKCHIP_VOP2_CLUSTER0:
> > +	case ROCKCHIP_VOP2_CLUSTER1:
> > +	case ROCKCHIP_VOP2_CLUSTER2:
> > +	case ROCKCHIP_VOP2_CLUSTER3:
> > +		is_cluster = true;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	layer = platdata->layers[win_id];
> > +
> > +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
> > +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
> > +
> > +	/* TODO: Support VOP2 CLUSTER */
> > +	if (is_cluster) {
> > +		dev_err(dev, "win_id is a cluster, not supported.\n");
> > +		return -ENOSYS;
> > +	}
> > +
> > +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
> > +
> > +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
> > +
> > +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
> > +	       &esmart->esmart_region0_act_info);
> > +
> > +	/* Set offset to 0,0 */
> > +	writel(0, &esmart->esmart_region0_dsp_offset);
> > +
> > +	writel(V_DSP_WIDTH(hactive - 1) |
> > +			V_DSP_HEIGHT(vactive - 1),
> > +			&esmart->esmart_region0_dsp_info);
> > +
> > +	switch (fb_bits_per_pixel) {
> > +	case 16:
> > +		rgb_mode = RGB565;
> > +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> > +		break;
> > +	case 24:
> > +		rgb_mode = RGB888;
> > +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> > +		break;
> > +	case 32:
> > +	default:
> > +		rgb_mode = ARGB8888;
> > +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> > +		break;
> > +	}
> > +
> > +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
> > +
> > +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
> > +	       &esmart->esmart_region0_mst_ctl);
> > +
> > +	/* Set esmart to the destination video port */
> > +	reg = V_ESMART_SEL_PORT(layer - 4, port);
> > +
> > +	/*
> > +	 * VOP2 requires every port mux to be configured.
> > +	 *
> > +	 * As U-Boot only supports singledisplay, we'll set all
> > +	 * unused ports to set layer to 8 (disabled).
> > +	 */
> > +	for (int i = 0; i < 4; i++) {
> > +		if (i != port)
> > +			reg |= V_PORT_MUX(8, i);
> > +	}
> > +
> > +	writel(reg, &overlay->port_sel);
> > +
> > +	/* Set layer 0 to win_id */
> > +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
> > +
> > +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
> > +	writel(reg, &overlay->overlay_ctrl);
> > +
> > +	priv->layer = layer;
> > +
> > +	return 0;
> > +}
> > +
> > +static void rkvop2_set_pin_polarity(struct udevice *dev,
> > +				    enum vop_modes mode, u32 polarity)
> > +{
> > +	struct rkvop2_driverdata *ops =
> > +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> > +
> > +	if (ops->set_pin_polarity)
> > +		ops->set_pin_polarity(dev, mode, polarity);
> > +}
> > +
> > +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
> > +{
> > +	struct rkvop2_driverdata *ops =
> > +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> > +
> > +	if (ops->enable_output)
> > +		ops->enable_output(dev, mode, port);
> > +}
> > +
> > +static void rkvop2_mode_set(struct udevice *dev,
> > +			    const struct display_timing *edid,
> > +		enum vop_modes mode, int port,
> > +		struct rkvop2_platdata *platdata)
> > +{
> > +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> > +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
> > +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
> > +	struct rkvop2_driverdata *data =
> > +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> > +
> > +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
> > +
> > +	u32 hactive = edid->hactive.typ;
> > +	u32 vactive = edid->vactive.typ;
> > +	u32 hsync_len = edid->hsync_len.typ;
> > +	u32 hback_porch = edid->hback_porch.typ;
> > +	u32 vsync_len = edid->vsync_len.typ;
> > +	u32 vback_porch = edid->vback_porch.typ;
> > +	u32 hfront_porch = edid->hfront_porch.typ;
> > +	u32 vfront_porch = edid->vfront_porch.typ;
> > +	int mode_flags;
> > +	u32 pin_polarity;
> > +	u32 reg;
> > +
> > +	pin_polarity = BIT(DCLK_INVERT);
> > +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
> > +		pin_polarity |= BIT(HSYNC_POSITIVE);
> > +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> > +		pin_polarity |= BIT(VSYNC_POSITIVE);
> > +
> > +	rkvop2_enable_output(dev, mode, port);
> > +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
> > +
> > +	mode_flags = 0;  /* RGB888 */
> > +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
> > +	    mode == VOP_MODE_HDMI)
> > +		mode_flags = 15;  /* RGBaaa */
> 
> You should also set RGBaaa for VOP_MODE_EDP.
> 
Will do in next series.

> 
> > +
> > +	reg = V_DSP_OUT_MODE(mode_flags);
> > +
> > +	debug("(%s, %s): bg_dly: %d\n",
> > +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
> > +
> > +	if (platdata->bg_dly[port] < 0) {
> > +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
> > +		return;
> > +	}
> > +
> > +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
> > +	       &post->prescan_htimings);
> > +
> > +	writel(V_HSYNC(hsync_len) |
> > +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
> > +			&post->dsp_htotal_hs_end);
> > +
> > +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> > +			V_HASP(hsync_len + hback_porch),
> > +			&post->dsp_hact_st_end);
> > +
> > +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> > +			V_VASP(vsync_len + vback_porch),
> > +			&post->dsp_vact_st_end);
> > +
> > +	writel(V_VSYNC(vsync_len) |
> > +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
> > +			&post->dsp_vtotal_vs_end);
> > +
> > +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> > +			V_HASP(hsync_len + hback_porch),
> > +			&post->dsp_hact_info);
> > +
> > +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> > +			V_VASP(vsync_len + vback_porch),
> > +			&post->dsp_vact_info);
> > +
> > +	/* No scaling */
> > +	writel(0x10001000, &post->scl_factor_yrgb);
> > +
> > +	writel(reg, &post->dsp_ctrl);
> > +
> > +	rkvop2_cfg_regdone(sysctrl, port);
> > +}
> > +
> > +/**
> > + * rk_display_init() - Try to enable the given display device
> > + *
> > + * This function performs many steps:
> > + * - Finds the display device being referenced by @ep_node
> > + * - Puts the VOP's ID into its uclass platform data
> > + * - Probes the device to set it up
> > + * - Reads the timing information (from EDID or panel)
> > + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
> > + * - Enables the display (the display device handles this and will do different
> > + *     things depending on the display type)
> > + * - Tells the uclass about the display resolution so that the console will
> > + *     appear correctly
> > + *
> > + * @dev:	VOP device that we want to connect to the display
> > + * @fbbase:	Frame buffer address
> > + * @vp_node:	Device tree node to process
> > + * Return: 0 if OK, -ve if something went wrong
> > + */
> > +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> > +{
> > +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> > +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> > +	struct rkvop2_driverdata *drvdata =
> > +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> > +	struct rkvop2_platdata *platdata =
> > +		(struct rkvop2_platdata *)drvdata->platdata;
> > +	ofnode ep_node;
> > +	int vop_id, port_id, win_id;
> > +	struct display_timing timing;
> > +	struct udevice *disp;
> > +	int ret;
> > +	u32 remote_phandle;
> > +	struct display_plat *disp_uc_plat;
> > +	enum video_log2_bpp l2bpp;
> > +	ofnode remote;
> > +	const char *compat;
> > +	char dclk_name[9];
> > +	struct clk dclk;
> > +
> > +	debug("%s(%s, 0x%lx, %s)\n", __func__,
> > +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
> > +
> > +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
> > +	if (port_id < 0) {
> > +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
> > +		return port_id;
> > +	}
> > +
> > +	ep_node = ofnode_first_subnode(vp_node);
> > +	if (!ofnode_valid(ep_node)) {
> > +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> > +	if (ret) {
> > +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
> > +		return ret;
> > +	}
> > +
> > +	remote = ofnode_get_by_phandle(remote_phandle);
> > +	if (!ofnode_valid(remote))
> > +		return -EINVAL;
> > +
> > +	remote = ofnode_get_parent(remote);
> > +	if (!ofnode_valid(remote))
> > +		return -EINVAL;
> > +
> > +	/*
> > +	 * The remote-endpoint references into a subnode of the encoder
> > +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
> > +	 * the following:
> > +	 *
> > +	 * hdmi: hdmi@fe0a0000 {
> > +	 *   ports {
> > +	 *     hdmi_in: port {
> > +	 *       hdmi_in_vp0: endpoint { ... };
> > +	 *     }
> > +	 *   }
> > +	 * }
> > +	 *
> > +	 * This isn't any different from how VOP1 works, so we'll adapt
> > +	 * the same method of finding the display from the original code
> > +	 * (find the enclosing device of "UCLASS_DISPLAY")
> > +	 *
> > +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
> > +	 * DW MIPI DSI driver for Rockchip.
> > +	 */
> > +	while (ofnode_valid(remote)) {
> > +		remote = ofnode_get_parent(remote);
> > +		if (!ofnode_valid(remote)) {
> > +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
> > +			      __func__, dev_read_name(dev));
> > +			return -EINVAL;
> > +		}
> > +
> > +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> > +		if (disp)
> > +			break;
> > +	};
> > +	compat = ofnode_get_property(remote, "compatible", NULL);
> > +	if (!compat) {
> > +		debug("%s(%s): Failed to find compatible property\n",
> > +		      __func__, dev_read_name(dev));
> > +		return -EINVAL;
> > +	}
> > +	if (strstr(compat, "edp")) {
> > +		vop_id = VOP_MODE_EDP;
> > +	} else if (strstr(compat, "mipi")) {
> > +		vop_id = VOP_MODE_MIPI;
> > +	} else if (strstr(compat, "hdmi")) {
> > +		vop_id = VOP_MODE_HDMI;
> > +	} else if (strstr(compat, "rk3588-dp")) {
> 
> Can we directly use "dp" ?
> 
We can, however if the device tree is misconfigured then it'll pick up VP connected devices with "dp" (such
as "rk3588-h*dp*tx-phy") and consider that to be a displayport device.

> 
> > +		vop_id = VOP_MODE_DP;
> > +	} else if (strstr(compat, "lvds")) {
> > +		vop_id = VOP_MODE_LVDS;
> > +	} else {
> > +		debug("%s(%s): Failed to find vop mode for %s\n",
> > +		      __func__, dev_read_name(dev), compat);
> > +		return -EINVAL;
> > +	}
> > +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
> > +
> > +	/* Get the video port clock and enable it */
> > +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
> > +	ret = clk_get_by_name(dev, dclk_name, &dclk);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	disp_uc_plat = dev_get_uclass_plat(disp);
> > +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> > +	if (display_in_use(disp)) {
> > +		debug("   - device in use\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	disp_uc_plat->source_id = vop_id;
> > +	disp_uc_plat->src_dev = dev;
> > +
> > +	ret = device_probe(disp);
> > +	if (ret) {
> > +		debug("%s: device '%s' display won't probe (ret=%d)\n",
> > +		      __func__, dev->name, ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = display_read_timing(disp, &timing);
> > +	if (ret) {
> > +		debug("%s: Failed to read timings\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	/* Set clock rate on video port to display timings */
> > +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
> > +	if (ret < 0) {
> > +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
> > +	      dclk_name, clk_get_rate(&dclk));
> > +
> > +	/* Set bitwidth for vop display according to vop mode */
> > +	switch (vop_id) {
> > +	case VOP_MODE_EDP:
> > +	case VOP_MODE_MIPI:
> > +	case VOP_MODE_HDMI:
> > +	case VOP_MODE_DP:
> > +	case VOP_MODE_LVDS:
> > +		l2bpp = VIDEO_BPP32;
> > +		break;
> > +	default:
> > +		l2bpp = VIDEO_BPP16;
> 
> It seems that we always end up with VIDEO_BPP32?
> 
U-Boot doesn't support VIDEO_BPP24 format.

> 
> > +	}
> > +
> > +	/*
> > +	 * We'll use the default platform-specific win_id from Linux
> > +	 * so that Linux can take over U-Boot plane when Linux reconfigures
> > +	 * VOP2.
> > +	 */
> > +	win_id = platdata->vp_lyr[port_id];
> > +	if (win_id < 0) {
> > +		dev_err(dev, "win_id is null, don't setup\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> > +
> > +	ret = display_enable(disp, 1 << l2bpp, &timing);
> > +	if (ret)
> > +		return ret;
> > +
> > +	uc_priv->xsize = timing.hactive.typ;
> > +	uc_priv->ysize = timing.vactive.typ;
> > +	uc_priv->bpix = l2bpp;
> > +
> > +	priv->vp = port_id;
> > +
> > +	debug("fb=%lx, size=%d %d\n", fbbase,
> > +	      uc_priv->xsize, uc_priv->ysize);
> > +
> > +	return 0;
> > +}
> > +
> > +int rk_vop2_probe(struct udevice *dev)
> > +{
> > +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> > +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> > +	int ret = 0;
> > +	ofnode port, node;
> > +
> > +	/* Before relocation we don't need to do anything */
> > +	if (!(gd->flags & GD_FLG_RELOC))
> > +		return 0;
> > +
> > +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
> > +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
> > +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
> > +	}
> > +
> > +	priv->regs = dev_read_addr_ptr(dev);
> > +
> > +	/* Try all the ports until we find one that works. */
> > +	port = dev_read_subnode(dev, "ports");
> > +	if (!ofnode_valid(port)) {
> > +		debug("%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 = rk_display_init(dev, plat->base, node);
> > +		if (ret)
> > +			debug("Device failed: ret=%d\n", ret);
> > +		if (!ret)
> > +			break;
> > +	}
> > +	video_set_flush_dcache(dev, true);
> > +
> > +	return ret;
> > +}
> > +
> > +int rk_vop2_bind(struct udevice *dev)
> > +{
> > +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> > +
> > +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
> > +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
> > +
> > +	return 0;
> > +}
> > diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
> > new file mode 100644
> > index 00000000000..5d668070014
> > --- /dev/null
> > +++ b/drivers/video/rockchip/rk_vop2.h
> > @@ -0,0 +1,76 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> > + */
> > +
> > +#ifndef __RK_VOP2_H__
> > +#define __RK_VOP2_H__
> > +
> > +#include <asm/arch-rockchip/vop_rk3568.h>
> > +
> > +struct rk_vop2_priv {
> > +	void *grf;
> > +	void *regs;
> > +	int vp;
> > +	int layer;
> > +};
> > +
> > +enum vop2_features {
> > +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
> > +};
> > +
> > +enum vop2_layer {
> > +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> > +	ROCKCHIP_VOP2_CLUSTER1,
> > +	ROCKCHIP_VOP2_CLUSTER2,
> > +	ROCKCHIP_VOP2_CLUSTER3,
> > +	ROCKCHIP_VOP2_ESMART0,
> > +	ROCKCHIP_VOP2_ESMART1,
> > +	ROCKCHIP_VOP2_ESMART2,
> > +	ROCKCHIP_VOP2_ESMART3,
> > +	ROCKCHIP_VOP2_SMART0 = 6,
> 
> Why is it necessary to assign a value?
> 
SMART0/1 shares the same address and map as ESMART2/3.

> 
> > +	ROCKCHIP_VOP2_SMART1,
> > +	ROCKCHIP_VOP2_RESERVED = -1,
> > +};
> > +
> > 
> -- 
> Best,
> Chaoyi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-12-18  4:47     ` Dang Huynh
@ 2025-12-23  1:34       ` Chaoyi Chen
  2025-12-25  6:35         ` Dang Huynh
  0 siblings, 1 reply; 25+ messages in thread
From: Chaoyi Chen @ 2025-12-23  1:34 UTC (permalink / raw)
  To: Dang Huynh
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Peter Robinson, Miquel Raynal,
	Jonas Karlman, Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

Hello Dang,

On 12/18/2025 12:47 PM, Dang Huynh wrote:
> On Mon, Nov 10, 2025 at 11:24:01AM +0800, Chaoyi Chen wrote:
>> On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:
>>
>>> From: Dang Huynh <dang.huynh@mainlining.org>
>>>
>>> VOP2 (Video Output Processor v2) is a display controller on Rockchip
>>> SoCs. It can be found on RK3566/8 and RK3588.
>>>
>>> This commit currently only supports RK3566/8.
>>>
>>> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
>>> ---
>>>   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
>>>   drivers/video/rockchip/Makefile                 |   3 +-
>>>   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
>>>   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
>>>   drivers/video/rockchip/rk_vop2.h                |  76 ++++
>>>   5 files changed, 1138 insertions(+), 1 deletion(-)
>>
>> [...]
>>
>>> +
>>> +/*
>>> + * RK3566 datasheet omits the VP2, even though it exist in the hardware
>>> + * so let's not use it.
>>> + */
>>> +struct rkvop2_platdata rk3566_platdata = {
>>> +	.delay = 20,
>>> +	.bg_dly = {42, 40, -1},
>>> +	/* SMART0, ESMART0 */
>>> +	.vp_lyr = {3, 2, -1},
>>
>> For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".
>>
>> You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.
>>
> You can use ESMART0 and SMART0 at the same time? That's how Linux 6.18 would configure VOP for both VP0 and VP1.
> 

No. I mean you can not use SMART0 for VP0 and ESMART0 for VP1. They
should be same win type.

>>
>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
>>> +};
>>> +
>>> +struct rkvop2_platdata rk3568_platdata = {
>>> +	.delay = 20,
>>> +	.bg_dly = {42, 40, 40},
>>> +	/* SMART0, SMART1, ESMART1 */
>>> +	.vp_lyr = {3, 7, 6},
>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
>>> +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
>>> +};
>>> +
>>> +struct rkvop2_driverdata rk3566_driverdata = {
>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
>>> +	.enable_output = rk3568_enable_output,
>>> +	.platdata = &rk3566_platdata,
>>> +};
>>> +
>>> +struct rkvop2_driverdata rk3568_driverdata = {
>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
>>> +	.enable_output = rk3568_enable_output,
>>> +	.platdata = &rk3568_platdata,
>>> +};
>>> +
>>> +static const struct udevice_id rk3568_vop_ids[] = {
>>> +	{ .compatible = "rockchip,rk3566-vop",
>>> +	  .data = (ulong)&rk3566_driverdata },
>>> +	{ .compatible = "rockchip,rk3568-vop",
>>> +	  .data = (ulong)&rk3568_driverdata },
>>> +	{ }
>>> +};
>>> +
>>> +static const struct video_ops rk3568_vop_ops = {
>>> +};
>>> +
>>> +U_BOOT_DRIVER(rk3568_vop) = {
>>> +	.name	= "rk3568_vop",
>>> +	.id	= UCLASS_VIDEO,
>>> +	.of_match = rk3568_vop_ids,
>>> +	.ops	= &rk3568_vop_ops,
>>> +	.bind	= rk_vop2_bind,
>>> +	.probe	= rk3568_vop_probe,
>>> +	.remove = rk3568_vop_remove,
>>> +	.priv_auto = sizeof(struct rk_vop2_priv),
>>> +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
>>> +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
>>> +#else
>>> +	.flags = DM_FLAG_PRE_RELOC,
>>> +#endif
>>> +};
>>> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
>>> new file mode 100644
>>> index 00000000000..992f215d416
>>> --- /dev/null
>>> +++ b/drivers/video/rockchip/rk_vop2.c
>>> @@ -0,0 +1,520 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
>>> + *
>>> + * Based on rk_vop.c:
>>> + *   Copyright (c) 2015 Google, Inc
>>> + *   Copyright 2014 Rockchip Inc.
>>> + */
>>> +
>>> +#include <clk.h>
>>> +#include <display.h>
>>> +#include <dm.h>
>>> +#include <dm/device_compat.h>
>>> +#include <edid.h>
>>> +#include <log.h>
>>> +#include <regmap.h>
>>> +#include <reset.h>
>>> +#include <syscon.h>
>>> +#include <video.h>
>>> +#include <asm/global_data.h>
>>> +#include <asm/gpio.h>
>>> +#include <asm/io.h>
>>> +#include <asm/arch-rockchip/clock.h>
>>> +#include <asm/arch-rockchip/vop_rk3568.h>
>>> +#include <dm/device-internal.h>
>>> +#include <dm/uclass-internal.h>
>>> +#include <efi.h>
>>> +#include <efi_loader.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/err.h>
>>> +#include <power/regulator.h>
>>> +
>>> +#include "rk_vop2.h"
>>> +
>>> +DECLARE_GLOBAL_DATA_PTR;
>>> +
>>> +enum vop_pol {
>>> +	HSYNC_POSITIVE = 0,
>>> +	VSYNC_POSITIVE = 1,
>>> +	DEN_NEGATIVE   = 2,
>>> +	DCLK_INVERT    = 3
>>> +};
>>> +
>>> +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
>>> +{
>>> +	u32 reg;
>>> +
>>> +	reg = M_GLOBAL_REGDONE;
>>> +
>>> +	/*
>>> +	 * For RK3588, changes will only take effect when the same bit is
>>> +	 * leftshifted by 16.
>>> +	 */
>>> +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
>>> +
>>> +	writel(reg, &sysctrl->reg_cfg_done);
>>> +}
>>> +
>>> +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
>>> +			 int fb_bits_per_pixel, const struct display_timing *edid,
>>> +			 int port, int win_id, struct rkvop2_platdata *platdata)
>>> +{
>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>> +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
>>> +	struct rk3568_vop_esmart *esmart;
>>> +	bool is_cluster = false;
>>> +	u8 layer;
>>> +	u32 reg;
>>> +	u32 rgb_mode;
>>> +	u32 hactive = edid->hactive.typ;
>>> +	u32 vactive = edid->vactive.typ;
>>> +
>>> +	if (platdata->layers[win_id] < 0)
>>> +		return -EINVAL;
>>> +
>>> +	switch (platdata->layers[win_id]) {
>>> +	case ROCKCHIP_VOP2_CLUSTER0:
>>> +	case ROCKCHIP_VOP2_CLUSTER1:
>>> +	case ROCKCHIP_VOP2_CLUSTER2:
>>> +	case ROCKCHIP_VOP2_CLUSTER3:
>>> +		is_cluster = true;
>>> +		break;
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	layer = platdata->layers[win_id];
>>> +
>>> +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
>>> +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
>>> +
>>> +	/* TODO: Support VOP2 CLUSTER */
>>> +	if (is_cluster) {
>>> +		dev_err(dev, "win_id is a cluster, not supported.\n");
>>> +		return -ENOSYS;
>>> +	}
>>> +
>>> +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
>>> +
>>> +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
>>> +
>>> +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
>>> +	       &esmart->esmart_region0_act_info);
>>> +
>>> +	/* Set offset to 0,0 */
>>> +	writel(0, &esmart->esmart_region0_dsp_offset);
>>> +
>>> +	writel(V_DSP_WIDTH(hactive - 1) |
>>> +			V_DSP_HEIGHT(vactive - 1),
>>> +			&esmart->esmart_region0_dsp_info);
>>> +
>>> +	switch (fb_bits_per_pixel) {
>>> +	case 16:
>>> +		rgb_mode = RGB565;
>>> +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>> +		break;
>>> +	case 24:
>>> +		rgb_mode = RGB888;
>>> +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>> +		break;
>>> +	case 32:
>>> +	default:
>>> +		rgb_mode = ARGB8888;
>>> +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>> +		break;
>>> +	}
>>> +
>>> +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
>>> +
>>> +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
>>> +	       &esmart->esmart_region0_mst_ctl);
>>> +
>>> +	/* Set esmart to the destination video port */
>>> +	reg = V_ESMART_SEL_PORT(layer - 4, port);
>>> +
>>> +	/*
>>> +	 * VOP2 requires every port mux to be configured.
>>> +	 *
>>> +	 * As U-Boot only supports singledisplay, we'll set all
>>> +	 * unused ports to set layer to 8 (disabled).
>>> +	 */
>>> +	for (int i = 0; i < 4; i++) {
>>> +		if (i != port)
>>> +			reg |= V_PORT_MUX(8, i);
>>> +	}
>>> +
>>> +	writel(reg, &overlay->port_sel);
>>> +
>>> +	/* Set layer 0 to win_id */
>>> +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
>>> +
>>> +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
>>> +	writel(reg, &overlay->overlay_ctrl);
>>> +
>>> +	priv->layer = layer;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void rkvop2_set_pin_polarity(struct udevice *dev,
>>> +				    enum vop_modes mode, u32 polarity)
>>> +{
>>> +	struct rkvop2_driverdata *ops =
>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>> +
>>> +	if (ops->set_pin_polarity)
>>> +		ops->set_pin_polarity(dev, mode, polarity);
>>> +}
>>> +
>>> +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
>>> +{
>>> +	struct rkvop2_driverdata *ops =
>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>> +
>>> +	if (ops->enable_output)
>>> +		ops->enable_output(dev, mode, port);
>>> +}
>>> +
>>> +static void rkvop2_mode_set(struct udevice *dev,
>>> +			    const struct display_timing *edid,
>>> +		enum vop_modes mode, int port,
>>> +		struct rkvop2_platdata *platdata)
>>> +{
>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>> +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
>>> +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
>>> +	struct rkvop2_driverdata *data =
>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>> +
>>> +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
>>> +
>>> +	u32 hactive = edid->hactive.typ;
>>> +	u32 vactive = edid->vactive.typ;
>>> +	u32 hsync_len = edid->hsync_len.typ;
>>> +	u32 hback_porch = edid->hback_porch.typ;
>>> +	u32 vsync_len = edid->vsync_len.typ;
>>> +	u32 vback_porch = edid->vback_porch.typ;
>>> +	u32 hfront_porch = edid->hfront_porch.typ;
>>> +	u32 vfront_porch = edid->vfront_porch.typ;
>>> +	int mode_flags;
>>> +	u32 pin_polarity;
>>> +	u32 reg;
>>> +
>>> +	pin_polarity = BIT(DCLK_INVERT);
>>> +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
>>> +		pin_polarity |= BIT(HSYNC_POSITIVE);
>>> +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
>>> +		pin_polarity |= BIT(VSYNC_POSITIVE);
>>> +
>>> +	rkvop2_enable_output(dev, mode, port);
>>> +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
>>> +
>>> +	mode_flags = 0;  /* RGB888 */
>>> +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
>>> +	    mode == VOP_MODE_HDMI)
>>> +		mode_flags = 15;  /* RGBaaa */
>>
>> You should also set RGBaaa for VOP_MODE_EDP.
>>
> Will do in next series.
> 
>>
>>> +
>>> +	reg = V_DSP_OUT_MODE(mode_flags);
>>> +
>>> +	debug("(%s, %s): bg_dly: %d\n",
>>> +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
>>> +
>>> +	if (platdata->bg_dly[port] < 0) {
>>> +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
>>> +		return;
>>> +	}
>>> +
>>> +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
>>> +	       &post->prescan_htimings);
>>> +
>>> +	writel(V_HSYNC(hsync_len) |
>>> +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
>>> +			&post->dsp_htotal_hs_end);
>>> +
>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
>>> +			V_HASP(hsync_len + hback_porch),
>>> +			&post->dsp_hact_st_end);
>>> +
>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
>>> +			V_VASP(vsync_len + vback_porch),
>>> +			&post->dsp_vact_st_end);
>>> +
>>> +	writel(V_VSYNC(vsync_len) |
>>> +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
>>> +			&post->dsp_vtotal_vs_end);
>>> +
>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
>>> +			V_HASP(hsync_len + hback_porch),
>>> +			&post->dsp_hact_info);
>>> +
>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
>>> +			V_VASP(vsync_len + vback_porch),
>>> +			&post->dsp_vact_info);
>>> +
>>> +	/* No scaling */
>>> +	writel(0x10001000, &post->scl_factor_yrgb);
>>> +
>>> +	writel(reg, &post->dsp_ctrl);
>>> +
>>> +	rkvop2_cfg_regdone(sysctrl, port);
>>> +}
>>> +
>>> +/**
>>> + * rk_display_init() - Try to enable the given display device
>>> + *
>>> + * This function performs many steps:
>>> + * - Finds the display device being referenced by @ep_node
>>> + * - Puts the VOP's ID into its uclass platform data
>>> + * - Probes the device to set it up
>>> + * - Reads the timing information (from EDID or panel)
>>> + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
>>> + * - Enables the display (the display device handles this and will do different
>>> + *     things depending on the display type)
>>> + * - Tells the uclass about the display resolution so that the console will
>>> + *     appear correctly
>>> + *
>>> + * @dev:	VOP device that we want to connect to the display
>>> + * @fbbase:	Frame buffer address
>>> + * @vp_node:	Device tree node to process
>>> + * Return: 0 if OK, -ve if something went wrong
>>> + */
>>> +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>>> +{
>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>>> +	struct rkvop2_driverdata *drvdata =
>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>> +	struct rkvop2_platdata *platdata =
>>> +		(struct rkvop2_platdata *)drvdata->platdata;
>>> +	ofnode ep_node;
>>> +	int vop_id, port_id, win_id;
>>> +	struct display_timing timing;
>>> +	struct udevice *disp;
>>> +	int ret;
>>> +	u32 remote_phandle;
>>> +	struct display_plat *disp_uc_plat;
>>> +	enum video_log2_bpp l2bpp;
>>> +	ofnode remote;
>>> +	const char *compat;
>>> +	char dclk_name[9];
>>> +	struct clk dclk;
>>> +
>>> +	debug("%s(%s, 0x%lx, %s)\n", __func__,
>>> +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
>>> +
>>> +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
>>> +	if (port_id < 0) {
>>> +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
>>> +		return port_id;
>>> +	}
>>> +
>>> +	ep_node = ofnode_first_subnode(vp_node);
>>> +	if (!ofnode_valid(ep_node)) {
>>> +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
>>> +	if (ret) {
>>> +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
>>> +		return ret;
>>> +	}
>>> +
>>> +	remote = ofnode_get_by_phandle(remote_phandle);
>>> +	if (!ofnode_valid(remote))
>>> +		return -EINVAL;
>>> +
>>> +	remote = ofnode_get_parent(remote);
>>> +	if (!ofnode_valid(remote))
>>> +		return -EINVAL;
>>> +
>>> +	/*
>>> +	 * The remote-endpoint references into a subnode of the encoder
>>> +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
>>> +	 * the following:
>>> +	 *
>>> +	 * hdmi: hdmi@fe0a0000 {
>>> +	 *   ports {
>>> +	 *     hdmi_in: port {
>>> +	 *       hdmi_in_vp0: endpoint { ... };
>>> +	 *     }
>>> +	 *   }
>>> +	 * }
>>> +	 *
>>> +	 * This isn't any different from how VOP1 works, so we'll adapt
>>> +	 * the same method of finding the display from the original code
>>> +	 * (find the enclosing device of "UCLASS_DISPLAY")
>>> +	 *
>>> +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
>>> +	 * DW MIPI DSI driver for Rockchip.
>>> +	 */
>>> +	while (ofnode_valid(remote)) {
>>> +		remote = ofnode_get_parent(remote);
>>> +		if (!ofnode_valid(remote)) {
>>> +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
>>> +			      __func__, dev_read_name(dev));
>>> +			return -EINVAL;
>>> +		}
>>> +
>>> +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
>>> +		if (disp)
>>> +			break;
>>> +	};
>>> +	compat = ofnode_get_property(remote, "compatible", NULL);
>>> +	if (!compat) {
>>> +		debug("%s(%s): Failed to find compatible property\n",
>>> +		      __func__, dev_read_name(dev));
>>> +		return -EINVAL;
>>> +	}
>>> +	if (strstr(compat, "edp")) {
>>> +		vop_id = VOP_MODE_EDP;
>>> +	} else if (strstr(compat, "mipi")) {
>>> +		vop_id = VOP_MODE_MIPI;
>>> +	} else if (strstr(compat, "hdmi")) {
>>> +		vop_id = VOP_MODE_HDMI;
>>> +	} else if (strstr(compat, "rk3588-dp")) {
>>
>> Can we directly use "dp" ?
>>
> We can, however if the device tree is misconfigured then it'll pick up VP connected devices with "dp" (such
> as "rk3588-h*dp*tx-phy") and consider that to be a displayport device.
> 
>>
>>> +		vop_id = VOP_MODE_DP;
>>> +	} else if (strstr(compat, "lvds")) {
>>> +		vop_id = VOP_MODE_LVDS;
>>> +	} else {
>>> +		debug("%s(%s): Failed to find vop mode for %s\n",
>>> +		      __func__, dev_read_name(dev), compat);
>>> +		return -EINVAL;
>>> +	}
>>> +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
>>> +
>>> +	/* Get the video port clock and enable it */
>>> +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
>>> +	ret = clk_get_by_name(dev, dclk_name, &dclk);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	disp_uc_plat = dev_get_uclass_plat(disp);
>>> +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
>>> +	if (display_in_use(disp)) {
>>> +		debug("   - device in use\n");
>>> +		return -EBUSY;
>>> +	}
>>> +
>>> +	disp_uc_plat->source_id = vop_id;
>>> +	disp_uc_plat->src_dev = dev;
>>> +
>>> +	ret = device_probe(disp);
>>> +	if (ret) {
>>> +		debug("%s: device '%s' display won't probe (ret=%d)\n",
>>> +		      __func__, dev->name, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = display_read_timing(disp, &timing);
>>> +	if (ret) {
>>> +		debug("%s: Failed to read timings\n", __func__);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Set clock rate on video port to display timings */
>>> +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
>>> +	      dclk_name, clk_get_rate(&dclk));
>>> +
>>> +	/* Set bitwidth for vop display according to vop mode */
>>> +	switch (vop_id) {
>>> +	case VOP_MODE_EDP:
>>> +	case VOP_MODE_MIPI:
>>> +	case VOP_MODE_HDMI:
>>> +	case VOP_MODE_DP:
>>> +	case VOP_MODE_LVDS:
>>> +		l2bpp = VIDEO_BPP32;
>>> +		break;
>>> +	default:
>>> +		l2bpp = VIDEO_BPP16;
>>
>> It seems that we always end up with VIDEO_BPP32?
>>
> U-Boot doesn't support VIDEO_BPP24 format.
> 
>>
>>> +	}
>>> +
>>> +	/*
>>> +	 * We'll use the default platform-specific win_id from Linux
>>> +	 * so that Linux can take over U-Boot plane when Linux reconfigures
>>> +	 * VOP2.
>>> +	 */
>>> +	win_id = platdata->vp_lyr[port_id];
>>> +	if (win_id < 0) {
>>> +		dev_err(dev, "win_id is null, don't setup\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
>>> +
>>> +	ret = display_enable(disp, 1 << l2bpp, &timing);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	uc_priv->xsize = timing.hactive.typ;
>>> +	uc_priv->ysize = timing.vactive.typ;
>>> +	uc_priv->bpix = l2bpp;
>>> +
>>> +	priv->vp = port_id;
>>> +
>>> +	debug("fb=%lx, size=%d %d\n", fbbase,
>>> +	      uc_priv->xsize, uc_priv->ysize);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int rk_vop2_probe(struct udevice *dev)
>>> +{
>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>> +	int ret = 0;
>>> +	ofnode port, node;
>>> +
>>> +	/* Before relocation we don't need to do anything */
>>> +	if (!(gd->flags & GD_FLG_RELOC))
>>> +		return 0;
>>> +
>>> +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
>>> +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
>>> +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
>>> +	}
>>> +
>>> +	priv->regs = dev_read_addr_ptr(dev);
>>> +
>>> +	/* Try all the ports until we find one that works. */
>>> +	port = dev_read_subnode(dev, "ports");
>>> +	if (!ofnode_valid(port)) {
>>> +		debug("%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 = rk_display_init(dev, plat->base, node);
>>> +		if (ret)
>>> +			debug("Device failed: ret=%d\n", ret);
>>> +		if (!ret)
>>> +			break;
>>> +	}
>>> +	video_set_flush_dcache(dev, true);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +int rk_vop2_bind(struct udevice *dev)
>>> +{
>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>>> +
>>> +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
>>> +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
>>> +
>>> +	return 0;
>>> +}
>>> diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
>>> new file mode 100644
>>> index 00000000000..5d668070014
>>> --- /dev/null
>>> +++ b/drivers/video/rockchip/rk_vop2.h
>>> @@ -0,0 +1,76 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
>>> + */
>>> +
>>> +#ifndef __RK_VOP2_H__
>>> +#define __RK_VOP2_H__
>>> +
>>> +#include <asm/arch-rockchip/vop_rk3568.h>
>>> +
>>> +struct rk_vop2_priv {
>>> +	void *grf;
>>> +	void *regs;
>>> +	int vp;
>>> +	int layer;
>>> +};
>>> +
>>> +enum vop2_features {
>>> +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
>>> +};
>>> +
>>> +enum vop2_layer {
>>> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
>>> +	ROCKCHIP_VOP2_CLUSTER1,
>>> +	ROCKCHIP_VOP2_CLUSTER2,
>>> +	ROCKCHIP_VOP2_CLUSTER3,
>>> +	ROCKCHIP_VOP2_ESMART0,
>>> +	ROCKCHIP_VOP2_ESMART1,
>>> +	ROCKCHIP_VOP2_ESMART2,
>>> +	ROCKCHIP_VOP2_ESMART3,
>>> +	ROCKCHIP_VOP2_SMART0 = 6,
>>
>> Why is it necessary to assign a value?
>>
> SMART0/1 shares the same address and map as ESMART2/3.
>

That make sense. Please add some comments to describe this :)

-- 
Best, 
Chaoyi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-12-23  1:34       ` Chaoyi Chen
@ 2025-12-25  6:35         ` Dang Huynh
  2025-12-29  3:13           ` Chaoyi Chen
  0 siblings, 1 reply; 25+ messages in thread
From: Dang Huynh @ 2025-12-25  6:35 UTC (permalink / raw)
  To: Chaoyi Chen
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Peter Robinson, Miquel Raynal,
	Jonas Karlman, Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

Hi Chaoyi,

On Tue, Dec 23, 2025 at 09:34:54AM +0800, Chaoyi Chen wrote:
> Hello Dang,
> 
> On 12/18/2025 12:47 PM, Dang Huynh wrote:
> > On Mon, Nov 10, 2025 at 11:24:01AM +0800, Chaoyi Chen wrote:
> >> On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:
> >>
> >>> From: Dang Huynh <dang.huynh@mainlining.org>
> >>>
> >>> VOP2 (Video Output Processor v2) is a display controller on Rockchip
> >>> SoCs. It can be found on RK3566/8 and RK3588.
> >>>
> >>> This commit currently only supports RK3566/8.
> >>>
> >>> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> >>> ---
> >>>   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
> >>>   drivers/video/rockchip/Makefile                 |   3 +-
> >>>   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
> >>>   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
> >>>   drivers/video/rockchip/rk_vop2.h                |  76 ++++
> >>>   5 files changed, 1138 insertions(+), 1 deletion(-)
> >>
> >> [...]
> >>
> >>> +
> >>> +/*
> >>> + * RK3566 datasheet omits the VP2, even though it exist in the hardware
> >>> + * so let's not use it.
> >>> + */
> >>> +struct rkvop2_platdata rk3566_platdata = {
> >>> +	.delay = 20,
> >>> +	.bg_dly = {42, 40, -1},
> >>> +	/* SMART0, ESMART0 */
> >>> +	.vp_lyr = {3, 2, -1},
> >>
> >> For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".
> >>
> >> You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.
> >>
> > You can use ESMART0 and SMART0 at the same time? That's how Linux 6.18 would configure VOP for both VP0 and VP1.
> > 
> 
> No. I mean you can not use SMART0 for VP0 and ESMART0 for VP1. They
> should be same win type.
> 
Given in v2 I've been told by Andy to made it platform-specific..
https://lore.kernel.org/all/79e71a91.ebe.1955eb65686.Coremail.andyshrk@163.com/

How does Linux turn off the layer if we aren't resetting the controller?
Because currently the (E)SMART layer is disabled before booting Linux or VOP2 will malfunction.

> >>
> >>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
> >>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> >>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> >>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
> >>> +};
> >>> +
> >>> +struct rkvop2_platdata rk3568_platdata = {
> >>> +	.delay = 20,
> >>> +	.bg_dly = {42, 40, 40},
> >>> +	/* SMART0, SMART1, ESMART1 */
> >>> +	.vp_lyr = {3, 7, 6},
> >>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
> >>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> >>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> >>> +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
> >>> +};
> >>> +
> >>> +struct rkvop2_driverdata rk3566_driverdata = {
> >>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> >>> +	.set_pin_polarity = rk3568_set_pin_polarity,
> >>> +	.enable_output = rk3568_enable_output,
> >>> +	.platdata = &rk3566_platdata,
> >>> +};
> >>> +
> >>> +struct rkvop2_driverdata rk3568_driverdata = {
> >>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> >>> +	.set_pin_polarity = rk3568_set_pin_polarity,
> >>> +	.enable_output = rk3568_enable_output,
> >>> +	.platdata = &rk3568_platdata,
> >>> +};
> >>> +
> >>> +static const struct udevice_id rk3568_vop_ids[] = {
> >>> +	{ .compatible = "rockchip,rk3566-vop",
> >>> +	  .data = (ulong)&rk3566_driverdata },
> >>> +	{ .compatible = "rockchip,rk3568-vop",
> >>> +	  .data = (ulong)&rk3568_driverdata },
> >>> +	{ }
> >>> +};
> >>> +
> >>> +static const struct video_ops rk3568_vop_ops = {
> >>> +};
> >>> +
> >>> +U_BOOT_DRIVER(rk3568_vop) = {
> >>> +	.name	= "rk3568_vop",
> >>> +	.id	= UCLASS_VIDEO,
> >>> +	.of_match = rk3568_vop_ids,
> >>> +	.ops	= &rk3568_vop_ops,
> >>> +	.bind	= rk_vop2_bind,
> >>> +	.probe	= rk3568_vop_probe,
> >>> +	.remove = rk3568_vop_remove,
> >>> +	.priv_auto = sizeof(struct rk_vop2_priv),
> >>> +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
> >>> +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
> >>> +#else
> >>> +	.flags = DM_FLAG_PRE_RELOC,
> >>> +#endif
> >>> +};
> >>> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> >>> new file mode 100644
> >>> index 00000000000..992f215d416
> >>> --- /dev/null
> >>> +++ b/drivers/video/rockchip/rk_vop2.c
> >>> @@ -0,0 +1,520 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +/*
> >>> + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
> >>> + *
> >>> + * Based on rk_vop.c:
> >>> + *   Copyright (c) 2015 Google, Inc
> >>> + *   Copyright 2014 Rockchip Inc.
> >>> + */
> >>> +
> >>> +#include <clk.h>
> >>> +#include <display.h>
> >>> +#include <dm.h>
> >>> +#include <dm/device_compat.h>
> >>> +#include <edid.h>
> >>> +#include <log.h>
> >>> +#include <regmap.h>
> >>> +#include <reset.h>
> >>> +#include <syscon.h>
> >>> +#include <video.h>
> >>> +#include <asm/global_data.h>
> >>> +#include <asm/gpio.h>
> >>> +#include <asm/io.h>
> >>> +#include <asm/arch-rockchip/clock.h>
> >>> +#include <asm/arch-rockchip/vop_rk3568.h>
> >>> +#include <dm/device-internal.h>
> >>> +#include <dm/uclass-internal.h>
> >>> +#include <efi.h>
> >>> +#include <efi_loader.h>
> >>> +#include <linux/bitops.h>
> >>> +#include <linux/err.h>
> >>> +#include <power/regulator.h>
> >>> +
> >>> +#include "rk_vop2.h"
> >>> +
> >>> +DECLARE_GLOBAL_DATA_PTR;
> >>> +
> >>> +enum vop_pol {
> >>> +	HSYNC_POSITIVE = 0,
> >>> +	VSYNC_POSITIVE = 1,
> >>> +	DEN_NEGATIVE   = 2,
> >>> +	DCLK_INVERT    = 3
> >>> +};
> >>> +
> >>> +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
> >>> +{
> >>> +	u32 reg;
> >>> +
> >>> +	reg = M_GLOBAL_REGDONE;
> >>> +
> >>> +	/*
> >>> +	 * For RK3588, changes will only take effect when the same bit is
> >>> +	 * leftshifted by 16.
> >>> +	 */
> >>> +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
> >>> +
> >>> +	writel(reg, &sysctrl->reg_cfg_done);
> >>> +}
> >>> +
> >>> +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
> >>> +			 int fb_bits_per_pixel, const struct display_timing *edid,
> >>> +			 int port, int win_id, struct rkvop2_platdata *platdata)
> >>> +{
> >>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>> +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
> >>> +	struct rk3568_vop_esmart *esmart;
> >>> +	bool is_cluster = false;
> >>> +	u8 layer;
> >>> +	u32 reg;
> >>> +	u32 rgb_mode;
> >>> +	u32 hactive = edid->hactive.typ;
> >>> +	u32 vactive = edid->vactive.typ;
> >>> +
> >>> +	if (platdata->layers[win_id] < 0)
> >>> +		return -EINVAL;
> >>> +
> >>> +	switch (platdata->layers[win_id]) {
> >>> +	case ROCKCHIP_VOP2_CLUSTER0:
> >>> +	case ROCKCHIP_VOP2_CLUSTER1:
> >>> +	case ROCKCHIP_VOP2_CLUSTER2:
> >>> +	case ROCKCHIP_VOP2_CLUSTER3:
> >>> +		is_cluster = true;
> >>> +		break;
> >>> +	default:
> >>> +		break;
> >>> +	}
> >>> +
> >>> +	layer = platdata->layers[win_id];
> >>> +
> >>> +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
> >>> +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
> >>> +
> >>> +	/* TODO: Support VOP2 CLUSTER */
> >>> +	if (is_cluster) {
> >>> +		dev_err(dev, "win_id is a cluster, not supported.\n");
> >>> +		return -ENOSYS;
> >>> +	}
> >>> +
> >>> +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
> >>> +
> >>> +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
> >>> +
> >>> +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
> >>> +	       &esmart->esmart_region0_act_info);
> >>> +
> >>> +	/* Set offset to 0,0 */
> >>> +	writel(0, &esmart->esmart_region0_dsp_offset);
> >>> +
> >>> +	writel(V_DSP_WIDTH(hactive - 1) |
> >>> +			V_DSP_HEIGHT(vactive - 1),
> >>> +			&esmart->esmart_region0_dsp_info);
> >>> +
> >>> +	switch (fb_bits_per_pixel) {
> >>> +	case 16:
> >>> +		rgb_mode = RGB565;
> >>> +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>> +		break;
> >>> +	case 24:
> >>> +		rgb_mode = RGB888;
> >>> +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>> +		break;
> >>> +	case 32:
> >>> +	default:
> >>> +		rgb_mode = ARGB8888;
> >>> +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>> +		break;
> >>> +	}
> >>> +
> >>> +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
> >>> +
> >>> +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
> >>> +	       &esmart->esmart_region0_mst_ctl);
> >>> +
> >>> +	/* Set esmart to the destination video port */
> >>> +	reg = V_ESMART_SEL_PORT(layer - 4, port);
> >>> +
> >>> +	/*
> >>> +	 * VOP2 requires every port mux to be configured.
> >>> +	 *
> >>> +	 * As U-Boot only supports singledisplay, we'll set all
> >>> +	 * unused ports to set layer to 8 (disabled).
> >>> +	 */
> >>> +	for (int i = 0; i < 4; i++) {
> >>> +		if (i != port)
> >>> +			reg |= V_PORT_MUX(8, i);
> >>> +	}
> >>> +
> >>> +	writel(reg, &overlay->port_sel);
> >>> +
> >>> +	/* Set layer 0 to win_id */
> >>> +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
> >>> +
> >>> +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
> >>> +	writel(reg, &overlay->overlay_ctrl);
> >>> +
> >>> +	priv->layer = layer;
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static void rkvop2_set_pin_polarity(struct udevice *dev,
> >>> +				    enum vop_modes mode, u32 polarity)
> >>> +{
> >>> +	struct rkvop2_driverdata *ops =
> >>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>> +
> >>> +	if (ops->set_pin_polarity)
> >>> +		ops->set_pin_polarity(dev, mode, polarity);
> >>> +}
> >>> +
> >>> +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
> >>> +{
> >>> +	struct rkvop2_driverdata *ops =
> >>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>> +
> >>> +	if (ops->enable_output)
> >>> +		ops->enable_output(dev, mode, port);
> >>> +}
> >>> +
> >>> +static void rkvop2_mode_set(struct udevice *dev,
> >>> +			    const struct display_timing *edid,
> >>> +		enum vop_modes mode, int port,
> >>> +		struct rkvop2_platdata *platdata)
> >>> +{
> >>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>> +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
> >>> +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
> >>> +	struct rkvop2_driverdata *data =
> >>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>> +
> >>> +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
> >>> +
> >>> +	u32 hactive = edid->hactive.typ;
> >>> +	u32 vactive = edid->vactive.typ;
> >>> +	u32 hsync_len = edid->hsync_len.typ;
> >>> +	u32 hback_porch = edid->hback_porch.typ;
> >>> +	u32 vsync_len = edid->vsync_len.typ;
> >>> +	u32 vback_porch = edid->vback_porch.typ;
> >>> +	u32 hfront_porch = edid->hfront_porch.typ;
> >>> +	u32 vfront_porch = edid->vfront_porch.typ;
> >>> +	int mode_flags;
> >>> +	u32 pin_polarity;
> >>> +	u32 reg;
> >>> +
> >>> +	pin_polarity = BIT(DCLK_INVERT);
> >>> +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
> >>> +		pin_polarity |= BIT(HSYNC_POSITIVE);
> >>> +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> >>> +		pin_polarity |= BIT(VSYNC_POSITIVE);
> >>> +
> >>> +	rkvop2_enable_output(dev, mode, port);
> >>> +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
> >>> +
> >>> +	mode_flags = 0;  /* RGB888 */
> >>> +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
> >>> +	    mode == VOP_MODE_HDMI)
> >>> +		mode_flags = 15;  /* RGBaaa */
> >>
> >> You should also set RGBaaa for VOP_MODE_EDP.
> >>
> > Will do in next series.
> > 
> >>
> >>> +
> >>> +	reg = V_DSP_OUT_MODE(mode_flags);
> >>> +
> >>> +	debug("(%s, %s): bg_dly: %d\n",
> >>> +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
> >>> +
> >>> +	if (platdata->bg_dly[port] < 0) {
> >>> +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
> >>> +		return;
> >>> +	}
> >>> +
> >>> +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
> >>> +	       &post->prescan_htimings);
> >>> +
> >>> +	writel(V_HSYNC(hsync_len) |
> >>> +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
> >>> +			&post->dsp_htotal_hs_end);
> >>> +
> >>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> >>> +			V_HASP(hsync_len + hback_porch),
> >>> +			&post->dsp_hact_st_end);
> >>> +
> >>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> >>> +			V_VASP(vsync_len + vback_porch),
> >>> +			&post->dsp_vact_st_end);
> >>> +
> >>> +	writel(V_VSYNC(vsync_len) |
> >>> +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
> >>> +			&post->dsp_vtotal_vs_end);
> >>> +
> >>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> >>> +			V_HASP(hsync_len + hback_porch),
> >>> +			&post->dsp_hact_info);
> >>> +
> >>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> >>> +			V_VASP(vsync_len + vback_porch),
> >>> +			&post->dsp_vact_info);
> >>> +
> >>> +	/* No scaling */
> >>> +	writel(0x10001000, &post->scl_factor_yrgb);
> >>> +
> >>> +	writel(reg, &post->dsp_ctrl);
> >>> +
> >>> +	rkvop2_cfg_regdone(sysctrl, port);
> >>> +}
> >>> +
> >>> +/**
> >>> + * rk_display_init() - Try to enable the given display device
> >>> + *
> >>> + * This function performs many steps:
> >>> + * - Finds the display device being referenced by @ep_node
> >>> + * - Puts the VOP's ID into its uclass platform data
> >>> + * - Probes the device to set it up
> >>> + * - Reads the timing information (from EDID or panel)
> >>> + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
> >>> + * - Enables the display (the display device handles this and will do different
> >>> + *     things depending on the display type)
> >>> + * - Tells the uclass about the display resolution so that the console will
> >>> + *     appear correctly
> >>> + *
> >>> + * @dev:	VOP device that we want to connect to the display
> >>> + * @fbbase:	Frame buffer address
> >>> + * @vp_node:	Device tree node to process
> >>> + * Return: 0 if OK, -ve if something went wrong
> >>> + */
> >>> +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >>> +{
> >>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> >>> +	struct rkvop2_driverdata *drvdata =
> >>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>> +	struct rkvop2_platdata *platdata =
> >>> +		(struct rkvop2_platdata *)drvdata->platdata;
> >>> +	ofnode ep_node;
> >>> +	int vop_id, port_id, win_id;
> >>> +	struct display_timing timing;
> >>> +	struct udevice *disp;
> >>> +	int ret;
> >>> +	u32 remote_phandle;
> >>> +	struct display_plat *disp_uc_plat;
> >>> +	enum video_log2_bpp l2bpp;
> >>> +	ofnode remote;
> >>> +	const char *compat;
> >>> +	char dclk_name[9];
> >>> +	struct clk dclk;
> >>> +
> >>> +	debug("%s(%s, 0x%lx, %s)\n", __func__,
> >>> +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
> >>> +
> >>> +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
> >>> +	if (port_id < 0) {
> >>> +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
> >>> +		return port_id;
> >>> +	}
> >>> +
> >>> +	ep_node = ofnode_first_subnode(vp_node);
> >>> +	if (!ofnode_valid(ep_node)) {
> >>> +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> >>> +	if (ret) {
> >>> +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	remote = ofnode_get_by_phandle(remote_phandle);
> >>> +	if (!ofnode_valid(remote))
> >>> +		return -EINVAL;
> >>> +
> >>> +	remote = ofnode_get_parent(remote);
> >>> +	if (!ofnode_valid(remote))
> >>> +		return -EINVAL;
> >>> +
> >>> +	/*
> >>> +	 * The remote-endpoint references into a subnode of the encoder
> >>> +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
> >>> +	 * the following:
> >>> +	 *
> >>> +	 * hdmi: hdmi@fe0a0000 {
> >>> +	 *   ports {
> >>> +	 *     hdmi_in: port {
> >>> +	 *       hdmi_in_vp0: endpoint { ... };
> >>> +	 *     }
> >>> +	 *   }
> >>> +	 * }
> >>> +	 *
> >>> +	 * This isn't any different from how VOP1 works, so we'll adapt
> >>> +	 * the same method of finding the display from the original code
> >>> +	 * (find the enclosing device of "UCLASS_DISPLAY")
> >>> +	 *
> >>> +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
> >>> +	 * DW MIPI DSI driver for Rockchip.
> >>> +	 */
> >>> +	while (ofnode_valid(remote)) {
> >>> +		remote = ofnode_get_parent(remote);
> >>> +		if (!ofnode_valid(remote)) {
> >>> +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
> >>> +			      __func__, dev_read_name(dev));
> >>> +			return -EINVAL;
> >>> +		}
> >>> +
> >>> +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> >>> +		if (disp)
> >>> +			break;
> >>> +	};
> >>> +	compat = ofnode_get_property(remote, "compatible", NULL);
> >>> +	if (!compat) {
> >>> +		debug("%s(%s): Failed to find compatible property\n",
> >>> +		      __func__, dev_read_name(dev));
> >>> +		return -EINVAL;
> >>> +	}
> >>> +	if (strstr(compat, "edp")) {
> >>> +		vop_id = VOP_MODE_EDP;
> >>> +	} else if (strstr(compat, "mipi")) {
> >>> +		vop_id = VOP_MODE_MIPI;
> >>> +	} else if (strstr(compat, "hdmi")) {
> >>> +		vop_id = VOP_MODE_HDMI;
> >>> +	} else if (strstr(compat, "rk3588-dp")) {
> >>
> >> Can we directly use "dp" ?
> >>
> > We can, however if the device tree is misconfigured then it'll pick up VP connected devices with "dp" (such
> > as "rk3588-h*dp*tx-phy") and consider that to be a displayport device.
> > 
> >>
> >>> +		vop_id = VOP_MODE_DP;
> >>> +	} else if (strstr(compat, "lvds")) {
> >>> +		vop_id = VOP_MODE_LVDS;
> >>> +	} else {
> >>> +		debug("%s(%s): Failed to find vop mode for %s\n",
> >>> +		      __func__, dev_read_name(dev), compat);
> >>> +		return -EINVAL;
> >>> +	}
> >>> +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
> >>> +
> >>> +	/* Get the video port clock and enable it */
> >>> +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
> >>> +	ret = clk_get_by_name(dev, dclk_name, &dclk);
> >>> +	if (ret < 0)
> >>> +		return ret;
> >>> +
> >>> +	disp_uc_plat = dev_get_uclass_plat(disp);
> >>> +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> >>> +	if (display_in_use(disp)) {
> >>> +		debug("   - device in use\n");
> >>> +		return -EBUSY;
> >>> +	}
> >>> +
> >>> +	disp_uc_plat->source_id = vop_id;
> >>> +	disp_uc_plat->src_dev = dev;
> >>> +
> >>> +	ret = device_probe(disp);
> >>> +	if (ret) {
> >>> +		debug("%s: device '%s' display won't probe (ret=%d)\n",
> >>> +		      __func__, dev->name, ret);
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	ret = display_read_timing(disp, &timing);
> >>> +	if (ret) {
> >>> +		debug("%s: Failed to read timings\n", __func__);
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	/* Set clock rate on video port to display timings */
> >>> +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
> >>> +	if (ret < 0) {
> >>> +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
> >>> +	      dclk_name, clk_get_rate(&dclk));
> >>> +
> >>> +	/* Set bitwidth for vop display according to vop mode */
> >>> +	switch (vop_id) {
> >>> +	case VOP_MODE_EDP:
> >>> +	case VOP_MODE_MIPI:
> >>> +	case VOP_MODE_HDMI:
> >>> +	case VOP_MODE_DP:
> >>> +	case VOP_MODE_LVDS:
> >>> +		l2bpp = VIDEO_BPP32;
> >>> +		break;
> >>> +	default:
> >>> +		l2bpp = VIDEO_BPP16;
> >>
> >> It seems that we always end up with VIDEO_BPP32?
> >>
> > U-Boot doesn't support VIDEO_BPP24 format.
> > 
> >>
> >>> +	}
> >>> +
> >>> +	/*
> >>> +	 * We'll use the default platform-specific win_id from Linux
> >>> +	 * so that Linux can take over U-Boot plane when Linux reconfigures
> >>> +	 * VOP2.
> >>> +	 */
> >>> +	win_id = platdata->vp_lyr[port_id];
> >>> +	if (win_id < 0) {
> >>> +		dev_err(dev, "win_id is null, don't setup\n");
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
> >>> +	if (ret < 0)
> >>> +		return ret;
> >>> +
> >>> +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> >>> +
> >>> +	ret = display_enable(disp, 1 << l2bpp, &timing);
> >>> +	if (ret)
> >>> +		return ret;
> >>> +
> >>> +	uc_priv->xsize = timing.hactive.typ;
> >>> +	uc_priv->ysize = timing.vactive.typ;
> >>> +	uc_priv->bpix = l2bpp;
> >>> +
> >>> +	priv->vp = port_id;
> >>> +
> >>> +	debug("fb=%lx, size=%d %d\n", fbbase,
> >>> +	      uc_priv->xsize, uc_priv->ysize);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +int rk_vop2_probe(struct udevice *dev)
> >>> +{
> >>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> >>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>> +	int ret = 0;
> >>> +	ofnode port, node;
> >>> +
> >>> +	/* Before relocation we don't need to do anything */
> >>> +	if (!(gd->flags & GD_FLG_RELOC))
> >>> +		return 0;
> >>> +
> >>> +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
> >>> +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
> >>> +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
> >>> +	}
> >>> +
> >>> +	priv->regs = dev_read_addr_ptr(dev);
> >>> +
> >>> +	/* Try all the ports until we find one that works. */
> >>> +	port = dev_read_subnode(dev, "ports");
> >>> +	if (!ofnode_valid(port)) {
> >>> +		debug("%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 = rk_display_init(dev, plat->base, node);
> >>> +		if (ret)
> >>> +			debug("Device failed: ret=%d\n", ret);
> >>> +		if (!ret)
> >>> +			break;
> >>> +	}
> >>> +	video_set_flush_dcache(dev, true);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +int rk_vop2_bind(struct udevice *dev)
> >>> +{
> >>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> >>> +
> >>> +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
> >>> +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
> >>> new file mode 100644
> >>> index 00000000000..5d668070014
> >>> --- /dev/null
> >>> +++ b/drivers/video/rockchip/rk_vop2.h
> >>> @@ -0,0 +1,76 @@
> >>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>> +/*
> >>> + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> >>> + */
> >>> +
> >>> +#ifndef __RK_VOP2_H__
> >>> +#define __RK_VOP2_H__
> >>> +
> >>> +#include <asm/arch-rockchip/vop_rk3568.h>
> >>> +
> >>> +struct rk_vop2_priv {
> >>> +	void *grf;
> >>> +	void *regs;
> >>> +	int vp;
> >>> +	int layer;
> >>> +};
> >>> +
> >>> +enum vop2_features {
> >>> +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
> >>> +};
> >>> +
> >>> +enum vop2_layer {
> >>> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> >>> +	ROCKCHIP_VOP2_CLUSTER1,
> >>> +	ROCKCHIP_VOP2_CLUSTER2,
> >>> +	ROCKCHIP_VOP2_CLUSTER3,
> >>> +	ROCKCHIP_VOP2_ESMART0,
> >>> +	ROCKCHIP_VOP2_ESMART1,
> >>> +	ROCKCHIP_VOP2_ESMART2,
> >>> +	ROCKCHIP_VOP2_ESMART3,
> >>> +	ROCKCHIP_VOP2_SMART0 = 6,
> >>
> >> Why is it necessary to assign a value?
> >>
> > SMART0/1 shares the same address and map as ESMART2/3.
> >
> 
> That make sense. Please add some comments to describe this :)
> 
> -- 
> Best, 
> Chaoyi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-12-25  6:35         ` Dang Huynh
@ 2025-12-29  3:13           ` Chaoyi Chen
  2025-12-30  5:42             ` Dang Huynh
  0 siblings, 1 reply; 25+ messages in thread
From: Chaoyi Chen @ 2025-12-29  3:13 UTC (permalink / raw)
  To: Dang Huynh
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Peter Robinson, Miquel Raynal,
	Jonas Karlman, Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

On 12/25/2025 2:35 PM, Dang Huynh wrote:
> Hi Chaoyi,
> 
> On Tue, Dec 23, 2025 at 09:34:54AM +0800, Chaoyi Chen wrote:
>> Hello Dang,
>>
>> On 12/18/2025 12:47 PM, Dang Huynh wrote:
>>> On Mon, Nov 10, 2025 at 11:24:01AM +0800, Chaoyi Chen wrote:
>>>> On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:
>>>>
>>>>> From: Dang Huynh <dang.huynh@mainlining.org>
>>>>>
>>>>> VOP2 (Video Output Processor v2) is a display controller on Rockchip
>>>>> SoCs. It can be found on RK3566/8 and RK3588.
>>>>>
>>>>> This commit currently only supports RK3566/8.
>>>>>
>>>>> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
>>>>> ---
>>>>>   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
>>>>>   drivers/video/rockchip/Makefile                 |   3 +-
>>>>>   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
>>>>>   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
>>>>>   drivers/video/rockchip/rk_vop2.h                |  76 ++++
>>>>>   5 files changed, 1138 insertions(+), 1 deletion(-)
>>>>
>>>> [...]
>>>>
>>>>> +
>>>>> +/*
>>>>> + * RK3566 datasheet omits the VP2, even though it exist in the hardware
>>>>> + * so let's not use it.
>>>>> + */
>>>>> +struct rkvop2_platdata rk3566_platdata = {
>>>>> +	.delay = 20,
>>>>> +	.bg_dly = {42, 40, -1},
>>>>> +	/* SMART0, ESMART0 */
>>>>> +	.vp_lyr = {3, 2, -1},
>>>>
>>>> For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".
>>>>
>>>> You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.
>>>>
>>> You can use ESMART0 and SMART0 at the same time? That's how Linux 6.18 would configure VOP for both VP0 and VP1.
>>>
>>
>> No. I mean you can not use SMART0 for VP0 and ESMART0 for VP1. They
>> should be same win type.
>>
> Given in v2 I've been told by Andy to made it platform-specific..
> https://lore.kernel.org/all/79e71a91.ebe.1955eb65686.Coremail.andyshrk@163.com/
>

Sorry, I misunderstood you point. The code is okay for me.

> How does Linux turn off the layer if we aren't resetting the controller?
> Because currently the (E)SMART layer is disabled before booting Linux or VOP2 will malfunction.
> 

And this seems to be another problem... You've already disabled them in U-Boot,
haven't you?

>>>>
>>>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
>>>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
>>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
>>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
>>>>> +};
>>>>> +
>>>>> +struct rkvop2_platdata rk3568_platdata = {
>>>>> +	.delay = 20,
>>>>> +	.bg_dly = {42, 40, 40},
>>>>> +	/* SMART0, SMART1, ESMART1 */
>>>>> +	.vp_lyr = {3, 7, 6},
>>>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
>>>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
>>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
>>>>> +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
>>>>> +};
>>>>> +
>>>>> +struct rkvop2_driverdata rk3566_driverdata = {
>>>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
>>>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
>>>>> +	.enable_output = rk3568_enable_output,
>>>>> +	.platdata = &rk3566_platdata,
>>>>> +};
>>>>> +
>>>>> +struct rkvop2_driverdata rk3568_driverdata = {
>>>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
>>>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
>>>>> +	.enable_output = rk3568_enable_output,
>>>>> +	.platdata = &rk3568_platdata,
>>>>> +};
>>>>> +
>>>>> +static const struct udevice_id rk3568_vop_ids[] = {
>>>>> +	{ .compatible = "rockchip,rk3566-vop",
>>>>> +	  .data = (ulong)&rk3566_driverdata },
>>>>> +	{ .compatible = "rockchip,rk3568-vop",
>>>>> +	  .data = (ulong)&rk3568_driverdata },
>>>>> +	{ }
>>>>> +};
>>>>> +
>>>>> +static const struct video_ops rk3568_vop_ops = {
>>>>> +};
>>>>> +
>>>>> +U_BOOT_DRIVER(rk3568_vop) = {
>>>>> +	.name	= "rk3568_vop",
>>>>> +	.id	= UCLASS_VIDEO,
>>>>> +	.of_match = rk3568_vop_ids,
>>>>> +	.ops	= &rk3568_vop_ops,
>>>>> +	.bind	= rk_vop2_bind,
>>>>> +	.probe	= rk3568_vop_probe,
>>>>> +	.remove = rk3568_vop_remove,
>>>>> +	.priv_auto = sizeof(struct rk_vop2_priv),
>>>>> +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
>>>>> +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
>>>>> +#else
>>>>> +	.flags = DM_FLAG_PRE_RELOC,
>>>>> +#endif
>>>>> +};
>>>>> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
>>>>> new file mode 100644
>>>>> index 00000000000..992f215d416
>>>>> --- /dev/null
>>>>> +++ b/drivers/video/rockchip/rk_vop2.c
>>>>> @@ -0,0 +1,520 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +/*
>>>>> + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
>>>>> + *
>>>>> + * Based on rk_vop.c:
>>>>> + *   Copyright (c) 2015 Google, Inc
>>>>> + *   Copyright 2014 Rockchip Inc.
>>>>> + */
>>>>> +
>>>>> +#include <clk.h>
>>>>> +#include <display.h>
>>>>> +#include <dm.h>
>>>>> +#include <dm/device_compat.h>
>>>>> +#include <edid.h>
>>>>> +#include <log.h>
>>>>> +#include <regmap.h>
>>>>> +#include <reset.h>
>>>>> +#include <syscon.h>
>>>>> +#include <video.h>
>>>>> +#include <asm/global_data.h>
>>>>> +#include <asm/gpio.h>
>>>>> +#include <asm/io.h>
>>>>> +#include <asm/arch-rockchip/clock.h>
>>>>> +#include <asm/arch-rockchip/vop_rk3568.h>
>>>>> +#include <dm/device-internal.h>
>>>>> +#include <dm/uclass-internal.h>
>>>>> +#include <efi.h>
>>>>> +#include <efi_loader.h>
>>>>> +#include <linux/bitops.h>
>>>>> +#include <linux/err.h>
>>>>> +#include <power/regulator.h>
>>>>> +
>>>>> +#include "rk_vop2.h"
>>>>> +
>>>>> +DECLARE_GLOBAL_DATA_PTR;
>>>>> +
>>>>> +enum vop_pol {
>>>>> +	HSYNC_POSITIVE = 0,
>>>>> +	VSYNC_POSITIVE = 1,
>>>>> +	DEN_NEGATIVE   = 2,
>>>>> +	DCLK_INVERT    = 3
>>>>> +};
>>>>> +
>>>>> +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
>>>>> +{
>>>>> +	u32 reg;
>>>>> +
>>>>> +	reg = M_GLOBAL_REGDONE;
>>>>> +
>>>>> +	/*
>>>>> +	 * For RK3588, changes will only take effect when the same bit is
>>>>> +	 * leftshifted by 16.
>>>>> +	 */
>>>>> +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
>>>>> +
>>>>> +	writel(reg, &sysctrl->reg_cfg_done);
>>>>> +}
>>>>> +
>>>>> +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
>>>>> +			 int fb_bits_per_pixel, const struct display_timing *edid,
>>>>> +			 int port, int win_id, struct rkvop2_platdata *platdata)
>>>>> +{
>>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>>>> +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
>>>>> +	struct rk3568_vop_esmart *esmart;
>>>>> +	bool is_cluster = false;
>>>>> +	u8 layer;
>>>>> +	u32 reg;
>>>>> +	u32 rgb_mode;
>>>>> +	u32 hactive = edid->hactive.typ;
>>>>> +	u32 vactive = edid->vactive.typ;
>>>>> +
>>>>> +	if (platdata->layers[win_id] < 0)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	switch (platdata->layers[win_id]) {
>>>>> +	case ROCKCHIP_VOP2_CLUSTER0:
>>>>> +	case ROCKCHIP_VOP2_CLUSTER1:
>>>>> +	case ROCKCHIP_VOP2_CLUSTER2:
>>>>> +	case ROCKCHIP_VOP2_CLUSTER3:
>>>>> +		is_cluster = true;
>>>>> +		break;
>>>>> +	default:
>>>>> +		break;
>>>>> +	}
>>>>> +
>>>>> +	layer = platdata->layers[win_id];
>>>>> +
>>>>> +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
>>>>> +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
>>>>> +
>>>>> +	/* TODO: Support VOP2 CLUSTER */
>>>>> +	if (is_cluster) {
>>>>> +		dev_err(dev, "win_id is a cluster, not supported.\n");
>>>>> +		return -ENOSYS;
>>>>> +	}
>>>>> +
>>>>> +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
>>>>> +
>>>>> +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
>>>>> +
>>>>> +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
>>>>> +	       &esmart->esmart_region0_act_info);
>>>>> +
>>>>> +	/* Set offset to 0,0 */
>>>>> +	writel(0, &esmart->esmart_region0_dsp_offset);
>>>>> +
>>>>> +	writel(V_DSP_WIDTH(hactive - 1) |
>>>>> +			V_DSP_HEIGHT(vactive - 1),
>>>>> +			&esmart->esmart_region0_dsp_info);
>>>>> +
>>>>> +	switch (fb_bits_per_pixel) {
>>>>> +	case 16:
>>>>> +		rgb_mode = RGB565;
>>>>> +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>>>> +		break;
>>>>> +	case 24:
>>>>> +		rgb_mode = RGB888;
>>>>> +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>>>> +		break;
>>>>> +	case 32:
>>>>> +	default:
>>>>> +		rgb_mode = ARGB8888;
>>>>> +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
>>>>> +		break;
>>>>> +	}
>>>>> +
>>>>> +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
>>>>> +
>>>>> +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
>>>>> +	       &esmart->esmart_region0_mst_ctl);
>>>>> +
>>>>> +	/* Set esmart to the destination video port */
>>>>> +	reg = V_ESMART_SEL_PORT(layer - 4, port);
>>>>> +
>>>>> +	/*
>>>>> +	 * VOP2 requires every port mux to be configured.
>>>>> +	 *
>>>>> +	 * As U-Boot only supports singledisplay, we'll set all
>>>>> +	 * unused ports to set layer to 8 (disabled).
>>>>> +	 */
>>>>> +	for (int i = 0; i < 4; i++) {
>>>>> +		if (i != port)
>>>>> +			reg |= V_PORT_MUX(8, i);
>>>>> +	}
>>>>> +
>>>>> +	writel(reg, &overlay->port_sel);
>>>>> +
>>>>> +	/* Set layer 0 to win_id */
>>>>> +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
>>>>> +
>>>>> +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
>>>>> +	writel(reg, &overlay->overlay_ctrl);
>>>>> +
>>>>> +	priv->layer = layer;
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static void rkvop2_set_pin_polarity(struct udevice *dev,
>>>>> +				    enum vop_modes mode, u32 polarity)
>>>>> +{
>>>>> +	struct rkvop2_driverdata *ops =
>>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>>>> +
>>>>> +	if (ops->set_pin_polarity)
>>>>> +		ops->set_pin_polarity(dev, mode, polarity);
>>>>> +}
>>>>> +
>>>>> +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
>>>>> +{
>>>>> +	struct rkvop2_driverdata *ops =
>>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>>>> +
>>>>> +	if (ops->enable_output)
>>>>> +		ops->enable_output(dev, mode, port);
>>>>> +}
>>>>> +
>>>>> +static void rkvop2_mode_set(struct udevice *dev,
>>>>> +			    const struct display_timing *edid,
>>>>> +		enum vop_modes mode, int port,
>>>>> +		struct rkvop2_platdata *platdata)
>>>>> +{
>>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>>>> +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
>>>>> +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
>>>>> +	struct rkvop2_driverdata *data =
>>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>>>> +
>>>>> +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
>>>>> +
>>>>> +	u32 hactive = edid->hactive.typ;
>>>>> +	u32 vactive = edid->vactive.typ;
>>>>> +	u32 hsync_len = edid->hsync_len.typ;
>>>>> +	u32 hback_porch = edid->hback_porch.typ;
>>>>> +	u32 vsync_len = edid->vsync_len.typ;
>>>>> +	u32 vback_porch = edid->vback_porch.typ;
>>>>> +	u32 hfront_porch = edid->hfront_porch.typ;
>>>>> +	u32 vfront_porch = edid->vfront_porch.typ;
>>>>> +	int mode_flags;
>>>>> +	u32 pin_polarity;
>>>>> +	u32 reg;
>>>>> +
>>>>> +	pin_polarity = BIT(DCLK_INVERT);
>>>>> +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
>>>>> +		pin_polarity |= BIT(HSYNC_POSITIVE);
>>>>> +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
>>>>> +		pin_polarity |= BIT(VSYNC_POSITIVE);
>>>>> +
>>>>> +	rkvop2_enable_output(dev, mode, port);
>>>>> +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
>>>>> +
>>>>> +	mode_flags = 0;  /* RGB888 */
>>>>> +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
>>>>> +	    mode == VOP_MODE_HDMI)
>>>>> +		mode_flags = 15;  /* RGBaaa */
>>>>
>>>> You should also set RGBaaa for VOP_MODE_EDP.
>>>>
>>> Will do in next series.
>>>
>>>>
>>>>> +
>>>>> +	reg = V_DSP_OUT_MODE(mode_flags);
>>>>> +
>>>>> +	debug("(%s, %s): bg_dly: %d\n",
>>>>> +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
>>>>> +
>>>>> +	if (platdata->bg_dly[port] < 0) {
>>>>> +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
>>>>> +		return;
>>>>> +	}
>>>>> +
>>>>> +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
>>>>> +	       &post->prescan_htimings);
>>>>> +
>>>>> +	writel(V_HSYNC(hsync_len) |
>>>>> +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
>>>>> +			&post->dsp_htotal_hs_end);
>>>>> +
>>>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
>>>>> +			V_HASP(hsync_len + hback_porch),
>>>>> +			&post->dsp_hact_st_end);
>>>>> +
>>>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
>>>>> +			V_VASP(vsync_len + vback_porch),
>>>>> +			&post->dsp_vact_st_end);
>>>>> +
>>>>> +	writel(V_VSYNC(vsync_len) |
>>>>> +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
>>>>> +			&post->dsp_vtotal_vs_end);
>>>>> +
>>>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
>>>>> +			V_HASP(hsync_len + hback_porch),
>>>>> +			&post->dsp_hact_info);
>>>>> +
>>>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
>>>>> +			V_VASP(vsync_len + vback_porch),
>>>>> +			&post->dsp_vact_info);
>>>>> +
>>>>> +	/* No scaling */
>>>>> +	writel(0x10001000, &post->scl_factor_yrgb);
>>>>> +
>>>>> +	writel(reg, &post->dsp_ctrl);
>>>>> +
>>>>> +	rkvop2_cfg_regdone(sysctrl, port);
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * rk_display_init() - Try to enable the given display device
>>>>> + *
>>>>> + * This function performs many steps:
>>>>> + * - Finds the display device being referenced by @ep_node
>>>>> + * - Puts the VOP's ID into its uclass platform data
>>>>> + * - Probes the device to set it up
>>>>> + * - Reads the timing information (from EDID or panel)
>>>>> + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
>>>>> + * - Enables the display (the display device handles this and will do different
>>>>> + *     things depending on the display type)
>>>>> + * - Tells the uclass about the display resolution so that the console will
>>>>> + *     appear correctly
>>>>> + *
>>>>> + * @dev:	VOP device that we want to connect to the display
>>>>> + * @fbbase:	Frame buffer address
>>>>> + * @vp_node:	Device tree node to process
>>>>> + * Return: 0 if OK, -ve if something went wrong
>>>>> + */
>>>>> +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>>>>> +{
>>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>>>> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>>>>> +	struct rkvop2_driverdata *drvdata =
>>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
>>>>> +	struct rkvop2_platdata *platdata =
>>>>> +		(struct rkvop2_platdata *)drvdata->platdata;
>>>>> +	ofnode ep_node;
>>>>> +	int vop_id, port_id, win_id;
>>>>> +	struct display_timing timing;
>>>>> +	struct udevice *disp;
>>>>> +	int ret;
>>>>> +	u32 remote_phandle;
>>>>> +	struct display_plat *disp_uc_plat;
>>>>> +	enum video_log2_bpp l2bpp;
>>>>> +	ofnode remote;
>>>>> +	const char *compat;
>>>>> +	char dclk_name[9];
>>>>> +	struct clk dclk;
>>>>> +
>>>>> +	debug("%s(%s, 0x%lx, %s)\n", __func__,
>>>>> +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
>>>>> +
>>>>> +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
>>>>> +	if (port_id < 0) {
>>>>> +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
>>>>> +		return port_id;
>>>>> +	}
>>>>> +
>>>>> +	ep_node = ofnode_first_subnode(vp_node);
>>>>> +	if (!ofnode_valid(ep_node)) {
>>>>> +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
>>>>> +	if (ret) {
>>>>> +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	remote = ofnode_get_by_phandle(remote_phandle);
>>>>> +	if (!ofnode_valid(remote))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	remote = ofnode_get_parent(remote);
>>>>> +	if (!ofnode_valid(remote))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	/*
>>>>> +	 * The remote-endpoint references into a subnode of the encoder
>>>>> +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
>>>>> +	 * the following:
>>>>> +	 *
>>>>> +	 * hdmi: hdmi@fe0a0000 {
>>>>> +	 *   ports {
>>>>> +	 *     hdmi_in: port {
>>>>> +	 *       hdmi_in_vp0: endpoint { ... };
>>>>> +	 *     }
>>>>> +	 *   }
>>>>> +	 * }
>>>>> +	 *
>>>>> +	 * This isn't any different from how VOP1 works, so we'll adapt
>>>>> +	 * the same method of finding the display from the original code
>>>>> +	 * (find the enclosing device of "UCLASS_DISPLAY")
>>>>> +	 *
>>>>> +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
>>>>> +	 * DW MIPI DSI driver for Rockchip.
>>>>> +	 */
>>>>> +	while (ofnode_valid(remote)) {
>>>>> +		remote = ofnode_get_parent(remote);
>>>>> +		if (!ofnode_valid(remote)) {
>>>>> +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
>>>>> +			      __func__, dev_read_name(dev));
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +
>>>>> +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
>>>>> +		if (disp)
>>>>> +			break;
>>>>> +	};
>>>>> +	compat = ofnode_get_property(remote, "compatible", NULL);
>>>>> +	if (!compat) {
>>>>> +		debug("%s(%s): Failed to find compatible property\n",
>>>>> +		      __func__, dev_read_name(dev));
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +	if (strstr(compat, "edp")) {
>>>>> +		vop_id = VOP_MODE_EDP;
>>>>> +	} else if (strstr(compat, "mipi")) {
>>>>> +		vop_id = VOP_MODE_MIPI;
>>>>> +	} else if (strstr(compat, "hdmi")) {
>>>>> +		vop_id = VOP_MODE_HDMI;
>>>>> +	} else if (strstr(compat, "rk3588-dp")) {
>>>>
>>>> Can we directly use "dp" ?
>>>>
>>> We can, however if the device tree is misconfigured then it'll pick up VP connected devices with "dp" (such
>>> as "rk3588-h*dp*tx-phy") and consider that to be a displayport device.
>>>
>>>>
>>>>> +		vop_id = VOP_MODE_DP;
>>>>> +	} else if (strstr(compat, "lvds")) {
>>>>> +		vop_id = VOP_MODE_LVDS;
>>>>> +	} else {
>>>>> +		debug("%s(%s): Failed to find vop mode for %s\n",
>>>>> +		      __func__, dev_read_name(dev), compat);
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
>>>>> +
>>>>> +	/* Get the video port clock and enable it */
>>>>> +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
>>>>> +	ret = clk_get_by_name(dev, dclk_name, &dclk);
>>>>> +	if (ret < 0)
>>>>> +		return ret;
>>>>> +
>>>>> +	disp_uc_plat = dev_get_uclass_plat(disp);
>>>>> +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
>>>>> +	if (display_in_use(disp)) {
>>>>> +		debug("   - device in use\n");
>>>>> +		return -EBUSY;
>>>>> +	}
>>>>> +
>>>>> +	disp_uc_plat->source_id = vop_id;
>>>>> +	disp_uc_plat->src_dev = dev;
>>>>> +
>>>>> +	ret = device_probe(disp);
>>>>> +	if (ret) {
>>>>> +		debug("%s: device '%s' display won't probe (ret=%d)\n",
>>>>> +		      __func__, dev->name, ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	ret = display_read_timing(disp, &timing);
>>>>> +	if (ret) {
>>>>> +		debug("%s: Failed to read timings\n", __func__);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	/* Set clock rate on video port to display timings */
>>>>> +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
>>>>> +	if (ret < 0) {
>>>>> +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
>>>>> +	      dclk_name, clk_get_rate(&dclk));
>>>>> +
>>>>> +	/* Set bitwidth for vop display according to vop mode */
>>>>> +	switch (vop_id) {
>>>>> +	case VOP_MODE_EDP:
>>>>> +	case VOP_MODE_MIPI:
>>>>> +	case VOP_MODE_HDMI:
>>>>> +	case VOP_MODE_DP:
>>>>> +	case VOP_MODE_LVDS:
>>>>> +		l2bpp = VIDEO_BPP32;
>>>>> +		break;
>>>>> +	default:
>>>>> +		l2bpp = VIDEO_BPP16;
>>>>
>>>> It seems that we always end up with VIDEO_BPP32?
>>>>
>>> U-Boot doesn't support VIDEO_BPP24 format.
>>>
>>>>
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * We'll use the default platform-specific win_id from Linux
>>>>> +	 * so that Linux can take over U-Boot plane when Linux reconfigures
>>>>> +	 * VOP2.
>>>>> +	 */
>>>>> +	win_id = platdata->vp_lyr[port_id];
>>>>> +	if (win_id < 0) {
>>>>> +		dev_err(dev, "win_id is null, don't setup\n");
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
>>>>> +	if (ret < 0)
>>>>> +		return ret;
>>>>> +
>>>>> +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
>>>>> +
>>>>> +	ret = display_enable(disp, 1 << l2bpp, &timing);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	uc_priv->xsize = timing.hactive.typ;
>>>>> +	uc_priv->ysize = timing.vactive.typ;
>>>>> +	uc_priv->bpix = l2bpp;
>>>>> +
>>>>> +	priv->vp = port_id;
>>>>> +
>>>>> +	debug("fb=%lx, size=%d %d\n", fbbase,
>>>>> +	      uc_priv->xsize, uc_priv->ysize);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +int rk_vop2_probe(struct udevice *dev)
>>>>> +{
>>>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
>>>>> +	int ret = 0;
>>>>> +	ofnode port, node;
>>>>> +
>>>>> +	/* Before relocation we don't need to do anything */
>>>>> +	if (!(gd->flags & GD_FLG_RELOC))
>>>>> +		return 0;
>>>>> +
>>>>> +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
>>>>> +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
>>>>> +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
>>>>> +	}
>>>>> +
>>>>> +	priv->regs = dev_read_addr_ptr(dev);
>>>>> +
>>>>> +	/* Try all the ports until we find one that works. */
>>>>> +	port = dev_read_subnode(dev, "ports");
>>>>> +	if (!ofnode_valid(port)) {
>>>>> +		debug("%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 = rk_display_init(dev, plat->base, node);
>>>>> +		if (ret)
>>>>> +			debug("Device failed: ret=%d\n", ret);
>>>>> +		if (!ret)
>>>>> +			break;
>>>>> +	}
>>>>> +	video_set_flush_dcache(dev, true);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +int rk_vop2_bind(struct udevice *dev)
>>>>> +{
>>>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>>>>> +
>>>>> +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
>>>>> +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
>>>>> new file mode 100644
>>>>> index 00000000000..5d668070014
>>>>> --- /dev/null
>>>>> +++ b/drivers/video/rockchip/rk_vop2.h
>>>>> @@ -0,0 +1,76 @@
>>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>>> +/*
>>>>> + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
>>>>> + */
>>>>> +
>>>>> +#ifndef __RK_VOP2_H__
>>>>> +#define __RK_VOP2_H__
>>>>> +
>>>>> +#include <asm/arch-rockchip/vop_rk3568.h>
>>>>> +
>>>>> +struct rk_vop2_priv {
>>>>> +	void *grf;
>>>>> +	void *regs;
>>>>> +	int vp;
>>>>> +	int layer;
>>>>> +};
>>>>> +
>>>>> +enum vop2_features {
>>>>> +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
>>>>> +};
>>>>> +
>>>>> +enum vop2_layer {
>>>>> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
>>>>> +	ROCKCHIP_VOP2_CLUSTER1,
>>>>> +	ROCKCHIP_VOP2_CLUSTER2,
>>>>> +	ROCKCHIP_VOP2_CLUSTER3,
>>>>> +	ROCKCHIP_VOP2_ESMART0,
>>>>> +	ROCKCHIP_VOP2_ESMART1,
>>>>> +	ROCKCHIP_VOP2_ESMART2,
>>>>> +	ROCKCHIP_VOP2_ESMART3,
>>>>> +	ROCKCHIP_VOP2_SMART0 = 6,
>>>>
>>>> Why is it necessary to assign a value?
>>>>
>>> SMART0/1 shares the same address and map as ESMART2/3.
>>>
>>
>> That make sense. Please add some comments to describe this :)
>>
>> -- 
>> Best, 
>> Chaoyi
> 
> 

-- 
Best, 
Chaoyi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 06/12] video: rockchip: Add VOP2 support
  2025-12-29  3:13           ` Chaoyi Chen
@ 2025-12-30  5:42             ` Dang Huynh
  0 siblings, 0 replies; 25+ messages in thread
From: Dang Huynh @ 2025-12-30  5:42 UTC (permalink / raw)
  To: Chaoyi Chen
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Peter Robinson, Miquel Raynal,
	Jonas Karlman, Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski, Andy Yan, Sandy Huang

On Mon, Dec 29, 2025 at 11:13:40AM +0800, Chaoyi Chen wrote:
> On 12/25/2025 2:35 PM, Dang Huynh wrote:
> > Hi Chaoyi,
> > 
> > On Tue, Dec 23, 2025 at 09:34:54AM +0800, Chaoyi Chen wrote:
> >> Hello Dang,
> >>
> >> On 12/18/2025 12:47 PM, Dang Huynh wrote:
> >>> On Mon, Nov 10, 2025 at 11:24:01AM +0800, Chaoyi Chen wrote:
> >>>> On 11/8/2025 1:38 PM, Dang Huynh via B4 Relay wrote:
> >>>>
> >>>>> From: Dang Huynh <dang.huynh@mainlining.org>
> >>>>>
> >>>>> VOP2 (Video Output Processor v2) is a display controller on Rockchip
> >>>>> SoCs. It can be found on RK3566/8 and RK3588.
> >>>>>
> >>>>> This commit currently only supports RK3566/8.
> >>>>>
> >>>>> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> >>>>> ---
> >>>>>   arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 280 +++++++++++++
> >>>>>   drivers/video/rockchip/Makefile                 |   3 +-
> >>>>>   drivers/video/rockchip/rk3568_vop.c             | 260 ++++++++++++
> >>>>>   drivers/video/rockchip/rk_vop2.c                | 520 ++++++++++++++++++++++++
> >>>>>   drivers/video/rockchip/rk_vop2.h                |  76 ++++
> >>>>>   5 files changed, 1138 insertions(+), 1 deletion(-)
> >>>>
> >>>> [...]
> >>>>
> >>>>> +
> >>>>> +/*
> >>>>> + * RK3566 datasheet omits the VP2, even though it exist in the hardware
> >>>>> + * so let's not use it.
> >>>>> + */
> >>>>> +struct rkvop2_platdata rk3566_platdata = {
> >>>>> +	.delay = 20,
> >>>>> +	.bg_dly = {42, 40, -1},
> >>>>> +	/* SMART0, ESMART0 */
> >>>>> +	.vp_lyr = {3, 2, -1},
> >>>>
> >>>> For RK3566, this should be "SMART0, SMART1" or "ESMART0, ESMART1".
> >>>>
> >>>> You can not use esmart and smart layers at the same time. And the two layers need to have the same MST address.
> >>>>
> >>> You can use ESMART0 and SMART0 at the same time? That's how Linux 6.18 would configure VOP for both VP0 and VP1.
> >>>
> >>
> >> No. I mean you can not use SMART0 for VP0 and ESMART0 for VP1. They
> >> should be same win type.
> >>
> > Given in v2 I've been told by Andy to made it platform-specific..
> > https://lore.kernel.org/all/79e71a91.ebe.1955eb65686.Coremail.andyshrk@163.com/
> >
> 
> Sorry, I misunderstood you point. The code is okay for me.
> 
> > How does Linux turn off the layer if we aren't resetting the controller?
> > Because currently the (E)SMART layer is disabled before booting Linux or VOP2 will malfunction.
> > 
> 
> And this seems to be another problem... You've already disabled them in U-Boot,
> haven't you?
> 
Yes, currently U-Boot will disable layers before leaving U-Boot.

> >>>>
> >>>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_RESERVED,
> >>>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> >>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> >>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED},
> >>>>> +};
> >>>>> +
> >>>>> +struct rkvop2_platdata rk3568_platdata = {
> >>>>> +	.delay = 20,
> >>>>> +	.bg_dly = {42, 40, 40},
> >>>>> +	/* SMART0, SMART1, ESMART1 */
> >>>>> +	.vp_lyr = {3, 7, 6},
> >>>>> +	.layers = {ROCKCHIP_VOP2_CLUSTER0, ROCKCHIP_VOP2_CLUSTER1,
> >>>>> +		   ROCKCHIP_VOP2_ESMART0, ROCKCHIP_VOP2_SMART0,
> >>>>> +		   ROCKCHIP_VOP2_RESERVED, ROCKCHIP_VOP2_RESERVED,
> >>>>> +		   ROCKCHIP_VOP2_ESMART1, ROCKCHIP_VOP2_SMART1},
> >>>>> +};
> >>>>> +
> >>>>> +struct rkvop2_driverdata rk3566_driverdata = {
> >>>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> >>>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
> >>>>> +	.enable_output = rk3568_enable_output,
> >>>>> +	.platdata = &rk3566_platdata,
> >>>>> +};
> >>>>> +
> >>>>> +struct rkvop2_driverdata rk3568_driverdata = {
> >>>>> +	.features = VOP_FEATURE_OUTPUT_10BIT,
> >>>>> +	.set_pin_polarity = rk3568_set_pin_polarity,
> >>>>> +	.enable_output = rk3568_enable_output,
> >>>>> +	.platdata = &rk3568_platdata,
> >>>>> +};
> >>>>> +
> >>>>> +static const struct udevice_id rk3568_vop_ids[] = {
> >>>>> +	{ .compatible = "rockchip,rk3566-vop",
> >>>>> +	  .data = (ulong)&rk3566_driverdata },
> >>>>> +	{ .compatible = "rockchip,rk3568-vop",
> >>>>> +	  .data = (ulong)&rk3568_driverdata },
> >>>>> +	{ }
> >>>>> +};
> >>>>> +
> >>>>> +static const struct video_ops rk3568_vop_ops = {
> >>>>> +};
> >>>>> +
> >>>>> +U_BOOT_DRIVER(rk3568_vop) = {
> >>>>> +	.name	= "rk3568_vop",
> >>>>> +	.id	= UCLASS_VIDEO,
> >>>>> +	.of_match = rk3568_vop_ids,
> >>>>> +	.ops	= &rk3568_vop_ops,
> >>>>> +	.bind	= rk_vop2_bind,
> >>>>> +	.probe	= rk3568_vop_probe,
> >>>>> +	.remove = rk3568_vop_remove,
> >>>>> +	.priv_auto = sizeof(struct rk_vop2_priv),
> >>>>> +#if CONFIG_IS_ENABLED(VIDEO_REMOVE)
> >>>>> +	.flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
> >>>>> +#else
> >>>>> +	.flags = DM_FLAG_PRE_RELOC,
> >>>>> +#endif
> >>>>> +};
> >>>>> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> >>>>> new file mode 100644
> >>>>> index 00000000000..992f215d416
> >>>>> --- /dev/null
> >>>>> +++ b/drivers/video/rockchip/rk_vop2.c
> >>>>> @@ -0,0 +1,520 @@
> >>>>> +// SPDX-License-Identifier: GPL-2.0
> >>>>> +/*
> >>>>> + * Copyright (c) 2024 - 2025 Dang Huynh <dang.huynh@mainlining.org>
> >>>>> + *
> >>>>> + * Based on rk_vop.c:
> >>>>> + *   Copyright (c) 2015 Google, Inc
> >>>>> + *   Copyright 2014 Rockchip Inc.
> >>>>> + */
> >>>>> +
> >>>>> +#include <clk.h>
> >>>>> +#include <display.h>
> >>>>> +#include <dm.h>
> >>>>> +#include <dm/device_compat.h>
> >>>>> +#include <edid.h>
> >>>>> +#include <log.h>
> >>>>> +#include <regmap.h>
> >>>>> +#include <reset.h>
> >>>>> +#include <syscon.h>
> >>>>> +#include <video.h>
> >>>>> +#include <asm/global_data.h>
> >>>>> +#include <asm/gpio.h>
> >>>>> +#include <asm/io.h>
> >>>>> +#include <asm/arch-rockchip/clock.h>
> >>>>> +#include <asm/arch-rockchip/vop_rk3568.h>
> >>>>> +#include <dm/device-internal.h>
> >>>>> +#include <dm/uclass-internal.h>
> >>>>> +#include <efi.h>
> >>>>> +#include <efi_loader.h>
> >>>>> +#include <linux/bitops.h>
> >>>>> +#include <linux/err.h>
> >>>>> +#include <power/regulator.h>
> >>>>> +
> >>>>> +#include "rk_vop2.h"
> >>>>> +
> >>>>> +DECLARE_GLOBAL_DATA_PTR;
> >>>>> +
> >>>>> +enum vop_pol {
> >>>>> +	HSYNC_POSITIVE = 0,
> >>>>> +	VSYNC_POSITIVE = 1,
> >>>>> +	DEN_NEGATIVE   = 2,
> >>>>> +	DCLK_INVERT    = 3
> >>>>> +};
> >>>>> +
> >>>>> +static void rkvop2_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)
> >>>>> +{
> >>>>> +	u32 reg;
> >>>>> +
> >>>>> +	reg = M_GLOBAL_REGDONE;
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * For RK3588, changes will only take effect when the same bit is
> >>>>> +	 * leftshifted by 16.
> >>>>> +	 */
> >>>>> +	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
> >>>>> +
> >>>>> +	writel(reg, &sysctrl->reg_cfg_done);
> >>>>> +}
> >>>>> +
> >>>>> +static int rkvop2_enable(struct udevice *dev, ulong fbbase,
> >>>>> +			 int fb_bits_per_pixel, const struct display_timing *edid,
> >>>>> +			 int port, int win_id, struct rkvop2_platdata *platdata)
> >>>>> +{
> >>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>>>> +	struct rk3568_vop_overlay *overlay = priv->regs + VOP2_OVERLAY_OFFSET;
> >>>>> +	struct rk3568_vop_esmart *esmart;
> >>>>> +	bool is_cluster = false;
> >>>>> +	u8 layer;
> >>>>> +	u32 reg;
> >>>>> +	u32 rgb_mode;
> >>>>> +	u32 hactive = edid->hactive.typ;
> >>>>> +	u32 vactive = edid->vactive.typ;
> >>>>> +
> >>>>> +	if (platdata->layers[win_id] < 0)
> >>>>> +		return -EINVAL;
> >>>>> +
> >>>>> +	switch (platdata->layers[win_id]) {
> >>>>> +	case ROCKCHIP_VOP2_CLUSTER0:
> >>>>> +	case ROCKCHIP_VOP2_CLUSTER1:
> >>>>> +	case ROCKCHIP_VOP2_CLUSTER2:
> >>>>> +	case ROCKCHIP_VOP2_CLUSTER3:
> >>>>> +		is_cluster = true;
> >>>>> +		break;
> >>>>> +	default:
> >>>>> +		break;
> >>>>> +	}
> >>>>> +
> >>>>> +	layer = platdata->layers[win_id];
> >>>>> +
> >>>>> +	debug("(%s, %s): win_id = %d - layer = %d - cluster: %d\n",
> >>>>> +	      dev_read_name(dev),  __func__, win_id, layer, is_cluster);
> >>>>> +
> >>>>> +	/* TODO: Support VOP2 CLUSTER */
> >>>>> +	if (is_cluster) {
> >>>>> +		dev_err(dev, "win_id is a cluster, not supported.\n");
> >>>>> +		return -ENOSYS;
> >>>>> +	}
> >>>>> +
> >>>>> +	esmart = priv->regs + VOP2_ESMART_OFFSET(layer - 4);
> >>>>> +
> >>>>> +	debug("(%s, %s): esmart addr: 0x%p\n", dev_read_name(dev), __func__, esmart);
> >>>>> +
> >>>>> +	writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
> >>>>> +	       &esmart->esmart_region0_act_info);
> >>>>> +
> >>>>> +	/* Set offset to 0,0 */
> >>>>> +	writel(0, &esmart->esmart_region0_dsp_offset);
> >>>>> +
> >>>>> +	writel(V_DSP_WIDTH(hactive - 1) |
> >>>>> +			V_DSP_HEIGHT(vactive - 1),
> >>>>> +			&esmart->esmart_region0_dsp_info);
> >>>>> +
> >>>>> +	switch (fb_bits_per_pixel) {
> >>>>> +	case 16:
> >>>>> +		rgb_mode = RGB565;
> >>>>> +		writel(V_RGB565_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>>>> +		break;
> >>>>> +	case 24:
> >>>>> +		rgb_mode = RGB888;
> >>>>> +		writel(V_RGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>>>> +		break;
> >>>>> +	case 32:
> >>>>> +	default:
> >>>>> +		rgb_mode = ARGB8888;
> >>>>> +		writel(V_ARGB888_VIRWIDTH(hactive), &esmart->esmart_region0_vir);
> >>>>> +		break;
> >>>>> +	}
> >>>>> +
> >>>>> +	writel(fbbase, &esmart->esmart_region0_mst_yrgb);
> >>>>> +
> >>>>> +	writel(V_ESMART_REGION0_DATA_FMT(rgb_mode) | M_ESMART_REGION0_MST_EN,
> >>>>> +	       &esmart->esmart_region0_mst_ctl);
> >>>>> +
> >>>>> +	/* Set esmart to the destination video port */
> >>>>> +	reg = V_ESMART_SEL_PORT(layer - 4, port);
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * VOP2 requires every port mux to be configured.
> >>>>> +	 *
> >>>>> +	 * As U-Boot only supports singledisplay, we'll set all
> >>>>> +	 * unused ports to set layer to 8 (disabled).
> >>>>> +	 */
> >>>>> +	for (int i = 0; i < 4; i++) {
> >>>>> +		if (i != port)
> >>>>> +			reg |= V_PORT_MUX(8, i);
> >>>>> +	}
> >>>>> +
> >>>>> +	writel(reg, &overlay->port_sel);
> >>>>> +
> >>>>> +	/* Set layer 0 to win_id */
> >>>>> +	writel(V_LAYER_SEL(0, win_id), &overlay->layer_sel);
> >>>>> +
> >>>>> +	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
> >>>>> +	writel(reg, &overlay->overlay_ctrl);
> >>>>> +
> >>>>> +	priv->layer = layer;
> >>>>> +
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static void rkvop2_set_pin_polarity(struct udevice *dev,
> >>>>> +				    enum vop_modes mode, u32 polarity)
> >>>>> +{
> >>>>> +	struct rkvop2_driverdata *ops =
> >>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>>>> +
> >>>>> +	if (ops->set_pin_polarity)
> >>>>> +		ops->set_pin_polarity(dev, mode, polarity);
> >>>>> +}
> >>>>> +
> >>>>> +static void rkvop2_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
> >>>>> +{
> >>>>> +	struct rkvop2_driverdata *ops =
> >>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>>>> +
> >>>>> +	if (ops->enable_output)
> >>>>> +		ops->enable_output(dev, mode, port);
> >>>>> +}
> >>>>> +
> >>>>> +static void rkvop2_mode_set(struct udevice *dev,
> >>>>> +			    const struct display_timing *edid,
> >>>>> +		enum vop_modes mode, int port,
> >>>>> +		struct rkvop2_platdata *platdata)
> >>>>> +{
> >>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>>>> +	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
> >>>>> +	struct rk3568_vop_post *post = priv->regs + VOP2_POST_OFFSET(port);
> >>>>> +	struct rkvop2_driverdata *data =
> >>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>>>> +
> >>>>> +	debug("(%s, %s): port addr: 0x%p\n", dev_read_name(dev), __func__, post);
> >>>>> +
> >>>>> +	u32 hactive = edid->hactive.typ;
> >>>>> +	u32 vactive = edid->vactive.typ;
> >>>>> +	u32 hsync_len = edid->hsync_len.typ;
> >>>>> +	u32 hback_porch = edid->hback_porch.typ;
> >>>>> +	u32 vsync_len = edid->vsync_len.typ;
> >>>>> +	u32 vback_porch = edid->vback_porch.typ;
> >>>>> +	u32 hfront_porch = edid->hfront_porch.typ;
> >>>>> +	u32 vfront_porch = edid->vfront_porch.typ;
> >>>>> +	int mode_flags;
> >>>>> +	u32 pin_polarity;
> >>>>> +	u32 reg;
> >>>>> +
> >>>>> +	pin_polarity = BIT(DCLK_INVERT);
> >>>>> +	if (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH)
> >>>>> +		pin_polarity |= BIT(HSYNC_POSITIVE);
> >>>>> +	if (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH)
> >>>>> +		pin_polarity |= BIT(VSYNC_POSITIVE);
> >>>>> +
> >>>>> +	rkvop2_enable_output(dev, mode, port);
> >>>>> +	rkvop2_set_pin_polarity(dev, mode, pin_polarity);
> >>>>> +
> >>>>> +	mode_flags = 0;  /* RGB888 */
> >>>>> +	if ((data->features & VOP_FEATURE_OUTPUT_10BIT) &&
> >>>>> +	    mode == VOP_MODE_HDMI)
> >>>>> +		mode_flags = 15;  /* RGBaaa */
> >>>>
> >>>> You should also set RGBaaa for VOP_MODE_EDP.
> >>>>
> >>> Will do in next series.
> >>>
> >>>>
> >>>>> +
> >>>>> +	reg = V_DSP_OUT_MODE(mode_flags);
> >>>>> +
> >>>>> +	debug("(%s, %s): bg_dly: %d\n",
> >>>>> +	      dev_read_name(dev), __func__, platdata->bg_dly[port]);
> >>>>> +
> >>>>> +	if (platdata->bg_dly[port] < 0) {
> >>>>> +		dev_err(dev, "bg_dly is zero for vp%d\n", port);
> >>>>> +		return;
> >>>>> +	}
> >>>>> +
> >>>>> +	writel(((platdata->bg_dly[port] + (hactive >> 1) - 1) << 16) | hsync_len,
> >>>>> +	       &post->prescan_htimings);
> >>>>> +
> >>>>> +	writel(V_HSYNC(hsync_len) |
> >>>>> +			V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
> >>>>> +			&post->dsp_htotal_hs_end);
> >>>>> +
> >>>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> >>>>> +			V_HASP(hsync_len + hback_porch),
> >>>>> +			&post->dsp_hact_st_end);
> >>>>> +
> >>>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> >>>>> +			V_VASP(vsync_len + vback_porch),
> >>>>> +			&post->dsp_vact_st_end);
> >>>>> +
> >>>>> +	writel(V_VSYNC(vsync_len) |
> >>>>> +			V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
> >>>>> +			&post->dsp_vtotal_vs_end);
> >>>>> +
> >>>>> +	writel(V_HEAP(hsync_len + hback_porch + hactive) |
> >>>>> +			V_HASP(hsync_len + hback_porch),
> >>>>> +			&post->dsp_hact_info);
> >>>>> +
> >>>>> +	writel(V_VAEP(vsync_len + vback_porch + vactive) |
> >>>>> +			V_VASP(vsync_len + vback_porch),
> >>>>> +			&post->dsp_vact_info);
> >>>>> +
> >>>>> +	/* No scaling */
> >>>>> +	writel(0x10001000, &post->scl_factor_yrgb);
> >>>>> +
> >>>>> +	writel(reg, &post->dsp_ctrl);
> >>>>> +
> >>>>> +	rkvop2_cfg_regdone(sysctrl, port);
> >>>>> +}
> >>>>> +
> >>>>> +/**
> >>>>> + * rk_display_init() - Try to enable the given display device
> >>>>> + *
> >>>>> + * This function performs many steps:
> >>>>> + * - Finds the display device being referenced by @ep_node
> >>>>> + * - Puts the VOP's ID into its uclass platform data
> >>>>> + * - Probes the device to set it up
> >>>>> + * - Reads the timing information (from EDID or panel)
> >>>>> + * - Sets up the VOP clocks, etc. for the selected pixel clock and display mode
> >>>>> + * - Enables the display (the display device handles this and will do different
> >>>>> + *     things depending on the display type)
> >>>>> + * - Tells the uclass about the display resolution so that the console will
> >>>>> + *     appear correctly
> >>>>> + *
> >>>>> + * @dev:	VOP device that we want to connect to the display
> >>>>> + * @fbbase:	Frame buffer address
> >>>>> + * @vp_node:	Device tree node to process
> >>>>> + * Return: 0 if OK, -ve if something went wrong
> >>>>> + */
> >>>>> +static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >>>>> +{
> >>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>>>> +	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
> >>>>> +	struct rkvop2_driverdata *drvdata =
> >>>>> +		(struct rkvop2_driverdata *)dev_get_driver_data(dev);
> >>>>> +	struct rkvop2_platdata *platdata =
> >>>>> +		(struct rkvop2_platdata *)drvdata->platdata;
> >>>>> +	ofnode ep_node;
> >>>>> +	int vop_id, port_id, win_id;
> >>>>> +	struct display_timing timing;
> >>>>> +	struct udevice *disp;
> >>>>> +	int ret;
> >>>>> +	u32 remote_phandle;
> >>>>> +	struct display_plat *disp_uc_plat;
> >>>>> +	enum video_log2_bpp l2bpp;
> >>>>> +	ofnode remote;
> >>>>> +	const char *compat;
> >>>>> +	char dclk_name[9];
> >>>>> +	struct clk dclk;
> >>>>> +
> >>>>> +	debug("%s(%s, 0x%lx, %s)\n", __func__,
> >>>>> +	      dev_read_name(dev), fbbase, ofnode_get_name(vp_node));
> >>>>> +
> >>>>> +	port_id = ofnode_read_u32_default(vp_node, "reg", -1);
> >>>>> +	if (port_id < 0) {
> >>>>> +		debug("%s(%s): no video port id\n", __func__, dev_read_name(dev));
> >>>>> +		return port_id;
> >>>>> +	}
> >>>>> +
> >>>>> +	ep_node = ofnode_first_subnode(vp_node);
> >>>>> +	if (!ofnode_valid(ep_node)) {
> >>>>> +		debug("%s(%s): no valid subnode\n", __func__, dev_read_name(dev));
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +
> >>>>> +	ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
> >>>>> +	if (ret) {
> >>>>> +		debug("%s(%s): no remote-endpoint\n", __func__, dev_read_name(dev));
> >>>>> +		return ret;
> >>>>> +	}
> >>>>> +
> >>>>> +	remote = ofnode_get_by_phandle(remote_phandle);
> >>>>> +	if (!ofnode_valid(remote))
> >>>>> +		return -EINVAL;
> >>>>> +
> >>>>> +	remote = ofnode_get_parent(remote);
> >>>>> +	if (!ofnode_valid(remote))
> >>>>> +		return -EINVAL;
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * The remote-endpoint references into a subnode of the encoder
> >>>>> +	 * (i.e. HDMI, MIPI, etc.) with the DTS looking something like
> >>>>> +	 * the following:
> >>>>> +	 *
> >>>>> +	 * hdmi: hdmi@fe0a0000 {
> >>>>> +	 *   ports {
> >>>>> +	 *     hdmi_in: port {
> >>>>> +	 *       hdmi_in_vp0: endpoint { ... };
> >>>>> +	 *     }
> >>>>> +	 *   }
> >>>>> +	 * }
> >>>>> +	 *
> >>>>> +	 * This isn't any different from how VOP1 works, so we'll adapt
> >>>>> +	 * the same method of finding the display from the original code
> >>>>> +	 * (find the enclosing device of "UCLASS_DISPLAY")
> >>>>> +	 *
> >>>>> +	 * We also look for UCLASS_VIDEO_BRIDGE so we can use the existing
> >>>>> +	 * DW MIPI DSI driver for Rockchip.
> >>>>> +	 */
> >>>>> +	while (ofnode_valid(remote)) {
> >>>>> +		remote = ofnode_get_parent(remote);
> >>>>> +		if (!ofnode_valid(remote)) {
> >>>>> +			debug("%s(%s): no UCLASS_DISPLAY for remote-endpoint\n",
> >>>>> +			      __func__, dev_read_name(dev));
> >>>>> +			return -EINVAL;
> >>>>> +		}
> >>>>> +
> >>>>> +		uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> >>>>> +		if (disp)
> >>>>> +			break;
> >>>>> +	};
> >>>>> +	compat = ofnode_get_property(remote, "compatible", NULL);
> >>>>> +	if (!compat) {
> >>>>> +		debug("%s(%s): Failed to find compatible property\n",
> >>>>> +		      __func__, dev_read_name(dev));
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +	if (strstr(compat, "edp")) {
> >>>>> +		vop_id = VOP_MODE_EDP;
> >>>>> +	} else if (strstr(compat, "mipi")) {
> >>>>> +		vop_id = VOP_MODE_MIPI;
> >>>>> +	} else if (strstr(compat, "hdmi")) {
> >>>>> +		vop_id = VOP_MODE_HDMI;
> >>>>> +	} else if (strstr(compat, "rk3588-dp")) {
> >>>>
> >>>> Can we directly use "dp" ?
> >>>>
> >>> We can, however if the device tree is misconfigured then it'll pick up VP connected devices with "dp" (such
> >>> as "rk3588-h*dp*tx-phy") and consider that to be a displayport device.
> >>>
> >>>>
> >>>>> +		vop_id = VOP_MODE_DP;
> >>>>> +	} else if (strstr(compat, "lvds")) {
> >>>>> +		vop_id = VOP_MODE_LVDS;
> >>>>> +	} else {
> >>>>> +		debug("%s(%s): Failed to find vop mode for %s\n",
> >>>>> +		      __func__, dev_read_name(dev), compat);
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +	debug("vop_id=%d - port=%d\n", vop_id, port_id);
> >>>>> +
> >>>>> +	/* Get the video port clock and enable it */
> >>>>> +	snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", port_id);
> >>>>> +	ret = clk_get_by_name(dev, dclk_name, &dclk);
> >>>>> +	if (ret < 0)
> >>>>> +		return ret;
> >>>>> +
> >>>>> +	disp_uc_plat = dev_get_uclass_plat(disp);
> >>>>> +	debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> >>>>> +	if (display_in_use(disp)) {
> >>>>> +		debug("   - device in use\n");
> >>>>> +		return -EBUSY;
> >>>>> +	}
> >>>>> +
> >>>>> +	disp_uc_plat->source_id = vop_id;
> >>>>> +	disp_uc_plat->src_dev = dev;
> >>>>> +
> >>>>> +	ret = device_probe(disp);
> >>>>> +	if (ret) {
> >>>>> +		debug("%s: device '%s' display won't probe (ret=%d)\n",
> >>>>> +		      __func__, dev->name, ret);
> >>>>> +		return ret;
> >>>>> +	}
> >>>>> +
> >>>>> +	ret = display_read_timing(disp, &timing);
> >>>>> +	if (ret) {
> >>>>> +		debug("%s: Failed to read timings\n", __func__);
> >>>>> +		return ret;
> >>>>> +	}
> >>>>> +
> >>>>> +	/* Set clock rate on video port to display timings */
> >>>>> +	ret = clk_set_rate(&dclk, timing.pixelclock.typ);
> >>>>> +	if (ret < 0) {
> >>>>> +		dev_err(dev, "Failed to set clock rate: %d\n", ret);
> >>>>> +		return ret;
> >>>>> +	}
> >>>>> +
> >>>>> +	debug("%s(%s): %s clkrate %lu\n", __func__, dev_read_name(dev),
> >>>>> +	      dclk_name, clk_get_rate(&dclk));
> >>>>> +
> >>>>> +	/* Set bitwidth for vop display according to vop mode */
> >>>>> +	switch (vop_id) {
> >>>>> +	case VOP_MODE_EDP:
> >>>>> +	case VOP_MODE_MIPI:
> >>>>> +	case VOP_MODE_HDMI:
> >>>>> +	case VOP_MODE_DP:
> >>>>> +	case VOP_MODE_LVDS:
> >>>>> +		l2bpp = VIDEO_BPP32;
> >>>>> +		break;
> >>>>> +	default:
> >>>>> +		l2bpp = VIDEO_BPP16;
> >>>>
> >>>> It seems that we always end up with VIDEO_BPP32?
> >>>>
> >>> U-Boot doesn't support VIDEO_BPP24 format.
> >>>
> >>>>
> >>>>> +	}
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * We'll use the default platform-specific win_id from Linux
> >>>>> +	 * so that Linux can take over U-Boot plane when Linux reconfigures
> >>>>> +	 * VOP2.
> >>>>> +	 */
> >>>>> +	win_id = platdata->vp_lyr[port_id];
> >>>>> +	if (win_id < 0) {
> >>>>> +		dev_err(dev, "win_id is null, don't setup\n");
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +
> >>>>> +	ret = rkvop2_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, win_id, platdata);
> >>>>> +	if (ret < 0)
> >>>>> +		return ret;
> >>>>> +
> >>>>> +	rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> >>>>> +
> >>>>> +	ret = display_enable(disp, 1 << l2bpp, &timing);
> >>>>> +	if (ret)
> >>>>> +		return ret;
> >>>>> +
> >>>>> +	uc_priv->xsize = timing.hactive.typ;
> >>>>> +	uc_priv->ysize = timing.vactive.typ;
> >>>>> +	uc_priv->bpix = l2bpp;
> >>>>> +
> >>>>> +	priv->vp = port_id;
> >>>>> +
> >>>>> +	debug("fb=%lx, size=%d %d\n", fbbase,
> >>>>> +	      uc_priv->xsize, uc_priv->ysize);
> >>>>> +
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +int rk_vop2_probe(struct udevice *dev)
> >>>>> +{
> >>>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> >>>>> +	struct rk_vop2_priv *priv = dev_get_priv(dev);
> >>>>> +	int ret = 0;
> >>>>> +	ofnode port, node;
> >>>>> +
> >>>>> +	/* Before relocation we don't need to do anything */
> >>>>> +	if (!(gd->flags & GD_FLG_RELOC))
> >>>>> +		return 0;
> >>>>> +
> >>>>> +	if (IS_ENABLED(CONFIG_EFI_LOADER)) {
> >>>>> +		debug("Adding to EFI map %d @ %lx\n", plat->size, plat->base);
> >>>>> +		efi_add_memory_map(plat->base, plat->size, EFI_RESERVED_MEMORY_TYPE);
> >>>>> +	}
> >>>>> +
> >>>>> +	priv->regs = dev_read_addr_ptr(dev);
> >>>>> +
> >>>>> +	/* Try all the ports until we find one that works. */
> >>>>> +	port = dev_read_subnode(dev, "ports");
> >>>>> +	if (!ofnode_valid(port)) {
> >>>>> +		debug("%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 = rk_display_init(dev, plat->base, node);
> >>>>> +		if (ret)
> >>>>> +			debug("Device failed: ret=%d\n", ret);
> >>>>> +		if (!ret)
> >>>>> +			break;
> >>>>> +	}
> >>>>> +	video_set_flush_dcache(dev, true);
> >>>>> +
> >>>>> +	return ret;
> >>>>> +}
> >>>>> +
> >>>>> +int rk_vop2_bind(struct udevice *dev)
> >>>>> +{
> >>>>> +	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> >>>>> +
> >>>>> +	plat->size = 4 * (CONFIG_VIDEO_ROCKCHIP_MAX_XRES *
> >>>>> +			CONFIG_VIDEO_ROCKCHIP_MAX_YRES);
> >>>>> +
> >>>>> +	return 0;
> >>>>> +}
> >>>>> diff --git a/drivers/video/rockchip/rk_vop2.h b/drivers/video/rockchip/rk_vop2.h
> >>>>> new file mode 100644
> >>>>> index 00000000000..5d668070014
> >>>>> --- /dev/null
> >>>>> +++ b/drivers/video/rockchip/rk_vop2.h
> >>>>> @@ -0,0 +1,76 @@
> >>>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>>> +/*
> >>>>> + * Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> >>>>> + */
> >>>>> +
> >>>>> +#ifndef __RK_VOP2_H__
> >>>>> +#define __RK_VOP2_H__
> >>>>> +
> >>>>> +#include <asm/arch-rockchip/vop_rk3568.h>
> >>>>> +
> >>>>> +struct rk_vop2_priv {
> >>>>> +	void *grf;
> >>>>> +	void *regs;
> >>>>> +	int vp;
> >>>>> +	int layer;
> >>>>> +};
> >>>>> +
> >>>>> +enum vop2_features {
> >>>>> +	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
> >>>>> +};
> >>>>> +
> >>>>> +enum vop2_layer {
> >>>>> +	ROCKCHIP_VOP2_CLUSTER0 = 0,
> >>>>> +	ROCKCHIP_VOP2_CLUSTER1,
> >>>>> +	ROCKCHIP_VOP2_CLUSTER2,
> >>>>> +	ROCKCHIP_VOP2_CLUSTER3,
> >>>>> +	ROCKCHIP_VOP2_ESMART0,
> >>>>> +	ROCKCHIP_VOP2_ESMART1,
> >>>>> +	ROCKCHIP_VOP2_ESMART2,
> >>>>> +	ROCKCHIP_VOP2_ESMART3,
> >>>>> +	ROCKCHIP_VOP2_SMART0 = 6,
> >>>>
> >>>> Why is it necessary to assign a value?
> >>>>
> >>> SMART0/1 shares the same address and map as ESMART2/3.
> >>>
> >>
> >> That make sense. Please add some comments to describe this :)
> >>
> >> -- 
> >> Best, 
> >> Chaoyi
> > 
> > 
> 
> -- 
> Best, 
> Chaoyi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 00/12] Rockchip VOP2 support
  2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
                   ` (11 preceding siblings ...)
  2025-11-08  5:38 ` [PATCH v6 12/12] clk: rockchip: rk3568: Use assigned VPLL clock when possible Dang Huynh via B4 Relay
@ 2026-01-08 22:14 ` Stefan Monnier
  2026-01-09  1:41   ` Peter Robinson
  12 siblings, 1 reply; 25+ messages in thread
From: Stefan Monnier @ 2026-01-08 22:14 UTC (permalink / raw)
  To: u-boot

Dang Huynh via B4 Relay [2025-11-08 12:37:56] wrote:
> This series adds VOP2 support to U-Boot and enable it for PineTab2.
> HDMI should work flawlessly but there's still some timings issue with
> DW MIPI DSI, perhaps that's something to look into later.

I'm trying to use this patch on my NanoPi R5S and the result is not
quite what I expect.  I suspect I'm doing something wrong.

I applied the above patch to the tip of the current u-boot (there was
a trivial conflict in a Makefile, but nothing of significance).
Then I applied the following patch:

    diff --git a/configs/nanopi-r5s-rk3568_defconfig b/configs/nanopi-r5s-rk3568_defconfig
    index 1653ab8b44f..d707dd5bc29 100644
    --- a/configs/nanopi-r5s-rk3568_defconfig
    +++ b/configs/nanopi-r5s-rk3568_defconfig
    @@ -56,6 +56,7 @@ CONFIG_DWC_ETH_QOS_ROCKCHIP=y
     CONFIG_RTL8169=y
     CONFIG_NVME_PCI=y
     CONFIG_PCIE_DW_ROCKCHIP=y
    +CONFIG_PHY_ROCKCHIP_INNO_HDMI=y
     CONFIG_PHY_ROCKCHIP_INNO_USB2=y
     CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
     CONFIG_SPL_PINCTRL=y
    @@ -80,4 +81,12 @@ CONFIG_SPL_USB_DWC3_GENERIC=y
     CONFIG_USB_GADGET=y
     CONFIG_USB_GADGET_DOWNLOAD=y
     CONFIG_USB_FUNCTION_ROCKUSB=y
    +CONFIG_USB_KEYBOARD=y
    +CONFIG_VIDEO=y
    +CONFIG_DISPLAY=y
    +CONFIG_VIDEO_ROCKCHIP=y
    +CONFIG_DISPLAY_ROCKCHIP_HDMI=y
    +CONFIG_VIDEO_DT_SIMPLEFB=y
    +CONFIG_VIDEO_BRIDGE=y
    +CONFIG_VIDEO_REMOVE=y
     CONFIG_ERRNO_STR=y

I also changed the U-Boot config to extend the autoboot timeout to 30s,
so I have plenty of time to hit a key on the keyboard.

and the result is as follows:

- A U-Boot logo greets me at the top-right corner of the screen.
  I'm really happy to see this logo, shows something is right with the
  HDMI code&config.
- The rest is all black, no text.
- Hitting a key on the USB-connected keyboard seems to have no effect:
  the screen stays desperately all black except for the U-Boot logo.
- After the 30s timeout, the system boots as usual.

I tried to disconnect&reconnect the keyboard as well as tried both USB
ports on the device.  Same difference.  Why do I get to see the U-Boot
logo but not the usual bootup text?


        Stefan


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 00/12] Rockchip VOP2 support
  2026-01-08 22:14 ` [PATCH v6 00/12] Rockchip VOP2 support Stefan Monnier
@ 2026-01-09  1:41   ` Peter Robinson
  2026-01-09  4:07     ` Stefan Monnier
  0 siblings, 1 reply; 25+ messages in thread
From: Peter Robinson @ 2026-01-09  1:41 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: u-boot

On Thu, 8 Jan 2026 at 22:18, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>
> Dang Huynh via B4 Relay [2025-11-08 12:37:56] wrote:
> > This series adds VOP2 support to U-Boot and enable it for PineTab2.
> > HDMI should work flawlessly but there's still some timings issue with
> > DW MIPI DSI, perhaps that's something to look into later.
>
> I'm trying to use this patch on my NanoPi R5S and the result is not
> quite what I expect.  I suspect I'm doing something wrong.
>
> I applied the above patch to the tip of the current u-boot (there was
> a trivial conflict in a Makefile, but nothing of significance).
> Then I applied the following patch:
>
>     diff --git a/configs/nanopi-r5s-rk3568_defconfig b/configs/nanopi-r5s-rk3568_defconfig
>     index 1653ab8b44f..d707dd5bc29 100644
>     --- a/configs/nanopi-r5s-rk3568_defconfig
>     +++ b/configs/nanopi-r5s-rk3568_defconfig
>     @@ -56,6 +56,7 @@ CONFIG_DWC_ETH_QOS_ROCKCHIP=y
>      CONFIG_RTL8169=y
>      CONFIG_NVME_PCI=y
>      CONFIG_PCIE_DW_ROCKCHIP=y
>     +CONFIG_PHY_ROCKCHIP_INNO_HDMI=y
>      CONFIG_PHY_ROCKCHIP_INNO_USB2=y
>      CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
>      CONFIG_SPL_PINCTRL=y
>     @@ -80,4 +81,12 @@ CONFIG_SPL_USB_DWC3_GENERIC=y
>      CONFIG_USB_GADGET=y
>      CONFIG_USB_GADGET_DOWNLOAD=y
>      CONFIG_USB_FUNCTION_ROCKUSB=y
>     +CONFIG_USB_KEYBOARD=y
>     +CONFIG_VIDEO=y
>     +CONFIG_DISPLAY=y
>     +CONFIG_VIDEO_ROCKCHIP=y
>     +CONFIG_DISPLAY_ROCKCHIP_HDMI=y
>     +CONFIG_VIDEO_DT_SIMPLEFB=y
>     +CONFIG_VIDEO_BRIDGE=y
>     +CONFIG_VIDEO_REMOVE=y
>      CONFIG_ERRNO_STR=y
>
> I also changed the U-Boot config to extend the autoboot timeout to 30s,
> so I have plenty of time to hit a key on the keyboard.
>
> and the result is as follows:
>
> - A U-Boot logo greets me at the top-right corner of the screen.
>   I'm really happy to see this logo, shows something is right with the
>   HDMI code&config.
> - The rest is all black, no text.
> - Hitting a key on the USB-connected keyboard seems to have no effect:
>   the screen stays desperately all black except for the U-Boot logo.
> - After the 30s timeout, the system boots as usual.
>
> I tried to disconnect&reconnect the keyboard as well as tried both USB
> ports on the device.  Same difference.  Why do I get to see the U-Boot
> logo but not the usual bootup text?

I suspect you have two problems.

For the lack of text being displayed can you check if you have
CONFIG_SPLASH_SCREEN=y in your .config? That will suppress all the
text. Try a '# CONFIG_SPLASH_SCREEN is not set' line in the config to
see if that helps.

For the USB I have seen issues on and off with rockchips devices.
Adding the following might be a good debug to ensure it runs and scans
the USB bus:

CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="usb start;"

If you have a USB TTL you should be able to see it all on serial
console no matter what gets displayed on HDMI.

Peter

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 00/12] Rockchip VOP2 support
  2026-01-09  1:41   ` Peter Robinson
@ 2026-01-09  4:07     ` Stefan Monnier
  0 siblings, 0 replies; 25+ messages in thread
From: Stefan Monnier @ 2026-01-09  4:07 UTC (permalink / raw)
  To: u-boot

Peter Robinson [2026-01-09 01:41:41] wrote:
> I suspect you have two problems.
>
> For the lack of text being displayed can you check if you have
> CONFIG_SPLASH_SCREEN=y in your .config? That will suppress all the
> text. Try a '# CONFIG_SPLASH_SCREEN is not set' line in the config to
> see if that helps.

Hmm... that doesn't seem to be it:

    % grep -i splash .config
    CONFIG_MTDPARTS_DEFAULT="nor0:0x100000(reserved),0x200000(uboot),0x100000(splash),0xc00000(Firmware)"
    # CONFIG_SPLASH_SCREEN is not set
    %

> For the USB I have seen issues on and off with rockchips devices.
> Adding the following might be a good debug to ensure it runs and scans
> the USB bus:
>
> CONFIG_USE_PREBOOT=y
> CONFIG_PREBOOT="usb start;"

Interesting.  I have now enabled USE_PREBOOT (which set CONFIG_PREBOOT
for me):

    % grep -i preboot .config
    CONFIG_USE_PREBOOT=y
    CONFIG_PREBOOT="usb start"
    CONFIG_PREBOOT_DEFINED=y
    %

[ I notice the absence of ";" compared to your suggestion, but since that
  came from U-Boot's own config rather than from me, I assume it's OK.  ]

But that didn't seem to make any difference.

> If you have a USB TTL you should be able to see it all on serial
> console no matter what gets displayed on HDMI.

I have now tried it on my Odroid-M1 (using a similar patch for `configs/odroid-m1-rk3568_defconfig`), where I do have a serial connection
which lets me see what's going on (I'm specifically after the VOP2 code
for my NanoPi because that one doesn't have a ready-to-use connector for
the serial and I'd rather not have to take out my old soldering iron for
that).

I don't see the U-Boot logo on my Odroid-M1, and the screen just turns
off saying "no signal" (until it turns back on when the Linux kernel
finally loads the rockchip drm module), so the HDMI code/config doesn't
seem right.  On the serial console I see:

    U-Boot 2026.01-00518-g664b0ba1dcde (Jan 08 2026 - 22:33:46 -0500)
     
    Model: Hardkernel ODROID-M1
    SoC:   RK3568B2
    DRAM:  4 GiB
    PMIC:  RK809 (on=0x02, off=0x00)
    Core:  629 devices, 35 uclasses, devicetree: separate
    MMC:   mmc@fe2b0000: 1, mmc@fe310000: 0
    Loading Environment from nowhere... OK
    In:    serial
    Out:   serial
    Err:   serial
    Model: Hardkernel ODROID-M1
    SoC:   RK3568B2
    Net:   eth0: ethernet@fe2a0000
     
    starting USB...
    Starting the controller
    USB XHCI 1.10
    Starting the controller
    USB XHCI 1.10
    USB EHCI 1.00
    USB OHCI 1.0
    USB EHCI 1.00
    USB OHCI 1.0
    Bus usb@fcc00000: 1 USB Device(s) found
    Bus usb@fd000000: 1 USB Device(s) found
    Bus usb@fd800000: 1 USB Device(s) found
    Bus usb@fd840000: 4 USB Device(s) found
    Bus usb@fd880000: 2 USB Device(s) found
    Bus usb@fd8c0000: 1 USB Device(s) found
           scanning usb for storage devices... 0 Storage Device(s) found
    Hit any key to stop autoboot: 1

so the USB code seems to do something, but for some reason it doesn't
see the keyboard.


        Stefan


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support
  2025-11-08  5:38 ` [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support Dang Huynh via B4 Relay
@ 2026-03-04 12:29   ` Peter Robinson
  2026-03-24 10:08     ` Dang Huynh
  2026-04-02  3:33     ` Dang Huynh
  0 siblings, 2 replies; 25+ messages in thread
From: Peter Robinson @ 2026-03-04 12:29 UTC (permalink / raw)
  To: dang.huynh
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski

Hey Dang,

Finally started to play with this patch set.

> From: Dang Huynh <dang.huynh@mainlining.org>
>
> Add support for video bridge to VOP2 so we can use the MIPI DSI
> bridge driver that we have.

I see a link failure when using this patch set with the
anbernic-rgxx3-rk3566_defconfig using 2026.04-rc3.

/usr/bin/ld.bfd: drivers/video/rockchip/rk_vop2.o: in function
`rk_display_init':
/builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:422:(.text.rk_vop2_probe+0x360):
undefined reference to `display_in_use'
/usr/bin/ld.bfd:
/builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:437:(.text.rk_vop2_probe+0x388):
undefined reference to `display_read_timing'
/usr/bin/ld.bfd:
/builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:492:(.text.rk_vop2_probe+0x794):
undefined reference to `display_enable'

Cheers,
Peter


> Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  drivers/video/rockchip/rk_vop2.c | 81 +++++++++++++++++++++++++++++-----------
>  1 file changed, 59 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> index 992f215d416..156daafa0c3 100644
> --- a/drivers/video/rockchip/rk_vop2.c
> +++ b/drivers/video/rockchip/rk_vop2.c
> @@ -13,10 +13,12 @@
>  #include <dm/device_compat.h>
>  #include <edid.h>
>  #include <log.h>
> +#include <panel.h>
>  #include <regmap.h>
>  #include <reset.h>
>  #include <syscon.h>
>  #include <video.h>
> +#include <video_bridge.h>
>  #include <asm/global_data.h>
>  #include <asm/gpio.h>
>  #include <asm/io.h>
> @@ -291,6 +293,7 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>         int vop_id, port_id, win_id;
>         struct display_timing timing;
>         struct udevice *disp;
> +       struct udevice *bridge;
>         int ret;
>         u32 remote_phandle;
>         struct display_plat *disp_uc_plat;
> @@ -357,8 +360,11 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>                         return -EINVAL;
>                 }
>
> +               if (IS_ENABLED(CONFIG_VIDEO_BRIDGE))
> +                       uclass_find_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &bridge);
> +
>                 uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> -               if (disp)
> +               if (disp || bridge)
>                         break;
>         };
>         compat = ofnode_get_property(remote, "compatible", NULL);
> @@ -390,27 +396,49 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>         if (ret < 0)
>                 return ret;
>
> -       disp_uc_plat = dev_get_uclass_plat(disp);
> -       debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> -       if (display_in_use(disp)) {
> -               debug("   - device in use\n");
> -               return -EBUSY;
> -       }
> +       if (bridge) {
> +               /* video bridge detected, probe it */
> +               ret = device_probe(bridge);
> +               if (ret) {
> +                       dev_err(dev, "Failed to probe video bridge: %d\n", ret);
> +                       return ret;
> +               }
>
> -       disp_uc_plat->source_id = vop_id;
> -       disp_uc_plat->src_dev = dev;
> +               /* Attach the DSI controller and the display to the bridge. */
> +               ret = video_bridge_attach(bridge);
> +               if (ret) {
> +                       dev_err(dev, "Failed to attach video bridge: %d\n", ret);
> +                       return ret;
> +               }
>
> -       ret = device_probe(disp);
> -       if (ret) {
> -               debug("%s: device '%s' display won't probe (ret=%d)\n",
> -                     __func__, dev->name, ret);
> -               return ret;
> -       }
> +               ret = video_bridge_get_display_timing(bridge, &timing);
> +               if (ret) {
> +                       dev_err(dev, "Failed to read timings: %d\n", ret);
> +                       return ret;
> +               }
> +       } else {
> +               disp_uc_plat = dev_get_uclass_plat(disp);
> +               debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> +               if (display_in_use(disp)) {
> +                       debug("   - device in use\n");
> +                       return -EBUSY;
> +               }
>
> -       ret = display_read_timing(disp, &timing);
> -       if (ret) {
> -               debug("%s: Failed to read timings\n", __func__);
> -               return ret;
> +               disp_uc_plat->source_id = vop_id;
> +               disp_uc_plat->src_dev = dev;
> +
> +               ret = device_probe(disp);
> +               if (ret) {
> +                       debug("%s: device '%s' display won't probe (ret=%d)\n",
> +                             __func__, dev->name, ret);
> +                       return ret;
> +               }
> +
> +               ret = display_read_timing(disp, &timing);
> +               if (ret) {
> +                       debug("%s: Failed to read timings\n", __func__);
> +                       return ret;
> +               }
>         }
>
>         /* Set clock rate on video port to display timings */
> @@ -453,9 +481,18 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
>
>         rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
>
> -       ret = display_enable(disp, 1 << l2bpp, &timing);
> -       if (ret)
> -               return ret;
> +       if (bridge) {
> +               /* Attach the DSI controller and the display to the bridge. */
> +               ret = video_bridge_set_backlight(bridge, 60);
> +               if (ret) {
> +                       dev_err(dev, "Failed to start the video bridge: %d\n", ret);
> +                       return ret;
> +               }
> +       } else {
> +               ret = display_enable(disp, 1 << l2bpp, &timing);
> +               if (ret)
> +                       return ret;
> +       }
>
>         uc_priv->xsize = timing.hactive.typ;
>         uc_priv->ysize = timing.vactive.typ;
>
> --
> 2.51.2
>
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support
  2026-03-04 12:29   ` Peter Robinson
@ 2026-03-24 10:08     ` Dang Huynh
  2026-04-02  3:33     ` Dang Huynh
  1 sibling, 0 replies; 25+ messages in thread
From: Dang Huynh @ 2026-03-24 10:08 UTC (permalink / raw)
  To: Peter Robinson
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski

Hi Peter,

On Wed, Mar 04, 2026 at 12:29:27PM +0000, Peter Robinson wrote:
> Hey Dang,
> 
> Finally started to play with this patch set.
> 
> > From: Dang Huynh <dang.huynh@mainlining.org>
> >
> > Add support for video bridge to VOP2 so we can use the MIPI DSI
> > bridge driver that we have.
> 
> I see a link failure when using this patch set with the
> anbernic-rgxx3-rk3566_defconfig using 2026.04-rc3.
> 
> /usr/bin/ld.bfd: drivers/video/rockchip/rk_vop2.o: in function
> `rk_display_init':
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:422:(.text.rk_vop2_probe+0x360):
> undefined reference to `display_in_use'
> /usr/bin/ld.bfd:
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:437:(.text.rk_vop2_probe+0x388):
> undefined reference to `display_read_timing'
> /usr/bin/ld.bfd:
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:492:(.text.rk_vop2_probe+0x794):
> undefined reference to `display_enable'
You need to enable CONFIG_DISPLAY. I'll be fixing this in the next series.

> 
> Cheers,
> Peter
> 
> 
> > Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> > ---
> >  drivers/video/rockchip/rk_vop2.c | 81 +++++++++++++++++++++++++++++-----------
> >  1 file changed, 59 insertions(+), 22 deletions(-)
> >
> > diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> > index 992f215d416..156daafa0c3 100644
> > --- a/drivers/video/rockchip/rk_vop2.c
> > +++ b/drivers/video/rockchip/rk_vop2.c
> > @@ -13,10 +13,12 @@
> >  #include <dm/device_compat.h>
> >  #include <edid.h>
> >  #include <log.h>
> > +#include <panel.h>
> >  #include <regmap.h>
> >  #include <reset.h>
> >  #include <syscon.h>
> >  #include <video.h>
> > +#include <video_bridge.h>
> >  #include <asm/global_data.h>
> >  #include <asm/gpio.h>
> >  #include <asm/io.h>
> > @@ -291,6 +293,7 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >         int vop_id, port_id, win_id;
> >         struct display_timing timing;
> >         struct udevice *disp;
> > +       struct udevice *bridge;
> >         int ret;
> >         u32 remote_phandle;
> >         struct display_plat *disp_uc_plat;
> > @@ -357,8 +360,11 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >                         return -EINVAL;
> >                 }
> >
> > +               if (IS_ENABLED(CONFIG_VIDEO_BRIDGE))
> > +                       uclass_find_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &bridge);
> > +
> >                 uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> > -               if (disp)
> > +               if (disp || bridge)
> >                         break;
> >         };
> >         compat = ofnode_get_property(remote, "compatible", NULL);
> > @@ -390,27 +396,49 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >         if (ret < 0)
> >                 return ret;
> >
> > -       disp_uc_plat = dev_get_uclass_plat(disp);
> > -       debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> > -       if (display_in_use(disp)) {
> > -               debug("   - device in use\n");
> > -               return -EBUSY;
> > -       }
> > +       if (bridge) {
> > +               /* video bridge detected, probe it */
> > +               ret = device_probe(bridge);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to probe video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> >
> > -       disp_uc_plat->source_id = vop_id;
> > -       disp_uc_plat->src_dev = dev;
> > +               /* Attach the DSI controller and the display to the bridge. */
> > +               ret = video_bridge_attach(bridge);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to attach video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> >
> > -       ret = device_probe(disp);
> > -       if (ret) {
> > -               debug("%s: device '%s' display won't probe (ret=%d)\n",
> > -                     __func__, dev->name, ret);
> > -               return ret;
> > -       }
> > +               ret = video_bridge_get_display_timing(bridge, &timing);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to read timings: %d\n", ret);
> > +                       return ret;
> > +               }
> > +       } else {
> > +               disp_uc_plat = dev_get_uclass_plat(disp);
> > +               debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> > +               if (display_in_use(disp)) {
> > +                       debug("   - device in use\n");
> > +                       return -EBUSY;
> > +               }
> >
> > -       ret = display_read_timing(disp, &timing);
> > -       if (ret) {
> > -               debug("%s: Failed to read timings\n", __func__);
> > -               return ret;
> > +               disp_uc_plat->source_id = vop_id;
> > +               disp_uc_plat->src_dev = dev;
> > +
> > +               ret = device_probe(disp);
> > +               if (ret) {
> > +                       debug("%s: device '%s' display won't probe (ret=%d)\n",
> > +                             __func__, dev->name, ret);
> > +                       return ret;
> > +               }
> > +
> > +               ret = display_read_timing(disp, &timing);
> > +               if (ret) {
> > +                       debug("%s: Failed to read timings\n", __func__);
> > +                       return ret;
> > +               }
> >         }
> >
> >         /* Set clock rate on video port to display timings */
> > @@ -453,9 +481,18 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >
> >         rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> >
> > -       ret = display_enable(disp, 1 << l2bpp, &timing);
> > -       if (ret)
> > -               return ret;
> > +       if (bridge) {
> > +               /* Attach the DSI controller and the display to the bridge. */
> > +               ret = video_bridge_set_backlight(bridge, 60);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to start the video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> > +       } else {
> > +               ret = display_enable(disp, 1 << l2bpp, &timing);
> > +               if (ret)
> > +                       return ret;
> > +       }
> >
> >         uc_priv->xsize = timing.hactive.typ;
> >         uc_priv->ysize = timing.vactive.typ;
> >
> > --
> > 2.51.2
> >
> >

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support
  2026-03-04 12:29   ` Peter Robinson
  2026-03-24 10:08     ` Dang Huynh
@ 2026-04-02  3:33     ` Dang Huynh
  1 sibling, 0 replies; 25+ messages in thread
From: Dang Huynh @ 2026-04-02  3:33 UTC (permalink / raw)
  To: Peter Robinson
  Cc: u-boot, Anatolij Gustschin, Simon Glass, Philipp Tomsich,
	Kever Yang, Tom Rini, Svyatoslav Ryhel, Alexander Graf,
	Alper Nebi Yasak, Ondrej Jirman, Ion Agorria, Dario Binacchi,
	Dragan Simic, Patrice Chotard, Miquel Raynal, Jonas Karlman,
	Nicolas Frattaroli, Lukasz Majewski, Sean Anderson,
	Piotr Zalewski

Hi Peter,

On Wed, Mar 04, 2026 at 12:29:27PM +0000, Peter Robinson wrote:
> Hey Dang,
> 
> Finally started to play with this patch set.
> 
> > From: Dang Huynh <dang.huynh@mainlining.org>
> >
> > Add support for video bridge to VOP2 so we can use the MIPI DSI
> > bridge driver that we have.
> 
> I see a link failure when using this patch set with the
> anbernic-rgxx3-rk3566_defconfig using 2026.04-rc3.
> 
> /usr/bin/ld.bfd: drivers/video/rockchip/rk_vop2.o: in function
> `rk_display_init':
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:422:(.text.rk_vop2_probe+0x360):
> undefined reference to `display_in_use'
> /usr/bin/ld.bfd:
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:437:(.text.rk_vop2_probe+0x388):
> undefined reference to `display_read_timing'
> /usr/bin/ld.bfd:
> /builddir/build/BUILD/uboot-tools-2026.04-build/u-boot-2026.04-rc3/drivers/video/rockchip/rk_vop2.c:492:(.text.rk_vop2_probe+0x794):
> undefined reference to `display_enable'

Interestingly on my machine, it shows:
/usr/lib/gcc/aarch64-linux-gnu/15.1.0/../../../../aarch64-linux-gnu/bin/ld: /tmp/cceER6YV.ltrans9.ltrans.o: in function `rk3568_vop_probe':
/media/LARGE_DATA/Work/pinetab2/u-boot/drivers/video/rockchip/rk_vop2.c:424:(.text.rk3568_vop_probe+0x43c): undefined reference to `display_in_use'
/usr/lib/gcc/aarch64-linux-gnu/15.1.0/../../../../aarch64-linux-gnu/bin/ld: /tmp/cceER6YV.ltrans9.ltrans.o:
/media/LARGE_DATA/Work/pinetab2/u-boot/drivers/video/rockchip/rk_vop2.c:439:(.text.rk3568_vop_probe+0x464): undefined reference to `display_read_timing'
/usr/lib/gcc/aarch64-linux-gnu/15.1.0/../../../../aarch64-linux-gnu/bin/ld: /tmp/cceER6YV.ltrans9.ltrans.o:
/media/LARGE_DATA/Work/pinetab2/u-boot/drivers/video/rockchip/rk_vop2.c:494:(.text.rk3568_vop_probe+0x88c): undefined reference to `display_enable'
collect2: fatal error: ld terminated with signal 11 [Segmentation fault], core dumped
compilation terminated.

Perhaps this is a linker bug? Removing rk_vop2.o from Makefile
makes it build perfectly fine though.

Though, I don't think there are any devices that uses VOP and
doesn't have a display, so I think we should make CONFIG_DISPLAY
a requirement.


> 
> Cheers,
> Peter
> 
> 
> > Reviewed-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> > ---
> >  drivers/video/rockchip/rk_vop2.c | 81 +++++++++++++++++++++++++++++-----------
> >  1 file changed, 59 insertions(+), 22 deletions(-)
> >
> > diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
> > index 992f215d416..156daafa0c3 100644
> > --- a/drivers/video/rockchip/rk_vop2.c
> > +++ b/drivers/video/rockchip/rk_vop2.c
> > @@ -13,10 +13,12 @@
> >  #include <dm/device_compat.h>
> >  #include <edid.h>
> >  #include <log.h>
> > +#include <panel.h>
> >  #include <regmap.h>
> >  #include <reset.h>
> >  #include <syscon.h>
> >  #include <video.h>
> > +#include <video_bridge.h>
> >  #include <asm/global_data.h>
> >  #include <asm/gpio.h>
> >  #include <asm/io.h>
> > @@ -291,6 +293,7 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >         int vop_id, port_id, win_id;
> >         struct display_timing timing;
> >         struct udevice *disp;
> > +       struct udevice *bridge;
> >         int ret;
> >         u32 remote_phandle;
> >         struct display_plat *disp_uc_plat;
> > @@ -357,8 +360,11 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >                         return -EINVAL;
> >                 }
> >
> > +               if (IS_ENABLED(CONFIG_VIDEO_BRIDGE))
> > +                       uclass_find_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &bridge);
> > +
> >                 uclass_find_device_by_ofnode(UCLASS_DISPLAY, remote, &disp);
> > -               if (disp)
> > +               if (disp || bridge)
> >                         break;
> >         };
> >         compat = ofnode_get_property(remote, "compatible", NULL);
> > @@ -390,27 +396,49 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >         if (ret < 0)
> >                 return ret;
> >
> > -       disp_uc_plat = dev_get_uclass_plat(disp);
> > -       debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> > -       if (display_in_use(disp)) {
> > -               debug("   - device in use\n");
> > -               return -EBUSY;
> > -       }
> > +       if (bridge) {
> > +               /* video bridge detected, probe it */
> > +               ret = device_probe(bridge);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to probe video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> >
> > -       disp_uc_plat->source_id = vop_id;
> > -       disp_uc_plat->src_dev = dev;
> > +               /* Attach the DSI controller and the display to the bridge. */
> > +               ret = video_bridge_attach(bridge);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to attach video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> >
> > -       ret = device_probe(disp);
> > -       if (ret) {
> > -               debug("%s: device '%s' display won't probe (ret=%d)\n",
> > -                     __func__, dev->name, ret);
> > -               return ret;
> > -       }
> > +               ret = video_bridge_get_display_timing(bridge, &timing);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to read timings: %d\n", ret);
> > +                       return ret;
> > +               }
> > +       } else {
> > +               disp_uc_plat = dev_get_uclass_plat(disp);
> > +               debug("Found device '%s', disp_uc_priv=%p\n", disp->name, disp_uc_plat);
> > +               if (display_in_use(disp)) {
> > +                       debug("   - device in use\n");
> > +                       return -EBUSY;
> > +               }
> >
> > -       ret = display_read_timing(disp, &timing);
> > -       if (ret) {
> > -               debug("%s: Failed to read timings\n", __func__);
> > -               return ret;
> > +               disp_uc_plat->source_id = vop_id;
> > +               disp_uc_plat->src_dev = dev;
> > +
> > +               ret = device_probe(disp);
> > +               if (ret) {
> > +                       debug("%s: device '%s' display won't probe (ret=%d)\n",
> > +                             __func__, dev->name, ret);
> > +                       return ret;
> > +               }
> > +
> > +               ret = display_read_timing(disp, &timing);
> > +               if (ret) {
> > +                       debug("%s: Failed to read timings\n", __func__);
> > +                       return ret;
> > +               }
> >         }
> >
> >         /* Set clock rate on video port to display timings */
> > @@ -453,9 +481,18 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode vp_node)
> >
> >         rkvop2_mode_set(dev, &timing, vop_id, port_id, platdata);
> >
> > -       ret = display_enable(disp, 1 << l2bpp, &timing);
> > -       if (ret)
> > -               return ret;
> > +       if (bridge) {
> > +               /* Attach the DSI controller and the display to the bridge. */
> > +               ret = video_bridge_set_backlight(bridge, 60);
> > +               if (ret) {
> > +                       dev_err(dev, "Failed to start the video bridge: %d\n", ret);
> > +                       return ret;
> > +               }
> > +       } else {
> > +               ret = display_enable(disp, 1 << l2bpp, &timing);
> > +               if (ret)
> > +                       return ret;
> > +       }
> >
> >         uc_priv->xsize = timing.hactive.typ;
> >         uc_priv->ysize = timing.vactive.typ;
> >
> > --
> > 2.51.2
> >
> >

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2026-04-02 15:03 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-08  5:37 [PATCH v6 00/12] Rockchip VOP2 support Dang Huynh via B4 Relay
2025-11-08  5:37 ` [PATCH v6 01/12] video: rockchip: dw-mipi-dsi: Depend on CONFIG_VIDEO_BRIDGE Dang Huynh via B4 Relay
2025-11-08  5:37 ` [PATCH v6 02/12] video: rockchip: dw_mipi_dsi: Improve pixel clock calculations Dang Huynh via B4 Relay
2025-11-08  5:37 ` [PATCH v6 03/12] video: rockchip: dw_mipi_dsi: Proceed when external PHY is not defined Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 04/12] video: rockchip: dw-mipi-dsi: Add get_display_timing support Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 05/12] video: Add BOE TH101MB31IG002-28A MIPI-DSI panel Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 06/12] video: rockchip: Add VOP2 support Dang Huynh via B4 Relay
2025-11-10  3:24   ` Chaoyi Chen
2025-12-18  4:47     ` Dang Huynh
2025-12-23  1:34       ` Chaoyi Chen
2025-12-25  6:35         ` Dang Huynh
2025-12-29  3:13           ` Chaoyi Chen
2025-12-30  5:42             ` Dang Huynh
2025-11-08  5:38 ` [PATCH v6 07/12] video: rockchip: vop2: Add video bridge support Dang Huynh via B4 Relay
2026-03-04 12:29   ` Peter Robinson
2026-03-24 10:08     ` Dang Huynh
2026-04-02  3:33     ` Dang Huynh
2025-11-08  5:38 ` [PATCH v6 08/12] arm: dts: rockchip: rk356x: Prerelocate VOP in U-Boot proper Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 09/12] configs: quartz64: Enable vidconsole Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 10/12] video: rockchip: Add HDMI support for RK3568 Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 11/12] configs: pinetab2-rk3566: Enable video and USB keyboard Dang Huynh via B4 Relay
2025-11-08  5:38 ` [PATCH v6 12/12] clk: rockchip: rk3568: Use assigned VPLL clock when possible Dang Huynh via B4 Relay
2026-01-08 22:14 ` [PATCH v6 00/12] Rockchip VOP2 support Stefan Monnier
2026-01-09  1:41   ` Peter Robinson
2026-01-09  4:07     ` Stefan Monnier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox