From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 06 Nov 2023 03:04:41 +0000
Subject: [PATCH] rk3568 rt init

---
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 1176 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 1,063 insertions(+), 113 deletions(-)

diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 35128d4..7757b07 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -35,6 +35,8 @@
 #include <linux/swab.h>
 #include <linux/sort.h>
 #include <linux/rockchip/cpu.h>
+#include <linux/workqueue.h>
+#include <linux/types.h>
 #include <soc/rockchip/rockchip_dmc.h>
 #include <soc/rockchip/rockchip-system-status.h>
 #include <uapi/linux/videodev2.h>
@@ -47,6 +49,7 @@
 #include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 #include "rockchip_vop_reg.h"
+#include "rockchip_post_csc.h"
 
 #define _REG_SET(vop2, name, off, reg, mask, v, relaxed) \
 		vop2_mask_write(vop2, off + reg.offset, mask, reg.shift, v, reg.write_mask, relaxed)
@@ -78,6 +81,8 @@
 
 #define VOP_CTRL_SET(x, name, v) \
 		REG_SET(x, name, 0, (x)->data->ctrl->name, v, false)
+
+#define VOP_CTRL_GET(x, name) vop2_read_reg(x, 0, &(x)->data->ctrl->name)
 
 #define VOP_INTR_GET(vop2, name) \
 		vop2_read_reg(vop2, 0, &vop2->data->ctrl->name)
@@ -477,6 +482,8 @@
 	uint8_t id;
 	bool layer_sel_update;
 	bool xmirror_en;
+	bool need_reset_p2i_flag;
+	atomic_t post_buf_empty_flag;
 	const struct vop2_video_port_regs *regs;
 
 	struct completion dsp_hold_completion;
@@ -555,6 +562,11 @@
 	bool gamma_lut_active;
 
 	/**
+	 * @lut_dma_rid: lut dma id
+	 */
+	u16 lut_dma_rid;
+
+	/**
 	 * @gamma_lut: atomic gamma look up table
 	 */
 	struct drm_color_lut *gamma_lut;
@@ -568,6 +580,11 @@
 	 * @cubic_lut_gem_obj: gem obj to store cubic lut
 	 */
 	struct rockchip_gem_object *cubic_lut_gem_obj;
+
+	/**
+	 * @hdr_lut_gem_obj: gem obj to store hdr lut
+	 */
+	struct rockchip_gem_object *hdr_lut_gem_obj;
 
 	/**
 	 * @cubic_lut: cubic look up table
@@ -591,10 +608,29 @@
 	struct drm_property *plane_mask_prop;
 
 	/**
+	 * @hdr_ext_data_prop: hdr extend data interaction with userspace
+	 */
+	struct drm_property *hdr_ext_data_prop;
+
+	int hdrvivid_mode;
+
+	/**
+	 * @acm_lut_data_prop: acm lut data interaction with userspace
+	 */
+	struct drm_property *acm_lut_data_prop;
+	/**
+	 * @post_csc_data_prop: post csc data interaction with userspace
+	 */
+	struct drm_property *post_csc_data_prop;
+
+	/**
 	 * @primary_plane_phy_id: vp primary plane phy id, the primary plane
 	 * will be used to show uboot logo and kernel logo
 	 */
 	enum vop2_layer_phy_id primary_plane_phy_id;
