drm/i915: implement ibx_hpd_irq_setup
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index 3c7bb0410b517fe84ae696c720441d553f63cd7f..64c79a0cb57099ba2296b97ef7ddb57700a5c890 100644 (file)
 #include "i915_trace.h"
 #include "intel_drv.h"
 
+static const u32 hpd_ibx[] = {
+       [HPD_CRT] = SDE_CRT_HOTPLUG,
+       [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG,
+       [HPD_PORT_B] = SDE_PORTB_HOTPLUG,
+       [HPD_PORT_C] = SDE_PORTC_HOTPLUG,
+       [HPD_PORT_D] = SDE_PORTD_HOTPLUG
+};
+
+static const u32 hpd_cpt[] = {
+       [HPD_CRT] = SDE_CRT_HOTPLUG_CPT,
+       [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
+       [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
+       [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT
+};
+
+static const u32 hpd_mask_i915[] = {
+       [HPD_CRT] = CRT_HOTPLUG_INT_EN,
+       [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN,
+       [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN,
+       [HPD_PORT_B] = PORTB_HOTPLUG_INT_EN,
+       [HPD_PORT_C] = PORTC_HOTPLUG_INT_EN,
+       [HPD_PORT_D] = PORTD_HOTPLUG_INT_EN
+};
+
+static const u32 hpd_status_gen4[] = {
+       [HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
+       [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X,
+       [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X,
+       [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
+       [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
+       [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
+};
+
+static const u32 hpd_status_i965[] = {
+        [HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
+        [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I965,
+        [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I965,
+        [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
+        [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
+        [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
+};
+
+static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
+       [HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
+       [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915,
+       [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915,
+       [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
+       [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
+       [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
+};
+
+
+
 /* For display hotplug interrupt */
 static void
 ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
@@ -60,26 +113,30 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 void
 i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 {
-       if ((dev_priv->pipestat[pipe] & mask) != mask) {
-               u32 reg = PIPESTAT(pipe);
+       u32 reg = PIPESTAT(pipe);
+       u32 pipestat = I915_READ(reg) & 0x7fff0000;
 
-               dev_priv->pipestat[pipe] |= mask;
-               /* Enable the interrupt, clear any pending status */
-               I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16));
-               POSTING_READ(reg);
-       }
+       if ((pipestat & mask) == mask)
+               return;
+
+       /* Enable the interrupt, clear any pending status */
+       pipestat |= mask | (mask >> 16);
+       I915_WRITE(reg, pipestat);
+       POSTING_READ(reg);
 }
 
 void
 i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 {
-       if ((dev_priv->pipestat[pipe] & mask) != 0) {
-               u32 reg = PIPESTAT(pipe);
+       u32 reg = PIPESTAT(pipe);
+       u32 pipestat = I915_READ(reg) & 0x7fff0000;
 
-               dev_priv->pipestat[pipe] &= ~mask;
-               I915_WRITE(reg, dev_priv->pipestat[pipe]);
-               POSTING_READ(reg);
-       }
+       if ((pipestat & mask) == 0)
+               return;
+
+       pipestat &= ~mask;
+       I915_WRITE(reg, pipestat);
+       POSTING_READ(reg);
 }
 
 /**
@@ -250,10 +307,9 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
                              struct timeval *vblank_time,
                              unsigned flags)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
 
-       if (pipe < 0 || pipe >= dev_priv->num_pipe) {
+       if (pipe < 0 || pipe >= INTEL_INFO(dev)->num_pipes) {
                DRM_ERROR("Invalid crtc %d\n", pipe);
                return -EINVAL;
        }
@@ -596,7 +652,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                         hotplug_status);
-                       if (hotplug_status & dev_priv->hotplug_supported_mask)
+                       if (hotplug_status & HOTPLUG_INT_STATUS_I915)
                                queue_work(dev_priv->wq,
                                           &dev_priv->hotplug_work);
 
@@ -937,6 +993,8 @@ static void i915_error_work_func(struct work_struct *work)
                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);
        }
 }
@@ -972,24 +1030,23 @@ static void i915_get_extra_instdone(struct drm_device *dev,
 
 #ifdef CONFIG_DEBUG_FS
 static struct drm_i915_error_object *
-i915_error_object_create(struct drm_i915_private *dev_priv,
-                        struct drm_i915_gem_object *src)
+i915_error_object_create_sized(struct drm_i915_private *dev_priv,
+                              struct drm_i915_gem_object *src,
+                              const int num_pages)
 {
        struct drm_i915_error_object *dst;
-       int i, count;
+       int i;
        u32 reloc_offset;
 
        if (src == NULL || src->pages == NULL)
                return NULL;
 
-       count = src->base.size / PAGE_SIZE;
-
-       dst = kmalloc(sizeof(*dst) + count * sizeof(u32 *), GFP_ATOMIC);
+       dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC);
        if (dst == NULL)
                return NULL;
 
        reloc_offset = src->gtt_offset;
-       for (i = 0; i < count; i++) {
+       for (i = 0; i < num_pages; i++) {
                unsigned long flags;
                void *d;
 
@@ -1039,7 +1096,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
 
                reloc_offset += PAGE_SIZE;
        }
-       dst->page_count = count;
+       dst->page_count = num_pages;
        dst->gtt_offset = src->gtt_offset;
 
        return dst;
@@ -1050,6 +1107,9 @@ unwind:
        kfree(dst);
        return NULL;
 }
+#define i915_error_object_create(dev_priv, src) \
+       i915_error_object_create_sized((dev_priv), (src), \
+                                      (src)->base.size>>PAGE_SHIFT)
 
 static void
 i915_error_object_free(struct drm_i915_error_object *obj)
@@ -1256,6 +1316,26 @@ static void i915_record_ring_state(struct drm_device *dev,
        error->cpu_ring_tail[ring->id] = ring->tail;
 }
 
+
+static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
+                                          struct drm_i915_error_state *error,
+                                          struct drm_i915_error_ring *ering)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       struct drm_i915_gem_object *obj;
+
+       /* Currently render ring is the only HW context user */
+       if (ring->id != RCS || !error->ccid)
+               return;
+
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+               if ((error->ccid & PAGE_MASK) == obj->gtt_offset) {
+                       ering->ctx = i915_error_object_create_sized(dev_priv,
+                                                                   obj, 1);
+               }
+       }
+}
+
 static void i915_gem_record_rings(struct drm_device *dev,
                                  struct drm_i915_error_state *error)
 {
@@ -1273,6 +1353,9 @@ static void i915_gem_record_rings(struct drm_device *dev,
                error->ring[i].ringbuffer =
                        i915_error_object_create(dev_priv, ring->obj);
 
+
+               i915_gem_record_active_context(ring, error, &error->ring[i]);
+
                count = 0;
                list_for_each_entry(request, &ring->request_list, list)
                        count++;
@@ -1328,14 +1411,15 @@ static void i915_capture_error_state(struct drm_device *dev)
                return;
        }
 
-       DRM_INFO("capturing error event; look for more information in"
+       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);
        error->eir = I915_READ(EIR);
        error->pgtbl_er = I915_READ(PGTBL_ER);
-       error->ccid = I915_READ(CCID);
+       if (HAS_HW_CONTEXTS(dev))
+               error->ccid = I915_READ(CCID);
 
        if (HAS_PCH_SPLIT(dev))
                error->ier = I915_READ(DEIER) | I915_READ(GTIER);
@@ -1356,8 +1440,9 @@ static void i915_capture_error_state(struct drm_device *dev)
        else if (INTEL_INFO(dev)->gen == 6)
                error->forcewake = I915_READ(FORCEWAKE);
 
-       for_each_pipe(pipe)
-               error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
+       if (!HAS_PCH_SPLIT(dev))
+               for_each_pipe(pipe)
+                       error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
 
        if (INTEL_INFO(dev)->gen >= 6) {
                error->error = I915_READ(ERROR_GEN6);
@@ -1567,7 +1652,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
        queue_work(dev_priv->wq, &dev_priv->gpu_error.work);
 }
 
-static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)
+static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
@@ -1777,6 +1862,37 @@ static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
        return false;
 }
 
+static bool semaphore_passed(struct intel_ring_buffer *ring)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       u32 acthd = intel_ring_get_active_head(ring) & HEAD_ADDR;
+       struct intel_ring_buffer *signaller;
+       u32 cmd, ipehr, acthd_min;
+
+       ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
+       if ((ipehr & ~(0x3 << 16)) !=
+           (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER))
+               return false;
+
+       /* ACTHD is likely pointing to the dword after the actual command,
+        * so scan backwards until we find the MBOX.
+        */
+       acthd_min = max((int)acthd - 3 * 4, 0);
+       do {
+               cmd = ioread32(ring->virtual_start + acthd);
+               if (cmd == ipehr)
+                       break;
+
+               acthd -= 4;
+               if (acthd < acthd_min)
+                       return false;
+       } while (1);
+
+       signaller = &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
+       return i915_seqno_passed(signaller->get_seqno(signaller, false),
+                                ioread32(ring->virtual_start+acthd+4)+1);
+}
+
 static bool kick_ring(struct intel_ring_buffer *ring)
 {
        struct drm_device *dev = ring->dev;
@@ -1788,6 +1904,15 @@ static bool kick_ring(struct intel_ring_buffer *ring)
                I915_WRITE_CTL(ring, tmp);
                return true;
        }
+
+       if (INTEL_INFO(dev)->gen >= 6 &&
+           tmp & RING_WAIT_SEMAPHORE &&
+           semaphore_passed(ring)) {
+               DRM_ERROR("Kicking stuck semaphore on %s\n",
+                         ring->name);
+               I915_WRITE_CTL(ring, tmp);
+               return true;
+       }
        return false;
 }
 
@@ -1903,7 +2028,13 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
 
        /* south display irq */
        I915_WRITE(SDEIMR, 0xffffffff);
-       I915_WRITE(SDEIER, 0x0);
+       /*
+        * SDEIER is also touched by the interrupt handler to work around missed
+        * PCH interrupts. Hence we can't update it after the interrupt handler
+        * is enabled - instead we unconditionally enable all PCH interrupt
+        * sources here, but then only unmask them as needed with SDEIMR.
+        */
+       I915_WRITE(SDEIER, 0xffffffff);
        POSTING_READ(SDEIER);
 }
 
@@ -1939,18 +2070,30 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 }
 
-/*
- * Enable digital hotplug on the PCH, and configure the DP short pulse
- * duration to 2ms (which is the minimum in the Display Port spec)
- *
- * This register is the same on all known PCH chips.
- */
-
-static void ibx_enable_hotplug(struct drm_device *dev)
+static void ibx_hpd_irq_setup(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32     hotplug;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *intel_encoder;
+       u32 mask = ~I915_READ(SDEIMR);
+       u32 hotplug;
+
+       if (HAS_PCH_IBX(dev)) {
+               list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
+                       mask |= hpd_ibx[intel_encoder->hpd_pin];
+       } else {
+               list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
+                       mask |= hpd_cpt[intel_encoder->hpd_pin];
+       }
+
+       I915_WRITE(SDEIMR, ~mask);
 
+       /*
+        * Enable digital hotplug on the PCH, and configure the DP short pulse
+        * duration to 2ms (which is the minimum in the Display Port spec)
+        *
+        * This register is the same on all known PCH chips.
+        */
        hotplug = I915_READ(PCH_PORT_HOTPLUG);
        hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK);
        hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
@@ -1965,20 +2108,11 @@ static void ibx_irq_postinstall(struct drm_device *dev)
        u32 mask;
 
        if (HAS_PCH_IBX(dev))
-               mask = SDE_HOTPLUG_MASK |
-                      SDE_GMBUS |
-                      SDE_AUX_MASK;
+               mask = SDE_GMBUS | SDE_AUX_MASK;
        else
-               mask = SDE_HOTPLUG_MASK_CPT |
-                      SDE_GMBUS_CPT |
-                      SDE_AUX_MASK_CPT;
-
+               mask = 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)
@@ -2089,9 +2223,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
                I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
                I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
 
-       dev_priv->pipestat[0] = 0;
-       dev_priv->pipestat[1] = 0;
-
        /* Hack for broken MSIs on VLV */
        pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000);
        pci_read_config_word(dev->pdev, 0x98, &msid);
@@ -2135,30 +2266,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        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 & 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)
-               hotplug_en |= SDVOB_HOTPLUG_INT_EN;
-       if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) {
-               hotplug_en |= CRT_HOTPLUG_INT_EN;
-               hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
-       }
-
-       I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
-}
-
 static void valleyview_irq_uninstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -2221,9 +2328,6 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       dev_priv->pipestat[0] = 0;
-       dev_priv->pipestat[1] = 0;
-
        I915_WRITE16(EMR,
                     ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
 
@@ -2246,6 +2350,37 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
        return 0;
 }
 
+/*
+ * Returns true when a page flip has completed.
+ */
+static bool i8xx_handle_vblank(struct drm_device *dev,
+                              int pipe, u16 iir)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(pipe);
+
+       if (!drm_handle_vblank(dev, pipe))
+               return false;
+
+       if ((iir & flip_pending) == 0)
+               return false;
+
+       intel_prepare_page_flip(dev, pipe);
+
+       /* We detect FlipDone by looking for the change in PendingFlip from '1'
+        * to '0' on the following vblank, i.e. IIR has the Pendingflip
+        * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence
+        * the flip is completed (no longer pending). Since this doesn't raise
+        * an interrupt per se, we watch for the change at vblank.
+        */
+       if (I915_READ16(ISR) & flip_pending)
+               return false;
+
+       intel_finish_page_flip(dev, pipe);
+
+       return true;
+}
+
 static irqreturn_t i8xx_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -2301,22 +2436,12 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
                        notify_ring(dev, &dev_priv->ring[RCS]);
 
                if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   drm_handle_vblank(dev, 0)) {
-                       if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) {
-                               intel_prepare_page_flip(dev, 0);
-                               intel_finish_page_flip(dev, 0);
-                               flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT;
-                       }
-               }
+                   i8xx_handle_vblank(dev, 0, iir))
+                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0);
 
                if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                   drm_handle_vblank(dev, 1)) {
-                       if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) {
-                               intel_prepare_page_flip(dev, 1);
-                               intel_finish_page_flip(dev, 1);
-                               flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
-                       }
-               }
+                   i8xx_handle_vblank(dev, 1, iir))
+                       flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1);
 
                iir = new_iir;
        }
