From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 3 May 2020 16:51:31 +0000
Subject: [PATCH] drm/rockchip: vop: filter modes outside 0.5% pixel clock
 tolerance

Filter modes that require a pixel clock that differ more then 0.5%
from the requested pixel clock.

This filter is only applied to tmds only connector and/or encoders.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 54 +++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index dbe4d411b30f..fac23d370ee0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1207,6 +1207,59 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+static bool vop_crtc_is_tmds(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+	struct drm_encoder *encoder;
+
+	switch (s->output_type) {
+	case DRM_MODE_CONNECTOR_LVDS:
+	case DRM_MODE_CONNECTOR_DSI:
+		return false;
+	case DRM_MODE_CONNECTOR_eDP:
+	case DRM_MODE_CONNECTOR_HDMIA:
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		return true;
+	}
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
+		if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
+			return true;
+
+	return false;
+}
+
+/*
+ * The VESA DMT standard specifies a 0.5% pixel clock frequency tolerance.
+ * The CVT spec reuses that tolerance in its examples.
+ */
+#define	CLOCK_TOLERANCE_PER_MILLE	5
+
+static enum drm_mode_status vop_crtc_mode_valid5(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode)
+{
+	struct vop *vop = to_vop(crtc);
+	long rounded_rate;
+	long lowest, highest;
+
+	if (!vop_crtc_is_tmds(crtc))
+		return MODE_OK;
+
+	rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
+	if (rounded_rate < 0)
+		return MODE_NOCLOCK;
+
+	lowest = mode->clock * (1000 - CLOCK_TOLERANCE_PER_MILLE);
+	if (rounded_rate < lowest)
+		return MODE_CLOCK_LOW;
+
+	highest = mode->clock * (1000 + CLOCK_TOLERANCE_PER_MILLE);
+	if (rounded_rate > highest)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
 static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
 				const struct drm_display_mode *mode,
 				struct drm_display_mode *adjusted_mode)

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 15:15:50 +0000
Subject: [PATCH] drm/rockchip: vop: filter interlaced modes

The current version of the driver does not support interlaced modes,
lets filter any interlaced mode.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index fac23d370ee0..9f7326c5b1f5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1245,6 +1245,9 @@ static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
 	if (!vop_crtc_is_tmds(crtc))
 		return MODE_OK;
 
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		return MODE_NO_INTERLACE;
+
 	rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
 	if (rounded_rate < 0)
 		return MODE_NOCLOCK;

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 11:46:16 +0000
Subject: [PATCH] drm/rockchip: vop: filter modes above max output supported

Filter any mode with a resolution not supported by the VOP.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 48 +++++++++++++++------
 1 file changed, 34 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 9f7326c5b1f5..30e252ba7184 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1229,6 +1229,24 @@ static bool vop_crtc_is_tmds(struct drm_crtc *crtc)
 	return false;
 }
 
+static enum drm_mode_status vop_crtc_size_valid(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode)
+{
+	struct vop *vop = to_vop(crtc);
+	const struct vop_rect *max_output = &vop->data->max_output;
+
+	if (max_output->width && max_output->height) {
+		/* only the size of the resulting rect matters */
+		if(drm_mode_validate_size(mode, max_output->width,
+					  max_output->height) != MODE_OK) {
+			return drm_mode_validate_size(mode, max_output->height,
+						      max_output->width);
+		}
+	}
+
+	return MODE_OK;
+}
+
 /*
  * The VESA DMT standard specifies a 0.5% pixel clock frequency tolerance.
  * The CVT spec reuses that tolerance in its examples.
@@ -1242,25 +1260,24 @@ static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
 	long rounded_rate;
 	long lowest, highest;
 
-	if (!vop_crtc_is_tmds(crtc))
-		return MODE_OK;
-
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-		return MODE_NO_INTERLACE;
+			return MODE_NO_INTERLACE;
 
-	rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
-	if (rounded_rate < 0)
-		return MODE_NOCLOCK;
+	if (vop_crtc_is_tmds(crtc)) {
+		rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
+		if (rounded_rate < 0)
+			return MODE_NOCLOCK;
 
-	lowest = mode->clock * (1000 - CLOCK_TOLERANCE_PER_MILLE);
-	if (rounded_rate < lowest)
-		return MODE_CLOCK_LOW;
+		lowest = mode->clock * (1000 - CLOCK_TOLERANCE_PER_MILLE);
+		if (rounded_rate < lowest)
+			return MODE_CLOCK_LOW;
 
-	highest = mode->clock * (1000 + CLOCK_TOLERANCE_PER_MILLE);
-	if (rounded_rate > highest)
-		return MODE_CLOCK_HIGH;
+		highest = mode->clock * (1000 + CLOCK_TOLERANCE_PER_MILLE);
+		if (rounded_rate > highest)
+			return MODE_CLOCK_HIGH;
+	}
 
-	return MODE_OK;
+	return vop_crtc_size_valid(crtc, mode);
 }
 
 static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1270,6 +1287,9 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
 	struct vop *vop = to_vop(crtc);
 	unsigned long rate;
 
+	if (vop_crtc_size_valid(crtc, adjusted_mode) != MODE_OK)
+		return false;
+
 	/*
 	 * Clock craziness.
 	 *

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 10 Oct 2020 10:16:32 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: encoder error handling

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 38dded2baaf7..9e460b7e14a4 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -592,7 +592,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->phy)) {
 		ret = PTR_ERR(hdmi->phy);
 		if (ret != -EPROBE_DEFER)
-			dev_err(hdmi->dev, "failed to get phy\n");
+			dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
 		return ret;
 	}
 
@@ -605,7 +605,12 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	}
 
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+	ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_DEV_ERROR(hdmi->dev, "Failed to init encoder: %d\n", ret);
+		goto err_disable_clk;
+	}
 
 	platform_set_drvdata(pdev, hdmi);
 
@@ -624,7 +629,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 
 err_bind:
 	drm_encoder_cleanup(encoder);
-
+err_disable_clk:
 	return ret;
 }
 

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 10 Oct 2020 14:32:21 +0000
Subject: [PATCH] drm/rockchip: vop: split rk3288 vop

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 9b25b8ffd0ce..a2b281e290e0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -740,7 +740,7 @@ static const struct vop_intr rk3288_vop_intr = {
 	.clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8),
 };
 
-static const struct vop_data rk3288_vop = {
+static const struct vop_data rk3288_vop_big = {
 	.version = VOP_VERSION(3, 1),
 	.feature = VOP_FEATURE_OUTPUT_RGB10,
 	.max_output = { 3840, 2160 },
@@ -753,6 +753,19 @@ static const struct vop_data rk3288_vop = {
 	.lut_size = 1024,
 };
 
+static const struct vop_data rk3288_vop_lit = {
+	.version = VOP_VERSION(3, 1),
+	.feature = VOP_FEATURE_OUTPUT_RGB10,
+	.max_output = { 2560, 1600 },
+	.intr = &rk3288_vop_intr,
+	.common = &rk3288_common,
+	.modeset = &rk3288_modeset,
+	.output = &rk3288_output,
+	.win = rk3288_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
+	.lut_size = 1024,
+};
+
 static const int rk3368_vop_intrs[] = {
 	FS_INTR,
 	0, 0,
@@ -1142,8 +1155,10 @@ static const struct of_device_id vop_driver_dt_match[] = {
 	  .data = &rk3066_vop },
 	{ .compatible = "rockchip,rk3188-vop",
 	  .data = &rk3188_vop },
-	{ .compatible = "rockchip,rk3288-vop",
-	  .data = &rk3288_vop },
+	{ .compatible = "rockchip,rk3288-vop-big",
+	  .data = &rk3288_vop_big },
+	{ .compatible = "rockchip,rk3288-vop-lit",
+	  .data = &rk3288_vop_lit },
 	{ .compatible = "rockchip,rk3368-vop",
 	  .data = &rk3368_vop },
 	{ .compatible = "rockchip,rk3366-vop",

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 10 Oct 2020 14:33:30 +0000
Subject: [PATCH] ARM: dts: rockchip: split rk3288 vop

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 arch/arm/boot/dts/rockchip/rk3288.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/rockchip/rk3288.dtsi b/arch/arm/boot/dts/rockchip/rk3288.dtsi
index 487b0e03d4b4..c60eacab8a79 100644
--- a/arch/arm/boot/dts/rockchip/rk3288.dtsi
+++ b/arch/arm/boot/dts/rockchip/rk3288.dtsi
@@ -1017,7 +1017,7 @@ rga: rga@ff920000 {
 	};
 
 	vopb: vop@ff930000 {
-		compatible = "rockchip,rk3288-vop";
+		compatible = "rockchip,rk3288-vop-big";
 		reg = <0x0 0xff930000 0x0 0x19c>, <0x0 0xff931000 0x0 0x1000>;
 		interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
@@ -1066,7 +1066,7 @@ vopb_mmu: iommu@ff930300 {
 	};
 
 	vopl: vop@ff940000 {
-		compatible = "rockchip,rk3288-vop";
+		compatible = "rockchip,rk3288-vop-lit";
 		reg = <0x0 0xff940000 0x0 0x19c>, <0x0 0xff941000 0x0 0x1000>;
 		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru ACLK_VOP1>, <&cru DCLK_VOP1>, <&cru HCLK_VOP1>;

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 18:00:44 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: add mtmdsclock parameter to phy
 configure ops

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 10 ++++++----
 drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c    |  3 ++-
 include/drm/bridge/dw_hdmi.h              |  3 ++-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 40d8ca37f5bc..22af42a08980 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -138,7 +138,8 @@ struct dw_hdmi_phy_data {
 	bool has_svsret;
 	int (*configure)(struct dw_hdmi *hdmi,
 			 const struct dw_hdmi_plat_data *pdata,
-			 unsigned long mpixelclock);
+			 unsigned long mpixelclock,
+			 unsigned long mtmdsclock);
 };
 
 struct dw_hdmi {
@@ -1585,7 +1586,8 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
  */
 static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 		const struct dw_hdmi_plat_data *pdata,