+
+	struct post_acm acm_info;
+	struct post_csc csc_info;
 };
 
 struct vop2 {
@@ -652,6 +688,7 @@
 	 */
 	uint32_t registered_num_wins;
 	uint8_t used_mixers;
+	uint8_t esmart_lb_mode;
 	/**
 	 * @active_vp_mask: Bitmask of active video ports;
 	 */
@@ -684,6 +721,8 @@
 	unsigned int enable_count;
 	struct clk *hclk;
 	struct clk *aclk;
+	struct work_struct post_buf_empty_work;
+	struct workqueue_struct *workqueue;
 
 	struct vop2_layer layers[ROCKCHIP_MAX_LAYER];
 	/* must put at the end of the struct */
@@ -717,6 +756,7 @@
 	{ MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" },
 	{ MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" },
 	{ MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" },
+	{ MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1x30" },
 };
 
 static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list)
@@ -991,8 +1031,23 @@
 static uint32_t vop2_read_vcnt(struct vop2_video_port *vp)
 {
 	uint32_t offset =  RK3568_SYS_STATUS0 + (vp->id << 2);
+	uint32_t vcnt0, vcnt1;
+	int i = 0;
 
-	return vop2_readl(vp->vop2, offset) >> 16;
+	for (i = 0; i < 10; i++) {
+		vcnt0 = vop2_readl(vp->vop2, offset) >> 16;
+		vcnt1 = vop2_readl(vp->vop2, offset) >> 16;
+
+		if ((vcnt1 - vcnt0) <= 1)
+			break;
+	}
+
+	if (i == 10) {
+		DRM_DEV_ERROR(vp->vop2->dev, "read VP%d vcnt error: %d %d\n", vp->id, vcnt0, vcnt1);
+		vcnt1 = vop2_readl(vp->vop2, offset) >> 16;
+	}
+
+	return vcnt1;
 }
 
 static void vop2_wait_for_irq_handler(struct drm_crtc *crtc)
@@ -1864,7 +1919,7 @@
 #define VOP2_COMMON_SCL_FAC_CHECK(src, dst, fac) \
 				(fac * (dst - 1) >> 16 < (src - 1))
 #define VOP3_COMMON_HOR_SCL_FAC_CHECK(src, dst, fac) \
-					(fac * (dst - 1) >> 16 <= (src - 1))
+					(fac * (dst - 1) >> 16 < (src - 1))
 
 static uint16_t vop2_scale_factor(enum scale_mode mode,
 				  int32_t filter_mode,
@@ -1983,12 +2038,30 @@
 		}
 	}
 
-	if (src_h >= (4 * dst_h)) {
-		ygt4 = 1;
-		src_h >>= 2;
-	} else if (src_h >= (2 * dst_h)) {
-		ygt2 = 1;
-		src_h >>= 1;
+	/**
+	 * The rk3528 is processed as 2 pixel/cycle,
+	 * so ygt2/ygt4 needs to be triggered in advance to improve performance
+	 * when src_w is bigger than 1920.
+	 * dst_h / src_h is at [1, 0.65)     ygt2=0; ygt4=0;
+	 * dst_h / src_h is at [0.65, 0.35)  ygt2=1; ygt4=0;
+	 * dst_h / src_h is at [0.35, 0)     ygt2=0; ygt4=1;
+	 */
+	if (vop2->version == VOP_VERSION_RK3528 && src_w > 1920) {
+		if (src_h >= (100 * dst_h / 35)) {
+			ygt4 = 1;
+			src_h >>= 2;
+		} else if ((src_h >= 100 * dst_h / 65) && (src_h < 100 * dst_h / 35)) {
+			ygt2 = 1;
+			src_h >>= 1;
+		}
+	} else {
+		if (src_h >= (4 * dst_h)) {
+			ygt4 = 1;
+			src_h >>= 2;
+		} else if (src_h >= (2 * dst_h)) {
+			ygt2 = 1;
+			src_h >>= 1;
+		}
 	}
 
 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
@@ -2065,10 +2138,17 @@
 		if (!is_vop3(vop2) ||
 		    (!vpstate->afbc_en && !vpstate->tiled_en) ||
 		    win_data->vsd_pre_filter_mode == VOP3_PRE_SCALE_DOWN_GT) {
-			if (cbcr_src_h >= (4 * dst_h))
-				ygt4 = 1;
-			else if (cbcr_src_h >= (2 * dst_h))
-				ygt2 = 1;
+			if (vop2->version == VOP_VERSION_RK3528 && src_w > 1920) {
+				if (cbcr_src_h >= (100 * dst_h / 35))
+					ygt4 = 1;
+				else if ((cbcr_src_h >= 100 * dst_h / 65) && (cbcr_src_h < 100 * dst_h / 35))
+					ygt2 = 1;
+			} else {
+				if (cbcr_src_h >= (4 * dst_h))
+					ygt4 = 1;
+				else if (cbcr_src_h >= (2 * dst_h))
+					ygt2 = 1;
+			}
 
 			if (ygt4)
 				cbcr_src_h >>= 2;
@@ -2230,29 +2310,49 @@
 	vpstate->r2y_en = 0;
 	vpstate->csc_mode = 0;
 
-	/* hdr2sdr and sdr2hdr will do csc itself */
-	if (vpstate->hdr2sdr_en) {
-		/*
-		 * This is hdr2sdr enabled plane
-		 * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr,
-		 * because hdr2sdr only support yuv input.
-		 */
-		if (!is_input_yuv) {
-			vpstate->r2y_en = 1;
-			vpstate->csc_mode = vop2_convert_csc_mode(output_csc, CSC_10BIT_DEPTH);
+	if (is_vop3(vp->vop2)) {
+		if (vpstate->hdr_in) {
+			if (is_input_yuv) {
+				vpstate->y2r_en = 1;
+				vpstate->csc_mode = vop2_convert_csc_mode(input_csc,
+									  CSC_13BIT_DEPTH);
+			}
+			return;
+		} else if (vp->sdr2hdr_en) {
+			if (is_input_yuv) {
+				vpstate->y2r_en = 1;
+				vpstate->csc_mode = vop2_convert_csc_mode(input_csc,
+									  csc_y2r_bit_depth);
+			}
+			return;
 		}
-		return;
-	} else if (!vpstate->hdr_in && vp->sdr2hdr_en) {
-		/*
-		 * This is sdr2hdr enabled plane
-		 * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr,
-		 * because sdr2hdr only support rgb input.
-		 */
-		if (is_input_yuv) {
-			vpstate->y2r_en = 1;
-			vpstate->csc_mode = vop2_convert_csc_mode(input_csc, csc_y2r_bit_depth);
+	} else {
+		/* hdr2sdr and sdr2hdr will do csc itself */
+		if (vpstate->hdr2sdr_en) {
+			/*
+			 * This is hdr2sdr enabled plane
+			 * If it's RGB layer do hdr2sdr, we need to do r2y before send to hdr2sdr,
+			 * because hdr2sdr only support yuv input.
+			 */
+			if (!is_input_yuv) {
+				vpstate->r2y_en = 1;
+				vpstate->csc_mode = vop2_convert_csc_mode(output_csc,
+									  CSC_10BIT_DEPTH);
+			}
+			return;
+		} else if (!vpstate->hdr_in && vp->sdr2hdr_en) {
+			/*
+			 * This is sdr2hdr enabled plane
+			 * If it's YUV layer do sdr2hdr, we need to do y2r before send to sdr2hdr,
+			 * because sdr2hdr only support rgb input.
+			 */
+			if (is_input_yuv) {
+				vpstate->y2r_en = 1;
+				vpstate->csc_mode = vop2_convert_csc_mode(input_csc,
+									  csc_y2r_bit_depth);
+			}
+			return;
 		}
-		return;
 	}
 
 	if (is_input_yuv && !is_output_yuv) {
@@ -2657,7 +2757,7 @@
 		fifo_throd = fb->pitches[0] >> 4;
 		if (fifo_throd >= vop2->data->wb->fifo_depth)
 			fifo_throd = vop2->data->wb->fifo_depth;
-		r2y = fb->format->is_yuv && (!is_yuv_output(vcstate->bus_format));
+		r2y = !vcstate->yuv_overlay && fb->format->is_yuv;
 
 		/*
 		 * the vp_id register config done immediately
@@ -2963,25 +3063,16 @@
  */
 static void vop3_layer_map_initial(struct vop2 *vop2, uint32_t current_vp_id)
 {
-	struct vop2_video_port *vp;
-	struct vop2_win *win;
-	unsigned long win_mask;
 	uint16_t vp_id;
-	int phys_id;
-	int i;
+	struct drm_plane *plane = NULL;
 
-	for (i = 0; i < vop2->data->nr_vps; i++) {
-		vp_id = i;
-		vp = &vop2->vps[vp_id];
-		vp->win_mask = vp->plane_mask;
-		win_mask = vp->win_mask;
-		for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) {
-			win = vop2_find_win_by_phys_id(vop2, phys_id);
-			VOP_CTRL_SET(vop2, win_vp_id[phys_id], vp_id);
-			win->vp_mask = BIT(vp_id);
-			win->old_vp_mask = win->vp_mask;
-			DRM_DEV_DEBUG(vop2->dev, "%s attach to vp%d\n", win->name, vp_id);
-		}
+	drm_for_each_plane(plane, vop2->drm_dev) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		vp_id = VOP_CTRL_GET(vop2, win_vp_id[win->phys_id]);
+		win->vp_mask = BIT(vp_id);
+		win->old_vp_mask = win->vp_mask;
+		vop2->vps[vp_id].win_mask |= BIT(win->phys_id);
 	}
 }
 
@@ -3081,8 +3172,17 @@
 		VOP_MODULE_SET(vop2, wb, axi_uv_id, 0xe);
 		vop2_wb_cfg_done(vp);
 
-		if (is_vop3(vop2))
-			VOP_CTRL_SET(vop2, esmart_lb_mode, vop2->data->esmart_lb_mode);
+		if (is_vop3(vop2)) {
+			VOP_CTRL_SET(vop2, dsp_vs_t_sel, 0);
+			VOP_CTRL_SET(vop2, esmart_lb_mode, vop2->esmart_lb_mode);
+		}
+
+		/*
+		 * This is unused and error init value for rk3528 vp1, if less of this config,
+		 * vp1 can't display normally.
+		 */
+		if (vop2->version == VOP_VERSION_RK3528)
+			vop2_mask_write(vop2, 0x700, 0x3, 4, 0, 0, true);
 
 		VOP_CTRL_SET(vop2, cfg_done_en, 1);
 		/*
@@ -3151,6 +3251,7 @@
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id];
 	int ret;
 
 	WARN_ON(vp->event);
@@ -3163,6 +3264,8 @@
 		VOP_MODULE_SET(vop2, vp, cubic_lut_en, 0);
 	}
 
+	if (vp_data->feature & VOP_FEATURE_VIVID_HDR)
+		VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 0);
 	vop2_disable_all_planes_for_crtc(crtc);
 
 	/*
@@ -3188,6 +3291,7 @@
 	vop2_dsp_hold_valid_irq_disable(crtc);
 
 	vop2_disable(crtc);
+	memset(&vp->active_tv_state, 0, sizeof(vp->active_tv_state));
 	vop2_unlock(vop2);
 
 	vop2->active_vp_mask &= ~BIT(vp->id);
@@ -3500,7 +3604,7 @@
 	uint32_t actual_w, actual_h, dsp_w, dsp_h;
 	uint32_t dsp_stx, dsp_sty;
 	uint32_t act_info, dsp_info, dsp_st;
-	uint32_t format;
+	uint32_t format, check_size;
 	uint32_t afbc_format;
 	uint32_t rb_swap;
 	uint32_t uv_swap;
@@ -3569,18 +3673,19 @@
 	actual_w = drm_rect_width(src) >> 16;
 	actual_h = drm_rect_height(src) >> 16;
 	dsp_w = drm_rect_width(dest);
-	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
+	if (dest->x1 + dsp_w > adjusted_mode->crtc_hdisplay) {
 		DRM_ERROR("vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
-			  vp->id, win->name, dest->x1, dsp_w, adjusted_mode->hdisplay);
+			  vp->id, win->name, dest->x1, dsp_w, adjusted_mode->crtc_hdisplay);
 		dsp_w = adjusted_mode->hdisplay - dest->x1;
 		if (dsp_w < 4)
 			dsp_w = 4;
 		actual_w = dsp_w * actual_w / drm_rect_width(dest);
 	}
 	dsp_h = drm_rect_height(dest);
-	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
+	check_size = adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE ? adjusted_mode->vdisplay : adjusted_mode->crtc_vdisplay;
+	if (dest->y1 + dsp_h > check_size) {
 		DRM_ERROR("vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
-			  vp->id, win->name, dest->y1, dsp_h, adjusted_mode->vdisplay);
+			  vp->id, win->name, dest->y1, dsp_h, adjusted_mode->crtc_vdisplay);
 		dsp_h = adjusted_mode->vdisplay - dest->y1;
 		if (dsp_h < 4)
 			dsp_h = 4;
@@ -3588,20 +3693,25 @@
 	}
 
 	/*
-	 * This is workaround solution for IC design:
-	 * esmart can't support scale down when actual_w % 16 == 1.
+	 * Workaround only for rk3568 vop
 	 */
-	if (!(win->feature & WIN_FEATURE_AFBDC)) {
-		if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
-			DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1\n", vp->id, win->name, actual_w);
-			actual_w -= 1;
+	if (vop2->version == VOP_VERSION_RK3568) {
+		/*
+		 * This is workaround solution for IC design:
+		 * esmart can't support scale down when actual_w % 16 == 1.
+		 */
+		if (!(win->feature & WIN_FEATURE_AFBDC)) {
+			if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
+				DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1\n", vp->id, win->name, actual_w);
+				actual_w -= 1;
+			}
 		}
-	}
 
-	if (vpstate->afbc_en && actual_w % 4) {
-		DRM_ERROR("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n",
-			  vp->id, win->name, actual_w);
-		actual_w = ALIGN_DOWN(actual_w, 4);
+		if (vpstate->afbc_en && actual_w % 4) {
+			DRM_ERROR("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n",
+				  vp->id, win->name, actual_w);
+			actual_w = ALIGN_DOWN(actual_w, 4);
+		}
 	}
 
 	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
@@ -3633,7 +3743,7 @@
 	if (vop2->version != VOP_VERSION_RK3568)
 		rk3588_vop2_win_cfg_axi(win);
 
-	if (is_vop3(vop2) && !vop2_cluster_window(win))
+	if (is_vop3(vop2) && !vop2_cluster_window(win) && !win->parent)
 		VOP_WIN_SET(vop2, win, scale_engine_num, win->scale_engine_num);
 
 	if (vpstate->afbc_en) {
@@ -3765,7 +3875,9 @@
 	if (vop2_cluster_window(win)) {
 		lb_mode = vop2_get_cluster_lb_mode(win, vpstate);
 		VOP_CLUSTER_SET(vop2, win, lb_mode, lb_mode);
+		VOP_CLUSTER_SET(vop2, win, scl_lb_mode, lb_mode == 1 ? 3 : 0);
 		VOP_CLUSTER_SET(vop2, win, enable, 1);
+		VOP_CLUSTER_SET(vop2, win, frm_reset_en, 1);
 	}
 	if (vcstate->output_if & VOP_OUTPUT_IF_BT1120 ||
 	    vcstate->output_if & VOP_OUTPUT_IF_BT656)
@@ -4736,6 +4848,27 @@
 				 struct drm_display_mode *adj_mode)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+
+	/*
+	 * For RK3568 and RK3588, the hactive of video timing must
+	 * be 4-pixel aligned.
+	 */
+	if (vop2->version == VOP_VERSION_RK3568 || vop2->version == VOP_VERSION_RK3588) {
+		if (adj_mode->hdisplay % 4) {
+			u16 old_hdisplay = adj_mode->hdisplay;
+			u16 align;
+
+			align = 4 - (adj_mode->hdisplay % 4);
+			adj_mode->hdisplay += align;
+			adj_mode->hsync_start += align;
+			adj_mode->hsync_end += align;
+			adj_mode->htotal += align;
+
+			DRM_WARN("VP%d: hactive need to be aligned with 4-pixel, %d -> %d\n",
+				 vp->id, old_hdisplay, adj_mode->hdisplay);
+		}
+	}
 
 	drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
 
@@ -4799,6 +4932,8 @@
 			to_rockchip_crtc_state(crtc->state);
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 	u16 vtotal = mode->crtc_vtotal;
 	u16 hdisplay = mode->crtc_hdisplay;
@@ -4838,8 +4973,16 @@
 		val = vact_st_f1 << 16 | vact_end_f1;
 		VOP_MODULE_SET(vop2, vp, vpost_st_end_f1, val);
 	}
-	VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y,
-		       is_yuv_output(vcstate->bus_format));
+
+	/*
+	 * BCSH[R2Y] -> POST Linebuffer[post scale] -> the background R2Y will be deal by post_dsp_out_r2y
+	 *
+	 * POST Linebuffer[post scale] -> ACM[R2Y] -> the background R2Y will be deal by ACM[R2Y]
+	 */
+	if (vp_data->feature & VOP_FEATURE_POST_ACM)
+		VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y, vcstate->yuv_overlay);
+	else
+		VOP_MODULE_SET(vop2, vp, post_dsp_out_r2y, is_yuv_output(vcstate->bus_format));
 }
 
 /*
@@ -4880,6 +5023,107 @@
 	return false;
 }
 
+/*
+ * For vop3 video port0, if hdr_vivid is not enable, the pipe delay time as follow:
+ * win_dly + config_win_dly + layer_mix_dly + sdr2hdr_dly + * hdr_mix_dly = config_bg_dly
+ *
+ * if hdr_vivid is enable, the hdr layer's pipe delay time as follow:
+ * win_dly + config_win_dly +hdrvivid_dly + hdr_mix_dly = config_bg_dly
+ *
+ * If hdrvivid and sdr2hdr bot enable, the time arrivr hdr_mix should be the same:
+ * win_dly + config_win_dly0 + hdrvivid_dly = win_dly + config_win_dly1 + laer_mix_dly +
+ * sdr2hdr_dly
+ *
+ * For vop3 video port1, the pipe delay time as follow:
+ * win_dly + config_win_dly + layer_mix_dly = config_bg_dly
+ *
+ * Here, win_dly, layer_mix_dly, sdr2hdr_dly, hdr_mix_dly, hdrvivid_dly is the hardware
+ * delay cycles. Config_win_dly and config_bg_dly is the register value that we can config.
+ * Different hdr vivid mode have different hdrvivid_dly. For sdr2hdr_dly, only sde2hdr
+ * enable, it will delay, otherwise, the sdr2hdr_dly is 0.
+ *
+ * For default, the config_win_dly will be 0, it just user to make the pipe to arrive
+ * hdr_mix at the same time.
+ */
+static void vop3_setup_pipe_dly(struct vop2_video_port *vp, const struct vop2_zpos *vop2_zpos)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_crtc *crtc = &vp->crtc;
+	const struct vop2_zpos *zpos;
+	struct drm_plane *plane;
+	struct vop2_plane_state *vpstate;
+	struct vop2_win *win;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	u16 hdisplay = adjusted_mode->crtc_hdisplay;
+	int bg_dly = 0x0;
+	int dly = 0x0;
+	int hdr_win_dly;
+	int sdr_win_dly;
+	int sdr2hdr_dly;
+	int pre_scan_dly;
+	int i;
+
+	/**
+	 * config bg dly, select the max delay num of hdrvivid and sdr2hdr module
+	 * as the increase value of bg delay num. If hdrvivid and sdr2hdr is not
+	 * work, the default bg_dly is 0x10. and the default win delay num is 0.
+	 */
+	if ((vp->hdr_en || vp->sdr2hdr_en) &&
+	    (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) {
+		/* set sdr2hdr_dly to 0 if sdr2hdr is disable */
+		sdr2hdr_dly = vp->sdr2hdr_en ? vp_data->sdr2hdr_dly : 0;
+
+		/* set the max delay pipe's config_win_dly as 0 */
+		if (vp_data->hdrvivid_dly[vp->hdrvivid_mode] >=
+		    sdr2hdr_dly + vp_data->layer_mix_dly) {
+			bg_dly = vp_data->win_dly + vp_data->hdrvivid_dly[vp->hdrvivid_mode] +
+				 vp_data->hdr_mix_dly;
+			hdr_win_dly = 0;
+			sdr_win_dly = vp_data->hdrvivid_dly[vp->hdrvivid_mode] -
+				      vp_data->layer_mix_dly - sdr2hdr_dly;
+		} else {
+			bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + sdr2hdr_dly +
+				 vp_data->hdr_mix_dly;
+			hdr_win_dly = sdr2hdr_dly + vp_data->layer_mix_dly -
+				      vp_data->hdrvivid_dly[vp->hdrvivid_mode];
+			sdr_win_dly = 0;
+		}
+	} else {
+		bg_dly = vp_data->win_dly + vp_data->layer_mix_dly + vp_data->hdr_mix_dly;
+		sdr_win_dly = 0;
+	}
+
+	pre_scan_dly = bg_dly + (hdisplay >> 1) - 1;
+	pre_scan_dly = (pre_scan_dly << 16) | hsync_len;
+	VOP_MODULE_SET(vop2, vp, bg_dly, bg_dly);
+	VOP_MODULE_SET(vop2, vp, pre_scan_htiming, pre_scan_dly);
+
+	/**
+	 * config win dly
+	 */
+	if (!vop2_zpos)
+		return;
+
+	for (i = 0; i < vp->nr_layers; i++) {
+		zpos = &vop2_zpos[i];
+		win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id);
+		plane = &win->base;
+		vpstate = to_vop2_plane_state(plane->state);
+
+		if ((vp->hdr_en || vp->sdr2hdr_en) &&
+		    (vp->hdrvivid_mode >= 0 && vp->hdrvivid_mode <= SDR2HLG)) {
+			dly = vpstate->hdr_in ? hdr_win_dly : sdr_win_dly;
+		}
+		if (vop2_cluster_window(win))
+			dly |= dly << 8;
+
+		VOP_CTRL_SET(vop2, win_dly[win->phys_id], dly);
+	}
+}
+
 static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
@@ -4900,7 +5144,6 @@
 	u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start;
 	u16 vact_end = vact_st + vdisplay;
 	bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE);
-	uint8_t out_mode;
 	bool dclk_inv, yc_swap = false;
 	int act_end;
 	uint32_t val;
@@ -4925,6 +5168,7 @@
 	if (vcstate->output_if & VOP_OUTPUT_IF_RGB) {
 		VOP_CTRL_SET(vop2, rgb_en, 1);
 		VOP_CTRL_SET(vop2, rgb_mux, vp_data->id);
+		VOP_CTRL_SET(vop2, rgb_pin_pol, val);
 		VOP_GRF_SET(vop2, grf_dclk_inv, dclk_inv);
 	}
 
@@ -5038,21 +5282,6 @@
 		VOP_CTRL_SET(vop2, hdmi_dclk_pol, 1);
 	}
 