@@ -2364,9 +2489,6 @@ 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;
 
-       dev_priv->pipestat[0] = 0;
-       dev_priv->pipestat[1] = 0;
-
        I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
 
        /* Unmask the interrupts that we always want on. */
@@ -2406,26 +2528,16 @@ static int i915_irq_postinstall(struct drm_device *dev)
 
 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)) {
-               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)
-                       hotplug_en |= SDVOB_HOTPLUG_INT_EN;
-               if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) {
-                       hotplug_en |= CRT_HOTPLUG_INT_EN;
-                       hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
-               }
+               drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+               struct drm_mode_config *mode_config = &dev->mode_config;
+               struct intel_encoder *encoder;
+               u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
+               hotplug_en &= ~HOTPLUG_INT_EN_MASK;
+               list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+                       hotplug_en |= hpd_mask_i915[encoder->hpd_pin];
+               hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
 
                /* Ignore TV since it's buggy */
 
@@ -2433,6 +2545,37 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
        }
 }
 
+/*
+ * Returns true when a page flip has completed.
+ */
+static bool i915_handle_vblank(struct drm_device *dev,
+                              int plane, int pipe, u32 iir)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
+
+       if (!drm_handle_vblank(dev, pipe))
+               return false;
+
+       if ((iir & flip_pending) == 0)
+               return false;
+
+       intel_prepare_page_flip(dev, plane);
+
+       /* We detect FlipDone by looking for the change in PendingFlip from '1'
+        * to '0' on the following vblank, i.e. IIR has the Pendingflip
+        * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence
+        * the flip is completed (no longer pending). Since this doesn't raise
+        * an interrupt per se, we watch for the change at vblank.
+        */
+       if (I915_READ(ISR) & flip_pending)
+               return false;
+
+       intel_finish_page_flip(dev, pipe);
+
+       return true;
+}
+
 static irqreturn_t i915_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -2442,10 +2585,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
        u32 flip_mask =
                I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
                I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
