summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2013-07-04 15:02:33 +1000
committerDave Airlie <airlied@redhat.com>2013-07-05 10:44:18 +1000
commitd84300bf793471cc20c7553601c45d6f70dd2b1e (patch)
treefd30ee57cf7c8ede42123ed4c61a7d9cfd4d1269
parentb86487a6b671ff7107fbf6d3ff10c2da970cd1c3 (diff)
qxl: add suspend/resume/hibernate support.
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>
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c134
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h1
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.c5
3 files changed, 132 insertions, 8 deletions
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 00e57b76f4f5..df0b577a6608 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -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,
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index 70a67862673a..aacb791464a3 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -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,
diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c
index 62a046e4a036..1191fe7788c9 100644
--- a/drivers/gpu/drm/qxl/qxl_object.c
+++ b/drivers/gpu/drm/qxl/qxl_object.c
@@ -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);
+}