drm/i915: Register a shrinker to free inactive lists under memory pressure
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / i915_drv.c
index 0692622..c57c174 100644 (file)
 
 #include "drm_pciids.h"
 #include <linux/console.h>
+#include "drm_crtc_helper.h"
 
-static unsigned int i915_modeset = -1;
+static int i915_modeset = -1;
 module_param_named(modeset, i915_modeset, int, 0400);
 
 unsigned int i915_fbpercrtc = 0;
 module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
 
+unsigned int i915_powersave = 1;
+module_param_named(powersave, i915_powersave, int, 0400);
+
+static struct drm_driver driver;
+
 static struct pci_device_id pciidlist[] = {
        i915_PCI_IDS
 };
@@ -55,8 +61,8 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (!dev || !dev_priv) {
-               printk(KERN_ERR "dev: %p, dev_priv: %p\n", dev, dev_priv);
-               printk(KERN_ERR "DRM not initialized, aborting suspend.\n");
+               DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv);
+               DRM_ERROR("DRM not initialized, aborting suspend.\n");
                return -ENODEV;
        }
 
@@ -65,15 +71,17 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
 
        pci_save_state(dev->pdev);
 
-       i915_save_state(dev);
-
        /* If KMS is active, we do the leavevt stuff here */
-       if (drm_core_check_feature(dev, DRIVER_MODESET) && i915_gem_idle(dev)) {
-               dev_err(&dev->pdev->dev, "GEM idle failed, aborting suspend\n");
-               return -EBUSY;
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               if (i915_gem_idle(dev))
+                       dev_err(&dev->pdev->dev,
+                               "GEM idle failed, resume may fail\n");
+               drm_irq_uninstall(dev);
        }
 
-       intel_opregion_free(dev);
+       i915_save_state(dev);
+
+       intel_opregion_free(dev, 1);
 
        if (state.event == PM_EVENT_SUSPEND) {
                /* Shut down the device */
@@ -81,6 +89,8 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
                pci_set_power_state(dev->pdev, PCI_D3hot);
        }
 
+       dev_priv->suspended = 1;
+
        return 0;
 }
 
@@ -97,7 +107,7 @@ static int i915_resume(struct drm_device *dev)
 
        i915_restore_state(dev);
 
-       intel_opregion_init(dev);
+       intel_opregion_init(dev, 1);
 
        /* KMS EnterVT equivalent */
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
@@ -108,11 +118,173 @@ static int i915_resume(struct drm_device *dev)
                if (ret != 0)
                        ret = -1;
                mutex_unlock(&dev->struct_mutex);
+
+               drm_irq_install(dev);
+       }
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               /* Resume the modeset for every activated CRTC */
+               drm_helper_resume_force_mode(dev);
        }
 
+       dev_priv->suspended = 0;
+
        return ret;
 }
 
