drm/i915: wait for actual vblank, not just 20ms
authorJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 18 Aug 2010 20:20:54 +0000 (13:20 -0700)
committerEric Anholt <eric@anholt.net>
Sun, 22 Aug 2010 05:59:23 +0000 (22:59 -0700)
Waiting for a hard coded 20ms isn't always enough to make sure a vblank
period has actually occurred, so add code to make sure we really have
passed through a vblank period (or that the pipe is off when disabling).

This prevents problems with mode setting and link training, and seems to
fix a bug like https://bugs.freedesktop.org/show_bug.cgi?id=29278, but
on an HP 8440p instead.  Hopefully also fixes
https://bugs.freedesktop.org/show_bug.cgi?id=29141.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_tv.c

index a63e9a176386afa12d68b9495ac5b5c7b4472b07..67e3ec1a6af9dc482870e842b3739a86581176ae 100644 (file)
 #define PIPE_DITHER_TYPE_ST01          (1 << 2)
 /* Pipe A */
 #define PIPEADSL               0x70000
+#define   DSL_LINEMASK         0x00000fff
 #define PIPEACONF              0x70008
 #define   PIPEACONF_ENABLE     (1<<31)
 #define   PIPEACONF_DISABLE    0
index c43176d77549b36801ce0a5a7dbed9848f3d2abc..eb31fdf758e822680069a1eca0987f73b90072b1 100644 (file)
@@ -328,7 +328,7 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder
                I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER);
                /* Wait for next Vblank to substitue
                 * border color for Color info */
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank(dev, pipe);
                st00 = I915_READ8(VGA_MSR_WRITE);
                status = ((st00 & (1 << 4)) != 0) ?
                        connector_status_connected :
index 14c45b1e8778b08b4e47bbd16b3b1779e4f2573d..bdea9464b6783332a0437724a0a0aad85a78c631 100644 (file)
@@ -977,14 +977,54 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
     return true;
 }
 
-void
-intel_wait_for_vblank(struct drm_device *dev)
+/**
+ * intel_wait_for_vblank - wait for vblank on a given pipe
+ * @dev: drm device
+ * @pipe: pipe to wait for
+ *
+ * Wait for vblank to occur on a given pipe.  Needed for various bits of
+ * mode setting code.
+ */
+void intel_wait_for_vblank(struct drm_device *dev, int pipe)
 {
-       /* Wait for 20ms, i.e. one cycle at 50hz. */
-       if (in_dbg_master())
-               mdelay(20); /* The kernel debugger cannot call msleep() */
-       else
-               msleep(20);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipestat_reg = (pipe == 0 ? PIPEASTAT : PIPEBSTAT);
+
+       /* Wait for vblank interrupt bit to set */
+       if (wait_for((I915_READ(pipestat_reg) &
+                     PIPE_VBLANK_INTERRUPT_STATUS) == 0,
+                    50, 0))
+               DRM_DEBUG_KMS("vblank wait timed out\n");
+}
+
+/**
+ * intel_wait_for_vblank_off - wait for vblank after disabling a pipe
+ * @dev: drm device
+ * @pipe: pipe to wait for
+ *
+ * After disabling a pipe, we can't wait for vblank in the usual way,
+ * spinning on the vblank interrupt status bit, since we won't actually
+ * see an interrupt when the pipe is disabled.
+ *
+ * So this function waits for the display line value to settle (it
+ * usually ends up stopping at the start of the next frame).
+ */
+void intel_wait_for_vblank_off(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipedsl_reg = (pipe == 0 ? PIPEADSL : PIPEBDSL);
+       unsigned long timeout = jiffies + msecs_to_jiffies(100);
+       u32 last_line;
+
+       /* Wait for the display line to settle */
+       do {
+               last_line = I915_READ(pipedsl_reg) & DSL_LINEMASK;
+               mdelay(5);
+       } while (((I915_READ(pipedsl_reg) & DSL_LINEMASK) != last_line) &&
+                time_after(timeout, jiffies));
+
+       if (time_after(jiffies, timeout))
+               DRM_DEBUG_KMS("vblank wait timed out\n");
 }
 
 /* Parameters have changed, update FBC info */
@@ -1057,8 +1097,6 @@ void i8xx_disable_fbc(struct drm_device *dev)
                return;
        }
 
-       intel_wait_for_vblank(dev);
-
        DRM_DEBUG_KMS("disabled FBC\n");
 }
 
@@ -1115,7 +1153,6 @@ void g4x_disable_fbc(struct drm_device *dev)
        dpfc_ctl = I915_READ(DPFC_CONTROL);
        dpfc_ctl &= ~DPFC_CTL_EN;
        I915_WRITE(DPFC_CONTROL, dpfc_ctl);
-       intel_wait_for_vblank(dev);
 
        DRM_DEBUG_KMS("disabled FBC\n");
 }
@@ -1176,7 +1213,6 @@ void ironlake_disable_fbc(struct drm_device *dev)
        dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
        dpfc_ctl &= ~DPFC_CTL_EN;
        I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
-       intel_wait_for_vblank(dev);
 
        DRM_DEBUG_KMS("disabled FBC\n");
 }
@@ -1475,7 +1511,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        if ((IS_I965G(dev) || plane == 0))
                intel_update_fbc(crtc, &crtc->mode);
 
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
        intel_increase_pllclock(crtc, true);
 
        return 0;
