drm/i915: only enable hotplug for detected outputs
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / i915_irq.c
index 4b0bcbd..1733eea 100644 (file)
  *
  */
 
+#include <linux/sysrq.h>
 #include "drmP.h"
 #include "drm.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
+#include "i915_trace.h"
 #include "intel_drv.h"
 
 #define MAX_NOPID ((u32)~0)
  * we leave them always unmasked in IMR and then control enabling them through
  * PIPESTAT alone.
  */
-#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
-                                  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |  \
-                                  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
+#define I915_INTERRUPT_ENABLE_FIX                      \
+       (I915_ASLE_INTERRUPT |                          \
+        I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |          \
+        I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |          \
+        I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |  \
+        I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT |  \
+        I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
 
 /** Interrupts that we mask and unmask at runtime. */
 #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
                                         DRM_I915_VBLANK_PIPE_B)
 
 void
+ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->gt_irq_mask_reg & mask) != 0) {
+               dev_priv->gt_irq_mask_reg &= ~mask;
+               I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+               (void) I915_READ(GTIMR);
+       }
+}
+
+static inline void
+ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->gt_irq_mask_reg & mask) != mask) {
+               dev_priv->gt_irq_mask_reg |= mask;
+               I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+               (void) I915_READ(GTIMR);
+       }
+}
+
+/* For display hotplug interrupt */
+void
+ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != 0) {
+               dev_priv->irq_mask_reg &= ~mask;
+               I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(DEIMR);
+       }
+}
+
+static inline void
+ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != mask) {
+               dev_priv->irq_mask_reg |= mask;
+               I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(DEIMR);
+       }
+}
+
+void
 i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
        if ((dev_priv->irq_mask_reg & mask) != 0) {
@@ -113,6 +160,20 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 }
 
 /**
+ * intel_enable_asle - enable ASLE interrupt for OpRegion
+ */
+void intel_enable_asle (struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       if (IS_IRONLAKE(dev))
+               ironlake_enable_display_irq(dev_priv, DE_GSE);
+       else
+               i915_enable_pipestat(dev_priv, 1,
+                                    I915_LEGACY_BLC_EVENT_ENABLE);
+}
+
+/**
  * i915_pipe_enabled - check if a pipe is enabled
  * @dev: DRM device
  * @pipe: pipe to check
@@ -147,7 +208,8 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
        low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-               DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+               DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
+                               "pipe %d\n", pipe);
                return 0;
        }
 
@@ -176,7 +238,8 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
        int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-               DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+               DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
+                                       "pipe %d\n", pipe);
                return 0;
        }
 
@@ -191,11 +254,304 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
                                                    hotplug_work);
        struct drm_device *dev = dev_priv->dev;
-
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+
+       if (mode_config->num_connector) {
+               list_for_each_entry(connector, &mode_config->connector_list, head) {
+                       struct intel_output *intel_output = to_intel_output(connector);
+       
+                       if (intel_output->hot_plug)
+                               (*intel_output->hot_plug) (intel_output);
+               }
+       }
        /* Just fire off a uevent and let userspace tell us what to do */
        drm_sysfs_hotplug_event(dev);
 }
 
