From 2968ee95e06045d0a0410acd87030eb7f0f9530a Mon Sep 17 00:00:00 2001
From: Damon Ding <damon.ding@rock-chips.com>
Date: Fri, 7 Mar 2025 17:23:28 +0800
Subject: [PATCH] drm/rockchip: vop2: add support to synchronize VPs
 automatically

For some application scenario, such as multi-screen splicing, multiple
VPs should display synchronously.

To Synchronize VP0 and VP1, the DTS configurations may be like:

&vop {
	rockchip,sync-vp-mask = /bits/ 8 <3>;
};

The 8-bits value 3 means VP0(1 << 0) and VP1(1 << 1).

Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
[rebase]
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 73 +++++++++++---------
 1 file changed, 41 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index b61416793b44a..c41770566e52d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -910,6 +910,8 @@ struct vop2 {
 
 	uint16_t port_mux_cfg;
 
+	uint8_t sync_vp_mask;
+
 	uint32_t *regsbak;
 	struct regmap *grf;
 	struct regmap *sys_grf;
@@ -1190,6 +1192,38 @@ static void vop2_crtc_standby(struct drm_crtc *crtc, bool standby)
 	}
 }
 
+static int vop2_crtc_sync(struct drm_crtc *crtc, unsigned long crtc_mask)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	int vp_id;
+	bool hold = true;
+
+	/* It it not allowed to operate on a vp which is not active yet */
+	crtc_mask &= vop2->active_vp_mask;
+
+	DRM_INFO("Sync crtc_mask: 0x%lx\n", crtc_mask);
+	for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
+		vp = &vop2->vps[vp_id];
+		VOP_MODULE_SET(vop2, vp, standby, 1);
+	}
+
+	do {
+		mdelay(50);
+		for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
+			vp = &vop2->vps[vp_id];
+			hold |= VOP_MODULE_GET(vop2, vp, standby);
+		}
+	} while (!hold);
+
+	for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
+		vp = &vop2->vps[vp_id];
+		VOP_MODULE_SET(vop2, vp, standby, 0);
+	}
+
+	return 0;
+}
+
 static void vop2_crtc_output_post_enable(struct drm_crtc *crtc, int intf)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
@@ -1202,6 +1236,11 @@ static void vop2_crtc_output_post_enable(struct drm_crtc *crtc, int intf)
 	else if (intf & VOP_OUTPUT_IF_DP2)
 		VOP_CTRL_SET(vop2, dp2_en, 1);
 
+	if (vop2->sync_vp_mask) {
+		if (vop2->active_vp_mask == vop2->sync_vp_mask)
+			vop2_crtc_sync(crtc, vop2->sync_vp_mask);
+	}
+
 	dev_info(vop2->dev, "vop enable intf:%x\n", intf);
 }
 
@@ -7955,38 +7994,6 @@ static int vop2_crtc_get_crc(struct drm_crtc *crtc)
 	return 0;
 }
 
-static int vop2_crtc_sync(struct drm_crtc *crtc, unsigned long crtc_mask)
-{
-	struct vop2_video_port *vp = to_vop2_video_port(crtc);
-	struct vop2 *vop2 = vp->vop2;
-	int vp_id;
-	bool hold = true;
-
-	/* It it not allowed to operate on a vp which is not active yet */
-	crtc_mask &= vop2->active_vp_mask;
-
-	DRM_INFO("Sync crtc_mask: 0x%lx\n", crtc_mask);
-	for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
-		vp = &vop2->vps[vp_id];
-		VOP_MODULE_SET(vop2, vp, standby, 1);
-	}
-
-	do {
-		mdelay(50);
-		for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
-			vp = &vop2->vps[vp_id];
-			hold |= VOP_MODULE_GET(vop2, vp, standby);
-		}
-	} while (!hold);
-
-	for_each_set_bit(vp_id, &crtc_mask, ROCKCHIP_MAX_CRTC) {
-		vp = &vop2->vps[vp_id];
-		VOP_MODULE_SET(vop2, vp, standby, 0);
-	}
-
-	return 0;
-}
-
 static int vop2_crtc_enable(struct drm_crtc *crtc, unsigned long crtc_mask)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
@@ -14622,6 +14629,8 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
 		}
 	}
 
+	of_property_read_u8(dev->of_node, "rockchip,sync-vp-mask", &vop2->sync_vp_mask);
+
 	vop2_extend_clk_init(vop2);
 	spin_lock_init(&vop2->reg_lock);
 	spin_lock_init(&vop2->irq_lock);
-- 
GitLab