[PATCH 05/10] video: rockchip: Add VOP2 support

Andy Yan andyshrk at 163.com
Sun Jan 19 10:10:10 CET 2025


Hi Danq,

At 2025-01-16 17:15:54, "Dang Huynh" <danct12 at riseup.net> wrote:
>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 <danct12 at riseup.net>
>---
> arch/arm/include/asm/arch-rockchip/vop_rk3568.h | 249 +++++++++++++
> drivers/video/rockchip/Makefile                 |   3 +-
> drivers/video/rockchip/rk3568_vop.c             | 214 +++++++++++
> drivers/video/rockchip/rk_vop2.c                | 474 ++++++++++++++++++++++++
> drivers/video/rockchip/rk_vop2.h                |  58 +++
> 5 files changed, 997 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 0000000000000000000000000000000000000000..bfcdf7a07cc275dc0ea48ae6a7889e8f2d8335cf
>--- /dev/null
>+++ b/arch/arm/include/asm/arch-rockchip/vop_rk3568.h
>@@ -0,0 +1,249 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ * Copyright (c) 2024 Dang Huynh <danct12 at riseup.net>
>+ *
>+ * 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_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_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 f55beceebf118bbfc6f85b9edf7f64eaa13ffe62..2f89a979a2848733be5a6d05817ad76ce3ad3a34 100644
>--- a/drivers/video/rockchip/Makefile
>+++ b/drivers/video/rockchip/Makefile
>@@ -4,10 +4,11 @@
> # Wolfgang Denk, DENX Software Engineering, wd at 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 0000000000000000000000000000000000000000..077b74d0b75a98222af24c48d092c2af871d5e63
>--- /dev/null
>+++ b/drivers/video/rockchip/rk3568_vop.c
>@@ -0,0 +1,214 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Copyright (c) 2024 Dang Huynh <danct12 at riseup.net>
>+ *
>+ * 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 <log.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 rkvop_initialize(struct udevice *dev)
>+{
>+	struct rk_vop2_priv *priv = dev_get_priv(dev);
>+	struct rk3568_vop_sysctrl *sysctrl = priv->regs + VOP2_SYSREG_OFFSET;
>+	struct clk aclk;
>+	int ret;
>+
>+	ret = clk_get_by_name(dev, "aclk", &aclk);
>+	if (ret < 0)
>+		return ret;
>+
>+	ret = clk_enable(&aclk);
>+	if (ret < 0) {
>+		printf("Failed to enable aclk\n");
>+		return ret;
>+	}
>+
>+	debug("aclk rate: %ld\n", clk_get_rate(&aclk));
>+
>+	/* 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;
>+}
>+
>+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 = rkvop_initialize(dev);
>+	if (ret)
>+		return ret;
>+
>+	return rk_vop2_probe(dev);
>+}
>+
>+struct rkvop_platdata rk3568_platdata = {
>+	.delay = 20,
>+	.bg_dly = {42, 40, 40},
>+};
>+
>+struct rkvop_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)&rk3568_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,
>+	.priv_auto	= sizeof(struct rk_vop2_priv),
>+};
>diff --git a/drivers/video/rockchip/rk_vop2.c b/drivers/video/rockchip/rk_vop2.c
>new file mode 100644
>index 0000000000000000000000000000000000000000..21751342de2f079d884b5247ade696efb95131c7
>--- /dev/null
>+++ b/drivers/video/rockchip/rk_vop2.c
>@@ -0,0 +1,474 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Copyright (c) 2024 Dang Huynh <danct12 at riseup.net>
>+ *
>+ * 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 rkvop_cfg_regdone(struct rk3568_vop_sysctrl *sysctrl, int port)

I suggest all the functions go with prefix vop2 instead of rkvop, distinguish its name
from that of the first generation of rk_vop2.c.

>+{
>+	u32 reg;
>+
>+	reg = M_GLOBAL_REGDONE;
>+
>+	/*
>+	 * For RK3588, changes will only take effect when the same bit is
>+	 * leftshifted by 16.

 I think you mean rk3568 here.


>+	 */
>+	reg |= M_LOAD_GLOBAL(port) | M_LOAD_GLOBAL(port) << 16;
>+
>+	writel(reg, &sysctrl->reg_cfg_done);
>+}
>+
>+static void rkvop_enable(struct udevice *dev, ulong fbbase,
>+			 int fb_bits_per_pixel,
>+		const struct display_timing *edid, int port,
>+		struct rkvop_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 = priv->regs + VOP2_ESMART_OFFSET(0);
>+	u32 reg;
>+	u32 rgb_mode;
>+	u32 hactive = edid->hactive.typ;
>+	u32 vactive = edid->vactive.typ;
>+
>+	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);
>+
>+	reg = readl(&overlay->overlay_ctrl) | M_LAYER_SEL_REGDONE_EN;
>+	writel(reg, &overlay->overlay_ctrl);
>+
>+	/* Set layer 0 to esmart0 */
>+	writel(V_LAYER_SEL(0, 2), &overlay->layer_sel);
>+
>+	/* Set esmart to the destination video port */
>+	reg = V_ESMART_SEL_PORT(0, 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);
>+}
>+
>+static void rkvop_set_pin_polarity(struct udevice *dev,
>+				   enum vop_modes mode, u32 polarity)
>+{
>+	struct rkvop_driverdata *ops =
>+		(struct rkvop_driverdata *)dev_get_driver_data(dev);
>+
>+	if (ops->set_pin_polarity)
>+		ops->set_pin_polarity(dev, mode, polarity);
>+}
>+
>+static void rkvop_enable_output(struct udevice *dev, enum vop_modes mode, u32 port)
>+{
>+	struct rkvop_driverdata *ops =
>+		(struct rkvop_driverdata *)dev_get_driver_data(dev);
>+
>+	if (ops->enable_output)
>+		ops->enable_output(dev, mode, port);
>+}
>+
>+static void rkvop_mode_set(struct udevice *dev,
>+			   const struct display_timing *edid,
>+		enum vop_modes mode, int port,
>+		struct rkvop_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 rkvop_driverdata *data =
>+		(struct rkvop_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);
>+
>+	rkvop_enable_output(dev, mode, port);
>+	rkvop_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]);
>+
>+	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);
>+
>+	rkvop_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 video_priv *uc_priv = dev_get_uclass_priv(dev);
>+	struct rkvop_driverdata *drvdata =
>+		(struct rkvop_driverdata *)dev_get_driver_data(dev);
>+	struct rkvop_platdata *platdata =
>+		(struct rkvop_platdata *)drvdata->platdata;
>+	ofnode ep_node;
>+	int vop_id, port_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 at 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\n", vop_id);
>+	debug("port=%d\n", 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;
>+
>+	ret = clk_enable(&dclk);
>+	if (ret < 0) {
>+		printf("Failed to enable %s\n", dclk_name);
>+		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) {
>+		printf("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;
>+	}
>+
>+	rkvop_enable(dev, fbbase, 1 << l2bpp, &timing, port_id, platdata);
>+
>+	rkvop_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;
>+
>+	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;
>+
>+#ifdef 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);
>+#endif
>+
>+	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, 1);
>+
>+	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 0000000000000000000000000000000000000000..096d925841556f0a7545f095e34be22a00b89881
>--- /dev/null
>+++ b/drivers/video/rockchip/rk_vop2.h
>@@ -0,0 +1,58 @@
>+/* 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;
>+};
>+
>+enum vop_features {
>+	VOP_FEATURE_OUTPUT_10BIT = (1 << 0),
>+};
>+
>+struct rkvop_platdata {
>+	const u8 delay;
>+	const u8 bg_dly[3]; /* VOP2 supports up to 4 video ports (0-3) */
>+};
>+
>+struct rkvop_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);


Do you have tests with this u-boot video enabled then boot a linux kernel also with
drm display enabled?

Because the u-boot video been enabled with no-mmu, when it runs into linux kernel
with the window/plane you enable at u-boot stage, when linux kernel enable the iommu,
there is a moment the window will read the memory through iommu, but the framebuffer
you set in u-boot stage has not been mapped by iommu, there is a high probability of causing
an IOMMU page fault.
>+
>+#endif
>
>-- 
>2.48.1


More information about the U-Boot mailing list