diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 7f33767..4357f8f 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4377,6 +4377,10 @@ int evergreen_rlc_resume(struct radeon_device *rdev) u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc) { + u32 adl = RREG32(0x6ecc + crtc_offsets[crtc]); + u32 fvr = RREG32(CRTC_STATUS_FRAME_COUNT + 4 + crtc_offsets[crtc]); + DRM_DEBUG_VBL("crtc %d: adl0: %d adl8: %d adls: %d Fd %d Vd %d\n", crtc, adl & (1 << 0), adl & (1 << 8), (adl >> 16) & 0xf, fvr & 0xffff, fvr >> 16); + if (crtc >= rdev->num_crtc) return 0; else diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index a8d9927..76b3449 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1910,11 +1910,22 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, vbl_start = mode->crtc_vdisplay; vbl_end = 0; } - + DRM_DEBUG_VBL("crtc %d: vpos %d : vs %d , ve %d, vs2 %d, vt %d\n", pipe, *vpos, vbl_start, vbl_end, mode->crtc_vdisplay, mode->crtc_vtotal); /* Test scanout position against vblank region. */ if ((*vpos < vbl_start) && (*vpos >= vbl_end)) in_vbl = false; + /* In vblank? */ + if (in_vbl) + ret |= DRM_SCANOUTPOS_IN_VBLANK; + + /* Called from driver internal vblank counter query code? */ + if (flags & 0x80000000) { + /* Caller wants distance from vbl_start */ + *vpos -= vbl_start; + return ret; + } + /* Check if inside vblank area and apply corrective offsets: * vpos will then be >=0 in video scanout area, but negative * within vblank area, counting down the number of lines until @@ -1930,10 +1941,6 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, /* Correct for shifted end of vbl at vbl_end. */ *vpos = *vpos - vbl_end; - /* In vblank? */ - if (in_vbl) - ret |= DRM_SCANOUTPOS_IN_VBLANK; - /* 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 diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 0ec6fcc..885f934 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -755,6 +755,8 @@ void radeon_driver_preclose_kms(struct drm_device *dev, */ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) { + int vpos, hpos, stat, bump_line; + u32 count; struct radeon_device *rdev = dev->dev_private; if (crtc < 0 || crtc >= rdev->num_crtc) { @@ -762,7 +764,57 @@ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) return -EINVAL; } - return radeon_get_vblank_counter(rdev, crtc); + /* If called from hw vblank irq then we need hw quirk treatment. + * The hw fires its vblank irq typically 2-3 scanlines before + * the vblank, triggering sampling of our hw counter by DRM + * core before it increments at start of vblank, which is bad + * for vblank counting and timestamping, causing frequent + * off-by-one errors. Try to cook the hw count here to make it + * appear to the caller as if it was sampled after it incremented. + */ + if (rdev->mode_info.crtcs[crtc]) { + do { + count = radeon_get_vblank_counter(rdev, crtc); + /* Ask function to return vpos as distance to start of + * vblank, instead of regular vertical scanout pos. + */ + stat = radeon_get_crtc_scanoutpos( + dev, crtc, + 0x80000000, &vpos, &hpos, NULL, NULL, + &rdev->mode_info.crtcs[crtc]->base.hwmode); + } while (count != radeon_get_vblank_counter(rdev, crtc)); + + if (((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) != + (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE))) { + DRM_DEBUG_VBL("Query failed! stat %d\n", stat); + } + else { + /* Bump counter if we are at >= leading edge of vblank, + * but before wraparound to zero where the hw counter + * increments. Bump counter already 10 scanlines before + * vblank if we are called from vblank irq handler, as + * that one fires a few scanlines before vblank but + * should get a count as if it was called inside vblank. + */ + bump_line = in_irq() ? -10 : 0; + if (vpos >= bump_line) { + count++; + DRM_DEBUG_VBL("crtc %d: vpos %d >= bump_line %d -> Bumped.\n", + crtc, vpos, bump_line); + } + else { + DRM_DEBUG_VBL("crtc %d: vpos %d\n", + crtc, vpos); + } + } + } + else { + /* Fallback to use value as is. */ + count = radeon_get_vblank_counter(rdev, crtc); + DRM_DEBUG_VBL("NULL mode info!\n"); + } + + return count; } /** diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 97a9048..460ca2f 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -835,6 +835,9 @@ int rs600_irq_process(struct radeon_device *rdev) u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc) { + u32 fvr = RREG32((crtc == 0) ? (R_0060A4_D1CRTC_STATUS_FRAME_COUNT + 4) : (R_0068A4_D2CRTC_STATUS_FRAME_COUNT + 4)); + DRM_DEBUG_VBL("crtc %d: Fd%d Vd %d\n", crtc, fvr & 0xffff, fvr >> 16); + if (crtc == 0) return RREG32(R_0060A4_D1CRTC_STATUS_FRAME_COUNT); else