From 2f7c68cb55ecb7331f2381deb497c27155f32faf Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 03 Jan 2024 09:43:39 +0000
Subject: [PATCH] update kernel to 5.10.198

---
 kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2919 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 2,512 insertions(+), 407 deletions(-)

diff --git a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 25cf927..3253cf2 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -1,35 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
  */
 
 #include <linux/clk.h>
+#include <linux/gpio/consumer.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
 #include <linux/pm_runtime.h>
 
 #include <drm/drm_of.h>
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_dsc.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
 
 #include <uapi/linux/videodev2.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
+#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
+
 #define RK3228_GRF_SOC_CON2		0x0408
-#define RK3228_DDC_MASK_EN		((3 << 13) | (3 << (13 + 16)))
+#define RK3228_HDMI_SDAIN_MSK		BIT(14)
+#define RK3228_HDMI_SCLIN_MSK		BIT(13)
 #define RK3228_GRF_SOC_CON6		0x0418
-#define RK3228_IO_3V_DOMAIN		((7 << 4) | (7 << (4 + 16)))
+#define RK3228_HDMI_HPD_VSEL		BIT(6)
+#define RK3228_HDMI_SDA_VSEL		BIT(5)
+#define RK3228_HDMI_SCL_VSEL		BIT(4)
 
 #define RK3288_GRF_SOC_CON6		0x025C
 #define RK3288_HDMI_LCDC_SEL		BIT(4)
@@ -57,13 +65,85 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
+#define RK3528_VO_GRF_HDMI_MASK		0x60014
+#define RK3528_HDMI_SNKDET_SEL		BIT(6)
+#define RK3528_HDMI_SNKDET		BIT(5)
+#define RK3528_HDMI_CECIN_MSK		BIT(2)
+#define RK3528_HDMI_SDAIN_MSK		BIT(1)
+#define RK3528_HDMI_SCLIN_MSK		BIT(0)
+
+#define RK3528PMU_GRF_SOC_CON6		0x70018
+#define RK3528_HDMI_SDA5V_GRF		BIT(6)
+#define RK3528_HDMI_SCL5V_GRF		BIT(5)
+#define RK3528_HDMI_CEC5V_GRF		BIT(4)
+#define RK3528_HDMI_HPD5V_GRF		BIT(3)
+
+#define RK3528_GPIO_SWPORT_DR_L		0x0000
+#define RK3528_GPIO0_A2_DR		BIT(2)
+
 #define RK3568_GRF_VO_CON1		0x0364
 #define RK3568_HDMI_SDAIN_MSK		BIT(15)
 #define RK3568_HDMI_SCLIN_MSK		BIT(14)
 
-#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
-#define RK_HDMI_COLORIMETRY_BT2020	(HDMI_COLORIMETRY_EXTENDED + \
-					 HDMI_EXTENDED_COLORIMETRY_BT2020)
+#define RK3588_GRF_SOC_CON2		0x0308
+#define RK3588_HDMI1_HPD_INT_MSK	BIT(15)
+#define RK3588_HDMI1_HPD_INT_CLR	BIT(14)
+#define RK3588_HDMI0_HPD_INT_MSK	BIT(13)
+#define RK3588_HDMI0_HPD_INT_CLR	BIT(12)
+#define RK3588_GRF_SOC_CON7		0x031c
+#define RK3588_SET_HPD_PATH_MASK	(0x3 << 12)
+#define RK3588_GRF_SOC_STATUS1		0x0384
+#define RK3588_HDMI0_LOW_MORETHAN100MS	BIT(20)
+#define RK3588_HDMI0_HPD_PORT_LEVEL	BIT(19)
+#define RK3588_HDMI0_IHPD_PORT		BIT(18)
+#define RK3588_HDMI0_OHPD_INT		BIT(17)
+#define RK3588_HDMI0_LEVEL_INT		BIT(16)
+#define RK3588_HDMI0_INTR_CHANGE_CNT	(0x7 << 13)
+#define RK3588_HDMI1_LOW_MORETHAN100MS	BIT(28)
+#define RK3588_HDMI1_HPD_PORT_LEVEL	BIT(27)
+#define RK3588_HDMI1_IHPD_PORT		BIT(26)
+#define RK3588_HDMI1_OHPD_INT		BIT(25)
+#define RK3588_HDMI1_LEVEL_INT		BIT(24)
+#define RK3588_HDMI1_INTR_CHANGE_CNT	(0x7 << 21)
+
+#define RK3588_GRF_VO1_CON1		0x0004
+#define HDCP1_P1_GPIO_IN		BIT(9)
+#define RK3588_GRF_VO1_CON3		0x000c
+#define RK3588_COLOR_FORMAT_MASK	0xf
+#define RK3588_RGB			0
+#define RK3588_YUV422			0x1
+#define RK3588_YUV444			0x2
+#define RK3588_YUV420			0x3
+#define RK3588_COMPRESSED_DATA		0xb
+#define RK3588_COLOR_DEPTH_MASK		(0xf << 4)
+#define RK3588_8BPC			0
+#define RK3588_10BPC			(0x6 << 4)
+#define RK3588_CECIN_MASK		BIT(8)
+#define RK3588_SCLIN_MASK		BIT(9)
+#define RK3588_SDAIN_MASK		BIT(10)
+#define RK3588_MODE_MASK		BIT(11)
+#define RK3588_COMPRESS_MODE_MASK	BIT(12)
+#define RK3588_I2S_SEL_MASK		BIT(13)
+#define RK3588_SPDIF_SEL_MASK		BIT(14)
+#define RK3588_GRF_VO1_CON4		0x0010
+#define RK3588_HDMI21_MASK		BIT(0)
+#define RK3588_GRF_VO1_CON9		0x0024
+#define RK3588_HDMI0_GRANT_SEL		BIT(10)
+#define RK3588_HDMI0_GRANT_SW		BIT(11)
+#define RK3588_HDMI1_GRANT_SEL		BIT(12)
+#define RK3588_HDMI1_GRANT_SW		BIT(13)
+#define RK3588_GRF_VO1_CON4		0x0010
+#define RK3588_HDMI_HDCP14_MEM_EN	BIT(15)
+#define RK3588_GRF_VO1_CON6		0x0018
+#define RK3588_GRF_VO1_CON7		0x001c
+
+#define COLOR_DEPTH_10BIT		BIT(31)
+#define HDMI_FRL_MODE			BIT(30)
+#define HDMI_EARC_MODE			BIT(29)
+#define DATA_RATE_MASK			0xFFFFFFF
+
+#define HDMI20_MAX_RATE			600000
+#define HDMI_8K60_RATE			2376000
 
 /**
  * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips
@@ -77,72 +157,99 @@
 	int	ddc_en_reg;
 	u32	lcdsel_big;
 	u32	lcdsel_lit;
+	bool	split_mode;
 };
 
-/* HDMI output pixel format */
-enum drm_hdmi_output_type {
-	DRM_HDMI_OUTPUT_DEFAULT_RGB, /* default RGB */
-	DRM_HDMI_OUTPUT_YCBCR444, /* YCBCR 444 */
-	DRM_HDMI_OUTPUT_YCBCR422, /* YCBCR 422 */
-	DRM_HDMI_OUTPUT_YCBCR420, /* YCBCR 420 */
-	DRM_HDMI_OUTPUT_YCBCR_HQ, /* Highest subsampled YUV */
-	DRM_HDMI_OUTPUT_YCBCR_LQ, /* Lowest subsampled YUV */
-	DRM_HDMI_OUTPUT_INVALID, /* Guess what ? */
-};
-
-enum dw_hdmi_rockchip_color_depth {
-	ROCKCHIP_HDMI_DEPTH_8,
-	ROCKCHIP_HDMI_DEPTH_10,
-	ROCKCHIP_HDMI_DEPTH_12,
-	ROCKCHIP_HDMI_DEPTH_16,
-	ROCKCHIP_HDMI_DEPTH_420_10,
-	ROCKCHIP_HDMI_DEPTH_420_12,
-	ROCKCHIP_HDMI_DEPTH_420_16
+enum hdmi_frl_rate_per_lane {
+	FRL_12G_PER_LANE = 12,
+	FRL_10G_PER_LANE = 10,
+	FRL_8G_PER_LANE = 8,
+	FRL_6G_PER_LANE = 6,
+	FRL_3G_PER_LANE = 3,
 };
 
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
+	struct regmap *vo1_regmap;
+	void __iomem *gpio_base;
 	struct drm_encoder encoder;
+	struct drm_device *drm_dev;
 	const struct rockchip_hdmi_chip_data *chip_data;
+	struct dw_hdmi_plat_data *plat_data;
+	struct clk *aud_clk;
 	struct clk *phyref_clk;
 	struct clk *grf_clk;
 	struct clk *hclk_vio;
+	struct clk *hclk_vo1;
 	struct clk *hclk_vop;
+	struct clk *hpd_clk;
+	struct clk *pclk;
+	struct clk *earc_clk;
+	struct clk *hdmitx_ref;
+	struct clk *link_clk;
 	struct dw_hdmi *hdmi;
+	struct dw_hdmi_qp *hdmi_qp;
 
 	struct phy *phy;
-	int max_tmdsclk;
+
+	u32 max_tmdsclk;
 	bool unsupported_yuv_input;
 	bool unsupported_deep_color;
 	bool skip_check_420_mode;
-	bool mode_changed;
+	bool hpd_wake_en;
 	u8 force_output;
 	u8 id;
+	bool hpd_stat;
+	bool is_hdmi_qp;
 
 	unsigned long bus_format;
 	unsigned long output_bus_format;
 	unsigned long enc_out_encoding;
+	unsigned long prev_bus_format;
 	int color_changed;
+	int hpd_irq;
 
 	struct drm_property *color_depth_property;
 	struct drm_property *hdmi_output_property;
 	struct drm_property *colordepth_capacity;
 	struct drm_property *outputmode_capacity;
-	struct drm_property *colorimetry_property;
 	struct drm_property *quant_range;
 	struct drm_property *hdr_panel_metadata_property;
+	struct drm_property *next_hdr_sink_data_property;
 	struct drm_property *output_hdmi_dvi;
 	struct drm_property *output_type_capacity;
+	struct drm_property *allm_capacity;
+	struct drm_property *allm_enable;
+	struct drm_property *hdcp_state_property;
 
 	struct drm_property_blob *hdr_panel_blob_ptr;
+	struct drm_property_blob *next_hdr_data_ptr;
 
 	unsigned int colordepth;
 	unsigned int colorimetry;
 	unsigned int hdmi_quant_range;
 	unsigned int phy_bus_width;
-	enum drm_hdmi_output_type hdmi_output;
+	unsigned int enable_allm;
+	enum rk_if_color_format hdmi_output;
 	struct rockchip_drm_sub_dev sub_dev;
+
+	u8 max_frl_rate_per_lane;
+	u8 max_lanes;
+	u8 add_func;
+	u8 edid_colorimetry;
+	u8 hdcp_status;
+	struct rockchip_drm_dsc_cap dsc_cap;
+	struct next_hdr_sink_data next_hdr_data;
+	struct dw_hdmi_link_config link_cfg;
+	struct gpio_desc *enable_gpio;
+
+	struct delayed_work work;
+	struct workqueue_struct *workqueue;
+	struct gpio_desc *hpd_gpiod;
+	struct pinctrl *p;
+	struct pinctrl_state *idle_state;
+	struct pinctrl_state *default_state;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -260,94 +367,6 @@
 	}
 };
 
-static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_rk356x[] = {
-	{
-		30666000, {
-			{ 0x00b3, 0x0000 },
-			{ 0x2153, 0x0000 },
-			{ 0x40f3, 0x0000 },
-		},
-	},  {
-		36800000, {
-			{ 0x00b3, 0x0000 },
-			{ 0x2153, 0x0000 },
-			{ 0x40a2, 0x0001 },
-		},
-	},  {
-		46000000, {
-			{ 0x00b3, 0x0000 },
-			{ 0x2142, 0x0001 },
-			{ 0x40a2, 0x0001 },
-		},
-	},  {
-		61333000, {
-			{ 0x0072, 0x0001 },
-			{ 0x2142, 0x0001 },
-			{ 0x40a2, 0x0001 },
-		},
-	},  {
-		73600000, {
-			{ 0x0072, 0x0001 },
-			{ 0x2142, 0x0001 },
-			{ 0x4061, 0x0002 },
-		},
-	},  {
-		92000000, {
-			{ 0x0072, 0x0001 },
-			{ 0x2145, 0x0002 },
-			{ 0x4061, 0x0002 },
-		},
-	},  {
-		122666000, {
-			{ 0x0051, 0x0002 },
-			{ 0x2145, 0x0002 },
-			{ 0x4061, 0x0002 },
-		},
-	},  {
-		147200000, {
-			{ 0x0051, 0x0002 },
-			{ 0x2145, 0x0002 },
-			{ 0x4064, 0x0003 },
-		},
-	},  {
-		184000000, {
-			{ 0x0051, 0x0002 },
-			{ 0x214c, 0x0003 },
-			{ 0x4064, 0x0003 },
-		},
-	},  {
-		226666000, {
-			{ 0x0040, 0x0003 },
-			{ 0x214c, 0x0003 },
-			{ 0x4064, 0x0003 },
-		},
-	},  {
-		272000000, {
-			{ 0x0040, 0x0003 },
-			{ 0x214c, 0x0003 },
-			{ 0x5a64, 0x0003 },
-		},
-	},  {
-		340000000, {
-			{ 0x0040, 0x0002 },
-			{ 0x3b4c, 0x0003 },
-			{ 0x5a64, 0x0003 },
-		},
-	},  {
-		600000000, {
-			{ 0x1a40, 0x0003 },
-			{ 0x3b4c, 0x0003 },
-			{ 0x5a64, 0x0003 },
-		},
-	},  {
-		~0UL, {
-			{ 0x0000, 0x0000 },
-			{ 0x0000, 0x0000 },
-			{ 0x0000, 0x0000 },
-		},
-	}
-};
-
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
 	{
 		30666000, {
@@ -437,19 +456,6 @@
 	}
 };
 