-	if ((vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
-	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) ||
-	    vcstate->output_if & VOP_OUTPUT_IF_BT656)
-		out_mode = ROCKCHIP_OUT_MODE_P888;
-	else
-		out_mode = vcstate->output_mode;
-	VOP_MODULE_SET(vop2, vp, out_mode, out_mode);
-
-	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
-		VOP_MODULE_SET(vop2, vp, dsp_data_swap, DSP_RB_SWAP);
-	else
-		VOP_MODULE_SET(vop2, vp, dsp_data_swap, 0);
-
-	vop2_dither_setup(crtc);
-
 	VOP_MODULE_SET(vop2, vp, htotal_pw, (htotal << 16) | hsync_len);
 	val = hact_st << 16;
 	val |= hact_end;
@@ -5105,9 +5334,20 @@
 		VOP_MODULE_SET(vop2, vp, dclk_div2_phase_lock, 0);
 	}
 
-	clk_set_rate(vp->dclk, adjusted_mode->crtc_clock * 1000);
+	/*
+	 * For RK3528, the path of CVBS output is like:
+	 * VOP BT656 ENCODER -> CVBS BT656 DECODER -> CVBS ENCODER -> CVBS VDAC
+	 * The vop2 dclk should be four times crtc_clock for CVBS sampling clock needs.
+	 */
+	if (vop2->version == VOP_VERSION_RK3528 && vcstate->output_if & VOP_OUTPUT_IF_BT656)
+		clk_set_rate(vp->dclk, 4 * adjusted_mode->crtc_clock * 1000);
+	else
+		clk_set_rate(vp->dclk, adjusted_mode->crtc_clock * 1000);
 
 	vop2_post_config(crtc);
