]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/i915/i915_irq.c
drm/i915: Finish page flips and update primary planes after a GPU reset
[karo-tx-linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index fe843389c7b473e2035124c38330c3a965450773..9fde49a299990da73abed9d5e8e29e41c3e549ac 100644 (file)
@@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct intel_encoder *encoder;
 
+       /* HPD irq before everything is fully set up. */
+       if (!dev_priv->enable_hotplug_processing)
+               return;
+
        mutex_lock(&mode_config->mutex);
        DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
@@ -300,9 +304,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_helper_hpd_irq_event(dev);
 }
 
-/* defined intel_pm.c */
-extern spinlock_t mchdev_lock;
-
 static void ironlake_handle_rps_change(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -355,8 +356,8 @@ static void notify_ring(struct drm_device *dev,
 
        wake_up_all(&ring->irq_queue);
        if (i915_enable_hangcheck) {
-               dev_priv->hangcheck_count = 0;
-               mod_timer(&dev_priv->hangcheck_timer,
+               dev_priv->gpu_error.hangcheck_count = 0;
+               mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                          round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
        }
 }
@@ -524,6 +525,20 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
        queue_work(dev_priv->wq, &dev_priv->rps.work);
 }
 
+static void gmbus_irq_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       wake_up_all(&dev_priv->gmbus_wait_queue);
+}
+
+static void dp_aux_irq_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       wake_up_all(&dev_priv->gmbus_wait_queue);
+}
+
 static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -533,7 +548,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
        unsigned long irqflags;
        int pipe;
        u32 pipe_stats[I915_MAX_PIPES];
-       bool blc_event;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -590,8 +604,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                        I915_READ(PORT_HOTPLUG_STAT);
                }
 
-               if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
-                       blc_event = true;
+               if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+                       gmbus_irq_handler(dev);
 
                if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
                        gen6_queue_rps_work(dev_priv, pm_iir);
@@ -618,8 +632,11 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
                                 (pch_iir & SDE_AUDIO_POWER_MASK) >>
                                 SDE_AUDIO_POWER_SHIFT);
 
+       if (pch_iir & SDE_AUX_MASK)
+               dp_aux_irq_handler(dev);
+
        if (pch_iir & SDE_GMBUS)
-               DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n");
+               gmbus_irq_handler(dev);
 
        if (pch_iir & SDE_AUDIO_HDCP_MASK)
                DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n");
@@ -662,10 +679,10 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                                 SDE_AUDIO_POWER_SHIFT_CPT);
 
        if (pch_iir & SDE_AUX_MASK_CPT)
-               DRM_DEBUG_DRIVER("AUX channel interrupt\n");
+               dp_aux_irq_handler(dev);
 
        if (pch_iir & SDE_GMBUS_CPT)
-               DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n");
+               gmbus_irq_handler(dev);
 
        if (pch_iir & SDE_AUDIO_CP_REQ_CPT)
                DRM_DEBUG_DRIVER("Audio CP request interrupt\n");
@@ -703,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        if (de_iir) {
+               if (de_iir & DE_AUX_CHANNEL_A_IVB)
+                       dp_aux_irq_handler(dev);
+
                if (de_iir & DE_GSE_IVB)
                        intel_opregion_gse_intr(dev);
 
@@ -758,7 +778,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        struct drm_device *dev = (struct drm_device *) arg;
        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, pm_iir;
+       u32 de_iir, gt_iir, de_ier, pm_iir;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -769,11 +789,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        gt_iir = I915_READ(GTIIR);
-       pch_iir = I915_READ(SDEIIR);
        pm_iir = I915_READ(GEN6_PMIIR);
 
-       if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 &&
-           (!IS_GEN6(dev) || pm_iir == 0))
+       if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0))
                goto done;
 
        ret = IRQ_HANDLED;
@@ -783,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        else
                snb_gt_irq_handler(dev, dev_priv, gt_iir);
 
+       if (de_iir & DE_AUX_CHANNEL_A)
+               dp_aux_irq_handler(dev);
+
        if (de_iir & DE_GSE)
                intel_opregion_gse_intr(dev);
 
@@ -804,10 +825,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 
        /* check event from PCH */
        if (de_iir & DE_PCH_EVENT) {
+               u32 pch_iir = I915_READ(SDEIIR);
+
                if (HAS_PCH_CPT(dev))
                        cpt_irq_handler(dev, pch_iir);
                else
                        ibx_irq_handler(dev, pch_iir);
+
+               /* should clear PCH hotplug event before clear CPU irq */
+               I915_WRITE(SDEIIR, pch_iir);
        }
 
        if (IS_GEN5(dev) &&  de_iir & DE_PCU_EVENT)
