drm/omap: Add PM capabilities
authorAndy Gross <andy.gross@ti.com>
Wed, 19 Dec 2012 20:53:37 +0000 (14:53 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Jan 2013 21:19:55 +0000 (13:19 -0800)
Added power management capabilities into the omapdrm and DMM drivers.
During suspend, we don't need to do anything to maintain the state of
the LUT.  We have all the necessary information to recreate the mappings
of the GEM object list maintained by the omapdrm driver.

On resume, the DMM resume handler will first reprogram the LUT to point
to the dummy page.  The subsequent resume handler in the omapdrm will call
into the DMM and reprogram each of the buffer objects.  This will ensure
that all of the necessary objects will be pinned into the DMM properly.

Order of suspend/resume handlers is done by device creation.  We create
the DMM device before the omapdrm, so the correct order is maintained.

Signed-off-by: Andy Gross <andy.gross@ti.com>
Signed-off-by: Rob Clark <rob@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/omapdrm/omap_dmm_tiler.c
drivers/staging/omapdrm/omap_drv.c
drivers/staging/omapdrm/omap_drv.h
drivers/staging/omapdrm/omap_gem.c

index 59bf43899fc09d20ac5c01a0c1894f4b166bc189..c42c5e500e02ba11a58a5e0abcab4877055f7d06 100644 (file)
@@ -903,12 +903,46 @@ error:
 }
 #endif
 
+#ifdef CONFIG_PM
+static int omap_dmm_resume(struct device *dev)
+{
+       struct tcm_area area;
+       int i;
+
+       if (!omap_dmm)
+               return -ENODEV;
+
+       area = (struct tcm_area) {
+               .is2d = true,
+               .tcm = NULL,
+               .p1.x = omap_dmm->container_width - 1,
+               .p1.y = omap_dmm->container_height - 1,
+       };
+
+       /* initialize all LUTs to dummy page entries */
+       for (i = 0; i < omap_dmm->num_lut; i++) {
+               area.tcm = omap_dmm->tcm[i];
+               if (fill(&area, NULL, 0, 0, true))
+                       dev_err(dev, "refill failed");
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops omap_dmm_pm_ops = {
+       .resume = omap_dmm_resume,
+};
+#endif
+
 struct platform_driver omap_dmm_driver = {
        .probe = omap_dmm_probe,
        .remove = omap_dmm_remove,
        .driver = {
                .owner = THIS_MODULE,
                .name = DMM_DRIVER_NAME,
+#ifdef CONFIG_PM
+               .pm = &omap_dmm_pm_ops,
+#endif
        },
 };
 
index ae5ecc2efbc758268d692d732482d1f49f13c352..d246f8543d2ee20b8fe5df7a9386d969da1dba95 100644 (file)
@@ -368,6 +368,9 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
                /* well, limp along without an fbdev.. maybe X11 will work? */
        }
 
+       /* store off drm_device for use in pm ops */
+       dev_set_drvdata(dev->dev, dev);
+
        drm_kms_helper_poll_init(dev);
 
        return 0;
@@ -393,6 +396,8 @@ static int dev_unload(struct drm_device *dev)
        kfree(dev->dev_private);
        dev->dev_private = NULL;
 
+       dev_set_drvdata(dev->dev, NULL);
+
        return 0;
 }
 
@@ -558,10 +563,19 @@ static int pdev_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static const struct dev_pm_ops omapdrm_pm_ops = {
+       .resume = omap_gem_resume,
+};
+#endif
+
 struct platform_driver pdev = {
                .driver = {
                        .name = DRIVER_NAME,
                        .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+                       .pm = &omapdrm_pm_ops,
+#endif
                },
                .probe = pdev_probe,
                .remove = pdev_remove,
index cd1f22b0b124ddbf9eb1eff0efe5b77ddb7fd061..f921027e7500efb8de4fc350f330ec34ea552044 100644 (file)
@@ -135,6 +135,10 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
 void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
 #endif
 
+#ifdef CONFIG_PM
+int omap_gem_resume(struct device *dev);
+#endif
+
 int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
 void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
 irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
index c38992b76fc9f86bf9a4664dfc2e90516041b422..08f1e292ed201c57a65bddf67e84de6695a5bf0a 100644 (file)
@@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj)
        return omap_obj->vaddr;
 }
 
+#ifdef CONFIG_PM
+/* re-pin objects in DMM in resume path: */
+int omap_gem_resume(struct device *dev)
+{
+       struct drm_device *drm_dev = dev_get_drvdata(dev);
+       struct omap_drm_private *priv = drm_dev->dev_private;
+       struct omap_gem_object *omap_obj;
+       int ret = 0;
+
+       list_for_each_entry(omap_obj, &priv->obj_list, mm_list) {
+               if (omap_obj->block) {
+                       struct drm_gem_object *obj = &omap_obj->base;
+                       uint32_t npages = obj->size >> PAGE_SHIFT;
+                       WARN_ON(!omap_obj->pages);  /* this can't happen */
+                       ret = tiler_pin(omap_obj->block,
+                                       omap_obj->pages, npages,
+                                       omap_obj->roll, true);
+                       if (ret) {
+                               dev_err(dev, "could not repin: %d\n", ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_DEBUG_FS
 void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {