Merge branch 'gpu-switcher' of /ssd/git//linux-2.6 into drm-next-stage
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / i915_dma.c
index 3e658d6..8bfc0bb 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/vgaarb.h>
 #include <linux/acpi.h>
 #include <linux/pnp.h>
+#include <linux/vga_switcheroo.h>
 
 /* Really want an OS-independent resettable timer.  Would like to have
  * this loop run for (eg) 3 sec, but have the timer reset every time
@@ -1381,6 +1382,32 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state)
                return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
 }
 
+static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+       if (state == VGA_SWITCHEROO_ON) {
+               printk(KERN_INFO "i915: switched off\n");
+               /* i915 resume handler doesn't set to D0 */
+               pci_set_power_state(dev->pdev, PCI_D0);
+               i915_resume(dev);
+       } else {
+               printk(KERN_ERR "i915: switched off\n");
+               i915_suspend(dev, pmm);
+       }
+}
+
+static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
 static int i915_load_modeset_init(struct drm_device *dev,
                                  unsigned long prealloc_start,
                                  unsigned long prealloc_size,
@@ -1442,6 +1469,12 @@ static int i915_load_modeset_init(struct drm_device *dev,
        if (ret)
                goto destroy_ringbuffer;
 
+       ret = vga_switcheroo_register_client(dev->pdev,
+                                            i915_switcheroo_set_state,
+                                            i915_switcheroo_can_switch);
+       if (ret)
+               goto destroy_ringbuffer;
+
        intel_modeset_init(dev);
 
        ret = drm_irq_install(dev);
@@ -1733,6 +1766,7 @@ int i915_driver_unload(struct drm_device *dev)
                        dev_priv->child_dev_num = 0;
                }
                drm_irq_uninstall(dev);
+               vga_switcheroo_unregister_client(dev->pdev);
                vga_client_register(dev->pdev, NULL, NULL, NULL);
        }
 
@@ -1802,6 +1836,7 @@ void i915_driver_lastclose(struct drm_device * dev)
 
        if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
                drm_fb_helper_restore();
+               vga_switcheroo_process_delayed_switch();
                return;
        }