+/**
+ * i965_reset - reset chip after a hang
+ * @dev: drm device to reset
+ * @flags: reset domains
+ *
+ * Reset the chip.  Useful if a hang is detected. Returns zero on successful
+ * reset or otherwise an error code.
+ *
+ * Procedure is fairly simple:
+ *   - reset the chip using the reset reg
+ *   - re-init context state
+ *   - re-init hardware status page
+ *   - re-init ring buffer
+ *   - re-init interrupt state
+ *   - re-init display
+ */
+int i965_reset(struct drm_device *dev, u8 flags)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       unsigned long timeout;
+       u8 gdrst;
+       /*
+        * We really should only reset the display subsystem if we actually
+        * need to
+        */
+       bool need_display = true;
+
+       mutex_lock(&dev->struct_mutex);
+
+       /*
+        * Clear request list
+        */
+       i915_gem_retire_requests(dev);
+
+       if (need_display)
+               i915_save_display(dev);
+
+       if (IS_I965G(dev) || IS_G4X(dev)) {
+               /*
+                * Set the domains we want to reset, then the reset bit (bit 0).
+                * Clear the reset bit after a while and wait for hardware status
+                * bit (bit 1) to be set
+                */
+               pci_read_config_byte(dev->pdev, GDRST, &gdrst);
+               pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0));
+               udelay(50);
+               pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe);
+
+               /* ...we don't want to loop forever though, 500ms should be plenty */
+              timeout = jiffies + msecs_to_jiffies(500);
+               do {
+                       udelay(100);
+                       pci_read_config_byte(dev->pdev, GDRST, &gdrst);
+               } while ((gdrst & 0x1) && time_after(timeout, jiffies));
+
+               if (gdrst & 0x1) {
+                       WARN(true, "i915: Failed to reset chip\n");
+                       mutex_unlock(&dev->struct_mutex);
+                       return -EIO;
+               }
+       } else {
+               DRM_ERROR("Error occurred. Don't know how to reset this chip.\n");
+               return -ENODEV;
+       }
+
+       /* Ok, now get things going again... */
+
+       /*
+        * Everything depends on having the GTT running, so we need to start
+        * there.  Fortunately we don't need to do this unless we reset the
+        * chip at a PCI level.
+        *
+        * Next we need to restore the context, but we don't use those
+        * yet either...
+        *
+        * Ring buffer needs to be re-initialized in the KMS case, or if X
+        * was running at the time of the reset (i.e. we weren't VT
+        * switched away).
+        */
+       if (drm_core_check_feature(dev, DRIVER_MODESET) ||
+           !dev_priv->mm.suspended) {
+               drm_i915_ring_buffer_t *ring = &dev_priv->ring;
+               struct drm_gem_object *obj = ring->ring_obj;
+               struct drm_i915_gem_object *obj_priv = obj->driver_private;
+               dev_priv->mm.suspended = 0;
+
+               /* Stop the ring if it's running. */
+               I915_WRITE(PRB0_CTL, 0);
+               I915_WRITE(PRB0_TAIL, 0);
+               I915_WRITE(PRB0_HEAD, 0);
+
+               /* Initialize the ring. */
+               I915_WRITE(PRB0_START, obj_priv->gtt_offset);
+               I915_WRITE(PRB0_CTL,
+                          ((obj->size - 4096) & RING_NR_PAGES) |
+                          RING_NO_REPORT |
+                          RING_VALID);
+               if (!drm_core_check_feature(dev, DRIVER_MODESET))
+                       i915_kernel_lost_context(dev);
+               else {
+                       ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
+                       ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
+                       ring->space = ring->head - (ring->tail + 8);
+                       if (ring->space < 0)
+                               ring->space += ring->Size;
+               }
+
+               mutex_unlock(&dev->struct_mutex);
+               drm_irq_uninstall(dev);
+               drm_irq_install(dev);
+               mutex_lock(&dev->struct_mutex);
+       }
+
+       /*
+        * Display needs restore too...
+        */
+       if (need_display)
+               i915_restore_display(dev);
+
+       mutex_unlock(&dev->struct_mutex);
+       return 0;
+}
+
+
+static int __devinit
+i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       return drm_get_dev(pdev, ent, &driver);
+}
+
+static void
+i915_pci_remove(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+
+       drm_put_dev(dev);
+}
+
+static int
+i915_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+
+       return i915_suspend(dev, state);
+}
+
+static int
+i915_pci_resume(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+
+       return i915_resume(dev);
+}
+
 static struct vm_operations_struct i915_gem_vm_ops = {
        .fault = i915_gem_fault,
        .open = drm_gem_vm_open,
@@ -146,8 +318,10 @@ static struct drm_driver driver = {
        .get_reg_ofs = drm_core_get_reg_ofs,
        .master_create = i915_master_create,
        .master_destroy = i915_master_destroy,
-       .proc_init = i915_gem_proc_init,
-       .proc_cleanup = i915_gem_proc_cleanup,
+#if defined(CONFIG_DEBUG_FS)
+       .debugfs_init = i915_debugfs_init,
+       .debugfs_cleanup = i915_debugfs_cleanup,
+#endif
        .gem_init_object = i915_gem_init_object,
        .gem_free_object = i915_gem_free_object,
        .gem_vm_ops = &i915_gem_vm_ops,
@@ -168,6 +342,12 @@ static struct drm_driver driver = {
        .pci_driver = {
                 .name = DRIVER_NAME,
                 .id_table = pciidlist,
+                .probe = i915_pci_probe,
+                .remove = i915_pci_remove,
+#ifdef CONFIG_PM
+                .resume = i915_pci_resume,
+                .suspend = i915_pci_suspend,
+#endif
        },
 
        .name = DRIVER_NAME,
@@ -182,6 +362,8 @@ static int __init i915_init(void)
 {
        driver.num_ioctls = i915_max_ioctl;
 
+       i915_gem_shrinker_init();
+
        /*
         * If CONFIG_DRM_I915_KMS is set, default to KMS unless
         * explicitly disabled with the module pararmeter.
@@ -208,6 +390,7 @@ static int __init i915_init(void)
 
 static void __exit i915_exit(void)
 {
+       i915_gem_shrinker_exit();
        drm_exit(&driver);
 }