-		unsigned long mpixelclock)
+		unsigned long mpixelclock,
+		unsigned long mtmdsclock)
 {
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
@@ -1660,9 +1662,9 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
 
 	/* Write to the PHY as configured by the platform */
 	if (pdata->configure_phy)
-		ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
+		ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock, mtmdsclock);
 	else
-		ret = phy->configure(hdmi, pdata, mpixelclock);
+		ret = phy->configure(hdmi, pdata, mpixelclock, mtmdsclock);
 	if (ret) {
 		dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
 			mpixelclock);
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
index 18ed14911b98..9c75095a25c5 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
@@ -53,7 +53,8 @@ rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
 }
 
 static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data,
-				   unsigned long mpixelclock)
+				   unsigned long mpixelclock,
+				   unsigned long mtmdsclock)
 {
 	const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
 
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index f668e75fbabe..48fb72f9614f 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -159,7 +159,8 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
 	int (*configure_phy)(struct dw_hdmi *hdmi, void *data,
-			     unsigned long mpixelclock);
+			     unsigned long mpixelclock,
+			     unsigned long mtmdsclock);
 
 	unsigned int disable_cec : 1;
 };

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 21:34:48 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: support configuring phy for deep color

Q: Should we rename dw_hdmi_curr_ctrl and dw_hdmi_phy_config mpixelclock to mtmdsclock ?

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 22af42a08980..7fd45a7006b1 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1592,6 +1592,7 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+	int depth;
 
 	/* TOFIX Will need 420 specific PHY configuration tables */
 
@@ -1601,11 +1602,11 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 			break;
 
 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-		if (mpixelclock <= curr_ctrl->mpixelclock)
+		if (mtmdsclock <= curr_ctrl->mpixelclock)
 			break;
 
 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
-		if (mpixelclock <= phy_config->mpixelclock)
+		if (mtmdsclock <= phy_config->mpixelclock)
 			break;
 
 	if (mpll_config->mpixelclock == ~0UL ||
@@ -1613,11 +1614,17 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	    phy_config->mpixelclock == ~0UL)
 		return -EINVAL;
 
-	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
+	depth = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+	if (depth > 8 && mpixelclock != mtmdsclock)
+		depth = fls(depth - 8) - 1;
+	else
+		depth = 0;
+
+	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
 			      HDMI_3D_TX_PHY_CPCE_CTRL);
-	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
+	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
 			      HDMI_3D_TX_PHY_GMPCTRL);
-	dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
+	dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
 			      HDMI_3D_TX_PHY_CURRCTRL);
 
 	dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 22:25:15 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: add mpll_cfg_420 for ycbcr420 mode

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 +++-
 include/drm/bridge/dw_hdmi.h              | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 7fd45a7006b1..a2d101ebf7a7 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1594,7 +1594,9 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
 	int depth;
 
-	/* TOFIX Will need 420 specific PHY configuration tables */
+	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
+	    pdata->mpll_cfg_420)
+		mpll_config = pdata->mpll_cfg_420;
 
 	/* PLL/MPLL Cfg - always match on final entry */
 	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 48fb72f9614f..02554d324b4b 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -156,6 +156,7 @@ struct dw_hdmi_plat_data {
 
 	/* Synopsys PHY support */
 	const struct dw_hdmi_mpll_config *mpll_cfg;
+	const struct dw_hdmi_mpll_config *mpll_cfg_420;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
 	int (*configure_phy)(struct dw_hdmi *hdmi, void *data,

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 22:26:19 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: add YCbCr420 mpll cfg for rk3399

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index d42ac9fa3246..a37565649c13 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -177,6 +177,46 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	}
 };
 
+static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
+	{
+		30666000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2157, 0x0000 },
+			{ 0x40f7, 0x0000 },
+		},
+	},  {
+		92000000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2143, 0x0001 },
+			{ 0x40a3, 0x0001 },
+		},
+	},  {
+		184000000, {
+			{ 0x0073, 0x0001 },
+			{ 0x2146, 0x0002 },
+			{ 0x4062, 0x0002 },
+		},
+	},  {
+		340000000, {
+			{ 0x0052, 0x0003 },
+			{ 0x214d, 0x0003 },
+			{ 0x4065, 0x0003 },
+		},
+	},  {
+		600000000, {
+			{ 0x0041, 0x0003 },
+			{ 0x3b4d, 0x0003 },
+			{ 0x5a65, 0x0003 },
+		},
+	},  {
+		~0UL, {
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+		},
+	}
+};
+
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
@@ -474,6 +514,7 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_mpll_cfg_420,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3399_chip_data,

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shunqing Chen <csq@rock-chips.com>
Date: Wed, 15 Jul 2020 15:19:11 +0800
Subject: [PATCH] drm/rockchip: dw-hdmi: add YCbCr420 mpll cfg for rk3288w

Signed-off-by: Shunqing Chen <csq@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index a37565649c13..66fee351f4a7 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -217,6 +217,46 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
 	}
 };
 
+static const struct dw_hdmi_mpll_config rockchip_rk3288w_mpll_cfg_420[] = {
+	{
+		30666000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2157, 0x0000 },
+			{ 0x40f7, 0x0000 },
+		},
+	},  {
+		92000000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2143, 0x0001 },
+			{ 0x40a3, 0x0001 },
+		},
+	},  {
+		184000000, {
+			{ 0x0073, 0x0001 },
+			{ 0x2146, 0x0002 },
+			{ 0x4062, 0x0002 },
+		},
+	},  {
+		340000000, {
+			{ 0x0052, 0x0003 },
+			{ 0x214d, 0x0003 },
+			{ 0x4065, 0x0003 },
+		},
+	},  {
+		600000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a65, 0x0003 },
+		},
+	},  {
+		~0UL, {
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+		},
+	}
+};
+
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
@@ -479,6 +519,7 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_rk3288w_mpll_cfg_420,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3288_chip_data,

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: add bridge and switch to
 drm_bridge_funcs

Switch the dw-hdmi driver to drm_bridge_funcs by implementing
a new local bridge, connecting it to the dw-hdmi bridge.

Also enable bridge format negotiation by implementing
atomic_get_input_bus_fmts and support for 8-bit RGB 4:4:4.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 118 ++++++++++++++------
 1 file changed, 81 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 66fee351f4a7..d6d8f3335813 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -5,6 +5,7 @@
 
 #include <linux/clk.h>
 #include <linux/mfd/syscon.h>
+#include <linux/media-bus-format.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
@@ -72,6 +73,7 @@ struct rockchip_hdmi_chip_data {
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
+	struct drm_bridge bridge;
 	struct rockchip_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
 	struct clk *ref_clk;
@@ -82,11 +84,9 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
-static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
+static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_bridge *bridge)
 {
-	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
-
-	return container_of(rkencoder, struct rockchip_hdmi, encoder);
+	return container_of(bridge, struct rockchip_hdmi, bridge);
 }
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
@@ -335,31 +335,21 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
 
 	return MODE_OK;
 }
-
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
+static void
+dw_hdmi_rockchip_bridge_mode_set(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 const struct drm_display_mode *adjusted_mode)
 {
-}
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
 
-static bool
-dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
-				    const struct drm_display_mode *mode,
-				    struct drm_display_mode *adj_mode)
-{
-	return true;
+	clk_set_rate(hdmi->ref_clk, adjusted_mode->clock * 1000);
 }
 
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
-					      struct drm_display_mode *mode,
-					      struct drm_display_mode *adj_mode)
+static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
 {
-	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+	struct drm_encoder *encoder = bridge->encoder;
 
-	clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
-}
-
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
-{
-	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 	u32 val;
 	int ret;
 
@@ -387,10 +377,21 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 		      ret ? "LIT" : "BIG");
 }
 
+static bool is_rgb(u32 format)
+{
+	switch (format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static int
-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
-				      struct drm_crtc_state *crtc_state,
-				      struct drm_connector_state *conn_state)
+dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
+				     struct drm_bridge_state *bridge_state,
+				     struct drm_crtc_state *crtc_state,
+				     struct drm_connector_state *conn_state)
 {
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 
@@ -400,12 +401,38 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
-static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
-	.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
-	.mode_set   = dw_hdmi_rockchip_encoder_mode_set,
-	.enable     = dw_hdmi_rockchip_encoder_enable,
-	.disable    = dw_hdmi_rockchip_encoder_disable,
-	.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
+static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
+					struct drm_bridge_state *bridge_state,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state,
+					u32 output_fmt,
+					unsigned int *num_input_fmts)
+{
+	u32 *input_fmt;
+
+	*num_input_fmts = 0;
+
+	if (!is_rgb(output_fmt))
+		return NULL;
+
+	input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
+	if (!input_fmt)
+		return NULL;
+
+	*num_input_fmts = 1;
+	*input_fmt = output_fmt;
+
+	return input_fmt;
+}
+
+static const struct drm_bridge_funcs dw_hdmi_rockchip_bridge_funcs = {
+	.mode_set = dw_hdmi_rockchip_bridge_mode_set,
+	.enable = dw_hdmi_rockchip_bridge_enable,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_get_input_bus_fmts = dw_hdmi_rockchip_get_input_bus_fmts,
+	.atomic_check = dw_hdmi_rockchip_bridge_atomic_check,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
 };
 
 static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
@@ -602,6 +629,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
+	struct drm_bridge *next_bridge;
 	struct drm_encoder *encoder;
 	struct rockchip_hdmi *hdmi;
 	int ret;
