Commit a87e55f8 authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Joonas Lahtinen

drm/i915: Fix vblank timestamp/frame counter jumps on gen2

Previously I was under the impression that the scanline counter
reads 0 when the pipe is off. Turns out that's not correct, and
instead the scanline counter simply stops when the pipe stops, and
it retains it's last value until the pipe starts up again, at which
point the scanline counter jumps to vblank start.

These jumps can cause the timestamp to jump backwards by one frame.
Since we use the timestamps to guesstimage also the frame counter
value on gen2, that would cause the frame counter to also jump
backwards, which leads to a massice difference from the previous value.
The end result is that flips/vblank events don't appear to complete as
they're stuck waiting for the frame counter to catch up to that massive
difference.

Fix the problem properly by actually making sure the scanline counter
has started to move before we assume that it's safe to enable vblank
processing.

v2: Less pointless duplication in the code (Chris)

Cc: stable@vger.kernel.org
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Fixes: b7792d8b ("drm/i915: Wait for pipe to start before sampling vblank timestamps on gen2")
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20171129153732.3612-1-ville.syrjala@linux.intel.com
(cherry picked from commit 8fedd64d)
Signed-off-by: default avatarJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
parent dda4b8f7
...@@ -1000,7 +1000,8 @@ enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, ...@@ -1000,7 +1000,8 @@ enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
return crtc->config->cpu_transcoder; return crtc->config->cpu_transcoder;
} }
static bool pipe_dsl_stopped(struct drm_i915_private *dev_priv, enum pipe pipe) static bool pipe_scanline_is_moving(struct drm_i915_private *dev_priv,
enum pipe pipe)
{ {
i915_reg_t reg = PIPEDSL(pipe); i915_reg_t reg = PIPEDSL(pipe);
u32 line1, line2; u32 line1, line2;
...@@ -1015,7 +1016,28 @@ static bool pipe_dsl_stopped(struct drm_i915_private *dev_priv, enum pipe pipe) ...@@ -1015,7 +1016,28 @@ static bool pipe_dsl_stopped(struct drm_i915_private *dev_priv, enum pipe pipe)
msleep(5); msleep(5);
line2 = I915_READ(reg) & line_mask; line2 = I915_READ(reg) & line_mask;
return line1 == line2; return line1 != line2;
}
static void wait_for_pipe_scanline_moving(struct intel_crtc *crtc, bool state)
{
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
enum pipe pipe = crtc->pipe;
/* Wait for the display line to settle/start moving */
if (wait_for(pipe_scanline_is_moving(dev_priv, pipe) == state, 100))
DRM_ERROR("pipe %c scanline %s wait timed out\n",
pipe_name(pipe), onoff(state));
}
static void intel_wait_for_pipe_scanline_stopped(struct intel_crtc *crtc)
{
wait_for_pipe_scanline_moving(crtc, false);
}
static void intel_wait_for_pipe_scanline_moving(struct intel_crtc *crtc)
{
wait_for_pipe_scanline_moving(crtc, true);
} }
/* /*
...@@ -1038,7 +1060,6 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc) ...@@ -1038,7 +1060,6 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc)
{ {
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
enum transcoder cpu_transcoder = crtc->config->cpu_transcoder; enum transcoder cpu_transcoder = crtc->config->cpu_transcoder;
enum pipe pipe = crtc->pipe;
if (INTEL_GEN(dev_priv) >= 4) { if (INTEL_GEN(dev_priv) >= 4) {
i915_reg_t reg = PIPECONF(cpu_transcoder); i915_reg_t reg = PIPECONF(cpu_transcoder);
...@@ -1049,9 +1070,7 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc) ...@@ -1049,9 +1070,7 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc)
100)) 100))
WARN(1, "pipe_off wait timed out\n"); WARN(1, "pipe_off wait timed out\n");
} else { } else {
/* Wait for the display line to settle */ intel_wait_for_pipe_scanline_stopped(crtc);
if (wait_for(pipe_dsl_stopped(dev_priv, pipe), 100))
WARN(1, "pipe_off wait timed out\n");
} }
} }
...@@ -1936,15 +1955,14 @@ static void intel_enable_pipe(struct intel_crtc *crtc) ...@@ -1936,15 +1955,14 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
POSTING_READ(reg); POSTING_READ(reg);
/* /*
* Until the pipe starts DSL will read as 0, which would cause * Until the pipe starts PIPEDSL reads will return a stale value,
* an apparent vblank timestamp jump, which messes up also the * which causes an apparent vblank timestamp jump when PIPEDSL
* frame count when it's derived from the timestamps. So let's * resets to its proper value. That also messes up the frame count
* wait for the pipe to start properly before we call * when it's derived from the timestamps. So let's wait for the
* drm_crtc_vblank_on() * pipe to start properly before we call drm_crtc_vblank_on()
*/ */
if (dev->max_vblank_count == 0 && if (dev->max_vblank_count == 0)
wait_for(intel_get_crtc_scanline(crtc) != crtc->scanline_offset, 50)) intel_wait_for_pipe_scanline_moving(crtc);
DRM_ERROR("pipe %c didn't start\n", pipe_name(pipe));
} }
/** /**
...@@ -14643,6 +14661,8 @@ void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) ...@@ -14643,6 +14661,8 @@ void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
{ {
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
DRM_DEBUG_KMS("disabling pipe %c due to force quirk\n", DRM_DEBUG_KMS("disabling pipe %c due to force quirk\n",
pipe_name(pipe)); pipe_name(pipe));
...@@ -14652,8 +14672,7 @@ void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) ...@@ -14652,8 +14672,7 @@ void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
I915_WRITE(PIPECONF(pipe), 0); I915_WRITE(PIPECONF(pipe), 0);
POSTING_READ(PIPECONF(pipe)); POSTING_READ(PIPECONF(pipe));
if (wait_for(pipe_dsl_stopped(dev_priv, pipe), 100)) intel_wait_for_pipe_scanline_stopped(crtc);
DRM_ERROR("pipe %c off wait timed out\n", pipe_name(pipe));
I915_WRITE(DPLL(pipe), DPLL_VGA_MODE_DIS); I915_WRITE(DPLL(pipe), DPLL_VGA_MODE_DIS);
POSTING_READ(DPLL(pipe)); POSTING_READ(DPLL(pipe));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment