qxl: add suspend/resume/hibernate support.
authorDave Airlie <airlied@redhat.com>
Thu, 4 Jul 2013 05:02:33 +0000 (15:02 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 5 Jul 2013 00:44:18 +0000 (10:44 +1000)
This adds suspend/resume and hibernate support for the KMS driver. it evicts
all the objects, turns off the outputs, and waits for the hw to go idle,

On resume, it resets the memslots, rings, monitors object and forces modeset.

Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/qxl/qxl_drv.c
drivers/gpu/drm/qxl/qxl_drv.h
drivers/gpu/drm/qxl/qxl_object.c

index 00e57b76f4f548c5253e8b310ea284e0c5c478ae..df0b577a66081bb86bc4671293f8fee28cd273d0 100644 (file)
@@ -33,8 +33,9 @@
 
 #include "drmP.h"
 #include "drm/drm.h"
-
+#include "drm_crtc_helper.h"
 #include "qxl_drv.h"
+#include "qxl_object.h"
 
 extern int qxl_max_ioctls;
 static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
@@ -77,13 +78,6 @@ qxl_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
-static struct pci_driver qxl_pci_driver = {
-        .name = DRIVER_NAME,
-        .id_table = pciidlist,
-        .probe = qxl_pci_probe,
-        .remove = qxl_pci_remove,
-};
-
 static const struct file_operations qxl_fops = {
        .owner = THIS_MODULE,
        .open = drm_open,
@@ -94,6 +88,130 @@ static const struct file_operations qxl_fops = {
        .mmap = qxl_mmap,
 };
 
+static int qxl_drm_freeze(struct drm_device *dev)
+{
+       struct pci_dev *pdev = dev->pdev;
+       struct qxl_device *qdev = dev->dev_private;
+       struct drm_crtc *crtc;
+
+       drm_kms_helper_poll_disable(dev);
+
+       console_lock();
+       qxl_fbdev_set_suspend(qdev, 1);
+       console_unlock();
+
+       /* unpin the front buffers */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+               if (crtc->enabled)
+                       (*crtc_funcs->disable)(crtc);
+       }
+
+       qxl_destroy_monitors_object(qdev);
+       qxl_surf_evict(qdev);
+       qxl_vram_evict(qdev);
+
+       while (!qxl_check_idle(qdev->command_ring));
+       while (!qxl_check_idle(qdev->release_ring))
+               qxl_queue_garbage_collect(qdev, 1);
+
+       pci_save_state(pdev);
+
+       return 0;
+}
+
+static int qxl_drm_resume(struct drm_device *dev, bool thaw)
+{
+       struct qxl_device *qdev = dev->dev_private;
+
+       qdev->ram_header->int_mask = QXL_INTERRUPT_MASK;
+       if (!thaw) {
+               qxl_reinit_memslots(qdev);
+               qxl_ring_init_hdr(qdev->release_ring);
+       }
+
+       qxl_create_monitors_object(qdev);
+       drm_helper_resume_force_mode(dev);
+
+       console_lock();
+       qxl_fbdev_set_suspend(qdev, 0);
+       console_unlock();
+
+       drm_kms_helper_poll_enable(dev);
+       return 0;
+}
+
+static int qxl_pm_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       int error;
+
+       error = qxl_drm_freeze(drm_dev);
+       if (error)
+               return error;
+
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+       return 0;
+}
+
+static int qxl_pm_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (pci_enable_device(pdev)) {
+               return -EIO;
+       }
+
+       return qxl_drm_resume(drm_dev, false);
+}
+
+static int qxl_pm_thaw(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return qxl_drm_resume(drm_dev, true);
+}
+
+static int qxl_pm_freeze(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return qxl_drm_freeze(drm_dev);
+}
+
+static int qxl_pm_restore(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct qxl_device *qdev = drm_dev->dev_private;
+
+       qxl_io_reset(qdev);
+       return qxl_drm_resume(drm_dev, false);
+}
+
+static const struct dev_pm_ops qxl_pm_ops = {
+       .suspend = qxl_pm_suspend,
+       .resume = qxl_pm_resume,
+       .freeze = qxl_pm_freeze,
+       .thaw = qxl_pm_thaw,
+       .poweroff = qxl_pm_freeze,
+       .restore = qxl_pm_restore,
+};
+static struct pci_driver qxl_pci_driver = {
+        .name = DRIVER_NAME,
+        .id_table = pciidlist,
+        .probe = qxl_pci_probe,
+        .remove = qxl_pci_remove,
+        .driver.pm = &qxl_pm_ops,
+};
+
 static struct drm_driver qxl_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET |
                           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
index 70a67862673aeb303c461c867ed7fdc873a70083..aacb791464a34b770be0cd776fd7162d0fd0f344 100644 (file)
@@ -333,6 +333,7 @@ void qxl_bo_fini(struct qxl_device *qdev);
 
 void qxl_reinit_memslots(struct qxl_device *qdev);
 int qxl_surf_evict(struct qxl_device *qdev);
+int qxl_vram_evict(struct qxl_device *qdev);
 
 struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header,
                                 int element_size,
index 62a046e4a0362978798b213e30fa8968ea5bb13c..1191fe7788c932d6c10e678815724c1dd44b8f16 100644 (file)
@@ -368,3 +368,8 @@ int qxl_surf_evict(struct qxl_device *qdev)
 {
        return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
 }
+
+int qxl_vram_evict(struct qxl_device *qdev)
+{
+       return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_VRAM);
+}