diff options
Diffstat (limited to 'xserver/hw/xfree86/drivers/modesetting/vblank.c')
-rw-r--r-- | xserver/hw/xfree86/drivers/modesetting/vblank.c | 190 |
1 files changed, 139 insertions, 51 deletions
diff --git a/xserver/hw/xfree86/drivers/modesetting/vblank.c b/xserver/hw/xfree86/drivers/modesetting/vblank.c index 8682f4d91..561229f30 100644 --- a/xserver/hw/xfree86/drivers/modesetting/vblank.c +++ b/xserver/hw/xfree86/drivers/modesetting/vblank.c @@ -173,7 +173,7 @@ ms_dri2_crtc_covering_drawable(DrawablePtr pDraw) static Bool ms_get_kernel_ust_msc(xf86CrtcPtr crtc, - uint32_t *msc, uint64_t *ust) + uint64_t *msc, uint64_t *ust) { ScreenPtr screen = crtc->randr_crtc->pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); @@ -182,6 +182,19 @@ ms_get_kernel_ust_msc(xf86CrtcPtr crtc, drmVBlank vbl; int ret; + if (ms->has_queue_sequence || !ms->tried_queue_sequence) { + uint64_t ns; + ms->tried_queue_sequence = TRUE; + + ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, + msc, &ns); + if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { + ms->has_queue_sequence = TRUE; + if (ret == 0) + *ust = ns / 1000; + return ret == 0; + } + } /* Get current count */ vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe; vbl.request.sequence = 0; @@ -198,68 +211,127 @@ ms_get_kernel_ust_msc(xf86CrtcPtr crtc, } } +Bool +ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags, + uint64_t msc, uint64_t *msc_queued, uint32_t seq) +{ + ScreenPtr screen = crtc->randr_crtc->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmVBlank vbl; + int ret; + + for (;;) { + /* Queue an event at the specified sequence */ + if (ms->has_queue_sequence || !ms->tried_queue_sequence) { + uint32_t drm_flags = 0; + uint64_t kernel_queued; + + ms->tried_queue_sequence = TRUE; + + if (flags & MS_QUEUE_RELATIVE) + drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE; + if (flags & MS_QUEUE_NEXT_ON_MISS) + drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS; + + ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, + drm_flags, msc, &kernel_queued, seq); + if (ret == 0) { + if (msc_queued) + *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE); + ms->has_queue_sequence = TRUE; + return TRUE; + } + + if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { + ms->has_queue_sequence = TRUE; + goto check; + } + } + vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe; + if (flags & MS_QUEUE_RELATIVE) + vbl.request.type |= DRM_VBLANK_RELATIVE; + else + vbl.request.type |= DRM_VBLANK_ABSOLUTE; + if (flags & MS_QUEUE_NEXT_ON_MISS) + vbl.request.type |= DRM_VBLANK_NEXTONMISS; + + vbl.request.sequence = msc; + vbl.request.signal = seq; + ret = drmWaitVBlank(ms->fd, &vbl); + if (ret == 0) { + if (msc_queued) + *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE); + return TRUE; + } + check: + if (errno != EBUSY) { + ms_drm_abort_seq(scrn, seq); + return FALSE; + } + ms_flush_drm_events(screen); + } +} + /** - * Convert a 32-bit kernel MSC sequence number to a 64-bit local sequence - * number, adding in the vblank_offset and high 32 bits, and dealing - * with 64-bit wrapping + * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local + * sequence number, adding in the high 32 bits, and dealing with 32-bit + * wrapping if needed. */ uint64_t -ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence) +ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit) { drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; - sequence += drmmode_crtc->vblank_offset; - if ((int32_t) (sequence - drmmode_crtc->msc_prev) < -0x40000000) - drmmode_crtc->msc_high += 0x100000000L; + if (!is64bit) { + /* sequence is provided as a 32 bit value from one of the 32 bit apis, + * e.g., drmWaitVBlank(), classic vblank events, or pageflip events. + * + * Track and handle 32-Bit wrapping, somewhat robust against occasional + * out-of-order not always monotonically increasing sequence values. + */ + if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000)) + drmmode_crtc->msc_high += 0x100000000L; + + if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000)) + drmmode_crtc->msc_high -= 0x100000000L; + + drmmode_crtc->msc_prev = sequence; + + return drmmode_crtc->msc_high + sequence; + } + + /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence / + * drmCrtcQueueSequence apis and events. Pass through sequence unmodified, + * but update the 32-bit tracking variables with reliable ground truth. + * + * With 64-Bit api in use, the only !is64bit input is from pageflip events, + * and any pageflip event is usually preceeded by some is64bit input from + * swap scheduling, so this should provide reliable mapping for pageflip + * events based on true 64-bit input as baseline as well. + */ drmmode_crtc->msc_prev = sequence; - return drmmode_crtc->msc_high + sequence; + drmmode_crtc->msc_high = sequence & 0xffffffff00000000; + + return sequence; } int ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc) { - uint32_t kernel_msc; + ScreenPtr screen = crtc->randr_crtc->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); + uint64_t kernel_msc; if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust)) return BadMatch; - *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc); + *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence); return Success; } -#define MAX_VBLANK_OFFSET 1000 - -/** - * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number, - * removing the high 32 bits and subtracting out the vblank_offset term. - * - * This also updates the vblank_offset when it notices that the value should - * change. - */ -uint32_t -ms_crtc_msc_to_kernel_msc(xf86CrtcPtr crtc, uint64_t expect) -{ - drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; - uint64_t msc; - uint64_t ust; - int64_t diff; - - if (ms_get_crtc_ust_msc(crtc, &ust, &msc) == Success) { - diff = expect - msc; - - /* We're way off here, assume that the kernel has lost its mind - * and smack the vblank back to something sensible - */ - if (diff < -MAX_VBLANK_OFFSET || MAX_VBLANK_OFFSET < diff) { - drmmode_crtc->vblank_offset += (int32_t) diff; - if (drmmode_crtc->vblank_offset > -MAX_VBLANK_OFFSET && - drmmode_crtc->vblank_offset < MAX_VBLANK_OFFSET) - drmmode_crtc->vblank_offset = 0; - } - } - return (uint32_t) (expect - drmmode_crtc->vblank_offset); -} - /** * Check for pending DRM events and process them. */ @@ -375,25 +447,40 @@ ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), * drm event queue and calls the handler for it. */ static void -ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, - void *user_ptr) +ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data) { struct ms_drm_queue *q, *tmp; - uint32_t user_data = (uint32_t) (intptr_t) user_ptr; + uint32_t seq = (uint32_t) user_data; xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { - if (q->seq == user_data) { + if (q->seq == seq) { uint64_t msc; - msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame); + msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit); xorg_list_del(&q->list); - q->handler(msc, (uint64_t) sec * 1000000 + usec, q->data); + q->handler(msc, ns / 1000, q->data); free(q); break; } } } +static void +ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data) +{ + /* frame is true 64 bit wrapped into 64 bit */ + ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data); +} + +static void +ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, + void *user_ptr) +{ + /* frame is 32 bit wrapped into 64 bit */ + ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000, + FALSE, (uint32_t) (uintptr_t) user_ptr); +} + Bool ms_vblank_screen_init(ScreenPtr screen) { @@ -402,9 +489,10 @@ ms_vblank_screen_init(ScreenPtr screen) modesettingEntPtr ms_ent = ms_ent_priv(scrn); xorg_list_init(&ms_drm_queue); - ms->event_context.version = 2; + ms->event_context.version = 4; ms->event_context.vblank_handler = ms_drm_handler; ms->event_context.page_flip_handler = ms_drm_handler; + ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit; /* We need to re-register the DRM fd for the synchronisation * feedback on every server generation, so perform the |