-       u32 flip[2] = {
-               I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT,
-               I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT
-       };
        int pipe, ret = IRQ_NONE;
 
        atomic_inc(&dev_priv->irq_received);
@@ -2489,7 +2628,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
-                       if (hotplug_status & dev_priv->hotplug_supported_mask)
+                       if (hotplug_status & HOTPLUG_INT_STATUS_I915)
                                queue_work(dev_priv->wq,
                                           &dev_priv->hotplug_work);
 
@@ -2507,14 +2646,10 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
                        int plane = pipe;
                        if (IS_MOBILE(dev))
                                plane = !plane;
+
                        if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
-                           drm_handle_vblank(dev, pipe)) {
-                               if (iir & flip[plane]) {
-                                       intel_prepare_page_flip(dev, plane);
-                                       intel_finish_page_flip(dev, pipe);
-                                       flip_mask &= ~flip[plane];
-                               }
-                       }
+                           i915_handle_vblank(dev, plane, pipe, iir))
+                               flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane);
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
@@ -2603,13 +2738,13 @@ static int i965_irq_postinstall(struct drm_device *dev)
                               I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
 
        enable_mask = ~dev_priv->irq_mask;
+       enable_mask &= ~(I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
+                        I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
        enable_mask |= I915_USER_INTERRUPT;
 
        if (IS_G4X(dev))
                enable_mask |= I915_BSD_USER_INTERRUPT;
 
-       dev_priv->pipestat[0] = 0;
-       dev_priv->pipestat[1] = 0;
        i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
 
        /*
@@ -2642,38 +2777,22 @@ static int i965_irq_postinstall(struct drm_device *dev)
 static void i965_hpd_irq_setup(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *encoder;
        u32 hotplug_en;
 
        /* Note HDMI and DP share hotplug bits */
        hotplug_en = 0;
-       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;
-               if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_G4X)
-                       hotplug_en |= SDVOB_HOTPLUG_INT_EN;
-       } else {
-               if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I965)
-                       hotplug_en |= SDVOC_HOTPLUG_INT_EN;
-               if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I965)
-                       hotplug_en |= SDVOB_HOTPLUG_INT_EN;
-       }
-       if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) {
-               hotplug_en |= CRT_HOTPLUG_INT_EN;
-
-               /* Programming the CRT detection parameters tends
-                  to generate a spurious hotplug event about three
-                  seconds later.  So just do it once.
-                  */
-               if (IS_G4X(dev))
-                       hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
-               hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
-       }
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+               /* enable bits are the same for all generations */
+               hotplug_en |= hpd_mask_i915[encoder->hpd_pin];
+       /* Programming the CRT detection parameters tends
+          to generate a spurious hotplug event about three
+          seconds later.  So just do it once.
+       */
+       if (IS_G4X(dev))
+               hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
+       hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
 
        /* Ignore TV since it's buggy */
 