-static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr_rk356x[] = {
-	/*      pixelclk    bpp8    bpp10   bpp12 */
-	{
-		272000000, { 0x0000, 0x0000, 0x0000 },
-	},  {
-		340000000, { 0x0001, 0x0000, 0x0000 },
-	},  {
-		600000000, { 0x0000, 0x0000, 0x0000 },
-	},  {
-		~0UL,      { 0x0000, 0x0000, 0x0000},
-	}
-};
-
 static struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	/*pixelclk   symbol   term   vlev*/
 	{ 74250000,  0x8009, 0x0004, 0x0272},
@@ -459,6 +465,744 @@
 	{ ~0UL,	     0x0000, 0x0000, 0x0000},
 	{ ~0UL,      0x0000, 0x0000, 0x0000},
 };
+
+enum ROW_INDEX_BPP {
+	ROW_INDEX_6BPP = 0,
+	ROW_INDEX_8BPP,
+	ROW_INDEX_10BPP,
+	ROW_INDEX_12BPP,
+	ROW_INDEX_23BPP,
+	MAX_ROW_INDEX
+};
+
+enum COLUMN_INDEX_BPC {
+	COLUMN_INDEX_8BPC = 0,
+	COLUMN_INDEX_10BPC,
+	COLUMN_INDEX_12BPC,
+	COLUMN_INDEX_14BPC,
+	COLUMN_INDEX_16BPC,
+	MAX_COLUMN_INDEX
+};
+
+#define PPS_TABLE_LEN 8
+#define PPS_BPP_LEN 4
+#define PPS_BPC_LEN 2
+
+struct pps_data {
+	u32 pic_width;
+	u32 pic_height;
+	u32 slice_width;
+	u32 slice_height;
+	bool convert_rgb;
+	u8 bpc;
+	u8 bpp;
+	u8 raw_pps[128];
+};
+
+/*
+ * Selected Rate Control Related Parameter Recommended Values
+ * from DSC_v1.11 spec & C Model release: DSC_model_20161212
+ */
+static struct pps_data pps_datas[PPS_TABLE_LEN] = {
+	{
+		/* 7680x4320/960X96 rgb 8bpc 12bpp */
+		7680, 4320, 960, 96, 1, 8, 192,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
+			0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
+			0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
+			0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00,
+			0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
+			0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 11bpp */
+		7680, 4320, 960, 96, 1, 8, 176,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
+			0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
+			0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
+			0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
+			0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 10bpp */
+		7680, 4320, 960, 96, 1, 8, 160,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
+			0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
+			0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
+			0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
+			0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 9bpp */
+		7680, 4320, 960, 96, 1, 8, 144,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
+			0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
+			0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
+			0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
+			0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 12bpp */
+		7680, 4320, 960, 96, 1, 10, 192,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
+			0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
+			0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
+			0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00,
+			0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
+			0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 11bpp */
+		7680, 4320, 960, 96, 1, 10, 176,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
+			0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
+			0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
+			0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
+			0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 10bpp */
+		7680, 4320, 960, 96, 1, 10, 160,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
+			0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
+			0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
+			0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
+			0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 9bpp */
+		7680, 4320, 960, 96, 1, 10, 144,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
+			0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
+			0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
+			0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
+			0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+};
+
+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+		return true;
+
+	default:
+	return false;
+	}
+}
+
+static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		return 8;
+
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		return 10;
+
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+		return 12;
+
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+		return 16;
+
+	default:
+		return 0;
+	}
+}
+
+static int hdmi_bus_fmt_to_color_format(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+		return RK_IF_FORMAT_YCBCR420;
+
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+		return RK_IF_FORMAT_YCBCR444;
+
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+	case MEDIA_BUS_FMT_YVYU12_1X24:
+		return RK_IF_FORMAT_YCBCR422;
+
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+	default:
+		return RK_IF_FORMAT_RGB;
+	}
+}
+
+static unsigned int
+hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock)
+{
+	unsigned int tmdsclock = pixelclock;
+	unsigned int depth =
+		hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
+
+	if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) {
+		switch (depth) {
+		case 16:
+			tmdsclock = pixelclock * 2;
+			break;
+		case 12:
+			tmdsclock = pixelclock * 3 / 2;
+			break;
+		case 10:
+			tmdsclock = pixelclock * 5 / 4;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return tmdsclock;
+}
+
+static int rockchip_hdmi_match_by_id(struct device *dev, const void *data)
+{
+	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+	const unsigned int *id = data;
+
+	return hdmi->id == *id;
+}
+
+static struct rockchip_hdmi *
+rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id)
+{
+	struct device *dev;
+
+	dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id);
+	if (!dev)
+		return NULL;
+
+	return dev_get_drvdata(dev);
+}
+
+static void hdmi_select_link_config(struct rockchip_hdmi *hdmi,
+				    struct drm_crtc_state *crtc_state,
+				    unsigned int tmdsclk)
+{
+	struct drm_display_mode mode = {};
+	int max_lanes, max_rate_per_lane;
+	int max_dsc_lanes, max_dsc_rate_per_lane;
+	unsigned long max_frl_rate;
+
+	drm_mode_copy(&mode, &crtc_state->mode);
+	if (hdmi->plat_data->split_mode)
+		drm_mode_convert_to_origin_mode(&mode);
+
+	max_lanes = hdmi->max_lanes;
+	max_rate_per_lane = hdmi->max_frl_rate_per_lane;
+	max_frl_rate = max_lanes * max_rate_per_lane * 1000000;
+
+	hdmi->link_cfg.dsc_mode = false;
+	hdmi->link_cfg.frl_lanes = max_lanes;
+	hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
+	hdmi->link_cfg.add_func = hdmi->add_func;
+
+	if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) {
+		dev_info(hdmi->dev, "use tmds mode\n");
+		hdmi->link_cfg.frl_mode = false;
+		return;
+	}
+
+	hdmi->link_cfg.frl_mode = true;
+
+	if (!hdmi->dsc_cap.v_1p2)
+		return;
+
+	max_dsc_lanes = hdmi->dsc_cap.max_lanes;
+	max_dsc_rate_per_lane =
+		hdmi->dsc_cap.max_frl_rate_per_lane;
+
+	if (mode.clock >= HDMI_8K60_RATE &&
+	    !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) &&
+	    !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) {
+		hdmi->link_cfg.dsc_mode = true;
+		hdmi->link_cfg.frl_lanes = max_dsc_lanes;
+		hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane;
+	} else {
+		hdmi->link_cfg.dsc_mode = false;
+		hdmi->link_cfg.frl_lanes = max_lanes;
+		hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+static int hdmi_dsc_get_slice_height(int vactive)
+{
+	int slice_height;
+
+	/*
+	 * Slice Height determination : HDMI2.1 Section 7.7.5.2
+	 * Select smallest slice height >=96, that results in a valid PPS and
+	 * requires minimum padding lines required for final slice.
+	 *
+	 * Assumption : Vactive is even.
+	 */
+	for (slice_height = 96; slice_height <= vactive; slice_height += 2)
+		if (vactive % slice_height == 0)
+			return slice_height;
+
+	return 0;
+}
+
+static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi,
+				   struct drm_crtc_state *crtc_state,
+				   int src_max_slices, int src_max_slice_width,
+				   int hdmi_max_slices, int hdmi_throughput)
+{
+/* Pixel rates in KPixels/sec */
+#define HDMI_DSC_PEAK_PIXEL_RATE		2720000
+/*
+ * Rates at which the source and sink are required to process pixels in each
+ * slice, can be two levels: either at least 340000KHz or at least 40000KHz.
+ */
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0		340000
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1		400000
+
+/* Spec limits the slice width to 2720 pixels */
+#define MAX_HDMI_SLICE_WIDTH			2720
+	int kslice_adjust;
+	int adjusted_clk_khz;
+	int min_slices;
+	int target_slices;
+	int max_throughput; /* max clock freq. in khz per slice */
+	int max_slice_width;
+	int slice_width;
+	int pixel_clock = crtc_state->mode.clock;
+
+	if (!hdmi_throughput)
+		return 0;
+
+	/*
+	 * Slice Width determination : HDMI2.1 Section 7.7.5.1
+	 * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as
+	 * for 4:4:4 is 1.0. Multiplying these factors by 10 and later
+	 * dividing adjusted clock value by 10.
+	 */
+	if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) ||
+	    hdmi_bus_fmt_is_rgb(hdmi->output_bus_format))
+		kslice_adjust = 10;
+	else
+		kslice_adjust = 5;
+
+	/*
+	 * As per spec, the rate at which the source and the sink process
+	 * the pixels per slice are at two levels: at least 340Mhz or 400Mhz.
+	 * This depends upon the pixel clock rate and output formats
+	 * (kslice adjust).
+	 * If pixel clock * kslice adjust >= 2720MHz slices can be processed
+	 * at max 340MHz, otherwise they can be processed at max 400MHz.
+	 */
+
+	adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10);
+
+	if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE)
+		max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0;
+	else
+		max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1;
+
+	/*
+	 * Taking into account the sink's capability for maximum
+	 * clock per slice (in MHz) as read from HF-VSDB.
+	 */
+	max_throughput = min(max_throughput, hdmi_throughput * 1000);
+
+	min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput);
+	max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width);
+
+	/*
+	 * Keep on increasing the num of slices/line, starting from min_slices
+	 * per line till we get such a number, for which the slice_width is
+	 * just less than max_slice_width. The slices/line selected should be
+	 * less than or equal to the max horizontal slices that the combination
+	 * of PCON encoder and HDMI decoder can support.
+	 */
+	do {
+		if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1)
+			target_slices = 1;
+		else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2)
+			target_slices = 2;
+		else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4)
+			target_slices = 4;
+		else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8)
+			target_slices = 8;
+		else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12)
+			target_slices = 12;
+		else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16)
+			target_slices = 16;
+		else
+			return 0;
+
+		slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices);
+		if (slice_width > max_slice_width)
+			min_slices = target_slices + 1;
+	} while (slice_width > max_slice_width);
+
+	return target_slices;
+}
+
+static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi,
+			   struct drm_crtc_state *crtc_state)
+{
+	int hdmi_throughput = hdmi->dsc_cap.clk_per_slice;
+	int hdmi_max_slices = hdmi->dsc_cap.max_slices;
+	int rk_max_slices = 8;
+	int rk_max_slice_width = 2048;
+
+	return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices,
+				       rk_max_slice_width,
+				       hdmi_max_slices, hdmi_throughput);
+}
+
+static int
+hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp,
+		 int slice_width, int num_slices, bool hdmi_all_bpp,
+		 int hdmi_max_chunk_bytes)
+{
+	int max_dsc_bpp, min_dsc_bpp;
+	int target_bytes;
+	bool bpp_found = false;
+	int bpp_decrement_x16;
+	int bpp_target;
+	int bpp_target_x16;
+
+	/*
+	 * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec
+	 * Start with the max bpp and keep on decrementing with
+	 * fractional bpp, if supported by PCON DSC encoder
+	 *
+	 * for each bpp we check if no of bytes can be supported by HDMI sink
+	 */
+
+	/* only 9\10\12 bpp was tested */
+	min_dsc_bpp = 9;
+	max_dsc_bpp = 12;
+
+	/*
+	 * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink
+	 * Section 7.7.34 : Source shall not enable compressed Video
+	 * Transport with bpp_target settings above 12 bpp unless
+	 * DSC_all_bpp is set to 1.
+	 */
+	if (!hdmi_all_bpp)
+		max_dsc_bpp = min(max_dsc_bpp, 12);
+
+	/*
+	 * The Sink has a limit of compressed data in bytes for a scanline,
+	 * as described in max_chunk_bytes field in HFVSDB block of edid.
+	 * The no. of bytes depend on the target bits per pixel that the
+	 * source configures. So we start with the max_bpp and calculate
+	 * the target_chunk_bytes. We keep on decrementing the target_bpp,
+	 * till we get the target_chunk_bytes just less than what the sink's
+	 * max_chunk_bytes, or else till we reach the min_dsc_bpp.
+	 *
+	 * The decrement is according to the fractional support from PCON DSC
+	 * encoder. For fractional BPP we use bpp_target as a multiple of 16.
+	 *
+	 * bpp_target_x16 = bpp_target * 16
+	 * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps
+	 * {1/16, 1/8, 1/4, 1/2, 1} respectively.
+	 */
+
+	bpp_target = max_dsc_bpp;
+
+	/* src does not support fractional bpp implies decrement by 16 for bppx16 */
+	if (!src_fractional_bpp)
+		src_fractional_bpp = 1;
+	bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp);
+	bpp_target_x16 = bpp_target * 16;
+
+	while (bpp_target_x16 > (min_dsc_bpp * 16)) {
+		int bpp;
+
+		bpp = DIV_ROUND_UP(bpp_target_x16, 16);
+		target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8);
+		if (target_bytes <= hdmi_max_chunk_bytes) {
+			bpp_found = true;
+			break;
+		}
+		bpp_target_x16 -= bpp_decrement_x16;
+	}
+	if (bpp_found)
+		return bpp_target_x16;
+
+	return 0;
+}
+
+static int
+dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi,
+		int num_slices, int slice_width)
+{
+	bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp;
+	int fractional_bpp = 0;
+	int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024;
+
+	return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width,
+				num_slices, hdmi_all_bpp,
+				hdmi_max_chunk_bytes);
+}
+
+static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi,
+				   u16 pic_width, u16 pic_height,
+				   u16 slice_width, u16 slice_height,
+				   u16 bits_per_pixel, u8 bits_per_component)
+{
+	int i;
+
+	for (i = 0; i < PPS_TABLE_LEN; i++)
+		if (pic_width == pps_datas[i].pic_width &&
+		    pic_height == pps_datas[i].pic_height &&
+		    slice_width == pps_datas[i].slice_width &&
+		    slice_height == pps_datas[i].slice_height &&
+		    bits_per_component == pps_datas[i].bpc &&
+		    bits_per_pixel == pps_datas[i].bpp &&
+		    hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb)
+			break;
+
+	if (i == PPS_TABLE_LEN) {
+		dev_err(hdmi->dev, "can't find pps cfg!\n");
+		return -EINVAL;
+	}
+
+	memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128);
+	hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) *
+		(pic_width / slice_width);
+
+	return 0;
+}
+
+static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi,
+				     struct rockchip_crtc_state *s,
+				     struct drm_crtc_state *crtc_state)
+{
+	int ret;
+	int slice_height;
+	int slice_width;
+	int bits_per_pixel;
+	int slice_count;
+	bool hdmi_is_dsc_1_2;
+	unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
+
+	if (!crtc_state)
+		return;
+
+	hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2;
+
+	if (!hdmi_is_dsc_1_2)
+		return;
+
+	slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay);
+	if (!slice_height)
+		return;
+
+	slice_count = hdmi_dsc_slices(hdmi, crtc_state);
+	if (!slice_count)
+		return;
+
+	slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count);
+
+	bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width);
+	if (!bits_per_pixel)
+		return;
+
+	ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay,
+				      crtc_state->mode.vdisplay, slice_width,
+				      slice_height, bits_per_pixel, depth);
+
+	if (ret) {
+		dev_err(hdmi->dev, "set vdsc cfg failed\n");
+		return;
+	}
+	dev_info(hdmi->dev, "dsc_enable\n");
+	s->dsc_enable = 1;
+	s->dsc_sink_cap.version_major = 1;
+	s->dsc_sink_cap.version_minor = 2;
+	s->dsc_sink_cap.slice_width = slice_width;
+	s->dsc_sink_cap.slice_height = slice_height;
+	s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel;
+	s->dsc_sink_cap.block_pred = 1;
+	s->dsc_sink_cap.native_420 = 0;
+
+	memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128);
+}
+/////////////////////////////////////////////////////////////////////////////////////////
 
 static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi,
 					  u32 *config,
