drm/i915: Fix up the bdw pipe interrupt enable lists
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / i915_irq.c
index b201a21..e1bfc85 100644 (file)
@@ -442,7 +442,7 @@ done:
 
 
 void
-i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
+i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
 {
        u32 reg = PIPESTAT(pipe);
        u32 pipestat = I915_READ(reg) & 0x7fff0000;
@@ -459,7 +459,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 }
 
 void
-i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
+i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
 {
        u32 reg = PIPESTAT(pipe);
        u32 pipestat = I915_READ(reg) & 0x7fff0000;
@@ -487,9 +487,10 @@ static void i915_enable_asle_pipestat(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
 
-       i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_ENABLE);
        if (INTEL_INFO(dev)->gen >= 4)
-               i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
+               i915_enable_pipestat(dev_priv, PIPE_A,
+                                    PIPE_LEGACY_BLC_EVENT_ENABLE);
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
@@ -1118,6 +1119,56 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                ivybridge_parity_error_irq_handler(dev, gt_iir);
 }
 
+static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
+                                      struct drm_i915_private *dev_priv,
+                                      u32 master_ctl)
+{
+       u32 rcs, bcs, vcs;
+       uint32_t tmp = 0;
+       irqreturn_t ret = IRQ_NONE;
+
+       if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
+               tmp = I915_READ(GEN8_GT_IIR(0));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       rcs = tmp >> GEN8_RCS_IRQ_SHIFT;
+                       bcs = tmp >> GEN8_BCS_IRQ_SHIFT;
+                       if (rcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[RCS]);
+                       if (bcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[BCS]);
+                       I915_WRITE(GEN8_GT_IIR(0), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT0)!\n");
+       }
+
+       if (master_ctl & GEN8_GT_VCS1_IRQ) {
+               tmp = I915_READ(GEN8_GT_IIR(1));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
+                       if (vcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[VCS]);
+                       I915_WRITE(GEN8_GT_IIR(1), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT1)!\n");
+       }
+
+       if (master_ctl & GEN8_GT_VECS_IRQ) {
+               tmp = I915_READ(GEN8_GT_IIR(3));
+               if (tmp) {
+                       ret = IRQ_HANDLED;
+                       vcs = tmp >> GEN8_VECS_IRQ_SHIFT;
+                       if (vcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[VECS]);
+                       I915_WRITE(GEN8_GT_IIR(3), tmp);
+               } else
+                       DRM_ERROR("The master control interrupt lied (GT3)!\n");
+       }
+
+       return ret;
+}
+
 #define HPD_STORM_DETECT_PERIOD 1000
 #define HPD_STORM_THRESHOLD 5
 
@@ -1190,42 +1241,101 @@ static void dp_aux_irq_handler(struct drm_device *dev)
 }
 
 #if defined(CONFIG_DEBUG_FS)
-static void ivb_pipe_crc_update(struct drm_device *dev, enum pipe pipe)
+static void display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
+                                        uint32_t crc0, uint32_t crc1,
+                                        uint32_t crc2, uint32_t crc3,
+                                        uint32_t crc4)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
        struct intel_pipe_crc_entry *entry;
        int head, tail;
 
+       spin_lock(&pipe_crc->lock);
+
        if (!pipe_crc->entries) {
+               spin_unlock(&pipe_crc->lock);
                DRM_ERROR("spurious interrupt\n");
                return;
        }
 
-       head = atomic_read(&pipe_crc->head);
-       tail = atomic_read(&pipe_crc->tail);
+       head = pipe_crc->head;
+       tail = pipe_crc->tail;
 
        if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) {
+               spin_unlock(&pipe_crc->lock);
                DRM_ERROR("CRC buffer overflowing\n");
                return;
        }
 
        entry = &pipe_crc->entries[head];
 
-       entry->frame = I915_READ(PIPEFRAME(pipe));
-       entry->crc[0] = I915_READ(PIPE_CRC_RES_1_IVB(pipe));
-       entry->crc[1] = I915_READ(PIPE_CRC_RES_2_IVB(pipe));
-       entry->crc[2] = I915_READ(PIPE_CRC_RES_3_IVB(pipe));
-       entry->crc[3] = I915_READ(PIPE_CRC_RES_4_IVB(pipe));
-       entry->crc[4] = I915_READ(PIPE_CRC_RES_5_IVB(pipe));
+       entry->frame = dev->driver->get_vblank_counter(dev, pipe);
+       entry->crc[0] = crc0;
+       entry->crc[1] = crc1;
+       entry->crc[2] = crc2;
+       entry->crc[3] = crc3;
+       entry->crc[4] = crc4;
 
        head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
