From 588d403becca3bbf4e6e32c9bab9d15a6967c9f7 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +0000
Subject: [PATCH 12/59] WIP/1000: 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 c7841c7d5daf..a50218412fb0 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>
@@ -73,6 +74,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;
 	const struct dw_hdmi_plat_data *plat_data;
@@ -83,11 +85,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[] = {
@@ -343,31 +343,21 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_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;
 
@@ -394,10 +384,21 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 	dev_dbg(hdmi->dev, "vop %s output to hdmi\n", 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);
 
@@ -407,12 +408,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,
@@ -616,6 +643,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;
@@ -685,20 +713,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)) {
@@ -706,8 +735,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);
 }
 
-- 
2.34.1

