drm/omap: Merge HPD enable operation with HPD callback registration
[linux-2.6-block.git] / drivers / gpu / drm / omapdrm / omap_drv.c
index 1b6601e9b10723a42a7ebbd5d73894373021e5b9..0cca16c323d962c276853f4d434a527462cfc701 100644 (file)
@@ -15,6 +15,8 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/of.h>
+#include <linux/sort.h>
 #include <linux/sys_soc.h>
 
 #include <drm/drm_atomic.h>
@@ -127,9 +129,9 @@ static const struct drm_mode_config_funcs omap_mode_config_funcs = {
        .atomic_commit = drm_atomic_helper_commit,
 };
 
-static int get_connector_type(struct omap_dss_device *dssdev)
+static int get_connector_type(struct omap_dss_device *display)
 {
-       switch (dssdev->type) {
+       switch (display->type) {
        case OMAP_DISPLAY_TYPE_HDMI:
                return DRM_MODE_CONNECTOR_HDMIA;
        case OMAP_DISPLAY_TYPE_DVI:
@@ -149,33 +151,92 @@ static int get_connector_type(struct omap_dss_device *dssdev)
        }
 }
 
-static void omap_disconnect_dssdevs(void)
+static void omap_disconnect_pipelines(struct drm_device *ddev)
 {
-       struct omap_dss_device *dssdev = NULL;
+       struct omap_drm_private *priv = ddev->dev_private;
+       unsigned int i;
+
+       for (i = 0; i < priv->num_pipes; i++) {
+               struct omap_drm_pipeline *pipe = &priv->pipes[i];
+
+               omapdss_device_disconnect(NULL, pipe->output);
+
+               omapdss_device_put(pipe->output);
+               omapdss_device_put(pipe->display);
+               pipe->output = NULL;
+               pipe->display = NULL;
+       }
+
+       memset(&priv->channels, 0, sizeof(priv->channels));
+
+       priv->num_pipes = 0;
+}
+
+static int omap_compare_pipes(const void *a, const void *b)
+{
+       const struct omap_drm_pipeline *pipe1 = a;
+       const struct omap_drm_pipeline *pipe2 = b;
 
-       for_each_dss_dev(dssdev)
-               dssdev->driver->disconnect(dssdev);
+       if (pipe1->display->alias_id > pipe2->display->alias_id)
+               return 1;
+       else if (pipe1->display->alias_id < pipe2->display->alias_id)
+               return -1;
+       return 0;
 }
 
-static int omap_connect_dssdevs(void)
+static int omap_connect_pipelines(struct drm_device *ddev)
 {
+       struct omap_drm_private *priv = ddev->dev_private;
+       struct omap_dss_device *output = NULL;
+       unsigned int i;
        int r;
-       struct omap_dss_device *dssdev = NULL;
 
        if (!omapdss_stack_is_ready())
                return -EPROBE_DEFER;
 
-       for_each_dss_dev(dssdev) {
-               r = dssdev->driver->connect(dssdev);
+       for_each_dss_output(output) {
+               r = omapdss_device_connect(priv->dss, NULL, output);
                if (r == -EPROBE_DEFER) {
-                       omap_dss_put_device(dssdev);
+                       omapdss_device_put(output);
                        goto cleanup;
                } else if (r) {
-                       dev_warn(dssdev->dev, "could not connect display: %s\n",
-                               dssdev->name);
+                       dev_warn(output->dev, "could not connect output %s\n",
+                                output->name);
+               } else {
+                       struct omap_drm_pipeline *pipe;
+
+                       pipe = &priv->pipes[priv->num_pipes++];
+                       pipe->output = omapdss_device_get(output);
+                       pipe->display = omapdss_display_get(output);
+
+                       if (priv->num_pipes == ARRAY_SIZE(priv->pipes)) {
+                               /* To balance the 'for_each_dss_output' loop */
+                               omapdss_device_put(output);
+                               break;
+                       }
                }
        }
 
+       /* Sort the list by DT aliases */
+       sort(priv->pipes, priv->num_pipes, sizeof(priv->pipes[0]),
+            omap_compare_pipes, NULL);
+
+       /*
+        * Populate the pipeline lookup table by DISPC channel. Only one display
+        * is allowed per channel.
+        */
+       for (i = 0; i < priv->num_pipes; ++i) {
+               struct omap_drm_pipeline *pipe = &priv->pipes[i];
+               enum omap_channel channel = pipe->output->dispc_channel;
+
+               if (WARN_ON(priv->channels[channel] != NULL)) {
+                       r = -EINVAL;
+                       goto cleanup;
+               }
+
+               priv->channels[channel] = pipe;
+       }
+
        return 0;
 
 cleanup:
@@ -183,7 +244,7 @@ cleanup:
         * if we are deferring probe, we disconnect the devices we previously
         * connected
         */
-       omap_disconnect_dssdevs();
+       omap_disconnect_pipelines(ddev);
 
        return r;
 }
@@ -204,10 +265,9 @@ static int omap_modeset_init_properties(struct drm_device *dev)
 static int omap_modeset_init(struct drm_device *dev)
 {
        struct omap_drm_private *priv = dev->dev_private;
-       struct omap_dss_device *dssdev = NULL;
        int num_ovls = priv->dispc_ops->get_num_ovls(priv->dispc);
        int num_mgrs = priv->dispc_ops->get_num_mgrs(priv->dispc);
-       int num_crtcs, crtc_idx, plane_idx;
+       unsigned int i;
        int ret;
        u32 plane_crtc_mask;
 
@@ -225,87 +285,62 @@ static int omap_modeset_init(struct drm_device *dev)
         * configuration does not match the expectations or exceeds
         * the available resources, the configuration is rejected.
         */
-       num_crtcs = 0;
-       for_each_dss_dev(dssdev)
-               if (omapdss_device_is_connected(dssdev))
-                       num_crtcs++;
-
-       if (num_crtcs > num_mgrs || num_crtcs > num_ovls ||
-           num_crtcs > ARRAY_SIZE(priv->crtcs) ||
-           num_crtcs > ARRAY_SIZE(priv->planes) ||
-           num_crtcs > ARRAY_SIZE(priv->encoders) ||
-           num_crtcs > ARRAY_SIZE(priv->connectors)) {
+       if (priv->num_pipes > num_mgrs || priv->num_pipes > num_ovls) {
                dev_err(dev->dev, "%s(): Too many connected displays\n",
                        __func__);
                return -EINVAL;
        }
 
-       /* All planes can be put to any CRTC */
-       plane_crtc_mask = (1 << num_crtcs) - 1;
+       /* Create all planes first. They can all be put to any CRTC. */
+       plane_crtc_mask = (1 << priv->num_pipes) - 1;
 
-       dssdev = NULL;
+       for (i = 0; i < num_ovls; i++) {
+               enum drm_plane_type type = i < priv->num_pipes
+                                        ? DRM_PLANE_TYPE_PRIMARY
+                                        : DRM_PLANE_TYPE_OVERLAY;
+               struct drm_plane *plane;
 
-       crtc_idx = 0;
-       plane_idx = 0;
-       for_each_dss_dev(dssdev) {
+               if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)))
+                       return -EINVAL;
+
+               plane = omap_plane_init(dev, i, type, plane_crtc_mask);
+               if (IS_ERR(plane))
+                       return PTR_ERR(plane);
+
+               priv->planes[priv->num_planes++] = plane;
+       }
+
+       /* Create the CRTCs, encoders and connectors. */
+       for (i = 0; i < priv->num_pipes; i++) {
+               struct omap_drm_pipeline *pipe = &priv->pipes[i];
+               struct omap_dss_device *display = pipe->display;
                struct drm_connector *connector;
                struct drm_encoder *encoder;
-               struct drm_plane *plane;
                struct drm_crtc *crtc;
 
-               if (!omapdss_device_is_connected(dssdev))
-                       continue;
-
-               encoder = omap_encoder_init(dev, dssdev);
+               encoder = omap_encoder_init(dev, display);
                if (!encoder)
                        return -ENOMEM;
 
                connector = omap_connector_init(dev,
-                               get_connector_type(dssdev), dssdev, encoder);
+                               get_connector_type(display), display, encoder);
                if (!connector)
                        return -ENOMEM;
 
-               plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_PRIMARY,
-                                       plane_crtc_mask);
-               if (IS_ERR(plane))
-                       return PTR_ERR(plane);
-
-               crtc = omap_crtc_init(dev, plane, dssdev);
+               crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
                if (IS_ERR(crtc))
                        return PTR_ERR(crtc);
 
                drm_connector_attach_encoder(connector, encoder);
-               encoder->possible_crtcs = (1 << crtc_idx);
+               encoder->possible_crtcs = 1 << i;
 
-               priv->crtcs[priv->num_crtcs++] = crtc;
-               priv->planes[priv->num_planes++] = plane;
-               priv->encoders[priv->num_encoders++] = encoder;
-               priv->connectors[priv->num_connectors++] = connector;
-
-               plane_idx++;
-               crtc_idx++;
+               pipe->crtc = crtc;
+               pipe->encoder = encoder;
+               pipe->connector = connector;
        }
 
