drm/vmwgfx: Add new-style PM hooks to improve hibernation behavior

Add the new-style PM hooks prepare and complete. This allows us to
power up the device again after the hibernation image has been created, and
display output will thus be active until the VM is finally powered off.

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 91eeade..f3e481f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -752,34 +752,10 @@
 		 */
 		ttm_bo_swapout_all(&dev_priv->bdev);
 
-		/**
-		 * 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);
-
 		break;
 	case PM_POST_HIBERNATION:
 	case PM_POST_SUSPEND:
 	case PM_POST_RESTORE:
-		if (!dev_priv->suspended) {
-			printk(KERN_WARNING
-			       "[%s] Driver is not suspended at resume"
-			       " point.\n", VMWGFX_DRIVER_NAME);
-
-			break;
-		}
-
-		/**
-		 * Reclaim 3d reference held by fbdev and potentially
-		 * start fifo.
-		 */
-		if (dev_priv->enable_fb)
-			vmw_3d_resource_inc(dev_priv);
-
-		dev_priv->suspended = false;
 		ttm_suspend_unlock(&vmaster->lock);
 
 		break;
@@ -795,7 +771,7 @@
  * These might not be needed with the virtual SVGA device.
  */
 
-int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+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);
@@ -812,13 +788,81 @@
 	return 0;
 }
 
-int vmw_pci_resume(struct pci_dev *pdev)
+static int vmw_pci_resume(struct pci_dev *pdev)
 {
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
 	return pci_enable_device(pdev);
 }
 
+static int vmw_pm_suspend(struct device *kdev)
+{
+	struct pci_dev *pdev = to_pci_dev(kdev);
+	struct pm_message dummy;
+
+	dummy.event = 0;
+
+	return vmw_pci_suspend(pdev, dummy);
+}
+
+static int vmw_pm_resume(struct device *kdev)
+{
+	struct pci_dev *pdev = to_pci_dev(kdev);
+
+	return vmw_pci_resume(pdev);
+}
+
+static int vmw_pm_prepare(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);
+
+	if (dev_priv->num_3d_resources != 0) {
+
+		DRM_INFO("Can't suspend or hibernate "
+			 "while 3D resources are active.\n");
+
+		if (dev_priv->enable_fb)
+			vmw_3d_resource_inc(dev_priv);
+		dev_priv->suspended = false;
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void vmw_pm_complete(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);
+
+	/**
+	 * Reclaim 3d reference held by fbdev and potentially
+	 * start fifo.
+	 */
+	if (dev_priv->enable_fb)
+		vmw_3d_resource_inc(dev_priv);
+
+	dev_priv->suspended = false;
+}
+
+static const struct dev_pm_ops vmw_pm_ops = {
+	.prepare = vmw_pm_prepare,
+	.complete = vmw_pm_complete,
+	.suspend = vmw_pm_suspend,
+	.resume = vmw_pm_resume,
+};
+
 static struct drm_driver driver = {
 	.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
 	DRIVER_MODESET,
@@ -852,15 +896,16 @@
 #if defined(CONFIG_COMPAT)
 		 .compat_ioctl = drm_compat_ioctl,
 #endif
-		 },
+	},
 	.pci_driver = {
-		       .name = VMWGFX_DRIVER_NAME,
-		       .id_table = vmw_pci_id_list,
-		       .probe = vmw_probe,
-		       .remove = vmw_remove,
-		       .suspend = vmw_pci_suspend,
-		       .resume = vmw_pci_resume
-		       },
+		 .name = VMWGFX_DRIVER_NAME,
+		 .id_table = vmw_pci_id_list,
+		 .probe = vmw_probe,
+		 .remove = vmw_remove,
+		 .driver = {
+			 .pm = &vmw_pm_ops
+		 }
+	 },
 	.name = VMWGFX_DRIVER_NAME,
 	.desc = VMWGFX_DRIVER_DESC,
 	.date = VMWGFX_DRIVER_DATE,