+
+	if (is_vop3(vop2))
+		vop3_setup_pipe_dly(vp, NULL);
 
 	vop2_cfg_done(crtc);
 
@@ -5154,6 +5394,224 @@
 				  struct drm_crtc_state *crtc_state)
 {
 	return 0;
+}
+
+static void vop3_disable_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id);
+	struct drm_plane *plane = &win->base;
+	struct drm_plane_state *pstate = plane->state;
+	struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate);
+
+	VOP_MODULE_SET(vop2, vp, hdr10_en, 0);
+	VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0);
+	VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, 0);
+	VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 0);
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_en, 0);
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, 0);
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_auto_gating_en, 1);
+
+	vp->hdr_en = false;
+	vp->hdr_in = false;
+	vp->hdr_out = false;
+	vp->sdr2hdr_en = false;
+	vpstate->hdr_in = false;
+	vpstate->hdr2sdr_en = false;
+}
+
+static void vop3_setup_hdrvivid(struct vop2_video_port *vp, uint8_t win_phys_id)
+{
+	struct vop2 *vop2 = vp->vop2;
+	struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id);
+	struct drm_plane *plane = &win->base;
+	struct drm_plane_state *pstate = plane->state;
+	struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate);
+	struct drm_crtc_state *cstate = vp->crtc.state;
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate);
+	unsigned long win_mask = vp->win_mask;
+	int phys_id;
+	struct hdrvivid_regs *hdrvivid_data;
+	struct hdr_extend *hdr_data;
+	bool have_sdr_layer = false;
+	uint32_t hdr_mode;
+	int i;
+	u32 *tone_lut_kvaddr;
+	dma_addr_t tone_lut_mst;
+
+	vp->hdr_en = false;
+	vp->hdr_in = false;
+	vp->hdr_out = false;
+	vp->sdr2hdr_en = false;
+	vpstate->hdr_in = false;
+	vpstate->hdr2sdr_en = false;
+
+	hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data;
+	hdrvivid_data = &hdr_data->hdrvivid_data;
+
+	hdr_mode = hdrvivid_data->hdr_mode;
+
+	if (hdr_mode > SDR2HLG && hdr_mode != SDR2HDR10_USERSPACE &&
+	    hdr_mode != SDR2HLG_USERSPACE) {
+		DRM_ERROR("Invalid HDR mode:%d, beyond the mode range\n", hdr_mode);
+		return;
+	}
+
+	/* adjust userspace hdr mode value to kernel value */
+	if (hdr_mode == SDR2HDR10_USERSPACE)
+		hdr_mode = SDR2HDR10;
+	if (hdr_mode == SDR2HLG_USERSPACE)
+		hdr_mode = SDR2HLG;
+
+	if (hdr_mode <= HDR102SDR && vpstate->eotf != SMPTE_ST2084 && vpstate->eotf != HLG) {
+		DRM_ERROR("Invalid HDR mode:%d, mismatch plane eotf:%d\n", hdr_mode,
+			  vpstate->eotf);
+		return;
+	}
+
+	vp->hdrvivid_mode = hdr_mode;
+	vcstate->yuv_overlay = false;
+
+	if (hdr_mode <= HDR102SDR) {
+		vp->hdr_en = true;
+		vp->hdr_in = true;
+		vpstate->hdr_in = true;
+	} else {
+		vp->sdr2hdr_en = true;
+	}
+
+	/*
+	 * To confirm whether need to enable sdr2hdr.
+	 */
+	for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) {
+		win = vop2_find_win_by_phys_id(vop2, phys_id);
+		plane = &win->base;
+		pstate = plane->state;
+		vpstate = to_vop2_plane_state(pstate);
+
+		/* skip inactive plane */
+		if (!vop2_plane_active(pstate))
+			continue;
+
+		if (vpstate->eotf != SMPTE_ST2084 && vpstate->eotf != HLG) {
+			have_sdr_layer = true;
+			break;
+		}
+	}
+
+	if (hdr_mode == PQHDR2SDR_WITH_DYNAMIC || hdr_mode == HLG2SDR_WITH_DYNAMIC ||
+	    hdr_mode == HLG2SDR_WITHOUT_DYNAMIC || hdr_mode == HDR102SDR) {
+		vpstate->hdr2sdr_en = true;
+	} else {
+		vp->hdr_out = true;
+		if (have_sdr_layer)
+			vp->sdr2hdr_en = true;
+	}
+
+	/**
+	 * Config hdr ctrl registers
+	 */
+	vop2_writel(vop2, RK3528_SDR2HDR_CTRL, hdrvivid_data->sdr2hdr_ctrl);
+	vop2_writel(vop2, RK3528_HDRVIVID_CTRL, hdrvivid_data->hdrvivid_ctrl);
+
+	VOP_MODULE_SET(vop2, vp, hdr10_en, vp->hdr_en);
+	if (vp->hdr_en) {
+		VOP_MODULE_SET(vop2, vp, hdr_vivid_en, (hdr_mode == HDR_BYPASS) ? 0 : 1);
+		VOP_MODULE_SET(vop2, vp, hdr_vivid_path_mode,
+			       (hdr_mode == HDR102SDR) ? PQHDR2SDR_WITH_DYNAMIC : hdr_mode);
+		VOP_MODULE_SET(vop2, vp, hdr_vivid_bypass_en, (hdr_mode == HDR_BYPASS) ? 1 : 0);
+	} else {
+		VOP_MODULE_SET(vop2, vp, hdr_vivid_en, 0);
+	}
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_en, vp->sdr2hdr_en);
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_path_en, vp->sdr2hdr_en);
+	VOP_MODULE_SET(vop2, vp, sdr2hdr_auto_gating_en, vp->sdr2hdr_en ? 0 : 1);
+
+	vop2_writel(vop2, RK3528_SDR_CFG_COE0, hdrvivid_data->sdr2hdr_coe0);
+	vop2_writel(vop2, RK3528_SDR_CFG_COE1, hdrvivid_data->sdr2hdr_coe1);
+	vop2_writel(vop2, RK3528_SDR_CSC_COE00_01, hdrvivid_data->sdr2hdr_csc_coe00_01);
+	vop2_writel(vop2, RK3528_SDR_CSC_COE02_10, hdrvivid_data->sdr2hdr_csc_coe02_10);
+	vop2_writel(vop2, RK3528_SDR_CSC_COE11_12, hdrvivid_data->sdr2hdr_csc_coe11_12);
+	vop2_writel(vop2, RK3528_SDR_CSC_COE20_21, hdrvivid_data->sdr2hdr_csc_coe20_21);
+	vop2_writel(vop2, RK3528_SDR_CSC_COE22, hdrvivid_data->sdr2hdr_csc_coe22);
+
+	vop2_writel(vop2, RK3528_HDR_PQ_GAMMA, hdrvivid_data->hdr_pq_gamma);
+	vop2_writel(vop2, RK3528_HLG_RFIX_SCALEFAC, hdrvivid_data->hlg_rfix_scalefac);
+	vop2_writel(vop2, RK3528_HLG_MAXLUMA, hdrvivid_data->hlg_maxluma);
+	vop2_writel(vop2, RK3528_HLG_R_TM_LIN2NON, hdrvivid_data->hlg_r_tm_lin2non);
+
+	vop2_writel(vop2, RK3528_HDR_CSC_COE00_01, hdrvivid_data->hdr_csc_coe00_01);
+	vop2_writel(vop2, RK3528_HDR_CSC_COE02_10, hdrvivid_data->hdr_csc_coe02_10);
+	vop2_writel(vop2, RK3528_HDR_CSC_COE11_12, hdrvivid_data->hdr_csc_coe11_12);
+	vop2_writel(vop2, RK3528_HDR_CSC_COE20_21, hdrvivid_data->hdr_csc_coe20_21);
+	vop2_writel(vop2, RK3528_HDR_CSC_COE22, hdrvivid_data->hdr_csc_coe22);
+
+	tone_lut_kvaddr = (u32 *)vp->hdr_lut_gem_obj->kvaddr;
+	tone_lut_mst = vp->hdr_lut_gem_obj->dma_addr;
+
+	for (i = 0; i < RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH; i++)
+		*tone_lut_kvaddr++ =  hdrvivid_data->tone_sca_axi_tab[i];
+
+	VOP_MODULE_SET(vop2, vp, lut_dma_rid, vp->lut_dma_rid - vp->id);
+	VOP_MODULE_SET(vop2, vp, hdr_lut_mode, 1);
+	VOP_MODULE_SET(vop2, vp, hdr_lut_mst, tone_lut_mst);
+	VOP_MODULE_SET(vop2, vp, hdr_lut_update_en, 1);
+	VOP_CTRL_SET(vop2, lut_dma_en, 1);
+
+	for (i = 0; i < RK_HDRVIVID_GAMMA_CURVE_LENGTH; i++)
+		vop2_writel(vop2, RK3528_HDRGAMMA_CURVE + i * 4, hdrvivid_data->hdrgamma_curve[i]);
+
+	for (i = 0; i < RK_HDRVIVID_GAMMA_MDFVALUE_LENGTH; i++)
+		vop2_writel(vop2, RK3528_HDRGAMMA_MDFVALUE + i * 4,
+			    hdrvivid_data->hdrgamma_mdfvalue[i]);
+
+	for (i = 0; i < RK_SDR2HDR_INVGAMMA_CURVE_LENGTH; i++)
+		vop2_writel(vop2, RK3528_SDRINVGAMMA_CURVE + i * 4,
+			    hdrvivid_data->sdrinvgamma_curve[i]);
+
+	for (i = 0; i < RK_SDR2HDR_INVGAMMA_S_IDX_LENGTH; i++)
+		vop2_writel(vop2, RK3528_SDRINVGAMMA_STARTIDX + i * 4,
+			    hdrvivid_data->sdrinvgamma_startidx[i]);
+
+	for (i = 0; i < RK_SDR2HDR_INVGAMMA_C_IDX_LENGTH; i++)
+		vop2_writel(vop2, RK3528_SDRINVGAMMA_CHANGEIDX + i * 4,
+			    hdrvivid_data->sdrinvgamma_changeidx[i]);
+
+	for (i = 0; i < RK_SDR2HDR_SMGAIN_LENGTH; i++)
+		vop2_writel(vop2, RK3528_SDR_SMGAIN + i * 4, hdrvivid_data->sdr_smgain[i]);
+}
+
+static void vop3_setup_dynamic_hdr(struct vop2_video_port *vp, uint8_t win_phys_id)
+{
+	struct drm_crtc_state *cstate = vp->crtc.state;
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate);
+	struct hdr_extend *hdr_data;
+	uint32_t hdr_format;
+
+	/* If hdr extend data is null, exit hdr mode */
+	if (!vcstate->hdr_ext_data) {
+		vop3_disable_dynamic_hdr(vp, win_phys_id);
+		return;
+	}
+
+	hdr_data = (struct hdr_extend *)vcstate->hdr_ext_data->data;
+	hdr_format = hdr_data->hdr_type;
+
+	switch (hdr_format) {
+	case HDR_NONE:
+	case HDR_HDR10:
+	case HDR_HLGSTATIC:
+	case HDR_HDRVIVID:
+		/*
+		 * hdr module support hdr10, hlg, vividhdr
+		 * sdr2hdr module support hdrnone for sdr2hdr
+		 */
+		vop3_setup_hdrvivid(vp, win_phys_id);
+		break;
+	default:
+		DRM_DEBUG("unsupprot hdr format:%u\n", hdr_format);
+		break;
+	}
 }
 
 static void vop2_setup_hdr10(struct vop2_video_port *vp, uint8_t win_phys_id)