@@ -484,6 +1228,150 @@
 	return 0;
 }
 
+static void repo_hpd_event(struct work_struct *p_work)
+{
+	struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work);
+	bool change;
+
+	change = drm_helper_hpd_irq_event(hdmi->drm_dev);
+	if (change) {
+		dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat);
+		dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change);
+	}
+}
+
+static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct rockchip_hdmi *hdmi = dev_id;
+	u32 intr_stat, val;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+	if (intr_stat) {
+		dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat);
+
+		if (!hdmi->id)
+			val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
+					    RK3588_HDMI0_HPD_INT_MSK);
+		else
+			val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK,
+					    RK3588_HDMI1_HPD_INT_MSK);
+		regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+		return IRQ_WAKE_THREAD;
+	}
+
+	return IRQ_NONE;
+}
+
+static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id)
+{
+	struct rockchip_hdmi *hdmi = dev_id;
+	u32 intr_stat, val;
+	int msecs;
+	bool stat;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+	if (!intr_stat)
+		return IRQ_NONE;
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR);
+		if (intr_stat & RK3588_HDMI0_LEVEL_INT)
+			stat = true;
+		else
+			stat = false;
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR);
+		if (intr_stat & RK3588_HDMI1_LEVEL_INT)
+			stat = true;
+		else
+			stat = false;
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+	if (stat) {
+		hdmi->hpd_stat = true;
+		msecs = 150;
+	} else {
+		hdmi->hpd_stat = false;
+		msecs = 20;
+	}
+	mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+	return IRQ_HANDLED;
+}
+
+static void init_hpd_work(struct rockchip_hdmi *hdmi)
+{
+	hdmi->workqueue = create_workqueue("hpd_queue");
+	INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
+}
+
+static irqreturn_t rockchip_hdmi_hpd_irq_handler(int irq, void *arg)
+{
+	u32 val;
+	struct rockchip_hdmi *hdmi = arg;
+
+	val = gpiod_get_value(hdmi->hpd_gpiod);
+	if (val) {
+		val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET);
+		if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod)
+			dw_hdmi_set_hpd_wake(hdmi->hdmi);
+	} else {
+		val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET);
+	}
+	regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val);
+
+	return IRQ_HANDLED;
+}
+
+static void dw_hdmi_rk3528_gpio_hpd_init(struct rockchip_hdmi *hdmi)
+{
+	u32 val;
+
+	if (hdmi->hpd_gpiod) {
+		/* gpio0_a2's input enable is controlled by gpio output data bit */
+		val = HIWORD_UPDATE(RK3528_GPIO0_A2_DR, RK3528_GPIO0_A2_DR);
+		writel(val, hdmi->gpio_base + RK3528_GPIO_SWPORT_DR_L);
+
+		val = HIWORD_UPDATE(RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK |
+				    RK3528_HDMI_SCLIN_MSK,
+				    RK3528_HDMI_SNKDET_SEL | RK3528_HDMI_SDAIN_MSK |
+				    RK3528_HDMI_SCLIN_MSK);
+	} else {
+		val = HIWORD_UPDATE(RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK,
+				    RK3528_HDMI_SDAIN_MSK | RK3528_HDMI_SCLIN_MSK);
+	}
+
+	regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val);
+
+	val = gpiod_get_value(hdmi->hpd_gpiod);
+	if (val) {
+		val = HIWORD_UPDATE(RK3528_HDMI_SNKDET, RK3528_HDMI_SNKDET);
+		if (hdmi->hdmi && hdmi->hpd_wake_en && hdmi->hpd_gpiod)
+			dw_hdmi_set_hpd_wake(hdmi->hdmi);
+	} else {
+		val = HIWORD_UPDATE(0, RK3528_HDMI_SNKDET);
+	}
+	regmap_write(hdmi->regmap, RK3528_VO_GRF_HDMI_MASK, val);
+}
+
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	int ret, val, phy_table_size;
@@ -494,6 +1382,14 @@
 	if (IS_ERR(hdmi->regmap)) {
 		DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
 		return PTR_ERR(hdmi->regmap);
+	}
+
+	if (hdmi->is_hdmi_qp) {
+		hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
+		if (IS_ERR(hdmi->vo1_regmap)) {
+			DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n");
+			return PTR_ERR(hdmi->vo1_regmap);
+		}
 	}
 
 	hdmi->phyref_clk = devm_clk_get(hdmi->dev, "vpll");
@@ -539,20 +1435,63 @@
 		return PTR_ERR(hdmi->hclk_vop);
 	}
 
-	ret = of_property_read_u32(np, "max-tmdsclk",
-				   &hdmi->max_tmdsclk);
-	if (ret != -EINVAL && ret < 0) {
-		DRM_DEV_ERROR(hdmi->dev, "incorrect max tmdsclk\n");
-		return ret;
-	} else if (ret == -EINVAL) {
-		DRM_DEV_DEBUG(hdmi->dev,
-			      "max tmdsclk is not set, set to 594M\n");
-		hdmi->max_tmdsclk = 594000;
+	hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud");
+	if (IS_ERR(hdmi->aud_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk),
+			      "failed to get aud_clk clock\n");
+		return PTR_ERR(hdmi->aud_clk);
 	}
-	hdmi->unsupported_yuv_input =
-		of_property_read_bool(np, "unsupported-yuv-input");
-	hdmi->unsupported_deep_color =
-		of_property_read_bool(np, "unsupported-deep-color");
+
+	hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd");
+	if (IS_ERR(hdmi->hpd_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk),
+			      "failed to get hpd_clk clock\n");
+		return PTR_ERR(hdmi->hpd_clk);
+	}
+
+	hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1");
+	if (IS_ERR(hdmi->hclk_vo1)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1),
+			      "failed to get hclk_vo1 clock\n");
+		return PTR_ERR(hdmi->hclk_vo1);
+	}
+
+	hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc");
+	if (IS_ERR(hdmi->earc_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk),
+			      "failed to get earc_clk clock\n");
+		return PTR_ERR(hdmi->earc_clk);
+	}
+
+	hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref");
+	if (IS_ERR(hdmi->hdmitx_ref)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref),
+			      "failed to get hdmitx_ref clock\n");
+		return PTR_ERR(hdmi->hdmitx_ref);
+	}
+
+	hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk),
+			      "failed to get pclk clock\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	hdmi->link_clk = devm_clk_get_optional(hdmi->dev, "link_clk");
+	if (IS_ERR(hdmi->link_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->link_clk),
+			      "failed to get link_clk clock\n");
+		return PTR_ERR(hdmi->link_clk);
+	}
+
+	hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
+						    GPIOD_OUT_HIGH);
+	if (IS_ERR(hdmi->enable_gpio)) {
+		ret = PTR_ERR(hdmi->enable_gpio);
+		dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret);
+		return ret;
+	}
+
 	hdmi->skip_check_420_mode =
 		of_property_read_bool(np, "skip-check-420-mode");
 
@@ -578,27 +1517,85 @@
 		dev_dbg(hdmi->dev, "use default hdmi phy table\n");
 	}
 
+	hdmi->hpd_gpiod = devm_gpiod_get_optional(hdmi->dev, "hpd", GPIOD_IN);
+
+	if (IS_ERR(hdmi->hpd_gpiod)) {
+		dev_err(hdmi->dev, "error getting HDP GPIO: %ld\n",
+			PTR_ERR(hdmi->hpd_gpiod));
+		return PTR_ERR(hdmi->hpd_gpiod);
+	}
+
+	if (hdmi->hpd_gpiod) {
+		struct resource *res;
+		struct platform_device *pdev = to_platform_device(hdmi->dev);
+
+		/* gpio interrupt reflects hpd status */
+		hdmi->hpd_irq = gpiod_to_irq(hdmi->hpd_gpiod);
+		if (hdmi->hpd_irq < 0)
+			return -EINVAL;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!res) {
+			DRM_DEV_ERROR(hdmi->dev, "failed to get gpio regs\n");
+			return -EINVAL;
+		}
+
+		hdmi->gpio_base = devm_ioremap(hdmi->dev, res->start, resource_size(res));
+		if (IS_ERR(hdmi->gpio_base)) {
+			DRM_DEV_ERROR(hdmi->dev, "Unable to get gpio ioregmap\n");
+			return PTR_ERR(hdmi->gpio_base);
+		}
+
+		dw_hdmi_rk3528_gpio_hpd_init(hdmi);
+		ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq, NULL,
+						rockchip_hdmi_hpd_irq_handler,
+						IRQF_TRIGGER_RISING |
+						IRQF_TRIGGER_FALLING |
+						IRQF_ONESHOT,
+						"hdmi-hpd", hdmi);
+		if (ret) {
+			dev_err(hdmi->dev, "failed to request hpd IRQ: %d\n", ret);
+			return ret;
+		}
+
+		hdmi->hpd_wake_en = device_property_read_bool(hdmi->dev, "hpd-wake-up");
+		if (hdmi->hpd_wake_en)
+			enable_irq_wake(hdmi->hpd_irq);
+	}
+
+	hdmi->p = devm_pinctrl_get(hdmi->dev);
+	if (IS_ERR(hdmi->p)) {
+		dev_err(hdmi->dev, "could not get pinctrl\n");
+		return PTR_ERR(hdmi->p);
+	}
+
+	hdmi->idle_state = pinctrl_lookup_state(hdmi->p, "idle");
+	if (IS_ERR(hdmi->idle_state)) {
+		dev_dbg(hdmi->dev, "idle state is not defined\n");
+		return 0;
+	}
+
+	hdmi->default_state = pinctrl_lookup_state(hdmi->p, "default");
+	if (IS_ERR(hdmi->default_state)) {
+		dev_err(hdmi->dev, "could not find default state\n");
+		return PTR_ERR(hdmi->default_state);
+	}
+
 	return 0;
 }
 
 static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
+dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
+			    const struct drm_display_info *info,
 			    const struct drm_display_mode *mode)
 {
+	struct drm_connector *connector = container_of(info, struct drm_connector, display_info);
 	struct drm_encoder *encoder = connector->encoder;
 	enum drm_mode_status status = MODE_OK;
 	struct drm_device *dev = connector->dev;
 	struct rockchip_drm_private *priv = dev->dev_private;
 	struct drm_crtc *crtc;
 	struct rockchip_hdmi *hdmi;
-
-	/*
-	 * Pixel clocks we support are always < 2GHz and so fit in an
-	 * int.  We should make sure source rate does too so we don't get
-	 * overflow when we multiply by 1000.
-	 */
-	if (mode->clock > INT_MAX / 1000)
-		return MODE_BAD;
 
 	if (!encoder) {
 		const struct drm_connector_helper_funcs *funcs;
@@ -615,6 +1612,21 @@
 		return MODE_BAD;
 
 	hdmi = to_rockchip_hdmi(encoder);
+
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->enable_gpio && mode->clock > 600000)
+			return MODE_BAD;
+
+		return MODE_OK;
+	}
+
+	/*
+	 * Pixel clocks we support are always < 2GHz and so fit in an
+	 * int.  We should make sure source rate does too so we don't get
+	 * overflow when we multiply by 1000.
+	 */
+	if (mode->clock > INT_MAX / 1000)
+		return MODE_BAD;
 
 	/*
 	 * If sink max TMDS clock < 340MHz, we should check the mode pixel
@@ -633,8 +1645,12 @@
 			return MODE_BAD;
 	};
 
-	if (hdmi->phy)
-		phy_set_bus_width(hdmi->phy, 8);
+	if (hdmi->phy) {
+		if (hdmi->is_hdmi_qp)
+			phy_set_bus_width(hdmi->phy, mode->clock * 10);
+		else
+			phy_set_bus_width(hdmi->phy, 8);
+	}
 
 	/*
 	 * ensure all drm display mode can work, if someone want support more
@@ -660,18 +1676,29 @@
 	return status;
 }
 
-static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
-	.destroy = drm_encoder_cleanup,
-};
-
 static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 	struct drm_crtc *crtc = encoder->crtc;
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+	struct rockchip_crtc_state *s;
 
-	if (!hdmi->mode_changed)
-		s->output_if &= ~VOP_OUTPUT_IF_HDMI0;
+	if (!crtc || !crtc->state) {
+		dev_info(hdmi->dev, "%s old crtc state is null\n", __func__);
+		return;
+	}
+
+	s = to_rockchip_crtc_state(crtc->state);
+
+	if (crtc->state->active_changed) {
+		if (hdmi->plat_data->split_mode) {
+			s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1);
+		} else {
+			if (!hdmi->id)
+				s->output_if &= ~VOP_OUTPUT_IF_HDMI0;
+			else
+				s->output_if &= ~VOP_OUTPUT_IF_HDMI1;
+		}
+	}
 	/*
 	 * when plug out hdmi it will be switch cvbs and then phy bus width
 	 * must be set as 8
@@ -688,14 +1715,23 @@
 	int mux;
 	int ret;
 
-	if (WARN_ON(!crtc || !crtc->state))
+	if (!crtc || !crtc->state) {
+		dev_info(hdmi->dev, "%s old crtc state is null\n", __func__);
 		return;
+	}
 
 	if (hdmi->phy)
 		phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
 
 	clk_set_rate(hdmi->phyref_clk,
 		     crtc->state->adjusted_mode.crtc_clock * 1000);
+
+	if (hdmi->is_hdmi_qp) {
+		if (hdmi->link_cfg.frl_mode)
+			gpiod_set_value(hdmi->enable_gpio, 0);
+		else
+			gpiod_set_value(hdmi->enable_gpio, 1);
+	}
 
 	if (hdmi->chip_data->lcdsel_grf_reg < 0)
 		return;
@@ -735,6 +1771,205 @@
 		      ret ? "LIT" : "BIG");
 }
 
+static int _dw_hdmi_rockchip_encoder_loader_protect(struct rockchip_hdmi *hdmi, bool on)
+{
+	int ret;
+
+	if (on) {
+		if (hdmi->is_hdmi_qp) {
+			ret = clk_prepare_enable(hdmi->link_clk);
+			if (ret < 0) {
+				DRM_DEV_ERROR(hdmi->dev, "failed to enable link_clk %d\n", ret);
+				return ret;
+			}
+		}
+
+		hdmi->phy->power_count++;
+	} else {
+		clk_disable_unprepare(hdmi->link_clk);
+		hdmi->phy->power_count--;
+	}
+
+	return 0;
+}
+
+static int dw_hdmi_rockchip_encoder_loader_protect(struct drm_encoder *encoder, bool on)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	struct rockchip_hdmi *secondary;
+
+	_dw_hdmi_rockchip_encoder_loader_protect(hdmi, on);
+	if (hdmi->plat_data->right) {
+		secondary = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id);
+		_dw_hdmi_rockchip_encoder_loader_protect(secondary, on);
+	}
+
+	return 0;
+}
+
+static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi)
+{
+	int val;
+	bool is_hdmi0;
+
+	if (!hdmi->id)
+		is_hdmi0 = true;
+	else
+		is_hdmi0 = false;
+
+	if (!hdmi->link_cfg.frl_mode) {
+		val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+
+		val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+		return;
+	}
+
+	val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK);
+	if (is_hdmi0)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+
+	if (hdmi->link_cfg.dsc_mode) {
+		val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA,
+				    RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+	} else {
+		val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+	}
+}
+
+static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format,
+				    u32 depth)
+{
+	u32 val = 0;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+		val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK);
+		break;
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK);
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK);
+		break;
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+		val = HIWORD_UPDATE(RK3588_YUV422, RK3588_COLOR_FORMAT_MASK);
+		break;
+	default:
+		dev_err(hdmi->dev, "can't set correct color format\n");
+		return;
+	}
+
+	if (hdmi->link_cfg.dsc_mode)
+		val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK);
+
+	if (depth == 8 || bus_format == MEDIA_BUS_FMT_YUYV10_1X20)
+		val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK);
+	else
+		val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK);
+
+	if (!hdmi->id)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+}
+
+static void rk3588_set_hdcp_status(void *data, u8 status)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	hdmi->hdcp_status = status;
+}
+
+static void rk3588_set_hdcp2_enable(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	if (enable)
+		val = HIWORD_UPDATE(HDCP1_P1_GPIO_IN, HDCP1_P1_GPIO_IN);
+	else
+		val = HIWORD_UPDATE(0, HDCP1_P1_GPIO_IN);
+
+	regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON1, val);
+}
+
+static void rk3588_set_grf_cfg(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	int color_depth;
+
+	rk3588_set_link_mode(hdmi);
+	color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format);
+	rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth);
+}
+
+static u64 rk3588_get_grf_color_fmt(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val, depth;
+	u64 bus_format;
+
+	if (!hdmi->id)
+		regmap_read(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, &val);
+	else
+		regmap_read(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, &val);
+
+	depth = (val & RK3588_COLOR_DEPTH_MASK) >> 4;
+
+	switch (val & RK3588_COLOR_FORMAT_MASK) {
+	case RK3588_YUV444:
+		if (!depth)
+			bus_format = MEDIA_BUS_FMT_YUV8_1X24;
+		else
+			bus_format = MEDIA_BUS_FMT_YUV10_1X30;
+		break;
+	case RK3588_YUV422:
+		bus_format = MEDIA_BUS_FMT_YUYV10_1X20;
+		break;
+	case RK3588_YUV420:
+		if (!depth)
+			bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+		else
+			bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+		break;
+	case RK3588_RGB:
+		if (!depth)
+			bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+		else
+			bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+		break;
+	default:
+		dev_err(hdmi->dev, "can't get correct color format\n");
+		bus_format = MEDIA_BUS_FMT_YUV8_1X24;
+		break;
+	}
+
+	return bus_format;
+}
+
 static void
 dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
 			       struct drm_crtc_state *crtc_state,
@@ -747,65 +1982,81 @@
 			       unsigned int *eotf)
 {
 	struct drm_display_info *info = &conn_state->connector->display_info;
-	struct drm_display_mode *mode = &crtc_state->mode;
+	struct drm_display_mode mode = {};
 	struct hdr_output_metadata *hdr_metadata;
-	u32 vic = drm_match_cea_mode(mode);
-	unsigned long tmdsclock, pixclock = mode->crtc_clock;
+	u32 vic;
+	unsigned long tmdsclock, pixclock;
 	unsigned int color_depth;
 	bool support_dc = false;
-	bool sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi);
-	int max_tmds_clock = info->max_tmds_clock;
+	bool sink_is_hdmi = true;
+	bool yuv422_out = false;
+	u32 max_tmds_clock = info->max_tmds_clock;
 	int output_eotf;
 
-	*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+	drm_mode_copy(&mode, &crtc_state->mode);
+	pixclock = mode.crtc_clock;
+	if (hdmi->plat_data->split_mode) {
+		drm_mode_convert_to_origin_mode(&mode);
+		pixclock /= 2;
+	}
+
+	vic = drm_match_cea_mode(&mode);
+
+	if (!hdmi->is_hdmi_qp)
+		sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi);
+	else
+		sink_is_hdmi = dw_hdmi_qp_get_output_whether_hdmi(hdmi->hdmi_qp);
+
+	*color_format = RK_IF_FORMAT_RGB;
 
 	switch (hdmi->hdmi_output) {
-	case DRM_HDMI_OUTPUT_YCBCR_HQ:
+	case RK_IF_FORMAT_YCBCR_HQ:
 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+			*color_format = RK_IF_FORMAT_YCBCR444;
 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+			*color_format = RK_IF_FORMAT_YCBCR422;
 		else if (conn_state->connector->ycbcr_420_allowed &&
-			 drm_mode_is_420(info, mode) && pixclock >= 594000)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+			 drm_mode_is_420(info, &mode) &&
+			 (pixclock >= 594000 && !hdmi->is_hdmi_qp))
+			*color_format = RK_IF_FORMAT_YCBCR420;
 		break;
-	case DRM_HDMI_OUTPUT_YCBCR_LQ:
+	case RK_IF_FORMAT_YCBCR_LQ:
 		if (conn_state->connector->ycbcr_420_allowed &&
-		    drm_mode_is_420(info, mode) && pixclock >= 594000)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		    drm_mode_is_420(info, &mode) && pixclock >= 594000)
+			*color_format = RK_IF_FORMAT_YCBCR420;
 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+			*color_format = RK_IF_FORMAT_YCBCR422;
 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+			*color_format = RK_IF_FORMAT_YCBCR444;
 		break;
-	case DRM_HDMI_OUTPUT_YCBCR420:
+	case RK_IF_FORMAT_YCBCR420:
 		if (conn_state->connector->ycbcr_420_allowed &&
-		    drm_mode_is_420(info, mode) && pixclock >= 594000)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		    drm_mode_is_420(info, &mode) && pixclock >= 594000)
+			*color_format = RK_IF_FORMAT_YCBCR420;
 		break;
-	case DRM_HDMI_OUTPUT_YCBCR422:
+	case RK_IF_FORMAT_YCBCR422:
 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+			*color_format = RK_IF_FORMAT_YCBCR422;
 		break;
-	case DRM_HDMI_OUTPUT_YCBCR444:
+	case RK_IF_FORMAT_YCBCR444:
 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+			*color_format = RK_IF_FORMAT_YCBCR444;
 		break;
-	case DRM_HDMI_OUTPUT_DEFAULT_RGB:
+	case RK_IF_FORMAT_RGB:
 	default:
 		break;
 	}
 
-	if (*color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB &&
+	if (*color_format == RK_IF_FORMAT_RGB &&
 	    info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
 		support_dc = true;
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR444 &&
+	if (*color_format == RK_IF_FORMAT_YCBCR444 &&
 	    info->edid_hdmi_dc_modes &
 	    (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30))
 		support_dc = true;
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR422)
+	if (*color_format == RK_IF_FORMAT_YCBCR422)
 		support_dc = true;
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420 &&
+	if (*color_format == RK_IF_FORMAT_YCBCR420 &&
 	    info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
 		support_dc = true;
 
@@ -814,53 +2065,66 @@
 	else
 		color_depth = 8;
 
-	if (!sink_is_hdmi) {
-		*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
-		color_depth = 8;
-	}
-
-	*eotf = TRADITIONAL_GAMMA_SDR;
+	*eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
 	if (conn_state->hdr_output_metadata) {
 		hdr_metadata = (struct hdr_output_metadata *)
 			conn_state->hdr_output_metadata->data;
 		output_eotf = hdr_metadata->hdmi_metadata_type1.eotf;
-		if (output_eotf > TRADITIONAL_GAMMA_HDR &&
-		    output_eotf < FUTURE_EOTF)
+		if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR &&
+		    output_eotf <= HDMI_EOTF_BT_2100_HLG)
 			*eotf = output_eotf;
 	}
 
-	if ((*eotf > TRADITIONAL_GAMMA_HDR &&
-	     conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf &
-	     BIT(*eotf)) || (hdmi->colorimetry ==
-	     RK_HDMI_COLORIMETRY_BT2020 && info->hdmi.colorimetry &
-	     (BIT(6) | BIT(7))))
-		*enc_out_encoding = V4L2_YCBCR_ENC_BT2020;
-	else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
-		 (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18))
-		*enc_out_encoding = V4L2_YCBCR_ENC_601;
-	else
-		*enc_out_encoding = V4L2_YCBCR_ENC_709;
+	hdmi->colorimetry = conn_state->colorspace;
 
-	if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
-		/* BT2020 require color depth at lest 10bit */
-		color_depth = 10;
-		/* We prefer use YCbCr422 to send 10bit */
-		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
-			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+	/* bt2020 sdr/hdr output */
+	if ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) &&
+	    (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC) &&
+	    hdmi->edid_colorimetry & (BIT(6) | BIT(7))) {
+		*enc_out_encoding = V4L2_YCBCR_ENC_BT2020;
+		yuv422_out = true;
+	/* bt709 hdr output */
+	} else if ((hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_CYCC) &&
+		   (hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_YCC) &&
+		   (conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf & BIT(*eotf) &&
+		    *eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR)) {
+		*enc_out_encoding = V4L2_YCBCR_ENC_709;
+		yuv422_out = true;
+	} else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
+		   (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) {
+		*enc_out_encoding = V4L2_YCBCR_ENC_601;
+	} else {
+		*enc_out_encoding = V4L2_YCBCR_ENC_709;
 	}
 
-	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+	if ((yuv422_out || hdmi->hdmi_output == RK_IF_FORMAT_YCBCR_HQ) && color_depth == 10 &&
+	    (hdmi_bus_fmt_color_depth(hdmi->prev_bus_format) == 8 ||
+	     hdmi_bus_fmt_to_color_format(hdmi->prev_bus_format) == RK_IF_FORMAT_YCBCR422)) {
+		/* We prefer use YCbCr422 to send hdr 10bit */
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*color_format = RK_IF_FORMAT_YCBCR422;
+	}
+
+	if (mode.flags & DRM_MODE_FLAG_DBLCLK)
 		pixclock *= 2;
-	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+	if ((mode.flags & DRM_MODE_FLAG_3D_MASK) ==
 		DRM_MODE_FLAG_3D_FRAME_PACKING)
 		pixclock *= 2;
 
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8)
+	if (hdmi->is_hdmi_qp && mode.clock >= 600000)
+		*color_format = RK_IF_FORMAT_YCBCR420;
+
+	if (!sink_is_hdmi) {
+		*color_format = RK_IF_FORMAT_RGB;
+		color_depth = 8;
+	}
+
+	if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8)
 		tmdsclock = pixclock;
 	else
 		tmdsclock = pixclock * (color_depth) / 8;
 
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420)
+	if (*color_format == RK_IF_FORMAT_YCBCR420)
 		tmdsclock /= 2;
 
 	/* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */
@@ -869,67 +2133,76 @@
 
 	max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk);
 
+	if (hdmi->is_hdmi_qp && hdmi->link_cfg.rate_per_lane && mode.clock > 600000)
+		max_tmds_clock =
+			hdmi->link_cfg.frl_lanes * hdmi->link_cfg.rate_per_lane * 1000000;
+
 	if (tmdsclock > max_tmds_clock) {
 		if (max_tmds_clock >= 594000) {
 			color_depth = 8;
 		} else if (max_tmds_clock > 340000) {
-			if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
-				*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+			if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000)
+				*color_format = RK_IF_FORMAT_YCBCR420;
 		} else {
 			color_depth = 8;
-			if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
-				*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+			if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000)
+				*color_format = RK_IF_FORMAT_YCBCR420;
 		}
 	}
 
-	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420) {
+	if (*color_format == RK_IF_FORMAT_YCBCR420) {
 		*output_mode = ROCKCHIP_OUT_MODE_YUV420;
 		if (color_depth > 8)
 			*bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
 		else
 			*bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
 		*bus_width = color_depth / 2;
-		hdmi->output_bus_format = *bus_format;
 	} else {
 		*output_mode = ROCKCHIP_OUT_MODE_AAAA;
-
 		if (color_depth > 8) {
-			if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB)
-				hdmi->output_bus_format = MEDIA_BUS_FMT_YUV10_1X30;
-			else
-				hdmi->output_bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
-
-			if (!hdmi->unsupported_yuv_input)
-				*bus_format = hdmi->output_bus_format;
+			if (*color_format != RK_IF_FORMAT_RGB &&
+			    !hdmi->unsupported_yuv_input)
+				*bus_format = MEDIA_BUS_FMT_YUV10_1X30;
 			else
 				*bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
 		} else {
-			if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB)
-				hdmi->output_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
-			else
-				hdmi->output_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-
-			if (!hdmi->unsupported_yuv_input)
-				*bus_format = hdmi->output_bus_format;
+			if (*color_format != RK_IF_FORMAT_RGB &&
+			    !hdmi->unsupported_yuv_input)
+				*bus_format = MEDIA_BUS_FMT_YUV8_1X24;
 			else
 				*bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 		}
-
-		if (*color_format == DRM_HDMI_OUTPUT_YCBCR422) {
+		if (*color_format == RK_IF_FORMAT_YCBCR422)
 			*bus_width = 8;
+		else
+			*bus_width = color_depth;
+	}
 
+	hdmi->bus_format = *bus_format;
+
+	if (*color_format == RK_IF_FORMAT_YCBCR422) {
+		if (hdmi->is_hdmi_qp) {
+			if (color_depth == 12)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV12_1X24;
+			else if (color_depth == 10)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV10_1X20;
+			else
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV8_1X16;
+
+			*bus_format = hdmi->output_bus_format;
+			hdmi->bus_format = *bus_format;
+			*output_mode = ROCKCHIP_OUT_MODE_YUV422;
+		} else {
 			if (color_depth == 12)
 				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
 			else if (color_depth == 10)
 				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
 			else
 				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
-		} else {
-			*bus_width = color_depth;
 		}
+	} else {
+		hdmi->output_bus_format = *bus_format;
 	}
-
-	hdmi->bus_format = *bus_format;
 }
 
 static bool
@@ -963,49 +2236,101 @@
 {
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
-	unsigned int colorformat, bus_width;
+	unsigned int colorformat, bus_width, tmdsclk;
+	struct drm_display_mode mode = {};
 	unsigned int output_mode;
 	unsigned long bus_format;
+	int color_depth;
+	bool secondary = false;
+
+	/*
+	 * There are two hdmi but only one encoder in split mode,
+	 * so we need to check twice.
+	 */
+secondary:
+	drm_mode_copy(&mode, &crtc_state->mode);
+
+	if (hdmi->plat_data->split_mode)
+		drm_mode_convert_to_origin_mode(&mode);
 
 	dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
 				       &colorformat,
 				       &output_mode, &bus_format, &bus_width,
 				       &hdmi->enc_out_encoding, &s->eotf);
 
+	s->bus_format = bus_format;
+	if (hdmi->is_hdmi_qp) {
+		color_depth = hdmi_bus_fmt_color_depth(bus_format);
+		tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock);
+		if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
+			tmdsclk /= 2;
+		hdmi_select_link_config(hdmi, crtc_state, tmdsclk);
+
+		if (hdmi->link_cfg.frl_mode) {
+			/* in the current version, support max 40G frl */
+			if (hdmi->link_cfg.rate_per_lane >= 10) {
+				hdmi->link_cfg.frl_lanes = 4;
+				hdmi->link_cfg.rate_per_lane = 10;
+			}
+			bus_width = hdmi->link_cfg.frl_lanes *
+				hdmi->link_cfg.rate_per_lane * 1000000;
+			/* 10 bit color depth and frl mode */
+			if (color_depth == 10)
+				bus_width |=
+					COLOR_DEPTH_10BIT | HDMI_FRL_MODE;
+			else
+				bus_width |= HDMI_FRL_MODE;
+		} else {
+			bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10);
+			if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
+				bus_width /= 2;
+
+			if (color_depth == 10 && !hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format))
+				bus_width |= COLOR_DEPTH_10BIT;
+		}
+	}
+
 	hdmi->phy_bus_width = bus_width;
+
 	if (hdmi->phy)
 		phy_set_bus_width(hdmi->phy, bus_width);
 
 	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
 	s->tv_state = &conn_state->tv;
-	s->output_if |= VOP_OUTPUT_IF_HDMI0;
+
+	if (hdmi->plat_data->split_mode) {
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
+		if (hdmi->plat_data->right && hdmi->id)
+			s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
+		s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1;
+	} else {
+		if (!hdmi->id)
+			s->output_if |= VOP_OUTPUT_IF_HDMI0;
+		else
+			s->output_if |= VOP_OUTPUT_IF_HDMI1;
+	}
 
 	s->output_mode = output_mode;
-	s->bus_format = bus_format;
 	hdmi->bus_format = s->bus_format;
-
-	hdmi->mode_changed = crtc_state->mode_changed;
 
 	if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
 		s->color_space = V4L2_COLORSPACE_BT2020;
-	else if (colorformat == DRM_HDMI_OUTPUT_DEFAULT_RGB)
+	else if (colorformat == RK_IF_FORMAT_RGB)
 		s->color_space = V4L2_COLORSPACE_DEFAULT;
 	else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709)
 		s->color_space = V4L2_COLORSPACE_REC709;
 	else
 		s->color_space = V4L2_COLORSPACE_SMPTE170M;
 
+	if (hdmi->plat_data->split_mode && !secondary) {
+		hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id);
+		secondary = true;
+		goto secondary;
+	}
+
 	return 0;
 }
 
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
-					      struct drm_display_mode *mode,
-					      struct drm_display_mode *adj)
-{
-	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
-
-	clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000);
-}
 
 static unsigned long
 dw_hdmi_rockchip_get_input_bus_format(void *data)
@@ -1063,6 +2388,14 @@
 	return hdmi->hdr_panel_blob_ptr;
 }
 
+static void dw_hdmi_rockchip_update_color_format(struct drm_connector_state *conn_state,
+						 void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	dw_hdmi_rockchip_check_color(conn_state, hdmi);
+}
+
 static bool
 dw_hdmi_rockchip_get_color_changed(void *data)
 {
@@ -1076,6 +2409,198 @@
 	return ret;
 }
 
+static int
+dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector,
+				   struct edid *edid)
+{
+	if (!connector || !edid)
+		return -EINVAL;
+
+	return rockchip_drm_get_yuv422_format(connector, edid);
+}
+
+static int
+dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!edid)
+		return -EINVAL;
+
+	memset(&hdmi->dsc_cap, 0, sizeof(hdmi->dsc_cap));
+	hdmi->max_frl_rate_per_lane = 0;
+	hdmi->max_lanes = 0;
+	hdmi->add_func = 0;
+
+	return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap,
+					  &hdmi->max_frl_rate_per_lane,
+					  &hdmi->max_lanes, &hdmi->add_func, edid);
+}
+
+static int
+dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid,
+				   struct drm_connector *connector)
+{
+	int ret;
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data;
+	size_t size = sizeof(*sink_data);
+	struct drm_property *property = hdmi->next_hdr_sink_data_property;
+	struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr;
+
+	if (!edid)
+		return -EINVAL;
+
+	rockchip_drm_parse_next_hdr(sink_data, edid);
+
+	ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data,
+					       &connector->base, property);
+
+	return ret;
+};
+
+static int dw_hdmi_rockchip_get_colorimetry(void *data, struct edid *edid)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return rockchip_drm_parse_colorimetry_data_block(&hdmi->edid_colorimetry, edid);
+}
+
+static
+struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return &hdmi->link_cfg;
+}
+
+static int dw_hdmi_rockchip_get_vp_id(struct drm_crtc_state *crtc_state)
+{
+	struct rockchip_crtc_state *s;
+
+	s = to_rockchip_crtc_state(crtc_state);
+
+	return s->vp_id;
+}
+
+static int dw_hdmi_dclk_set(void *data, bool enable, int vp_id)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	char clk_name[16];
+	struct clk *dclk;
+	int ret;
+
+	snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp_id);
+
+	dclk = devm_clk_get_optional(hdmi->dev, clk_name);
+	if (IS_ERR(dclk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name);
+		return PTR_ERR(dclk);
+	} else if (!dclk) {
+		if (hdmi->is_hdmi_qp) {
+			DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name);
+			return -ENOENT;
+		}
+
+		return 0;
+	}
+
+	if (enable) {
+		ret = clk_prepare_enable(dclk);
+		if (ret < 0)
+			DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n",
+				      vp_id, ret);
+	} else {
+		clk_disable_unprepare(dclk);
+	}
+
+	return 0;
+}
+
+static int dw_hdmi_link_clk_set(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u64 phy_clk = hdmi->phy_bus_width;
+	int ret;
+
+	if (enable) {
+		ret = clk_prepare_enable(hdmi->link_clk);
+		if (ret < 0) {
+			DRM_DEV_ERROR(hdmi->dev, "failed to enable link_clk %d\n", ret);
+			return ret;
+		}
+
+		if (((phy_clk & DATA_RATE_MASK) <= 6000000) &&
+		    (phy_clk & COLOR_DEPTH_10BIT))
+			phy_clk = (phy_clk & DATA_RATE_MASK) * 10 * 8;
+		else
+			phy_clk = (phy_clk & DATA_RATE_MASK) * 100;
+
+		/*
+		 * To be compatible with vop dclk usage scenarios, hdmi phy pll clk
+		 * is set according to dclk rate.
+		 * But phy pll actual frequency will varies according to the color depth.
+		 * So we should get the actual frequency or clk_set_rate may not change
+		 * pll frequency when 8/10 bit switch.
+		 */
+		clk_get_rate(hdmi->link_clk);
+		clk_set_rate(hdmi->link_clk, phy_clk);
+	} else {
+		clk_disable_unprepare(hdmi->link_clk);
+	}
+	return 0;
+}
+
+static bool
+dw_hdmi_rockchip_check_hdr_color_change(struct drm_connector_state *conn_state,
+					void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!conn_state || !data)
+		return false;
+
+	if (dw_hdmi_rockchip_check_color(conn_state, hdmi))
+		return true;
+
+	return false;
+}
+
+static void dw_hdmi_rockchip_set_prev_bus_format(void *data, unsigned long bus_format)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	hdmi->prev_bus_format = bus_format;
+}
+
+static void dw_hdmi_rockchip_set_ddc_io(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!hdmi->p || !hdmi->idle_state || !hdmi->default_state)
+		return;
+
+	if (!enable) {
+		if (pinctrl_select_state(hdmi->p, hdmi->idle_state))
+			dev_err(hdmi->dev, "could not select idle state\n");
+	} else {
+		if (pinctrl_select_state(hdmi->p, hdmi->default_state))
+			dev_err(hdmi->dev, "could not select default state\n");
+	}
+}
+
+static void dw_hdmi_rockchip_set_hdcp14_mem(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	val = HIWORD_UPDATE(enable << 15, RK3588_HDMI_HDCP14_MEM_EN);
+	if (!hdmi->id)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+}
+
 static const struct drm_prop_enum_list color_depth_enum_list[] = {
 	{ 0, "Automatic" }, /* Prefer highest color depth */
 	{ 8, "24bit" },
@@ -1083,24 +2608,19 @@
 };
 
 static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = {
-	{ DRM_HDMI_OUTPUT_DEFAULT_RGB, "output_rgb" },
-	{ DRM_HDMI_OUTPUT_YCBCR444, "output_ycbcr444" },
-	{ DRM_HDMI_OUTPUT_YCBCR422, "output_ycbcr422" },
-	{ DRM_HDMI_OUTPUT_YCBCR420, "output_ycbcr420" },
-	{ DRM_HDMI_OUTPUT_YCBCR_HQ, "output_ycbcr_high_subsampling" },
-	{ DRM_HDMI_OUTPUT_YCBCR_LQ, "output_ycbcr_low_subsampling" },
-	{ DRM_HDMI_OUTPUT_INVALID, "invalid_output" },
+	{ RK_IF_FORMAT_RGB, "rgb" },
+	{ RK_IF_FORMAT_YCBCR444, "ycbcr444" },
+	{ RK_IF_FORMAT_YCBCR422, "ycbcr422" },
+	{ RK_IF_FORMAT_YCBCR420, "ycbcr420" },
+	{ RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" },
+	{ RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" },
+	{ RK_IF_FORMAT_MAX, "invalid_output" },
 };
 
 static const struct drm_prop_enum_list quant_range_enum_list[] = {
 	{ HDMI_QUANTIZATION_RANGE_DEFAULT, "default" },
 	{ HDMI_QUANTIZATION_RANGE_LIMITED, "limit" },
 	{ HDMI_QUANTIZATION_RANGE_FULL, "full" },
-};
-
-static const struct drm_prop_enum_list colorimetry_enum_list[] = {
-	{ HDMI_COLORIMETRY_NONE, "None" },
-	{ RK_HDMI_COLORIMETRY_BT2020, "ITU_2020" },
 };
 
 static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = {
@@ -1114,58 +2634,76 @@
 	{ 1, "HDMI" },
 };
 
+static const struct drm_prop_enum_list allm_enable_list[] = {
+	{ 0, "disable" },
+	{ 1, "enable" },
+};
+
 static void
 dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
 				   unsigned int color, int version,
-				   void *data)
+				   void *data, bool allm_en)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 	struct drm_property *prop;
 	struct rockchip_drm_private *private = connector->dev->dev_private;
+	int ret;
 
 	switch (color) {
 	case MEDIA_BUS_FMT_RGB101010_1X30:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+		hdmi->hdmi_output = RK_IF_FORMAT_RGB;
 		hdmi->colordepth = 10;
 		break;
 	case MEDIA_BUS_FMT_YUV8_1X24:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444;
 		hdmi->colordepth = 8;
 		break;
 	case MEDIA_BUS_FMT_YUV10_1X30:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444;
 		hdmi->colordepth = 10;
 		break;
 	case MEDIA_BUS_FMT_UYVY10_1X20:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422;
 		hdmi->colordepth = 10;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422;
 		hdmi->colordepth = 8;
 		break;
 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420;
 		hdmi->colordepth = 8;
 		break;
 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+		hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420;
 		hdmi->colordepth = 10;
 		break;
 	default:
-		hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+		hdmi->hdmi_output = RK_IF_FORMAT_RGB;
 		hdmi->colordepth = 8;
 	}
 
 	hdmi->bus_format = color;
+	hdmi->prev_bus_format = color;
 
-	if (hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR422) {
-		if (hdmi->colordepth == 12)
-			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
-		else if (hdmi->colordepth == 10)
-			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
-		else
-			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
+	if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) {
+		if (hdmi->is_hdmi_qp) {
+			if (hdmi->colordepth == 12)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV12_1X24;
+			else if (hdmi->colordepth == 10)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV10_1X20;
+			else
+				hdmi->output_bus_format = MEDIA_BUS_FMT_YUYV8_1X16;
+		} else {
+			if (hdmi->colordepth == 12)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
+			else if (hdmi->colordepth == 10)
+				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
+			else
+				hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
+		}
 	} else {
 		hdmi->output_bus_format = hdmi->bus_format;
 	}
@@ -1173,7 +2711,7 @@
 	/* RK3368 does not support deep color mode */
 	if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) {
 		prop = drm_property_create_enum(connector->dev, 0,
-						"hdmi_output_depth",
+						RK_IF_PROP_COLOR_DEPTH,
 						color_depth_enum_list,
 						ARRAY_SIZE(color_depth_enum_list));
 		if (prop) {
@@ -1182,7 +2720,7 @@
 		}
 	}
 
-	prop = drm_property_create_enum(connector->dev, 0, "hdmi_output_format",
+	prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT,
 					drm_hdmi_output_enum_list,
 					ARRAY_SIZE(drm_hdmi_output_enum_list));
 	if (prop) {
@@ -1190,17 +2728,8 @@
 		drm_object_attach_property(&connector->base, prop, 0);
 	}
 
-	prop = drm_property_create_enum(connector->dev, 0,
-					"hdmi_output_colorimetry",
-					colorimetry_enum_list,
-					ARRAY_SIZE(colorimetry_enum_list));
-	if (prop) {
-		hdmi->colorimetry_property = prop;
-		drm_object_attach_property(&connector->base, prop, 0);
-	}
-
 	prop = drm_property_create_range(connector->dev, 0,
-					 "hdmi_color_depth_capacity",
+					 RK_IF_PROP_COLOR_DEPTH_CAPS,
 					 0, 0xff);
 	if (prop) {
 		hdmi->colordepth_capacity = prop;
@@ -1208,22 +2737,12 @@
 	}
 
 	prop = drm_property_create_range(connector->dev, 0,
-					 "hdmi_output_mode_capacity",
+					 RK_IF_PROP_COLOR_FORMAT_CAPS,
 					 0, 0xf);
 	if (prop) {
 		hdmi->outputmode_capacity = prop;
 		drm_object_attach_property(&connector->base, prop, 0);
 	}
-
-	prop = drm_property_create_enum(connector->dev, 0,
-					"hdmi_quant_range",
-					quant_range_enum_list,
-					ARRAY_SIZE(quant_range_enum_list));
-	if (prop) {
-		hdmi->quant_range = prop;
-		drm_object_attach_property(&connector->base, prop, 0);
-	}
-
 
 	prop = drm_property_create(connector->dev,
 				   DRM_MODE_PROP_BLOB |
@@ -1232,6 +2751,34 @@
 	if (prop) {
 		hdmi->hdr_panel_metadata_property = prop;
 		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create(connector->dev,
+				   DRM_MODE_PROP_BLOB |
+				   DRM_MODE_PROP_IMMUTABLE,
+				   "NEXT_HDR_SINK_DATA", 0);
+	if (prop) {
+		hdmi->next_hdr_sink_data_property = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	if (hdmi->is_hdmi_qp) {
+		prop = drm_property_create_bool(connector->dev, 0, "allm_capacity");
+		if (prop) {
+			hdmi->allm_capacity = prop;
+			drm_object_attach_property(&connector->base, prop,
+						   !!(hdmi->add_func & SUPPORT_HDMI_ALLM));
+		}
+
+		prop = drm_property_create_enum(connector->dev, 0,
+						"allm_enable",
+						allm_enable_list,
+						ARRAY_SIZE(allm_enable_list));
+		if (prop) {
+			hdmi->allm_enable = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+		hdmi->enable_allm = allm_en;
 	}
 
 	prop = drm_property_create_enum(connector->dev, 0,
@@ -1244,18 +2791,48 @@
 	}
 
 	prop = drm_property_create_enum(connector->dev, 0,
-					 "output_type_capacity",
-					 output_type_cap_list,
-					 ARRAY_SIZE(output_type_cap_list));
+					"output_type_capacity",
+					output_type_cap_list,
+					ARRAY_SIZE(output_type_cap_list));
 	if (prop) {
 		hdmi->output_type_capacity = prop;
 		drm_object_attach_property(&connector->base, prop, 0);
 	}
 
+	if (!hdmi->is_hdmi_qp) {
+		prop = drm_property_create_enum(connector->dev, 0,
+						"hdmi_quant_range",
+						quant_range_enum_list,
+						ARRAY_SIZE(quant_range_enum_list));
+		if (prop) {
+			hdmi->quant_range = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+	}
+
 	prop = connector->dev->mode_config.hdr_output_metadata_property;
-	if (version >= 0x211a)
+	if (hdmi->is_hdmi_qp)
 		drm_object_attach_property(&connector->base, prop, 0);
-	drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
+
+	if (!drm_mode_create_hdmi_colorspace_property(connector))
+		drm_object_attach_property(&connector->base,
+					   connector->colorspace_property, 0);
+	drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id);
+
+	ret = drm_connector_attach_content_protection_property(connector, true);
+	if (ret) {
+		dev_err(hdmi->dev, "failed to attach content protection: %d\n", ret);
+		return;
+	}
+
+	prop = drm_property_create_range(connector->dev, 0, RK_IF_PROP_ENCRYPTED,
+					 RK_IF_HDCP_ENCRYPTED_NONE, RK_IF_HDCP_ENCRYPTED_LEVEL2);
+	if (!prop) {
+		dev_err(hdmi->dev, "create hdcp encrypted prop for hdmi%d failed\n", hdmi->id);
+		return;
+	}
+	hdmi->hdcp_state_property = prop;
+	drm_object_attach_property(&connector->base, prop, RK_IF_HDCP_ENCRYPTED_NONE);
 }
 
 static void
@@ -1294,16 +2871,16 @@
 		hdmi->quant_range = NULL;
 	}
 
-	if (hdmi->colorimetry_property) {
-		drm_property_destroy(connector->dev,
-				     hdmi->colorimetry_property);
-		hdmi->colordepth_capacity = NULL;
-	}
-
 	if (hdmi->hdr_panel_metadata_property) {
 		drm_property_destroy(connector->dev,
 				     hdmi->hdr_panel_metadata_property);
 		hdmi->hdr_panel_metadata_property = NULL;
+	}
+
+	if (hdmi->next_hdr_sink_data_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->next_hdr_sink_data_property);
+		hdmi->next_hdr_sink_data_property = NULL;
 	}
 
 	if (hdmi->output_hdmi_dvi) {
@@ -1316,6 +2893,17 @@
 		drm_property_destroy(connector->dev,
 				     hdmi->output_type_capacity);
 		hdmi->output_type_capacity = NULL;
+	}
+
+	if (hdmi->allm_capacity) {
+		drm_property_destroy(connector->dev,
+				     hdmi->allm_capacity);
+		hdmi->allm_capacity = NULL;
+	}
+
+	if (hdmi->allm_enable) {
+		drm_property_destroy(connector->dev, hdmi->allm_enable);
+		hdmi->allm_enable = NULL;
 	}
 }
 
@@ -1353,14 +2941,16 @@
 		return 0;
 	} else if (property == config->hdr_output_metadata_property) {
 		return 0;
-	} else if (property == hdmi->colorimetry_property) {
-		hdmi->colorimetry = val;
-		return 0;
 	} else if (property == hdmi->output_hdmi_dvi) {
-		if (hdmi->force_output != val)
-			hdmi->color_changed++;
-		hdmi->force_output = val;
-		dw_hdmi_set_output_type(hdmi->hdmi, val);
+		if (!hdmi->is_hdmi_qp) {
+			if (hdmi->force_output != val)
+				hdmi->color_changed++;
+			hdmi->force_output = val;
+			dw_hdmi_set_output_type(hdmi->hdmi, val);
+		} else {
+			hdmi->force_output = val;
+			dw_hdmi_qp_set_output_type(hdmi->hdmi_qp, val);
+		}
 		return 0;
 	} else if (property == hdmi->colordepth_capacity) {
 		return 0;
@@ -1368,9 +2958,22 @@
 		return 0;
 	} else if (property == hdmi->output_type_capacity) {
 		return 0;
+	} else if (property == hdmi->allm_capacity) {
+		return 0;
+	} else if (property == hdmi->allm_enable) {
+		u64 allm_enable = hdmi->enable_allm;
+
+		hdmi->enable_allm = val;
+		if (allm_enable != hdmi->enable_allm)
+			dw_hdmi_qp_set_allm_enable(hdmi->hdmi_qp, hdmi->enable_allm);
+		return 0;
+	} else if (property == hdmi->hdcp_state_property) {
+		return 0;
 	}
 