@@ -679,20 +707,21 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 					   RK3568_HDMI_SCLIN_MSK));
 	}
 
-	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
-
 	ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to init encoder: %d\n", ret);
 		goto err_disable_clk;
 	}
 
+	hdmi->bridge.funcs = &dw_hdmi_rockchip_bridge_funcs;
+	drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
+
 	platform_set_drvdata(pdev, hdmi);
 
-	hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+	hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
 
 	/*
-	 * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
+	 * If dw_hdmi_probe() fails we'll never call dw_hdmi_unbind(),
 	 * which would have called the encoder cleanup.  Do it manually.
 	 */
 	if (IS_ERR(hdmi->hdmi)) {
@@ -700,8 +729,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		goto err_bind;
 	}
 
+	next_bridge = of_drm_find_bridge(pdev->dev.of_node);
+	if (!next_bridge) {
+		ret = -EPROBE_DEFER;
+		goto err_dw_hdmi_remove;
+	}
+
+	ret = drm_bridge_attach(encoder, next_bridge, &hdmi->bridge, 0);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			DRM_DEV_ERROR(hdmi->dev, "Failed to attach dw-hdmi bridge: %d\n", ret);
+		goto err_dw_hdmi_remove;
+	}
+
 	return 0;
 
+err_dw_hdmi_remove:
+	dw_hdmi_remove(hdmi->hdmi);
 err_bind:
 	drm_encoder_cleanup(encoder);
 err_disable_clk:
@@ -719,7 +763,7 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
-	dw_hdmi_unbind(hdmi->hdmi);
+	dw_hdmi_remove(hdmi->hdmi);
 	drm_encoder_cleanup(&hdmi->encoder.encoder);
 	clk_disable_unprepare(hdmi->ref_clk);
 

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 9 Oct 2020 15:29:27 +0000
Subject: [PATCH] drm/rockchip: vop: add immutable zpos property

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  | 2 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 ++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 092bf863110b..e2ee0d6a8d55 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -133,6 +133,8 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
 	dev->mode_config.max_width = 4096;
 	dev->mode_config.max_height = 4096;
 
+	dev->mode_config.normalize_zpos = true;
+
 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 30e252ba7184..897f7980ee5d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1917,7 +1917,7 @@ static irqreturn_t vop_isr(int irq, void *data)
 	return ret;
 }
 
-static void vop_plane_add_properties(struct drm_plane *plane,
+static void vop_plane_add_properties(struct drm_plane *plane, int zpos,
 				     const struct vop_win_data *win_data)
 {
 	unsigned int flags = 0;
@@ -1927,6 +1927,8 @@ static void vop_plane_add_properties(struct drm_plane *plane,
 	if (flags)
 		drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
 						   DRM_MODE_ROTATE_0 | flags);
+
+	drm_plane_create_zpos_immutable_property(plane, zpos);
 }
 
 static int vop_create_crtc(struct vop *vop)
@@ -1967,7 +1969,7 @@ static int vop_create_crtc(struct vop *vop)
 
 		plane = &vop_win->base;
 		drm_plane_helper_add(plane, &plane_helper_funcs);
-		vop_plane_add_properties(plane, win_data);
+		vop_plane_add_properties(plane, i, win_data);
 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 			primary = plane;
 		else if (plane->type == DRM_PLANE_TYPE_CURSOR)

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 10 Oct 2020 09:20:44 +0000
Subject: [PATCH] drm/rockchip: vop: add plane color properties

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 32 +++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 897f7980ee5d..eadf1b0f1704 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1917,8 +1917,23 @@ static irqreturn_t vop_isr(int irq, void *data)
 	return ret;
 }
 
+static bool plane_supports_yuv_format(const struct drm_plane *plane)
+{
+	const struct drm_format_info *info;
+	int i;
+
+	for (i = 0; i < plane->format_count; i++) {
+		info = drm_format_info(plane->format_types[i]);
+		if (info->is_yuv)
+			return true;
+	}
+
+	return false;
+}
+
 static void vop_plane_add_properties(struct drm_plane *plane, int zpos,
-				     const struct vop_win_data *win_data)
+				     const struct vop_win_data *win_data,
+				     const struct vop_data *vop_data)
 {
 	unsigned int flags = 0;
 
@@ -1929,6 +1944,19 @@ static void vop_plane_add_properties(struct drm_plane *plane, int zpos,
 						   DRM_MODE_ROTATE_0 | flags);
 
 	drm_plane_create_zpos_immutable_property(plane, zpos);
+
+	if (!plane_supports_yuv_format(plane))
+		return;
+
+	flags = BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709);
+	if (vop_data->feature & VOP_FEATURE_OUTPUT_RGB10)
+		flags |= BIT(DRM_COLOR_YCBCR_BT2020);
+
+	drm_plane_create_color_properties(plane, flags,
+					  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
+					  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
+					  DRM_COLOR_YCBCR_BT601,
+					  DRM_COLOR_YCBCR_LIMITED_RANGE);
 }
 
 static int vop_create_crtc(struct vop *vop)
@@ -1969,7 +1997,7 @@ static int vop_create_crtc(struct vop *vop)
 
 		plane = &vop_win->base;
 		drm_plane_helper_add(plane, &plane_helper_funcs);
-		vop_plane_add_properties(plane, i, win_data);
+		vop_plane_add_properties(plane, i, win_data, vop_data);
 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 			primary = plane;
 		else if (plane->type == DRM_PLANE_TYPE_CURSOR)

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nickey Yang <nickey.yang@rock-chips.com>
Date: Mon, 17 Jul 2017 16:35:34 +0800
Subject: [PATCH] HACK: clk: rockchip: rk3288: dedicate npll for vopb and hdmi
 use

MINIARM: set npll be used for hdmi only

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 arch/arm/boot/dts/rockchip/rk3288.dtsi     | 2 ++
 drivers/clk/rockchip/clk-rk3288.c | 9 +++++----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/rockchip/rk3288.dtsi b/arch/arm/boot/dts/rockchip/rk3288.dtsi