-       atomic_set(&pipe_crc->head, head);
+       pipe_crc->head = head;
+
+       spin_unlock(&pipe_crc->lock);
+
+       wake_up_interruptible(&pipe_crc->wq);
 }
 #else
-static void ivb_pipe_crc_update(struct drm_device *dev, int pipe) {}
+static inline void
+display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
+                            uint32_t crc0, uint32_t crc1,
+                            uint32_t crc2, uint32_t crc3,
+                            uint32_t crc4) {}
 #endif
 
+
+static void hsw_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
+                                    0, 0, 0, 0);
+}
+
+static void ivb_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_2_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_3_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_4_IVB(pipe)),
+                                    I915_READ(PIPE_CRC_RES_5_IVB(pipe)));
+}
+
+static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t res1, res2;
+
+       if (INTEL_INFO(dev)->gen >= 3)
+               res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe));
+       else
+               res1 = 0;
+
+       if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
+               res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe));
+       else
+               res2 = 0;
+
+       display_pipe_crc_irq_handler(dev, pipe,
+                                    I915_READ(PIPE_CRC_RES_RED(pipe)),
+                                    I915_READ(PIPE_CRC_RES_GREEN(pipe)),
+                                    I915_READ(PIPE_CRC_RES_BLUE(pipe)),
+                                    res1, res2);
+}
+
 /* The RPS events need forcewake, so we add them to a work queue and mask their
  * IMR bits until the work is done. Other interrupts can be processed without
  * the work queue. */
@@ -1300,6 +1410,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                                intel_prepare_page_flip(dev, pipe);
                                intel_finish_page_flip(dev, pipe);
                        }
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
                /* Consume port.  Then clear IIR or we'll miss events */
@@ -1388,30 +1501,26 @@ static void ivb_err_int_handler(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 err_int = I915_READ(GEN7_ERR_INT);
+       enum pipe pipe;
 
        if (err_int & ERR_INT_POISON)
                DRM_ERROR("Poison interrupt\n");
 
-       if (err_int & ERR_INT_FIFO_UNDERRUN_A)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
-                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
-
-       if (err_int & ERR_INT_FIFO_UNDERRUN_B)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
-                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
-
-       if (err_int & ERR_INT_FIFO_UNDERRUN_C)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
-                       DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
-
-       if (err_int & ERR_INT_PIPE_CRC_DONE_A)
-               ivb_pipe_crc_update(dev, PIPE_A);
-
-       if (err_int & ERR_INT_PIPE_CRC_DONE_B)
-               ivb_pipe_crc_update(dev, PIPE_B);
+       for_each_pipe(pipe) {
+               if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) {
+                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
+                                                                 false))
+                               DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
+                                                pipe_name(pipe));
+               }
 
-       if (err_int & ERR_INT_PIPE_CRC_DONE_C)
-               ivb_pipe_crc_update(dev, PIPE_C);
+               if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
+                       if (IS_IVYBRIDGE(dev))
+                               ivb_pipe_crc_irq_handler(dev, pipe);
+                       else
+                               hsw_pipe_crc_irq_handler(dev, pipe);
+               }
+       }
 
        I915_WRITE(GEN7_ERR_INT, err_int);
 }
@@ -1482,6 +1591,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
 static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
 
        if (de_iir & DE_AUX_CHANNEL_A)
                dp_aux_irq_handler(dev);
@@ -1489,31 +1599,26 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
        if (de_iir & DE_GSE)
                intel_opregion_asle_intr(dev);
 
-       if (de_iir & DE_PIPEA_VBLANK)
-               drm_handle_vblank(dev, 0);
-
-       if (de_iir & DE_PIPEB_VBLANK)
-               drm_handle_vblank(dev, 1);
-
        if (de_iir & DE_POISON)
                DRM_ERROR("Poison interrupt\n");
 
-       if (de_iir & DE_PIPEA_FIFO_UNDERRUN)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
-                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+       for_each_pipe(pipe) {
+               if (de_iir & DE_PIPE_VBLANK(pipe))
+                       drm_handle_vblank(dev, pipe);
 
-       if (de_iir & DE_PIPEB_FIFO_UNDERRUN)
-               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
-                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+               if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
+                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+                               DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
+                                                pipe_name(pipe));
 
-       if (de_iir & DE_PLANEA_FLIP_DONE) {
-               intel_prepare_page_flip(dev, 0);
-               intel_finish_page_flip_plane(dev, 0);
-       }
+               if (de_iir & DE_PIPE_CRC_DONE(pipe))
+                       i9xx_pipe_crc_irq_handler(dev, pipe);
 
