Automatically try re-enabling TearFree after a flip failed

Specifically, after both the page flip and vblank ioctls failed, but
then the vblank ioctl started working again. This can happen
intermittently e.g. when hotplugging a DP display. Previously, TearFree
would stay disabled in that case until a modeset was triggered somehow.

Bugzilla: https://bugs.freedesktop.org/103791
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Michel Dänzer
2018-11-21 18:32:04 +01:00
committed by Michel Dänzer
parent 4e7a24ac5a
commit bcfa6c258f
2 changed files with 71 additions and 11 deletions

View File

@@ -748,15 +748,32 @@ amdgpu_prime_scanout_update(PixmapDirtyUpdatePtr dirty)
if (!drmmode_wait_vblank(xf86_crtc, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
1, drm_queue_seq, NULL, NULL)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"drmmode_wait_vblank failed for PRIME update: %s\n",
strerror(errno));
if (!(drmmode_crtc->scanout_status & DRMMODE_SCANOUT_VBLANK_FAILED)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"drmmode_wait_vblank failed for PRIME update: %s\n",
strerror(errno));
drmmode_crtc->scanout_status |= DRMMODE_SCANOUT_VBLANK_FAILED;
}
drmmode_crtc->drmmode->event_context.vblank_handler(pAMDGPUEnt->fd,
0, 0, 0,
(void*)drm_queue_seq);
drmmode_crtc->wait_flip_nesting_level++;
amdgpu_drm_queue_handle_deferred(xf86_crtc);
return;
}
if (drmmode_crtc->scanout_status ==
(DRMMODE_SCANOUT_FLIP_FAILED | DRMMODE_SCANOUT_VBLANK_FAILED)) {
/* The page flip and vblank ioctls failed before, but the vblank
* ioctl is working again, so we can try re-enabling TearFree
*/
xf86_crtc->funcs->set_mode_major(xf86_crtc, &xf86_crtc->mode,
xf86_crtc->rotation,
xf86_crtc->x, xf86_crtc->y);
}
drmmode_crtc->scanout_status &= ~DRMMODE_SCANOUT_VBLANK_FAILED;
}
static void
@@ -807,12 +824,22 @@ amdgpu_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
if (drmmode_page_flip_target_relative(pAMDGPUEnt, drmmode_crtc,
drmmode_crtc->flip_pending->handle,
0, drm_queue_seq, 0) != 0) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n",
__func__, strerror(errno));
if (!(drmmode_crtc->scanout_status & DRMMODE_SCANOUT_FLIP_FAILED)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"flip queue failed in %s: %s, TearFree inactive\n",
__func__, strerror(errno));
drmmode_crtc->scanout_status |= DRMMODE_SCANOUT_FLIP_FAILED;
}
amdgpu_drm_abort_entry(drm_queue_seq);
return;
}
if (drmmode_crtc->scanout_status & DRMMODE_SCANOUT_FLIP_FAILED) {
xf86DrvMsg(scrn->scrnIndex, X_INFO, "TearFree active again\n");
drmmode_crtc->scanout_status &= ~DRMMODE_SCANOUT_FLIP_FAILED;
}
drmmode_crtc->scanout_id = scanout_id;
drmmode_crtc->scanout_update_pending = drm_queue_seq;
}
@@ -1029,15 +1056,32 @@ amdgpu_scanout_update(xf86CrtcPtr xf86_crtc)
if (!drmmode_wait_vblank(xf86_crtc, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
1, drm_queue_seq, NULL, NULL)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"drmmode_wait_vblank failed for scanout update: %s\n",
strerror(errno));
if (!(drmmode_crtc->scanout_status & DRMMODE_SCANOUT_VBLANK_FAILED)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"drmmode_wait_vblank failed for scanout update: %s\n",
strerror(errno));
drmmode_crtc->scanout_status |= DRMMODE_SCANOUT_VBLANK_FAILED;
}
drmmode_crtc->drmmode->event_context.vblank_handler(pAMDGPUEnt->fd,
0, 0, 0,
(void*)drm_queue_seq);
drmmode_crtc->wait_flip_nesting_level++;
amdgpu_drm_queue_handle_deferred(xf86_crtc);
return;
}
if (drmmode_crtc->scanout_status ==
(DRMMODE_SCANOUT_FLIP_FAILED | DRMMODE_SCANOUT_VBLANK_FAILED)) {
/* The page flip and vblank ioctls failed before, but the vblank
* ioctl is working again, so we can try re-enabling TearFree
*/
xf86_crtc->funcs->set_mode_major(xf86_crtc, &xf86_crtc->mode,
xf86_crtc->rotation,
xf86_crtc->x, xf86_crtc->y);
}
drmmode_crtc->scanout_status &= ~DRMMODE_SCANOUT_VBLANK_FAILED;
}
static void
@@ -1089,9 +1133,13 @@ amdgpu_scanout_flip(ScreenPtr pScreen, AMDGPUInfoPtr info,
if (drmmode_page_flip_target_relative(pAMDGPUEnt, drmmode_crtc,
drmmode_crtc->flip_pending->handle,
0, drm_queue_seq, 0) != 0) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s, "
"TearFree inactive until next modeset\n",
__func__, strerror(errno));
if (!(drmmode_crtc->scanout_status & DRMMODE_SCANOUT_FLIP_FAILED)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"flip queue failed in %s: %s, TearFree inactive\n",
__func__, strerror(errno));
drmmode_crtc->scanout_status |= DRMMODE_SCANOUT_FLIP_FAILED;
}
amdgpu_drm_abort_entry(drm_queue_seq);
RegionCopy(DamageRegion(drmmode_crtc->scanout_damage),
&drmmode_crtc->scanout_last_region);
@@ -1103,6 +1151,11 @@ amdgpu_scanout_flip(ScreenPtr pScreen, AMDGPUInfoPtr info,
return;
}
if (drmmode_crtc->scanout_status & DRMMODE_SCANOUT_FLIP_FAILED) {
xf86DrvMsg(scrn->scrnIndex, X_INFO, "TearFree active again\n");
drmmode_crtc->scanout_status &= ~DRMMODE_SCANOUT_FLIP_FAILED;
}
drmmode_crtc->scanout_id = scanout_id;
drmmode_crtc->scanout_update_pending = drm_queue_seq;
}

View File

@@ -91,6 +91,12 @@ struct drmmode_fb {
uint32_t handle;
};
enum drmmode_scanout_status {
DRMMODE_SCANOUT_OK,
DRMMODE_SCANOUT_FLIP_FAILED = 1u << 0,
DRMMODE_SCANOUT_VBLANK_FAILED = 1u << 1,
};
struct drmmode_scanout {
struct amdgpu_buffer *bo;
PixmapPtr pixmap;
@@ -110,6 +116,7 @@ typedef struct {
unsigned scanout_id;
uintptr_t scanout_update_pending;
Bool tear_free;
enum drmmode_scanout_status scanout_status;
Bool vrr_enabled;
PixmapPtr prime_scanout_pixmap;