+irqreturn_t ironlake_irq_handler(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       int ret = IRQ_NONE;
+       u32 de_iir, gt_iir, de_ier, pch_iir;
+       u32 new_de_iir, new_gt_iir, new_pch_iir;
+       struct drm_i915_master_private *master_priv;
+
+       /* disable master interrupt before clearing iir  */
+       de_ier = I915_READ(DEIER);
+       I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
+       (void)I915_READ(DEIER);
+
+       de_iir = I915_READ(DEIIR);
+       gt_iir = I915_READ(GTIIR);
+       pch_iir = I915_READ(SDEIIR);
+
+       for (;;) {
+               if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
+                       break;
+
+               ret = IRQ_HANDLED;
+
+               /* should clear PCH hotplug event before clear CPU irq */
+               I915_WRITE(SDEIIR, pch_iir);
+               new_pch_iir = I915_READ(SDEIIR);
+
+               I915_WRITE(DEIIR, de_iir);
+               new_de_iir = I915_READ(DEIIR);
+               I915_WRITE(GTIIR, gt_iir);
+               new_gt_iir = I915_READ(GTIIR);
+
+               if (dev->primary->master) {
+                       master_priv = dev->primary->master->driver_priv;
+                       if (master_priv->sarea_priv)
+                               master_priv->sarea_priv->last_dispatch =
+                                       READ_BREADCRUMB(dev_priv);
+               }
+
+               if (gt_iir & GT_USER_INTERRUPT) {
+                       u32 seqno = i915_get_gem_seqno(dev);
+                       dev_priv->mm.irq_gem_seqno = seqno;
+                       trace_i915_gem_request_complete(dev, seqno);
+                       DRM_WAKEUP(&dev_priv->irq_queue);
+               }
+
+               if (de_iir & DE_GSE)
+                       ironlake_opregion_gse_intr(dev);
+
+               /* check event from PCH */
+               if ((de_iir & DE_PCH_EVENT) &&
+                       (pch_iir & SDE_HOTPLUG_MASK)) {
+                       queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+               }
+
+               de_iir = new_de_iir;
+               gt_iir = new_gt_iir;
+               pch_iir = new_pch_iir;
+       }
+
+       I915_WRITE(DEIER, de_ier);
+       (void)I915_READ(DEIER);
+
+       return ret;
+}
+
+/**
+ * i915_error_work_func - do process context error handling work
+ * @work: work struct
+ *
+ * Fire an error uevent so userspace can see that a hang or error
+ * was detected.
+ */
+static void i915_error_work_func(struct work_struct *work)
+{
+       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+                                                   error_work);
+       struct drm_device *dev = dev_priv->dev;
+       char *error_event[] = { "ERROR=1", NULL };
+       char *reset_event[] = { "RESET=1", NULL };
+       char *reset_done_event[] = { "ERROR=0", NULL };
+
+       DRM_DEBUG_DRIVER("generating error event\n");
+       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
+
+       if (atomic_read(&dev_priv->mm.wedged)) {
+               if (IS_I965G(dev)) {
+                       DRM_DEBUG_DRIVER("resetting chip\n");
+                       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
+                       if (!i965_reset(dev, GDRST_RENDER)) {
+                               atomic_set(&dev_priv->mm.wedged, 0);
+                               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
+                       }
+               } else {
+                       DRM_DEBUG_DRIVER("reboot required\n");
+               }
+       }
+}
+
+/**
+ * i915_capture_error_state - capture an error record for later analysis
+ * @dev: drm device
+ *
+ * Should be called when an error is detected (either a hang or an error
+ * interrupt) to capture error state from the time of the error.  Fills
+ * out a structure which becomes available in debugfs for user level tools
+ * to pick up.
+ */
+static void i915_capture_error_state(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_error_state *error;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->error_lock, flags);
+       if (dev_priv->first_error)
+               goto out;
+
+       error = kmalloc(sizeof(*error), GFP_ATOMIC);
+       if (!error) {
+               DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n");
+               goto out;
+       }
+
+       error->eir = I915_READ(EIR);
+       error->pgtbl_er = I915_READ(PGTBL_ER);
+       error->pipeastat = I915_READ(PIPEASTAT);
+       error->pipebstat = I915_READ(PIPEBSTAT);
+       error->instpm = I915_READ(INSTPM);
+       if (!IS_I965G(dev)) {
+               error->ipeir = I915_READ(IPEIR);
+               error->ipehr = I915_READ(IPEHR);
+               error->instdone = I915_READ(INSTDONE);
+               error->acthd = I915_READ(ACTHD);
+       } else {
+               error->ipeir = I915_READ(IPEIR_I965);
+               error->ipehr = I915_READ(IPEHR_I965);
+               error->instdone = I915_READ(INSTDONE_I965);
+               error->instps = I915_READ(INSTPS);
+               error->instdone1 = I915_READ(INSTDONE1);
+               error->acthd = I915_READ(ACTHD_I965);
+       }
+
+       do_gettimeofday(&error->time);
+
+       dev_priv->first_error = error;
+
+out:
+       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+}
+
+/**
+ * i915_handle_error - handle an error interrupt
+ * @dev: drm device
+ *
+ * Do some basic checking of regsiter state at error interrupt time and
+ * dump it to the syslog.  Also call i915_capture_error_state() to make
+ * sure we get a record and make it available in debugfs.  Fire a uevent
+ * so userspace knows something bad happened (should trigger collection
+ * of a ring dump etc.).
+ */
+static void i915_handle_error(struct drm_device *dev, bool wedged)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 eir = I915_READ(EIR);
+       u32 pipea_stats = I915_READ(PIPEASTAT);
+       u32 pipeb_stats = I915_READ(PIPEBSTAT);
+
+       i915_capture_error_state(dev);
+
+       printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
+              eir);
+
+       if (IS_G4X(dev)) {
+               if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
+                       u32 ipeir = I915_READ(IPEIR_I965);
+
+                       printk(KERN_ERR "  IPEIR: 0x%08x\n",
+                              I915_READ(IPEIR_I965));
+                       printk(KERN_ERR "  IPEHR: 0x%08x\n",
+                              I915_READ(IPEHR_I965));
+                       printk(KERN_ERR "  INSTDONE: 0x%08x\n",
+                              I915_READ(INSTDONE_I965));
+                       printk(KERN_ERR "  INSTPS: 0x%08x\n",
+                              I915_READ(INSTPS));
+                       printk(KERN_ERR "  INSTDONE1: 0x%08x\n",
+                              I915_READ(INSTDONE1));
+                       printk(KERN_ERR "  ACTHD: 0x%08x\n",
+                              I915_READ(ACTHD_I965));
+                       I915_WRITE(IPEIR_I965, ipeir);
+                       (void)I915_READ(IPEIR_I965);
+               }
+               if (eir & GM45_ERROR_PAGE_TABLE) {
+                       u32 pgtbl_err = I915_READ(PGTBL_ER);
+                       printk(KERN_ERR "page table error\n");
+                       printk(KERN_ERR "  PGTBL_ER: 0x%08x\n",
+                              pgtbl_err);
+                       I915_WRITE(PGTBL_ER, pgtbl_err);
+                       (void)I915_READ(PGTBL_ER);
+               }
+       }
+
+       if (IS_I9XX(dev)) {
+               if (eir & I915_ERROR_PAGE_TABLE) {
+                       u32 pgtbl_err = I915_READ(PGTBL_ER);
+                       printk(KERN_ERR "page table error\n");
+                       printk(KERN_ERR "  PGTBL_ER: 0x%08x\n",
+                              pgtbl_err);
+                       I915_WRITE(PGTBL_ER, pgtbl_err);
+                       (void)I915_READ(PGTBL_ER);
+               }
+       }
+
+       if (eir & I915_ERROR_MEMORY_REFRESH) {
+               printk(KERN_ERR "memory refresh error\n");
+               printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
+                      pipea_stats);
+               printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
+                      pipeb_stats);
+               /* pipestat has already been acked */
+       }
+       if (eir & I915_ERROR_INSTRUCTION) {
+               printk(KERN_ERR "instruction error\n");
+               printk(KERN_ERR "  INSTPM: 0x%08x\n",
+                      I915_READ(INSTPM));
+               if (!IS_I965G(dev)) {
+                       u32 ipeir = I915_READ(IPEIR);
+
+                       printk(KERN_ERR "  IPEIR: 0x%08x\n",
+                              I915_READ(IPEIR));
+                       printk(KERN_ERR "  IPEHR: 0x%08x\n",
+                              I915_READ(IPEHR));
+                       printk(KERN_ERR "  INSTDONE: 0x%08x\n",
+                              I915_READ(INSTDONE));
+                       printk(KERN_ERR "  ACTHD: 0x%08x\n",
+                              I915_READ(ACTHD));
+                       I915_WRITE(IPEIR, ipeir);
+                       (void)I915_READ(IPEIR);
+               } else {
+                       u32 ipeir = I915_READ(IPEIR_I965);
+
+                       printk(KERN_ERR "  IPEIR: 0x%08x\n",
+                              I915_READ(IPEIR_I965));
+                       printk(KERN_ERR "  IPEHR: 0x%08x\n",
+                              I915_READ(IPEHR_I965));
+                       printk(KERN_ERR "  INSTDONE: 0x%08x\n",
+                              I915_READ(INSTDONE_I965));
+                       printk(KERN_ERR "  INSTPS: 0x%08x\n",
+                              I915_READ(INSTPS));
+                       printk(KERN_ERR "  INSTDONE1: 0x%08x\n",
+                              I915_READ(INSTDONE1));
+                       printk(KERN_ERR "  ACTHD: 0x%08x\n",
+                              I915_READ(ACTHD_I965));
+                       I915_WRITE(IPEIR_I965, ipeir);
+                       (void)I915_READ(IPEIR_I965);
+               }
+       }
+
+       I915_WRITE(EIR, eir);
+       (void)I915_READ(EIR);
+       eir = I915_READ(EIR);
+       if (eir) {
+               /*
+                * some errors might have become stuck,
+                * mask them.
+                */
+               DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
+               I915_WRITE(EMR, I915_READ(EMR) | eir);
+               I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
+       }
+
+       if (wedged) {
+               atomic_set(&dev_priv->mm.wedged, 1);
+
+               /*
+                * Wakeup waiting processes so they don't hang
+                */
+               DRM_WAKEUP(&dev_priv->irq_queue);
+       }
+
+       queue_work(dev_priv->wq, &dev_priv->error_work);
+}
+
 irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -212,6 +568,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
        atomic_inc(&dev_priv->irq_received);
 
