From 80715b2f7b97154ad8d791ddf85a386081fcceab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 15 May 2014 20:23:23 +0300 Subject: [PATCH] drm/i915: Fix gen2 and hsw+ scanline counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ä Reviewed-by: "Akash Goel " Reviewed-by: "Sourab Gupta " Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=78997 Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 14 ++++----- drivers/gpu/drm/i915/intel_display.c | 45 +++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 2 ++ 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 087e76b9ee35..304f86a3162c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -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, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d53758723ac4..6db85e28cb66 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -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); } } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 287b89e97456..d7c52b2c546a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -409,6 +409,8 @@ struct intel_crtc { } wm; wait_queue_head_t vbl_wait; + + int scanline_offset; }; struct intel_plane_wm_parameters {