From 545cdd5510205f01cd9604e23385bac468d45c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:16:30 +0300 Subject: [PATCH 01/14] drm: Pass the display mode to drm_calc_timestamping_constants() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't really use hwmode anymore in i915, so eliminating its use from the core code seems prudent. Just pass the appropriate mode to drm_calc_timestamping_constants(). Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_crtc_helper.c | 2 +- drivers/gpu/drm/drm_irq.c | 17 +++++++++-------- drivers/gpu/drm/i915/intel_display.c | 3 ++- include/drm/drmP.h | 3 ++- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 01361aba033b4..245fe4fa9c9ed 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -536,7 +536,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, * are later needed by vblank and swap-completion * timestamping. They are derived from true hwmode. */ - drm_calc_timestamping_constants(crtc); + drm_calc_timestamping_constants(crtc, &crtc->hwmode); /* FIXME: add subpixel order */ done: diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index e7de2da572342..3837132086f0d 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -445,20 +445,22 @@ int drm_control(struct drm_device *dev, void *data, * adjustments into account. * * @crtc drm_crtc whose timestamp constants should be updated. + * @mode display mode containing the scanout timings * */ -void drm_calc_timestamping_constants(struct drm_crtc *crtc) +void drm_calc_timestamping_constants(struct drm_crtc *crtc, + const struct drm_display_mode *mode) { s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; u64 dotclock; /* Dot clock in Hz: */ - dotclock = (u64) crtc->hwmode.clock * 1000; + dotclock = (u64) mode->clock * 1000; /* Fields of interlaced scanout modes are only half a frame duration. * Double the dotclock to get half the frame-/line-/pixelduration. */ - if (crtc->hwmode.flags & DRM_MODE_FLAG_INTERLACE) + if (mode->flags & DRM_MODE_FLAG_INTERLACE) dotclock *= 2; /* Valid dotclock? */ @@ -469,10 +471,9 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) * nanoseconds: */ pixeldur_ns = (s64) div64_u64(1000000000, dotclock); - linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * + linedur_ns = (s64) div64_u64(((u64) mode->crtc_htotal * 1000000000), dotclock); - frame_size = crtc->hwmode.crtc_htotal * - crtc->hwmode.crtc_vtotal; + frame_size = mode->crtc_htotal * mode->crtc_vtotal; framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, dotclock); } else @@ -484,8 +485,8 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) crtc->framedur_ns = framedur_ns; DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n", - crtc->base.id, crtc->hwmode.crtc_htotal, - crtc->hwmode.crtc_vtotal, crtc->hwmode.crtc_vdisplay); + crtc->base.id, mode->crtc_htotal, + mode->crtc_vtotal, mode->crtc_vdisplay); DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n", crtc->base.id, (int) dotclock/1000, (int) framedur_ns, (int) linedur_ns, (int) pixeldur_ns); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e77d4b8856a79..920fcff8818c4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9693,7 +9693,8 @@ static int __intel_set_mode(struct drm_crtc *crtc, * are later needed by vblank and swap-completion * timestamping. They are derived from true hwmode. */ - drm_calc_timestamping_constants(crtc); + drm_calc_timestamping_constants(crtc, + &pipe_config->adjusted_mode); } /* FIXME: add subpixel order */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 63eab2b72ee7a..46bf8ae7e3024 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1402,7 +1402,8 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, struct timeval *vblank_time, unsigned flags, struct drm_crtc *refcrtc); -extern void drm_calc_timestamping_constants(struct drm_crtc *crtc); +extern void drm_calc_timestamping_constants(struct drm_crtc *crtc, + const struct drm_display_mode *mode); extern bool drm_mode_parse_command_line_for_connector(const char *mode_option, From 7da903ef04851aba81e4ddabf65c15fb71b7ce47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:57:31 +0300 Subject: [PATCH 02/14] drm: Pass the display mode to drm_calc_vbltimestamp_from_scanoutpos() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using crtc->hwmode, just pass the relevant mode to drm_calc_vbltimestamp_from_scanoutpos(). This removes the last hwmode usage from core drm. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 6 +++--- drivers/gpu/drm/i915/i915_irq.c | 3 ++- drivers/gpu/drm/radeon/radeon_kms.c | 2 +- include/drm/drmP.h | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3837132086f0d..db93b07723dd2 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -522,6 +522,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * 0 = Default. * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. * @refcrtc: drm_crtc* of crtc which defines scanout timing. + * @mode: mode which defines the scanout timings * * Returns negative value on error, failure or if not supported in current * video mode: @@ -541,11 +542,11 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, int *max_error, struct timeval *vblank_time, unsigned flags, - struct drm_crtc *refcrtc) + const struct drm_crtc *refcrtc, + const struct drm_display_mode *mode) { ktime_t stime, etime, mono_time_offset; struct timeval tv_etime; - struct drm_display_mode *mode; int vbl_status, vtotal, vdisplay; int vpos, hpos, i; s64 framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; @@ -562,7 +563,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, return -EIO; } - mode = &refcrtc->hwmode; vtotal = mode->crtc_vtotal; vdisplay = mode->crtc_vdisplay; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6d11e253218a7..b6e4a762806be 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -809,7 +809,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error, vblank_time, flags, - crtc); + crtc, + &to_intel_crtc(crtc)->config.adjusted_mode); } static bool intel_hpd_irq_event(struct drm_device *dev, diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 5bf50cec017ef..5444948cf01da 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -712,7 +712,7 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, vblank_time, flags, - drmcrtc); + drmcrtc, &drmcrtc->hwmode); } #define KMS_INVALID_IOCTL(name) \ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 46bf8ae7e3024..3f57c77acef83 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1401,7 +1401,8 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, int *max_error, struct timeval *vblank_time, unsigned flags, - struct drm_crtc *refcrtc); + const struct drm_crtc *refcrtc, + const struct drm_display_mode *mode); extern void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); From 4b4b9238a30e308b85be2249eadad74ca5d02178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:59:30 +0300 Subject: [PATCH 03/14] drm/i915: Kill hwmode save/restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm core no longer uses crtc->hwmode, and neither does i915, so we can totally ignore it in i915. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/i915/intel_display.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 920fcff8818c4..6efac680d01a9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9597,21 +9597,19 @@ static int __intel_set_mode(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_display_mode *saved_mode, *saved_hwmode; + struct drm_display_mode *saved_mode; struct intel_crtc_config *pipe_config = NULL; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; int ret = 0; - saved_mode = kcalloc(2, sizeof(*saved_mode), GFP_KERNEL); + saved_mode = kmalloc(sizeof(*saved_mode), GFP_KERNEL); if (!saved_mode) return -ENOMEM; - saved_hwmode = saved_mode + 1; intel_modeset_affected_pipes(crtc, &modeset_pipes, &prepare_pipes, &disable_pipes); - *saved_hwmode = crtc->hwmode; *saved_mode = crtc->mode; /* Hack: Because we don't (yet) support global modeset on multiple @@ -9686,9 +9684,6 @@ static int __intel_set_mode(struct drm_crtc *crtc, dev_priv->display.crtc_enable(&intel_crtc->base); if (modeset_pipes) { - /* Store real post-adjustment hardware mode. */ - crtc->hwmode = pipe_config->adjusted_mode; - /* Calculate and store various constants which * are later needed by vblank and swap-completion * timestamping. They are derived from true hwmode. @@ -9699,10 +9694,8 @@ static int __intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: - if (ret && crtc->enabled) { - crtc->hwmode = *saved_hwmode; + if (ret && crtc->enabled) crtc->mode = *saved_mode; - } out: kfree(pipe_config); From c326c0a9c98c06346dd7e37815c651e9382bb7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 28 Oct 2013 12:53:41 +0200 Subject: [PATCH 04/14] drm/i915: Call drm_calc_timestamping_constants() earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the pixel/line/frame duration information when we switch to the new pipe config. This will keep the timestamping constants in better sync with the real hardware state. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/i915/intel_display.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6efac680d01a9..14b024becb913 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9660,6 +9660,14 @@ static int __intel_set_mode(struct drm_crtc *crtc, /* mode_set/enable/disable functions rely on a correct pipe * config. */ to_intel_crtc(crtc)->config = *pipe_config; + + /* + * Calculate and store various constants which + * are later needed by vblank and swap-completion + * timestamping. They are derived from true hwmode. + */ + drm_calc_timestamping_constants(crtc, + &pipe_config->adjusted_mode); } /* Only after disabling all output pipelines that will be changed can we @@ -9683,15 +9691,6 @@ static int __intel_set_mode(struct drm_crtc *crtc, for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) dev_priv->display.crtc_enable(&intel_crtc->base); - if (modeset_pipes) { - /* Calculate and store various constants which - * are later needed by vblank and swap-completion - * timestamping. They are derived from true hwmode. - */ - drm_calc_timestamping_constants(crtc, - &pipe_config->adjusted_mode); - } - /* FIXME: add subpixel order */ done: if (ret && crtc->enabled) From 21b21560e9f7be6c3f415dfd7a840f72a250255c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:22:52 +0300 Subject: [PATCH 05/14] drm: Improve drm_calc_timestamping_constants() documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the long blurp to into the body of the comment, leaving only a short summary line at the top. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index db93b07723dd2..03f6a149b8874 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -436,17 +436,16 @@ int drm_control(struct drm_device *dev, void *data, } /** - * drm_calc_timestamping_constants - Calculate and - * store various constants which are later needed by - * vblank and swap-completion timestamping, e.g, by - * drm_calc_vbltimestamp_from_scanoutpos(). - * They are derived from crtc's true scanout timing, - * so they take things like panel scaling or other - * adjustments into account. + * drm_calc_timestamping_constants - Calculate vblank timestamp constants * * @crtc drm_crtc whose timestamp constants should be updated. * @mode display mode containing the scanout timings * + * Calculate and store various constants which are later + * needed by vblank and swap-completion timestamping, e.g, + * by drm_calc_vbltimestamp_from_scanoutpos(). They are + * derived from crtc's true scanout timing, so they take + * things like panel scaling or other adjustments into account. */ void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode) From 0dae35a3886c883bf993c8776dafd8a3bb2fef44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:11:01 +0300 Subject: [PATCH 06/14] drm: Simplify the math in drm_calc_timestamping_constants() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_calc_timestamping_constants() makes the math more complex than necessary. - multipying the dotclock by 1000 is pointless, just makes all the numbers bigger - div64_u64() is also pointless, div_u64 is enough - pixeldur_ns doesn't need any 64bit math Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 03f6a149b8874..3283669a9083e 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -451,10 +451,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode) { s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; - u64 dotclock; - - /* Dot clock in Hz: */ - dotclock = (u64) mode->clock * 1000; + int dotclock = mode->clock; /* Fields of interlaced scanout modes are only half a frame duration. * Double the dotclock to get half the frame-/line-/pixelduration. @@ -464,17 +461,16 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, /* Valid dotclock? */ if (dotclock > 0) { - int frame_size; - /* Convert scanline length in pixels and video dot clock to - * line duration, frame duration and pixel duration in - * nanoseconds: + int frame_size = mode->crtc_htotal * mode->crtc_vtotal; + + /* + * Convert scanline length in pixels and video + * dot clock to line duration, frame duration + * and pixel duration in nanoseconds: */ - pixeldur_ns = (s64) div64_u64(1000000000, dotclock); - linedur_ns = (s64) div64_u64(((u64) mode->crtc_htotal * - 1000000000), dotclock); - frame_size = mode->crtc_htotal * mode->crtc_vtotal; - framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, - dotclock); + pixeldur_ns = 1000000 / dotclock; + linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); + framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); } else DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", crtc->base.id); @@ -487,7 +483,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, crtc->base.id, mode->crtc_htotal, mode->crtc_vtotal, mode->crtc_vdisplay); DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n", - crtc->base.id, (int) dotclock/1000, (int) framedur_ns, + crtc->base.id, dotclock, (int) framedur_ns, (int) linedur_ns, (int) pixeldur_ns); } EXPORT_SYMBOL(drm_calc_timestamping_constants); From 265d09aa8254c0a8fc9c2d7ecb0b440e92961e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sun, 27 Oct 2013 21:20:10 +0200 Subject: [PATCH 07/14] drm/radeon: Populate crtc_clock in radeon_atom_get_tv_timings() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crtc_clock is now supposed to be the actual pixel clock corresponding to the other crtc_ timing values. Populate crtc_clock appropriately in radeon_atom_get_tv_timings(). This was the only obvious place where we frob with the crtc_ timigns directly instead of calling drm_mode_set_crtcinfo() which would also update crtc_clock. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/radeon/radeon_atombios.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 80a56ad40c52d..2f5925d3662ab 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1792,7 +1792,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, if (misc & ATOM_DOUBLE_CLOCK_MODE) mode->flags |= DRM_MODE_FLAG_DBLSCAN; - mode->clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; + mode->crtc_clock = mode->clock = + le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; if (index == 1) { /* PAL timings appear to have wrong values for totals */ @@ -1835,7 +1836,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, if (misc & ATOM_DOUBLE_CLOCK_MODE) mode->flags |= DRM_MODE_FLAG_DBLSCAN; - mode->clock = le16_to_cpu(dtd_timings->usPixClk) * 10; + mode->crtc_clock = mode->clock = + le16_to_cpu(dtd_timings->usPixClk) * 10; break; } return true; From 77666b72260da3a5290471de77e2d2261244db83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sun, 27 Oct 2013 21:22:58 +0200 Subject: [PATCH 08/14] drm: Use crtc_clock in drm_calc_timestamping_constants() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_calc_timestamping_constants() computes the pixel/line/frame durations based on the crtc_ timing values. The corresponding pixel clock is in mode->crtc_clock, so we need to use that instead of mode->clock. This should fix drm_calc_timestamping_constants() for frame packing stereo modes. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3283669a9083e..52a234c4160e8 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -451,7 +451,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode) { s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; - int dotclock = mode->clock; + int dotclock = mode->crtc_clock; /* Fields of interlaced scanout modes are only half a frame duration. * Double the dotclock to get half the frame-/line-/pixelduration. From 3c184f69917d16e295934fcbe0803355b544df64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 26 Oct 2013 17:38:52 +0300 Subject: [PATCH 09/14] drm: Change {pixel,line,frame}dur_ns from s64 to int MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using s64 for the timestamping constants is wasteful. Signed 32bit integers get us a range of over +-2 seconds. Presuming that no-one wants to a vrefresh rate less than 0.5, we can switch to using int for the timestamping constants. We save a few bytes in drm_crtc and avoid a bunch of 64bit math. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 18 +++++++++--------- include/drm/drm_crtc.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 52a234c4160e8..91e8b4c55ea0d 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -450,7 +450,7 @@ int drm_control(struct drm_device *dev, void *data, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode) { - s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; + int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; int dotclock = mode->crtc_clock; /* Fields of interlaced scanout modes are only half a frame duration. @@ -483,8 +483,8 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, crtc->base.id, mode->crtc_htotal, mode->crtc_vtotal, mode->crtc_vdisplay); DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n", - crtc->base.id, dotclock, (int) framedur_ns, - (int) linedur_ns, (int) pixeldur_ns); + crtc->base.id, dotclock, framedur_ns, + linedur_ns, pixeldur_ns); } EXPORT_SYMBOL(drm_calc_timestamping_constants); @@ -544,7 +544,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, struct timeval tv_etime; int vbl_status, vtotal, vdisplay; int vpos, hpos, i; - s64 framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; + int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; bool invbl; if (crtc < 0 || crtc >= dev->num_crtcs) { @@ -607,18 +607,18 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); /* Accept result with < max_error nsecs timing uncertainty. */ - if (duration_ns <= (s64) *max_error) + if (duration_ns <= *max_error) break; } /* Noisy system timing? */ if (i == DRM_TIMESTAMP_MAXRETRIES) { DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n", - crtc, (int) duration_ns/1000, *max_error/1000, i); + crtc, duration_ns/1000, *max_error/1000, i); } /* Return upper bound of timestamp precision error. */ - *max_error = (int) duration_ns; + *max_error = duration_ns; /* Check if in vblank area: * vpos is >=0 in video scanout area, but negative @@ -631,7 +631,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, * since start of scanout at first display scanline. delta_ns * can be negative if start of scanout hasn't happened yet. */ - delta_ns = (s64) vpos * linedur_ns + (s64) hpos * pixeldur_ns; + delta_ns = vpos * linedur_ns + hpos * pixeldur_ns; /* Is vpos outside nominal vblank area, but less than * 1/100 of a frame height away from start of vblank? @@ -669,7 +669,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, crtc, (int)vbl_status, hpos, vpos, (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, - (int)duration_ns/1000, i); + duration_ns/1000, i); vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD; if (invbl) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f32c5cd51f412..a158ec0f97abc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -447,7 +447,7 @@ struct drm_crtc { uint16_t *gamma_store; /* Constants needed for precise vblank and swap timestamping. */ - s64 framedur_ns, linedur_ns, pixeldur_ns; + int framedur_ns, linedur_ns, pixeldur_ns; /* if you are using the helper */ void *helper_private; From d31faf65b8300fa120f4b31d90270fbac36fc779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 28 Oct 2013 16:31:41 +0200 Subject: [PATCH 10/14] drm/i915: Fix scanoutpos calculations for interlaced modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanline counter counts lines in the current field, not the entire frame. But the crtc_ timings are the values for the entire frame. Divide the vertical timings by 2 to make them match the scanline counter. The rounding was carefully chosen to make it do the right thing wrt. the observed scanline counter and ISR vblank bit behaviour. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/i915/i915_irq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b6e4a762806be..eacbb2f9e0912 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -698,6 +698,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, vbl_start = mode->crtc_vblank_start; vbl_end = mode->crtc_vblank_end; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vbl_start = DIV_ROUND_UP(vbl_start, 2); + vbl_end /= 2; + vtotal /= 2; + } + ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; /* From c0ae24c17ed0864b42d449733460140322f275e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 28 Oct 2013 19:53:25 +0200 Subject: [PATCH 11/14] drm: Fix vblank timestamping constants for interlaced modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're currently miscalculating the line and pixel durations for interlaced modes. crtc_htotal and crtc_vtotal are the full frame timings, and so is crtc_clock, so we can compute the line and pixel durations from those w/o any extra adjustments. But we actually want framedur_ns to be the field, not frame, duration, so we must divide it by two. This should make the scanout based vblank timestamp corrections work correctly with interlaced modes, at least for i915. It all depends whether we keep the field or frame timings in the display mode crtc_ timings. v2: Preserve halve->half typo fix that happened in the meantine Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 91e8b4c55ea0d..55239d285f251 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -453,12 +453,6 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; int dotclock = mode->crtc_clock; - /* Fields of interlaced scanout modes are only half a frame duration. - * Double the dotclock to get half the frame-/line-/pixelduration. - */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - dotclock *= 2; - /* Valid dotclock? */ if (dotclock > 0) { int frame_size = mode->crtc_htotal * mode->crtc_vtotal; @@ -471,6 +465,12 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, pixeldur_ns = 1000000 / dotclock; linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); + + /* + * Fields of interlaced scanout modes are only half a frame duration. + */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + framedur_ns /= 2; } else DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", crtc->base.id); From abca9e45449876ca4e66f7e31c850753cde344a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 28 Oct 2013 20:50:48 +0200 Subject: [PATCH 12/14] drm: Pass 'flags' from the caller to .get_scanout_position() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparation for moving the early vblank IRQ logic into radeon_get_crtc_scanoutpos(). v2: Fix radeon_drv.c compile warning (Mario) Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 3 ++- drivers/gpu/drm/radeon/radeon_display.c | 7 ++++--- drivers/gpu/drm/radeon/radeon_drv.c | 1 + drivers/gpu/drm/radeon/radeon_mode.h | 1 + drivers/gpu/drm/radeon/radeon_pm.c | 2 +- include/drm/drmP.h | 2 ++ 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 55239d285f251..e55619f496711 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -586,7 +586,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, * Get vertical and horizontal scanout position vpos, hpos, * and bounding timestamps stime, etime, pre/post query. */ - vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, + vbl_status = dev->driver->get_scanout_position(dev, crtc, flags, &vpos, &hpos, &stime, &etime); /* diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index eacbb2f9e0912..fb4801c7d9e49 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -675,7 +675,8 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) } static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, - int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) + unsigned int flags, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 7ea647b84733a..567215be4728d 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -306,7 +306,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) * to complete in this vblank? */ if (update_pending && - (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, + (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 0, &vpos, &hpos, NULL, NULL)) && ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) || (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) { @@ -1610,6 +1610,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, * * \param dev Device to query. * \param crtc Crtc to query. + * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). * \param *vpos Location where vertical scanout position should be stored. * \param *hpos Location where horizontal scanout position should go. * \param *stime Target location for timestamp taken immediately before @@ -1631,8 +1632,8 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, * unknown small number of scanlines wrt. real scanout position. * */ -int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime) +int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) { u32 stat_crtc = 0, vbl = 0, position = 0; int vbl_start, vbl_end, vtotal, ret = 0; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 67fadcf4590f0..1235a78fbba15 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -109,6 +109,7 @@ int radeon_gem_object_open(struct drm_gem_object *obj, void radeon_gem_object_close(struct drm_gem_object *obj, struct drm_file *file_priv); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, + unsigned int flags, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime); extern const struct drm_ioctl_desc radeon_ioctls_kms[]; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 28bba631b80c6..b8e37a6d6f4ed 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -800,6 +800,7 @@ extern int radeon_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, + unsigned int flags, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 0b24c4c7dcf90..eee1b6831b068 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1482,7 +1482,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev) */ for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { if (rdev->pm.active_crtcs & (1 << crtc)) { - vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos, NULL, NULL); + vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL); if ((vbl_status & DRM_SCANOUTPOS_VALID) && !(vbl_status & DRM_SCANOUTPOS_INVBL)) in_vbl = false; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3f57c77acef83..04086c5be930e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -845,6 +845,7 @@ struct drm_driver { * * \param dev DRM device. * \param crtc Id of the crtc to query. + * \param flags Flags from the caller (DRM_CALLED_FROM_VBLIRQ or 0). * \param *vpos Target location for current vertical scanout position. * \param *hpos Target location for current horizontal scanout position. * \param *stime Target location for timestamp taken immediately before @@ -867,6 +868,7 @@ struct drm_driver { * */ int (*get_scanout_position) (struct drm_device *dev, int crtc, + unsigned int flags, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime); From 8072bfa6045a264d3913102a35fab125b06603a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 28 Oct 2013 21:22:52 +0200 Subject: [PATCH 13/14] drm/radeon: Move the early vblank IRQ fixup to radeon_get_crtc_scanoutpos() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit i915 doesn't need this kludge for most platforms. Although we do appear to need something similar on certain platforms, but we can be more accurate when we apply the adjustment since we know exactly why the scanline counter doesn't always quite match the vblank status. Also the current code doesn't handle interlaced modes correctly, and we already deal with interlaced modes in i915 code. So let's just move the current code to radeon_get_crtc_scanoutpos() since that's why it was added. For i915 we'll add a more finely targeted variant. v2: Fix vpos vs. *vpos bug (Mario) Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_irq.c | 25 ++----------------------- drivers/gpu/drm/radeon/radeon_display.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index e55619f496711..c2676b5908d9f 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -542,7 +542,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, { ktime_t stime, etime, mono_time_offset; struct timeval tv_etime; - int vbl_status, vtotal, vdisplay; + int vbl_status; int vpos, hpos, i; int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; bool invbl; @@ -558,9 +558,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, return -EIO; } - vtotal = mode->crtc_vtotal; - vdisplay = mode->crtc_vdisplay; - /* Durations of frames, lines, pixels in nanoseconds. */ framedur_ns = refcrtc->framedur_ns; linedur_ns = refcrtc->linedur_ns; @@ -569,7 +566,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, /* If mode timing undefined, just return as no-op: * Happens during initial modesetting of a crtc. */ - if (vtotal <= 0 || vdisplay <= 0 || framedur_ns == 0) { + if (framedur_ns == 0) { DRM_DEBUG("crtc %d: Noop due to uninitialized mode.\n", crtc); return -EAGAIN; } @@ -633,24 +630,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, */ delta_ns = vpos * linedur_ns + hpos * pixeldur_ns; - /* Is vpos outside nominal vblank area, but less than - * 1/100 of a frame height away from start of vblank? - * If so, assume this isn't a massively delayed vblank - * interrupt, but a vblank interrupt that fired a few - * microseconds before true start of vblank. Compensate - * by adding a full frame duration to the final timestamp. - * Happens, e.g., on ATI R500, R600. - * - * We only do this if DRM_CALLED_FROM_VBLIRQ. - */ - if ((flags & DRM_CALLED_FROM_VBLIRQ) && !invbl && - ((vdisplay - vpos) < vtotal / 100)) { - delta_ns = delta_ns - framedur_ns; - - /* Signal this correction as "applied". */ - vbl_status |= 0x8; - } - if (!drm_timestamp_monotonic) etime = ktime_sub(etime, mono_time_offset); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 567215be4728d..d680608f6f5bc 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1775,5 +1775,27 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl if (in_vbl) ret |= DRM_SCANOUTPOS_INVBL; + /* Is vpos outside nominal vblank area, but less than + * 1/100 of a frame height away from start of vblank? + * If so, assume this isn't a massively delayed vblank + * interrupt, but a vblank interrupt that fired a few + * microseconds before true start of vblank. Compensate + * by adding a full frame duration to the final timestamp. + * Happens, e.g., on ATI R500, R600. + * + * We only do this if DRM_CALLED_FROM_VBLIRQ. + */ + if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { + vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; + vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + + if (vbl_start - *vpos < vtotal / 100) { + *vpos -= vtotal; + + /* Signal this correction as "applied". */ + ret |= 0x8; + } + } + return ret; } From 095163bad59bfeed294a81e0d873fa8943e4fa01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 29 Oct 2013 00:04:43 +0200 Subject: [PATCH 14/14] drm/i915: Add a kludge for DSL incrementing too late and ISR not working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On pre-PCH platforms ISR doesn't seem to be an actual ISR, at least as far as display interrupts are concerned. Instead it sort of looks like some ISR bits just directly reflect the corresponding bit from PIPESTAT. The bit appears in the ISR only if the PIPESTAT interrupt is enabled. So in that sense it sort of looks a bit like the south interrupt scheme on PCH platforms. So it goes something a bit like this: PIPESTAT.status & PIPESTAT.enable -> ISR -> IMR -> IIR -> IER -> actual interrupt In any case that means the intel_pipe_in_vblank_locked() doesn't actually work for pre-PCH platforms. As a last resort, add a similar kludge as radeon has that fixes things up if we got called from the vblank interrupt, but the scanline counter value indicates that we're not quite there yet. We know that the scanline counter increments at hsync but is otherwise accurate, so we can limit the kludge to the line just prior to vblank start, instead of the relative distance that radeon uses. Reviewed-by: mario.kleiner.de@gmail.com Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/i915/i915_irq.c | 79 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index fb4801c7d9e49..17d8fcb1b6f7a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__)) #define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__)) -static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) +static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t status; - int reg; - if (IS_VALLEYVIEW(dev)) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = VLV_ISR; - } else if (IS_GEN2(dev)) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = ISR; - } else if (INTEL_INFO(dev)->gen < 5) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = ISR; - } else if (INTEL_INFO(dev)->gen < 7) { + if (INTEL_INFO(dev)->gen < 7) { status = pipe == PIPE_A ? DE_PIPEA_VBLANK : DE_PIPEB_VBLANK; - - reg = DEISR; } else { switch (pipe) { default: @@ -664,14 +643,9 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) status = DE_PIPEC_VBLANK_IVB; break; } - - reg = DEISR; } - if (IS_GEN2(dev)) - return __raw_i915_read16(dev_priv, reg) & status; - else - return __raw_i915_read32(dev_priv, reg) & status; + return __raw_i915_read32(dev_priv, DEISR) & status; } static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, @@ -729,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, else position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; - /* - * 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 = intel_pipe_in_vblank_locked(dev, pipe); - if ((in_vbl && position == vbl_start - 1) || - (!in_vbl && position == vbl_end - 1)) - position = (position + 1) % vtotal; + if (HAS_PCH_SPLIT(dev)) { + /* + * 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 = ilk_pipe_in_vblank_locked(dev, pipe); + if ((in_vbl && position == vbl_start - 1) || + (!in_vbl && position == vbl_end - 1)) + position = (position + 1) % vtotal; + } else { + /* + * ISR vblank status bits don't work the way we'd want + * them to work on non-PCH platforms (for + * ilk_pipe_in_vblank_locked()), and there doesn't + * appear any other way to determine if we're currently + * in vblank. + * + * Instead let's assume that we're already in vblank if + * we got called from the vblank interrupt and the + * scanline counter value indicates that we're on the + * line just prior to vblank start. This should result + * in the correct answer, unless the vblank interrupt + * delivery really got delayed for almost exactly one + * full frame/field. + */ + if (flags & DRM_CALLED_FROM_VBLIRQ && + position == vbl_start - 1) { + position = (position + 1) % vtotal; + + /* Signal this correction as "applied". */ + ret |= 0x8; + } + } } else { /* Have access to pixelcount since start of frame. * We can split this into vertical and horizontal