+       if (IS_IRONLAKE(dev))
+               return ironlake_irq_handler(dev);
+
        iir = I915_READ(IIR);
 
        if (IS_I965G(dev)) {
@@ -234,15 +593,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                pipea_stats = I915_READ(PIPEASTAT);
                pipeb_stats = I915_READ(PIPEBSTAT);
 
+               if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+                       i915_handle_error(dev, false);
+
                /*
                 * Clear the PIPE(A|B)STAT regs before the IIR
                 */
                if (pipea_stats & 0x8000ffff) {
+                       if (pipea_stats &  PIPE_FIFO_UNDERRUN_STATUS)
+                               DRM_DEBUG_DRIVER("pipe a underrun\n");
                        I915_WRITE(PIPEASTAT, pipea_stats);
                        irq_received = 1;
                }
 
                if (pipeb_stats & 0x8000ffff) {
+                       if (pipeb_stats &  PIPE_FIFO_UNDERRUN_STATUS)
+                               DRM_DEBUG_DRIVER("pipe b underrun\n");
                        I915_WRITE(PIPEBSTAT, pipeb_stats);
                        irq_received = 1;
                }
@@ -258,10 +624,11 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                    (iir & I915_DISPLAY_PORT_INTERRUPT)) {
                        u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
 
-                       DRM_DEBUG("hotplug event received, stat 0x%08x\n",
+                       DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
                        if (hotplug_status & dev_priv->hotplug_supported_mask)
-                               schedule_work(&dev_priv->hotplug_work);
+                               queue_work(dev_priv->wq,
+                                          &dev_priv->hotplug_work);
 
                        I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
                        I915_READ(PORT_HOTPLUG_STAT);
@@ -278,18 +645,30 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                }
 
                if (iir & I915_USER_INTERRUPT) {
-                       dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
+                       u32 seqno = i915_get_gem_seqno(dev);
+                       dev_priv->mm.irq_gem_seqno = seqno;
+                       trace_i915_gem_request_complete(dev, seqno);
                        DRM_WAKEUP(&dev_priv->irq_queue);
+                       dev_priv->hangcheck_count = 0;
+                       mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
                }
 
+               if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT)
+                       intel_prepare_page_flip(dev, 0);
+
+               if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT)
+                       intel_prepare_page_flip(dev, 1);
+
                if (pipea_stats & vblank_status) {
                        vblank++;
                        drm_handle_vblank(dev, 0);
+                       intel_finish_page_flip(dev, 0);
                }
 
                if (pipeb_stats & vblank_status) {
                        vblank++;
                        drm_handle_vblank(dev, 1);
+                       intel_finish_page_flip(dev, 1);
                }
 
                if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