@@ -2689,6 +2808,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
        unsigned long irqflags;
        int irq_received;
        int ret = IRQ_NONE, pipe;
+       u32 flip_mask =
+               I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
+               I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -2697,7 +2819,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
        for (;;) {
                bool blc_event = false;
 
-               irq_received = iir != 0;
+               irq_received = (iir & ~flip_mask) != 0;
 
                /* Can't rely on pipestat interrupt bit in iir as it might
                 * have been cleared after the pipestat interrupt was received.
@@ -2736,7 +2858,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
-                       if (hotplug_status & dev_priv->hotplug_supported_mask)
+                       if (hotplug_status & (IS_G4X(dev) ?
+                                             HOTPLUG_INT_STATUS_G4X :
+                                             HOTPLUG_INT_STATUS_I965))
                                queue_work(dev_priv->wq,
                                           &dev_priv->hotplug_work);
 
@@ -2744,7 +2868,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                        I915_READ(PORT_HOTPLUG_STAT);
                }
 
-               I915_WRITE(IIR, iir);
+               I915_WRITE(IIR, iir & ~flip_mask);
                new_iir = I915_READ(IIR); /* Flush posted writes */
 
                if (iir & I915_USER_INTERRUPT)
@@ -2752,18 +2876,10 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                if (iir & I915_BSD_USER_INTERRUPT)
                        notify_ring(dev, &dev_priv->ring[VCS]);
 
-               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);
-
                for_each_pipe(pipe) {
                        if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
-                           drm_handle_vblank(dev, pipe)) {
-                               i915_pageflip_stall_check(dev, pipe);
-                               intel_finish_page_flip(dev, pipe);
-                       }
+                           i915_handle_vblank(dev, pipe, pipe, iir))
+                               flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
 
                        if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
                                blc_event = true;
@@ -2857,7 +2973,7 @@ 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;
-               dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup;
+               dev_priv->display.hpd_irq_setup = i915_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;
@@ -2866,6 +2982,7 @@ 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;
+               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;
@@ -2873,6 +2990,7 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ironlake_enable_vblank;
                dev->driver->disable_vblank = ironlake_disable_vblank;
+               dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
        } else {
                if (INTEL_INFO(dev)->gen == 2) {
                        dev->driver->irq_preinstall = i8xx_irq_preinstall;
This page took 0.034923 seconds and 5 git commands to generate.