Hi, On Fri, Sep 08, 2017 at 03:50:12PM +0800, Chen-Yu Tsai wrote: > The device tree binding for sun4i-drm says: > > For all connections between components up to the TCONs in the display > pipeline, when there are multiple components of the same type at the > same depth, the local endpoint ID must be the same as the remote > component's index. For example, if the remote endpoint is Frontend 1, > then the local endpoint ID must be 1. > > We should be able to get the TCON's ID directly from any of the remote > endpoints from its input port. With the ID, we can then go through the > list of registered engines and find a matching one by ID. > > However the A31 device tree is incorrect. We assumed that there were no > cross pipeline connections between the backends and TCONs. As a result, > in all the endpoints between the backends and TCONs of the second > display pipeline, the endpoint IDs were incorrectly set to 0, when in > fact they should've been set to 1. > > To maintain compatibility with this incorrect device tree, we first > check if the TCON's input port has 1 or many endpoints. If there are > more than 1, then it is likely a fixed version, and we can proceed > with the new method. If there is only 1 endpoint, then it is possibly > an incorrect version, or it could be the SoC only has one pipeline. > In either case we fall back to using the old method of traversing > the input connections to find a matching engine, and then get its > ID. > > Signed-off-by: Chen-Yu Tsai <wens@xxxxxxxx> > --- > drivers/gpu/drm/sun4i/sun4i_tcon.c | 121 ++++++++++++++++++++++++++++++++++++- > 1 file changed, 118 insertions(+), 3 deletions(-) > > diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c > index 065654dbfb2c..33c20d2f9fb1 100644 > --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c > +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c > @@ -463,8 +463,9 @@ static int sun4i_tcon_init_regmap(struct device *dev, > * function in fact searches the corresponding engine, and the ID is > * requested via the get_id function of the engine. > */ > -static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, > - struct device_node *node) > +static struct sunxi_engine * > +sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv, > + struct device_node *node) > { > struct device_node *port, *ep, *remote; > struct sunxi_engine *engine; > @@ -502,7 +503,7 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, > } > > /* keep looking through upstream ports */ > - engine = sun4i_tcon_find_engine(drv, remote); > + engine = sun4i_tcon_find_engine_traverse(drv, remote); > if (!IS_ERR(engine)) { > of_node_put(remote); > of_node_put(port); > @@ -513,6 +514,120 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, > return ERR_PTR(-EINVAL); > } > > +/* > + * The device tree binding says that the remote endpoint ID of any > + * connection between components, up to and including the TCON, of > + * the display pipeline should be equal to the actual ID of the local > + * component. Thus we can look at any one of the input connections of > + * the TCONs, and use that connection's remote endpoint ID as our own. > + * > + * Since the user of this function already finds the input port, > + * the port is passed in directly without further checks. > + */ > +static int sun4i_tcon_of_get_id_from_port(struct device_node *port) > +{ > + struct device_node *ep; > + int ret = -EINVAL; > + > + /* try finding an upstream endpoint */ > + for_each_available_child_of_node(port, ep) { > + struct device_node *remote; > + u32 reg; > + > + remote = of_graph_get_remote_endpoint(ep); > + if (!remote) > + continue; > + > + ret = of_property_read_u32(remote, "reg", ®); > + if (ret) > + continue; > + > + ret = reg; > + } > + > + return ret; > +} > + > +/* > + * Once we know the TCON's id, we can look through the list of > + * engines to find a matching one. We assume all engines have > + * been probed and added to the list. > + */ > +static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv, > + int id) > +{ > + struct sunxi_engine *engine; > + > + list_for_each_entry(engine, &drv->engine_list, list) > + if (engine->id == id) > + return engine; > + > + return ERR_PTR(-EINVAL); > +} > + > +/* > + * On SoCs with the old display pipeline design (Display Engine 1.0), > + * we assumed the TCON was always tied to just one backend. However > + * this proved not to be the case. On the A31, the TCON can select > + * either backend as its source. On the A20 (and likely on the A10), > + * the backend can choose which TCON to output to. > + * > + * The device tree binding says that the remote endpoint ID of any > + * connection between components, up to and including the TCON, of > + * the display pipeline should be equal to the actual ID of the local > + * component. Thus we should be able to look at any one of the input > + * connections of the TCONs, and use that connection's remote endpoint > + * ID as our own. > + * > + * However the connections between the backend and TCON were assumed > + * to be always singular, and their endpoit IDs were all incorrectly > + * set to 0. This means for these old device trees, we cannot just look > + * up the remote endpoint ID of a TCON input endpoint. TCON1 would be > + * incorrectly identified as TCON0. > + * > + * This function first checks if the TCON node has 2 input endpoints. > + * If so, then the device tree is a corrected version, and it will use > + * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above > + * to fetch the ID and engine directly. If not, then it is likely an > + * old device trees, where the endpoint IDs were incorrect, but did not > + * have endpoint connections between the backend and TCON across > + * different display pipelines. It will fall back to the old method of > + * traversing the of_graph to try and find a matching engine by device > + * node. > + * > + * In the case of single display pipeline device trees, either method > + * works. > + */ > +static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, > + struct device_node *node) > +{ > + struct device_node *port; > + struct sunxi_engine *engine; > + > + port = of_graph_get_port_by_id(node, 0); > + if (!port) > + return ERR_PTR(-EINVAL); > + > + /* > + * Is this a corrected device tree with cross pipeline > + * connections between the backend and TCON? > + */ > + if (of_get_child_count(port) > 1) { > + /* Get our ID directly from an upstream endpoint */ > + int id = sun4i_tcon_of_get_id_from_port(port); > + > + /* Get our engine by matching our ID */ > + engine = sun4i_tcon_get_engine_by_id(drv, id); > + > + of_node_put(port); > + return engine; > + } > + > + /* Fallback to old method by traversing input endpoints */ > + of_node_put(port); > + return sun4i_tcon_find_engine_traverse(drv, node); In the old DT case, I'd like to have a warning displayed in the log saying that the DT is too old and we won't be able to support the dual-pipeline properly, so that the users can at least know that they should update. Can you add it in a follow-up? Thanks! -- Maxime Ripard, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com
Attachment:
signature.asc
Description: PGP signature