-       /*
-        * Create normal planes for the remaining overlays:
-        */
-       for (; plane_idx < num_ovls; plane_idx++) {
-               struct drm_plane *plane;
-
-               if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)))
-                       return -EINVAL;
-
-               plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_OVERLAY,
-                       plane_crtc_mask);
-               if (IS_ERR(plane))
-                       return PTR_ERR(plane);
-
-               priv->planes[priv->num_planes++] = plane;
-       }
-
-       DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n",
-               priv->num_planes, priv->num_crtcs, priv->num_encoders,
-               priv->num_connectors);
+       DBG("registered %u planes, %u crtcs/encoders/connectors\n",
+           priv->num_planes, priv->num_pipes);
 
        dev->mode_config.min_width = 8;
        dev->mode_config.min_height = 2;
@@ -335,27 +370,25 @@ static int omap_modeset_init(struct drm_device *dev)
 /*
  * Enable the HPD in external components if supported
  */
-static void omap_modeset_enable_external_hpd(void)
+static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
 {
-       struct omap_dss_device *dssdev = NULL;
+       struct omap_drm_private *priv = ddev->dev_private;
+       int i;
 
-       for_each_dss_dev(dssdev) {
-               if (dssdev->driver->enable_hpd)
-                       dssdev->driver->enable_hpd(dssdev);
-       }
+       for (i = 0; i < priv->num_pipes; i++)
+               omap_connector_enable_hpd(priv->pipes[i].connector);
 }
 
 /*
  * Disable the HPD in external components if supported
  */
