drm/i915: Improve the accuracy of get_scanout_pos on CTG+
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Mon, 23 Sep 2013 10:02:07 +0000 (13:02 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 14 Oct 2013 15:12:35 +0000 (17:12 +0200)
The DSL register increments at the start of horizontal sync, so it
manages to miss the entire active portion of the current line.

Improve the get_scanoutpos accuracy a bit when the scanout position is
close to the start or end of vblank. We can do that by double checking
the DSL value against the vblank status bit from ISR.

Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: mario.kleiner.de@gmail.com
Tested-by: mario.kleiner.de@gmail.com
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_irq.c

index a923dea245ace3b255d8ae98734dd18f699c6e42..dfa5c68c276340a866f1e82d1fa4f6d9310d5a66 100644 (file)
@@ -592,6 +592,47 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
        return I915_READ(reg);
 }
 
+static bool g4x_pipe_in_vblank(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t status;
+
+       if (IS_VALLEYVIEW(dev)) {
+               status = pipe == PIPE_A ?
+                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
+                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+               return I915_READ(VLV_ISR) & status;
+       } else if (IS_G4X(dev)) {
+               status = pipe == PIPE_A ?
+                       I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
+                       I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+               return I915_READ(ISR) & status;
+       } else if (INTEL_INFO(dev)->gen < 7) {
+               status = pipe == PIPE_A ?
+                       DE_PIPEA_VBLANK :
+                       DE_PIPEB_VBLANK;
+
+               return I915_READ(DEISR) & status;
+       } else {
+               switch (pipe) {
+               default:
+               case PIPE_A:
+                       status = DE_PIPEA_VBLANK_IVB;
+                       break;
+               case PIPE_B:
+                       status = DE_PIPEB_VBLANK_IVB;
+                       break;
+               case PIPE_C:
+                       status = DE_PIPEC_VBLANK_IVB;
+                       break;
+               }
+
+               return I915_READ(DEISR) & status;
+       }
+}
+
 static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
                             int *vpos, int *hpos)
 {
@@ -622,6 +663,18 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
                 * scanout position from Display scan line register.
                 */
                position = I915_READ(PIPEDSL(pipe)) & 0x1fff;
+
+               /*
+                * The scanline counter increments at the leading edge
+                * of hsync, ie. it completely misses the active portion
+                * of the line. Fix up the counter at both edges of vblank
+                * to get a more accurate picture whether we're in vblank
+                * or not.
+                */
+               in_vbl = g4x_pipe_in_vblank(dev, pipe);
+               if ((in_vbl && position == vbl_start - 1) ||
+                   (!in_vbl && position == vbl_end - 1))
+                       position = (position + 1) % vtotal;
        } else {
                /* Have access to pixelcount since start of frame.
                 * We can split this into vertical and horizontal