summaryrefslogtreecommitdiffstats
path: root/vmwgfx-Rework-device-initialization.patch
diff options
context:
space:
mode:
Diffstat (limited to 'vmwgfx-Rework-device-initialization.patch')
-rw-r--r--vmwgfx-Rework-device-initialization.patch890
1 files changed, 890 insertions, 0 deletions
diff --git a/vmwgfx-Rework-device-initialization.patch b/vmwgfx-Rework-device-initialization.patch
new file mode 100644
index 000000000..183ba9c28
--- /dev/null
+++ b/vmwgfx-Rework-device-initialization.patch
@@ -0,0 +1,890 @@
+From c1d9b32d8ee2e97e2867fa759eb84d436cca0311 Mon Sep 17 00:00:00 2001
+From: Thomas Hellstrom <thellstrom@vmware.com>
+Date: Thu, 25 Jun 2015 10:47:43 -0700
+Subject: [PATCH 1/2] vmwgfx: Rework device initialization
+
+This commit reworks device initialization so that we always enable the
+FIFO at driver load, deferring SVGA enable until either first modeset
+or fbdev enable.
+This should always leave the fifo properly enabled for render- and
+control nodes.
+In addition,
+*) We disable the use of VRAM when SVGA is not enabled.
+*) We simplify PM support so that we only throw out resources on hibernate,
+not on suspend, since the device keeps its state on suspend.
+
+Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
+Reviewed-by: Sinclair Yeh <syeh@vmware.com>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_context.c | 8 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 337 ++++++++++++++++++--------------
+ drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 19 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 4 +
+ drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c | 12 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 +
+ drivers/gpu/drm/vmwgfx/vmwgfx_mob.c | 6 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 +
+ drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 4 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 12 +-
+ 10 files changed, 230 insertions(+), 174 deletions(-)
+
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
+index 5ac92874404d..a8e370a55e90 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
+@@ -140,7 +140,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res)
+ cmd->body.cid = cpu_to_le32(res->id);
+
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ }
+
+ static int vmw_gb_context_init(struct vmw_private *dev_priv,
+@@ -220,7 +220,7 @@ static int vmw_context_init(struct vmw_private *dev_priv,
+ cmd->body.cid = cpu_to_le32(res->id);
+
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+ vmw_resource_activate(res, vmw_hw_context_destroy);
+ return 0;
+
+@@ -281,7 +281,7 @@ static int vmw_gb_context_create(struct vmw_resource *res)
+ cmd->header.size = sizeof(cmd->body);
+ cmd->body.cid = res->id;
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+
+ return 0;
+
+@@ -414,7 +414,7 @@ static int vmw_gb_context_destroy(struct vmw_resource *res)
+ if (dev_priv->query_cid == res->id)
+ dev_priv->query_cid_valid = false;
+ vmw_resource_release_id(res);
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+index 620bb5cf617c..a4766acd0ea2 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+@@ -339,24 +339,47 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv)
+ return ret;
+ }
+
+-static int vmw_request_device(struct vmw_private *dev_priv)
++/**
++ * vmw_request_device_late - Perform late device setup
++ *
++ * @dev_priv: Pointer to device private.
++ *
++ * This function performs setup of otables and enables large command
++ * buffer submission. These tasks are split out to a separate function
++ * because it reverts vmw_release_device_early and is intended to be used
++ * by an error path in the hibernation code.
++ */
++static int vmw_request_device_late(struct vmw_private *dev_priv)
+ {
+ int ret;
+
+- ret = vmw_fifo_init(dev_priv, &dev_priv->fifo);
+- if (unlikely(ret != 0)) {
+- DRM_ERROR("Unable to initialize FIFO.\n");
+- return ret;
+- }
+- vmw_fence_fifo_up(dev_priv->fman);
+ if (dev_priv->has_mob) {
+ ret = vmw_otables_setup(dev_priv);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Unable to initialize "
+ "guest Memory OBjects.\n");
+- goto out_no_mob;
++ return ret;
+ }
+ }
++
++ return 0;
++}
++
++static int vmw_request_device(struct vmw_private *dev_priv)
++{
++ int ret;
++
++ ret = vmw_fifo_init(dev_priv, &dev_priv->fifo);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Unable to initialize FIFO.\n");
++ return ret;
++ }
++ vmw_fence_fifo_up(dev_priv->fman);
++
++ ret = vmw_request_device_late(dev_priv);
++ if (ret)
++ goto out_no_mob;
++
+ ret = vmw_dummy_query_bo_create(dev_priv);
+ if (unlikely(ret != 0))
+ goto out_no_query_bo;
+@@ -364,15 +387,25 @@ static int vmw_request_device(struct vmw_private *dev_priv)
+ return 0;
+
+ out_no_query_bo:
+- if (dev_priv->has_mob)
++ if (dev_priv->has_mob) {
++ (void) ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB);
+ vmw_otables_takedown(dev_priv);
++ }
+ out_no_mob:
+ vmw_fence_fifo_down(dev_priv->fman);
+ vmw_fifo_release(dev_priv, &dev_priv->fifo);
+ return ret;
+ }
+
+-static void vmw_release_device(struct vmw_private *dev_priv)
++/**
++ * vmw_release_device_early - Early part of fifo takedown.
++ *
++ * @dev_priv: Pointer to device private struct.
++ *
++ * This is the first part of command submission takedown, to be called before
++ * buffer management is taken down.
++ */
++static void vmw_release_device_early(struct vmw_private *dev_priv)
+ {
+ /*
+ * Previous destructions should've released
+@@ -382,64 +415,24 @@ static void vmw_release_device(struct vmw_private *dev_priv)
+ BUG_ON(dev_priv->pinned_bo != NULL);
+
+ ttm_bo_unref(&dev_priv->dummy_query_bo);
+- if (dev_priv->has_mob)
++ if (dev_priv->has_mob) {
++ ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB);
+ vmw_otables_takedown(dev_priv);
+- vmw_fence_fifo_down(dev_priv->fman);
+- vmw_fifo_release(dev_priv, &dev_priv->fifo);
+-}
+-
+-
+-/**
+- * Increase the 3d resource refcount.
+- * If the count was prevously zero, initialize the fifo, switching to svga
+- * mode. Note that the master holds a ref as well, and may request an
+- * explicit switch to svga mode if fb is not running, using @unhide_svga.
+- */
+-int vmw_3d_resource_inc(struct vmw_private *dev_priv,
+- bool unhide_svga)
+-{
+- int ret = 0;
+-
+- mutex_lock(&dev_priv->release_mutex);
+- if (unlikely(dev_priv->num_3d_resources++ == 0)) {
+- ret = vmw_request_device(dev_priv);
+- if (unlikely(ret != 0))
+- --dev_priv->num_3d_resources;
+- } else if (unhide_svga) {
+- vmw_write(dev_priv, SVGA_REG_ENABLE,
+- vmw_read(dev_priv, SVGA_REG_ENABLE) &
+- ~SVGA_REG_ENABLE_HIDE);
+ }
+-
+- mutex_unlock(&dev_priv->release_mutex);
+- return ret;
+ }
+
+ /**
+- * Decrease the 3d resource refcount.
+- * If the count reaches zero, disable the fifo, switching to vga mode.
+- * Note that the master holds a refcount as well, and may request an
+- * explicit switch to vga mode when it releases its refcount to account
+- * for the situation of an X server vt switch to VGA with 3d resources
+- * active.
++ * vmw_release_device_late - Late part of fifo takedown.
++ *
++ * @dev_priv: Pointer to device private struct.
++ *
++ * This is the last part of the command submission takedown, to be called when
++ * command submission is no longer needed. It may wait on pending fences.
+ */
+-void vmw_3d_resource_dec(struct vmw_private *dev_priv,
+- bool hide_svga)
++static void vmw_release_device_late(struct vmw_private *dev_priv)
+ {
+- int32_t n3d;
+-
+- mutex_lock(&dev_priv->release_mutex);
+- if (unlikely(--dev_priv->num_3d_resources == 0))
+- vmw_release_device(dev_priv);
+- else if (hide_svga)
+- vmw_write(dev_priv, SVGA_REG_ENABLE,
+- vmw_read(dev_priv, SVGA_REG_ENABLE) |
+- SVGA_REG_ENABLE_HIDE);
+-
+- n3d = (int32_t) dev_priv->num_3d_resources;
+- mutex_unlock(&dev_priv->release_mutex);
+-
+- BUG_ON(n3d < 0);
++ vmw_fence_fifo_down(dev_priv->fman);
++ vmw_fifo_release(dev_priv, &dev_priv->fifo);
+ }
+
+ /**
+@@ -603,6 +596,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
+ spin_lock_init(&dev_priv->hw_lock);
+ spin_lock_init(&dev_priv->waiter_lock);
+ spin_lock_init(&dev_priv->cap_lock);
++ spin_lock_init(&dev_priv->svga_lock);
+
+ for (i = vmw_res_context; i < vmw_res_max; ++i) {
+ idr_init(&dev_priv->res_idr[i]);
+@@ -714,17 +708,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
+ dev_priv->active_master = &dev_priv->fbdev_master;
+
+
+- ret = ttm_bo_device_init(&dev_priv->bdev,
+- dev_priv->bo_global_ref.ref.object,
+- &vmw_bo_driver,
+- dev->anon_inode->i_mapping,
+- VMWGFX_FILE_PAGE_OFFSET,
+- false);
+- if (unlikely(ret != 0)) {
+- DRM_ERROR("Failed initializing TTM buffer object driver.\n");
+- goto out_err1;
+- }
+-
+ dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start,
+ dev_priv->mmio_size);
+
+@@ -787,13 +770,28 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
+ goto out_no_fman;
+ }
+
++ ret = ttm_bo_device_init(&dev_priv->bdev,
++ dev_priv->bo_global_ref.ref.object,
++ &vmw_bo_driver,
++ dev->anon_inode->i_mapping,
++ VMWGFX_FILE_PAGE_OFFSET,
++ false);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed initializing TTM buffer object driver.\n");
++ goto out_no_bdev;
++ }
+
++ /*
++ * Enable VRAM, but initially don't use it until SVGA is enabled and
++ * unhidden.
++ */
+ ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
+ (dev_priv->vram_size >> PAGE_SHIFT));
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed initializing memory manager for VRAM.\n");
+ goto out_no_vram;
+ }
++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false;
+
+ dev_priv->has_gmr = true;
+ if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) ||
+@@ -814,18 +812,18 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
+ }
+ }
+
+- vmw_kms_save_vga(dev_priv);
+-
+- /* Start kms and overlay systems, needs fifo. */
+ ret = vmw_kms_init(dev_priv);
+ if (unlikely(ret != 0))
+ goto out_no_kms;
+ vmw_overlay_init(dev_priv);
+
++ ret = vmw_request_device(dev_priv);
++ if (ret)
++ goto out_no_fifo;
++
+ if (dev_priv->enable_fb) {
+- ret = vmw_3d_resource_inc(dev_priv, true);
+- if (unlikely(ret != 0))
+- goto out_no_fifo;
++ vmw_fifo_resource_inc(dev_priv);
++ vmw_svga_enable(dev_priv);
+ vmw_fb_init(dev_priv);
+ }
+
+@@ -838,13 +836,14 @@ out_no_fifo:
+ vmw_overlay_close(dev_priv);
+ vmw_kms_close(dev_priv);
+ out_no_kms:
+- vmw_kms_restore_vga(dev_priv);
+ if (dev_priv->has_mob)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
+ if (dev_priv->has_gmr)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
+ out_no_vram:
++ (void)ttm_bo_device_release(&dev_priv->bdev);
++out_no_bdev:
+ vmw_fence_manager_takedown(dev_priv->fman);
+ out_no_fman:
+ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
+@@ -860,8 +859,6 @@ out_err4:
+ iounmap(dev_priv->mmio_virt);
+ out_err3:
+ arch_phys_wc_del(dev_priv->mmio_mtrr);
+- (void)ttm_bo_device_release(&dev_priv->bdev);
+-out_err1:
+ vmw_ttm_global_release(dev_priv);
+ out_err0:
+ for (i = vmw_res_context; i < vmw_res_max; ++i)
+@@ -883,18 +880,22 @@ static int vmw_driver_unload(struct drm_device *dev)
+ vfree(dev_priv->ctx.cmd_bounce);
+ if (dev_priv->enable_fb) {
+ vmw_fb_close(dev_priv);
+- vmw_kms_restore_vga(dev_priv);
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
++ vmw_svga_disable(dev_priv);
+ }
++
+ vmw_kms_close(dev_priv);
+ vmw_overlay_close(dev_priv);
+
+- if (dev_priv->has_mob)
+- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
+ if (dev_priv->has_gmr)
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
+
++ vmw_release_device_early(dev_priv);
++ if (dev_priv->has_mob)
++ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
++ (void) ttm_bo_device_release(&dev_priv->bdev);
++ vmw_release_device_late(dev_priv);
+ vmw_fence_manager_takedown(dev_priv->fman);
+ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
+ drm_irq_uninstall(dev_priv->dev);
+@@ -1148,27 +1149,13 @@ static int vmw_master_set(struct drm_device *dev,
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ int ret = 0;
+
+- if (!dev_priv->enable_fb) {
+- ret = vmw_3d_resource_inc(dev_priv, true);
+- if (unlikely(ret != 0))
+- return ret;
+- vmw_kms_save_vga(dev_priv);
+- vmw_write(dev_priv, SVGA_REG_TRACES, 0);
+- }
+-
+ if (active) {
+ BUG_ON(active != &dev_priv->fbdev_master);
+ ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile);
+ if (unlikely(ret != 0))
+- goto out_no_active_lock;
++ return ret;
+
+ ttm_lock_set_kill(&active->lock, true, SIGTERM);
+- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
+- if (unlikely(ret != 0)) {
+- DRM_ERROR("Unable to clean VRAM on "
+- "master drop.\n");
+- }
+-
+ dev_priv->active_master = NULL;
+ }
+
+@@ -1182,14 +1169,6 @@ static int vmw_master_set(struct drm_device *dev,
+ dev_priv->active_master = vmaster;
+
+ return 0;
+-
+-out_no_active_lock:
+- if (!dev_priv->enable_fb) {
+- vmw_kms_restore_vga(dev_priv);
+- vmw_3d_resource_dec(dev_priv, true);
+- vmw_write(dev_priv, SVGA_REG_TRACES, 1);
+- }
+- return ret;
+ }
+
+ static void vmw_master_drop(struct drm_device *dev,
+@@ -1214,16 +1193,9 @@ static void vmw_master_drop(struct drm_device *dev,
+ }
+
+ ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
+- vmw_execbuf_release_pinned_bo(dev_priv);
+
+- if (!dev_priv->enable_fb) {
+- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
+- if (unlikely(ret != 0))
+- DRM_ERROR("Unable to clean VRAM on master drop.\n");
+- vmw_kms_restore_vga(dev_priv);
+- vmw_3d_resource_dec(dev_priv, true);
+- vmw_write(dev_priv, SVGA_REG_TRACES, 1);
+- }
++ if (!dev_priv->enable_fb)
++ vmw_svga_disable(dev_priv);
+
+ dev_priv->active_master = &dev_priv->fbdev_master;
+ ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
+@@ -1233,6 +1205,74 @@ static void vmw_master_drop(struct drm_device *dev,
+ vmw_fb_on(dev_priv);
+ }
+
++/**
++ * __vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM.
++ *
++ * @dev_priv: Pointer to device private struct.
++ * Needs the reservation sem to be held in non-exclusive mode.
++ */
++void __vmw_svga_enable(struct vmw_private *dev_priv)
++{
++ spin_lock(&dev_priv->svga_lock);
++ if (!dev_priv->bdev.man[TTM_PL_VRAM].use_type) {
++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE);
++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = true;
++ }
++ spin_unlock(&dev_priv->svga_lock);
++}
++
++/**
++ * vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM.
++ *
++ * @dev_priv: Pointer to device private struct.
++ */
++void vmw_svga_enable(struct vmw_private *dev_priv)
++{
++ ttm_read_lock(&dev_priv->reservation_sem, false);
++ __vmw_svga_enable(dev_priv);
++ ttm_read_unlock(&dev_priv->reservation_sem);
++}
++
++/**
++ * __vmw_svga_disable - Disable SVGA mode and use of VRAM.
++ *
++ * @dev_priv: Pointer to device private struct.
++ * Needs the reservation sem to be held in exclusive mode.
++ * Will not empty VRAM. VRAM must be emptied by caller.
++ */
++void __vmw_svga_disable(struct vmw_private *dev_priv)
++{
++ spin_lock(&dev_priv->svga_lock);
++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) {
++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false;
++ vmw_write(dev_priv, SVGA_REG_ENABLE,
++ SVGA_REG_ENABLE_ENABLE_HIDE);
++ }
++ spin_unlock(&dev_priv->svga_lock);
++}
++
++/**
++ * vmw_svga_disable - Disable SVGA_MODE, and use of VRAM. Keep the fifo
++ * running.
++ *
++ * @dev_priv: Pointer to device private struct.
++ * Will empty VRAM.
++ */
++void vmw_svga_disable(struct vmw_private *dev_priv)
++{
++ ttm_write_lock(&dev_priv->reservation_sem, false);
++ spin_lock(&dev_priv->svga_lock);
++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) {
++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false;
++ vmw_write(dev_priv, SVGA_REG_ENABLE,
++ SVGA_REG_ENABLE_ENABLE_HIDE);
++ spin_unlock(&dev_priv->svga_lock);
++ if (ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM))
++ DRM_ERROR("Failed evicting VRAM buffers.\n");
++ } else
++ spin_unlock(&dev_priv->svga_lock);
++ ttm_write_unlock(&dev_priv->reservation_sem);
++}
+
+ static void vmw_remove(struct pci_dev *pdev)
+ {
+@@ -1250,21 +1290,21 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
+
+ switch (val) {
+ case PM_HIBERNATION_PREPARE:
+- case PM_SUSPEND_PREPARE:
+ ttm_suspend_lock(&dev_priv->reservation_sem);
+
+- /**
++ /*
+ * This empties VRAM and unbinds all GMR bindings.
+ * Buffer contents is moved to swappable memory.
+ */
+ vmw_execbuf_release_pinned_bo(dev_priv);
+ vmw_resource_evict_all(dev_priv);
++ vmw_release_device_early(dev_priv);
+ ttm_bo_swapout_all(&dev_priv->bdev);
+-
++ vmw_fence_fifo_down(dev_priv->fman);
+ break;
+ case PM_POST_HIBERNATION:
+- case PM_POST_SUSPEND:
+ case PM_POST_RESTORE:
++ vmw_fence_fifo_up(dev_priv->fman);
+ ttm_suspend_unlock(&dev_priv->reservation_sem);
+
+ break;
+@@ -1276,20 +1316,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
+ return 0;
+ }
+
+-/**
+- * These might not be needed with the virtual SVGA device.
+- */
+-
+ static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+ {
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+- if (dev_priv->num_3d_resources != 0) {
+- DRM_INFO("Can't suspend or hibernate "
+- "while 3D resources are active.\n");
++ if (dev_priv->refuse_hibernation)
+ return -EBUSY;
+- }
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+@@ -1321,56 +1354,62 @@ static int vmw_pm_resume(struct device *kdev)
+ return vmw_pci_resume(pdev);
+ }
+
+-static int vmw_pm_prepare(struct device *kdev)
++static int vmw_pm_freeze(struct device *kdev)
+ {
+ struct pci_dev *pdev = to_pci_dev(kdev);
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+- /**
+- * Release 3d reference held by fbdev and potentially
+- * stop fifo.
+- */
+ dev_priv->suspended = true;
+ if (dev_priv->enable_fb)
+- vmw_3d_resource_dec(dev_priv, true);
+-
+- if (dev_priv->num_3d_resources != 0) {
+-
+- DRM_INFO("Can't suspend or hibernate "
+- "while 3D resources are active.\n");
++ vmw_fifo_resource_dec(dev_priv);
+
++ if (atomic_read(&dev_priv->num_fifo_resources) != 0) {
++ DRM_ERROR("Can't hibernate while 3D resources are active.\n");
+ if (dev_priv->enable_fb)
+- vmw_3d_resource_inc(dev_priv, true);
++ vmw_fifo_resource_inc(dev_priv);
++ WARN_ON(vmw_request_device_late(dev_priv));
+ dev_priv->suspended = false;
+ return -EBUSY;
+ }
+
++ if (dev_priv->enable_fb)
++ __vmw_svga_disable(dev_priv);
++
++ vmw_release_device_late(dev_priv);
++
+ return 0;
+ }
+
+-static void vmw_pm_complete(struct device *kdev)
++static int vmw_pm_restore(struct device *kdev)
+ {
+ struct pci_dev *pdev = to_pci_dev(kdev);
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
++ int ret;
+
+ vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
+ (void) vmw_read(dev_priv, SVGA_REG_ID);
+
+- /**
+- * Reclaim 3d reference held by fbdev and potentially
+- * start fifo.
+- */
+ if (dev_priv->enable_fb)
+- vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
++
++ ret = vmw_request_device(dev_priv);
++ if (ret)
++ return ret;
++
++ if (dev_priv->enable_fb)
++ __vmw_svga_enable(dev_priv);
+
+ dev_priv->suspended = false;
++
++ return 0;
+ }
+
+ static const struct dev_pm_ops vmw_pm_ops = {
+- .prepare = vmw_pm_prepare,
+- .complete = vmw_pm_complete,
++ .freeze = vmw_pm_freeze,
++ .thaw = vmw_pm_restore,
++ .restore = vmw_pm_restore,
+ .suspend = vmw_pm_suspend,
+ .resume = vmw_pm_resume,
+ };
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+index d26a6daa9719..a5f221eaf076 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+@@ -484,6 +484,7 @@ struct vmw_private {
+
+ bool stealth;
+ bool enable_fb;
++ spinlock_t svga_lock;
+
+ /**
+ * Master management.
+@@ -493,9 +494,10 @@ struct vmw_private {
+ struct vmw_master fbdev_master;
+ struct notifier_block pm_nb;
+ bool suspended;
++ bool refuse_hibernation;
+
+ struct mutex release_mutex;
+- uint32_t num_3d_resources;
++ atomic_t num_fifo_resources;
+
+ /*
+ * Replace this with an rwsem as soon as we have down_xx_interruptible()
+@@ -587,8 +589,9 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv,
+ return val;
+ }
+
+-int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga);
+-void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga);
++extern void vmw_svga_enable(struct vmw_private *dev_priv);
++extern void vmw_svga_disable(struct vmw_private *dev_priv);
++
+
+ /**
+ * GMR utilities - vmwgfx_gmr.c
+@@ -1116,4 +1119,14 @@ static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv)
+ {
+ return (struct ttm_mem_global *) dev_priv->mem_global_ref.object;
+ }
++
++static inline void vmw_fifo_resource_inc(struct vmw_private *dev_priv)
++{
++ atomic_inc(&dev_priv->num_fifo_resources);
++}
++
++static inline void vmw_fifo_resource_dec(struct vmw_private *dev_priv)
++{
++ atomic_dec(&dev_priv->num_fifo_resources);
++}
+ #endif
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+index 0a474f391fad..0e062613a7db 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -596,7 +596,10 @@ int vmw_fb_off(struct vmw_private *vmw_priv)
+
+ info = vmw_priv->fb_info;
+ par = info->par;
++ if (!par->bo_ptr)
++ return 0;
+
++ vmw_kms_save_vga(vmw_priv);
+ spin_lock_irqsave(&par->dirty.lock, flags);
+ par->dirty.active = false;
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
+@@ -648,6 +651,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv)
+ spin_lock_irqsave(&par->dirty.lock, flags);
+ par->dirty.active = true;
+ spin_unlock_irqrestore(&par->dirty.lock, flags);
++ vmw_kms_restore_vga(vmw_priv);
+
+ err_no_buffer:
+ vmw_fb_set_par(info);
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+index 39f2b03888e7..cd5d9f3fe0e0 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+@@ -98,7 +98,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
+ uint32_t max;
+ uint32_t min;
+- uint32_t dummy;
+
+ fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
+ fifo->static_buffer = vmalloc(fifo->static_buffer_size);
+@@ -112,10 +111,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
+ mutex_init(&fifo->fifo_mutex);
+ init_rwsem(&fifo->rwsem);
+
+- /*
+- * Allow mapping the first page read-only to user-space.
+- */
+-
+ DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH));
+ DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT));
+ DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL));
+@@ -123,7 +118,9 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
+ dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE);
+ dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE);
+ dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES);
+- vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
++
++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE_ENABLE_HIDE);
++ vmw_write(dev_priv, SVGA_REG_TRACES, 0);
+
+ min = 4;
+ if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)
+@@ -155,7 +152,8 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
+ atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno);
+ iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE);
+ vmw_marker_queue_init(&fifo->marker_queue);
+- return vmw_fifo_send_fence(dev_priv, &dummy);
++
++ return 0;
+ }
+
+ void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+index 5c289f748ab4..53579f278b63 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+@@ -280,6 +280,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
+ }
+
+ vmw_fb_off(dev_priv);
++ vmw_svga_enable(dev_priv);
+
+ crtc->primary->fb = fb;
+ encoder->crtc = crtc;
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c
+index 04a64b8cd3cd..f06d60f41fa7 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c
+@@ -574,7 +574,7 @@ void vmw_mob_unbind(struct vmw_private *dev_priv,
+ vmw_fence_single_bo(bo, NULL);
+ ttm_bo_unreserve(bo);
+ }
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ }
+
+ /*
+@@ -627,7 +627,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv,
+ mob->pt_level += VMW_MOBFMT_PTDEPTH_1 - SVGA3D_MOBFMT_PTDEPTH_1;
+ }
+
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
+ if (unlikely(cmd == NULL)) {
+@@ -648,7 +648,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv,
+ return 0;
+
+ out_no_cmd_space:
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ if (pt_set_up)
+ ttm_bo_unref(&mob->pt_bo);
+
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+index 7dc591d04d9a..9e8eb364a6ac 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+@@ -332,6 +332,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
+ }
+
+ vmw_fb_off(dev_priv);
++ vmw_svga_enable(dev_priv);
+
+ if (mode->hdisplay != crtc->mode.hdisplay ||
+ mode->vdisplay != crtc->mode.vdisplay) {
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+index 6a4584a43aa6..6110a433ebfe 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+@@ -165,7 +165,7 @@ static int vmw_gb_shader_create(struct vmw_resource *res)
+ cmd->body.type = shader->type;
+ cmd->body.sizeInBytes = shader->size;
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+
+ return 0;
+
+@@ -275,7 +275,7 @@ static int vmw_gb_shader_destroy(struct vmw_resource *res)
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+ mutex_unlock(&dev_priv->binding_mutex);
+ vmw_resource_release_id(res);
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+index 4ecdbf3e59da..4d0c98edeb6a 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+@@ -340,7 +340,7 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res)
+ dev_priv->used_memory_size -= res->backup_size;
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+ }
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ }
+
+ /**
+@@ -576,14 +576,14 @@ static int vmw_surface_init(struct vmw_private *dev_priv,
+
+ BUG_ON(res_free == NULL);
+ if (!dev_priv->has_mob)
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+ ret = vmw_resource_init(dev_priv, res, true, res_free,
+ (dev_priv->has_mob) ? &vmw_gb_surface_func :
+ &vmw_legacy_surface_func);
+
+ if (unlikely(ret != 0)) {
+ if (!dev_priv->has_mob)
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ res_free(res);
+ return ret;
+ }
+@@ -1028,7 +1028,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res)
+ if (likely(res->id != -1))
+ return 0;
+
+- (void) vmw_3d_resource_inc(dev_priv, false);
++ vmw_fifo_resource_inc(dev_priv);
+ ret = vmw_resource_alloc_id(res);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to allocate a surface id.\n");
+@@ -1068,7 +1068,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res)
+ out_no_fifo:
+ vmw_resource_release_id(res);
+ out_no_id:
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+ return ret;
+ }
+
+@@ -1213,7 +1213,7 @@ static int vmw_gb_surface_destroy(struct vmw_resource *res)
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
+ mutex_unlock(&dev_priv->binding_mutex);
+ vmw_resource_release_id(res);
+- vmw_3d_resource_dec(dev_priv, false);
++ vmw_fifo_resource_dec(dev_priv);
+
+ return 0;
+ }
+--
+2.4.3
+