-       if (de_iir & DE_PLANEB_FLIP_DONE) {
-               intel_prepare_page_flip(dev, 1);
-               intel_finish_page_flip_plane(dev, 1);
+               /* plane/pipes map 1:1 on ilk+ */
+               if (de_iir & DE_PLANE_FLIP_DONE(pipe)) {
+                       intel_prepare_page_flip(dev, pipe);
+                       intel_finish_page_flip_plane(dev, pipe);
+               }
        }
 
        /* check event from PCH */
@@ -1536,7 +1641,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int i;
+       enum pipe i;
 
        if (de_iir & DE_ERR_INT_IVB)
                ivb_err_int_handler(dev);
@@ -1547,10 +1652,12 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
        if (de_iir & DE_GSE_IVB)
                intel_opregion_asle_intr(dev);
 
-       for (i = 0; i < 3; i++) {
-               if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
+       for_each_pipe(i) {
+               if (de_iir & (DE_PIPE_VBLANK_IVB(i)))
                        drm_handle_vblank(dev, i);
-               if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
+
+               /* plane/pipes map 1:1 on ilk+ */
+               if (de_iir & DE_PLANE_FLIP_DONE_IVB(i)) {
                        intel_prepare_page_flip(dev, i);
                        intel_finish_page_flip_plane(dev, i);
                }
@@ -1635,6 +1742,76 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        return ret;
 }
 
+static irqreturn_t gen8_irq_handler(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 master_ctl;
+       irqreturn_t ret = IRQ_NONE;
+       uint32_t tmp = 0;
+       enum pipe pipe;
+
+       atomic_inc(&dev_priv->irq_received);
+
+       master_ctl = I915_READ(GEN8_MASTER_IRQ);
+       master_ctl &= ~GEN8_MASTER_IRQ_CONTROL;
+       if (!master_ctl)
+               return IRQ_NONE;
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl);
+
+       if (master_ctl & GEN8_DE_MISC_IRQ) {
+               tmp = I915_READ(GEN8_DE_MISC_IIR);
+               if (tmp & GEN8_DE_MISC_GSE)
+                       intel_opregion_asle_intr(dev);
+               else if (tmp)
+                       DRM_ERROR("Unexpected DE Misc interrupt\n");
+               else
+                       DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
+
+               if (tmp) {
+                       I915_WRITE(GEN8_DE_MISC_IIR, tmp);
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       for_each_pipe(pipe) {
+               uint32_t pipe_iir;
+
+               if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
+                       continue;
+
+               pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
+               if (pipe_iir & GEN8_PIPE_VBLANK)
+                       drm_handle_vblank(dev, pipe);
+
+               if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
+                       intel_prepare_page_flip(dev, pipe);
+                       intel_finish_page_flip_plane(dev, pipe);
+               }
+
+               if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
+                       DRM_ERROR("Fault errors on pipe %c\n: 0x%08x",
+                                 pipe_name(pipe),
+                                 pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS);
+               }
+
+               if (pipe_iir) {
+                       ret = IRQ_HANDLED;
+                       I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir);
+               } else
+                       DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
+       }
+
+       I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       return ret;
+}
+
 static void i915_error_wake_up(struct drm_i915_private *dev_priv,
                               bool reset_completed)
 {
@@ -1953,7 +2130,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
        uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
-                                                    DE_PIPE_VBLANK_ILK(pipe);
+                                                    DE_PIPE_VBLANK(pipe);
 
        if (!i915_pipe_enabled(dev, pipe))
                return -EINVAL;
@@ -1976,7 +2153,7 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
        imr = I915_READ(VLV_IMR);
-       if (pipe == 0)
+       if (pipe == PIPE_A)
                imr &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
        else
                imr &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
@@ -1988,6 +2165,25 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
        return 0;
 }
 
+static int gen8_enable_vblank(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+       uint32_t imr;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       imr = I915_READ(GEN8_DE_PIPE_IMR(pipe));
+       if ((imr & GEN8_PIPE_VBLANK) == 1) {
+               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), imr & ~GEN8_PIPE_VBLANK);
+               POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       }
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+       return 0;
+}
+
 /* Called from drm generic code, passed 'crtc' which
  * we use as a pipe index
  */
@@ -2011,7 +2207,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
        uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
-                                                    DE_PIPE_VBLANK_ILK(pipe);
+                                                    DE_PIPE_VBLANK(pipe);
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
        ironlake_disable_display_irq(dev_priv, bit);
@@ -2028,7 +2224,7 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
        i915_disable_pipestat(dev_priv, pipe,
                              PIPE_START_VBLANK_INTERRUPT_ENABLE);
        imr = I915_READ(VLV_IMR);
