vblank.c revision d44ca368
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 RRCrtcPtr crtc, best_crtc; 124 int coverage, best_coverage; 125 int c; 126 BoxRec crtc_box, cover_box; 127 128 best_crtc = NULL; 129 best_coverage = 0; 130 131 if (!dixPrivateKeyRegistered(rrPrivKey)) 132 return NULL; 133 134 pScrPriv = rrGetScrPriv(pScreen); 135 136 if (!pScrPriv) 137 return NULL; 138 139 for (c = 0; c < pScrPriv->numCrtcs; c++) { 140 crtc = pScrPriv->crtcs[c]; 141 142 /* If the CRTC is off, treat it as not covering */ 143 if (!rr_crtc_on(crtc, screen_is_xf86_hint)) 144 continue; 145 146 rr_crtc_box(crtc, &crtc_box); 147 box_intersect(&cover_box, &crtc_box, box); 148 coverage = box_area(&cover_box); 149 if (coverage > best_coverage) { 150 best_crtc = crtc; 151 best_coverage = coverage; 152 } 153 } 154 155 return best_crtc; 156} 157 158static RRCrtcPtr 159rr_crtc_covering_box_on_slave(ScreenPtr pScreen, BoxPtr box) 160{ 161 if (!pScreen->isGPU) { 162 ScreenPtr slave; 163 RRCrtcPtr crtc = NULL; 164 165 xorg_list_for_each_entry(slave, &pScreen->slave_list, slave_head) { 166 if (!slave->is_output_slave) 167 continue; 168 169 crtc = rr_crtc_covering_box(slave, box, FALSE); 170 if (crtc) 171 return crtc; 172 } 173 } 174 175 return NULL; 176} 177 178xf86CrtcPtr 179ms_dri2_crtc_covering_drawable(DrawablePtr pDraw) 180{ 181 ScreenPtr pScreen = pDraw->pScreen; 182 RRCrtcPtr crtc = NULL; 183 BoxRec box; 184 185 box.x1 = pDraw->x; 186 box.y1 = pDraw->y; 187 box.x2 = box.x1 + pDraw->width; 188 box.y2 = box.y1 + pDraw->height; 189 190 crtc = rr_crtc_covering_box(pScreen, &box, TRUE); 191 if (crtc) { 192 return crtc->devPrivate; 193 } 194 return NULL; 195} 196 197RRCrtcPtr 198ms_randr_crtc_covering_drawable(DrawablePtr pDraw) 199{ 200 ScreenPtr pScreen = pDraw->pScreen; 201 RRCrtcPtr crtc = NULL; 202 BoxRec box; 203 204 box.x1 = pDraw->x; 205 box.y1 = pDraw->y; 206 box.x2 = box.x1 + pDraw->width; 207 box.y2 = box.y1 + pDraw->height; 208 209 crtc = rr_crtc_covering_box(pScreen, &box, TRUE); 210 if (!crtc) { 211 crtc = rr_crtc_covering_box_on_slave(pScreen, &box); 212 } 213 return crtc; 214} 215 216static Bool 217ms_get_kernel_ust_msc(xf86CrtcPtr crtc, 218 uint64_t *msc, uint64_t *ust) 219{ 220 ScreenPtr screen = crtc->randr_crtc->pScreen; 221 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 222 modesettingPtr ms = modesettingPTR(scrn); 223 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 224 drmVBlank vbl; 225 int ret; 226 227 if (ms->has_queue_sequence || !ms->tried_queue_sequence) { 228 uint64_t ns; 229 ms->tried_queue_sequence = TRUE; 230 231 ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, 232 msc, &ns); 233 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { 234 ms->has_queue_sequence = TRUE; 235 if (ret == 0) 236 *ust = ns / 1000; 237 return ret == 0; 238 } 239 } 240 /* Get current count */ 241 vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe; 242 vbl.request.sequence = 0; 243 vbl.request.signal = 0; 244 ret = drmWaitVBlank(ms->fd, &vbl); 245 if (ret) { 246 *msc = 0; 247 *ust = 0; 248 return FALSE; 249 } else { 250 *msc = vbl.reply.sequence; 251 *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec; 252 return TRUE; 253 } 254} 255 256Bool 257ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags, 258 uint64_t msc, uint64_t *msc_queued, uint32_t seq) 259{ 260 ScreenPtr screen = crtc->randr_crtc->pScreen; 261 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 262 modesettingPtr ms = modesettingPTR(scrn); 263 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 264 drmVBlank vbl; 265 int ret; 266 267 for (;;) { 268 /* Queue an event at the specified sequence */ 269 if (ms->has_queue_sequence || !ms->tried_queue_sequence) { 270 uint32_t drm_flags = 0; 271 uint64_t kernel_queued; 272 273 ms->tried_queue_sequence = TRUE; 274 275 if (flags & MS_QUEUE_RELATIVE) 276 drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE; 277 if (flags & MS_QUEUE_NEXT_ON_MISS) 278 drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS; 279 280 ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id, 281 drm_flags, msc, &kernel_queued, seq); 282 if (ret == 0) { 283 if (msc_queued) 284 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE); 285 ms->has_queue_sequence = TRUE; 286 return TRUE; 287 } 288 289 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) { 290 ms->has_queue_sequence = TRUE; 291 goto check; 292 } 293 } 294 vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe; 295 if (flags & MS_QUEUE_RELATIVE) 296 vbl.request.type |= DRM_VBLANK_RELATIVE; 297 else 298 vbl.request.type |= DRM_VBLANK_ABSOLUTE; 299 if (flags & MS_QUEUE_NEXT_ON_MISS) 300 vbl.request.type |= DRM_VBLANK_NEXTONMISS; 301 302 vbl.request.sequence = msc; 303 vbl.request.signal = seq; 304 ret = drmWaitVBlank(ms->fd, &vbl); 305 if (ret == 0) { 306 if (msc_queued) 307 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE); 308 return TRUE; 309 } 310 check: 311 if (errno != EBUSY) { 312 ms_drm_abort_seq(scrn, seq); 313 return FALSE; 314 } 315 ms_flush_drm_events(screen); 316 } 317} 318 319/** 320 * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local 321 * sequence number, adding in the high 32 bits, and dealing with 32-bit 322 * wrapping if needed. 323 */ 324uint64_t 325ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit) 326{ 327 drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; 328 329 if (!is64bit) { 330 /* sequence is provided as a 32 bit value from one of the 32 bit apis, 331 * e.g., drmWaitVBlank(), classic vblank events, or pageflip events. 332 * 333 * Track and handle 32-Bit wrapping, somewhat robust against occasional 334 * out-of-order not always monotonically increasing sequence values. 335 */ 336 if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000)) 337 drmmode_crtc->msc_high += 0x100000000L; 338 339 if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000)) 340 drmmode_crtc->msc_high -= 0x100000000L; 341 342 drmmode_crtc->msc_prev = sequence; 343 344 return drmmode_crtc->msc_high + sequence; 345 } 346 347 /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence / 348 * drmCrtcQueueSequence apis and events. Pass through sequence unmodified, 349 * but update the 32-bit tracking variables with reliable ground truth. 350 * 351 * With 64-Bit api in use, the only !is64bit input is from pageflip events, 352 * and any pageflip event is usually preceeded by some is64bit input from 353 * swap scheduling, so this should provide reliable mapping for pageflip 354 * events based on true 64-bit input as baseline as well. 355 */ 356 drmmode_crtc->msc_prev = sequence; 357 drmmode_crtc->msc_high = sequence & 0xffffffff00000000; 358 359 return sequence; 360} 361 362int 363ms_get_crtc_ust_msc(xf86CrtcPtr crtc, uint64_t *ust, uint64_t *msc) 364{ 365 ScreenPtr screen = crtc->randr_crtc->pScreen; 366 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 367 modesettingPtr ms = modesettingPTR(scrn); 368 uint64_t kernel_msc; 369 370 if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust)) 371 return BadMatch; 372 *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence); 373 374 return Success; 375} 376 377/** 378 * Check for pending DRM events and process them. 379 */ 380static void 381ms_drm_socket_handler(int fd, int ready, void *data) 382{ 383 ScreenPtr screen = data; 384 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 385 modesettingPtr ms = modesettingPTR(scrn); 386 387 if (data == NULL) 388 return; 389 390 drmHandleEvent(fd, &ms->event_context); 391} 392 393/* 394 * Enqueue a potential drm response; when the associated response 395 * appears, we've got data to pass to the handler from here 396 */ 397uint32_t 398ms_drm_queue_alloc(xf86CrtcPtr crtc, 399 void *data, 400 ms_drm_handler_proc handler, 401 ms_drm_abort_proc abort) 402{ 403 ScreenPtr screen = crtc->randr_crtc->pScreen; 404 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 405 struct ms_drm_queue *q; 406 407 q = calloc(1, sizeof(struct ms_drm_queue)); 408 409 if (!q) 410 return 0; 411 if (!ms_drm_seq) 412 ++ms_drm_seq; 413 q->seq = ms_drm_seq++; 414 q->scrn = scrn; 415 q->crtc = crtc; 416 q->data = data; 417 q->handler = handler; 418 q->abort = abort; 419 420 xorg_list_add(&q->list, &ms_drm_queue); 421 422 return q->seq; 423} 424 425/** 426 * Abort one queued DRM entry, removing it 427 * from the list, calling the abort function and 428 * freeing the memory 429 */ 430static void 431ms_drm_abort_one(struct ms_drm_queue *q) 432{ 433 xorg_list_del(&q->list); 434 q->abort(q->data); 435 free(q); 436} 437 438/** 439 * Abort all queued entries on a specific scrn, used 440 * when resetting the X server 441 */ 442static void 443ms_drm_abort_scrn(ScrnInfoPtr scrn) 444{ 445 struct ms_drm_queue *q, *tmp; 446 447 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 448 if (q->scrn == scrn) 449 ms_drm_abort_one(q); 450 } 451} 452 453/** 454 * Abort by drm queue sequence number. 455 */ 456void 457ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq) 458{ 459 struct ms_drm_queue *q, *tmp; 460 461 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 462 if (q->seq == seq) { 463 ms_drm_abort_one(q); 464 break; 465 } 466 } 467} 468 469/* 470 * Externally usable abort function that uses a callback to match a single 471 * queued entry to abort 472 */ 473void 474ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), 475 void *match_data) 476{ 477 struct ms_drm_queue *q; 478 479 xorg_list_for_each_entry(q, &ms_drm_queue, list) { 480 if (match(q->data, match_data)) { 481 ms_drm_abort_one(q); 482 break; 483 } 484 } 485} 486 487/* 488 * General DRM kernel handler. Looks for the matching sequence number in the 489 * drm event queue and calls the handler for it. 490 */ 491static void 492ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data) 493{ 494 struct ms_drm_queue *q, *tmp; 495 uint32_t seq = (uint32_t) user_data; 496 497 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 498 if (q->seq == seq) { 499 uint64_t msc; 500 501 msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit); 502 xorg_list_del(&q->list); 503 q->handler(msc, ns / 1000, q->data); 504 free(q); 505 break; 506 } 507 } 508} 509 510static void 511ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data) 512{ 513 /* frame is true 64 bit wrapped into 64 bit */ 514 ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data); 515} 516 517static void 518ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, 519 void *user_ptr) 520{ 521 /* frame is 32 bit wrapped into 64 bit */ 522 ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000, 523 FALSE, (uint32_t) (uintptr_t) user_ptr); 524} 525 526Bool 527ms_vblank_screen_init(ScreenPtr screen) 528{ 529 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 530 modesettingPtr ms = modesettingPTR(scrn); 531 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 532 xorg_list_init(&ms_drm_queue); 533 534 ms->event_context.version = 4; 535 ms->event_context.vblank_handler = ms_drm_handler; 536 ms->event_context.page_flip_handler = ms_drm_handler; 537 ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit; 538 539 /* We need to re-register the DRM fd for the synchronisation 540 * feedback on every server generation, so perform the 541 * registration within ScreenInit and not PreInit. 542 */ 543 if (ms_ent->fd_wakeup_registered != serverGeneration) { 544 SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen); 545 ms_ent->fd_wakeup_registered = serverGeneration; 546 ms_ent->fd_wakeup_ref = 1; 547 } else 548 ms_ent->fd_wakeup_ref++; 549 550 return TRUE; 551} 552 553void 554ms_vblank_close_screen(ScreenPtr screen) 555{ 556 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 557 modesettingPtr ms = modesettingPTR(scrn); 558 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 559 560 ms_drm_abort_scrn(scrn); 561 562 if (ms_ent->fd_wakeup_registered == serverGeneration && 563 !--ms_ent->fd_wakeup_ref) { 564 RemoveNotifyFd(ms->fd); 565 } 566} 567