drm/sun4i: tcon: Find matching display backend by device node matching
authorChen-Yu Tsai <wens@csie.org>
Fri, 21 Apr 2017 08:38:54 +0000 (16:38 +0800)
committerMaxime Ripard <maxime.ripard@free-electrons.com>
Sun, 14 May 2017 06:31:57 +0000 (08:31 +0200)
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>
drivers/gpu/drm/sun4i/sun4i_tcon.c

index 5adb643ed41d9aca3cfa9c7926d7a1f77a243f4b..b13a7c2029df9b470117e013c08bcd9e84a8f4ac 100644 (file)
@@ -403,6 +403,53 @@ static int sun4i_tcon_init_regmap(struct device *dev,
        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 *dev, struct device *master,
        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 *dev, struct device *master,
                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");