-static void omap_modeset_disable_external_hpd(void)
+static void omap_modeset_disable_external_hpd(struct drm_device *ddev)
 {
-       struct omap_dss_device *dssdev = NULL;
+       struct omap_drm_private *priv = ddev->dev_private;
+       int i;
 
-       for_each_dss_dev(dssdev) {
-               if (dssdev->driver->disable_hpd)
-                       dssdev->driver->disable_hpd(dssdev);
-       }
+       for (i = 0; i < priv->num_pipes; i++)
+               omap_connector_disable_hpd(priv->pipes[i].connector);
 }
 
 /*
@@ -525,6 +558,14 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
 
        DBG("%s", dev_name(dev));
 
+       /* Allocate and initialize the DRM device. */
+       ddev = drm_dev_alloc(&omap_drm_driver, dev);
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
+
+       priv->ddev = ddev;
+       ddev->dev_private = priv;
+
        priv->dev = dev;
        priv->dss = omapdss_get_dss();
        priv->dispc = dispc_get_dispc(priv->dss);
@@ -532,7 +573,7 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
 
        omap_crtc_pre_init(priv);
 
-       ret = omap_connect_dssdevs();
+       ret = omap_connect_pipelines(ddev);
        if (ret)
                goto err_crtc_uninit;
 
@@ -543,16 +584,6 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
        mutex_init(&priv->list_lock);
        INIT_LIST_HEAD(&priv->obj_list);
 
-       /* Allocate and initialize the DRM device. */
-       ddev = drm_dev_alloc(&omap_drm_driver, priv->dev);
-       if (IS_ERR(ddev)) {
-               ret = PTR_ERR(ddev);
-               goto err_destroy_wq;
-       }
-
-       priv->ddev = ddev;
-       ddev->dev_private = priv;
-
        /* Get memory bandwidth limits */
        if (priv->dispc_ops->get_memory_bandwidth_limit)
                priv->max_bandwidth =
@@ -563,23 +594,23 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
        ret = omap_modeset_init(ddev);
        if (ret) {
                dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret);
-               goto err_free_drm_dev;
+               goto err_gem_deinit;
        }
 
        /* Initialize vblank handling, start with all CRTCs disabled. */
-       ret = drm_vblank_init(ddev, priv->num_crtcs);
+       ret = drm_vblank_init(ddev, priv->num_pipes);
        if (ret) {
                dev_err(priv->dev, "could not init vblank\n");
                goto err_cleanup_modeset;
        }
 
-       for (i = 0; i < priv->num_crtcs; i++)
-               drm_crtc_vblank_off(priv->crtcs[i]);
+       for (i = 0; i < priv->num_pipes; i++)
+               drm_crtc_vblank_off(priv->pipes[i].crtc);
 
        omap_fbdev_init(ddev);
 
        drm_kms_helper_poll_init(ddev);