@@ -816,8 +842,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS)
                gen6_queue_rps_work(dev_priv, pm_iir);
 
-       /* should clear PCH hotplug event before clear CPU irq */
-       I915_WRITE(SDEIIR, pch_iir);
        I915_WRITE(GTIIR, gt_iir);
        I915_WRITE(DEIIR, de_iir);
        I915_WRITE(GEN6_PMIIR, pm_iir);
@@ -838,23 +862,62 @@ done:
  */
 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 i915_gpu_error *error = container_of(work, struct i915_gpu_error,
+                                                   work);
+       drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t,
+                                                   gpu_error);
        struct drm_device *dev = dev_priv->dev;
+       struct intel_ring_buffer *ring;
        char *error_event[] = { "ERROR=1", NULL };
        char *reset_event[] = { "RESET=1", NULL };
        char *reset_done_event[] = { "ERROR=0", NULL };
+       int i, ret;
 
        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
 
-       if (atomic_read(&dev_priv->mm.wedged)) {
+       /*
+        * Note that there's only one work item which does gpu resets, so we
+        * need not worry about concurrent gpu resets potentially incrementing
+        * error->reset_counter twice. We only need to take care of another
+        * racing irq/hangcheck declaring the gpu dead for a second time. A
+        * quick check for that is good enough: schedule_work ensures the
+        * correct ordering between hang detection and this work item, and since
+        * the reset in-progress bit is only ever set by code outside of this
+        * work we don't need to worry about any other races.
+        */
+       if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) {
                DRM_DEBUG_DRIVER("resetting chip\n");
-               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
-               if (!i915_reset(dev)) {
-                       atomic_set(&dev_priv->mm.wedged, 0);
-                       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
+               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
+                                  reset_event);
+
+               ret = i915_reset(dev);
+
+               if (ret == 0) {
+                       /*
+                        * After all the gem state is reset, increment the reset
+                        * counter and wake up everyone waiting for the reset to
+                        * complete.
+                        *
+                        * Since unlock operations are a one-sided barrier only,
+                        * we need to insert a barrier here to order any seqno
+                        * updates before
+                        * the counter increment.
+                        */
+                       smp_mb__before_atomic_inc();
+                       atomic_inc(&dev_priv->gpu_error.reset_counter);
+
+                       kobject_uevent_env(&dev->primary->kdev.kobj,
+                                          KOBJ_CHANGE, reset_done_event);
+               } else {
+                       atomic_set(&error->reset_counter, I915_WEDGED);
                }
-               complete_all(&dev_priv->error_completion);
+
+               for_each_ring(ring, dev_priv, i)
+                       wake_up_all(&ring->irq_queue);
+
+               intel_display_handle_reset(dev);
+
+               wake_up_all(&dev_priv->gpu_error.reset_queue);
        }
 }
 
@@ -915,7 +978,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                        goto unwind;
 
                local_irq_save(flags);
-               if (reloc_offset < dev_priv->mm.gtt_mappable_end &&
+               if (reloc_offset < dev_priv->gtt.mappable_end &&
                    src->has_global_gtt_mapping) {
                        void __iomem *s;
 
@@ -924,10 +987,18 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                         * captures what the GPU read.
                         */
 
-                       s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+                       s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
                                                     reloc_offset);
                        memcpy_fromio(d, s, PAGE_SIZE);
                        io_mapping_unmap_atomic(s);
+               } else if (src->stolen) {
+                       unsigned long offset;
+
+                       offset = dev_priv->mm.stolen_base;
+                       offset += src->stolen->start;
+                       offset += i << PAGE_SHIFT;
+
+                       memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE);
                } else {
                        struct page *page;
                        void *s;
@@ -1074,6 +1145,8 @@ static void i915_gem_record_fences(struct drm_device *dev,
                        error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4));
                break;
 
+       default:
+               BUG();
        }
 }
 
@@ -1222,9 +1295,9 @@ static void i915_capture_error_state(struct drm_device *dev)
        unsigned long flags;
        int i, pipe;
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       error = dev_priv->first_error;
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       error = dev_priv->gpu_error.first_error;
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
        if (error)
                return;
 