index c60eacab8a79..d1ae42757242 100644
--- a/arch/arm/boot/dts/rockchip/rk3288.dtsi
+++ b/arch/arm/boot/dts/rockchip/rk3288.dtsi
@@ -1026,6 +1026,8 @@ vopb: vop@ff930000 {
 		resets = <&cru SRST_LCDC0_AXI>, <&cru SRST_LCDC0_AHB>, <&cru SRST_LCDC0_DCLK>;
 		reset-names = "axi", "ahb", "dclk";
 		iommus = <&vopb_mmu>;
+		assigned-clocks = <&cru DCLK_VOP0>;
+		assigned-clock-parents = <&cru PLL_NPLL>;
 		status = "disabled";
 
 		vopb_out: port {
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index baa5aebd3277..5cfcbaaa154e 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -195,8 +195,9 @@ PNAME(mux_ddrphy_p)		= { "dpll_ddr", "gpll_ddr" };
 PNAME(mux_aclk_cpu_src_p)	= { "cpll_aclk_cpu", "gpll_aclk_cpu" };
 
 PNAME(mux_pll_src_cpll_gpll_p)		= { "cpll", "gpll" };
-PNAME(mux_pll_src_npll_cpll_gpll_p)	= { "npll", "cpll", "gpll" };
-PNAME(mux_pll_src_cpll_gpll_npll_p)	= { "cpll", "gpll", "npll" };
+PNAME(mux_pll_src_npll_cpll_gpll_p)	= { "prevent:npll", "cpll", "gpll" };
+PNAME(mux_pll_src_cpll_gpll_npll_p)	= { "cpll", "gpll", "prevent:npll" };
+PNAME(vop0_mux_pll_src_cpll_gpll_npll_p) = { "prevent:cpll", "prevent:gpll", "npll" };
 PNAME(mux_pll_src_cpll_gpll_usb480m_p)	= { "cpll", "gpll", "unstable:usbphy480m_src" };
 PNAME(mux_pll_src_cpll_gll_usb_npll_p)	= { "cpll", "gpll", "unstable:usbphy480m_src", "npll" };
 
@@ -232,7 +233,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
 		     RK3288_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
 	[npll] = PLL(pll_rk3066, PLL_NPLL, "npll",  mux_pll_p, 0, RK3288_PLL_CON(16),
-		     RK3288_MODE_CON, 14, 9, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
+		     RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
 };
 
 static struct clk_div_table div_hclk_cpu_t[] = {
@@ -442,7 +443,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
 			RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS,
 			RK3288_CLKGATE_CON(3), 4, GFLAGS),
 
-	COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, 0,
+	COMPOSITE(DCLK_VOP0, "dclk_vop0", vop0_mux_pll_src_cpll_gpll_npll_p, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
 			RK3288_CLKSEL_CON(27), 0, 2, MFLAGS, 8, 8, DFLAGS,
 			RK3288_CLKGATE_CON(3), 1, GFLAGS),
 	COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cpll_gpll_npll_p, 0,

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 4 Aug 2018 14:51:14 +0200
Subject: [PATCH] HACK: clk: rockchip: rk3288: use npll table to to improve
 HDMI compatibility

Based on https://github.com/TinkerBoard/debian_kernel/commit/3d90870530b8a2901681f7b7fa598ee7381e49f3

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/clk/rockchip/clk-rk3288.c | 39 ++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 5cfcbaaa154e..fa1c6e646bdf 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -121,6 +121,43 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
 	{ /* sentinel */ },
 };
 
+static struct rockchip_pll_rate_table rk3288_npll_rates[] = {
+	RK3066_PLL_RATE_NB(594000000, 1, 198, 8, 1),
+	RK3066_PLL_RATE_NB(585000000, 6, 585, 4, 32),
+	RK3066_PLL_RATE_NB(432000000, 3, 216, 4, 32),
+	RK3066_PLL_RATE_NB(426000000, 3, 213, 4, 32),
+	RK3066_PLL_RATE_NB(400000000, 1, 100, 6, 32),
+	RK3066_PLL_RATE(348500000, 8, 697, 6),
+	RK3066_PLL_RATE_NB(342000000, 3, 171, 4, 32),
+	RK3066_PLL_RATE_NB(297000000, 2, 198, 8, 16),
+	RK3066_PLL_RATE_NB(270000000, 1, 135, 12, 32),
+	RK3066_PLL_RATE_NB(260000000, 1, 130, 12, 32),
+	RK3066_PLL_RATE(241500000, 2, 161, 8),
+	RK3066_PLL_RATE(162000000, 1, 81, 12),
+	RK3066_PLL_RATE(154000000, 6, 539, 14),
+	RK3066_PLL_RATE_NB(148500000, 1, 99, 16, 32),
+	RK3066_PLL_RATE(148352000, 13, 1125, 14),
+	RK3066_PLL_RATE_NB(146250000, 6, 585, 16, 32),
+	RK3066_PLL_RATE(121750000, 6, 487, 16),
+	RK3066_PLL_RATE(119000000, 3, 238, 16),
+	RK3066_PLL_RATE_NB(108000000, 1, 54, 12, 32),
+	RK3066_PLL_RATE_NB(106500000, 4, 213, 12, 32),
+	RK3066_PLL_RATE(101000000, 3, 202, 16),
+	RK3066_PLL_RATE(88750000, 6, 355, 16),
+	RK3066_PLL_RATE_NB(85500000, 4, 171, 12, 32),
+	RK3066_PLL_RATE(83500000, 3, 167, 16),
+	RK3066_PLL_RATE(79500000, 1, 53, 16),
+	RK3066_PLL_RATE_NB(74250000, 4, 198, 16, 32),
+	RK3066_PLL_RATE(74176000, 26, 1125, 14),
+	RK3066_PLL_RATE(72000000, 1, 48, 16),
+	RK3066_PLL_RATE(71000000, 3, 142, 16),
+	RK3066_PLL_RATE(68250000, 2, 91, 16),
+	RK3066_PLL_RATE(65000000, 3, 130, 16),
+	RK3066_PLL_RATE(40000000, 3, 80, 16),
+	RK3066_PLL_RATE(33750000, 2, 45, 16),
+	{ /* sentinel */ },
+};
+
 #define RK3288_DIV_ACLK_CORE_M0_MASK	0xf
 #define RK3288_DIV_ACLK_CORE_M0_SHIFT	0
 #define RK3288_DIV_ACLK_CORE_MP_MASK	0xf
@@ -233,7 +270,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
 	[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
 		     RK3288_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
 	[npll] = PLL(pll_rk3066, PLL_NPLL, "npll",  mux_pll_p, 0, RK3288_PLL_CON(16),
-		     RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
+		     RK3288_MODE_CON, 14, 9, 0, rk3288_npll_rates),
 };
 
 static struct clk_div_table div_hclk_cpu_t[] = {

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 25 May 2020 20:36:45 +0000
Subject: [PATCH] HACK: clk: rockchip: rk3399: dedicate vpll for vopb and hdmi
 use

Rockchip PLLs are kown provide the least jitter for
vco rates between 800 MHz and 2 GHz. I added the
rates for VPLL which are used for VOPs dclk and there-
fore HDMI phy in that manner and used the rates which
require the lowest frac divs.
Additionally I added some rates which are useful to
provide additional VESA and non-VESA rates for HDMI
output.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/clk/rockchip/clk-rk3399.c | 49 ++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 306910a3a0d3..436d2789611c 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -105,6 +105,39 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
 	{ /* sentinel */ },
 };
 
+static struct rockchip_pll_rate_table rk3399_vpll_rates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE( 594000000, 1, 74, 3, 1, 0,  4194304),  /* vco = 1782000000  fout = 594000000 */
+	RK3036_PLL_RATE( 593406592, 1, 74, 3, 1, 0,  2949838),  /* vco = 1780219777  fout = 593406592.36908 */
+	RK3036_PLL_RATE( 319750000, 1, 79, 6, 1, 0, 15728640),  /* vco = 1918500000  fout = 319750000 */
+	RK3036_PLL_RATE( 297000000, 1, 74, 6, 1, 0,  4194304),  /* vco = 1782000000  fout = 297000000 */
+	RK3036_PLL_RATE( 296703296, 1, 74, 6, 1, 0,  2949838),  /* vco = 1780219777  fout = 296703296.18454 */
+	RK3036_PLL_RATE( 241500000, 1, 60, 6, 1, 0,  6291456),  /* vco = 1449000000  fout = 241500000 */
+	RK3036_PLL_RATE( 162000000, 1, 67, 5, 2, 0,  8388608),  /* vco = 1620000000  fout = 162000000 */
+	RK3036_PLL_RATE( 148500000, 1, 74, 6, 2, 0,  4194304),  /* vco = 1782000000  fout = 148500000*/
+	RK3036_PLL_RATE( 148351648, 1, 74, 6, 2, 0,  2949838),  /* vco = 1780219777  fout = 148351648.09227 */
+	RK3036_PLL_RATE( 136750000, 1, 68, 2, 6, 0,  6291456),  /* vco = 1641000000  fout = 136750000 */
+	RK3036_PLL_RATE( 135000000, 1, 56, 5, 2, 0,  4194304),  /* vco = 1350000000  fout = 135000000 */
+	RK3036_PLL_RATE( 119000000, 1, 59, 6, 2, 0,  8388608),  /* vco = 1428000000  fout = 119000000 */
+	RK3036_PLL_RATE( 108000000, 1, 63, 7, 2, 1,        0),  /* vco = 1512000000  fout = 108000000 */
+	RK3036_PLL_RATE( 106500000, 1, 62, 7, 2, 0,  2097152),  /* vco = 1491000000  fout = 106500000 */
+	RK3036_PLL_RATE(  88750000, 1, 55, 5, 3, 0,  7864320),  /* vco = 1331250000  fout = 88750000 */
+	RK3036_PLL_RATE(  85500000, 1, 57, 4, 4, 1,        0),  /* vco = 1368000000  fout = 85500000 */
+	RK3036_PLL_RATE(  78750000, 1, 59, 6, 3, 0,  1048576),  /* vco = 1417500000  fout = 78750000 */
+	RK3036_PLL_RATE(  74250000, 1, 74, 6, 4, 0,  4194304),  /* vco = 1782000000  fout = 74250000 */
+	RK3036_PLL_RATE(  74175824, 1, 74, 6, 4, 0,  2949838),  /* vco = 1780219777  fout = 74175824.046135 */
+	RK3036_PLL_RATE(  71000000, 1, 71, 6, 4, 1,        0),  /* vco = 1704000000  fout = 71000000 */
+	RK3036_PLL_RATE(  65000000, 1, 65, 6, 4, 0,        0),  /* vco = 1560000000  fout = 65000000 */
+	RK3036_PLL_RATE(  59340659, 1, 59, 6, 4, 0,  5715310),  /* vco = 1424175816  fout = 59340659.022331 */
+	RK3036_PLL_RATE(  54000000, 1, 63, 7, 4, 1,        0),  /* vco = 1512000000  fout = 54000000 */
+	RK3036_PLL_RATE(  49500000, 1, 72, 5, 7, 0,  3145728),  /* vco = 1732500000  fout = 49500000 */
+	RK3036_PLL_RATE(  40000000, 1, 70, 7, 6, 1,        0),  /* vco = 1680000000  fout = 40000000 */
+	RK3036_PLL_RATE(  31500000, 1, 55, 7, 6, 0,  2097152),  /* vco = 1323000000  fout = 31500000 */
+	RK3036_PLL_RATE(  27000000, 1, 55, 7, 7, 0,  2097152),  /* vco = 1323000000  fout = 27000000 */
+	RK3036_PLL_RATE(  26973026, 1, 55, 7, 7, 0,  1173214),  /* vco = 1321678296  fout = 26973026.450799 */
+	{ /* sentinel */ },
+};
+
 /* CRU parents */
 PNAME(mux_pll_p)				= { "xin24m", "xin32k" };
 
