summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/pl111
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2018-02-06 10:35:40 +0100
committerLinus Walleij <linus.walleij@linaro.org>2018-02-07 09:08:51 +0100
commit3b6ec458578bf065e102e8bb4b9e61dbc0caf5a4 (patch)
tree8079f4eb6b63de890c60e1ccc000264d9a55e4dc /drivers/gpu/drm/pl111
parent08e3211251e36a506ff6b0c31620e362b5800f47 (diff)
drm/pl111: Support multiple endpoints on the CLCD
The Versatile PL110 implementations use multiple endpoints: from the PL111 port, the lines are routed through a PLD, and from there forked so the same lines go to a VGA DAC and an external TFT panel connector. This is discrete wireing so there is no way to turn of one output, i.e. this is really two endpoints, not two ports. We model this with multiple endpoints, so we need to loop over the available endpoints, check for panel or bridge on each and accumulate the result before continuing. The code already will give the panel preference over the bridge, if present, so the output will be sent to the panel if both a panel and a bridge is present on two endpoints of the same port. If they all return -EPROBE_DEFER we return -EPROBE_DEFER as well. If just one endpoint is present on the port, the behaviour is the same as before. Reviewed-by: Eric Anholt <eric@anholt.net> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Link: https://patchwork.freedesktop.org/patch/msgid/20180206093540.8147-5-linus.walleij@linaro.org
Diffstat (limited to 'drivers/gpu/drm/pl111')
-rw-r--r--drivers/gpu/drm/pl111/pl111_drv.c62
1 files changed, 56 insertions, 6 deletions
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
index 94fbc7cc5e19..1231905150d0 100644
--- a/drivers/gpu/drm/pl111/pl111_drv.c
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -58,6 +58,8 @@
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -85,9 +87,13 @@ static int pl111_modeset_init(struct drm_device *dev)
{
struct drm_mode_config *mode_config;
struct pl111_drm_dev_private *priv = dev->dev_private;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
+ struct device_node *np = dev->dev->of_node;
+ struct device_node *remote;
+ struct drm_panel *panel = NULL;
+ struct drm_bridge *bridge = NULL;
+ bool defer = false;
int ret = 0;
+ int i;
drm_mode_config_init(dev);
mode_config = &dev->mode_config;
@@ -97,10 +103,54 @@ static int pl111_modeset_init(struct drm_device *dev)
mode_config->min_height = 1;
mode_config->max_height = 768;
- ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
- 0, 0, &panel, &bridge);
- if (ret && ret != -ENODEV)
- return ret;
+ i = 0;
+ for_each_endpoint_of_node(np, remote) {
+ struct drm_panel *tmp_panel;
+ struct drm_bridge *tmp_bridge;
+
+ dev_dbg(dev->dev, "checking endpoint %d\n", i);
+
+ ret = drm_of_find_panel_or_bridge(dev->dev->of_node,
+ 0, i,
+ &tmp_panel,
+ &tmp_bridge);
+ if (ret) {
+ if (ret == -EPROBE_DEFER) {
+ /*
+ * Something deferred, but that is often just
+ * another way of saying -ENODEV, but let's
+ * cast a vote for later deferral.
+ */
+ defer = true;
+ } else if (ret != -ENODEV) {
+ /* Continue, maybe something else is working */
+ dev_err(dev->dev,
+ "endpoint %d returns %d\n", i, ret);
+ }
+ }
+
+ if (tmp_panel) {
+ dev_info(dev->dev,
+ "found panel on endpoint %d\n", i);
+ panel = tmp_panel;
+ }
+ if (tmp_bridge) {
+ dev_info(dev->dev,
+ "found bridge on endpoint %d\n", i);
+ bridge = tmp_bridge;
+ }
+
+ i++;
+ }
+
+ /*
+ * If we can't find neither panel nor bridge on any of the
+ * endpoints, and any of them retured -EPROBE_DEFER, then
+ * let's defer this driver too.
+ */
+ if ((!panel && !bridge) && defer)
+ return -EPROBE_DEFER;
+
if (panel) {
bridge = drm_panel_bridge_add(panel,
DRM_MODE_CONNECTOR_Unknown);