@@ -1235,7 +1308,8 @@ static void i915_capture_error_state(struct drm_device *dev)
                return;
        }
 
-       DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n",
+       DRM_INFO("capturing error event; look for more information in"
+                "/sys/kernel/debug/dri/%d/i915_error_state\n",
                 dev->primary->index);
 
        kref_init(&error->ref);
@@ -1318,12 +1392,12 @@ static void i915_capture_error_state(struct drm_device *dev)
        error->overlay = intel_overlay_capture_error_state(dev);
        error->display = intel_display_capture_error_state(dev);
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       if (dev_priv->first_error == NULL) {
-               dev_priv->first_error = error;
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       if (dev_priv->gpu_error.first_error == NULL) {
+               dev_priv->gpu_error.first_error = error;
                error = NULL;
        }
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
        if (error)
                i915_error_state_free(&error->ref);
@@ -1335,10 +1409,10 @@ void i915_destroy_error_state(struct drm_device *dev)
        struct drm_i915_error_state *error;
        unsigned long flags;
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       error = dev_priv->first_error;
-       dev_priv->first_error = NULL;
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       error = dev_priv->gpu_error.first_error;
+       dev_priv->gpu_error.first_error = NULL;
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
        if (error)
                kref_put(&error->ref, i915_error_state_free);
@@ -1459,17 +1533,18 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
        i915_report_and_clear_eir(dev);
 
        if (wedged) {
-               INIT_COMPLETION(dev_priv->error_completion);
-               atomic_set(&dev_priv->mm.wedged, 1);
+               atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG,
+                               &dev_priv->gpu_error.reset_counter);
 
                /*
-                * Wakeup waiting processes so they don't hang
+                * Wakeup waiting processes so that the reset work item
+                * doesn't deadlock trying to grab various locks.
                 */
                for_each_ring(ring, dev_priv, i)
                        wake_up_all(&ring->irq_queue);
        }
 
-       queue_work(dev_priv->wq, &dev_priv->error_work);
+       queue_work(dev_priv->wq, &dev_priv->gpu_error.work);
 }
 
 static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)
@@ -1700,7 +1775,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (dev_priv->hangcheck_count++ > 1) {
+       if (dev_priv->gpu_error.hangcheck_count++ > 1) {
                bool hung = true;
 
                DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
@@ -1759,25 +1834,29 @@ void i915_hangcheck_elapsed(unsigned long data)
                        goto repeat;
                }
 
-               dev_priv->hangcheck_count = 0;
+               dev_priv->gpu_error.hangcheck_count = 0;
                return;
        }
 
        i915_get_extra_instdone(dev, instdone);
-       if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 &&
-           memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) {
+       if (memcmp(dev_priv->gpu_error.last_acthd, acthd,
+                  sizeof(acthd)) == 0 &&
+           memcmp(dev_priv->gpu_error.prev_instdone, instdone,
+                  sizeof(instdone)) == 0) {
                if (i915_hangcheck_hung(dev))
                        return;
        } else {
-               dev_priv->hangcheck_count = 0;
+               dev_priv->gpu_error.hangcheck_count = 0;
 
-               memcpy(dev_priv->last_acthd, acthd, sizeof(acthd));
-               memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone));
+               memcpy(dev_priv->gpu_error.last_acthd, acthd,
+                      sizeof(acthd));
+               memcpy(dev_priv->gpu_error.prev_instdone, instdone,
+                      sizeof(instdone));
        }
 
 repeat:
        /* Reset timer case chip hangs without another request being added */
-       mod_timer(&dev_priv->hangcheck_timer,
+       mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                  round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
 }
 
@@ -1847,7 +1926,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
  * This register is the same on all known PCH chips.
  */
 
-static void ironlake_enable_pch_hotplug(struct drm_device *dev)
+static void ibx_enable_hotplug(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32     hotplug;
@@ -1860,14 +1939,36 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev)
        I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
 }
 
+static void ibx_irq_postinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 mask;
+
+       if (HAS_PCH_IBX(dev))
+               mask = SDE_HOTPLUG_MASK |
+                      SDE_GMBUS |
+                      SDE_AUX_MASK;
+       else
+               mask = SDE_HOTPLUG_MASK_CPT |
+                      SDE_GMBUS_CPT |
+                      SDE_AUX_MASK_CPT;
+
+       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       I915_WRITE(SDEIMR, ~mask);
+       I915_WRITE(SDEIER, mask);
+       POSTING_READ(SDEIER);
+
+       ibx_enable_hotplug(dev);
+}
+
 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 |
