drm/i915: Fix mmio vs. CS flip race on ILK+
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / i915 / intel_display.c
index a68dcce6ac3949c030d538c9a54a19b9ed417d33..930746527e9d8c0e46a4ed0ce0e48aa0b7d8dbe2 100644 (file)
@@ -1574,7 +1574,6 @@ static void chv_enable_pll(struct intel_crtc *crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe = crtc->pipe;
        enum dpio_channel port = vlv_pipe_to_channel(pipe);
-       int dpll = DPLL(crtc->pipe);
        u32 tmp;
 
        assert_pipe_disabled(dev_priv, crtc->pipe);
@@ -1594,19 +1593,15 @@ static void chv_enable_pll(struct intel_crtc *crtc)
        udelay(1);
 
        /* Enable PLL */
-       tmp = I915_READ(dpll);
-       tmp |= DPLL_VCO_ENABLE;
-       I915_WRITE(dpll, tmp);
+       I915_WRITE(DPLL(pipe), crtc->config.dpll_hw_state.dpll);
 
        /* Check PLL is locked */
-       if (wait_for(((I915_READ(dpll) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
+       if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
                DRM_ERROR("PLL %d failed to lock\n", pipe);
 
-       /* Deassert soft data lane reset*/
-       tmp = vlv_dpio_read(dev_priv, pipe, VLV_PCS_DW0(port));
-       tmp |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
-       vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), tmp);
-
+       /* not sure when this should be written */
+       I915_WRITE(DPLL_MD(pipe), crtc->config.dpll_hw_state.dpll_md);
+       POSTING_READ(DPLL_MD(pipe));
 
        mutex_unlock(&dev_priv->dpio_lock);
 }
@@ -1699,14 +1694,27 @@ static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
 
 static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
 {
-       int dpll = DPLL(pipe);
+       enum dpio_channel port = vlv_pipe_to_channel(pipe);
        u32 val;
 
+       /* Make sure the pipe isn't still relying on us */
+       assert_pipe_disabled(dev_priv, pipe);
+
        /* Set PLL en = 0 */
-       val = I915_READ(dpll);
-       val &= ~DPLL_VCO_ENABLE;
-       I915_WRITE(dpll, val);
+       val = DPLL_SSC_REF_CLOCK_CHV;
+       if (pipe != PIPE_A)
+               val |= DPLL_INTEGRATED_CRI_CLK_VLV;
+       I915_WRITE(DPLL(pipe), val);
+       POSTING_READ(DPLL(pipe));
 
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Disable 10bit clock to display controller */
+       val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
+       val &= ~DPIO_DCLKP_EN;
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
@@ -5507,11 +5515,18 @@ static void chv_update_pll(struct intel_crtc *crtc)
        int pipe = crtc->pipe;
        int dpll_reg = DPLL(crtc->pipe);
        enum dpio_channel port = vlv_pipe_to_channel(pipe);
-       u32 val, loopfilter, intcoeff;
+       u32 loopfilter, intcoeff;
        u32 bestn, bestm1, bestm2, bestp1, bestp2, bestm2_frac;
        int refclk;
 
-       mutex_lock(&dev_priv->dpio_lock);
+       crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV |
+               DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
+               DPLL_VCO_ENABLE;
+       if (pipe != PIPE_A)
+               crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+
+       crtc->config.dpll_hw_state.dpll_md =
+               (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
 
        bestn = crtc->config.dpll.n;
        bestm2_frac = crtc->config.dpll.m2 & 0x3fffff;
@@ -5523,19 +5538,10 @@ static void chv_update_pll(struct intel_crtc *crtc)
        /*
         * Enable Refclk and SSC
         */
-       val = I915_READ(dpll_reg);
-       val |= (DPLL_SSC_REF_CLOCK_CHV | DPLL_REFA_CLK_ENABLE_VLV);
-       I915_WRITE(dpll_reg, val);
-
-       /* Propagate soft reset to data lane reset */
-       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS_DW0(port));
-       val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
-       vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), val);
+       I915_WRITE(dpll_reg,
+                  crtc->config.dpll_hw_state.dpll & ~DPLL_VCO_ENABLE);
 
-       /* Disable 10bit clock to display controller */
-       val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
-       val &= ~DPIO_DCLKP_EN;
-       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val);
+       mutex_lock(&dev_priv->dpio_lock);
 
        /* p1 and p2 divider */
        vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW13(port),
@@ -8840,6 +8846,48 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
        do_intel_finish_page_flip(dev, crtc);
 }
 
