From d7b2340cd84ba5ce3494e8acbf52191e1d85bbe8 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Wed, 20 Jan 2021 22:15:36 +0100
Subject: [PATCH 22/23] wip h3/h5 cvbs

---
 arch/arm/boot/dts/allwinner/sun8i-h3.dtsi    | 22 +++++
 arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi | 95 +++++++++++++++++++-
 arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 22 +++++
 drivers/clk/sunxi-ng/ccu-sun8i-h3.c          | 14 ++-
 drivers/gpu/drm/sun4i/sun4i_tv.c             | 35 +++++++-
 drivers/gpu/drm/sun4i/sun8i_mixer.c          | 38 ++++++++
 drivers/gpu/drm/sun4i/sun8i_mixer.h          |  5 +-
 7 files changed, 223 insertions(+), 8 deletions(-)

diff --git a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
index eac2349a2380..411640fca509 100644
--- a/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/allwinner/sun8i-h3.dtsi
@@ -239,6 +239,20 @@ ths: thermal-sensor@1c25000 {
 			nvmem-cell-names = "calibration";
 			#thermal-sensor-cells = <0>;
 		};
+
+		tve: tv-encoder@1e00000 {
+			compatible = "allwinner,sun8i-h3-tv-encoder";
+			reg = <0x01e00000 0x1000>;
+			clocks = <&ccu CLK_BUS_TVE>;
+			resets = <&ccu RST_BUS_TVE>;
+			status = "disabled";
+
+			port {
+				tve_in_tcon1: endpoint {
+					remote-endpoint = <&tcon1_out_tve>;
+				};
+			};
+		};
 	};
 
 	thermal-zones {
@@ -286,6 +300,10 @@ &mbus {
 	compatible = "allwinner,sun8i-h3-mbus";
 };
 
+&mixer1 {
+	resets = <&display_clocks RST_WB>;
+};
+
 &mmc0 {
 	compatible = "allwinner,sun7i-a20-mmc";
 	clocks = <&ccu CLK_BUS_MMC0>,
@@ -333,3 +351,7 @@ &rtc {
 &sid {
 	compatible = "allwinner,sun8i-h3-sid";
 };
+
+&tcon1_out_tve {
+	remote-endpoint = <&tve_in_tcon1>;
+};
diff --git a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
index ade1cd50e445..dc2ae4270347 100644
--- a/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/allwinner/sunxi-h3-h5.dtsi
@@ -102,7 +102,7 @@ osc32k: osc32k_clk {
 
 	de: display-engine {
 		compatible = "allwinner,sun8i-h3-display-engine";
-		allwinner,pipelines = <&mixer0>;
+		allwinner,pipelines = <&mixer0>, <&mixer1>;
 		status = "disabled";
 	};
 
@@ -139,11 +139,50 @@ ports {
 				#size-cells = <0>;
 
 				mixer0_out: port@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
 					reg = <1>;
 
-					mixer0_out_tcon0: endpoint {
+					mixer0_out_tcon0: endpoint@0 {
+						reg = <0>;
 						remote-endpoint = <&tcon0_in_mixer0>;
 					};
+
+					mixer0_out_tcon1: endpoint@1 {
+						reg = <1>;
+						remote-endpoint = <&tcon1_in_mixer0>;
+					};
+				};
+			};
+		};
+
+		mixer1: mixer@1200000 {
+			compatible = "allwinner,sun8i-h3-de2-mixer-1";
+			reg = <0x01200000 0x100000>;
+			clocks = <&display_clocks CLK_BUS_MIXER1>,
+				 <&display_clocks CLK_MIXER1>;
+			clock-names = "bus",
+				      "mod";
+			/* reset is added by SoC dtsi */
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				mixer1_out: port@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+
+					mixer1_out_tcon0: endpoint@0 {
+						reg = <0>;
+						remote-endpoint = <&tcon0_in_mixer1>;
+					};
+
+					mixer1_out_tcon1: endpoint@1 {
+						reg = <1>;
+						remote-endpoint = <&tcon1_in_mixer1>;
+					};
 				};
 			};
 		};
@@ -172,11 +211,19 @@ ports {
 				#size-cells = <0>;
 
 				tcon0_in: port@0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
 					reg = <0>;
 
-					tcon0_in_mixer0: endpoint {
+					tcon0_in_mixer0: endpoint@0 {
+						reg = <0>;
 						remote-endpoint = <&mixer0_out_tcon0>;
 					};
+
+					tcon0_in_mixer1: endpoint@1 {
+						reg = <1>;
+						remote-endpoint = <&mixer1_out_tcon0>;
+					};
 				};
 
 				tcon0_out: port@1 {
@@ -192,6 +239,48 @@ tcon0_out_hdmi: endpoint@1 {
 			};
 		};
 
+		tcon1: lcd-controller@1c0d000 {
+			compatible = "allwinner,sun8i-h3-tcon-tv",
+				     "allwinner,sun8i-a83t-tcon-tv";
+			reg = <0x01c0d000 0x1000>;
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_TCON1>, <&ccu CLK_TVE>;
+			clock-names = "ahb", "tcon-ch1";
+			resets = <&ccu RST_BUS_TCON1>;
+			reset-names = "lcd";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				tcon1_in: port@0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+
+					tcon1_in_mixer0: endpoint@0 {
+						reg = <0>;
+						remote-endpoint = <&mixer0_out_tcon1>;
+					};
+
+					tcon1_in_mixer1: endpoint@1 {
+						reg = <1>;
+						remote-endpoint = <&mixer1_out_tcon1>;
+					};
+				};
+
+				tcon1_out: port@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+
+					tcon1_out_tve: endpoint@1 {
+						reg = <1>;
+					};
+				};
+			};
+		};
+
 		mmc0: mmc@1c0f000 {
 			/* compatible and clocks are in per SoC .dtsi file */
 			reg = <0x01c0f000 0x1000>;
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
index d3caf27b6a55..31e67b91e4fa 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -184,6 +184,20 @@ ths: thermal-sensor@1c25000 {
 			nvmem-cell-names = "calibration";
 			#thermal-sensor-cells = <1>;
 		};
+
+		tve: tv-encoder@1e40000 {
+			compatible = "allwinner,sun50i-h5-tv-encoder";
+			reg = <0x01e40000 0x1000>;
+			clocks = <&ccu CLK_BUS_TVE>;
+			resets = <&ccu RST_BUS_TVE>;
+			status = "disabled";
+
+			port {
+				tve_in_tcon1: endpoint {
+					remote-endpoint = <&tcon1_out_tve>;
+				};
+			};
+		};
 	};
 
 	thermal-zones {
@@ -237,6 +251,10 @@ &mbus {
 	compatible = "allwinner,sun50i-h5-mbus";
 };
 
+&mixer1 {
+	resets = <&display_clocks RST_MIXER1>;
+};
+
 &mmc0 {
 	compatible = "allwinner,sun50i-h5-mmc",
 		     "allwinner,sun50i-a64-mmc";
@@ -272,3 +290,7 @@ &rtc {
 &sid {
 	compatible = "allwinner,sun50i-h5-sid";
 };
+
+&tcon1_out_tve {
+	remote-endpoint = <&tve_in_tcon1>;
+};
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 74274c17efb3..c4f4ba836408 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -463,8 +463,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents,
 				 CLK_SET_RATE_PARENT);
 
 static const char * const tve_parents[] = { "pll-de", "pll-periph1" };
-static SUNXI_CCU_M_WITH_MUX_GATE(tve_clk, "tve", tve_parents,
-				 0x120, 0, 4, 24, 3, BIT(31), 0);
+struct ccu_div tve_clk = {
+	.enable	= BIT(31),
+	.div	= _SUNXI_CCU_DIV(0, 4),
+	.mux	= _SUNXI_CCU_MUX(24, 3),
+	.fixed_post_div = 16,
+	.common	= {
+		.reg		= 0x120,
+		.features	= CCU_FEATURE_FIXED_POSTDIV,
+		.hw.init	= CLK_HW_INIT_PARENTS("tve", tve_parents,
+						      &ccu_div_ops, 0),
+	},
+};
 
 static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" };
 static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents,
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index ec65d9d59de7..d2235d5a7416 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -10,6 +10,7 @@
 #include <linux/component.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
@@ -159,6 +160,11 @@ struct tv_mode {
 	const struct resync_parameters	*resync_params;
 };
 
+struct sun4i_tv_quirks {
+	unsigned int calibration;
+	unsigned int unknown : 1;
+};
+
 struct sun4i_tv {
 	struct drm_connector	connector;
 	struct drm_encoder	encoder;
@@ -419,7 +425,7 @@ static const struct regmap_config sun4i_tv_regmap_config = {
 	.reg_bits	= 32,
 	.val_bits	= 32,
 	.reg_stride	= 4,
-	.max_register	= SUN4I_TVE_WSS_DATA2_REG,
+	.max_register	= 0x400,
 	.name		= "tv-encoder",
 };
 
@@ -429,13 +435,19 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = data;
 	struct sun4i_drv *drv = drm->dev_private;
+	const struct sun4i_tv_quirks *quirks;
 	struct sun4i_tv *tv;
 	void __iomem *regs;
 	int ret;
 
+	quirks = of_device_get_match_data(dev);
+	if (!quirks)
+		return -EINVAL;
+
 	tv = devm_kzalloc(dev, sizeof(*tv), GFP_KERNEL);
 	if (!tv)
 		return -ENOMEM;
+
 	tv->drv = drv;
 	dev_set_drvdata(dev, tv);
 
@@ -472,6 +484,11 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
 	}
 	clk_prepare_enable(tv->clk);
 
+	if (quirks->calibration)
+		regmap_write(tv->regs, 0x304, quirks->calibration);
+	if (quirks->unknown)
+		regmap_write(tv->regs, 0x30c, 0x00101110);
+
 	drm_encoder_helper_add(&tv->encoder,
 			       &sun4i_tv_helper_funcs);
 	ret = drm_simple_encoder_init(drm, &tv->encoder,
@@ -551,8 +568,22 @@ static void sun4i_tv_remove(struct platform_device *pdev)
 	component_del(&pdev->dev, &sun4i_tv_ops);
 }
 
+static const struct sun4i_tv_quirks a10_quirks = {
+};
+
+static const struct sun4i_tv_quirks h3_quirks = {
+	.calibration = 0x02000c00,
+};
+
+static const struct sun4i_tv_quirks h5_quirks = {
+	.calibration = 0x02850000,
+	.unknown = 1,
+};
+
 static const struct of_device_id sun4i_tv_of_table[] = {
-	{ .compatible = "allwinner,sun4i-a10-tv-encoder" },
+	{ .compatible = "allwinner,sun4i-a10-tv-encoder", .data = &a10_quirks },
+	{ .compatible = "allwinner,sun8i-h3-tv-encoder", .data = &h3_quirks },
+	{ .compatible = "allwinner,sun50i-h5-tv-encoder", .data = &h5_quirks },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_tv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 8e32d0b24ac9..854f5ab76463 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -36,6 +36,12 @@ struct de2_fmt_info {
 	u32	de2_fmt;
 };
 
+static const u32 sun8i_rgb2yuv_coef[12] = {
+	0x00000107, 0x00000204, 0x00000064, 0x00004200,
+	0x00001f68, 0x00001ed6, 0x000001c2, 0x00020200,
+	0x000001c2, 0x00001e87, 0x00001fb7, 0x00020200,
+};
+
 static const struct de2_fmt_info de2_formats[] = {
 	{
 		.drm_fmt = DRM_FORMAT_ARGB8888,
@@ -369,11 +375,30 @@ static u32 *sun8i_mixer_get_supported_fmts(struct sunxi_engine *engine, u32 *num
 	return formats;
 }
 
+static void sun8i_mixer_apply_color_correction(struct sunxi_engine *engine)
+{
+	DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
+
+	regmap_bulk_write(engine->regs, SUN8I_MIXER_DCSC_COEF_REG(0),
+			  sun8i_rgb2yuv_coef, 12);
+	regmap_write(engine->regs, SUN8I_MIXER_DCSC_EN, 1);
+}
+
+static void sun8i_mixer_disable_color_correction(struct sunxi_engine *engine)
+{
+	DRM_DEBUG_DRIVER("Disabling color correction\n");
+
+	/* Disable color correction */
+	regmap_write(engine->regs, SUN8I_MIXER_DCSC_EN, 0);
+}
+
 static const struct sunxi_engine_ops sun8i_engine_ops = {
 	.commit			= sun8i_mixer_commit,
 	.layers_init		= sun8i_layers_init,
 	.mode_set		= sun8i_mixer_mode_set,
 	.get_supported_fmts	= sun8i_mixer_get_supported_fmts,
+	.apply_color_correction		= sun8i_mixer_apply_color_correction,
+	.disable_color_correction	= sun8i_mixer_disable_color_correction,
 };
 
 static const struct regmap_config sun8i_mixer_regmap_config = {
@@ -631,6 +656,15 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
 	.vi_num		= 1,
 };
 
+static const struct sun8i_mixer_cfg sun8i_h3_mixer1_cfg = {
+	.ccsc		= 1,
+	.mod_rate	= 432000000,
+	.scaler_mask	= 0x3,
+	.scanline_yuv	= 2048,
+	.ui_num		= 1,
+	.vi_num		= 1,
+};
+
 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
 	.ccsc		= CCSC_MIXER0_LAYOUT,
 	.mod_rate	= 297000000,
@@ -718,6 +752,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = {
 		.compatible = "allwinner,sun8i-h3-de2-mixer-0",
 		.data = &sun8i_h3_mixer0_cfg,
 	},
+	{
+		.compatible = "allwinner,sun8i-h3-de2-mixer-1",
+		.data = &sun8i_h3_mixer1_cfg,
+	},
 	{
 		.compatible = "allwinner,sun8i-r40-de2-mixer-0",
 		.data = &sun8i_r40_mixer0_cfg,
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 13401643c7bf..5951adb66e69 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -118,6 +118,10 @@
 /* format 20 is packed YVU444 10-bit */
 /* format 21 is packed YUV444 10-bit */
 
+/* The DCSC sub-engine is used to do color space conversation */
+#define SUN8I_MIXER_DCSC_EN			0xb0000
+#define SUN8I_MIXER_DCSC_COEF_REG(x)		(0xb0010 + 0x4 * (x))
+
 /*
  * Sub-engines listed bellow are unused for now. The EN registers are here only
  * to be used to disable these sub-engines.
@@ -128,7 +132,6 @@
 #define SUN8I_MIXER_PEAK_EN			0xa6000
 #define SUN8I_MIXER_ASE_EN			0xa8000
 #define SUN8I_MIXER_FCC_EN			0xaa000
-#define SUN8I_MIXER_DCSC_EN			0xb0000
 
 #define SUN50I_MIXER_FCE_EN			0x70000
 #define SUN50I_MIXER_PEAK_EN			0x70800
-- 
2.42.0