@@ -5648,7 +6106,7 @@
 		vop2_writel(vop2, dst_alpha_ctrl_offset + offset, alpha.dst_alpha_ctrl.val);
 	}
 
-	if (vp_data->feature & VOP_FEATURE_HDR10) {
+	if (vp_data->feature & (VOP_FEATURE_HDR10 | VOP_FEATURE_VIVID_HDR)) {
 		src_color_ctrl_offset = ovl_regs->hdr_mix_regs->src_color_ctrl.offset;
 		dst_color_ctrl_offset = ovl_regs->hdr_mix_regs->dst_color_ctrl.offset;
 		src_alpha_ctrl_offset = ovl_regs->hdr_mix_regs->src_alpha_ctrl.offset;
@@ -5923,6 +6381,7 @@
 	struct vop2_cluster cluster;
 	uint8_t nr_layers = 0;
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	const struct vop2_video_port_data *vp_data = &vop2->data->vp[vp->id];
 
 	vcstate->yuv_overlay = is_yuv_output(vcstate->bus_format);
 	vop2_zpos = kmalloc_array(vop2->data->win_size, sizeof(*vop2_zpos), GFP_KERNEL);
@@ -5982,21 +6441,26 @@
 
 		sort(vop2_zpos, nr_layers, sizeof(vop2_zpos[0]), vop2_zpos_cmp, NULL);
 
-		if (is_vop3(vop2))
+		if (is_vop3(vop2)) {
 			vop3_setup_layer_sel_for_vp(vp, vop2_zpos);
-		else
-			vop2_setup_layer_mixer_for_vp(vp, vop2_zpos);
-		vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id);
-		if (is_vop3(vop2))
+			if (vp_data->feature & VOP_FEATURE_VIVID_HDR)
+				vop3_setup_dynamic_hdr(vp, vop2_zpos[0].win_phys_id);
 			vop3_setup_alpha(vp, vop2_zpos);
-		else
+			vop3_setup_pipe_dly(vp, vop2_zpos);
+		} else {
+			vop2_setup_layer_mixer_for_vp(vp, vop2_zpos);
+			vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id);
 			vop2_setup_alpha(vp, vop2_zpos);
-		vop2_setup_dly_for_vp(vp);
-		vop2_setup_dly_for_window(vp, vop2_zpos);
+			vop2_setup_dly_for_vp(vp);
+			vop2_setup_dly_for_window(vp, vop2_zpos);
+		}
 	} else {
-		if (!is_vop3(vop2))
+		if (!is_vop3(vop2)) {
 			vop2_calc_bg_ovl_and_port_mux(vp);
-		vop2_setup_dly_for_vp(vp);
+			vop2_setup_dly_for_vp(vp);
+		} else {
+			vop3_setup_pipe_dly(vp, NULL);
+		}
 	}
 
 	/* The pre alpha overlay of Cluster still need process in one win mode. */
@@ -6124,16 +6588,193 @@
 	vop2_bcsh_reg_update(vcstate, vp, &bcsh_state);
 }
 
