drm/i915: Fix gen2 and hsw+ scanline counter
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 15 May 2014 17:23:23 +0000 (20:23 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 22 May 2014 13:06:38 +0000 (15:06 +0200)
On gen2 the scanline counter behaves a bit differently from the
later generations. Instead of adding one to the raw scanline
counter value, we must subtract one.

On HSW/BDW the scanline counter requires a +2 adjustment on HDMI
outputs. DP outputs on the on the other require the typical +1
adjustment.

As the fixup we must apply to the hardware scanline counter
depends on several factors, compute the desired offset at modeset
time and tuck it away for when it's needed.

v2: Clarify HSW+ situation

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: "Akash Goel <akash.goels@gmail.com>"
Reviewed-by: "Sourab Gupta <sourabgupta@gmail.com>"
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=78997
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h

index 087e76b9ee355ece9cb6124c21236a7800485d1b..304f86a3162cc9bbaedac2a52637da5699d69183 100644 (file)
@@ -889,9 +889,9 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
        enum pipe pipe = crtc->pipe;
-       int vtotal = mode->crtc_vtotal;
-       int position;
+       int position, vtotal;
 
+       vtotal = mode->crtc_vtotal;
        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
                vtotal /= 2;
 
@@ -901,14 +901,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
                position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
 
        /*
-        * Scanline counter increments at leading edge of hsync, and
-        * it starts counting from vtotal-1 on the first active line.
-        * That means the scanline counter value is always one less
-        * than what we would expect. Ie. just after start of vblank,
-        * which also occurs at start of hsync (on the last active line),
-        * the scanline counter will read vblank_start-1.
+        * See update_scanline_offset() for the details on the
+        * scanline_offset adjustment.
         */
-       return (position + 1) % vtotal;
+       return (position + crtc->scanline_offset) % vtotal;
 }
 
 static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
index d53758723ac477f10a4c84f6c513fbb7230f946e..6db85e28cb66351ea75d0172339cee91ef666c63 100644 (file)
@@ -10231,6 +10231,44 @@ void ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config
             pipe_config->adjusted_mode.crtc_clock, dotclock);
 }
 
+static void update_scanline_offset(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+
+       /*
+        * The scanline counter increments at the leading edge of hsync.
+        *
+        * On most platforms it starts counting from vtotal-1 on the
+        * first active line. That means the scanline counter value is
+        * always one less than what we would expect. Ie. just after
+        * start of vblank, which also occurs at start of hsync (on the
+        * last active line), the scanline counter will read vblank_start-1.
+        *
+        * On gen2 the scanline counter starts counting from 1 instead
+        * of vtotal-1, so we have to subtract one (or rather add vtotal-1
+        * to keep the value positive), instead of adding one.
+        *
+        * On HSW+ the behaviour of the scanline counter depends on the output
+        * type. For DP ports it behaves like most other platforms, but on HDMI
+        * there's an extra 1 line difference. So we need to add two instead of
+        * one to the value.
+        */
+       if (IS_GEN2(dev)) {
+               const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+               int vtotal;
+
+               vtotal = mode->crtc_vtotal;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       vtotal /= 2;
+
+               crtc->scanline_offset = vtotal - 1;
+       } else if (HAS_DDI(dev) &&
+                  intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) {
+               crtc->scanline_offset = 2;
+       } else
+               crtc->scanline_offset = 1;
+}
+
 static int __intel_set_mode(struct drm_crtc *crtc,
                            struct drm_display_mode *mode,
                            int x, int y, struct drm_framebuffer *fb)
@@ -10349,8 +10387,11 @@ static int __intel_set_mode(struct drm_crtc *crtc,
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
-       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
+       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
+               update_scanline_offset(intel_crtc);
+
                dev_priv->display.crtc_enable(&intel_crtc->base);
+       }
 
        /* FIXME: add subpixel order */
 done:
@@ -11900,6 +11941,8 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
                 */
                crtc->cpu_fifo_underrun_disabled = true;
                crtc->pch_fifo_underrun_disabled = true;
+
+               update_scanline_offset(crtc);
        }
 }
 
index 287b89e97456ae93b6a6e205538b279d4d169565..d7c52b2c546aea71f250afaa8394349df2dc9c1f 100644 (file)
@@ -409,6 +409,8 @@ struct intel_crtc {
        } wm;
 
        wait_queue_head_t vbl_wait;
+
+       int scanline_offset;
 };
 
 struct intel_plane_wm_parameters {