+/* Is 'a' after or equal to 'b'? */
+static bool g4x_flip_count_after_eq(u32 a, u32 b)
+{
+       return !((a - b) & 0x80000000);
+}
+
+static bool page_flip_finished(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /*
+        * The relevant registers doen't exist on pre-ctg.
+        * As the flip done interrupt doesn't trigger for mmio
+        * flips on gmch platforms, a flip count check isn't
+        * really needed there. But since ctg has the registers,
+        * include it in the check anyway.
+        */
+       if (INTEL_INFO(dev)->gen < 5 && !IS_G4X(dev))
+               return true;
+
+       /*
+        * A DSPSURFLIVE check isn't enough in case the mmio and CS flips
+        * used the same base address. In that case the mmio flip might
+        * have completed, but the CS hasn't even executed the flip yet.
+        *
+        * A flip count check isn't enough as the CS might have updated
+        * the base address just after start of vblank, but before we
+        * managed to process the interrupt. This means we'd complete the
+        * CS flip too soon.
+        *
+        * Combining both checks should get us a good enough result. It may
+        * still happen that the CS flip has been executed, but has not
+        * yet actually completed. But in case the base address is the same
+        * anyway, we don't really care.
+        */
+       return (I915_READ(DSPSURFLIVE(crtc->plane)) & ~0xfff) ==
+               crtc->unpin_work->gtt_offset &&
+               g4x_flip_count_after_eq(I915_READ(PIPE_FLIPCOUNT_GM45(crtc->pipe)),
+                                   crtc->unpin_work->flip_count);
+}
+
 void intel_prepare_page_flip(struct drm_device *dev, int plane)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -8852,7 +8900,7 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
         * is also accompanied by a spurious intel_prepare_page_flip().
         */
        spin_lock_irqsave(&dev->event_lock, flags);
-       if (intel_crtc->unpin_work)
+       if (intel_crtc->unpin_work && page_flip_finished(intel_crtc))
                atomic_inc_not_zero(&intel_crtc->unpin_work->pending);
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
@@ -8882,6 +8930,9 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
        if (ret)
                goto err;
 
+       intel_crtc->unpin_work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
        ret = intel_ring_begin(ring, 6);
        if (ret)
                goto err_unpin;
@@ -8898,7 +8949,7 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, 0); /* aux display base address, unused */
 
        intel_mark_page_flip_active(intel_crtc);
@@ -8927,6 +8978,9 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
        if (ret)
                goto err;
 
+       intel_crtc->unpin_work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
        ret = intel_ring_begin(ring, 6);
        if (ret)
                goto err_unpin;
@@ -8940,7 +8994,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, MI_NOOP);
 
        intel_mark_page_flip_active(intel_crtc);
@@ -8969,6 +9023,9 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
        if (ret)
                goto err;
 
+       intel_crtc->unpin_work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
        ret = intel_ring_begin(ring, 4);
        if (ret)
                goto err_unpin;
@@ -8980,8 +9037,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring,
-                       (i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset) |
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset |
                        obj->tiling_mode);
 
        /* XXX Enabling the panel-fitter across page-flip is so far
@@ -9018,6 +9074,9 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
        if (ret)
                goto err;
 
+       intel_crtc->unpin_work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
        ret = intel_ring_begin(ring, 4);
        if (ret)
                goto err_unpin;
@@ -9025,7 +9084,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
 
        /* Contrary to the suggestions in the documentation,
         * "Enable Panel Fitter" does not seem to be required when page
@@ -9067,6 +9126,9 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
        if (ret)
                goto err;
 
+       intel_crtc->unpin_work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
        switch (intel_crtc->plane) {
        case PLANE_A:
                plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_A;
@@ -9144,7 +9206,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
 
        intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit);
        intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode));
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, (MI_NOOP));
 
        intel_mark_page_flip_active(intel_crtc);
@@ -9242,6 +9304,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        atomic_inc(&intel_crtc->unpin_work_count);
        intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
 
+       if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
+               work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(intel_crtc->pipe)) + 1;
+
        ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, page_flip_flags);
        if (ret)
                goto cleanup_pending;
@@ -11053,6 +11118,15 @@ static void intel_setup_outputs(struct drm_device *dev)
                                intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C);
                }
 
+               if (IS_CHERRYVIEW(dev)) {
+                       if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) {
+                               intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID,
+                                               PORT_D);
+                               if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED)
+                                       intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D);
+                       }
+               }
+
                intel_dsi_init(dev);
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;