Hi,
This is v2 of the series previously named "drm/sun4i: Support two display pipelines". As the name change suggests, the driver now supports any number of pipelines, though the hardware only has 2 or 3.
Changes since v1:
- Add component endpoint ID numbering scheme to device tree binding.
- Use lists to keep references to registered backends and tcons.
- Save pointer to device node for backends.
- Traverse the device tree of_graph starting from the tcons, going up towards the inputs, and matching the device nodes with the device nodes of registered backends, to find the one linked with the tcon the search started from.
- Copy the ID for the tcon from its upstream backend, instead of trying, and possibly failing, to figure it out from the device tree.
- Split out hunk dropping trailing 0 from a backend error message.
Patch 1 adds the component endpoint ID numbering scheme to the device tree binding. New in v2.
Patch 2 adds lists to track registered display backends and TCONs, instead of just one pointer per component type. Previously added arrays of pointers in v1.
Patch 3 drops the trailing 0 from one of the backend's bind error messages. This was previously part of the patch "drm/sun4i: Support two display pipelines".
Patch 4 adds a function to fetch a backend's ID from the device tree. Unchanged.
Patch 5 adds a device node field to the backend data structure and saves a reference to the underlying device node of the backend. New in v2.
Patch 6 makes the tcon driver find its upstream backend by traversing the of_graph and matching device nodes against the device nodes of registered backends. New in v2.
Patch 7 makes the tcon driver use the ID from its associated backend. New in v2. This is not immediately used in this series, but will be used in similar fashion for downstream encoders to figure out IDs and muxing
Patch 8 adds device nodes for sun6i's second display pipeline. Unchanged.
Patch 9 enables sun6i's tcon0 by default. Unchanged.
With this series, the sun4i drm driver now supports registering multiple display pipelines. However the driver does not guard against setups the hardware does not support, such as driving 2 encoders with incompatible dot clocks from the same source clock. Muxing of downstream encoders is not supported either, as we have no drivers for hardware that uses them. The WiP HDMI driver will be the first.
While this series enables the second display pipeline, there's no usable output at the moment. For the A31, the second TCON's panel interface uses the same pins as the Ethernet controller. However Ethernet is used on most boards.
Regards ChenYu
Chen-Yu Tsai (9): dt-bindings: display: sun4i: Add component endpoint ID numbering scheme drm/sun4i: Use lists to track registered display backends and TCONs drm/sun4i: backend: Drop trailing 0 from backend in error message drm/sun4i: backend: Fetch backend ID from device tree drm/sun4i: backend: Save pointer to device tree node drm/sun4i: tcon: Find matching display backend by device node matching drm/sun4i: tcon: Copy ID from associated backend ARM: dts: sun6i: Add second display pipeline device nodes ARM: dts: sun6i: Enable tcon0 by default
.../bindings/display/sunxi/sun4i-drm.txt | 10 ++ arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 1 - arch/arm/boot/dts/sun6i-a31.dtsi | 169 ++++++++++++++++++++- drivers/gpu/drm/sun4i/sun4i_backend.c | 53 ++++++- drivers/gpu/drm/sun4i/sun4i_backend.h | 8 + drivers/gpu/drm/sun4i/sun4i_drv.c | 2 + drivers/gpu/drm/sun4i/sun4i_drv.h | 4 +- drivers/gpu/drm/sun4i/sun4i_tcon.c | 62 +++++++- drivers/gpu/drm/sun4i/sun4i_tcon.h | 6 + 9 files changed, 307 insertions(+), 8 deletions(-)
The Allwinner display pipeline contains many hardware components, some of which can consume data from one of multiple upstream components. The numbering scheme of these components must be encoded into the device tree so the driver can figure out which component out of two or more of the same type it is supposed to use or program.
This patch adds the constraint that local endpoint IDs must be the index or number of the remote endpoint's hardware block, for all components in the display pipeline up to the TCONs.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index 57a8d0610062..7acdbf14ae1c 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -4,6 +4,16 @@ Allwinner A10 Display Pipeline The Allwinner A10 Display pipeline is composed of several components that are going to be documented below:
+For the input port of all components up to the TCON in the display +pipeline, if there are multiple components, the local endpoint IDs +must correspond to the index of the upstream block. For example, if +the remote endpoint is Frontend 1, then the local endpoint ID must +be 1. + +Conversely, for the output ports of the same group, the remote endpoint +ID must be the index of the local hardware block. If the local backend +is backend 1, then the remote endpoint ID must be 1. + TV Encoder ----------
On Fri, Apr 21, 2017 at 04:38:49PM +0800, Chen-Yu Tsai wrote:
The Allwinner display pipeline contains many hardware components, some of which can consume data from one of multiple upstream components. The numbering scheme of these components must be encoded into the device tree so the driver can figure out which component out of two or more of the same type it is supposed to use or program.
This patch adds the constraint that local endpoint IDs must be the index or number of the remote endpoint's hardware block, for all components in the display pipeline up to the TCONs.
Signed-off-by: Chen-Yu Tsai wens@csie.org
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index 57a8d0610062..7acdbf14ae1c 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -4,6 +4,16 @@ Allwinner A10 Display Pipeline The Allwinner A10 Display pipeline is composed of several components that are going to be documented below:
+For the input port of all components up to the TCON in the display +pipeline, if there are multiple components, the local endpoint IDs +must correspond to the index of the upstream block. For example, if +the remote endpoint is Frontend 1, then the local endpoint ID must +be 1.
+Conversely, for the output ports of the same group, the remote endpoint +ID must be the index of the local hardware block. If the local backend +is backend 1, then the remote endpoint ID must be 1.
It would be clearer if you just explicitly listed IDs and their connections. From how this is worded, it would not work if you had connections like this:
DevA 0 DevA 1 DevB 0 DevB 1
These would need to be endpoints 0-3 in TCON, and that doesn't reflect the remote devices' index.
Rob
Hi,
On Fri, Apr 28, 2017 at 08:48:41AM -0500, Rob Herring wrote:
On Fri, Apr 21, 2017 at 04:38:49PM +0800, Chen-Yu Tsai wrote:
The Allwinner display pipeline contains many hardware components, some of which can consume data from one of multiple upstream components. The numbering scheme of these components must be encoded into the device tree so the driver can figure out which component out of two or more of the same type it is supposed to use or program.
This patch adds the constraint that local endpoint IDs must be the index or number of the remote endpoint's hardware block, for all components in the display pipeline up to the TCONs.
Signed-off-by: Chen-Yu Tsai wens@csie.org
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index 57a8d0610062..7acdbf14ae1c 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -4,6 +4,16 @@ Allwinner A10 Display Pipeline The Allwinner A10 Display pipeline is composed of several components that are going to be documented below:
+For the input port of all components up to the TCON in the display +pipeline, if there are multiple components, the local endpoint IDs +must correspond to the index of the upstream block. For example, if +the remote endpoint is Frontend 1, then the local endpoint ID must +be 1.
+Conversely, for the output ports of the same group, the remote endpoint +ID must be the index of the local hardware block. If the local backend +is backend 1, then the remote endpoint ID must be 1.
It would be clearer if you just explicitly listed IDs and their connections. From how this is worded, it would not work if you had connections like this:
DevA 0 DevA 1 DevB 0 DevB 1
These would need to be endpoints 0-3 in TCON, and that doesn't reflect the remote devices' index.
Chen-Yu, can you send a patch to rephrase the doc that way?
Thanks, Maxime
To support multiple display pipelines, we need to keep track of the multiple display backends and TCONs registered with the driver.
Switch to lists to track registered components. Components are only appended to their respective lists if the bind process was successful. The TCON bind function now defers if a backend was not registered.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- drivers/gpu/drm/sun4i/sun4i_backend.c | 6 +++++- drivers/gpu/drm/sun4i/sun4i_backend.h | 4 ++++ drivers/gpu/drm/sun4i/sun4i_drv.c | 2 ++ drivers/gpu/drm/sun4i/sun4i_drv.h | 4 +++- drivers/gpu/drm/sun4i/sun4i_tcon.c | 14 ++++++++++++-- drivers/gpu/drm/sun4i/sun4i_tcon.h | 4 ++++ 6 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index d660741ba475..95a77c6a9161 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -19,6 +19,7 @@ #include <drm/drm_plane_helper.h>
#include <linux/component.h> +#include <linux/list.h> #include <linux/reset.h>
#include "sun4i_backend.h" @@ -310,7 +311,6 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, if (!backend) return -ENOMEM; dev_set_drvdata(dev, backend); - drv->backend = backend;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); @@ -369,6 +369,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, } }
+ list_add_tail(&backend->list, &drv->backend_list); + /* Reset the registers */ for (i = 0x800; i < 0x1000; i += 4) regmap_write(backend->regs, i, 0); @@ -400,6 +402,8 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master, { struct sun4i_backend *backend = dev_get_drvdata(dev);
+ list_del(&backend->list); + if (of_device_is_compatible(dev->of_node, "allwinner,sun8i-a33-display-backend")) sun4i_backend_free_sat(dev); diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 83e63cc702b4..9c8287309c65 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -14,6 +14,7 @@ #define _SUN4I_BACKEND_H_
#include <linux/clk.h> +#include <linux/list.h> #include <linux/regmap.h> #include <linux/reset.h>
@@ -149,6 +150,9 @@ struct sun4i_backend {
struct clk *sat_clk; struct reset_control *sat_reset; + + /* Backend list management */ + struct list_head list; };
void sun4i_backend_apply_color_correction(struct sun4i_backend *backend); diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 767bbadcc85d..89c51fd6e9af 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -101,6 +101,8 @@ static int sun4i_drv_bind(struct device *dev) goto free_drm; } drm->dev_private = drv; + INIT_LIST_HEAD(&drv->backend_list); + INIT_LIST_HEAD(&drv->tcon_list);
ret = of_reserved_mem_device_init(dev); if (ret && ret != -ENODEV) { diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h index 5df50126ff52..835bdb5cc0c2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.h +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h @@ -14,10 +14,12 @@ #define _SUN4I_DRV_H_
#include <linux/clk.h> +#include <linux/list.h> #include <linux/regmap.h>
struct sun4i_drv { - struct sun4i_backend *backend; + struct list_head backend_list; + struct list_head tcon_list; struct sun4i_tcon *tcon;
struct drm_fbdev_cma *fbdev; diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 3ced0b1cef6e..52f37ef9a050 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -26,6 +26,7 @@ #include <linux/regmap.h> #include <linux/reset.h>
+#include "sun4i_backend.h" #include "sun4i_crtc.h" #include "sun4i_dotclock.h" #include "sun4i_drv.h" @@ -476,14 +477,18 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, { struct drm_device *drm = data; struct sun4i_drv *drv = drm->dev_private; + struct sun4i_backend *backend; struct sun4i_tcon *tcon; int ret;
+ /* Wait for a backend to be registered */ + if (list_empty(&drv->backend_list)) + return -EPROBE_DEFER; + tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); if (!tcon) return -ENOMEM; dev_set_drvdata(dev, tcon); - drv->tcon = tcon; tcon->drm = drm; tcon->dev = dev; tcon->quirks = of_device_get_match_data(dev); @@ -528,7 +533,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, goto err_free_dotclock; }
- tcon->crtc = sun4i_crtc_init(drm, drv->backend, tcon); + 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"); ret = PTR_ERR(tcon->crtc); @@ -539,6 +546,8 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, if (ret < 0) goto err_free_clocks;
+ list_add_tail(&tcon->list, &drv->tcon_list); + return 0;
err_free_dotclock: @@ -555,6 +564,7 @@ static void sun4i_tcon_unbind(struct device *dev, struct device *master, { struct sun4i_tcon *tcon = dev_get_drvdata(dev);
+ list_del(&tcon->list); sun4i_dclk_free(tcon); sun4i_tcon_free_clocks(tcon); } diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index f636343a935d..1bda4d183eec 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -17,6 +17,7 @@ #include <drm/drm_crtc.h>
#include <linux/kernel.h> +#include <linux/list.h> #include <linux/reset.h>
#define SUN4I_TCON_GCTL_REG 0x0 @@ -172,6 +173,9 @@ struct sun4i_tcon {
/* Associated crtc */ struct sun4i_crtc *crtc; + + /* TCON list management */ + struct list_head list; };
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
Now that we support multiple instances of backends, the trailing 0 implying only one backend no longer makes sense.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- drivers/gpu/drm/sun4i/sun4i_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 95a77c6a9161..e17e20036aa3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -320,7 +320,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, backend->regs = devm_regmap_init_mmio(dev, regs, &sun4i_backend_regmap_config); if (IS_ERR(backend->regs)) { - dev_err(dev, "Couldn't create the backend0 regmap\n"); + dev_err(dev, "Couldn't create the backend regmap\n"); return PTR_ERR(backend->regs); }
Some Allwinner SoCs have 2 display pipelines, as in 2 of each components, including the frontend, backend, TCON, and any other extras.
As the backend and TCON are always paired together and form the CRTC, we need to know which backend or TCON we are currently probing, so we can pair them when initializing the CRTC.
This patch figures out the backend's ID from the device tree and stores it in the backend's data structure. It does this by looking at the "reg" property of any remote endpoints connected to the backend's input port.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- drivers/gpu/drm/sun4i/sun4i_backend.c | 44 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_backend.h | 2 ++ 2 files changed, 46 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index e17e20036aa3..0b4222312e49 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -20,6 +20,7 @@
#include <linux/component.h> #include <linux/list.h> +#include <linux/of_graph.h> #include <linux/reset.h>
#include "sun4i_backend.h" @@ -289,6 +290,45 @@ static int sun4i_backend_free_sat(struct device *dev) { return 0; }
+/* + * The display backend can take video output from the display frontend, or + * the display enhancement unit on the A80, as input for one it its layers. + * This relationship within the display pipeline is encoded in the device + * tree with of_graph, and we use it here to figure out which backend, if + * there are 2 or more, we are currently probing. The number would be in + * the "reg" property of the upstream output port endpoint. + */ +static int sun4i_backend_of_get_id(struct device_node *node) +{ + struct device_node *port, *ep; + int ret = -EINVAL; + + /* input is port 0 */ + port = of_graph_get_port_by_id(node, 0); + if (!port) + return -EINVAL; + + /* try finding an upstream endpoint */ + for_each_available_child_of_node(port, ep) { + struct device_node *remote; + u32 reg; + + remote = of_parse_phandle(ep, "remote-endpoint", 0); + if (!remote) + continue; + + ret = of_property_read_u32(remote, "reg", ®); + if (ret) + continue; + + ret = reg; + } + + of_node_put(port); + + return ret; +} + static struct regmap_config sun4i_backend_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -312,6 +352,10 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, return -ENOMEM; dev_set_drvdata(dev, backend);
+ backend->id = sun4i_backend_of_get_id(dev->of_node); + if (backend->id < 0) + return backend->id; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 9c8287309c65..45b7fc110590 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -151,6 +151,8 @@ struct sun4i_backend { struct clk *sat_clk; struct reset_control *sat_reset;
+ int id; + /* Backend list management */ struct list_head list; };
Save a pointer to the backend's underlying device tree node in its data structure. This will be used later for downstream tcons to find and match their respective upstream backends.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- drivers/gpu/drm/sun4i/sun4i_backend.c | 1 + drivers/gpu/drm/sun4i/sun4i_backend.h | 2 ++ 2 files changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 0b4222312e49..e9eca057ff35 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -352,6 +352,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, return -ENOMEM; dev_set_drvdata(dev, backend);
+ backend->node = dev->of_node; backend->id = sun4i_backend_of_get_id(dev->of_node); if (backend->id < 0) return backend->id; diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 45b7fc110590..6327a2985fe6 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -15,6 +15,7 @@
#include <linux/clk.h> #include <linux/list.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/reset.h>
@@ -140,6 +141,7 @@ #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
struct sun4i_backend { + struct device_node *node; struct regmap *regs;
struct reset_control *reset;
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 --- 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 52f37ef9a050..4409e7b6c74d 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -472,6 +472,53 @@ struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node) return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER); }
+/* + * 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) { @@ -481,9 +528,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) @@ -533,8 +582,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");
The tcons and backends have a one-to-one relationship. Their IDs, or indexes in the documentation, are also the same.
Copy the ID from the associated backend and save it in the tcon structure. This will later be used when we add support for the output data path muxes.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 + drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 ++ 2 files changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 4409e7b6c74d..8301389c411d 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -540,6 +540,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, dev_set_drvdata(dev, tcon); tcon->drm = drm; tcon->dev = dev; + tcon->id = backend->id; tcon->quirks = of_device_get_match_data(dev);
tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index 1bda4d183eec..d37e1e2ed60e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -174,6 +174,8 @@ struct sun4i_tcon { /* Associated crtc */ struct sun4i_crtc *crtc;
+ int id; + /* TCON list management */ struct list_head list; };
The Allwinner A31/A31s SoCs have 2 display pipelines, as in 2 display frontends, backends, and tcons each. The relationship between the backends and tcons are 1:1, but the frontends can feed either backend.
Add device nodes and of graph nodes describing this relationship.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun6i-a31.dtsi | 168 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 9c999d3788f6..dfcf64515d7f 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -232,7 +232,7 @@
de: display-engine { compatible = "allwinner,sun6i-a31-display-engine"; - allwinner,pipelines = <&fe0>; + allwinner,pipelines = <&fe0>, <&fe1>; status = "disabled"; };
@@ -289,6 +289,43 @@ }; };
+ tcon1: lcd-controller@01c0d000 { + compatible = "allwinner,sun6i-a31-tcon"; + reg = <0x01c0d000 0x1000>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; + resets = <&ccu RST_AHB1_LCD1>; + reset-names = "lcd"; + clocks = <&ccu CLK_AHB1_LCD1>, + <&ccu CLK_LCD1_CH0>, + <&ccu CLK_LCD1_CH1>; + clock-names = "ahb", + "tcon-ch0", + "tcon-ch1"; + clock-output-names = "tcon1-pixel-clock"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon1_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + tcon1_in_drc1: endpoint@0 { + reg = <0>; + remote-endpoint = <&drc1_out_tcon1>; + }; + }; + + tcon1_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + }; + }; + mmc0: mmc@01c0f000 { compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; @@ -896,6 +933,130 @@ reg = <0>; remote-endpoint = <&be0_in_fe0>; }; + + fe0_out_be1: endpoint@1 { + reg = <1>; + remote-endpoint = <&be1_in_fe0>; + }; + }; + }; + }; + + fe1: display-frontend@01e20000 { + compatible = "allwinner,sun6i-a31-display-frontend"; + reg = <0x01e20000 0x20000>; + interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_AHB1_FE1>, <&ccu CLK_FE1>, + <&ccu CLK_DRAM_FE1>; + clock-names = "ahb", "mod", + "ram"; + resets = <&ccu RST_AHB1_FE1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + fe1_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + fe1_out_be0: endpoint@0 { + reg = <0>; + remote-endpoint = <&be0_in_fe1>; + }; + + fe1_out_be1: endpoint@1 { + reg = <1>; + remote-endpoint = <&be1_in_fe1>; + }; + }; + }; + }; + + be1: display-backend@01e40000 { + compatible = "allwinner,sun6i-a31-display-backend"; + reg = <0x01e40000 0x10000>; + interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_AHB1_BE1>, <&ccu CLK_BE1>, + <&ccu CLK_DRAM_BE1>; + clock-names = "ahb", "mod", + "ram"; + resets = <&ccu RST_AHB1_BE1>; + + assigned-clocks = <&ccu CLK_BE1>; + assigned-clock-rates = <300000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + be1_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + be1_in_fe0: endpoint@0 { + reg = <0>; + remote-endpoint = <&fe0_out_be1>; + }; + + be1_in_fe1: endpoint@1 { + reg = <1>; + remote-endpoint = <&fe1_out_be1>; + }; + }; + + be1_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + be1_out_drc1: endpoint@0 { + reg = <0>; + remote-endpoint = <&drc1_in_be1>; + }; + }; + }; + }; + + drc1: drc@01e50000 { + compatible = "allwinner,sun6i-a31-drc"; + reg = <0x01e50000 0x10000>; + interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_AHB1_DRC1>, <&ccu CLK_IEP_DRC1>, + <&ccu CLK_DRAM_DRC1>; + clock-names = "ahb", "mod", + "ram"; + resets = <&ccu RST_AHB1_DRC1>; + + assigned-clocks = <&ccu CLK_IEP_DRC1>; + assigned-clock-rates = <300000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + drc1_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + drc1_in_be1: endpoint@0 { + reg = <0>; + remote-endpoint = <&be1_out_drc1>; + }; + }; + + drc1_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + drc1_out_tcon1: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon1_in_drc1>; + }; }; }; }; @@ -926,6 +1087,11 @@ reg = <0>; remote-endpoint = <&fe0_out_be0>; }; + + be0_in_fe1: endpoint@1 { + reg = <1>; + remote-endpoint = <&fe1_out_be0>; + }; };
be0_out: port@1 {
tcon0 contains a muxing register used to mux tcon output to downstream hdmi or mipi dsi encoders. tcon0 must be available for the mux to be configured.
Whether the display subsystem is enabled or not is now solely controlled by the display-engine node.
Signed-off-by: Chen-Yu Tsai wens@csie.org --- arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 1 - arch/arm/boot/dts/sun6i-a31.dtsi | 1 - 2 files changed, 2 deletions(-)
diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts index b4c87a23e3f8..55607208468b 100644 --- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts +++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts @@ -319,7 +319,6 @@ &tcon0 { pinctrl-names = "default"; pinctrl-0 = <&lcd0_rgb888_pins>; - status = "okay"; };
&tcon0_out { diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index dfcf64515d7f..d0cede5aaeb5 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -264,7 +264,6 @@ "tcon-ch0", "tcon-ch1"; clock-output-names = "tcon0-pixel-clock"; - status = "disabled";
ports { #address-cells = <1>;
On Fri, Apr 21, 2017 at 04:38:48PM +0800, Chen-Yu Tsai wrote:
Hi,
This is v2 of the series previously named "drm/sun4i: Support two display pipelines". As the name change suggests, the driver now supports any number of pipelines, though the hardware only has 2 or 3.
Changes since v1:
Add component endpoint ID numbering scheme to device tree binding.
Use lists to keep references to registered backends and tcons.
Save pointer to device node for backends.
Traverse the device tree of_graph starting from the tcons, going up towards the inputs, and matching the device nodes with the device nodes of registered backends, to find the one linked with the tcon the search started from.
Copy the ID for the tcon from its upstream backend, instead of trying, and possibly failing, to figure it out from the device tree.
Split out hunk dropping trailing 0 from a backend error message.
Patch 1 adds the component endpoint ID numbering scheme to the device tree binding. New in v2.
Patch 2 adds lists to track registered display backends and TCONs, instead of just one pointer per component type. Previously added arrays of pointers in v1.
Patch 3 drops the trailing 0 from one of the backend's bind error messages. This was previously part of the patch "drm/sun4i: Support two display pipelines".
Patch 4 adds a function to fetch a backend's ID from the device tree. Unchanged.
Patch 5 adds a device node field to the backend data structure and saves a reference to the underlying device node of the backend. New in v2.
Patch 6 makes the tcon driver find its upstream backend by traversing the of_graph and matching device nodes against the device nodes of registered backends. New in v2.
Patch 7 makes the tcon driver use the ID from its associated backend. New in v2. This is not immediately used in this series, but will be used in similar fashion for downstream encoders to figure out IDs and muxing
Patch 8 adds device nodes for sun6i's second display pipeline. Unchanged.
Patch 9 enables sun6i's tcon0 by default. Unchanged.
Applied everything, thanks! Maxime
dri-devel@lists.freedesktop.org