@@ -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
@@ -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
@@ -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;
}
/**
@@ -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