@@ -123,7 +156,7 @@ PNAME(mux_ddrclk_p)				= { "clk_ddrc_lpll_src",
 PNAME(mux_aclk_cci_p)				= { "cpll_aclk_cci_src",
 						    "gpll_aclk_cci_src",
 						    "npll_aclk_cci_src",
-						    "vpll_aclk_cci_src" };
+						    "prevent:vpll" };
 PNAME(mux_cci_trace_p)				= { "cpll_cci_trace",
 						    "gpll_cci_trace" };
 PNAME(mux_cs_p)					= { "cpll_cs", "gpll_cs",
@@ -149,10 +182,12 @@ PNAME(mux_pll_src_cpll_gpll_npll_upll_24m_p)	= { "cpll", "gpll", "npll",
 PNAME(mux_pll_src_cpll_gpll_npll_ppll_upll_24m_p) = { "cpll", "gpll", "npll",
 						    "ppll", "upll", "xin24m" };
 
-PNAME(mux_pll_src_vpll_cpll_gpll_p)		= { "vpll", "cpll", "gpll" };
-PNAME(mux_pll_src_vpll_cpll_gpll_npll_p)	= { "vpll", "cpll", "gpll",
+PNAME(mux_pll_src_vpll_cpll_gpll_p)		= { "prevent:vpll", "cpll", "gpll" };
+PNAME(vop0_mux_pll_src_vpll_cpll_gpll_p)	= { "vpll", "prevent:cpll", "prevent:gpll" };
+
+PNAME(mux_pll_src_vpll_cpll_gpll_npll_p)	= { "prevent:vpll", "cpll", "gpll",
 						    "npll" };
-PNAME(mux_pll_src_vpll_cpll_gpll_24m_p)		= { "vpll", "cpll", "gpll",
+PNAME(mux_pll_src_vpll_cpll_gpll_24m_p)		= { "prevent:vpll", "cpll", "gpll",
 						    "xin24m" };
 
 PNAME(mux_dclk_vop0_p)			= { "dclk_vop0_div",
@@ -229,7 +264,7 @@ static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = {
 	[npll] = PLL(pll_rk3399, PLL_NPLL, "npll",  mux_pll_p, 0, RK3399_PLL_CON(40),
 		     RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
 	[vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll",  mux_pll_p, 0, RK3399_PLL_CON(48),
-		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
+		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_vpll_rates),
 };
 
 static struct rockchip_pll_clock rk3399_pmu_pll_clks[] __initdata = {
@@ -279,7 +314,7 @@ static struct rockchip_clk_branch rk3399_uart4_pmu_fracmux __initdata =
 			RK3399_PMU_CLKSEL_CON(5), 8, 2, MFLAGS);
 
 static struct rockchip_clk_branch rk3399_dclk_vop0_fracmux __initdata =
-	MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT,
+	MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
 			RK3399_CLKSEL_CON(49), 11, 1, MFLAGS);
 
 static struct rockchip_clk_branch rk3399_dclk_vop1_fracmux __initdata =
@@ -1162,7 +1197,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 	GATE(HCLK_VOP0_NOC, "hclk_vop0_noc", "hclk_vop0_pre", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(28), 0, GFLAGS),
 
-	COMPOSITE(DCLK_VOP0_DIV, "dclk_vop0_div", mux_pll_src_vpll_cpll_gpll_p, 0,
+	COMPOSITE(DCLK_VOP0_DIV, "dclk_vop0_div", vop0_mux_pll_src_vpll_cpll_gpll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
 			RK3399_CLKSEL_CON(49), 8, 2, MFLAGS, 0, 8, DFLAGS,
 			RK3399_CLKGATE_CON(10), 12, GFLAGS),
 

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 19 Jul 2020 16:35:11 +0000
Subject: [PATCH] HACK: dts: rockchip: do not use vopl for hdmi

---
 arch/arm/boot/dts/rockchip/rk3288.dtsi            | 9 ---------
 arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 9 ---------
 2 files changed, 18 deletions(-)

diff --git a/arch/arm/boot/dts/rockchip/rk3288.dtsi b/arch/arm/boot/dts/rockchip/rk3288.dtsi
index d1ae42757242..7b2cde230b87 100644
--- a/arch/arm/boot/dts/rockchip/rk3288.dtsi
+++ b/arch/arm/boot/dts/rockchip/rk3288.dtsi
@@ -1083,11 +1083,6 @@ vopl_out: port {
 			#address-cells = <1>;
 			#size-cells = <0>;
 
-			vopl_out_hdmi: endpoint@0 {
-				reg = <0>;
-				remote-endpoint = <&hdmi_in_vopl>;
-			};
-
 			vopl_out_edp: endpoint@1 {
 				reg = <1>;
 				remote-endpoint = <&edp_in_vopl>;
@@ -1227,10 +1222,6 @@ hdmi_in_vopb: endpoint@0 {
 					remote-endpoint = <&vopb_out_hdmi>;
 				};
 
-				hdmi_in_vopl: endpoint@1 {
-					reg = <1>;
-					remote-endpoint = <&vopl_out_hdmi>;
-				};
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi
index 92c2207e686c..980b12cb0a49 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi
@@ -1846,11 +1846,6 @@ vopl_out_edp: endpoint@1 {
 				remote-endpoint = <&edp_in_vopl>;
 			};
 
-			vopl_out_hdmi: endpoint@2 {
-				reg = <2>;
-				remote-endpoint = <&hdmi_in_vopl>;
-			};
-
 			vopl_out_mipi1: endpoint@3 {
 				reg = <3>;
 				remote-endpoint = <&mipi1_in_vopl>;
@@ -2048,10 +2043,6 @@ hdmi_in_vopb: endpoint@0 {
 					reg = <0>;
 					remote-endpoint = <&vopb_out_hdmi>;
 				};
-				hdmi_in_vopl: endpoint@1 {
-					reg = <1>;
-					remote-endpoint = <&vopl_out_hdmi>;
-				};
 			};
 		};
 	};

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/bridge: dw-hdmi: limit mode and bus format to
 max_tmds_clock

---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 120 ++++++++++++++--------
 1 file changed, 76 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index a2d101ebf7a7..7f6ffbc3e7b2 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2003,6 +2003,21 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
 		  HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
 }
 
+static unsigned int
+hdmi_get_tmdsclock(unsigned int bus_format, unsigned int pixelclock)
+{
+	int color_depth = hdmi_bus_fmt_color_depth(bus_format);
+	unsigned int tmdsclock = pixelclock;
+
+	if (!hdmi_bus_fmt_is_yuv422(bus_format) && color_depth > 8)
+		tmdsclock = (u64)pixelclock * color_depth / 8;
+
+	if (hdmi_bus_fmt_is_yuv420(bus_format))
+		tmdsclock /= 2;
+
+	return tmdsclock;
+}
+
 static void hdmi_av_composer(struct dw_hdmi *hdmi,
 			     const struct drm_display_info *display,
 			     const struct drm_display_mode *mode)
@@ -2014,29 +2029,11 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	unsigned int vdisplay, hdisplay;
 
 	vmode->mpixelclock = mode->clock * 1000;
+	vmode->mtmdsclock =
+		hdmi_get_tmdsclock(hdmi->hdmi_data.enc_out_bus_format,
+				   vmode->mpixelclock);
 
 	dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
-	vmode->mtmdsclock = vmode->mpixelclock;
-
-	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
-		switch (hdmi_bus_fmt_color_depth(
-				hdmi->hdmi_data.enc_out_bus_format)) {
-		case 16:
-			vmode->mtmdsclock = vmode->mpixelclock * 2;
-			break;
-		case 12:
-			vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
-			break;
-		case 10:
-			vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
-			break;
-		}
-	}
-
-	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
-		vmode->mtmdsclock /= 2;
-
 	dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
 
 	/* Set up HDMI_FC_INVIDCONF */
@@ -2663,8 +2660,21 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
  * - MEDIA_BUS_FMT_RGB888_1X24,
  */
 
-/* Can return a maximum of 11 possible output formats for a mode/connector */
-#define MAX_OUTPUT_SEL_FORMATS	11
+/* Can return a maximum of 15 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS	15
+
+static bool is_tmds_allowed(struct drm_display_info *info,
+			    struct drm_display_mode *mode,
+			    u32 bus_format)
+{
+	unsigned long tmdsclock = hdmi_get_tmdsclock(bus_format, mode->clock);
+	int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+
+	if (max_tmds_clock >= tmdsclock)
+		return true;
+
+	return false;
+}
 
 static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
 					struct drm_bridge_state *bridge_state,
@@ -2676,8 +2686,6 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
 	struct drm_display_info *info = &conn->display_info;
 	struct drm_display_mode *mode = &crtc_state->mode;
 	u8 max_bpc = conn_state->max_requested_bpc;
-	bool is_hdmi2_sink = info->hdmi.scdc.supported ||
-			     (info->color_formats & DRM_COLOR_FORMAT_YCBCR420);
 	u32 *output_fmts;
 	unsigned int i = 0;
 
@@ -2701,25 +2709,28 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
 	 * If the current mode enforces 4:2:0, force the output but format
 	 * to 4:2:0 and do not add the YUV422/444/RGB formats
 	 */
-	if (conn->ycbcr_420_allowed &&
-	    (drm_mode_is_420_only(info, mode) ||
-	     (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
+	if (conn->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+	    (info->color_formats & DRM_COLOR_FORMAT_YCBCR420)) {
 
 		/* Order bus formats from 16bit to 8bit if supported */
 		if (max_bpc >= 16 && info->bpc == 16 &&
-		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY16_0_5X48))
 			output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
 
 		if (max_bpc >= 12 && info->bpc >= 12 &&
-		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY12_0_5X36))
 			output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
 
 		if (max_bpc >= 10 && info->bpc >= 10 &&
-		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+		    (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY10_0_5X30))
 			output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
 
 		/* Default 8bit fallback */
-		output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+		if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY8_0_5X24))
+			output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
 
 		if (drm_mode_is_420_only(info, mode))
 			*num_output_fmts = i;
@@ -2732,40 +2744,51 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
 	 */
 
 	/* Default 8bit RGB fallback */
-	output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+	if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB888_1X24))
+		output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
 
 	if (max_bpc >= 16 && info->bpc == 16) {
-		if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+		if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV16_1X48))
 			output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
 
-		output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+		if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB161616_1X48))
+			output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
 	}
 
 	if (max_bpc >= 12 && info->bpc >= 12) {
-		if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+		if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY12_1X24))
 			output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
 
-		if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+		if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV12_1X36))
 			output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
 
-		output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+		if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB121212_1X36))
+			output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
 	}
 
 	if (max_bpc >= 10 && info->bpc >= 10) {
-		if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+		if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY10_1X20))
 			output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
 
-		if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+		if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+		    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV10_1X30))
 			output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
 
-		output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+		if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB101010_1X30))
+			output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
 	}
 
-	if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
+	if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR422) &&
+	    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY8_1X16))
 		output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
 