-       if (pipe == 0)
+       if (pipe == PIPE_A)
                imr |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
        else
                imr |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
@@ -2036,6 +2232,24 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
+static void gen8_disable_vblank(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+       uint32_t imr;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       imr = I915_READ(GEN8_DE_PIPE_IMR(pipe));
+       if ((imr & GEN8_PIPE_VBLANK) == 0) {
+               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), imr | GEN8_PIPE_VBLANK);
+               POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
+       }
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
 static u32
 ring_last_seqno(struct intel_ring_buffer *ring)
 {
@@ -2194,8 +2408,12 @@ static void i915_hangcheck_elapsed(unsigned long data)
                                if (waitqueue_active(&ring->irq_queue)) {
                                        /* Issue a wake-up to catch stuck h/w. */
                                        if (!test_and_set_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings)) {
-                                               DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
-                                                         ring->name);
+                                               if (!(dev_priv->gpu_error.test_irq_rings & intel_ring_flag(ring)))
+                                                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
+                                                                 ring->name);
+                                               else
+                                                       DRM_INFO("Fake missed irq on %s\n",
+                                                                ring->name);
                                                wake_up_all(&ring->irq_queue);
                                        }
                                        /* Safeguard against driver failure */
@@ -2366,6 +2584,53 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 }
 
+static void gen8_irq_preinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       /* IIR can theoretically queue up two events. Be paranoid */
+#define GEN8_IRQ_INIT_NDX(type, which) do { \
+               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IMR(which)); \
+               I915_WRITE(GEN8_##type##_IER(which), 0); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IIR(which)); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       } while (0)
+
+#define GEN8_IRQ_INIT(type) do { \
+               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IMR); \
+               I915_WRITE(GEN8_##type##_IER, 0); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+               POSTING_READ(GEN8_##type##_IIR); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+       } while (0)
+
+       GEN8_IRQ_INIT_NDX(GT, 0);
+       GEN8_IRQ_INIT_NDX(GT, 1);
+       GEN8_IRQ_INIT_NDX(GT, 2);
+       GEN8_IRQ_INIT_NDX(GT, 3);
+
+       for_each_pipe(pipe) {
+               GEN8_IRQ_INIT_NDX(DE_PIPE, pipe);
+       }
+
+       GEN8_IRQ_INIT(DE_PORT);
+       GEN8_IRQ_INIT(DE_MISC);
+       GEN8_IRQ_INIT(PCU);
+#undef GEN8_IRQ_INIT
+#undef GEN8_IRQ_INIT_NDX
+
+       POSTING_READ(GEN8_PCU_IIR);
+}
+
 static void ibx_hpd_irq_setup(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -2482,8 +2747,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        } else {
                display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
                                DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
-                               DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
-                               DE_PIPEA_FIFO_UNDERRUN | DE_POISON);
+                               DE_AUX_CHANNEL_A |
+                               DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
+                               DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE |
+                               DE_POISON);
                extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT;
        }
 
@@ -2517,7 +2784,8 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask;
-       u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
+       u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV |
+               PIPE_CRC_DONE_ENABLE;
        unsigned long irqflags;
 
        enable_mask = I915_DISPLAY_PORT_INTERRUPT;
@@ -2547,9 +2815,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        /* Interrupt setup is already guaranteed to be single-threaded, this is
         * just to make the assert_spin_locked check happy. */
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       i915_enable_pipestat(dev_priv, 0, pipestat_enable);
-       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
-       i915_enable_pipestat(dev_priv, 1, pipestat_enable);
+       i915_enable_pipestat(dev_priv, PIPE_A, pipestat_enable);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, pipestat_enable);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        I915_WRITE(VLV_IIR, 0xffffffff);
@@ -2568,6 +2836,115 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        return 0;
 }
 
