modeset.c revision c9febff31f1032065f96ad76fd31f31ac330fef9
1/* 2 * Mesa 3-D graphics library 3 * Version: 7.9 4 * 5 * Copyright (C) 2010 LunarG Inc. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Authors: 26 * Chia-I Wu <olv@lunarg.com> 27 */ 28 29#include "util/u_memory.h" 30#include "util/u_inlines.h" 31#include "egllog.h" 32 33#include "native_drm.h" 34 35static boolean 36drm_surface_validate(struct native_surface *nsurf, uint attachment_mask, 37 unsigned int *seq_num, struct pipe_resource **textures, 38 int *width, int *height) 39{ 40 struct drm_surface *drmsurf = drm_surface(nsurf); 41 42 if (!resource_surface_add_resources(drmsurf->rsurf, attachment_mask)) 43 return FALSE; 44 if (textures) 45 resource_surface_get_resources(drmsurf->rsurf, textures, attachment_mask); 46 47 if (seq_num) 48 *seq_num = drmsurf->sequence_number; 49 if (width) 50 *width = drmsurf->width; 51 if (height) 52 *height = drmsurf->height; 53 54 return TRUE; 55} 56 57/** 58 * Add textures as DRM framebuffers. 59 */ 60static boolean 61drm_surface_init_framebuffers(struct native_surface *nsurf, boolean need_back) 62{ 63 struct drm_surface *drmsurf = drm_surface(nsurf); 64 struct drm_display *drmdpy = drmsurf->drmdpy; 65 int num_framebuffers = (need_back) ? 2 : 1; 66 int i, err; 67 68 for (i = 0; i < num_framebuffers; i++) { 69 struct drm_framebuffer *fb; 70 enum native_attachment natt; 71 struct winsys_handle whandle; 72 uint block_bits; 73 74 if (i == 0) { 75 fb = &drmsurf->front_fb; 76 natt = NATIVE_ATTACHMENT_FRONT_LEFT; 77 } 78 else { 79 fb = &drmsurf->back_fb; 80 natt = NATIVE_ATTACHMENT_BACK_LEFT; 81 } 82 83 if (!fb->texture) { 84 /* make sure the texture has been allocated */ 85 resource_surface_add_resources(drmsurf->rsurf, 1 << natt); 86 fb->texture = 87 resource_surface_get_single_resource(drmsurf->rsurf, natt); 88 if (!fb->texture) 89 return FALSE; 90 } 91 92 /* already initialized */ 93 if (fb->buffer_id) 94 continue; 95 96 /* TODO detect the real value */ 97 fb->is_passive = TRUE; 98 99 memset(&whandle, 0, sizeof(whandle)); 100 whandle.type = DRM_API_HANDLE_TYPE_KMS; 101 102 if (!drmdpy->base.screen->resource_get_handle(drmdpy->base.screen, 103 fb->texture, &whandle)) 104 return FALSE; 105 106 block_bits = util_format_get_blocksizebits(drmsurf->color_format); 107 err = drmModeAddFB(drmdpy->fd, drmsurf->width, drmsurf->height, 108 block_bits, block_bits, whandle.stride, whandle.handle, 109 &fb->buffer_id); 110 if (err) { 111 fb->buffer_id = 0; 112 return FALSE; 113 } 114 } 115 116 return TRUE; 117} 118 119static boolean 120drm_surface_flush_frontbuffer(struct native_surface *nsurf) 121{ 122#ifdef DRM_MODE_FEATURE_DIRTYFB 123 struct drm_surface *drmsurf = drm_surface(nsurf); 124 struct drm_display *drmdpy = drmsurf->drmdpy; 125 126 if (drmsurf->front_fb.is_passive) 127 drmModeDirtyFB(drmdpy->fd, drmsurf->front_fb.buffer_id, NULL, 0); 128#endif 129 130 return TRUE; 131} 132 133static boolean 134drm_surface_copy_swap(struct native_surface *nsurf) 135{ 136 struct drm_surface *drmsurf = drm_surface(nsurf); 137 struct drm_display *drmdpy = drmsurf->drmdpy; 138 139 if (!resource_surface_copy_swap(drmsurf->rsurf, &drmdpy->base) || 140 !drm_surface_flush_frontbuffer(nsurf)) 141 return FALSE; 142 143 drmsurf->sequence_number++; 144 145 return TRUE; 146} 147 148static boolean 149drm_surface_swap_buffers(struct native_surface *nsurf) 150{ 151 struct drm_surface *drmsurf = drm_surface(nsurf); 152 struct drm_crtc *drmcrtc = &drmsurf->current_crtc; 153 struct drm_display *drmdpy = drmsurf->drmdpy; 154 struct drm_framebuffer tmp_fb; 155 int err; 156 157 if (!drmsurf->have_pageflip) 158 return drm_surface_copy_swap(nsurf); 159 160 if (!drmsurf->back_fb.buffer_id) { 161 if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE)) 162 return FALSE; 163 } 164 165 if (drmsurf->is_shown && drmcrtc->crtc) { 166 err = drmModePageFlip(drmdpy->fd, drmcrtc->crtc->crtc_id, 167 drmsurf->back_fb.buffer_id, 0, NULL); 168 if (err) { 169 drmsurf->have_pageflip = FALSE; 170 return drm_surface_copy_swap(nsurf); 171 } 172 } 173 174 /* swap the buffers */ 175 tmp_fb = drmsurf->front_fb; 176 drmsurf->front_fb = drmsurf->back_fb; 177 drmsurf->back_fb = tmp_fb; 178 179 resource_surface_swap_buffers(drmsurf->rsurf, 180 NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE); 181 /* the front/back textures are swapped */ 182 drmsurf->sequence_number++; 183 drmdpy->event_handler->invalid_surface(&drmdpy->base, 184 &drmsurf->base, drmsurf->sequence_number); 185 186 return TRUE; 187} 188 189static boolean 190drm_surface_present(struct native_surface *nsurf, 191 enum native_attachment natt, 192 boolean preserve, 193 uint swap_interval) 194{ 195 boolean ret; 196 197 if (swap_interval) 198 return FALSE; 199 200 switch (natt) { 201 case NATIVE_ATTACHMENT_FRONT_LEFT: 202 ret = drm_surface_flush_frontbuffer(nsurf); 203 break; 204 case NATIVE_ATTACHMENT_BACK_LEFT: 205 if (preserve) 206 ret = drm_surface_copy_swap(nsurf); 207 else 208 ret = drm_surface_swap_buffers(nsurf); 209 break; 210 default: 211 ret = FALSE; 212 break; 213 } 214 215 return ret; 216} 217 218static void 219drm_surface_wait(struct native_surface *nsurf) 220{ 221 /* no-op */ 222} 223 224static void 225drm_surface_destroy(struct native_surface *nsurf) 226{ 227 struct drm_surface *drmsurf = drm_surface(nsurf); 228 229 if (drmsurf->current_crtc.crtc) 230 drmModeFreeCrtc(drmsurf->current_crtc.crtc); 231 232 if (drmsurf->front_fb.buffer_id) 233 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id); 234 pipe_resource_reference(&drmsurf->front_fb.texture, NULL); 235 236 if (drmsurf->back_fb.buffer_id) 237 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id); 238 pipe_resource_reference(&drmsurf->back_fb.texture, NULL); 239 240 resource_surface_destroy(drmsurf->rsurf); 241 FREE(drmsurf); 242} 243 244static struct drm_surface * 245drm_display_create_surface(struct native_display *ndpy, 246 const struct native_config *nconf, 247 uint width, uint height) 248{ 249 struct drm_display *drmdpy = drm_display(ndpy); 250 struct drm_config *drmconf = drm_config(nconf); 251 struct drm_surface *drmsurf; 252 253 drmsurf = CALLOC_STRUCT(drm_surface); 254 if (!drmsurf) 255 return NULL; 256 257 drmsurf->drmdpy = drmdpy; 258 drmsurf->color_format = drmconf->base.color_format; 259 drmsurf->width = width; 260 drmsurf->height = height; 261 drmsurf->have_pageflip = TRUE; 262 263 drmsurf->rsurf = resource_surface_create(drmdpy->base.screen, 264 drmsurf->color_format, 265 PIPE_BIND_RENDER_TARGET | 266 PIPE_BIND_SAMPLER_VIEW | 267 PIPE_BIND_DISPLAY_TARGET | 268 PIPE_BIND_SCANOUT); 269 if (!drmsurf->rsurf) { 270 FREE(drmsurf); 271 return NULL; 272 } 273 274 resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height); 275 276 drmsurf->base.destroy = drm_surface_destroy; 277 drmsurf->base.present = drm_surface_present; 278 drmsurf->base.validate = drm_surface_validate; 279 drmsurf->base.wait = drm_surface_wait; 280 281 return drmsurf; 282} 283 284/** 285 * Choose a CRTC that supports all given connectors. 286 */ 287static uint32_t 288drm_display_choose_crtc(struct native_display *ndpy, 289 uint32_t *connectors, int num_connectors) 290{ 291 struct drm_display *drmdpy = drm_display(ndpy); 292 int idx; 293 294 for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) { 295 boolean found_crtc = TRUE; 296 int i, j; 297 298 for (i = 0; i < num_connectors; i++) { 299 drmModeConnectorPtr connector; 300 int encoder_idx = -1; 301 302 connector = drmModeGetConnector(drmdpy->fd, connectors[i]); 303 if (!connector) { 304 found_crtc = FALSE; 305 break; 306 } 307 308 /* find an encoder the CRTC supports */ 309 for (j = 0; j < connector->count_encoders; j++) { 310 drmModeEncoderPtr encoder = 311 drmModeGetEncoder(drmdpy->fd, connector->encoders[j]); 312 if (encoder->possible_crtcs & (1 << idx)) { 313 encoder_idx = j; 314 break; 315 } 316 drmModeFreeEncoder(encoder); 317 } 318 319 drmModeFreeConnector(connector); 320 if (encoder_idx < 0) { 321 found_crtc = FALSE; 322 break; 323 } 324 } 325 326 if (found_crtc) 327 break; 328 } 329 330 if (idx >= drmdpy->resources->count_crtcs) { 331 _eglLog(_EGL_WARNING, 332 "failed to find a CRTC that supports the given %d connectors", 333 num_connectors); 334 return 0; 335 } 336 337 return drmdpy->resources->crtcs[idx]; 338} 339 340/** 341 * Remember the original CRTC status and set the CRTC 342 */ 343static boolean 344drm_display_set_crtc(struct native_display *ndpy, int crtc_idx, 345 uint32_t buffer_id, uint32_t x, uint32_t y, 346 uint32_t *connectors, int num_connectors, 347 drmModeModeInfoPtr mode) 348{ 349 struct drm_display *drmdpy = drm_display(ndpy); 350 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx]; 351 uint32_t crtc_id; 352 int err; 353 354 if (drmcrtc->crtc) { 355 crtc_id = drmcrtc->crtc->crtc_id; 356 } 357 else { 358 int count = 0, i; 359 360 /* 361 * Choose the CRTC once. It could be more dynamic, but let's keep it 362 * simple for now. 363 */ 364 crtc_id = drm_display_choose_crtc(&drmdpy->base, 365 connectors, num_connectors); 366 367 /* save the original CRTC status */ 368 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 369 if (!drmcrtc->crtc) 370 return FALSE; 371 372 for (i = 0; i < drmdpy->num_connectors; i++) { 373 struct drm_connector *drmconn = &drmdpy->connectors[i]; 374 drmModeConnectorPtr connector = drmconn->connector; 375 drmModeEncoderPtr encoder; 376 377 encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id); 378 if (encoder) { 379 if (encoder->crtc_id == crtc_id) { 380 drmcrtc->connectors[count++] = connector->connector_id; 381 if (count >= Elements(drmcrtc->connectors)) 382 break; 383 } 384 drmModeFreeEncoder(encoder); 385 } 386 } 387 388 drmcrtc->num_connectors = count; 389 } 390 391 err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y, 392 connectors, num_connectors, mode); 393 if (err) { 394 drmModeFreeCrtc(drmcrtc->crtc); 395 drmcrtc->crtc = NULL; 396 drmcrtc->num_connectors = 0; 397 398 return FALSE; 399 } 400 401 return TRUE; 402} 403 404static boolean 405drm_display_program(struct native_display *ndpy, int crtc_idx, 406 struct native_surface *nsurf, uint x, uint y, 407 const struct native_connector **nconns, int num_nconns, 408 const struct native_mode *nmode) 409{ 410 struct drm_display *drmdpy = drm_display(ndpy); 411 struct drm_surface *drmsurf = drm_surface(nsurf); 412 const struct drm_mode *drmmode = drm_mode(nmode); 413 uint32_t connector_ids[32]; 414 uint32_t buffer_id; 415 drmModeModeInfo mode_tmp, *mode; 416 int i; 417 418 if (num_nconns > Elements(connector_ids)) { 419 _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns); 420 num_nconns = Elements(connector_ids); 421 } 422 423 if (drmsurf) { 424 if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE)) 425 return FALSE; 426 427 buffer_id = drmsurf->front_fb.buffer_id; 428 /* the mode argument of drmModeSetCrtc is not constified */ 429 mode_tmp = drmmode->mode; 430 mode = &mode_tmp; 431 } 432 else { 433 /* disable the CRTC */ 434 buffer_id = 0; 435 mode = NULL; 436 num_nconns = 0; 437 } 438 439 for (i = 0; i < num_nconns; i++) { 440 struct drm_connector *drmconn = drm_connector(nconns[i]); 441 connector_ids[i] = drmconn->connector->connector_id; 442 } 443 444 if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y, 445 connector_ids, num_nconns, mode)) { 446 _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx); 447 448 return FALSE; 449 } 450 451 if (drmdpy->shown_surfaces[crtc_idx]) 452 drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE; 453 drmdpy->shown_surfaces[crtc_idx] = drmsurf; 454 455 /* remember the settings for buffer swapping */ 456 if (drmsurf) { 457 uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id; 458 struct drm_crtc *drmcrtc = &drmsurf->current_crtc; 459 460 if (drmcrtc->crtc) 461 drmModeFreeCrtc(drmcrtc->crtc); 462 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 463 464 assert(num_nconns < Elements(drmcrtc->connectors)); 465 memcpy(drmcrtc->connectors, connector_ids, 466 sizeof(*connector_ids) * num_nconns); 467 drmcrtc->num_connectors = num_nconns; 468 469 drmsurf->is_shown = TRUE; 470 } 471 472 return TRUE; 473} 474 475static const struct native_mode ** 476drm_display_get_modes(struct native_display *ndpy, 477 const struct native_connector *nconn, 478 int *num_modes) 479{ 480 struct drm_display *drmdpy = drm_display(ndpy); 481 struct drm_connector *drmconn = drm_connector(nconn); 482 const struct native_mode **nmodes_return; 483 int count, i; 484 485 /* delete old data */ 486 if (drmconn->connector) { 487 drmModeFreeConnector(drmconn->connector); 488 FREE(drmconn->drm_modes); 489 490 drmconn->connector = NULL; 491 drmconn->drm_modes = NULL; 492 drmconn->num_modes = 0; 493 } 494 495 /* detect again */ 496 drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id); 497 if (!drmconn->connector) 498 return NULL; 499 500 count = drmconn->connector->count_modes; 501 drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes)); 502 if (!drmconn->drm_modes) { 503 drmModeFreeConnector(drmconn->connector); 504 drmconn->connector = NULL; 505 506 return NULL; 507 } 508 509 for (i = 0; i < count; i++) { 510 struct drm_mode *drmmode = &drmconn->drm_modes[i]; 511 drmModeModeInfoPtr mode = &drmconn->connector->modes[i]; 512 513 drmmode->mode = *mode; 514 515 drmmode->base.desc = drmmode->mode.name; 516 drmmode->base.width = drmmode->mode.hdisplay; 517 drmmode->base.height = drmmode->mode.vdisplay; 518 drmmode->base.refresh_rate = drmmode->mode.vrefresh; 519 /* not all kernels have vrefresh = refresh_rate * 1000 */ 520 if (drmmode->base.refresh_rate < 1000) 521 drmmode->base.refresh_rate *= 1000; 522 } 523 524 nmodes_return = MALLOC(count * sizeof(*nmodes_return)); 525 if (nmodes_return) { 526 for (i = 0; i < count; i++) 527 nmodes_return[i] = &drmconn->drm_modes[i].base; 528 if (num_modes) 529 *num_modes = count; 530 } 531 532 return nmodes_return; 533} 534 535static const struct native_connector ** 536drm_display_get_connectors(struct native_display *ndpy, int *num_connectors, 537 int *num_crtc) 538{ 539 struct drm_display *drmdpy = drm_display(ndpy); 540 const struct native_connector **connectors; 541 int i; 542 543 if (!drmdpy->connectors) { 544 drmdpy->connectors = 545 CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors)); 546 if (!drmdpy->connectors) 547 return NULL; 548 549 for (i = 0; i < drmdpy->resources->count_connectors; i++) { 550 struct drm_connector *drmconn = &drmdpy->connectors[i]; 551 552 drmconn->connector_id = drmdpy->resources->connectors[i]; 553 /* drmconn->connector is allocated when the modes are asked */ 554 } 555 556 drmdpy->num_connectors = drmdpy->resources->count_connectors; 557 } 558 559 connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors)); 560 if (connectors) { 561 for (i = 0; i < drmdpy->num_connectors; i++) 562 connectors[i] = &drmdpy->connectors[i].base; 563 if (num_connectors) 564 *num_connectors = drmdpy->num_connectors; 565 } 566 567 if (num_crtc) 568 *num_crtc = drmdpy->resources->count_crtcs; 569 570 return connectors; 571} 572 573static struct native_surface * 574drm_display_create_scanout_surface(struct native_display *ndpy, 575 const struct native_config *nconf, 576 uint width, uint height) 577{ 578 struct drm_surface *drmsurf; 579 580 drmsurf = drm_display_create_surface(ndpy, nconf, width, height); 581 return &drmsurf->base; 582} 583 584static struct native_display_modeset drm_display_modeset = { 585 .get_connectors = drm_display_get_connectors, 586 .get_modes = drm_display_get_modes, 587 .create_scanout_surface = drm_display_create_scanout_surface, 588 .program = drm_display_program 589}; 590 591void 592drm_display_fini_modeset(struct native_display *ndpy) 593{ 594 struct drm_display *drmdpy = drm_display(ndpy); 595 int i; 596 597 if (drmdpy->connectors) { 598 for (i = 0; i < drmdpy->num_connectors; i++) { 599 struct drm_connector *drmconn = &drmdpy->connectors[i]; 600 if (drmconn->connector) { 601 drmModeFreeConnector(drmconn->connector); 602 FREE(drmconn->drm_modes); 603 } 604 } 605 FREE(drmdpy->connectors); 606 } 607 608 if (drmdpy->shown_surfaces) { 609 FREE(drmdpy->shown_surfaces); 610 drmdpy->shown_surfaces = NULL; 611 } 612 613 if (drmdpy->saved_crtcs) { 614 for (i = 0; i < drmdpy->resources->count_crtcs; i++) { 615 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i]; 616 617 if (drmcrtc->crtc) { 618 /* restore crtc */ 619 drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id, 620 drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y, 621 drmcrtc->connectors, drmcrtc->num_connectors, 622 &drmcrtc->crtc->mode); 623 624 drmModeFreeCrtc(drmcrtc->crtc); 625 } 626 } 627 FREE(drmdpy->saved_crtcs); 628 } 629 630 if (drmdpy->resources) { 631 drmModeFreeResources(drmdpy->resources); 632 drmdpy->resources = NULL; 633 } 634 635 drmdpy->base.modeset = NULL; 636} 637 638boolean 639drm_display_init_modeset(struct native_display *ndpy) 640{ 641 struct drm_display *drmdpy = drm_display(ndpy); 642 643 /* resources are fixed, unlike crtc, connector, or encoder */ 644 drmdpy->resources = drmModeGetResources(drmdpy->fd); 645 if (!drmdpy->resources) { 646 _eglLog(_EGL_DEBUG, "Failed to get KMS resources. Disable modeset."); 647 return FALSE; 648 } 649 650 drmdpy->saved_crtcs = 651 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs)); 652 if (!drmdpy->saved_crtcs) { 653 drm_display_fini_modeset(&drmdpy->base); 654 return FALSE; 655 } 656 657 drmdpy->shown_surfaces = 658 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces)); 659 if (!drmdpy->shown_surfaces) { 660 drm_display_fini_modeset(&drmdpy->base); 661 return FALSE; 662 } 663 664 drmdpy->base.modeset = &drm_display_modeset; 665 666 return TRUE; 667} 668