]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/gpu/drm/exynos/exynos_drm_fimd.c
drm/exynos: add component framework support
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / exynos / exynos_drm_fimd.c
index 99ae79e09e8251c97cfb961d45b3bc5c07ace8f7..d1a282c3b0620984dcfccad8042e487d8c8b8864 100644 (file)
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/component.h>
 
 #include <video/of_display_timing.h>
 #include <video/of_videomode.h>
 #include <video/samsung_fimd.h>
+#include <drm/drm_panel.h>
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
@@ -186,12 +188,14 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
 }
 
 static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
-                       struct drm_device *drm_dev, int pipe)
+                       struct drm_device *drm_dev)
 {
        struct fimd_context *ctx = mgr->ctx;
+       struct exynos_drm_private *priv;
+       priv = drm_dev->dev_private;
 
-       ctx->drm_dev = drm_dev;
-       ctx->pipe = pipe;
+       mgr->drm_dev = ctx->drm_dev = drm_dev;
+       mgr->pipe = ctx->pipe = priv->pipe++;
 
        /*
         * enable drm irq mode.
@@ -832,8 +836,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
 }
 
 static struct exynos_drm_manager_ops fimd_manager_ops = {
-       .initialize = fimd_mgr_initialize,
-       .remove = fimd_mgr_remove,
        .dpms = fimd_dpms,
        .mode_fixup = fimd_mode_fixup,
        .mode_set = fimd_mode_set,
@@ -878,10 +880,12 @@ out:
        return IRQ_HANDLED;
 }
 
-static int fimd_probe(struct platform_device *pdev)
+static int fimd_bind(struct device *dev, struct device *master, void *data)
 {
-       struct device *dev = &pdev->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm_dev = data;
        struct fimd_context *ctx;
+       struct device_node *dn;
        struct resource *res;
        int win;
        int ret = -EINVAL;
@@ -939,11 +943,19 @@ static int fimd_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, &fimd_manager);
 
        fimd_manager.ctx = ctx;
-       exynos_drm_manager_register(&fimd_manager);
+       fimd_mgr_initialize(&fimd_manager, drm_dev);
 
-       exynos_dpi_probe(ctx->dev);
+       exynos_drm_crtc_create(&fimd_manager);
 
-       pm_runtime_enable(dev);
+       dn = exynos_dpi_of_find_panel_node(&pdev->dev);
+       if (dn) {
+               /*
+                * It should be called after exynos_drm_crtc_create call
+                * because exynos_dpi_probe call will try to find same lcd
+                * type of manager to setup possible_crtcs.
+                */
+               exynos_dpi_probe(drm_dev, dev);
+       }
 
        for (win = 0; win < WINDOWS_NR; win++)
                fimd_clear_win(ctx, win);
@@ -951,18 +963,59 @@ static int fimd_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int fimd_remove(struct platform_device *pdev)
+static void fimd_unbind(struct device *dev, struct device *master,
+                       void *data)
 {
-       struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
+       struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
+       struct drm_crtc *crtc = mgr->crtc;
+       struct device_node *dn;
+
+       fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
 
-       exynos_dpi_remove(&pdev->dev);
+       dn = exynos_dpi_of_find_panel_node(dev);
+       if (dn)
+               exynos_dpi_remove(mgr->drm_dev, dev);
 
-       exynos_drm_manager_unregister(&fimd_manager);
+       fimd_mgr_remove(mgr);
 
-       fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
+       crtc->funcs->destroy(crtc);
+}
+
+static const struct component_ops fimd_component_ops = {
+       .bind   = fimd_bind,
+       .unbind = fimd_unbind,
+};
+
+static int fimd_probe(struct platform_device *pdev)
+{
+       struct device_node *dn;
+
+       /* Check if fimd node has port node. */
+       dn = exynos_dpi_of_find_panel_node(&pdev->dev);
+       if (dn) {
+               struct drm_panel *panel;
+
+               /*
+                * Do not bind if there is the port node but a drm_panel
+                * isn't added to panel_list yet.
+                * In this case, fimd_probe will be called by defered probe
+                * again after the drm_panel is added to panel_list.
+                */
+               panel = of_drm_find_panel(dn);
+               if (!panel)
+                       return -EPROBE_DEFER;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+
+       return exynos_drm_component_add(&pdev->dev, &fimd_component_ops);
+}
 
+static int fimd_remove(struct platform_device *pdev)
+{
        pm_runtime_disable(&pdev->dev);
 
+       exynos_drm_component_del(&pdev->dev, &fimd_component_ops);
        return 0;
 }