+static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
+{
+       int i;
+
+       /* These are interrupts we'll toggle with the ring mask register */
+       uint32_t gt_interrupts[] = {
+               GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
+                       GT_RENDER_L3_PARITY_ERROR_INTERRUPT |
+                       GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
+               GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
+                       GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
+               0,
+               GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
+               };
+
+       for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) {
+               u32 tmp = I915_READ(GEN8_GT_IIR(i));
+               if (tmp)
+                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
+                                 i, tmp);
+               I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]);
+               I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]);
+       }
+       POSTING_READ(GEN8_GT_IER(0));
+}
+
+static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       uint32_t de_pipe_enables = GEN8_PIPE_FLIP_DONE |
+                                  GEN8_PIPE_VBLANK |
+                                  GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
+       int pipe;
+       dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_enables;
+       dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_enables;
+       dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_enables;
+
+       for_each_pipe(pipe) {
+               u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe));
+               if (tmp)
+                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
+                                 pipe, tmp);
+               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
+               I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables);
+       }
+       POSTING_READ(GEN8_DE_PIPE_ISR(0));
+
+       I915_WRITE(GEN8_DE_PORT_IMR, ~_PORT_DP_A_HOTPLUG);
+       I915_WRITE(GEN8_DE_PORT_IER, _PORT_DP_A_HOTPLUG);
+       POSTING_READ(GEN8_DE_PORT_IER);
+}
+
+static int gen8_irq_postinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen8_gt_irq_postinstall(dev_priv);
+       gen8_de_irq_postinstall(dev_priv);
+
+       ibx_irq_postinstall(dev);
+
+       I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+       POSTING_READ(GEN8_MASTER_IRQ);
+
+       return 0;
+}
+
+static void gen8_irq_uninstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       if (!dev_priv)
+               return;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+
+#define GEN8_IRQ_FINI_NDX(type, which) do { \
+               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+               I915_WRITE(GEN8_##type##_IER(which), 0); \
+               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       } while (0)
+
+#define GEN8_IRQ_FINI(type) do { \
+               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
+               I915_WRITE(GEN8_##type##_IER, 0); \
+               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+       } while (0)
+
+       GEN8_IRQ_FINI_NDX(GT, 0);
+       GEN8_IRQ_FINI_NDX(GT, 1);
+       GEN8_IRQ_FINI_NDX(GT, 2);
+       GEN8_IRQ_FINI_NDX(GT, 3);
+
+       for_each_pipe(pipe) {
+               GEN8_IRQ_FINI_NDX(DE_PIPE, pipe);
+       }
+
+       GEN8_IRQ_FINI(DE_PORT);
+       GEN8_IRQ_FINI(DE_MISC);
+       GEN8_IRQ_FINI(PCU);
+#undef GEN8_IRQ_FINI
+#undef GEN8_IRQ_FINI_NDX
+
+       POSTING_READ(GEN8_PCU_IIR);
+}
+
 static void valleyview_irq_uninstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -2640,6 +3017,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev)
 static int i8xx_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       unsigned long irqflags;
 
        I915_WRITE16(EMR,
                     ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
@@ -2660,6 +3038,13 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
                     I915_USER_INTERRUPT);
        POSTING_READ16(IER);
 
+       /* Interrupt setup is already guaranteed to be single-threaded, this is
+        * just to make the assert_spin_locked check happy. */
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
        return 0;
 }
 
@@ -2746,13 +3131,14 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
                if (iir & I915_USER_INTERRUPT)
                        notify_ring(dev, &dev_priv->ring[RCS]);
 
-               if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   i8xx_handle_vblank(dev, 0, iir))
-                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0);
+               for_each_pipe(pipe) {
+                       if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
+                           i8xx_handle_vblank(dev, pipe, iir))
+                               flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
 
-               if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   i8xx_handle_vblank(dev, 1, iir))
-                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1);
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
+               }
 
                iir = new_iir;
        }
@@ -2799,6 +3185,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask;
+       unsigned long irqflags;
 
        I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
 
@@ -2834,6 +3221,13 @@ static int i915_irq_postinstall(struct drm_device *dev)
 
        i915_enable_asle_pipestat(dev);
 
+       /* Interrupt setup is already guaranteed to be single-threaded, this is
+        * just to make the assert_spin_locked check happy. */
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
        return 0;
 }
 
@@ -2945,6 +3339,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
                if (blc_event || (iir & I915_ASLE_INTERRUPT))
@@ -3043,7 +3440,9 @@ static int i965_irq_postinstall(struct drm_device *dev)
        /* Interrupt setup is already guaranteed to be single-threaded, this is
         * just to make the assert_spin_locked check happy. */
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
+       i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        /*
@@ -3189,6 +3588,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
+
+                       if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+                               i9xx_pipe_crc_irq_handler(dev, pipe);
                }
 
 
@@ -3322,6 +3724,14 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->enable_vblank = valleyview_enable_vblank;
                dev->driver->disable_vblank = valleyview_disable_vblank;
                dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
+       } else if (IS_GEN8(dev)) {
+               dev->driver->irq_handler = gen8_irq_handler;
+               dev->driver->irq_preinstall = gen8_irq_preinstall;
+               dev->driver->irq_postinstall = gen8_irq_postinstall;
+               dev->driver->irq_uninstall = gen8_irq_uninstall;
+               dev->driver->enable_vblank = gen8_enable_vblank;
+               dev->driver->disable_vblank = gen8_disable_vblank;
+               dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;