From cf6bdd17cce3027d0e77ffa46ff79fb6e5005e15 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH 23/59] WIP/1000: 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 |  1 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  2 ++
 4 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 07a8796b1f47..a01586ad44a2 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -414,9 +414,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;
@@ -453,6 +465,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);
@@ -473,6 +490,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;
 
@@ -487,6 +505,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;
 
@@ -652,6 +673,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 784599231a6a..8d8a7e748def 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/overflow.h>
@@ -345,6 +346,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:
@@ -1459,7 +1473,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);
@@ -1476,6 +1490,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 1fa0ecdf734c..7e53eb3c1372 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -127,6 +127,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 234e03edae12..c72ed63c3d1d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -729,6 +729,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),
@@ -1174,6 +1175,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),
-- 
2.34.1