@@ -1593,7 +1629,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        if ((IS_I965G(dev) || plane == 0))
                intel_update_fbc(crtc, &crtc->mode);
 
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, pipe);
 
        if (old_fb) {
                intel_fb = to_intel_framebuffer(old_fb);
@@ -2343,10 +2379,8 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_READ(dspbase_reg);
                }
 
-               if (!IS_I9XX(dev)) {
-                       /* Wait for vblank for the disable to take effect */
-                       intel_wait_for_vblank(dev);
-               }
+               /* Wait for vblank for the disable to take effect */
+               intel_wait_for_vblank_off(dev, pipe);
 
                /* Don't disable pipe A or pipe A PLLs if needed */
                if (pipeconf_reg == PIPEACONF &&
@@ -2361,7 +2395,7 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
                }
 
                /* Wait for vblank for the disable to take effect. */
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank_off(dev, pipe);
 
                temp = I915_READ(dpll_reg);
                if ((temp & DPLL_VCO_ENABLE) != 0) {
@@ -4096,7 +4130,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        I915_WRITE(pipeconf_reg, pipeconf);
        I915_READ(pipeconf_reg);
 
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, pipe);
 
        if (IS_IRONLAKE(dev)) {
                /* enable address swizzle for tiling buffer */
@@ -4508,7 +4542,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
                encoder_funcs->commit(encoder);
        }
        /* let the connector get through one full cycle before testing */
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
 
        return crtc;
 }
@@ -4713,7 +4747,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
                dpll &= ~DISPLAY_RATE_SELECT_FPA1;
                I915_WRITE(dpll_reg, dpll);
                dpll = I915_READ(dpll_reg);
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank(dev, pipe);
                dpll = I915_READ(dpll_reg);
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
                        DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
@@ -4757,7 +4791,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
                dpll |= DISPLAY_RATE_SELECT_FPA1;
                I915_WRITE(dpll_reg, dpll);
                dpll = I915_READ(dpll_reg);
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank(dev, pipe);
                dpll = I915_READ(dpll_reg);
                if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
                        DRM_DEBUG_DRIVER("failed to downclock LVDS!\n");
index caaaa8f9db3e8adc2dfe5128c7a19032a47dca76..9caccd03dccb6841fea4be4c5fa43f5e8822ee33 100644 (file)
@@ -1145,12 +1145,13 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 {
        struct drm_device *dev = intel_dp->base.enc.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.enc.crtc);
        int ret;
 
        I915_WRITE(intel_dp->output_reg, dp_reg_value);
        POSTING_READ(intel_dp->output_reg);
        if (first)
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
 
        intel_dp_aux_native_write_1(intel_dp,
                                    DP_TRAINING_PATTERN_SET,
index 6ba56e1796ce93c22b614a8f455686dcc3a1b3e6..0e92aa07b38252a4ac3457e75dc2b5113d8d9ded 100644 (file)
@@ -219,7 +219,8 @@ extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
                                                    struct drm_crtc *crtc);
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
-extern void intel_wait_for_vblank(struct drm_device *dev);
+extern void intel_wait_for_vblank_off(struct drm_device *dev, int pipe);
+extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
 extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
 extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
                                                   struct drm_connector *connector,
index 5c765bb0845df9248f24cbb9ae56dc0c0acb11ca..093e914e8a41a3d58b645dcf8475cb24312b3e1e 100644 (file)
@@ -1218,6 +1218,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        u32 temp;
 
        if (mode != DRM_MODE_DPMS_ON) {
@@ -1240,7 +1241,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
                if ((temp & SDVO_ENABLE) == 0)
                        intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
                for (i = 0; i < 2; i++)
-                 intel_wait_for_vblank(dev);
+                       intel_wait_for_vblank(dev, intel_crtc->pipe);
 
                status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
                /* Warn if the device reported failure to sync.
index 1bd6e87950112e7af6b78696d655ddcfd894277b..d2029efee982c3baff92538556ddae0b7a44ea9b 100644 (file)
@@ -1158,11 +1158,11 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 
                /* Wait for vblank for the disable to take effect */
                if (!IS_I9XX(dev))
-                       intel_wait_for_vblank(dev);
+                       intel_wait_for_vblank(dev, intel_crtc->pipe);
 
                I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
                /* Wait for vblank for the disable to take effect. */
-               intel_wait_for_vblank(dev);
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
 
                /* Filter ctl must be set before TV_WIN_SIZE */
                I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
@@ -1231,6 +1231,7 @@ intel_tv_detect_type (struct intel_tv *intel_tv)
        struct drm_encoder *encoder = &intel_tv->base.enc;
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        unsigned long irqflags;
        u32 tv_ctl, save_tv_ctl;
        u32 tv_dac, save_tv_dac;
@@ -1267,11 +1268,11 @@ intel_tv_detect_type (struct intel_tv *intel_tv)
                   DAC_C_0_7_V);
        I915_WRITE(TV_CTL, tv_ctl);
        I915_WRITE(TV_DAC, tv_dac);
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
        tv_dac = I915_READ(TV_DAC);
        I915_WRITE(TV_DAC, save_tv_dac);
        I915_WRITE(TV_CTL, save_tv_ctl);
-       intel_wait_for_vblank(dev);
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
        /*
         *  A B C
         *  0 1 1 Composite