From b317fa3ba11a10eee45c3e64d6396015ff87e8dc Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 21 Apr 2017 16:38:54 +0800 Subject: [PATCH] drm/sun4i: tcon: Find matching display backend by device node matching 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 Signed-off-by: Maxime Ripard --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 55 +++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 5adb643ed41d..b13a7c2029df 100644 --- 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 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");