Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6383cf7d authored by Mario Kleiner's avatar Mario Kleiner Committed by Dave Airlie
Browse files

drm/radeon: Add function for display scanout position query.



radeon_get_crtc_scanoutpos() returns the current horizontal
and vertical scanout position of a crtc. It also reports if
the display scanout is currently inside the vblank area.

hpos reports current horizontal pixel scanout position.
vpos reports the current scanned out line as a value >= 0
in active scanout. If the scanout is inside vblank area, it
reports a negative value, the number of scanlines until
end of vblank aka start of active scanout, e.g., -3 ==
"At most 3 scanlines until end of vblank".

This code is derived from radeon_pm_in_vbl(), tested on
R500 and R600.

Signed-off-by: default avatarMario Kleiner <mario.kleiner@tuebingen.mpg.de>
Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent ba032a58
Loading
Loading
Loading
Loading
+153 −0
Original line number Original line Diff line number Diff line
@@ -989,3 +989,156 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
	}
	}
	return true;
	return true;
}
}

/*
 * Retrieve current video scanout position of crtc on a given gpu.
 *
 * \param rdev Device to query.
 * \param crtc Crtc to query.
 * \param *vpos Location where vertical scanout position should be stored.
 * \param *hpos Location where horizontal scanout position should go.
 *
 * Returns vpos as a positive number while in active scanout area.
 * Returns vpos as a negative number inside vblank, counting the number
 * of scanlines to go until end of vblank, e.g., -1 means "one scanline
 * until start of active scanout / end of vblank."
 *
 * \return Flags, or'ed together as follows:
 *
 * RADEON_SCANOUTPOS_VALID = Query successfull.
 * RADEON_SCANOUTPOS_INVBL = Inside vblank.
 * RADEON_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
 * this flag means that returned position may be offset by a constant but
 * unknown small number of scanlines wrt. real scanout position.
 *
 */
int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos)
{
	u32 stat_crtc = 0, vbl = 0, position = 0;
	int vbl_start, vbl_end, vtotal, ret = 0;
	bool in_vbl = true;

	if (ASIC_IS_DCE4(rdev)) {
		if (crtc == 0) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC0_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC0_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 1) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC1_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC1_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 2) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC2_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC2_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 3) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC3_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC3_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 4) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC4_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC4_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 5) {
			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
				     EVERGREEN_CRTC5_REGISTER_OFFSET);
			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
					  EVERGREEN_CRTC5_REGISTER_OFFSET);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
	} else if (ASIC_IS_AVIVO(rdev)) {
		if (crtc == 0) {
			vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
			position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 1) {
			vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
			position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
			ret |= RADEON_SCANOUTPOS_VALID;
		}
	} else {
		/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
		if (crtc == 0) {
			/* Assume vbl_end == 0, get vbl_start from
			 * upper 16 bits.
			 */
			vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) &
				RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
			/* Only retrieve vpos from upper 16 bits, set hpos == 0. */
			position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
			stat_crtc = RREG32(RADEON_CRTC_STATUS);
			if (!(stat_crtc & 1))
				in_vbl = false;

			ret |= RADEON_SCANOUTPOS_VALID;
		}
		if (crtc == 1) {
			vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
				RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
			position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
			stat_crtc = RREG32(RADEON_CRTC2_STATUS);
			if (!(stat_crtc & 1))
				in_vbl = false;

			ret |= RADEON_SCANOUTPOS_VALID;
		}
	}

	/* Decode into vertical and horizontal scanout position. */
	*vpos = position & 0x1fff;
	*hpos = (position >> 16) & 0x1fff;

	/* Valid vblank area boundaries from gpu retrieved? */
	if (vbl > 0) {
		/* Yes: Decode. */
		ret |= RADEON_SCANOUTPOS_ACCURATE;
		vbl_start = vbl & 0x1fff;
		vbl_end = (vbl >> 16) & 0x1fff;
	}
	else {
		/* No: Fake something reasonable which gives at least ok results. */
		vbl_start = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vdisplay;
		vbl_end = 0;
	}

	/* Test scanout position against vblank region. */
	if ((*vpos < vbl_start) && (*vpos >= vbl_end))
		in_vbl = false;

	/* 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
	 * start of scanout.
	 */

	/* Inside "upper part" of vblank area? Apply corrective offset if so: */
	if (in_vbl && (*vpos >= vbl_start)) {
		vtotal = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vtotal;
		*vpos = *vpos - vtotal;
	}

	/* Correct for shifted end of vbl at vbl_end. */
	*vpos = *vpos - vbl_end;

	/* In vblank? */
	if (in_vbl)
		ret |= RADEON_SCANOUTPOS_INVBL;

	return ret;
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -428,6 +428,11 @@ struct radeon_framebuffer {
	struct drm_gem_object *obj;
	struct drm_gem_object *obj;
};
};


/* radeon_get_crtc_scanoutpos() return flags */
#define RADEON_SCANOUTPOS_VALID        (1 << 0)
#define RADEON_SCANOUTPOS_INVBL        (1 << 1)
#define RADEON_SCANOUTPOS_ACCURATE     (1 << 2)

extern enum radeon_tv_std
extern enum radeon_tv_std
radeon_combios_get_tv_info(struct radeon_device *rdev);
radeon_combios_get_tv_info(struct radeon_device *rdev);
extern enum radeon_tv_std
extern enum radeon_tv_std
@@ -531,6 +536,8 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
				   int x, int y);
				   int x, int y);


extern int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos);

extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
extern struct edid *
extern struct edid *
radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);