+static void vop3_post_csc_config(struct drm_crtc *crtc, struct post_acm *acm, struct post_csc *csc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct vop2 *vop2 = vp->vop2;
+	struct post_csc_coef csc_coef;
+	bool acm_enable;
+	bool is_input_yuv = false;
+	bool is_output_yuv = false;
+	bool post_r2y_en = false;
+	bool post_csc_en = false;
+	int range_type;
+
+	if (!acm)
+		acm_enable = false;
+	else
+		acm_enable = acm->acm_enable;
+
+	if (acm_enable) {
+		if (!vcstate->yuv_overlay)
+			post_r2y_en = true;
+
+		/* do y2r in csc module */
+		if (!is_yuv_output(vcstate->bus_format))
+			post_csc_en = true;
+	} else {
+		if (!vcstate->yuv_overlay && is_yuv_output(vcstate->bus_format))
+			post_r2y_en = true;
+
+		/* do y2r in csc module */
+		if (vcstate->yuv_overlay && !is_yuv_output(vcstate->bus_format))
+			post_csc_en = true;
+	}
+
+	if (csc && csc->csc_enable)
+		post_csc_en = true;
+
+	if (vcstate->yuv_overlay || post_r2y_en)
+		is_input_yuv = true;
+
+	if (is_yuv_output(vcstate->bus_format))
+		is_output_yuv = true;
+
+	vcstate->post_csc_mode = vop2_convert_csc_mode(vcstate->color_space, CSC_13BIT_DEPTH);
+
+	if (post_csc_en) {
+		rockchip_calc_post_csc(csc, &csc_coef, vcstate->post_csc_mode, is_input_yuv,
+				       is_output_yuv);
+
+		VOP_MODULE_SET(vop2, vp, csc_coe00, csc_coef.csc_coef00);
+		VOP_MODULE_SET(vop2, vp, csc_coe01, csc_coef.csc_coef01);
+		VOP_MODULE_SET(vop2, vp, csc_coe02, csc_coef.csc_coef02);
+		VOP_MODULE_SET(vop2, vp, csc_coe10, csc_coef.csc_coef10);
+		VOP_MODULE_SET(vop2, vp, csc_coe11, csc_coef.csc_coef11);
+		VOP_MODULE_SET(vop2, vp, csc_coe12, csc_coef.csc_coef12);
+		VOP_MODULE_SET(vop2, vp, csc_coe20, csc_coef.csc_coef20);
+		VOP_MODULE_SET(vop2, vp, csc_coe21, csc_coef.csc_coef21);
+		VOP_MODULE_SET(vop2, vp, csc_coe22, csc_coef.csc_coef22);
+		VOP_MODULE_SET(vop2, vp, csc_offset0, csc_coef.csc_dc0);
+		VOP_MODULE_SET(vop2, vp, csc_offset1, csc_coef.csc_dc1);
+		VOP_MODULE_SET(vop2, vp, csc_offset2, csc_coef.csc_dc2);
+
+		range_type = csc_coef.range_type ? 0 : 1;
+		range_type <<= is_input_yuv ? 0 : 1;
+		VOP_MODULE_SET(vop2, vp, csc_mode, range_type);
+	}
+
+	VOP_MODULE_SET(vop2, vp, acm_r2y_en, post_r2y_en ? 1 : 0);
+	VOP_MODULE_SET(vop2, vp, csc_en, post_csc_en ? 1 : 0);
+	VOP_MODULE_SET(vop2, vp, acm_r2y_mode, vcstate->post_csc_mode);
+}
+
+static void vop3_post_acm_config(struct drm_crtc *crtc, struct post_acm *acm)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	s16 *lut_y;
+	s16 *lut_h;
+	s16 *lut_s;
+	u32 value;
+	int i;
+
+	writel(0, vop2->acm_regs + RK3528_ACM_CTRL);
+	VOP_MODULE_SET(vop2, vp, acm_bypass_en, 0);
+
+	if (!acm || !acm->acm_enable)
+		return;
+
+	/*
+	 * If acm update parameters, it need disable acm in the first frame,
+	 * then update parameters and enable acm in second frame.
+	 */
+	vop2_cfg_done(crtc);
+	readx_poll_timeout(readl, vop2->acm_regs + RK3528_ACM_CTRL, value, !value, 200, 50000);
+
+	value = RK3528_ACM_ENABLE + ((adjusted_mode->hdisplay & 0xfff) << 8) +
+		((adjusted_mode->vdisplay & 0xfff) << 20);
+	writel(value, vop2->acm_regs + RK3528_ACM_CTRL);
+
+
+	writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START);
+
+	value = (acm->y_gain & 0x3ff) + ((acm->h_gain << 10) & 0xffc00) +
+		((acm->s_gain << 20) & 0x3ff00000);
+	writel(value, vop2->acm_regs + RK3528_ACM_DELTA_RANGE);
+
+	lut_y = &acm->gain_lut_hy[0];
+	lut_h = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH];
+	lut_s = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH * 2];
+	for (i = 0; i < ACM_GAIN_LUT_HY_LENGTH; i++) {
+		value = (lut_y[i] & 0xff) + ((lut_h[i] << 8) & 0xff00) +
+			((lut_s[i] << 16) & 0xff0000);
+		writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HY_SEG0 + (i << 2));
+	}
+
+	lut_y = &acm->gain_lut_hs[0];
+	lut_h = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH];
+	lut_s = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH * 2];
+	for (i = 0; i < ACM_GAIN_LUT_HS_LENGTH; i++) {
+		value = (lut_y[i] & 0xff) + ((lut_h[i] << 8) & 0xff00) +
+			((lut_s[i] << 16) & 0xff0000);
+		writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HS_SEG0 + (i << 2));
+	}
+
+	lut_y = &acm->delta_lut_h[0];
+	lut_h = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH];
+	lut_s = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH * 2];
+	for (i = 0; i < ACM_DELTA_LUT_H_LENGTH; i++) {
+		value = (lut_y[i] & 0x3ff) + ((lut_h[i] << 12) & 0xff000) +
+			((lut_s[i] << 20) & 0x3ff00000);
+		writel(value, vop2->acm_regs + RK3528_ACM_YHS_DEL_HGAIN_SEG0 + (i << 2));
+	}
+
+	writel(1, vop2->acm_regs + RK3528_ACM_FETCH_DONE);
+}
+
+static void vop3_post_config(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct post_acm *acm;
+	struct post_csc *csc;
+
+	csc = vcstate->post_csc_data ? (struct post_csc *)vcstate->post_csc_data->data : NULL;
+	if (csc && memcmp(&vp->csc_info, csc, sizeof(struct post_csc)))
+		memcpy(&vp->csc_info, csc, sizeof(struct post_csc));
+	vop3_post_csc_config(crtc, &vp->acm_info, &vp->csc_info);
+
+	acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL;
+
+	if (acm && memcmp(&vp->acm_info, acm, sizeof(struct post_acm))) {
+		memcpy(&vp->acm_info, acm, sizeof(struct post_acm));
+		vop3_post_acm_config(crtc, &vp->acm_info);
+	} else if (crtc->state->active_changed) {
+		vop3_post_acm_config(crtc, &vp->acm_info);
+	}
+}
+
 static void vop2_cfg_update(struct drm_crtc *crtc,
 			    struct drm_crtc_state *old_crtc_state)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
 	struct vop2 *vop2 = vp->vop2;
+	const struct vop2_data *vop2_data = vop2->data;
+	const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
 	uint32_t val;
 	uint32_t r, g, b;
+	uint8_t out_mode;
 
 	spin_lock(&vop2->reg_lock);
+
+	if ((vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
+	     !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) ||
+	    vcstate->output_if & VOP_OUTPUT_IF_BT656)
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+	else
+		out_mode = vcstate->output_mode;
+	VOP_MODULE_SET(vop2, vp, out_mode, out_mode);
+
+	if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode))
+		VOP_MODULE_SET(vop2, vp, dsp_data_swap, DSP_RB_SWAP);
+	else
+		VOP_MODULE_SET(vop2, vp, dsp_data_swap, 0);
+
+	vop2_dither_setup(crtc);
 
 	VOP_MODULE_SET(vop2, vp, overlay_mode, vcstate->yuv_overlay);
 
@@ -6162,6 +6803,9 @@
 	vop2_post_config(crtc);
 
 	spin_unlock(&vop2->reg_lock);
+
+	if (vp_data->feature & (VOP_FEATURE_POST_ACM | VOP_FEATURE_POST_CSC))
+		vop3_post_config(crtc);
 }
 
 static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_cstate)
@@ -6304,6 +6948,13 @@
 		return NULL;
 
 	vcstate->vp_id = vp->id;
+	if (vcstate->hdr_ext_data)
+		drm_property_blob_get(vcstate->hdr_ext_data);
+	if (vcstate->acm_lut_data)
+		drm_property_blob_get(vcstate->acm_lut_data);
+	if (vcstate->post_csc_data)
+		drm_property_blob_get(vcstate->post_csc_data);
+
 	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
 	return &vcstate->base;
 }
@@ -6314,6 +6965,9 @@
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state);
 
 	__drm_atomic_helper_crtc_destroy_state(&vcstate->base);
+	drm_property_blob_put(vcstate->hdr_ext_data);
+	drm_property_blob_put(vcstate->acm_lut_data);
+	drm_property_blob_put(vcstate->post_csc_data);
 	kfree(vcstate);
 }
 
@@ -6436,9 +7090,52 @@
 		return 0;
 	}
 
+	if (property == vp->hdr_ext_data_prop)
+		return 0;
+
+	if (property == vp->acm_lut_data_prop)
+		return 0;
+
+	if (property == vp->post_csc_data_prop)
+		return 0;
+
 	DRM_ERROR("failed to get vop2 crtc property: %s\n", property->name);
 
 	return -EINVAL;
+}
+
+/* copied from drm_atomic.c */
+static int
+vop2_atomic_replace_property_blob_from_id(struct drm_device *dev,
+					 struct drm_property_blob **blob,
+					 uint64_t blob_id,
+					 ssize_t expected_size,
+					 ssize_t expected_elem_size,
+					 bool *replaced)
+{
+	struct drm_property_blob *new_blob = NULL;
+
+	if (blob_id != 0) {
+		new_blob = drm_property_lookup_blob(dev, blob_id);
+		if (new_blob == NULL)
+			return -EINVAL;
+
+		if (expected_size > 0 &&
+		    new_blob->length != expected_size) {
+			drm_property_blob_put(new_blob);
+			return -EINVAL;
+		}
+		if (expected_elem_size > 0 &&
+		    new_blob->length % expected_elem_size != 0) {
+			drm_property_blob_put(new_blob);
+			return -EINVAL;
+		}
+	}
+
+	*replaced |= drm_property_replace_blob(blob, new_blob);
+	drm_property_blob_put(new_blob);
+
+	return 0;
 }
 
 static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc,
