|
@@ -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);
|
|
|
+}
|
|
|
+
|
|
|
static int sun4i_tcon_bind(struct device *dev, struct device *master,
|
|
|
void *data)
|
|
|
{
|