-	if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
+	if ((info->color_formats & DRM_COLOR_FORMAT_YCBCR444) &&
+	    is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV8_1X24))
 		output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
 
 	*num_output_fmts = i;
 
@@ -2946,11 +2969,20 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
 	struct dw_hdmi *hdmi = bridge->driver_private;
 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
 	enum drm_mode_status mode_status = MODE_OK;
+	int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+	int clock = mode->clock;
 
 	/* We don't support double-clocked modes */
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		return MODE_BAD;
 
+	if (pdata->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+	    (info->color_formats & DRM_COLOR_FORMAT_YCBCR420))
+		clock /= 2;
+
+	if (clock > max_tmds_clock)
+		return MODE_CLOCK_HIGH;
+
 	if (pdata->mode_valid)
 		mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
 						mode);

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +0000
Subject: [PATCH] WIP: drm/rockchip: dw_hdmi: add 10-bit rgb bus format

---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 42 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |  1 +
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index d6d8f3335813..89424c5bc24a 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -84,6 +84,8 @@ struct rockchip_hdmi {
 	struct phy *phy;
 };
 
+#define to_crtc_state(x)	container_of(x, struct drm_crtc_state, x)
+
 static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_bridge *bridge)
 {
 	return container_of(bridge, struct rockchip_hdmi, bridge);
@@ -341,6 +343,11 @@ dw_hdmi_rockchip_bridge_mode_set(struct drm_bridge *bridge,
 				 const struct drm_display_mode *adjusted_mode)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+	struct drm_crtc_state *crtc_state = to_crtc_state(adjusted_mode);
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	if (hdmi->phy)
+		phy_set_bus_width(hdmi->phy, s->bus_width);
 
 	clk_set_rate(hdmi->ref_clk, adjusted_mode->clock * 1000);
 }
@@ -381,6 +388,17 @@ static bool is_rgb(u32 format)
 {
 	switch (format) {
 	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_10bit(u32 format)
+{
+	switch (format) {
+	case MEDIA_BUS_FMT_RGB101010_1X30:
 		return true;
 	default:
 		return false;
@@ -394,9 +412,24 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
 				     struct drm_connector_state *conn_state)
 {
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct drm_atomic_state *state = bridge_state->base.state;
+	struct drm_crtc_state *old_crtc_state;
+	struct rockchip_crtc_state *old_state;
+	u32 format = bridge_state->output_bus_cfg.format;
 
 	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+	s->output_bpc = 10;
+	s->bus_format = format;
+	s->bus_width = is_10bit(format) ? 10 : 8;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
+	if (old_crtc_state && !crtc_state->mode_changed) {
+		old_state = to_rockchip_crtc_state(old_crtc_state);
+		if (s->bus_format != old_state->bus_format ||
+		    s->bus_width != old_state->bus_width)
+			crtc_state->mode_changed = true;
+	}
 
 	return 0;
 }
@@ -408,10 +441,19 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
 					u32 output_fmt,
 					unsigned int *num_input_fmts)
 {
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+	struct drm_encoder *encoder = bridge->encoder;
 	u32 *input_fmt;
+	bool has_10bit = true;
 
 	*num_input_fmts = 0;
 
+	if (drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder))
+		has_10bit = false;
+
+	if (!has_10bit && is_10bit(output_fmt))
+		return NULL;
+
 	if (!is_rgb(output_fmt))
 		return NULL;
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 1641440837af..381e5ccab5f3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -34,6 +34,7 @@ struct rockchip_crtc_state {
 	u32 bus_format;
 	u32 bus_flags;
 	int color_space;
+	int bus_width;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 8 Dec 2019 23:42:44 +0000
Subject: [PATCH] WIP: drm: dw-hdmi: add content type connector property

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 7f6ffbc3e7b2..ae4c49e84470 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1790,6 +1790,7 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
 			    const struct drm_connector *connector,
 			    const struct drm_display_mode *mode)
 {
+	const struct drm_connector_state *conn_state = connector->state;
 	struct hdmi_avi_infoframe frame;
 	u8 val;
 
@@ -1847,6 +1848,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
 			HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
 	}
 
+	drm_hdmi_avi_infoframe_content_type(&frame, conn_state);
+
 	/*
 	 * The Designware IP uses a different byte format from standard
 	 * AVI info frames, though generally the bits are in the correct
@@ -2551,7 +2554,8 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
 	if (!crtc)
 		return 0;
 
-	if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
+	if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state) ||
+	    old_state->content_type != new_state->content_type) {
 		crtc_state = drm_atomic_get_crtc_state(state, crtc);
 		if (IS_ERR(crtc_state))
 			return PTR_ERR(crtc_state);
@@ -2619,6 +2623,8 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
 
 	drm_connector_attach_max_bpc_property(connector, 8, 16);
 
+	drm_connector_attach_content_type_property(connector);
+
 	if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
 		drm_connector_attach_hdr_output_metadata_property(connector);
 

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/rockchip: add yuv444 support

---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 29 ++++++++++++++++++++-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |  6 +++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 19 ++++++++++++++
 4 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 89424c5bc24a..05de2052d95d 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -68,6 +68,7 @@ struct rockchip_hdmi_chip_data {
 	u32	lcdsel_big;
 	u32	lcdsel_lit;
 	int	max_tmds_clock;
+	bool	ycbcr_444_allowed;
 };
 
 struct rockchip_hdmi {
@@ -395,10 +396,22 @@ static bool is_rgb(u32 format)
 	}
 }
 
+static bool is_yuv444(u32 format)
+{
+	switch (format) {
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static bool is_10bit(u32 format)
 {
 	switch (format) {
 	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
 		return true;
 	default:
 		return false;
@@ -415,12 +428,22 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
 	struct drm_atomic_state *state = bridge_state->base.state;
 	struct drm_crtc_state *old_crtc_state;
 	struct rockchip_crtc_state *old_state;
+	struct drm_bridge *next_bridge;
+	struct drm_bridge_state *next_bridge_state;
 	u32 format = bridge_state->output_bus_cfg.format;
 
 	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
 	s->output_bpc = 10;
 	s->bus_format = format;
+
+	next_bridge = drm_bridge_get_next_bridge(bridge);
+	if (next_bridge) {
+		next_bridge_state = drm_atomic_get_new_bridge_state(state,
+								next_bridge);
+		format = next_bridge_state->output_bus_cfg.format;
+	}
+
 	s->bus_width = is_10bit(format) ? 10 : 8;
 
 	old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
@@ -454,7 +477,10 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
 	if (!has_10bit && is_10bit(output_fmt))
 		return NULL;
 
-	if (!is_rgb(output_fmt))
+	if (is_yuv444(output_fmt)) {
+		if (!hdmi->chip_data->ycbcr_444_allowed)
+			return NULL;
+	} else if (!is_rgb(output_fmt))
 		return NULL;
 
 	input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
@@ -604,6 +630,7 @@ static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = {
 static struct rockchip_hdmi_chip_data rk3328_chip_data = {
 	.lcdsel_grf_reg = -1,
 	.max_tmds_clock = 594000,
+	.ycbcr_444_allowed = true,
 };
 
 static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index eadf1b0f1704..0e4eca0d5121 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -347,6 +347,17 @@ static int vop_convert_afbc_format(uint32_t format)
 	return -EINVAL;
 }
 
+static bool is_yuv_output(uint32_t bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
 				  uint32_t dst, bool is_horizontal,
 				  int vsu_mode, int *vskiplines)
@@ -1455,6 +1466,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 	u16 vact_end = vact_st + vdisplay;
 	uint32_t pin_pol, val;
 	int dither_bpc = s->output_bpc ? s->output_bpc : 10;
+	bool yuv_output = is_yuv_output(s->bus_format);
 	int ret;
 
 	if (old_state && old_state->self_refresh_active) {
@@ -1520,6 +1532,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 	    !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
 		s->output_mode = ROCKCHIP_OUT_MODE_P888;
 
+	VOP_REG_SET(vop, common, dsp_data_swap, yuv_output ? 2 : 0);
+
 	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
 		VOP_REG_SET(vop, common, pre_dither_down, 1);
 	else
@@ -1535,6 +1549,21 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	VOP_REG_SET(vop, common, out_mode, s->output_mode);
 
+	VOP_REG_SET(vop, common, overlay_mode, yuv_output);
+	VOP_REG_SET(vop, common, dsp_out_yuv, yuv_output);
+
+	/*
+	 * Background color is 10bit depth if vop version >= 3.5
+	 */
+	if (!yuv_output)
+		val = 0;
+	else if (VOP_MAJOR(vop_data->version) == 3 &&
+		 VOP_MINOR(vop_data->version) >= 5)
+		val = 0x20010200;
+	else
+		val = 0x801080;
+	VOP_REG_SET(vop, common, dsp_background, val);
+
 	VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
 	val = hact_st << 16;
 	val |= hact_end;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ca4e2b7415fe..47ad74ef1afb 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -119,10 +119,16 @@ struct vop_common {
 	struct vop_reg mmu_en;
 	struct vop_reg out_mode;
 	struct vop_reg standby;
+
+	struct vop_reg overlay_mode;
+	struct vop_reg dsp_data_swap;
+	struct vop_reg dsp_out_yuv;
+	struct vop_reg dsp_background;
 };
 
 struct vop_misc {
 	struct vop_reg global_regdone_en;
+	struct vop_reg win_channel[4];
 };
 
 struct vop_intr {
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index a2b281e290e0..b16a4c42773c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -705,6 +705,11 @@ static const struct vop_common rk3288_common = {
 	.dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
 	.out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
 	.cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0),
+
+	.overlay_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 16),
+	.dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
+	.dsp_out_yuv = VOP_REG(RK3288_POST_SCL_CTRL, 0x1, 2),
+	.dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
 };
 
 /*
@@ -926,6 +931,11 @@ static const struct vop_common rk3399_common = {
 	.dsp_blank = VOP_REG(RK3399_DSP_CTRL0, 0x3, 18),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.cfg_done = VOP_REG_SYNC(RK3399_REG_CFG_DONE, 0x1, 0),
+
+	.overlay_mode = VOP_REG(RK3399_SYS_CTRL, 0x1, 16),
+	.dsp_data_swap = VOP_REG(RK3399_DSP_CTRL0, 0x1f, 12),
+	.dsp_out_yuv = VOP_REG(RK3288_POST_SCL_CTRL, 0x1, 2),
+	.dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
 };
 
 static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win01_data = {
@@ -1096,6 +1106,10 @@ static const struct vop_output rk3328_output = {
 
 static const struct vop_misc rk3328_misc = {
 	.global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11),
+
+	.win_channel[0] = VOP_REG(RK3328_WIN0_CTRL2, 0xff, 0),
+	.win_channel[1] = VOP_REG(RK3328_WIN1_CTRL2, 0xff, 0),
+	.win_channel[2] = VOP_REG(RK3328_WIN2_CTRL2, 0xff, 0),
 };
 
 static const struct vop_common rk3328_common = {
@@ -1108,6 +1122,11 @@ static const struct vop_common rk3328_common = {
 	.dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18),
 	.out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0),
 	.cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0),
+
+	.overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16),
+	.dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12),
+	.dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2),
+	.dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0),
 };
 
 static const struct vop_intr rk3328_vop_intr = {

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/rockchip: add yuv420 support

---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 22 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 19 +++++++++++++++++-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 10 ++++++----
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  2 ++
 4 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 05de2052d95d..cb201612199f 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -407,9 +407,21 @@ static bool is_yuv444(u32 format)
 	}
 }
 
+static bool is_yuv420(u32 format)
+{
+	switch (format) {
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static bool is_10bit(u32 format)
 {
 	switch (format) {
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
 	case MEDIA_BUS_FMT_RGB101010_1X30:
 	case MEDIA_BUS_FMT_YUV10_1X30:
 		return true;
@@ -446,6 +458,11 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
 
 	s->bus_width = is_10bit(format) ? 10 : 8;
 
+	if (is_yuv420(format)) {
+		s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+		s->bus_width /= 2;
+	}
+
 	old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
 	if (old_crtc_state && !crtc_state->mode_changed) {
 		old_state = to_rockchip_crtc_state(old_crtc_state);
@@ -466,6 +483,7 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
 	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_connector *connector = conn_state->connector;
 	u32 *input_fmt;
 	bool has_10bit = true;
 
@@ -480,6 +498,9 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
 	if (is_yuv444(output_fmt)) {
 		if (!hdmi->chip_data->ycbcr_444_allowed)
 			return NULL;
+	} else if (is_yuv420(output_fmt)) {
+		if (!connector->ycbcr_420_allowed)
+			return NULL;
 	} else if (!is_rgb(output_fmt))
 		return NULL;
 
@@ -640,6 +661,7 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
 	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
 	.use_drm_infoframe = true,
+	.ycbcr_420_allowed = true,
 };
 
 static struct rockchip_hdmi_chip_data rk3399_chip_data = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 0e4eca0d5121..e50f71ad3ceb 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -10,6 +10,7 @@
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/log2.h>
+#include <linux/media-bus-format.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
@@ -348,6 +349,19 @@ static int vop_convert_afbc_format(uint32_t format)
 }
 
 static bool is_yuv_output(uint32_t bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool bus_fmt_has_uv_swapped(uint32_t bus_format)
 {
 	switch (bus_format) {
 	case MEDIA_BUS_FMT_YUV8_1X24:
@@ -1532,7 +1546,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 	    !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
 		s->output_mode = ROCKCHIP_OUT_MODE_P888;
 
-	VOP_REG_SET(vop, common, dsp_data_swap, yuv_output ? 2 : 0);
+	VOP_REG_SET(vop, common, dsp_data_swap, bus_fmt_has_uv_swapped(s->bus_format) ? 2 : 0);
 
 	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
 		VOP_REG_SET(vop, common, pre_dither_down, 1);
@@ -1549,6 +1563,9 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	VOP_REG_SET(vop, common, out_mode, s->output_mode);
 
+	VOP_REG_SET(vop, common, dclk_ddr,
+		    s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
+
 	VOP_REG_SET(vop, common, overlay_mode, yuv_output);
 	VOP_REG_SET(vop, common, dsp_out_yuv, yuv_output);
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 47ad74ef1afb..94a615dca672 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -121,6 +121,7 @@ struct vop_common {
 	struct vop_reg standby;
 
 	struct vop_reg overlay_mode;
+	struct vop_reg dclk_ddr;
 	struct vop_reg dsp_data_swap;
 	struct vop_reg dsp_out_yuv;
 	struct vop_reg dsp_background;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index b16a4c42773c..5463b04240f7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -707,6 +707,7 @@ static const struct vop_common rk3288_common = {
 	.cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0),
 
 	.overlay_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 16),
+	.dclk_ddr = VOP_REG(RK3288_DSP_CTRL0, 0x1, 8),
 	.dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
 	.dsp_out_yuv = VOP_REG(RK3288_POST_SCL_CTRL, 0x1, 2),
 	.dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
@@ -1124,6 +1125,7 @@ static const struct vop_common rk3328_common = {
 	.cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0),
 
 	.overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16),
+	.dclk_ddr = VOP_REG(RK3328_DSP_CTRL0, 0x1, 8),
 	.dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12),
 	.dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2),
 	.dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0),

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sat, 15 Aug 2020 23:20:34 +0200
Subject: [PATCH] drm/rockchip: enable ycbcr_420_allowed and ycbcr_444_allowed
 for RK3228

---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cb201612199f..8627f6826bfe 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -616,6 +616,7 @@ static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
 static struct rockchip_hdmi_chip_data rk3228_chip_data = {
 	.lcdsel_grf_reg = -1,
 	.max_tmds_clock = 594000,
+	.ycbcr_444_allowed = true,
 };
 
 static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
@@ -624,6 +625,7 @@ static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
 	.phy_ops = &rk3228_hdmi_phy_ops,
 	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
+	.ycbcr_420_allowed = true,
 };
 
 static struct rockchip_hdmi_chip_data rk3288_chip_data = {

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 18 Nov 2017 11:09:39 +0100
Subject: [PATCH] rockchip: vop: force skip lines if image too big

---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index e50f71ad3ceb..ef0a078c22f4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -958,6 +958,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 	int format;
 	int is_yuv = fb->format->is_yuv;
 	int i;
+	int skiplines = 0;
 
 	/*
 	 * can't update plane when vop is disabled.
@@ -976,8 +977,14 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 	obj = fb->obj[0];
 	rk_obj = to_rockchip_obj(obj);
 
+	/*
+	 * Force skip lines when image is yuv and 3840 width,
+	 * fixes a "jumping" green lines issue on RK3328.
+	 */
 	actual_w = drm_rect_width(src) >> 16;
-	actual_h = drm_rect_height(src) >> 16;
+	if (actual_w == 3840 && is_yuv)
+		skiplines = 1;
+	actual_h = drm_rect_height(src) >> (16 + skiplines);
 	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
 
 	dsp_info = (drm_rect_height(dest) - 1) << 16;
@@ -1019,7 +1026,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 
 	VOP_WIN_SET(vop, win, format, format);
 	VOP_WIN_SET(vop, win, fmt_10, is_fmt_10(fb->format->format));
-	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
+	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4 >> skiplines));
 	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
 	VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
 	VOP_WIN_SET(vop, win, y_mir_en,
@@ -1040,7 +1047,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
 
 		dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
-		VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
+		VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4 >> skiplines));
 		VOP_WIN_SET(vop, win, uv_mst, dma_addr);
 
 		for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sat, 10 Apr 2021 16:54:26 +0200
