Wait for pending flips to complete before turning off an output or CRTC

At least with older kernels, the flip may never complete otherwise,
which can result in us hanging in drmmode_set_mode_major.

Fixes: https://bugs.launchpad.net/ubuntu/+source/xserver-xorg-video-ati/+bug/1577170

Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Michel Dänzer
2016-05-10 18:45:30 +09:00
committed by Michel Dänzer
parent 9a1afbf61f
commit 9090309e05
5 changed files with 54 additions and 12 deletions

View File

@@ -307,9 +307,15 @@ drmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
CARD64 ust;
int ret;
drmmode_crtc->pending_dpms_mode = mode;
if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) {
drmVBlank vbl;
/* Wait for any pending flip to finish */
if (drmmode_crtc->flip_pending)
return;
/*
* On->Off transition: record the last vblank time,
* sequence number and frame period.
@@ -367,10 +373,14 @@ drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
drmmode_ptr drmmode = drmmode_crtc->drmmode;
/* Disable unused CRTCs */
if (!crtc->enabled || mode != DPMSModeOn)
if (!crtc->enabled || mode != DPMSModeOn) {
/* Wait for any pending flip to finish */
if (drmmode_crtc->flip_pending)
return;
drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
0, 0, 0, NULL, 0, NULL);
else if (drmmode_crtc->dpms_mode != DPMSModeOn)
} else if (drmmode_crtc->dpms_mode != DPMSModeOn)
crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
crtc->x, crtc->y);
}
@@ -1232,6 +1242,7 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res
drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, mode_res->crtcs[num]);
drmmode_crtc->drmmode = drmmode;
drmmode_crtc->dpms_mode = DPMSModeOff;
drmmode_crtc->pending_dpms_mode = DPMSModeOff;
crtc->driver_private = drmmode_crtc;
drmmode_crtc_hw_id(crtc);
@@ -1357,9 +1368,16 @@ drmmode_output_dpms(xf86OutputPtr output, int mode)
if (!koutput)
return;
if (mode != DPMSModeOn && crtc)
if (mode != DPMSModeOn && crtc) {
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_do_crtc_dpms(crtc, mode);
/* Wait for any pending flip to finish */
if (drmmode_crtc->flip_pending)
return;
}
drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
drmmode_output->dpms_enum_id, mode);
@@ -2190,9 +2208,32 @@ static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
};
static void
drmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
drmmode_clear_pending_flip(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_crtc->flip_pending = FALSE;
if (drmmode_crtc->pending_dpms_mode != DPMSModeOn &&
drmmode_crtc->dpms_mode != drmmode_crtc->pending_dpms_mode) {
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
int o;
for (o = 0; o < xf86_config->num_output; o++) {
xf86OutputPtr output = xf86_config->output[o];
if (output->crtc != crtc)
continue;
drmmode_output_dpms(output, drmmode_crtc->pending_dpms_mode);
drmmode_crtc_dpms(crtc, drmmode_crtc->pending_dpms_mode);
}
}
}
static void
drmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
{
drmmode_flipdata_ptr flipdata = event_data;
if (--flipdata->flip_count == 0) {
@@ -2202,13 +2243,12 @@ drmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
free(flipdata);
}
drmmode_crtc->flip_pending = FALSE;
drmmode_clear_pending_flip(crtc);
}
static void
drmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *event_data)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
RADEONInfoPtr info = RADEONPTR(crtc->scrn);
drmmode_flipdata_ptr flipdata = event_data;
@@ -2232,7 +2272,7 @@ drmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *even
free(flipdata);
}
drmmode_crtc->flip_pending = FALSE;
drmmode_clear_pending_flip(crtc);
}

View File

@@ -88,6 +88,8 @@ typedef struct {
unsigned scanout_id;
Bool scanout_update_pending;
int dpms_mode;
/* For when a flip is pending when DPMS off requested */
int pending_dpms_mode;
CARD64 dpms_last_ust;
uint32_t dpms_last_seq;
int dpms_last_fps;

View File

@@ -430,7 +430,7 @@ radeon_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id)
Bool force;
if (!xf86_crtc->enabled ||
drmmode_crtc->dpms_mode != DPMSModeOn ||
drmmode_crtc->pending_dpms_mode != DPMSModeOn ||
!drmmode_crtc->scanout[scanout_id].pixmap)
return FALSE;
@@ -564,7 +564,7 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
if (!xf86_crtc->enabled ||
drmmode_crtc->scanout_update_pending ||
!drmmode_crtc->scanout[0].pixmap ||
drmmode_crtc->dpms_mode != DPMSModeOn)
drmmode_crtc->pending_dpms_mode != DPMSModeOn)
return;
pDamage = drmmode_crtc->scanout[0].damage;

View File

@@ -268,7 +268,7 @@ radeon_present_check_flip(RRCrtcPtr crtc, WindowPtr window, PixmapPtr pixmap,
if (!drmmode_crtc || drmmode_crtc->rotate.bo != NULL)
return FALSE;
if (drmmode_crtc->dpms_mode == DPMSModeOn)
if (drmmode_crtc->pending_dpms_mode == DPMSModeOn)
num_crtcs_on++;
}
@@ -396,7 +396,7 @@ modeset:
if (!crtc->enabled)
continue;
if (drmmode_crtc->dpms_mode == DPMSModeOn)
if (drmmode_crtc->pending_dpms_mode == DPMSModeOn)
crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
crtc->x, crtc->y);
else

View File

@@ -71,7 +71,7 @@ radeon_box_area(BoxPtr box)
Bool radeon_crtc_is_enabled(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
return drmmode_crtc->dpms_mode == DPMSModeOn;
return drmmode_crtc->pending_dpms_mode == DPMSModeOn;
}
xf86CrtcPtr