@@ -6451,6 +7148,8 @@
 	struct drm_mode_config *mode_config = &drm_dev->mode_config;
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct vop2 *vop2 = vp->vop2;
+	bool replaced = false;
+	int ret;
 
 	if (property == mode_config->tv_left_margin_property) {
 		vcstate->left_margin = val;
@@ -6481,6 +7180,33 @@
 	if (property == vop2->line_flag_prop) {
 		vcstate->line_flag = val;
 		return 0;
+	}
+
+	if (property == vp->hdr_ext_data_prop) {
+		ret = vop2_atomic_replace_property_blob_from_id(drm_dev,
+								&vcstate->hdr_ext_data,
+								val,
+								-1, -1,
+								&replaced);
+		return ret;
+	}
+
+	if (property == vp->acm_lut_data_prop) {
+		ret = vop2_atomic_replace_property_blob_from_id(drm_dev,
+								&vcstate->acm_lut_data,
+								val,
+								sizeof(struct post_acm), -1,
+								&replaced);
+		return ret;
+	}
+
+	if (property == vp->post_csc_data_prop) {
+		ret = vop2_atomic_replace_property_blob_from_id(drm_dev,
+								&vcstate->post_csc_data,
+								val,
+								sizeof(struct post_csc), -1,
+								&replaced);
+		return ret;
 	}
 
 	DRM_ERROR("failed to set vop2 crtc property %s\n", property->name);
@@ -6694,6 +7420,16 @@
 			ret = IRQ_HANDLED;
 		}
 
+		if (vop2->version == VOP_VERSION_RK3528 && vp->id == 1) {
+			if (active_irqs & POST_BUF_EMPTY_INTR)
+				atomic_inc(&vp->post_buf_empty_flag);
+
+			if (active_irqs & FS_FIELD_INTR &&
+			    (atomic_read(&vp->post_buf_empty_flag) > 0 ||
+			     vp->need_reset_p2i_flag == true))
+				queue_work(vop2->workqueue, &vop2->post_buf_empty_work);
+		}
+
 		if (active_irqs & FS_FIELD_INTR) {
 			vop2_wb_handler(vp);
 			if (likely(!vp->skip_vsync) || (vp->layer_sel_update == false)) {
@@ -6784,6 +7520,51 @@
 	return 0;
 }
 
+static bool vop3_ignore_plane(struct vop2 *vop2, struct vop2_win *win)
+{
+	if (!is_vop3(vop2))
+		return false;
+
+	if (vop2->esmart_lb_mode == VOP3_ESMART_8K_MODE &&
+	    win->phys_id != ROCKCHIP_VOP2_ESMART0)
+		return true;
+	else if (vop2->esmart_lb_mode == VOP3_ESMART_4K_4K_MODE &&
+		 (win->phys_id == ROCKCHIP_VOP2_ESMART1 || win->phys_id == ROCKCHIP_VOP2_ESMART3))
+		return true;
+	else if (vop2->esmart_lb_mode == VOP3_ESMART_4K_2K_2K_MODE &&
+		 win->phys_id == ROCKCHIP_VOP2_ESMART1)
+		return true;
+	else
+		return false;
+}
+
+static u32 vop3_esmart_linebuffer_size(struct vop2 *vop2, struct vop2_win *win)
+{
+	if (!is_vop3(vop2) || vop2_cluster_window(win))
+		return vop2->data->max_output.width;
+
+	if (vop2->esmart_lb_mode == VOP3_ESMART_2K_2K_2K_2K_MODE ||
+	    (vop2->esmart_lb_mode == VOP3_ESMART_4K_2K_2K_MODE && win->phys_id != ROCKCHIP_VOP2_ESMART0))
+		return vop2->data->max_output.width / 2;
+	else
+		return vop2->data->max_output.width;
+}
+
+static void vop3_init_esmart_scale_engine(struct vop2 *vop2)
+{
+	u8 scale_engine_num = 0;
+	struct drm_plane *plane = NULL;
+
+	drm_for_each_plane(plane, vop2->drm_dev) {
+		struct vop2_win *win = to_vop2_win(plane);
+
+		if (win->parent || vop2_cluster_window(win))
+			continue;
+
+		win->scale_engine_num = scale_engine_num++;
+	}
+}
+
 static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, unsigned long possible_crtcs)
 {
 	struct rockchip_drm_private *private = vop2->drm_dev->dev_private;
@@ -6807,6 +7588,10 @@
 		if (win->feature & WIN_FEATURE_CLUSTER_SUB)
 			return -EACCES;
 	}
+
+	/* ignore some plane register according vop3 esmart lb mode */
+	if (vop3_ignore_plane(vop2, win))
+		return -EACCES;
 
 	ret = drm_universal_plane_init(vop2->drm_dev, &win->base, possible_crtcs,
 				       &vop2_plane_funcs, win->formats, win->nformats,
@@ -6847,7 +7632,7 @@
 							  "INPUT_WIDTH", 0, max_width);
 	win->input_height_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE,
 							   "INPUT_HEIGHT", 0, max_height);
-	max_width = vop2->data->max_output.width;
+	max_width = vop3_esmart_linebuffer_size(vop2, win);
 	max_height = vop2->data->max_output.height;
 	if (win->feature & WIN_FEATURE_CLUSTER_SUB)
 		max_width >>= 1;
@@ -6883,15 +7668,27 @@
 	return 0;
 }
 
-static struct drm_plane *vop2_cursor_plane_init(struct vop2_video_port *vp,
-						unsigned long possible_crtcs)
+static struct drm_plane *vop2_cursor_plane_init(struct vop2_video_port *vp)
 {
 	struct vop2 *vop2 = vp->vop2;
 	struct drm_plane *cursor = NULL;
 	struct vop2_win *win;
+	unsigned long possible_crtcs = 0;
 
 	win = vop2_find_win_by_phys_id(vop2, vp->cursor_win_id);
 	if (win) {
+		if (vop2->disable_win_move) {
+			const struct vop2_data *vop2_data = vop2->data;
+			struct drm_crtc *crtc = vop2_find_crtc_by_plane_mask(vop2, win->phys_id);
+
+			if (crtc)
+				possible_crtcs = drm_crtc_mask(crtc);
+			else
+				possible_crtcs = (1 << vop2_data->nr_vps) - 1;
+		}
+
+		if (win->possible_crtcs)
+			possible_crtcs = win->possible_crtcs;
 		win->type = DRM_PLANE_TYPE_CURSOR;
 		win->zpos = vop2->registered_num_wins - 1;
 		if (!vop2_plane_init(vop2, win, possible_crtcs))
@@ -6925,6 +7722,7 @@
 		if (!lut_len)
 			continue;
 		vp->gamma_lut_len = vp_data->gamma_lut_len;
+		vp->lut_dma_rid = vp_data->lut_dma_rid;
 		vp->lut = devm_kmalloc_array(dev, lut_len, sizeof(*vp->lut),
 					     GFP_KERNEL);
 		if (!vp->lut)
@@ -7009,6 +7807,53 @@
 	return 0;
 }
 
+static int vop2_crtc_create_hdr_property(struct vop2 *vop2, struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_property *prop;
+
+	prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "HDR_EXT_DATA", 0);
+	if (!prop) {
+		DRM_DEV_ERROR(vop2->dev, "create hdr ext data prop for vp%d failed\n", vp->id);
+		return -ENOMEM;
+	}
+	vp->hdr_ext_data_prop = prop;
+	drm_object_attach_property(&crtc->base, vp->hdr_ext_data_prop, 0);
+
+	return 0;
+}
+
+static int vop2_crtc_create_post_acm_property(struct vop2 *vop2, struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_property *prop;
+
+	prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "ACM_LUT_DATA", 0);
+	if (!prop) {
+		DRM_DEV_ERROR(vop2->dev, "create acm lut data prop for vp%d failed\n", vp->id);
+		return -ENOMEM;
+	}
+	vp->acm_lut_data_prop = prop;
+	drm_object_attach_property(&crtc->base, vp->acm_lut_data_prop, 0);
+
+	return 0;
+}
+
+static int vop2_crtc_create_post_csc_property(struct vop2 *vop2, struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct drm_property *prop;
+
+	prop = drm_property_create(vop2->drm_dev, DRM_MODE_PROP_BLOB, "POST_CSC_DATA", 0);
+	if (!prop) {
+		DRM_DEV_ERROR(vop2->dev, "create post csc data prop for vp%d failed\n", vp->id);
+		return -ENOMEM;
+	}
+	vp->post_csc_data_prop = prop;
+	drm_object_attach_property(&crtc->base, vp->post_csc_data_prop, 0);
+
+	return 0;
+}
 #define RK3566_MIRROR_PLANE_MASK (BIT(ROCKCHIP_VOP2_CLUSTER1) | BIT(ROCKCHIP_VOP2_ESMART1) | \
 				  BIT(ROCKCHIP_VOP2_SMART1))
 
@@ -7021,7 +7866,7 @@
 	const struct vop2_data *vop2_data = vop2->data;
 	struct drm_device *drm_dev = vop2->drm_dev;
 	struct device *dev = vop2->dev;