Subject: [PATCH] drm/bridge: dw-hdmi: fix RGB to YUV color space conversion

We are currently providing color space conversion coefficents
for RGB to YUV conversion for full range to full range.
This is wrong, since we are hardcoding YCC quantization range
limited in the AVI infoframe (which is correct according to
HDMI specs). This results in to dark colors if this conversion
is used.
I verfied this by setting YCC quantization range to full in
AVI infoframe which resulted in correct colors. Doing this,
however, will be ignored by some (most) sinks.

This patch fixes this, by providing CSC coefficents which
convert RGB full range to YUV limited range for both BT601
and BT709 colorspaces.

Fixes: 9aaf880ed4ee ("imx-drm: Add mx6 hdmi transmitter support")
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index ae4c49e84470..92e621f2714f 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -82,15 +82,15 @@ static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
 };
 
 static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
-	{ 0x2591, 0x1322, 0x074b, 0x0000 },
-	{ 0x6535, 0x2000, 0x7acc, 0x0200 },
-	{ 0x6acd, 0x7534, 0x2000, 0x0200 }
+	{ 0x2040, 0x1080, 0x0640, 0x0040 },
+	{ 0xe880, 0x1c00, 0xfb80, 0x0200 },
+	{ 0xed80, 0xf680, 0x1c00, 0x0200 }
 };
 
 static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
-	{ 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
-	{ 0x62f0, 0x2000, 0x7d11, 0x0200 },
-	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
+	{ 0x2740, 0x0bc0, 0x0400, 0x0040 },
+	{ 0xe680, 0x1c00, 0xfd80, 0x0200 },
+	{ 0xea40, 0xf980, 0x1c00, 0x0200 }
 };
 
 static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Tue, 1 Oct 2019 20:52:42 +0000
Subject: [PATCH] media: cec-adap: add debounce support when setting an invalid
 phys addr