-	DRM_ERROR("failed to set rockchip hdmi connector property %s\n", property->name);
+	DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+		  property->base.id, property->name);
+
 	return -EINVAL;
 }
 
@@ -1384,7 +2987,6 @@
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 	struct drm_display_info *info = &connector->display_info;
 	struct drm_mode_config *config = &connector->dev->mode_config;
-	struct rockchip_drm_private *private = connector->dev->dev_private;
 
 	if (property == hdmi->color_depth_property) {
 		*val = hdmi->colordepth;
@@ -1393,32 +2995,32 @@
 		*val = hdmi->hdmi_output;
 		return 0;
 	} else if (property == hdmi->colordepth_capacity) {
-		*val = BIT(ROCKCHIP_HDMI_DEPTH_8);
+		*val = BIT(RK_IF_DEPTH_8);
 		/* RK3368 only support 8bit */
 		if (hdmi->unsupported_deep_color)
 			return 0;
 		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_10);
+			*val |= BIT(RK_IF_DEPTH_10);
 		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_12);
+			*val |= BIT(RK_IF_DEPTH_12);
 		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_48)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_16);
+			*val |= BIT(RK_IF_DEPTH_16);
 		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_10);
+			*val |= BIT(RK_IF_DEPTH_420_10);
 		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_12);
+			*val |= BIT(RK_IF_DEPTH_420_12);
 		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
-			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_16);
+			*val |= BIT(RK_IF_DEPTH_420_16);
 		return 0;
 	} else if (property == hdmi->outputmode_capacity) {
-		*val = BIT(DRM_HDMI_OUTPUT_DEFAULT_RGB);
+		*val = BIT(RK_IF_FORMAT_RGB);
 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
-			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR444);
+			*val |= BIT(RK_IF_FORMAT_YCBCR444);
 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
-			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR422);
+			*val |= BIT(RK_IF_FORMAT_YCBCR422);
 		if (connector->ycbcr_420_allowed &&
 		    info->color_formats & DRM_COLOR_FORMAT_YCRCB420)
-			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR420);
+			*val |= BIT(RK_IF_FORMAT_YCBCR420);
 		return 0;
 	} else if (property == hdmi->quant_range) {
 		*val = hdmi->hdmi_quant_range;
@@ -1427,21 +3029,34 @@
 		*val = state->hdr_output_metadata ?
 			state->hdr_output_metadata->base.id : 0;
 		return 0;
-	} else if (property == hdmi->colorimetry_property) {
-		*val = hdmi->colorimetry;
-		return 0;
-	} else if (property == private->connector_id_prop) {
-		*val = hdmi->id;
-		return 0;
 	} else if (property == hdmi->output_hdmi_dvi) {
 		*val = hdmi->force_output;
 		return 0;
 	} else if (property == hdmi->output_type_capacity) {
-		*val = dw_hdmi_get_output_type_cap(hdmi->hdmi);
+		if (!hdmi->is_hdmi_qp)
+			*val = dw_hdmi_get_output_type_cap(hdmi->hdmi);
+		else
+			*val = dw_hdmi_qp_get_output_type_cap(hdmi->hdmi_qp);
+		return 0;
+	} else if (property == hdmi->allm_capacity) {
+		*val = !!(hdmi->add_func & SUPPORT_HDMI_ALLM);
+		return 0;
+	} else if (property == hdmi->allm_enable) {
+		*val = hdmi->enable_allm;
+		return 0;
+	} else if (property == hdmi->hdcp_state_property) {
+		if (hdmi->hdcp_status & BIT(1))
+			*val = RK_IF_HDCP_ENCRYPTED_LEVEL2;
+		else if (hdmi->hdcp_status & BIT(0))
+			*val = RK_IF_HDCP_ENCRYPTED_LEVEL1;
+		else
+			*val = RK_IF_HDCP_ENCRYPTED_NONE;
 		return 0;
 	}
 
-	DRM_ERROR("failed to get rockchip hdmi connector property %s\n", property->name);
+	DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+		  property->base.id, property->name);
+
 	return -EINVAL;
 }
 
@@ -1452,6 +3067,36 @@
 	.get_property		= dw_hdmi_rockchip_get_property,
 };
 
+static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
+					      struct drm_display_mode *mode,
+					      struct drm_display_mode *adj)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	struct drm_crtc *crtc;
+	struct rockchip_crtc_state *s;
+
+	if (!encoder->crtc)
+		return;
+	crtc = encoder->crtc;
+
+	if (!crtc->state)
+		return;
+	s = to_rockchip_crtc_state(crtc->state);
+
+	if (!s)
+		return;
+
+	if (hdmi->is_hdmi_qp) {
+		s->dsc_enable = 0;
+		if (hdmi->link_cfg.dsc_mode)
+			dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state);
+
+		phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+	}
+
+	clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000);
+}
+
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
 	.enable     = dw_hdmi_rockchip_encoder_enable,
 	.disable    = dw_hdmi_rockchip_encoder_disable,
@@ -1459,7 +3104,8 @@
 	.mode_set = dw_hdmi_rockchip_encoder_mode_set,
 };
 
-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
+static void
+dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 
@@ -1467,13 +3113,15 @@
 		phy_power_off(hdmi->phy);
 }
 
-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
-			     struct drm_display_mode *mode)
+static int
+dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
+			     const struct drm_display_info *display,
+			     const struct drm_display_mode *mode)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 
 	dw_hdmi_rockchip_genphy_disable(dw_hdmi, data);
-	dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi);
+	dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
 	return phy_power_on(hdmi->phy);
 }
 
@@ -1483,10 +3131,17 @@
 
 	dw_hdmi_phy_setup_hpd(dw_hdmi, data);
 
-	regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2,
-		     RK3228_DDC_MASK_EN);
-	regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON6,
-		     RK3228_IO_3V_DOMAIN);
+	regmap_write(hdmi->regmap,
+		RK3228_GRF_SOC_CON6,
+		HIWORD_UPDATE(RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL |
+			      RK3228_HDMI_SCL_VSEL,
+			      RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL |
+			      RK3228_HDMI_SCL_VSEL));
+
+	regmap_write(hdmi->regmap,
+		RK3228_GRF_SOC_CON2,
+		HIWORD_UPDATE(RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK,
+			      RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK));
 }
 
 static enum drm_connector_status
@@ -1534,6 +3189,90 @@
 			      RK3328_HDMI_HPD_IOE));
 }
 
+static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi,
+					    void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	while (hdmi->phy->power_count > 0)
+		phy_power_off(hdmi->phy);
+}
+
+static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data,
+					   struct drm_display_mode *mode)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data);
+
+	return phy_power_on(hdmi->phy);
+}
+
+static enum drm_connector_status
+dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+	u32 val;
+	int ret;
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
+
+	if (!hdmi->id) {
+		if (val & RK3588_HDMI0_LEVEL_INT) {
+			hdmi->hpd_stat = true;
+			ret = connector_status_connected;
+		} else {
+			hdmi->hpd_stat = false;
+			ret = connector_status_disconnected;
+		}
+	} else {
+		if (val & RK3588_HDMI1_LEVEL_INT) {
+			hdmi->hpd_stat = true;
+			ret = connector_status_connected;
+		} else {
+			hdmi->hpd_stat = false;
+			ret = connector_status_disconnected;
+		}
+	}
+
+	return ret;
+}
+
+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+}
+
+static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data,
+					u32 mode_mask, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!hdmi->phy)
+		return;
+
+	/* set phy earc/frl mode */
+	if (enable)
+		hdmi->phy_bus_width |= mode_mask;
+	else
+		hdmi->phy_bus_width &= ~mode_mask;
+
+	phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+}
+
 static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
 	.init		= dw_hdmi_rockchip_genphy_init,
 	.disable	= dw_hdmi_rockchip_genphy_disable,
@@ -1548,11 +3287,14 @@
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg = rockchip_mpll_cfg,
+	.cur_ctr = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
-	.phy_data   = &rk3228_chip_data,
-	.phy_ops    = &rk3228_hdmi_phy_ops,
-	.phy_name   = "inno_dw_hdmi_phy",
+	.phy_data = &rk3228_chip_data,
+	.phy_ops = &rk3228_hdmi_phy_ops,
+	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
+	.max_tmdsclk = 371250,
 	.ycbcr_420_allowed = true,
 };
 
@@ -1570,6 +3312,7 @@
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3288_chip_data,
 	.tmds_n_table = rockchip_werid_tmds_n_table,
+	.unsupported_yuv_input = true,
 	.ycbcr_420_allowed = true,
 };
 
@@ -1579,6 +3322,20 @@
 	.read_hpd	= dw_hdmi_rk3328_read_hpd,
 	.update_hpd	= dw_hdmi_phy_update_hpd,
 	.setup_hpd	= dw_hdmi_rk3328_setup_hpd,
+};
+
+static enum drm_connector_status
+dw_hdmi_rk3528_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
+{
+	return dw_hdmi_phy_read_hpd(dw_hdmi, data);
+}
+
+static const struct dw_hdmi_phy_ops rk3528_hdmi_phy_ops = {
+	.init		= dw_hdmi_rockchip_genphy_init,
+	.disable	= dw_hdmi_rockchip_genphy_disable,
+	.read_hpd	= dw_hdmi_rk3528_read_hpd,
+	.update_hpd	= dw_hdmi_phy_update_hpd,
+	.setup_hpd	= dw_hdmi_phy_setup_hpd,
 };
 
 static struct rockchip_hdmi_chip_data rk3328_chip_data = {
@@ -1594,6 +3351,8 @@
 	.phy_ops = &rk3328_hdmi_phy_ops,
 	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
+	.use_drm_infoframe = true,
+	.max_tmdsclk = 371250,
 	.ycbcr_420_allowed = true,
 };
 
@@ -1608,6 +3367,8 @@
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3368_chip_data,
+	.unsupported_deep_color = true,
+	.max_tmdsclk = 340000,
 	.ycbcr_420_allowed = true,
 };
 
@@ -1624,6 +3385,24 @@
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3399_chip_data,
+	.use_drm_infoframe = true,
+	.ycbcr_420_allowed = true,
+};
+
+static struct rockchip_hdmi_chip_data rk3528_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3528_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg = rockchip_mpll_cfg,
+	.cur_ctr = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3528_chip_data,
+	.phy_ops = &rk3528_hdmi_phy_ops,
+	.phy_name = "inno_dw_hdmi_phy2",
+	.phy_force_vendor = true,
+	.use_drm_infoframe = true,
 	.ycbcr_420_allowed = true,
 };
 
@@ -1634,12 +3413,38 @@
 
 static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
-	.mpll_cfg   = rockchip_mpll_cfg_rk356x,
+	.mpll_cfg   = rockchip_mpll_cfg,
 	.mpll_cfg_420 = rockchip_mpll_cfg_420,
-	.cur_ctr    = rockchip_cur_ctr_rk356x,
+	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3568_chip_data,
 	.ycbcr_420_allowed = true,
+	.use_drm_infoframe = true,
+};
+
+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
+	.init		= dw_hdmi_qp_rockchip_genphy_init,
+	.disable	= dw_hdmi_qp_rockchip_phy_disable,
+	.read_hpd	= dw_hdmi_rk3588_read_hpd,
+	.setup_hpd	= dw_hdmi_rk3588_setup_hpd,
+	.set_mode       = dw_hdmi_rk3588_phy_set_mode,
+};
+
+struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = {
+	.lcdsel_grf_reg = -1,
+	.ddc_en_reg = RK3588_GRF_VO1_CON3,
+	.split_mode = true,
+};
+
+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.phy_data = &rk3588_hdmi_chip_data,
+	.qp_phy_ops = &rk3588_hdmi_phy_ops,
+	.phy_name = "samsung_hdptx_phy",
+	.phy_force_vendor = true,
+	.ycbcr_420_allowed = true,
+	.is_hdmi_qp = true,
+	.use_drm_infoframe = true,
 };
 
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
@@ -1659,8 +3464,14 @@
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3528-dw-hdmi",
+	  .data = &rk3528_hdmi_drv_data
+	},
 	{ .compatible = "rockchip,rk3568-dw-hdmi",
 	  .data = &rk3568_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3588-dw-hdmi",
+	  .data = &rk3588_hdmi_drv_data
 	},
 	{},
 };
@@ -1670,32 +3481,24 @@
 				 void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	struct dw_hdmi_plat_data *plat_data;
-	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
 	struct rockchip_hdmi *hdmi;
-	int ret, id;
+	struct dw_hdmi_plat_data *plat_data;
+	struct rockchip_hdmi *secondary;
+	int ret;
+	u32 val;
 
 	if (!pdev->dev.of_node)
 		return -ENODEV;
 
-	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	hdmi = platform_get_drvdata(pdev);
 	if (!hdmi)
 		return -ENOMEM;
 
-	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
-	plat_data = devm_kmemdup(&pdev->dev, match->data,
-					     sizeof(*plat_data), GFP_KERNEL);
-	if (!plat_data)
-		return -ENOMEM;
+	plat_data = hdmi->plat_data;
+	hdmi->drm_dev = drm;
 
