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 <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
This commit is contained in:
Chen-Yu Tsai 2017-04-21 16:38:54 +08:00 committed by Maxime Ripard
parent 2c03e2fb06
commit b317fa3ba1

View File

@ -403,6 +403,53 @@ static int sun4i_tcon_init_regmap(struct device *dev,
return 0; 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, static int sun4i_tcon_bind(struct device *dev, struct device *master,
void *data) void *data)
{ {
@ -412,9 +459,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
struct sun4i_tcon *tcon; struct sun4i_tcon *tcon;
int ret; int ret;
/* Wait for a backend to be registered */ backend = sun4i_tcon_find_backend(drv, dev->of_node);
if (list_empty(&drv->backend_list)) if (IS_ERR(backend)) {
dev_err(dev, "Couldn't find matching backend\n");
return -EPROBE_DEFER; return -EPROBE_DEFER;
}
tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
if (!tcon) if (!tcon)
@ -464,8 +513,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
goto err_free_dotclock; goto err_free_dotclock;
} }
backend = list_first_entry(&drv->backend_list,
struct sun4i_backend, list);
tcon->crtc = sun4i_crtc_init(drm, backend, tcon); tcon->crtc = sun4i_crtc_init(drm, backend, tcon);
if (IS_ERR(tcon->crtc)) { if (IS_ERR(tcon->crtc)) {
dev_err(dev, "Couldn't create our CRTC\n"); dev_err(dev, "Couldn't create our CRTC\n");