-                          DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE;
+                          DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
+                          DE_AUX_CHANNEL_A;
        u32 render_irqs;
-       u32 hotplug_mask;
 
        dev_priv->irq_mask = ~display_mask;
 
@@ -1895,27 +1996,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIER, render_irqs);
        POSTING_READ(GTIER);
 
-       if (HAS_PCH_CPT(dev)) {
-               hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
-                               SDE_PORTB_HOTPLUG_CPT |
-                               SDE_PORTC_HOTPLUG_CPT |
-                               SDE_PORTD_HOTPLUG_CPT);
-       } else {
-               hotplug_mask = (SDE_CRT_HOTPLUG |
-                               SDE_PORTB_HOTPLUG |
-                               SDE_PORTC_HOTPLUG |
-                               SDE_PORTD_HOTPLUG |
-                               SDE_AUX_MASK);
-       }
-
-       dev_priv->pch_irq_mask = ~hotplug_mask;
-
-       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
-       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
-       I915_WRITE(SDEIER, hotplug_mask);
-       POSTING_READ(SDEIER);
-
-       ironlake_enable_pch_hotplug(dev);
+       ibx_irq_postinstall(dev);
 
        if (IS_IRONLAKE_M(dev)) {
                /* Clear & enable PCU event interrupts */
@@ -1935,9 +2016,9 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
                DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB |
                DE_PLANEC_FLIP_DONE_IVB |
                DE_PLANEB_FLIP_DONE_IVB |
-               DE_PLANEA_FLIP_DONE_IVB;
+               DE_PLANEA_FLIP_DONE_IVB |
+               DE_AUX_CHANNEL_A_IVB;
        u32 render_irqs;
-       u32 hotplug_mask;
 
        dev_priv->irq_mask = ~display_mask;
 
@@ -1961,18 +2042,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIER, render_irqs);
        POSTING_READ(GTIER);
 
-       hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
-                       SDE_PORTB_HOTPLUG_CPT |
-                       SDE_PORTC_HOTPLUG_CPT |
-                       SDE_PORTD_HOTPLUG_CPT);
-       dev_priv->pch_irq_mask = ~hotplug_mask;
-
-       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
-       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
-       I915_WRITE(SDEIER, hotplug_mask);
-       POSTING_READ(SDEIER);
-
-       ironlake_enable_pch_hotplug(dev);
+       ibx_irq_postinstall(dev);
 
        return 0;
 }
@@ -1981,7 +2051,6 @@ 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 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
        u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
        u32 render_irqs;
        u16 msid;
@@ -2010,6 +2079,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        msid |= (1<<14);
        pci_write_config_word(dev_priv->dev->pdev, 0x98, msid);
 
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       POSTING_READ(PORT_HOTPLUG_EN);
+
        I915_WRITE(VLV_IMR, dev_priv->irq_mask);
        I915_WRITE(VLV_IER, enable_mask);
        I915_WRITE(VLV_IIR, 0xffffffff);
@@ -2018,6 +2090,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 
        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_WRITE(VLV_IIR, 0xffffffff);
@@ -2038,13 +2111,22 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
 #endif
 
        I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
+
+       return 0;
+}
+
+static void valleyview_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
        /* 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 & PORTB_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTB_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTC_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTD_HOTPLUG_INT_EN;
        if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915)
                hotplug_en |= SDVOC_HOTPLUG_INT_EN;
        if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915)
@@ -2055,8 +2137,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        }
 
        I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
-
-       return 0;
 }
 
 static void valleyview_irq_uninstall(struct drm_device *dev)
@@ -2286,6 +2366,9 @@ static int i915_irq_postinstall(struct drm_device *dev)
                I915_USER_INTERRUPT;
 
        if (I915_HAS_HOTPLUG(dev)) {
+               I915_WRITE(PORT_HOTPLUG_EN, 0);
+               POSTING_READ(PORT_HOTPLUG_EN);
+
                /* Enable in IER... */
                enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
                /* and unmask in IMR */
@@ -2296,15 +2379,25 @@ static int i915_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
+       intel_opregion_enable_asle(dev);
+
+       return 0;
+}
+
+static void i915_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en;
+
        if (I915_HAS_HOTPLUG(dev)) {
-               u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
-
-               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;
+               hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
+               if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTB_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTC_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTD_HOTPLUG_INT_EN;
                if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915)
                        hotplug_en |= SDVOC_HOTPLUG_INT_EN;
                if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915)