-	struct drm_plane *plane;
+	struct drm_plane *primary;
 	struct drm_plane *cursor = NULL;
 	struct drm_crtc *crtc;
 	struct device_node *port;
@@ -7069,6 +7914,9 @@
 		vp->id = vp_data->id;
 		vp->regs = vp_data->regs;
 		vp->cursor_win_id = -1;
+		primary = NULL;
+		cursor = NULL;
+
 		if (vop2->disable_win_move)
 			possible_crtcs = BIT(registered_num_crtcs);
 
@@ -7116,6 +7964,7 @@
 				win->type = DRM_PLANE_TYPE_PRIMARY;
 			}
 		} else {
+			j = 0;
 			while (j < vop2->registered_num_wins) {
 				be_used_for_primary_plane = false;
 				win = &vop2->win[j];
@@ -7157,24 +8006,43 @@
 				DRM_DEV_ERROR(vop2->dev, "failed to init primary plane\n");
 				break;
 			}
-			plane = &win->base;
+			primary = &win->base;
 		}
 
 		/* some times we want a cursor window for some vp */
+		if (vp->cursor_win_id < 0) {
+			bool be_used_for_cursor_plane = false;
+
+			j = 0;
+			while (j < vop2->registered_num_wins) {
+				win = &vop2->win[j++];
+
+				if (win->parent || (win->feature & WIN_FEATURE_CLUSTER_SUB))
+					continue;
+
+				if (win->type != DRM_PLANE_TYPE_CURSOR)
+					continue;
+
+				for (k = 0; k < vop2_data->nr_vps; k++) {
+					if (vop2->vps[k].cursor_win_id == win->phys_id)
+						be_used_for_cursor_plane = true;
+				}
+				if (be_used_for_cursor_plane)
+					continue;
+				vp->cursor_win_id = win->phys_id;
+			}
+		}
+
 		if (vp->cursor_win_id >= 0) {
-			if (win->possible_crtcs)
-				possible_crtcs = win->possible_crtcs;
-			cursor = vop2_cursor_plane_init(vp, possible_crtcs);
+			cursor = vop2_cursor_plane_init(vp);
 			if (!cursor)
 				DRM_WARN("failed to init cursor plane for vp%d\n", vp->id);
 			else
 				DRM_DEV_INFO(vop2->dev, "%s as cursor plane for vp%d\n",
 					     cursor->name, vp->id);
-		} else {
-			cursor = NULL;
 		}
 
-		ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, cursor, &vop2_crtc_funcs,
+		ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, &vop2_crtc_funcs,
 						"video_port%d", vp->id);
 		if (ret) {
 			DRM_DEV_ERROR(vop2->dev, "crtc init for video_port%d failed\n", i);
@@ -7202,7 +8070,23 @@
 					   drm_dev->mode_config.tv_top_margin_property, 100);
 		drm_object_attach_property(&crtc->base,
 					   drm_dev->mode_config.tv_bottom_margin_property, 100);
-		vop2_crtc_create_plane_mask_property(vop2, crtc, plane_mask);
+		if (plane_mask)
+			vop2_crtc_create_plane_mask_property(vop2, crtc, plane_mask);
+
+		if (vp_data->feature & VOP_FEATURE_VIVID_HDR) {
+			vop2_crtc_create_hdr_property(vop2, crtc);
+			vp->hdr_lut_gem_obj = rockchip_gem_create_object(vop2->drm_dev,
+				RK_HDRVIVID_TONE_SCA_AXI_TAB_LENGTH * 4, true, 0);
+			if (IS_ERR(vp->hdr_lut_gem_obj)) {
+				DRM_ERROR("create hdr lut obj failed\n");
+				return -ENOMEM;
+			}
+		}
+		if (vp_data->feature & VOP_FEATURE_POST_ACM)
+			vop2_crtc_create_post_acm_property(vop2, crtc);
+		if (vp_data->feature & VOP_FEATURE_POST_CSC)
+			vop2_crtc_create_post_csc_property(vop2, crtc);
+
 		registered_num_crtcs++;
 	}
 
@@ -7260,12 +8144,18 @@
 			DRM_WARN("failed to init overlay plane %s, ret:%d\n", win->name, ret);
 	}
 
+	if (is_vop3(vop2))
+		vop3_init_esmart_scale_engine(vop2);
+
 	return registered_num_crtcs;
 }
 
 static void vop2_destroy_crtc(struct drm_crtc *crtc)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+
+	if (vp->hdr_lut_gem_obj)
+		rockchip_gem_free_object(&vp->hdr_lut_gem_obj->base);
 
 	of_node_put(crtc->port);
 
@@ -7322,7 +8212,6 @@
 		win->axi_id = win_data->axi_id;
 		win->axi_yrgb_id = win_data->axi_yrgb_id;
 		win->axi_uv_id = win_data->axi_uv_id;
-		win->scale_engine_num = win_data->scale_engine_num;
 		win->possible_crtcs = win_data->possible_crtcs;
 
 		num_wins++;
@@ -7351,6 +8240,7 @@
 			area->vsd_filter_mode = win_data->vsd_filter_mode;
 			area->hsd_pre_filter_mode = win_data->hsd_pre_filter_mode;
 			area->vsd_pre_filter_mode = win_data->vsd_pre_filter_mode;
+			area->possible_crtcs = win->possible_crtcs;
 
 			area->vop2 = vop2;
 			area->win_id = i;
@@ -7414,6 +8304,43 @@
 	return 0;
 }
 
+static void post_buf_empty_work_event(struct work_struct *work)
+{
+	struct vop2 *vop2 = container_of(work, struct vop2, post_buf_empty_work);
+	struct rockchip_drm_private *private = vop2->drm_dev->dev_private;
+	struct vop2_video_port *vp = &vop2->vps[1];
+
+	/*
+	 * For RK3528, VP1 only supports NTSC and PAL mode(both interlace). If
+	 * POST_BUF_EMPTY_INTR comes, it is needed to reset the p2i_en bit, in
+	 * order to update the line parity flag, which ensures the correct order
+	 * of odd and even lines.
+	 */
+	if (vop2->version == VOP_VERSION_RK3528) {
+		if (atomic_read(&vp->post_buf_empty_flag) > 0) {
+			atomic_set(&vp->post_buf_empty_flag, 0);
+
+			mutex_lock(&private->ovl_lock);
+			vop2_wait_for_fs_by_done_bit_status(vp);
+			VOP_MODULE_SET(vop2, vp, p2i_en, 0);
+			vop2_cfg_done(&vp->crtc);
+			vop2_wait_for_fs_by_done_bit_status(vp);
+			mutex_unlock(&private->ovl_lock);
+
+			vp->need_reset_p2i_flag = true;
+		} else if (vp->need_reset_p2i_flag == true) {
+			mutex_lock(&private->ovl_lock);
+			vop2_wait_for_fs_by_done_bit_status(vp);
+			VOP_MODULE_SET(vop2, vp, p2i_en, 1);
+			vop2_cfg_done(&vp->crtc);
+			vop2_wait_for_fs_by_done_bit_status(vp);
+			mutex_unlock(&private->ovl_lock);
+
+			vp->need_reset_p2i_flag = false;
+		}
+	}
+}
+
 static int vop2_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -7454,6 +8381,23 @@
 	vop2->disable_afbc_win = of_property_read_bool(dev->of_node, "disable-afbc-win");
 	vop2->disable_win_move = of_property_read_bool(dev->of_node, "disable-win-move");
 	vop2->skip_ref_fb = of_property_read_bool(dev->of_node, "skip-ref-fb");
+
+	/*
+	 * esmart lb mode default config at vop2_reg.c vop2_data.esmart_lb_mode,
+	 * you can rewrite at dts vop node:
+	 *
+	 * VOP3_ESMART_8K_MODE = 0,
+	 * VOP3_ESMART_4K_4K_MODE = 1,
+	 * VOP3_ESMART_4K_2K_2K_MODE = 2,
+	 * VOP3_ESMART_2K_2K_2K_2K_MODE = 3,
+	 *
+	 * &vop {
+	 *	 esmart_lb_mode = /bits/ 8 <2>;
+	 * };
+	 */
+	ret = of_property_read_u8(dev->of_node, "esmart_lb_mode", &vop2->esmart_lb_mode);
+	if (ret < 0)
+		vop2->esmart_lb_mode = vop2->data->esmart_lb_mode;
 
 	ret = vop2_win_init(vop2);
 	if (ret)
@@ -7538,6 +8482,12 @@
 	spin_lock_init(&vop2->irq_lock);
 	mutex_init(&vop2->vop2_lock);
 
+	if (vop2->version == VOP_VERSION_RK3528) {
+		atomic_set(&vop2->vps[1].post_buf_empty_flag, 0);
+		vop2->workqueue = create_workqueue("post_buf_empty_wq");
+		INIT_WORK(&vop2->post_buf_empty_work, post_buf_empty_work_event);
+	}
+
 	ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2);
 	if (ret)
 		return ret;

--
Gitblit v1.6.2