@@ -325,7 +704,7 @@ static int i915_emit_irq(struct drm_device * dev)
 
        i915_kernel_lost_context(dev);
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_DRIVER("\n");
 
        dev_priv->counter++;
        if (dev_priv->counter > 0x7FFFFFFFUL)
@@ -349,8 +728,12 @@ void i915_user_irq_get(struct drm_device *dev)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
-       if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
-               i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+       if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
+               if (IS_IRONLAKE(dev))
+                       ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               else
+                       i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+       }
        spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
 }
 
@@ -361,18 +744,32 @@ void i915_user_irq_put(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
-       if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
-               i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+       if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
+               if (IS_IRONLAKE(dev))
+                       ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               else
+                       i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+       }
        spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
 }
 
+void i915_trace_irq_get(struct drm_device *dev, u32 seqno)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       if (dev_priv->trace_irq_seqno == 0)
+               i915_user_irq_get(dev);
+
+       dev_priv->trace_irq_seqno = seqno;
+}
+
 static int i915_wait_irq(struct drm_device * dev, int irq_nr)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
        int ret = 0;
 
-       DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
+       DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
 
        if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
@@ -455,6 +852,9 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
        if (!(pipeconf & PIPEACONF_ENABLE))
                return -EINVAL;
 
+       if (IS_IRONLAKE(dev))
+               return 0;
+
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        if (IS_I965G(dev))
                i915_enable_pipestat(dev_priv, pipe,
@@ -474,6 +874,9 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
 
+       if (IS_IRONLAKE(dev))
+               return;
+
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        i915_disable_pipestat(dev_priv, pipe,
                              PIPE_VBLANK_INTERRUPT_ENABLE |
@@ -485,7 +888,7 @@ void i915_enable_interrupt (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                opregion_enable_asle(dev);
        dev_priv->irq_enabled = 1;
 }
@@ -545,14 +948,129 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
        return -EINVAL;
 }
 
+struct drm_i915_gem_request *i915_get_tail_request(struct drm_device *dev) {
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       return list_entry(dev_priv->mm.request_list.prev, struct drm_i915_gem_request, list);
+}
+
+/**
+ * This is called when the chip hasn't reported back with completed
+ * batchbuffers in a long time. The first time this is called we simply record
+ * ACTHD. If ACTHD hasn't changed by the time the hangcheck timer elapses
+ * again, we assume the chip is wedged and try to fix it.
+ */
+void i915_hangcheck_elapsed(unsigned long data)
+{
+       struct drm_device *dev = (struct drm_device *)data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       uint32_t acthd;
+       
+       if (!IS_I965G(dev))
+               acthd = I915_READ(ACTHD);
+       else
+               acthd = I915_READ(ACTHD_I965);
+
+       /* If all work is done then ACTHD clearly hasn't advanced. */
+       if (list_empty(&dev_priv->mm.request_list) ||
+                      i915_seqno_passed(i915_get_gem_seqno(dev), i915_get_tail_request(dev)->seqno)) {
+               dev_priv->hangcheck_count = 0;
+               return;
+       }
+
+       if (dev_priv->last_acthd == acthd && dev_priv->hangcheck_count > 0) {
+               DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
+               i915_handle_error(dev, true);
+               return;
+       } 
+
+       /* Reset timer case chip hangs without another request being added */
+       mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
+
+       if (acthd != dev_priv->last_acthd)
+               dev_priv->hangcheck_count = 0;
+       else
+               dev_priv->hangcheck_count++;
+
+       dev_priv->last_acthd = acthd;
+}
+
 /* drm_dma.h hooks
 */
+static void ironlake_irq_preinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       I915_WRITE(HWSTAM, 0xeffe);
+
+       /* XXX hotplug from PCH */
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       (void) I915_READ(DEIER);
+
+       /* and GT */
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       (void) I915_READ(GTIER);
+
+       /* south display irq */
+       I915_WRITE(SDEIMR, 0xffffffff);
+       I915_WRITE(SDEIER, 0x0);
+       (void) I915_READ(SDEIER);
+}
+
+static int ironlake_irq_postinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       /* enable kind of interrupts always enabled */
+       u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT;
+       u32 render_mask = GT_USER_INTERRUPT;
+       u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
+                          SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
+
+       dev_priv->irq_mask_reg = ~display_mask;
+       dev_priv->de_irq_enable_reg = display_mask;
+
+       /* should always can generate irq */
+       I915_WRITE(DEIIR, I915_READ(DEIIR));
+       I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+       I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
+       (void) I915_READ(DEIER);
+
+       /* user interrupt should be enabled, but masked initial */
+       dev_priv->gt_irq_mask_reg = 0xffffffff;
+       dev_priv->gt_irq_enable_reg = render_mask;
+
+       I915_WRITE(GTIIR, I915_READ(GTIIR));
+       I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+       I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
+       (void) I915_READ(GTIER);
+
+       dev_priv->pch_irq_mask_reg = ~hotplug_mask;
+       dev_priv->pch_irq_enable_reg = hotplug_mask;
+
+       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask_reg);
+       I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
+       (void) I915_READ(SDEIER);
+
+       return 0;
+}
+
 void i915_driver_irq_preinstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
        atomic_set(&dev_priv->irq_received, 0);
 