-       omap_modeset_enable_external_hpd();
+       omap_modeset_enable_external_hpd(ddev);
 
        /*
         * Register the DRM device with the core and the connectors with
@@ -592,21 +623,20 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
        return 0;
 
 err_cleanup_helpers:
-       omap_modeset_disable_external_hpd();
+       omap_modeset_disable_external_hpd(ddev);
        drm_kms_helper_poll_fini(ddev);
 
        omap_fbdev_fini(ddev);
 err_cleanup_modeset:
        drm_mode_config_cleanup(ddev);
        omap_drm_irq_uninstall(ddev);
-err_free_drm_dev:
+err_gem_deinit:
        omap_gem_deinit(ddev);
-       drm_dev_unref(ddev);
-err_destroy_wq:
        destroy_workqueue(priv->wq);
-       omap_disconnect_dssdevs();
+       omap_disconnect_pipelines(ddev);
 err_crtc_uninit:
-       omap_crtc_pre_uninit();
+       omap_crtc_pre_uninit(priv);
+       drm_dev_unref(ddev);
        return ret;
 }
 
@@ -618,7 +648,7 @@ static void omapdrm_cleanup(struct omap_drm_private *priv)
 
        drm_dev_unregister(ddev);
 
-       omap_modeset_disable_external_hpd();
+       omap_modeset_disable_external_hpd(ddev);
        drm_kms_helper_poll_fini(ddev);
 
        omap_fbdev_fini(ddev);
@@ -630,12 +660,12 @@ static void omapdrm_cleanup(struct omap_drm_private *priv)
        omap_drm_irq_uninstall(ddev);
        omap_gem_deinit(ddev);
 
-       drm_dev_unref(ddev);
-
        destroy_workqueue(priv->wq);
 
-       omap_disconnect_dssdevs();
-       omap_crtc_pre_uninit();
+       omap_disconnect_pipelines(ddev);
+       omap_crtc_pre_uninit(priv);
+
+       drm_dev_unref(ddev);
 }
 
 static int pdev_probe(struct platform_device *pdev)
@@ -677,36 +707,36 @@ static int pdev_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int omap_drm_suspend_all_displays(void)
+static int omap_drm_suspend_all_displays(struct drm_device *ddev)
 {
-       struct omap_dss_device *dssdev = NULL;
+       struct omap_drm_private *priv = ddev->dev_private;
+       int i;
 
-       for_each_dss_dev(dssdev) {
-               if (!dssdev->driver)
-                       continue;
+       for (i = 0; i < priv->num_pipes; i++) {
+               struct omap_dss_device *display = priv->pipes[i].display;
 
-               if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
-                       dssdev->driver->disable(dssdev);
-                       dssdev->activate_after_resume = true;
+               if (display->state == OMAP_DSS_DISPLAY_ACTIVE) {
+                       display->ops->disable(display);
+                       display->activate_after_resume = true;
                } else {
-                       dssdev->activate_after_resume = false;
+                       display->activate_after_resume = false;
                }
        }
 
        return 0;
 }
 
-static int omap_drm_resume_all_displays(void)
+static int omap_drm_resume_all_displays(struct drm_device *ddev)
 {
-       struct omap_dss_device *dssdev = NULL;
+       struct omap_drm_private *priv = ddev->dev_private;
+       int i;
 
-       for_each_dss_dev(dssdev) {
-               if (!dssdev->driver)
-                       continue;
+       for (i = 0; i < priv->num_pipes; i++) {
+               struct omap_dss_device *display = priv->pipes[i].display;
 
-               if (dssdev->activate_after_resume) {
-                       dssdev->driver->enable(dssdev);
-                       dssdev->activate_after_resume = false;
+               if (display->activate_after_resume) {
+                       display->ops->enable(display);
+                       display->activate_after_resume = false;
                }
        }
 
@@ -721,7 +751,7 @@ static int omap_drm_suspend(struct device *dev)
        drm_kms_helper_poll_disable(drm_dev);
 
        drm_modeset_lock_all(drm_dev);
-       omap_drm_suspend_all_displays();
+       omap_drm_suspend_all_displays(drm_dev);
        drm_modeset_unlock_all(drm_dev);
 
        return 0;
@@ -733,7 +763,7 @@ static int omap_drm_resume(struct device *dev)
        struct drm_device *drm_dev = priv->ddev;
 
        drm_modeset_lock_all(drm_dev);
-       omap_drm_resume_all_displays();
+       omap_drm_resume_all_displays(drm_dev);
        drm_modeset_unlock_all(drm_dev);
 
        drm_kms_helper_poll_enable(drm_dev);