present.c revision 2c83f951
1/* 2 * Copyright © 2014 Intel Corporation 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#ifdef HAVE_DIX_CONFIG_H 24#include "dix-config.h" 25#endif 26 27#include <assert.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <poll.h> 31#include <unistd.h> 32#include <stdio.h> 33#include <stdint.h> 34#include <string.h> 35#include <sys/ioctl.h> 36#include <sys/time.h> 37#include <sys/types.h> 38#include <time.h> 39 40#include <xf86.h> 41#include <xf86Crtc.h> 42#include <xf86drm.h> 43#include <xf86str.h> 44#include <present.h> 45 46#include "driver.h" 47#include "drmmode_display.h" 48 49#if 0 50#define DebugPresent(x) ErrorF x 51#else 52#define DebugPresent(x) 53#endif 54 55struct ms_present_vblank_event { 56 uint64_t event_id; 57}; 58 59static RRCrtcPtr 60ms_present_get_crtc(WindowPtr window) 61{ 62 xf86CrtcPtr xf86_crtc = ms_dri2_crtc_covering_drawable(&window->drawable); 63 return xf86_crtc ? xf86_crtc->randr_crtc : NULL; 64} 65 66static int 67ms_present_get_ust_msc(RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc) 68{ 69 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 70 71 return ms_get_crtc_ust_msc(xf86_crtc, ust, msc); 72} 73 74/* 75 * Flush the DRM event queue when full; makes space for new events. 76 * 77 * Returns a negative value on error, 0 if there was nothing to process, 78 * or 1 if we handled any events. 79 */ 80static int 81ms_flush_drm_events(ScreenPtr screen) 82{ 83 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 84 modesettingPtr ms = modesettingPTR(scrn); 85 86 struct pollfd p = { .fd = ms->fd, .events = POLLIN }; 87 int r; 88 89 do { 90 r = poll(&p, 1, 0); 91 } while (r == -1 && (errno == EINTR || errno == EAGAIN)); 92 93 /* If there was an error, r will be < 0. Return that. If there was 94 * nothing to process, r == 0. Return that. 95 */ 96 if (r <= 0) 97 return r; 98 99 /* Try to handle the event. If there was an error, return it. */ 100 r = drmHandleEvent(ms->fd, &ms->event_context); 101 if (r < 0) 102 return r; 103 104 /* Otherwise return 1 to indicate that we handled an event. */ 105 return 1; 106} 107 108/* 109 * Called when the queued vblank event has occurred 110 */ 111static void 112ms_present_vblank_handler(uint64_t msc, uint64_t usec, void *data) 113{ 114 struct ms_present_vblank_event *event = data; 115 116 DebugPresent(("\t\tmh %lld msc %llu\n", 117 (long long) event->event_id, (long long) msc)); 118 119 present_event_notify(event->event_id, usec, msc); 120 free(event); 121} 122 123/* 124 * Called when the queued vblank is aborted 125 */ 126static void 127ms_present_vblank_abort(void *data) 128{ 129 struct ms_present_vblank_event *event = data; 130 131 DebugPresent(("\t\tma %lld\n", (long long) event->event_id)); 132 133 free(event); 134} 135 136/* 137 * Queue an event to report back to the Present extension when the specified 138 * MSC has past 139 */ 140static int 141ms_present_queue_vblank(RRCrtcPtr crtc, 142 uint64_t event_id, 143 uint64_t msc) 144{ 145 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 146 ScreenPtr screen = crtc->pScreen; 147 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 148 modesettingPtr ms = modesettingPTR(scrn); 149 drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; 150 struct ms_present_vblank_event *event; 151 drmVBlank vbl; 152 int ret; 153 uint32_t seq; 154 155 event = calloc(sizeof(struct ms_present_vblank_event), 1); 156 if (!event) 157 return BadAlloc; 158 event->event_id = event_id; 159 seq = ms_drm_queue_alloc(xf86_crtc, event, 160 ms_present_vblank_handler, 161 ms_present_vblank_abort); 162 if (!seq) { 163 free(event); 164 return BadAlloc; 165 } 166 167 vbl.request.type = 168 DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe; 169 vbl.request.sequence = ms_crtc_msc_to_kernel_msc(xf86_crtc, msc); 170 vbl.request.signal = seq; 171 for (;;) { 172 ret = drmWaitVBlank(ms->fd, &vbl); 173 if (!ret) 174 break; 175 /* If we hit EBUSY, then try to flush events. If we can't, then 176 * this is an error. 177 */ 178 if (errno != EBUSY || ms_flush_drm_events(screen) < 0) { 179 ms_drm_abort_seq(scrn, seq); 180 return BadAlloc; 181 } 182 } 183 DebugPresent(("\t\tmq %lld seq %u msc %llu (hw msc %u)\n", 184 (long long) event_id, seq, (long long) msc, 185 vbl.request.sequence)); 186 return Success; 187} 188 189static Bool 190ms_present_event_match(void *data, void *match_data) 191{ 192 struct ms_present_vblank_event *event = data; 193 uint64_t *match = match_data; 194 195 return *match == event->event_id; 196} 197 198/* 199 * Remove a pending vblank event from the DRM queue so that it is not reported 200 * to the extension 201 */ 202static void 203ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) 204{ 205 ScreenPtr screen = crtc->pScreen; 206 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 207 208 ms_drm_abort(scrn, ms_present_event_match, &event_id); 209} 210 211/* 212 * Flush our batch buffer when requested by the Present extension. 213 */ 214static void 215ms_present_flush(WindowPtr window) 216{ 217#ifdef GLAMOR 218 ScreenPtr screen = window->drawable.pScreen; 219 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 220 modesettingPtr ms = modesettingPTR(scrn); 221 222 if (ms->drmmode.glamor) 223 glamor_block_handler(screen); 224#endif 225} 226 227#ifdef GLAMOR 228 229/* 230 * Event data for an in progress flip. 231 * This contains a pointer to the vblank event, 232 * and information about the flip in progress. 233 * a reference to this is stored in the per-crtc 234 * flips. 235 */ 236struct ms_flipdata { 237 ScreenPtr screen; 238 struct ms_present_vblank_event *event; 239 /* number of CRTC events referencing this */ 240 int flip_count; 241 uint64_t fe_msc; 242 uint64_t fe_usec; 243 uint32_t old_fb_id; 244}; 245 246/* 247 * Per crtc pageflipping infomation, 248 * These are submitted to the queuing code 249 * one of them per crtc per flip. 250 */ 251struct ms_crtc_pageflip { 252 Bool on_reference_crtc; 253 /* reference to the ms_flipdata */ 254 struct ms_flipdata *flipdata; 255}; 256 257/** 258 * Free an ms_crtc_pageflip. 259 * 260 * Drops the reference count on the flipdata. 261 */ 262static void 263ms_present_flip_free(struct ms_crtc_pageflip *flip) 264{ 265 struct ms_flipdata *flipdata = flip->flipdata; 266 267 free(flip); 268 if (--flipdata->flip_count > 0) 269 return; 270 free(flipdata); 271} 272 273/** 274 * Callback for the DRM event queue when a single flip has completed 275 * 276 * Once the flip has been completed on all pipes, notify the 277 * extension code telling it when that happened 278 */ 279static void 280ms_flip_handler(uint64_t msc, uint64_t ust, void *data) 281{ 282 struct ms_crtc_pageflip *flip = data; 283 ScreenPtr screen = flip->flipdata->screen; 284 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 285 modesettingPtr ms = modesettingPTR(scrn); 286 struct ms_flipdata *flipdata = flip->flipdata; 287 288 DebugPresent(("\t\tms:fh %lld c %d msc %llu ust %llu\n", 289 (long long) flipdata->event->event_id, 290 flipdata->flip_count, 291 (long long) msc, (long long) ust)); 292 293 if (flip->on_reference_crtc) { 294 flipdata->fe_msc = msc; 295 flipdata->fe_usec = ust; 296 } 297 298 if (flipdata->flip_count == 1) { 299 DebugPresent(("\t\tms:fc %lld c %d msc %llu ust %llu\n", 300 (long long) flipdata->event->event_id, 301 flipdata->flip_count, 302 (long long) flipdata->fe_msc, (long long) flipdata->fe_usec)); 303 304 305 ms_present_vblank_handler(flipdata->fe_msc, 306 flipdata->fe_usec, 307 flipdata->event); 308 309 drmModeRmFB(ms->fd, flipdata->old_fb_id); 310 } 311 ms_present_flip_free(flip); 312} 313 314/* 315 * Callback for the DRM queue abort code. A flip has been aborted. 316 */ 317static void 318ms_present_flip_abort(void *data) 319{ 320 struct ms_crtc_pageflip *flip = data; 321 struct ms_flipdata *flipdata = flip->flipdata; 322 323 DebugPresent(("\t\tms:fa %lld c %d\n", (long long) flipdata->event->event_id, flipdata->flip_count)); 324 325 if (flipdata->flip_count == 1) 326 free(flipdata->event); 327 328 ms_present_flip_free(flip); 329} 330 331static Bool 332queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, 333 struct ms_flipdata *flipdata, 334 int ref_crtc_vblank_pipe, uint32_t flags) 335{ 336 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 337 modesettingPtr ms = modesettingPTR(scrn); 338 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 339 struct ms_crtc_pageflip *flip; 340 uint32_t seq; 341 int err; 342 343 flip = calloc(1, sizeof(struct ms_crtc_pageflip)); 344 if (flip == NULL) { 345 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 346 "flip queue: carrier alloc failed.\n"); 347 return FALSE; 348 } 349 350 /* Only the reference crtc will finally deliver its page flip 351 * completion event. All other crtc's events will be discarded. 352 */ 353 flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe); 354 flip->flipdata = flipdata; 355 356 seq = ms_drm_queue_alloc(crtc, flip, ms_flip_handler, ms_present_flip_abort); 357 if (!seq) { 358 free(flip); 359 return FALSE; 360 } 361 362 DebugPresent(("\t\tms:fq %lld c %d -> %d seq %llu\n", 363 (long long) flipdata->event->event_id, 364 flipdata->flip_count, flipdata->flip_count + 1, 365 (long long) seq)); 366 367 /* take a reference on flipdata for use in flip */ 368 flipdata->flip_count++; 369 370 while (drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id, 371 ms->drmmode.fb_id, flags, (void *) (uintptr_t) seq)) { 372 err = errno; 373 /* We may have failed because the event queue was full. Flush it 374 * and retry. If there was nothing to flush, then we failed for 375 * some other reason and should just return an error. 376 */ 377 if (ms_flush_drm_events(screen) <= 0) { 378 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 379 "flip queue failed: %s\n", strerror(err)); 380 /* Aborting will also decrement flip_count and free(flip). */ 381 ms_drm_abort_seq(scrn, seq); 382 return FALSE; 383 } 384 385 /* We flushed some events, so try again. */ 386 xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n"); 387 } 388 389 /* The page flip succeded. */ 390 return TRUE; 391} 392 393 394static Bool 395ms_do_pageflip(ScreenPtr screen, 396 PixmapPtr new_front, 397 struct ms_present_vblank_event *event, 398 int ref_crtc_vblank_pipe, 399 Bool async) 400{ 401#ifndef GLAMOR_HAS_GBM 402 return FALSE; 403#else 404 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 405 modesettingPtr ms = modesettingPTR(scrn); 406 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 407 drmmode_bo new_front_bo; 408 uint32_t flags; 409 int i; 410 struct ms_flipdata *flipdata; 411 glamor_block_handler(screen); 412 413 new_front_bo.gbm = glamor_gbm_bo_from_pixmap(screen, new_front); 414 new_front_bo.dumb = NULL; 415 if (!new_front_bo.gbm) { 416 xf86DrvMsg(scrn->scrnIndex, X_ERROR, 417 "Failed to get GBM bo for flip to new front.\n"); 418 return FALSE; 419 } 420 421 flipdata = calloc(1, sizeof(struct ms_flipdata)); 422 if (!flipdata) { 423 drmmode_bo_destroy(&ms->drmmode, &new_front_bo); 424 xf86DrvMsg(scrn->scrnIndex, X_ERROR, 425 "Failed to allocate flipdata.\n"); 426 return FALSE; 427 } 428 429 flipdata->event = event; 430 flipdata->screen = screen; 431 432 /* 433 * Take a local reference on flipdata. 434 * if the first flip fails, the sequence abort 435 * code will free the crtc flip data, and drop 436 * it's reference which would cause this to be 437 * freed when we still required it. 438 */ 439 flipdata->flip_count++; 440 441 /* Create a new handle for the back buffer */ 442 flipdata->old_fb_id = ms->drmmode.fb_id; 443 if (drmModeAddFB(ms->fd, scrn->virtualX, scrn->virtualY, 444 scrn->depth, scrn->bitsPerPixel, 445 drmmode_bo_get_pitch(&new_front_bo), 446 drmmode_bo_get_handle(&new_front_bo), &ms->drmmode.fb_id)) { 447 goto error_out; 448 } 449 450 drmmode_bo_destroy(&ms->drmmode, &new_front_bo); 451 452 flags = DRM_MODE_PAGE_FLIP_EVENT; 453 if (async) 454 flags |= DRM_MODE_PAGE_FLIP_ASYNC; 455 456 /* Queue flips on all enabled CRTCs. 457 * 458 * Note that if/when we get per-CRTC buffers, we'll have to update this. 459 * Right now it assumes a single shared fb across all CRTCs, with the 460 * kernel fixing up the offset of each CRTC as necessary. 461 * 462 * Also, flips queued on disabled or incorrectly configured displays 463 * may never complete; this is a configuration error. 464 */ 465 for (i = 0; i < config->num_crtc; i++) { 466 xf86CrtcPtr crtc = config->crtc[i]; 467 468 if (!ms_crtc_on(crtc)) 469 continue; 470 471 if (!queue_flip_on_crtc(screen, crtc, flipdata, 472 ref_crtc_vblank_pipe, 473 flags)) { 474 goto error_undo; 475 } 476 } 477 478 /* 479 * Do we have more than our local reference, 480 * if so and no errors, then drop our local 481 * reference and return now. 482 */ 483 if (flipdata->flip_count > 1) { 484 flipdata->flip_count--; 485 return TRUE; 486 } 487 488error_undo: 489 490 /* 491 * Have we just got the local reference? 492 * free the framebuffer if so since nobody successfully 493 * submitted anything 494 */ 495 if (flipdata->flip_count == 1) { 496 drmModeRmFB(ms->fd, ms->drmmode.fb_id); 497 ms->drmmode.fb_id = flipdata->old_fb_id; 498 } 499 500error_out: 501 xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", 502 strerror(errno)); 503 /* if only the local reference - free the structure, 504 * else drop the local reference and return */ 505 if (flipdata->flip_count == 1) 506 free(flipdata); 507 else 508 flipdata->flip_count--; 509 510 return FALSE; 511#endif /* GLAMOR_HAS_GBM */ 512} 513 514/* 515 * Test to see if page flipping is possible on the target crtc 516 */ 517static Bool 518ms_present_check_flip(RRCrtcPtr crtc, 519 WindowPtr window, 520 PixmapPtr pixmap, 521 Bool sync_flip) 522{ 523 ScreenPtr screen = window->drawable.pScreen; 524 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 525 modesettingPtr ms = modesettingPTR(scrn); 526 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 527 int num_crtcs_on = 0; 528 int i; 529 530 if (!ms->drmmode.pageflip) 531 return FALSE; 532 533 if (!scrn->vtSema) 534 return FALSE; 535 536 for (i = 0; i < config->num_crtc; i++) { 537 drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private; 538 539 /* Don't do pageflipping if CRTCs are rotated. */ 540#ifdef GLAMOR_HAS_GBM 541 if (drmmode_crtc->rotate_bo.gbm) 542 return FALSE; 543#endif 544 545 if (ms_crtc_on(config->crtc[i])) 546 num_crtcs_on++; 547 } 548 549 /* We can't do pageflipping if all the CRTCs are off. */ 550 if (num_crtcs_on == 0) 551 return FALSE; 552 553 /* Check stride, can't change that on flip */ 554 if (pixmap->devKind != drmmode_bo_get_pitch(&ms->drmmode.front_bo)) 555 return FALSE; 556 557 /* Make sure there's a bo we can get to */ 558 /* XXX: actually do this. also...is it sufficient? 559 * if (!glamor_get_pixmap_private(pixmap)) 560 * return FALSE; 561 */ 562 563 return TRUE; 564} 565 566/* 567 * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true, 568 * then wait for vblank. Otherwise, flip immediately 569 */ 570static Bool 571ms_present_flip(RRCrtcPtr crtc, 572 uint64_t event_id, 573 uint64_t target_msc, 574 PixmapPtr pixmap, 575 Bool sync_flip) 576{ 577 ScreenPtr screen = crtc->pScreen; 578 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 579 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 580 drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; 581 Bool ret; 582 struct ms_present_vblank_event *event; 583 584 if (!ms_present_check_flip(crtc, screen->root, pixmap, sync_flip)) 585 return FALSE; 586 587 event = calloc(1, sizeof(struct ms_present_vblank_event)); 588 if (!event) 589 return FALSE; 590 591 event->event_id = event_id; 592 ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip); 593 if (!ret) 594 xf86DrvMsg(scrn->scrnIndex, X_ERROR, "present flip failed\n"); 595 596 return ret; 597} 598 599/* 600 * Queue a flip back to the normal frame buffer 601 */ 602static void 603ms_present_unflip(ScreenPtr screen, uint64_t event_id) 604{ 605 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 606 PixmapPtr pixmap = screen->GetScreenPixmap(screen); 607 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 608 int i; 609 struct ms_present_vblank_event *event; 610 611 event = calloc(1, sizeof(struct ms_present_vblank_event)); 612 if (!event) 613 return; 614 615 event->event_id = event_id; 616 617 if (ms_present_check_flip(NULL, screen->root, pixmap, TRUE) && 618 ms_do_pageflip(screen, pixmap, event, -1, FALSE)) { 619 return; 620 } 621 622 for (i = 0; i < config->num_crtc; i++) { 623 xf86CrtcPtr crtc = config->crtc[i]; 624 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 625 626 if (!crtc->enabled) 627 continue; 628 629 /* info->drmmode.fb_id still points to the FB for the last flipped BO. 630 * Clear it, drmmode_set_mode_major will re-create it 631 */ 632 if (drmmode_crtc->drmmode->fb_id) { 633 drmModeRmFB(drmmode_crtc->drmmode->fd, 634 drmmode_crtc->drmmode->fb_id); 635 drmmode_crtc->drmmode->fb_id = 0; 636 } 637 638 if (drmmode_crtc->dpms_mode == DPMSModeOn) 639 crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation, 640 crtc->x, crtc->y); 641 else 642 drmmode_crtc->need_modeset = TRUE; 643 } 644 645 present_event_notify(event_id, 0, 0); 646} 647#endif 648 649static present_screen_info_rec ms_present_screen_info = { 650 .version = PRESENT_SCREEN_INFO_VERSION, 651 652 .get_crtc = ms_present_get_crtc, 653 .get_ust_msc = ms_present_get_ust_msc, 654 .queue_vblank = ms_present_queue_vblank, 655 .abort_vblank = ms_present_abort_vblank, 656 .flush = ms_present_flush, 657 658 .capabilities = PresentCapabilityNone, 659#ifdef GLAMOR 660 .check_flip = ms_present_check_flip, 661 .flip = ms_present_flip, 662 .unflip = ms_present_unflip, 663#endif 664}; 665 666Bool 667ms_present_screen_init(ScreenPtr screen) 668{ 669 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 670 modesettingPtr ms = modesettingPTR(scrn); 671 uint64_t value; 672 int ret; 673 674 ret = drmGetCap(ms->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value); 675 if (ret == 0 && value == 1) 676 ms_present_screen_info.capabilities |= PresentCapabilityAsync; 677 678 return present_screen_init(screen, &ms_present_screen_info); 679} 680