+       INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+       INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+
+       if (IS_IRONLAKE(dev)) {
+               ironlake_irq_preinstall(dev);
+               return;
+       }
+
        if (I915_HAS_HOTPLUG(dev)) {
                I915_WRITE(PORT_HOTPLUG_EN, 0);
                I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -564,17 +1082,24 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
        I915_WRITE(IMR, 0xffffffff);
        I915_WRITE(IER, 0x0);
        (void) I915_READ(IER);
-       INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
 }
 
+/*
+ * Must be called after intel_modeset_init or hotplug interrupts won't be
+ * enabled correctly.
+ */
 int i915_driver_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
+       u32 error_mask;
+
+       DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
 
        dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 
-       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+       if (IS_IRONLAKE(dev))
+               return ironlake_irq_postinstall(dev);
 
        /* Unmask the interrupts that we always want on. */
        dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
@@ -585,25 +1110,44 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
        if (I915_HAS_HOTPLUG(dev)) {
                u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
 
-               /* Leave other bits alone */
-               hotplug_en |= HOTPLUG_EN_MASK;
+               /* Note HDMI and DP share bits */
+               if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS)
+                       hotplug_en |= HDMIB_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS)
+                       hotplug_en |= HDMIC_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS)
+                       hotplug_en |= HDMID_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS)
+                       hotplug_en |= SDVOC_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS)
+                       hotplug_en |= SDVOB_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS)
+                       hotplug_en |= CRT_HOTPLUG_INT_EN;
+               /* Ignore TV since it's buggy */
+
                I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
 