-	id = of_alias_get_id(dev->of_node, "hdmi");
-	if (id < 0)
-		id = 0;
-	hdmi->id = id;
-	hdmi->dev = &pdev->dev;
-	hdmi->chip_data = plat_data->phy_data;
 	plat_data->phy_data = hdmi;
 	plat_data->get_input_bus_format =
 		dw_hdmi_rockchip_get_input_bus_format;
@@ -1713,23 +3516,121 @@
 		dw_hdmi_rockchip_get_hdr_blob;
 	plat_data->get_color_changed =
 		dw_hdmi_rockchip_get_color_changed;
+	plat_data->get_yuv422_format =
+		dw_hdmi_rockchip_get_yuv422_format;
+	plat_data->get_edid_dsc_info =
+		dw_hdmi_rockchip_get_edid_dsc_info;
+	plat_data->get_next_hdr_data =
+		dw_hdmi_rockchip_get_next_hdr_data;
+	plat_data->get_colorimetry =
+		dw_hdmi_rockchip_get_colorimetry;
+	plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg;
+	plat_data->set_hdcp2_enable = rk3588_set_hdcp2_enable;
+	plat_data->set_hdcp_status = rk3588_set_hdcp_status;
+	plat_data->set_grf_cfg = rk3588_set_grf_cfg;
+	plat_data->get_grf_color_fmt = rk3588_get_grf_color_fmt;
+	plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode;
+	plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode;
+	plat_data->dclk_set = dw_hdmi_dclk_set;
+	plat_data->link_clk_set = dw_hdmi_link_clk_set;
+	plat_data->get_vp_id = dw_hdmi_rockchip_get_vp_id;
+	plat_data->update_color_format =
+		dw_hdmi_rockchip_update_color_format;
+	plat_data->check_hdr_color_change =
+		dw_hdmi_rockchip_check_hdr_color_change;
+	plat_data->set_prev_bus_format =
+		dw_hdmi_rockchip_set_prev_bus_format;
+	plat_data->set_ddc_io =
+		dw_hdmi_rockchip_set_ddc_io;
+	plat_data->set_hdcp14_mem =
+		dw_hdmi_rockchip_set_hdcp14_mem;
 	plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
 
-	encoder = &hdmi->encoder;
+	secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id);
+	/* If don't enable hdmi0 and hdmi1, we don't enable split mode */
+	if (hdmi->chip_data->split_mode && secondary) {
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
-	/*
-	 * If we failed to find the CRTC(s) which this encoder is
-	 * supposed to be connected to, it's because the CRTC has
-	 * not been registered yet.  Defer probing, and hope that
-	 * the required CRTC is added later.
-	 */
-	if (encoder->possible_crtcs == 0)
-		return -EPROBE_DEFER;
+		/*
+		 * hdmi can only attach bridge and init encoder/connector in the
+		 * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized
+		 * and plat_data->left/right will be null pointer. we must check if split
+		 * mode is on and determine the sequence of hdmi bind.
+		 */
+		if (device_property_read_bool(dev, "split-mode") ||
+		    device_property_read_bool(secondary->dev, "split-mode")) {
+			plat_data->split_mode = true;
+			secondary->plat_data->split_mode = true;
+			if (!secondary->plat_data->first_screen)
+				plat_data->first_screen = true;
+		}
+	}
+
+	if (!plat_data->first_screen) {
+		encoder = &hdmi->encoder;
+		encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node);
+		/*
+		 * If we failed to find the CRTC(s) which this encoder is
+		 * supposed to be connected to, it's because the CRTC has
+		 * not been registered yet.  Defer probing, and hope that
+		 * the required CRTC is added later.
+		 */
+		if (encoder->possible_crtcs == 0)
+			return -EPROBE_DEFER;
+
+		drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
+		drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	}
+
+	if (!plat_data->max_tmdsclk)
+		hdmi->max_tmdsclk = 594000;
+	else
+		hdmi->max_tmdsclk = plat_data->max_tmdsclk;
+
+	hdmi->is_hdmi_qp = plat_data->is_hdmi_qp;
+
+	hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input;
+	hdmi->unsupported_deep_color = plat_data->unsupported_deep_color;
 
 	ret = rockchip_hdmi_parse_dt(hdmi);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->aud_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hpd_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hclk_vo1);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->earc_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hdmitx_ref);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret);
 		return ret;
 	}
 
@@ -1739,6 +3640,39 @@
 					   RK3568_HDMI_SCLIN_MSK,
 					   RK3568_HDMI_SDAIN_MSK |
 					   RK3568_HDMI_SCLIN_MSK));
+	}
+
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->id) {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+					    RK3588_HDMI0_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		} else {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
+					    RK3588_HDMI1_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		}
+		init_hpd_work(hdmi);
 	}
 
 	ret = clk_prepare_enable(hdmi->phyref_clk);
@@ -1762,6 +3696,26 @@
 		return ret;
 	}
 
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->id)
+			val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
+		else
+			val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK);
+		regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+		hdmi->hpd_irq = platform_get_irq(pdev, 4);
+		if (hdmi->hpd_irq < 0)
+			return hdmi->hpd_irq;
+
+		ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq,
+						rockchip_hdmi_hardirq,
+						rockchip_hdmi_irq,
+						IRQF_SHARED, "dw-hdmi-qp-hpd",
+						hdmi);
+		if (ret)
+			return ret;
+	}
+
 	hdmi->phy = devm_phy_optional_get(dev, "hdmi");
 	if (IS_ERR(hdmi->phy)) {
 		hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy");
@@ -1773,13 +3727,52 @@
 		}
 	}
 
-	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
-	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
-			 DRM_MODE_ENCODER_TMDS, NULL);
+	if (hdmi->is_hdmi_qp) {
+		hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder, plat_data);
 
-	platform_set_drvdata(pdev, hdmi);
+		if (IS_ERR(hdmi->hdmi_qp)) {
+			ret = PTR_ERR(hdmi->hdmi_qp);
+			drm_encoder_cleanup(&hdmi->encoder);
+		}
 
-	hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+		if (plat_data->bridge) {
+			struct drm_connector *connector = NULL;
+			struct list_head *connector_list =
+				&plat_data->bridge->dev->mode_config.connector_list;
+
+			list_for_each_entry(connector, connector_list, head)
+				if (drm_connector_has_possible_encoder(connector,
+							&hdmi->encoder))
+					break;
+
+			hdmi->sub_dev.connector = connector;
+			hdmi->sub_dev.of_node = dev->of_node;
+			rockchip_drm_register_sub_dev(&hdmi->sub_dev);
+		} else if (plat_data->connector) {
+			hdmi->sub_dev.connector = plat_data->connector;
+			hdmi->sub_dev.loader_protect = dw_hdmi_rockchip_encoder_loader_protect;
+			if (secondary && device_property_read_bool(secondary->dev, "split-mode"))
+				hdmi->sub_dev.of_node = secondary->dev->of_node;
+			else
+				hdmi->sub_dev.of_node = hdmi->dev->of_node;
+
+			rockchip_drm_register_sub_dev(&hdmi->sub_dev);
+		}
+
+		if (plat_data->split_mode && secondary) {
+			if (device_property_read_bool(dev, "split-mode")) {
+				plat_data->right = secondary->hdmi_qp;
+				secondary->plat_data->left = hdmi->hdmi_qp;
+			} else {
+				plat_data->left = secondary->hdmi_qp;
+				secondary->plat_data->right = hdmi->hdmi_qp;
+			}
+		}
+
+		return ret;
+	}
+
+	hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder, plat_data);
 
 	/*
 	 * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
@@ -1787,9 +3780,15 @@
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
-		drm_encoder_cleanup(encoder);
+		drm_encoder_cleanup(&hdmi->encoder);
+		clk_disable_unprepare(hdmi->aud_clk);
 		clk_disable_unprepare(hdmi->phyref_clk);
 		clk_disable_unprepare(hdmi->hclk_vop);
+		clk_disable_unprepare(hdmi->hpd_clk);
+		clk_disable_unprepare(hdmi->hclk_vo1);
+		clk_disable_unprepare(hdmi->earc_clk);
+		clk_disable_unprepare(hdmi->hdmitx_ref);
+		clk_disable_unprepare(hdmi->pclk);
 	}
 
 	if (plat_data->connector) {
@@ -1806,11 +3805,27 @@
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (hdmi->is_hdmi_qp) {
+		cancel_delayed_work(&hdmi->work);
+		flush_workqueue(hdmi->workqueue);
+		destroy_workqueue(hdmi->workqueue);
+	}
+
 	if (hdmi->sub_dev.connector)
 		rockchip_drm_unregister_sub_dev(&hdmi->sub_dev);
-	dw_hdmi_unbind(hdmi->hdmi);
+
+	if (hdmi->is_hdmi_qp)
+		dw_hdmi_qp_unbind(hdmi->hdmi_qp);
+	else
+		dw_hdmi_unbind(hdmi->hdmi);
+	clk_disable_unprepare(hdmi->aud_clk);
 	clk_disable_unprepare(hdmi->phyref_clk);
 	clk_disable_unprepare(hdmi->hclk_vop);
+	clk_disable_unprepare(hdmi->hpd_clk);
+	clk_disable_unprepare(hdmi->hclk_vo1);
+	clk_disable_unprepare(hdmi->earc_clk);
+	clk_disable_unprepare(hdmi->hdmitx_ref);
+	clk_disable_unprepare(hdmi->pclk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
@@ -1820,6 +3835,33 @@
 
 static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
 {
+	struct rockchip_hdmi *hdmi;
+	const struct of_device_id *match;
+	struct dw_hdmi_plat_data *plat_data;
+	int id;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	id = of_alias_get_id(pdev->dev.of_node, "hdmi");
+	if (id < 0)
+		id = 0;
+
+	hdmi->id = id;
+	hdmi->dev = &pdev->dev;
+
+	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
+	plat_data = devm_kmemdup(&pdev->dev, match->data,
+				 sizeof(*plat_data), GFP_KERNEL);
+	if (!plat_data)
+		return -ENOMEM;
+
+	plat_data->id = hdmi->id;
+	hdmi->plat_data = plat_data;
+	hdmi->chip_data = plat_data->phy_data;
+
+	platform_set_drvdata(pdev, hdmi);
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_get_sync(&pdev->dev);
 
@@ -1828,12 +3870,25 @@
 
 static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev)
 {
-	struct rockchip_hdmi *hdmi = platform_get_drvdata(pdev);
+	struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev);
 
 	if (!hdmi)
 		return;
 
-	dw_hdmi_suspend(&pdev->dev, hdmi->hdmi);
+	if (hdmi->is_hdmi_qp) {
+		if (hdmi->hpd_irq)
+			disable_irq(hdmi->hpd_irq);
+		cancel_delayed_work(&hdmi->work);
+		flush_workqueue(hdmi->workqueue);
+		dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp);
+	} else {
+		if (hdmi->hpd_gpiod) {
+			disable_irq(hdmi->hpd_irq);
+			if (hdmi->hpd_wake_en)
+				disable_irq_wake(hdmi->hpd_irq);
+		}
+		dw_hdmi_suspend(hdmi->hdmi);
+	}
 	pm_runtime_put_sync(&pdev->dev);
 }
 
@@ -1849,7 +3904,15 @@
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
-	dw_hdmi_suspend(dev, hdmi->hdmi);
+	if (hdmi->is_hdmi_qp) {
+		if (hdmi->hpd_irq)
+			disable_irq(hdmi->hpd_irq);
+		dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp);
+	} else {
+		if (hdmi->hpd_gpiod)
+			disable_irq(hdmi->hpd_irq);
+		dw_hdmi_suspend(hdmi->hdmi);
+	}
 	pm_runtime_put_sync(dev);
 
 	return 0;
@@ -1858,14 +3921,56 @@
 static int dw_hdmi_rockchip_resume(struct device *dev)
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+	u32 val;
 
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->id) {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+					    RK3588_HDMI0_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		} else {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
+					    RK3588_HDMI1_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		}
+
+		dw_hdmi_qp_resume(dev, hdmi->hdmi_qp);
+		if (hdmi->hpd_irq)
+			enable_irq(hdmi->hpd_irq);
+		drm_helper_hpd_irq_event(hdmi->drm_dev);
+	} else {
+		if (hdmi->hpd_gpiod) {
+			dw_hdmi_rk3528_gpio_hpd_init(hdmi);
+			enable_irq(hdmi->hpd_irq);
+		}
+		dw_hdmi_resume(hdmi->hdmi);
+	}
 	pm_runtime_get_sync(dev);
-	dw_hdmi_resume(dev, hdmi->hdmi);
 
-	return  0;
+	return 0;
 }
 
-static const struct dev_pm_ops dw_hdmi_pm_ops = {
+static const struct dev_pm_ops dw_hdmi_rockchip_pm = {
 	SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend,
 				dw_hdmi_rockchip_resume)
 };
@@ -1876,7 +3981,7 @@
 	.shutdown = dw_hdmi_rockchip_shutdown,
 	.driver = {
 		.name = "dwhdmi-rockchip",
+		.pm = &dw_hdmi_rockchip_pm,
 		.of_match_table = dw_hdmi_rockchip_dt_ids,
-		.pm = &dw_hdmi_pm_ops,
 	},
 };

--
Gitblit v1.6.2