#include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/module.h>
+#include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
 
 struct imx_drm_crtc;
 
+struct imx_drm_component {
+       struct device_node *of_node;
+       struct list_head list;
+};
+
 struct imx_drm_device {
        struct drm_device                       *drm;
        struct imx_drm_crtc                     *crtc[MAX_CRTC];
        struct drm_crtc                         *crtc;
        int                                     pipe;
        struct imx_drm_crtc_helper_funcs        imx_drm_helper_funcs;
-       void                                    *cookie;
-       int                                     id;
-       int                                     mux_id;
+       struct device_node                      *port;
 };
 
 static int legacyfb_depth = 16;
 
 /*
  * imx_drm_add_crtc - add a new crtc
- *
- * The return value if !NULL is a cookie for the caller to pass to
- * imx_drm_remove_crtc later.
  */
 int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
                struct imx_drm_crtc **new_crtc,
                const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
-               void *cookie, int id)
+               struct device_node *port)
 {
        struct imx_drm_device *imxdrm = drm->dev_private;
        struct imx_drm_crtc *imx_drm_crtc;
 
        imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
        imx_drm_crtc->pipe = imxdrm->pipes++;
-       imx_drm_crtc->cookie = cookie;
-       imx_drm_crtc->id = id;
-       imx_drm_crtc->mux_id = imx_drm_crtc->pipe;
+       imx_drm_crtc->port = port;
        imx_drm_crtc->crtc = crtc;
 
        imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc;
 EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
 
 /*
- * Find the DRM CRTC possible mask for the device node cookie/id.
+ * Find the DRM CRTC possible mask for the connected endpoint.
  *
  * The encoder possible masks are defined by their position in the
  * mode_config crtc_list.  This means that CRTCs must not be added
  * or removed once the DRM device has been fully initialised.
  */
 static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm,
-       void *cookie, int id)
+       struct device_node *endpoint)
 {
+       struct device_node *port;
        unsigned i;
 
+       port = of_graph_get_remote_port(endpoint);
+       if (!port)
+               return 0;
+       of_node_put(port);
+
        for (i = 0; i < MAX_CRTC; i++) {
                struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i];
-               if (imx_drm_crtc && imx_drm_crtc->id == id &&
-                   imx_drm_crtc->cookie == cookie)
+               if (imx_drm_crtc && imx_drm_crtc->port == port)
                        return drm_crtc_mask(imx_drm_crtc->crtc);
        }
 
        return 0;
 }
 
+static struct device_node *imx_drm_of_get_next_endpoint(
+               const struct device_node *parent, struct device_node *prev)
+{
+       struct device_node *node = of_graph_get_next_endpoint(parent, prev);
+       of_node_put(prev);
+       return node;
+}
+
 int imx_drm_encoder_parse_of(struct drm_device *drm,
        struct drm_encoder *encoder, struct device_node *np)
 {
        struct imx_drm_device *imxdrm = drm->dev_private;
+       struct device_node *ep = NULL;
        uint32_t crtc_mask = 0;
-       int i, ret = 0;
+       int i;
 
-       for (i = 0; !ret; i++) {
-               struct of_phandle_args args;
-               uint32_t mask;
-               int id;
+       for (i = 0; ; i++) {
+               u32 mask;
 
-               ret = of_parse_phandle_with_args(np, "crtcs", "#crtc-cells", i,
-                                                &args);
-               if (ret == -ENOENT)
+               ep = imx_drm_of_get_next_endpoint(np, ep);
+               if (!ep)
                        break;
-               if (ret < 0)
-                       return ret;
 
-               id = args.args_count > 0 ? args.args[0] : 0;
-               mask = imx_drm_find_crtc_mask(imxdrm, args.np, id);
-               of_node_put(args.np);
+               mask = imx_drm_find_crtc_mask(imxdrm, ep);
 
                /*
                 * If we failed to find the CRTC(s) which this encoder is
                crtc_mask |= mask;
        }
 
+       if (ep)
+               of_node_put(ep);
+       if (i == 0)
+               return -ENOENT;
+
        encoder->possible_crtcs = crtc_mask;
 
        /* FIXME: this is the mask of outputs which can clone this output. */
 }
 EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
 
-int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder)
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int imx_drm_encoder_get_mux_id(struct device_node *node,
+                              struct drm_encoder *encoder)
 {
        struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc);
+       struct device_node *ep = NULL;
+       struct device_node *port;
+       int id, ret;
+
+       if (!node || !imx_crtc)
+               return -EINVAL;
+
+       do {
+               ep = imx_drm_of_get_next_endpoint(node, ep);
+               if (!ep)
+                       break;
+
+               port = of_graph_get_remote_port(ep);
+               of_node_put(port);
+               if (port == imx_crtc->port) {
+                       ret = of_property_read_u32(ep->parent, "reg", &id);
+                       of_node_put(ep);
+                       return ret ? ret : id;
+               }
+       } while (ep);
 
-       return imx_crtc ? imx_crtc->mux_id : -EINVAL;
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id);
 
        .patchlevel             = 0,
 };
 
