diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_display.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 29 |
1 files changed, 26 insertions, 3 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 7ea647b84733..d680608f6f5b 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; @@ -1774,5 +1775,27 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int 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; } |