Blob Blame History Raw
From: Chen-Yu Tsai <wens@csie.org>
Date: Fri, 21 Apr 2017 16:38:54 +0800
Subject: drm/sun4i: tcon: Find matching display backend by device node matching
Git-commit: b317fa3ba11a10eee45c3e64d6396015ff87e8dc
Patch-mainline: v4.13-rc1
References: FATE#326289 FATE#326079 FATE#326049 FATE#322398 FATE#326166

With Allwinner's Display Engine 1.0, each TCON's input is tied to a
specific display backend, and the 2 comprise what is known as a crtc
in DRM KMS land: The layer, framebuffer, and compositing functions are
provided by the backend, while the TCON provides the display timing
signals and vblank interrupts. This 1 to 1 relationship is represented
in the device tree. On some systems there is an intermediate DRC
component.

Pointers to both matching components must be provided when initializing
the crtc. As the backend is always registered before the associated
tcon, we can recursively search upwards through the of_graph to find
the matching backend.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/gpu/drm/sun4i/sun4i_tcon.c |   55 ++++++++++++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 4 deletions(-)

--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -403,6 +403,53 @@ static int sun4i_tcon_init_regmap(struct
 	return 0;
 }
 
+/*
+ * On SoCs with the old display pipeline design (Display Engine 1.0),
+ * the TCON is always tied to just one backend. Hence we can traverse
+ * the of_graph upwards to find the backend our tcon is connected to,
+ * and take its ID as our own.
+ *
+ * We can either identify backends from their compatible strings, which
+ * means maintaining a large list of them. Or, since the backend is
+ * registered and binded before the TCON, we can just go through the
+ * list of registered backends and compare the device node.
+ */
+static struct sun4i_backend *sun4i_tcon_find_backend(struct sun4i_drv *drv,
+						     struct device_node *node)
+{
+	struct device_node *port, *ep, *remote;
+	struct sun4i_backend *backend;
+
+	port = of_graph_get_port_by_id(node, 0);
+	if (!port)
+		return ERR_PTR(-EINVAL);
+
+	for_each_available_child_of_node(port, ep) {
+		remote = of_graph_get_remote_port_parent(ep);
+		if (!remote)
+			continue;
+
+		/* does this node match any registered backends? */
+		list_for_each_entry(backend, &drv->backend_list, list) {
+			if (remote == backend->node) {
+				of_node_put(remote);
+				of_node_put(port);
+				return backend;
+			}
+		}
+
+		/* keep looking through upstream ports */
+		backend = sun4i_tcon_find_backend(drv, remote);
+		if (!IS_ERR(backend)) {
+			of_node_put(remote);
+			of_node_put(port);
+			return backend;
+		}
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
 static int sun4i_tcon_bind(struct device *dev, struct device *master,
 			   void *data)
 {
@@ -412,9 +459,11 @@ static int sun4i_tcon_bind(struct device
 	struct sun4i_tcon *tcon;
 	int ret;
 
-	/* Wait for a backend to be registered */
-	if (list_empty(&drv->backend_list))
+	backend = sun4i_tcon_find_backend(drv, dev->of_node);
+	if (IS_ERR(backend)) {
+		dev_err(dev, "Couldn't find matching backend\n");
 		return -EPROBE_DEFER;
+	}
 
 	tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
 	if (!tcon)
@@ -464,8 +513,6 @@ static int sun4i_tcon_bind(struct device
 		goto err_free_dotclock;
 	}
 
-	backend = list_first_entry(&drv->backend_list,
-				   struct sun4i_backend, list);
 	tcon->crtc = sun4i_crtc_init(drm, backend, tcon);
 	if (IS_ERR(tcon->crtc)) {
 		dev_err(dev, "Couldn't create our CRTC\n");