summaryrefslogtreecommitdiff
path: root/xserver/hw/xfree86/drivers/modesetting/vblank.c
diff options
context:
space:
mode:
Diffstat (limited to 'xserver/hw/xfree86/drivers/modesetting/vblank.c')
-rw-r--r--xserver/hw/xfree86/drivers/modesetting/vblank.c190
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