Commit f5a80209 authored by Mario Kleiner's avatar Mario Kleiner Committed by Dave Airlie

drm/kms/radeon: Add support for precise vblank timestamping.

This patch adds new functions for use by the drm core:

.get_vblank_timestamp() provides a precise timestamp
for the end of the most recent (or current) vblank
interval of a given crtc, as needed for the DRI2
implementation of the OML_sync_control extension.

It is a thin wrapper around the drm function
drm_calc_vbltimestamp_from_scanoutpos() which does
almost all the work and is shared across drivers.

.get_scanout_position() provides the current horizontal
and vertical video scanout position and "in vblank"
status of a given crtc, as needed by the drm for use by
drm_calc_vbltimestamp_from_scanoutpos().

The function is also used by the dynamic gpu reclocking
code to determine when it is safe to reclock inside vblank.

For that purpose radeon_pm_in_vbl() is modified to
accomodate a small change in the function prototype of
the radeon_get_crtc_scanoutpos() which is hooked up to
.get_scanout_position().

This code has been tested on AVIVO hardware, a RV530
(ATI Mobility Radeon X1600) in a Intel Core-2 Duo MacBookPro
and some R600 variant (FireGL V7600) in a single cpu
AMD Athlon 64 PC.
Signed-off-by: default avatarMario Kleiner <mario.kleiner@tuebingen.mpg.de>
Reviewed-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 27641c3f
...@@ -1019,7 +1019,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1019,7 +1019,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
/* /*
* Retrieve current video scanout position of crtc on a given gpu. * Retrieve current video scanout position of crtc on a given gpu.
* *
* \param rdev Device to query. * \param dev Device to query.
* \param crtc Crtc to query. * \param crtc Crtc to query.
* \param *vpos Location where vertical scanout position should be stored. * \param *vpos Location where vertical scanout position should be stored.
* \param *hpos Location where horizontal scanout position should go. * \param *hpos Location where horizontal scanout position should go.
...@@ -1031,72 +1031,74 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1031,72 +1031,74 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
* *
* \return Flags, or'ed together as follows: * \return Flags, or'ed together as follows:
* *
* RADEON_SCANOUTPOS_VALID = Query successfull. * DRM_SCANOUTPOS_VALID = Query successfull.
* RADEON_SCANOUTPOS_INVBL = Inside vblank. * DRM_SCANOUTPOS_INVBL = Inside vblank.
* RADEON_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
* this flag means that returned position may be offset by a constant but * this flag means that returned position may be offset by a constant but
* unknown small number of scanlines wrt. real scanout position. * unknown small number of scanlines wrt. real scanout position.
* *
*/ */
int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos) int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos)
{ {
u32 stat_crtc = 0, vbl = 0, position = 0; u32 stat_crtc = 0, vbl = 0, position = 0;
int vbl_start, vbl_end, vtotal, ret = 0; int vbl_start, vbl_end, vtotal, ret = 0;
bool in_vbl = true; bool in_vbl = true;
struct radeon_device *rdev = dev->dev_private;
if (ASIC_IS_DCE4(rdev)) { if (ASIC_IS_DCE4(rdev)) {
if (crtc == 0) { if (crtc == 0) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC0_REGISTER_OFFSET); EVERGREEN_CRTC0_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC0_REGISTER_OFFSET); EVERGREEN_CRTC0_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 1) { if (crtc == 1) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC1_REGISTER_OFFSET); EVERGREEN_CRTC1_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC1_REGISTER_OFFSET); EVERGREEN_CRTC1_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 2) { if (crtc == 2) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC2_REGISTER_OFFSET); EVERGREEN_CRTC2_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC2_REGISTER_OFFSET); EVERGREEN_CRTC2_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 3) { if (crtc == 3) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC3_REGISTER_OFFSET); EVERGREEN_CRTC3_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC3_REGISTER_OFFSET); EVERGREEN_CRTC3_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 4) { if (crtc == 4) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC4_REGISTER_OFFSET); EVERGREEN_CRTC4_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC4_REGISTER_OFFSET); EVERGREEN_CRTC4_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 5) { if (crtc == 5) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC5_REGISTER_OFFSET); EVERGREEN_CRTC5_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC5_REGISTER_OFFSET); EVERGREEN_CRTC5_REGISTER_OFFSET);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
} else if (ASIC_IS_AVIVO(rdev)) { } else if (ASIC_IS_AVIVO(rdev)) {
if (crtc == 0) { if (crtc == 0) {
vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END); vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
position = RREG32(AVIVO_D1CRTC_STATUS_POSITION); position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 1) { if (crtc == 1) {
vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END); vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
position = RREG32(AVIVO_D2CRTC_STATUS_POSITION); position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
} else { } else {
/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
...@@ -1112,7 +1114,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, ...@@ -1112,7 +1114,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos,
if (!(stat_crtc & 1)) if (!(stat_crtc & 1))
in_vbl = false; in_vbl = false;
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
if (crtc == 1) { if (crtc == 1) {
vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) & vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
...@@ -1122,7 +1124,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, ...@@ -1122,7 +1124,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos,
if (!(stat_crtc & 1)) if (!(stat_crtc & 1))
in_vbl = false; in_vbl = false;
ret |= RADEON_SCANOUTPOS_VALID; ret |= DRM_SCANOUTPOS_VALID;
} }
} }
...@@ -1133,13 +1135,13 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, ...@@ -1133,13 +1135,13 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos,
/* Valid vblank area boundaries from gpu retrieved? */ /* Valid vblank area boundaries from gpu retrieved? */
if (vbl > 0) { if (vbl > 0) {
/* Yes: Decode. */ /* Yes: Decode. */
ret |= RADEON_SCANOUTPOS_ACCURATE; ret |= DRM_SCANOUTPOS_ACCURATE;
vbl_start = vbl & 0x1fff; vbl_start = vbl & 0x1fff;
vbl_end = (vbl >> 16) & 0x1fff; vbl_end = (vbl >> 16) & 0x1fff;
} }
else { else {
/* No: Fake something reasonable which gives at least ok results. */ /* No: Fake something reasonable which gives at least ok results. */
vbl_start = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vdisplay; vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay;
vbl_end = 0; vbl_end = 0;
} }
...@@ -1155,7 +1157,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, ...@@ -1155,7 +1157,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos,
/* Inside "upper part" of vblank area? Apply corrective offset if so: */ /* Inside "upper part" of vblank area? Apply corrective offset if so: */
if (in_vbl && (*vpos >= vbl_start)) { if (in_vbl && (*vpos >= vbl_start)) {
vtotal = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vtotal; vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal;
*vpos = *vpos - vtotal; *vpos = *vpos - vtotal;
} }
...@@ -1164,7 +1166,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, ...@@ -1164,7 +1166,7 @@ int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos,
/* In vblank? */ /* In vblank? */
if (in_vbl) if (in_vbl)
ret |= RADEON_SCANOUTPOS_INVBL; ret |= DRM_SCANOUTPOS_INVBL;
return ret; return ret;
} }
...@@ -66,6 +66,10 @@ int radeon_resume_kms(struct drm_device *dev); ...@@ -66,6 +66,10 @@ int radeon_resume_kms(struct drm_device *dev);
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc); u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc);
int radeon_enable_vblank_kms(struct drm_device *dev, int crtc); int radeon_enable_vblank_kms(struct drm_device *dev, int crtc);
void radeon_disable_vblank_kms(struct drm_device *dev, int crtc); void radeon_disable_vblank_kms(struct drm_device *dev, int crtc);
int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
int *max_error,
struct timeval *vblank_time,
unsigned flags);
void radeon_driver_irq_preinstall_kms(struct drm_device *dev); void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
int radeon_driver_irq_postinstall_kms(struct drm_device *dev); int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
void radeon_driver_irq_uninstall_kms(struct drm_device *dev); void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
...@@ -74,6 +78,8 @@ int radeon_dma_ioctl_kms(struct drm_device *dev, void *data, ...@@ -74,6 +78,8 @@ int radeon_dma_ioctl_kms(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
int radeon_gem_object_init(struct drm_gem_object *obj); int radeon_gem_object_init(struct drm_gem_object *obj);
void radeon_gem_object_free(struct drm_gem_object *obj); void radeon_gem_object_free(struct drm_gem_object *obj);
extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
int *vpos, int *hpos);
extern struct drm_ioctl_desc radeon_ioctls_kms[]; extern struct drm_ioctl_desc radeon_ioctls_kms[];
extern int radeon_max_kms_ioctl; extern int radeon_max_kms_ioctl;
int radeon_mmap(struct file *filp, struct vm_area_struct *vma); int radeon_mmap(struct file *filp, struct vm_area_struct *vma);
...@@ -277,6 +283,8 @@ static struct drm_driver kms_driver = { ...@@ -277,6 +283,8 @@ static struct drm_driver kms_driver = {
.get_vblank_counter = radeon_get_vblank_counter_kms, .get_vblank_counter = radeon_get_vblank_counter_kms,
.enable_vblank = radeon_enable_vblank_kms, .enable_vblank = radeon_enable_vblank_kms,
.disable_vblank = radeon_disable_vblank_kms, .disable_vblank = radeon_disable_vblank_kms,
.get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
.get_scanout_position = radeon_get_crtc_scanoutpos,
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
.debugfs_init = radeon_debugfs_init, .debugfs_init = radeon_debugfs_init,
.debugfs_cleanup = radeon_debugfs_cleanup, .debugfs_cleanup = radeon_debugfs_cleanup,
......
...@@ -277,6 +277,27 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc) ...@@ -277,6 +277,27 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
radeon_irq_set(rdev); radeon_irq_set(rdev);
} }
int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
int *max_error,
struct timeval *vblank_time,
unsigned flags)
{
struct drm_crtc *drmcrtc;
struct radeon_device *rdev = dev->dev_private;
if (crtc < 0 || crtc >= dev->num_crtcs) {
DRM_ERROR("Invalid crtc %d\n", crtc);
return -EINVAL;
}
/* Get associated drm_crtc: */
drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
/* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
vblank_time, flags,
drmcrtc);
}
/* /*
* IOCTL. * IOCTL.
......
...@@ -442,10 +442,6 @@ struct radeon_framebuffer { ...@@ -442,10 +442,6 @@ 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);
...@@ -562,7 +558,8 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc, ...@@ -562,7 +558,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 int radeon_get_crtc_scanoutpos(struct drm_device *dev, 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 *
......
...@@ -720,9 +720,9 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev) ...@@ -720,9 +720,9 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev)
*/ */
for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
if (rdev->pm.active_crtcs & (1 << crtc)) { if (rdev->pm.active_crtcs & (1 << crtc)) {
vbl_status = radeon_get_crtc_scanoutpos(rdev, crtc, &vpos, &hpos); vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos);
if ((vbl_status & RADEON_SCANOUTPOS_VALID) && if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
!(vbl_status & RADEON_SCANOUTPOS_INVBL)) !(vbl_status & DRM_SCANOUTPOS_INVBL))
in_vbl = false; in_vbl = false;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment