Skip to content

Commit

Permalink
qxl: add suspend/resume/hibernate support.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dave Airlie committed Jul 5, 2013
1 parent b86487a commit d84300b
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 8 deletions.
134 changes: 126 additions & 8 deletions drivers/gpu/drm/qxl/qxl_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) = {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/qxl/qxl_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions drivers/gpu/drm/qxl/qxl_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit d84300b

Please sign in to comment.