-static int compare_parent_of(struct device *dev, void *data)
-{
-       struct of_phandle_args *args = data;
-       return dev->parent && dev->parent->of_node == args->np;
-}
-
 static int compare_of(struct device *dev, void *data)
 {
-       return dev->of_node == data;
-}
-
-static int imx_drm_add_components(struct device *master, struct master *m)
-{
-       struct device_node *np = master->of_node;
-       unsigned i;
-       int ret;
+       struct device_node *np = data;
 
-       for (i = 0; ; i++) {
-               struct of_phandle_args args;
-
-               ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1,
-                                                      i, &args);
-               if (ret)
-                       break;
-
-               ret = component_master_add_child(m, compare_parent_of, &args);
-               of_node_put(args.np);
-
-               if (ret)
-                       return ret;
+       /* Special case for LDB, one device for two channels */
+       if (of_node_cmp(np->name, "lvds-channel") == 0) {
+               np = of_get_parent(np);
+               of_node_put(np);
        }
 
-       for (i = 0; ; i++) {
-               struct device_node *node;
+       return dev->of_node == np;
+}
 
-               node = of_parse_phandle(np, "connectors", i);
-               if (!node)
-                       break;
+static LIST_HEAD(imx_drm_components);
 
-               ret = component_master_add_child(m, compare_of, node);
-               of_node_put(node);
+static int imx_drm_add_components(struct device *master, struct master *m)
+{
+       struct imx_drm_component *component;
+       int ret;
 
+       list_for_each_entry(component, &imx_drm_components, list) {
+               ret = component_master_add_child(m, compare_of,
+                                                component->of_node);
                if (ret)
                        return ret;
        }
        .unbind = imx_drm_unbind,
 };
 
+static struct imx_drm_component *imx_drm_find_component(struct device *dev,
+               struct device_node *node)
+{
+       struct imx_drm_component *component;
+
+       list_for_each_entry(component, &imx_drm_components, list)
+               if (component->of_node == node)
+                       return component;
+
+       return NULL;
+}
+
+static int imx_drm_add_component(struct device *dev, struct device_node *node)
+{
+       struct imx_drm_component *component;
+
+       if (imx_drm_find_component(dev, node))
+               return 0;
+
+       component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+
+       component->of_node = node;
+       list_add_tail(&component->list, &imx_drm_components);
+
+       return 0;
+}
+
 static int imx_drm_platform_probe(struct platform_device *pdev)
 {
+       struct device_node *ep, *port, *remote;
        int ret;
+       int i;
+
+       /*
+        * Bind the IPU display interface ports first, so that
+        * imx_drm_encoder_parse_of called from encoder .bind callbacks
+        * works as expected.
+        */
+       for (i = 0; ; i++) {
+               port = of_parse_phandle(pdev->dev.of_node, "ports", i);
+               if (!port)
+                       break;
+
+               ret = imx_drm_add_component(&pdev->dev, port);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (i == 0) {
+               dev_err(&pdev->dev, "missing 'ports' property\n");
+               return -ENODEV;
+       }
+
+       /* Then bind all encoders */
+       for (i = 0; ; i++) {
+               port = of_parse_phandle(pdev->dev.of_node, "ports", i);
+               if (!port)
+                       break;
+
+               for_each_child_of_node(port, ep) {
+                       remote = of_graph_get_remote_port_parent(ep);
+                       if (!remote || !of_device_is_available(remote)) {
+                               of_node_put(remote);
+                               continue;
+                       }
+
+                       ret = imx_drm_add_component(&pdev->dev, remote);
+                       of_node_put(remote);
+                       if (ret < 0)
+                               return ret;
+               }
+               of_node_put(port);
+       }
 
        ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
        if (ret)
 }
 
 static const struct of_device_id imx_drm_dt_ids[] = {
-       { .compatible = "fsl,imx-drm", },
+       { .compatible = "fsl,imx-display-subsystem", },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, imx_drm_dt_ids);
 
                return ret;
        }
 
-       ret = imx_drm_add_crtc(drm, &ipu_crtc->base,
-                       &ipu_crtc->imx_crtc,
-                       &ipu_crtc_helper_funcs,
-                       ipu_crtc->dev->parent->of_node, pdata->di);
+       ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
+                       &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node);
        if (ret) {
                dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
                goto err_put_resources;
        return ret;
 }
 
+static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent,
+                                                 int port_id)
+{
+       struct device_node *port;
+       int id, ret;
+
+       port = of_get_child_by_name(parent, "port");
+       while (port) {
+               ret = of_property_read_u32(port, "reg", &id);
+               if (!ret && id == port_id)
+                       return port;
+
+               do {
+                       port = of_get_next_child(parent, port);
+                       if (!port)
+                               return NULL;
+               } while (of_node_cmp(port->name, "port"));
+       }
+
+       return NULL;
+}
+
 static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
 {
        struct ipu_client_platformdata *pdata = dev->platform_data;
 
 static int ipu_drm_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
+       struct ipu_client_platformdata *pdata = dev->platform_data;
        int ret;
 
-       if (!pdev->dev.platform_data)
+       if (!dev->platform_data)
                return -EINVAL;
 
-       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+       if (!dev->of_node) {
+               /* Associate crtc device with the corresponding DI port node */
+               dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node,
+                                                     pdata->di + 2);
+               if (!dev->of_node) {
+                       dev_err(dev, "missing port@%d node in %s\n",
+                               pdata->di + 2, dev->parent->of_node->full_name);
+                       return -ENODEV;
+               }
+       }
+
+       ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
        if (ret)
                return ret;
 
-       return component_add(&pdev->dev, &ipu_crtc_ops);
+       return component_add(dev, &ipu_crtc_ops);
 }
 
 static int ipu_drm_remove(struct platform_device *pdev)