1/* 2 * Copyright © 2013 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23/** @file vblank.c 24 * 25 * Support for tracking the DRM's vblank events. 26 */ 27 28#ifdef HAVE_DIX_CONFIG_H 29#include "dix-config.h" 30#endif 31 32#include <unistd.h> 33#include <xf86.h> 34#include <xf86Crtc.h> 35#include "driver.h" 36#include "drmmode_display.h" 37 38/** 39 * Tracking for outstanding events queued to the kernel. 40 * 41 * Each list entry is a struct ms_drm_queue, which has a uint32_t 42 * value generated from drm_seq that identifies the event and a 43 * reference back to the crtc/screen associated with the event. It's 44 * done this way rather than in the screen because we want to be able 45 * to drain the list of event handlers that should be called at server 46 * regen time, even though we don't close the drm fd and have no way 47 * to actually drain the kernel events. 48 */ 49static struct xorg_list ms_drm_queue; 50static uint32_t ms_drm_seq; 51 52static void box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) 53{ 54 dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1; 55 dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2; 56 if (dest->x1 >= dest->x2) { 57 dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; 58 return; 59 } 60 61 dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1; 62 dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2; 63 if (dest->y1 >= dest->y2) 64 dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; 65} 66 67static void rr_crtc_box(RRCrtcPtr crtc, BoxPtr crtc_box) 68{ 69 if (crtc->mode) { 70 crtc_box->x1 = crtc->x; 71 crtc_box->y1 = crtc->y; 72 switch (crtc->rotation) { 73 case RR_Rotate_0: 74 case RR_Rotate_180: 75 default: 76 crtc_box->x2 = crtc->x + crtc->mode->mode.width; 77 crtc_box->y2 = crtc->y + crtc->mode->mode.height; 78 break; 79 case RR_Rotate_90: 80 case RR_Rotate_270: 81 crtc_box->x2 = crtc->x + crtc->mode->mode.height; 82 crtc_box->y2 = crtc->y + crtc->mode->mode.width; 83 break; 84 } 85 } else 86 crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0; 87} 88 89static int box_area(BoxPtr box) 90{ 91 return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1); 92} 93 94static Bool rr_crtc_on(RRCrtcPtr crtc, Bool crtc_is_xf86_hint) 95{ 96 if (!crtc) { 97 return FALSE; 98 } 99 if (crtc_is_xf86_hint && crtc->devPrivate) { 100 return xf86_crtc_on(crtc->devPrivate); 101 } else { 102 return !!crtc->mode; 103 } 104} 105 106Bool 107xf86_crtc_on(xf86CrtcPtr crtc) 108{ 109 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 110 111 return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn; 112} 113 114 115/* 116 * Return the crtc covering 'box'. If two crtcs cover a portion of 117 * 'box', then prefer the crtc with greater coverage. 118 */ 119static RRCrtcPtr 120rr_crtc_covering_box(ScreenPtr pScreen, BoxPtr box, Bool screen_is_xf86_hint) 121{ 122 rrScrPrivPtr pScrPriv; 123 RROutputPtr primary_output; 124 RRCrtcPtr crtc, best_crtc, primary_crtc; 125 int coverage, best_coverage; 126 int c; 127 BoxRec crtc_box, cover_box; 128 129 best_crtc = NULL; 130 best_coverage = 0; 131 132 if (!dixPrivateKeyRegistered(rrPrivKey)) 133 return NULL; 134 135 pScrPriv = rrGetScrPriv(pScreen); 136 137 if (!pScrPriv) 138 return NULL; 139 140 primary_crtc = NULL; 141 primary_output = RRFirstOutput(pScreen); 142 if (primary_output) 143 primary_crtc = primary_output->crtc; 144 145 for (c = 0; c < pScrPriv->numCrtcs; c++) { 146 crtc = pScrPriv->crtcs[c]; 147 148 /* If the CRTC is off, treat it as not covering */ 149 if (!rr_crtc_on(crtc, screen_is_xf86_hint)) 150 continue; 151 152 rr_crtc_box(crtc, &crtc_box); 153 box_intersect(&cover_box, &crtc_box, box); 154 coverage = box_area(&cover_box); 155 if ((coverage > best_coverage) || 156 (coverage == best_coverage && crtc == primary_crtc)) { 157 best_crtc = crtc; 158 best_coverage = coverage; 159 } 160 } 161 162 return best_crtc; 163} 164 165static RRCrtcPtr 166rr_crtc_covering_box_on_secondary(ScreenPtr pScreen, BoxPtr box) 167{ 168 if (!pScreen->isGPU) { 169 ScreenPtr secondary; 170 RRCrtcPtr crtc = NULL; 171 172 xorg_list_for_each_entry(secondary, &pScreen->secondary_list, secondary_head) { 173 if (!secondary->is_output_secondary) 174 continue; 175 176 crtc = rr_crtc_covering_box(secondary, box, FALSE); 177 if (crtc) 178 return crtc; 179 } 180 } 181 182 return NULL; 183} 184 185xf86CrtcPtr 186ms_dri2_crtc_covering_drawable(DrawablePtr pDraw) 187{ 188 ScreenPtr pScreen = pDraw->pScreen; 189 RRCrtcPtr crtc = NULL; 190 BoxRec box; 191 192 box.x1 = pDraw->x; 193 box.y1 = pDraw->y; 194 box.x2 = box.x1 + pDraw->width; 195 box.y2 = box.y1 + pDraw->height; 196 197 crtc = rr_crtc_covering_box(pScreen, &box, TRUE); 198 if (crtc) { 199 return crtc->devPrivate; 200 } 201 return NULL; 202} 203 204RRCrtcPtr 205ms_randr_crtc_covering_drawable(DrawablePtr pDraw) 206{ 207 ScreenPtr pScreen = pDraw->pScreen; 208 RRCrtcPtr crtc = NULL; 209 BoxRec box; 210 211 box.x1 = pDraw->x; 212 box.y1 = pDraw->y; 213 box.x2 = box.x1 + pDraw->width; 214 box.y2 = box.y1 + pDraw->height; 215 216 crtc = rr_crtc_covering_box(pScreen, &box, TRUE); 217 if (!crtc) { 218 crtc = rr_crtc_covering_box_on_secondary(pScreen, &box); 219 } 220 return crtc; 221} 222 223static Bool 224ms_get_kernel_ust_msc(xf86CrtcPtr crtc, 225 uint64_t *msc, uint64_t *ust) 226{ 227 ScreenPtr screen = crtc->randr_crtc->pScreen; 228 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 229 modesettingPtr ms = modesettingPTR(scrn); 230 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 231 drmVBlank vbl; 232 int ret; 233 234 if (ms->has_queue_sequence || !ms->tried_queue_sequence) { 235 uint64_t ns; 236 ms->tried_queue_sequence = TRUE; 237 238 ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, 239 msc, &ns); 240 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { 241 ms->has_queue_sequence = TRUE; 242 if (ret == 0) 243 *ust = ns / 1000; 244 return ret == 0; 245 } 246 } 247 /* Get current count */ 248 vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe; 249 vbl.request.sequence = 0; 250 vbl.request.signal = 0; 251 ret = drmWaitVBlank(ms->fd, &vbl); 252 if (ret) { 253 *msc = 0; 254 *ust = 0; 255 return FALSE; 256 } else { 257 *msc = vbl.reply.sequence; 258 *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec; 259 return TRUE; 260 } 261} 262 263Bool 264ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags, 265 uint64_t msc, uint64_t *msc_queued, uint32_t seq) 266{ 267 ScreenPtr screen = crtc->randr_crtc->pScreen; 268 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 269 modesettingPtr ms = modesettingPTR(scrn); 270 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 271 drmVBlank vbl; 272 int ret; 273 274 for (;;) { 275 /* Queue an event at the specified sequence */ 276 if (ms->has_queue_sequence || !ms->tried_queue_sequence) { 277 uint32_t drm_flags = 0; 278 uint64_t kernel_queued; 279 280 ms->tried_queue_sequence = TRUE; 281 282 if (flags & MS_QUEUE_RELATIVE) 283 drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE; 284 if (flags & MS_QUEUE_NEXT_ON_MISS) 285 drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS; 286 287 ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, 288 drm_flags, msc, &kernel_queued, seq); 289 if (ret == 0) { 290 if (msc_queued) 291 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE); 292 ms->has_queue_sequence = TRUE; 293 return TRUE; 294 } 295 296 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { 297 ms->has_queue_sequence = TRUE; 298 goto check; 299 } 300 } 301 vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe; 302 if (flags & MS_QUEUE_RELATIVE) 303 vbl.request.type |= DRM_VBLANK_RELATIVE; 304 else 305 vbl.request.type |= DRM_VBLANK_ABSOLUTE; 306 if (flags & MS_QUEUE_NEXT_ON_MISS) 307 vbl.request.type |= DRM_VBLANK_NEXTONMISS; 308 309 vbl.request.sequence = msc; 310 vbl.request.signal = seq; 311 ret = drmWaitVBlank(ms->fd, &vbl); 312 if (ret == 0) { 313 if (msc_queued) 314 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE); 315 return TRUE; 316 } 317 check: 318 if (errno != EBUSY) { 319 ms_drm_abort_seq(scrn, seq); 320 return FALSE; 321 } 322 ms_flush_drm_events(screen); 323 } 324} 325 326/** 327 * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local 328 * sequence number, adding in the high 32 bits, and dealing with 32-bit 329 * wrapping if needed. 330 */ 331uint64_t 332ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit) 333{ 334 drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; 335 336 if (!is64bit) { 337 /* sequence is provided as a 32 bit value from one of the 32 bit apis, 338 * e.g., drmWaitVBlank(), classic vblank events, or pageflip events. 339 * 340 * Track and handle 32-Bit wrapping, somewhat robust against occasional 341 * out-of-order not always monotonically increasing sequence values. 342 */ 343 if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000)) 344 drmmode_crtc->msc_high += 0x100000000L; 345 346 if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000)) 347 drmmode_crtc->msc_high -= 0x100000000L; 348 349 drmmode_crtc->msc_prev = sequence; 350 351 return drmmode_crtc->msc_high + sequence; 352 } 353 354 /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence / 355 * drmCrtcQueueSequence apis and events. Pass through sequence unmodified, 356 * but update the 32-bit tracking variables with reliable ground truth. 357 * 358 * With 64-Bit api in use, the only !is64bit input is from pageflip events, 359 * and any pageflip event is usually preceded by some is64bit input from 360 * swap scheduling, so this should provide reliable mapping for pageflip 361 * events based on true 64-bit input as baseline as well. 362 */ 363 drmmode_crtc->msc_prev = sequence; 364 drmmode_crtc->msc_high = sequence & 0xffffffff00000000; 365 366 return sequence; 367} 368 369int 370ms_get_crtc_ust_msc(xf86CrtcPtr crtc, uint64_t *ust, uint64_t *msc) 371{ 372 ScreenPtr screen = crtc->randr_crtc->pScreen; 373 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 374 modesettingPtr ms = modesettingPTR(scrn); 375 uint64_t kernel_msc; 376 377 if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust)) 378 return BadMatch; 379 *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence); 380 381 return Success; 382} 383 384/** 385 * Check for pending DRM events and process them. 386 */ 387static void 388ms_drm_socket_handler(int fd, int ready, void *data) 389{ 390 ScreenPtr screen = data; 391 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 392 modesettingPtr ms = modesettingPTR(scrn); 393 394 if (data == NULL) 395 return; 396 397 drmHandleEvent(fd, &ms->event_context); 398} 399 400/* 401 * Enqueue a potential drm response; when the associated response 402 * appears, we've got data to pass to the handler from here 403 */ 404uint32_t 405ms_drm_queue_alloc(xf86CrtcPtr crtc, 406 void *data, 407 ms_drm_handler_proc handler, 408 ms_drm_abort_proc abort) 409{ 410 ScreenPtr screen = crtc->randr_crtc->pScreen; 411 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 412 struct ms_drm_queue *q; 413 414 q = calloc(1, sizeof(struct ms_drm_queue)); 415 416 if (!q) 417 return 0; 418 if (!ms_drm_seq) 419 ++ms_drm_seq; 420 q->seq = ms_drm_seq++; 421 q->scrn = scrn; 422 q->crtc = crtc; 423 q->data = data; 424 q->handler = handler; 425 q->abort = abort; 426 427 xorg_list_add(&q->list, &ms_drm_queue); 428 429 return q->seq; 430} 431 432/** 433 * Abort one queued DRM entry, removing it 434 * from the list, calling the abort function and 435 * freeing the memory 436 */ 437static void 438ms_drm_abort_one(struct ms_drm_queue *q) 439{ 440 xorg_list_del(&q->list); 441 q->abort(q->data); 442 free(q); 443} 444 445/** 446 * Abort all queued entries on a specific scrn, used 447 * when resetting the X server 448 */ 449static void 450ms_drm_abort_scrn(ScrnInfoPtr scrn) 451{ 452 struct ms_drm_queue *q, *tmp; 453 454 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 455 if (q->scrn == scrn) 456 ms_drm_abort_one(q); 457 } 458} 459 460/** 461 * Abort by drm queue sequence number. 462 */ 463void 464ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq) 465{ 466 struct ms_drm_queue *q, *tmp; 467 468 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 469 if (q->seq == seq) { 470 ms_drm_abort_one(q); 471 break; 472 } 473 } 474} 475 476/* 477 * Externally usable abort function that uses a callback to match a single 478 * queued entry to abort 479 */ 480void 481ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), 482 void *match_data) 483{ 484 struct ms_drm_queue *q; 485 486 xorg_list_for_each_entry(q, &ms_drm_queue, list) { 487 if (match(q->data, match_data)) { 488 ms_drm_abort_one(q); 489 break; 490 } 491 } 492} 493 494/* 495 * General DRM kernel handler. Looks for the matching sequence number in the 496 * drm event queue and calls the handler for it. 497 */ 498static void 499ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data) 500{ 501 struct ms_drm_queue *q, *tmp; 502 uint32_t seq = (uint32_t) user_data; 503 504 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 505 if (q->seq == seq) { 506 uint64_t msc; 507 508 msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit); 509 xorg_list_del(&q->list); 510 q->handler(msc, ns / 1000, q->data); 511 free(q); 512 break; 513 } 514 } 515} 516 517static void 518ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data) 519{ 520 /* frame is true 64 bit wrapped into 64 bit */ 521 ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data); 522} 523 524static void 525ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, 526 void *user_ptr) 527{ 528 /* frame is 32 bit wrapped into 64 bit */ 529 ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000, 530 FALSE, (uint32_t) (uintptr_t) user_ptr); 531} 532 533Bool 534ms_vblank_screen_init(ScreenPtr screen) 535{ 536 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 537 modesettingPtr ms = modesettingPTR(scrn); 538 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 539 xorg_list_init(&ms_drm_queue); 540 541 ms->event_context.version = 4; 542 ms->event_context.vblank_handler = ms_drm_handler; 543 ms->event_context.page_flip_handler = ms_drm_handler; 544 ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit; 545 546 /* We need to re-register the DRM fd for the synchronisation 547 * feedback on every server generation, so perform the 548 * registration within ScreenInit and not PreInit. 549 */ 550 if (ms_ent->fd_wakeup_registered != serverGeneration) { 551 SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen); 552 ms_ent->fd_wakeup_registered = serverGeneration; 553 ms_ent->fd_wakeup_ref = 1; 554 } else 555 ms_ent->fd_wakeup_ref++; 556 557 return TRUE; 558} 559 560void 561ms_vblank_close_screen(ScreenPtr screen) 562{ 563 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 564 modesettingPtr ms = modesettingPTR(scrn); 565 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 566 567 ms_drm_abort_scrn(scrn); 568 569 if (ms_ent->fd_wakeup_registered == serverGeneration && 570 !--ms_ent->fd_wakeup_ref) { 571 RemoveNotifyFd(ms->fd); 572 } 573} 574