When EDID is refreshed, HDMI cable is unplugged/replugged or
an AVR is power cycled the CEC phys addr gets invalidated.

This can cause some disruption of CEC communication when
adapter is being reconfigured.

Add a debounce_ms module option that can be used to debounce setting
an invalid phys addr. Default is not to use debouncing.

Using a configured debounce_ms of e.g. 5000 ms, cec reconfiguring
could be avoided when AVR was power cycled on my setup.

Power off AVR (default cec.debounce_ms=0):
[  101.536866] cec-dw_hdmi: new physical address f.f.f.f
[  102.495686] cec-dw_hdmi: new physical address 2.1.0.0
[  102.495913] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
[  102.628574] cec-dw_hdmi: config: la 1 pa 2.1.0.0
[  105.130115] cec-dw_hdmi: new physical address f.f.f.f
[  106.979705] cec-dw_hdmi: new physical address 2.1.0.0
[  106.979872] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
[  107.112399] cec-dw_hdmi: config: la 1 pa 2.1.0.0
[  108.979408] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
[  109.205386] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11

Power on AVR (default cec.debounce_ms=0):
[  158.398447] cec-dw_hdmi: new physical address f.f.f.f
[  161.977714] cec-dw_hdmi: new physical address 2.1.0.0
[  161.978766] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
[  162.115624] cec-dw_hdmi: config: la 1 pa 2.1.0.0
[  162.402750] cec-dw_hdmi: new physical address f.f.f.f
[  162.403389] cec-dw_hdmi: cec_transmit_msg_fh: adapter is unconfigured
[  162.886757] cec-dw_hdmi: new physical address 2.1.0.0
[  162.886964] cec-dw_hdmi: physical address: 2.1.0.0, claim 1 logical addresses
[  163.510725] cec-dw_hdmi: config: la 1 pa 2.1.0.0
[  173.034200] cec-dw_hdmi: message 10 89 02 05 timed out

Power off AVR (cec.debounce_ms=5000):
[  251.720471] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
[  251.922432] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11

Power on AVR (cec.debounce_ms=5000):
[  291.154262] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 5
[  291.296199] cec-dw_hdmi: reported physical address 2.0.0.0 for logical address 11

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/media/cec/core/cec-adap.c |  9 ++++++++-
 drivers/media/cec/core/cec-core.c | 18 ++++++++++++++++++
 drivers/media/cec/core/cec-priv.h |  1 +
 include/media/cec.h               |  2 ++
 4 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 41a79293ee02..542ab1425339 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1674,8 +1674,15 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
 	if (IS_ERR_OR_NULL(adap))
 		return;
 
+	cancel_delayed_work_sync(&adap->debounce_work);
+
 	mutex_lock(&adap->lock);
-	__cec_s_phys_addr(adap, phys_addr, block);
+	if (cec_debounce_ms > 0 && !block && phys_addr == CEC_PHYS_ADDR_INVALID &&
+	    adap->phys_addr != phys_addr)
+		schedule_delayed_work(&adap->debounce_work,
+				      msecs_to_jiffies(cec_debounce_ms));
+	else
+		__cec_s_phys_addr(adap, phys_addr, block);
 	mutex_unlock(&adap->lock);
 }
 EXPORT_SYMBOL_GPL(cec_s_phys_addr);
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index af358e901b5f..bece8c56e5af 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -40,6 +40,10 @@ static bool debug_phys_addr;
 module_param(debug_phys_addr, bool, 0644);
 MODULE_PARM_DESC(debug_phys_addr, "add CEC_CAP_PHYS_ADDR if set");
 
+int cec_debounce_ms;
+module_param_named(debounce_ms, cec_debounce_ms, int, 0644);
+MODULE_PARM_DESC(debounce_ms, "debounce invalid phys addr");
+
 static dev_t cec_dev_t;
 
 /* Active devices */
@@ -188,6 +192,8 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
 
 	mutex_unlock(&devnode->lock);
 
+	cancel_delayed_work_sync(&adap->debounce_work);
+
 	mutex_lock(&adap->lock);
 	__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
 	__cec_s_log_addrs(adap, NULL, false);
@@ -246,6 +252,17 @@ static const struct file_operations cec_error_inj_fops = {
 };
 #endif
 
+static void cec_s_phys_addr_debounce(struct work_struct *work)
+{
+	struct delayed_work *delayed_work = to_delayed_work(work);
+	struct cec_adapter *adap =
+		container_of(delayed_work, struct cec_adapter, debounce_work);
+
+	mutex_lock(&adap->lock);
+	__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
+	mutex_unlock(&adap->lock);
+}
+
 struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
 					 void *priv, const char *name, u32 caps,
 					 u8 available_las)
@@ -283,6 +300,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
 	INIT_LIST_HEAD(&adap->transmit_queue);
 	INIT_LIST_HEAD(&adap->wait_queue);
 	init_waitqueue_head(&adap->kthread_waitq);
+	INIT_DELAYED_WORK(&adap->debounce_work, cec_s_phys_addr_debounce);
 
 	/* adap->devnode initialization */
 	INIT_LIST_HEAD(&adap->devnode.fhs);
diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h
index b78df931aa74..ebbea63ea9de 100644
--- a/drivers/media/cec/core/cec-priv.h
+++ b/drivers/media/cec/core/cec-priv.h
@@ -37,6 +37,7 @@ static inline bool msg_is_raw(const struct cec_msg *msg)
 
 /* cec-core.c */
 extern int cec_debug;
+extern int cec_debounce_ms;
 
 /* cec-adap.c */
 int cec_monitor_all_cnt_inc(struct cec_adapter *adap);
diff --git a/include/media/cec.h b/include/media/cec.h
index abee41ae02d0..544eedb5d671 100644
--- a/include/media/cec.h
+++ b/include/media/cec.h
@@ -236,6 +236,8 @@ struct cec_adapter {
 	struct task_struct *kthread;
 	wait_queue_head_t kthread_waitq;
 
+	struct delayed_work debounce_work;
+
 	const struct cec_adap_ops *ops;
 	void *priv;
 	u32 capabilities;

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Fri, 22 Oct 2021 11:17:30 +0200
Subject: [PATCH] WIP: drm/bridge: synopsys: Fix CEC not working after
 power-cyclying

This fixes standby -> power-on on Rockchip platform for, at least,
RK3288/RK3328/RK3399 where CEC wasn't working after powering on again.
It might differ for other phy implementations:
The whole HPD-detection part shoud be reworked and we should in general
avoid to rely in RX_SENSE phy status (at least for HDMI), since it differs
depending on sink's implementation.

Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 92e621f2714f..7551e3ab77d6 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3179,12 +3179,6 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 				       phy_stat & HDMI_PHY_HPD,
 				       phy_stat & HDMI_PHY_RX_SENSE);
 
-		if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) {
-			mutex_lock(&hdmi->cec_notifier_mutex);
-			cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
-			mutex_unlock(&hdmi->cec_notifier_mutex);
-		}
-
 		if (phy_stat & HDMI_PHY_HPD)
 			status = connector_status_connected;
 
@@ -3201,6 +3195,14 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 			drm_helper_hpd_irq_event(hdmi->bridge.dev);
 			drm_bridge_hpd_notify(&hdmi->bridge, status);
 		}
+
+		if (status == connector_status_disconnected &&
+		    (phy_stat & HDMI_PHY_RX_SENSE) &&
+		    (phy_int_pol & HDMI_PHY_RX_SENSE)) {
+			mutex_lock(&hdmi->cec_notifier_mutex);
+			cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
+			mutex_unlock(&hdmi->cec_notifier_mutex);
+		}
 	}
 
 	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 9 Oct 2020 15:24:53 +0000
Subject: [PATCH] drm/rockchip: vop: create planes in window order

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 36 +++------------------
 1 file changed, 4 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 49619f794061..9915bf124374 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -2023,19 +2023,10 @@ static int vop_create_crtc(struct vop *vop)
 	int ret;
 	int i;
 
-	/*
-	 * Create drm_plane for primary and cursor planes first, since we need
-	 * to pass them to drm_crtc_init_with_planes, which sets the
-	 * "possible_crtcs" to the newly initialized crtc.
-	 */
 	for (i = 0; i < vop_data->win_size; i++) {
 		struct vop_win *vop_win = &vop->win[i];
 		const struct vop_win_data *win_data = vop_win->data;
 
-		if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
-		    win_data->type != DRM_PLANE_TYPE_CURSOR)
-			continue;
-
 		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
 					       0, &vop_plane_funcs,
 					       win_data->phy->data_formats,
@@ -2068,32 +2059,13 @@ static int vop_create_crtc(struct vop *vop)
 		drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);
 	}
 
-	/*
-	 * Create drm_planes for overlay windows with possible_crtcs restricted
-	 * to the newly created crtc.
-	 */
+	/* Set possible_crtcs to the newly created crtc for overlay windows */
 	for (i = 0; i < vop_data->win_size; i++) {
 		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = vop_win->data;
-		unsigned long possible_crtcs = drm_crtc_mask(crtc);
-
-		if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
-			continue;
 
-		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-					       possible_crtcs,
-					       &vop_plane_funcs,
-					       win_data->phy->data_formats,
-					       win_data->phy->nformats,
-					       win_data->phy->format_modifiers,
-					       win_data->type, NULL);
-		if (ret) {
-			DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
-				      ret);
-			goto err_cleanup_crtc;
-		}
-		drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
-		vop_plane_add_properties(&vop_win->base, win_data);
+		plane = &vop_win->base;
+		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+			plane->possible_crtcs = drm_crtc_mask(crtc);
 	}
 
 	port = of_get_child_by_name(dev->of_node, "port");