-               dev_priv->hotplug_supported_mask = CRT_HOTPLUG_INT_STATUS |
-                       TV_HOTPLUG_INT_STATUS | SDVOC_HOTPLUG_INT_STATUS |
-                       SDVOB_HOTPLUG_INT_STATUS;
-               if (IS_G4X(dev)) {
-                       dev_priv->hotplug_supported_mask |=
-                               HDMIB_HOTPLUG_INT_STATUS |
-                               HDMIC_HOTPLUG_INT_STATUS |
-                               HDMID_HOTPLUG_INT_STATUS;
-               }
                /* Enable in IER... */
                enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
                /* and unmask in IMR */
                i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT);
        }
 
+       /*
+        * Enable some error detection, note the instruction error mask
+        * bit is reserved, so we leave it masked.
+        */
+       if (IS_G4X(dev)) {
+               error_mask = ~(GM45_ERROR_PAGE_TABLE |
+                              GM45_ERROR_MEM_PRIV |
+                              GM45_ERROR_CP_PRIV |
+                              I915_ERROR_MEMORY_REFRESH);
+       } else {
+               error_mask = ~(I915_ERROR_PAGE_TABLE |
+                              I915_ERROR_MEMORY_REFRESH);
+       }
+       I915_WRITE(EMR, error_mask);
+
        /* Disable pipe interrupt enables, clear pending pipe status */
        I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
        I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
@@ -615,11 +1159,24 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
        (void) I915_READ(IER);
 
        opregion_enable_asle(dev);
-       DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
 
        return 0;
 }
 
+static void ironlake_irq_uninstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       I915_WRITE(HWSTAM, 0xffffffff);
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       I915_WRITE(DEIIR, I915_READ(DEIIR));
+
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       I915_WRITE(GTIIR, I915_READ(GTIIR));
+}
+
 void i915_driver_irq_uninstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -629,6 +1186,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
 
        dev_priv->vblank_pipe = 0;
 
+       if (IS_IRONLAKE(dev)) {
+               ironlake_irq_uninstall(dev);
+               return;
+       }
+
        if (I915_HAS_HOTPLUG(dev)) {
                I915_WRITE(PORT_HOTPLUG_EN, 0);
                I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));