@@ -2318,10 +2411,6 @@ static int i915_irq_postinstall(struct drm_device *dev)
 
                I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
        }
-
-       intel_opregion_enable_asle(dev);
-
-       return 0;
 }
 
 static irqreturn_t i915_irq_handler(int irq, void *arg)
@@ -2481,7 +2570,6 @@ static void i965_irq_preinstall(struct drm_device * dev)
 static int i965_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32 hotplug_en;
        u32 enable_mask;
        u32 error_mask;
 
@@ -2502,6 +2590,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
 
        dev_priv->pipestat[0] = 0;
        dev_priv->pipestat[1] = 0;
+       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
 
        /*
         * Enable some error detection, note the instruction error mask
@@ -2522,14 +2611,27 @@ static int i965_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       POSTING_READ(PORT_HOTPLUG_EN);
+
+       intel_opregion_enable_asle(dev);
+
+       return 0;
+}
+
+static void i965_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en;
+
        /* Note HDMI and DP share hotplug bits */
        hotplug_en = 0;
-       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 & PORTB_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTB_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTC_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTD_HOTPLUG_INT_EN;
        if (IS_G4X(dev)) {
                if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X)
                        hotplug_en |= SDVOC_HOTPLUG_INT_EN;
@@ -2556,10 +2658,6 @@ static int i965_irq_postinstall(struct drm_device *dev)
        /* Ignore TV since it's buggy */
 
        I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
-
-       intel_opregion_enable_asle(dev);
-
-       return 0;
 }
 
 static irqreturn_t i965_irq_handler(int irq, void *arg)
@@ -2655,6 +2753,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                if (blc_event || (iir & I915_ASLE_INTERRUPT))
                        intel_opregion_asle_intr(dev);
 
+               if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+                       gmbus_irq_handler(dev);
+
                /* With MSI, interrupts are only generated when iir
                 * transitions from zero to nonzero.  If another bit got
                 * set while we were handling the existing iir bits, then
@@ -2706,10 +2807,16 @@ void intel_irq_init(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
-       INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+       INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func);
        INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
        INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
 
+       setup_timer(&dev_priv->gpu_error.hangcheck_timer,
+                   i915_hangcheck_elapsed,
+                   (unsigned long) dev);
+
+       pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
        if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
@@ -2730,7 +2837,8 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->irq_uninstall = valleyview_irq_uninstall;
                dev->driver->enable_vblank = valleyview_enable_vblank;
                dev->driver->disable_vblank = valleyview_disable_vblank;
-       } else if (IS_IVYBRIDGE(dev)) {
+               dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup;
+       } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
                /* Share pre & uninstall handlers with ILK/SNB */
                dev->driver->irq_handler = ivybridge_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;
@@ -2738,14 +2846,6 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ivybridge_enable_vblank;
                dev->driver->disable_vblank = ivybridge_disable_vblank;
-       } else if (IS_HASWELL(dev)) {
-               /* Share interrupts handling with IVB */
-               dev->driver->irq_handler = ivybridge_irq_handler;
-               dev->driver->irq_preinstall = ironlake_irq_preinstall;
-               dev->driver->irq_postinstall = ivybridge_irq_postinstall;
-               dev->driver->irq_uninstall = ironlake_irq_uninstall;
-               dev->driver->enable_vblank = ivybridge_enable_vblank;
-               dev->driver->disable_vblank = ivybridge_disable_vblank;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;
@@ -2764,13 +2864,23 @@ void intel_irq_init(struct drm_device *dev)
                        dev->driver->irq_postinstall = i915_irq_postinstall;
                        dev->driver->irq_uninstall = i915_irq_uninstall;
                        dev->driver->irq_handler = i915_irq_handler;
+                       dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
                } else {
                        dev->driver->irq_preinstall = i965_irq_preinstall;
                        dev->driver->irq_postinstall = i965_irq_postinstall;
                        dev->driver->irq_uninstall = i965_irq_uninstall;
                        dev->driver->irq_handler = i965_irq_handler;
+                       dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup;
                }
                dev->driver->enable_vblank = i915_enable_vblank;
                dev->driver->disable_vblank = i915_disable_vblank;
        }
 }
+
+void intel_hpd_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->display.hpd_irq_setup)
+               dev_priv->display.hpd_irq_setup(dev);
+}