modeset.c revision 94bf657b2390a1cb72d748047e5c7014e4bc1752
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_swap_buffers(struct native_surface *nsurf) 135{ 136 struct drm_surface *drmsurf = drm_surface(nsurf); 137 struct drm_crtc *drmcrtc = &drmsurf->current_crtc; 138 struct drm_display *drmdpy = drmsurf->drmdpy; 139 struct drm_framebuffer tmp_fb; 140 int err; 141 142 if (!drmsurf->back_fb.buffer_id) { 143 if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE)) 144 return FALSE; 145 } 146 147 if (drmsurf->is_shown && drmcrtc->crtc) { 148 err = drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id, 149 drmsurf->back_fb.buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y, 150 drmcrtc->connectors, drmcrtc->num_connectors, &drmcrtc->crtc->mode); 151 if (err) 152 return FALSE; 153 } 154 155 /* swap the buffers */ 156 tmp_fb = drmsurf->front_fb; 157 drmsurf->front_fb = drmsurf->back_fb; 158 drmsurf->back_fb = tmp_fb; 159 160 resource_surface_swap_buffers(drmsurf->rsurf, 161 NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE); 162 /* the front/back textures are swapped */ 163 drmsurf->sequence_number++; 164 drmdpy->event_handler->invalid_surface(&drmdpy->base, 165 &drmsurf->base, drmsurf->sequence_number); 166 167 return TRUE; 168} 169 170static boolean 171drm_surface_present(struct native_surface *nsurf, 172 enum native_attachment natt, 173 boolean preserve, 174 uint swap_interval) 175{ 176 boolean ret; 177 178 if (preserve || swap_interval) 179 return FALSE; 180 181 switch (natt) { 182 case NATIVE_ATTACHMENT_FRONT_LEFT: 183 ret = drm_surface_flush_frontbuffer(nsurf); 184 break; 185 case NATIVE_ATTACHMENT_BACK_LEFT: 186 ret = drm_surface_swap_buffers(nsurf); 187 break; 188 default: 189 ret = FALSE; 190 break; 191 } 192 193 return ret; 194} 195 196static void 197drm_surface_wait(struct native_surface *nsurf) 198{ 199 /* no-op */ 200} 201 202static void 203drm_surface_destroy(struct native_surface *nsurf) 204{ 205 struct drm_surface *drmsurf = drm_surface(nsurf); 206 207 if (drmsurf->current_crtc.crtc) 208 drmModeFreeCrtc(drmsurf->current_crtc.crtc); 209 210 if (drmsurf->front_fb.buffer_id) 211 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id); 212 pipe_resource_reference(&drmsurf->front_fb.texture, NULL); 213 214 if (drmsurf->back_fb.buffer_id) 215 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id); 216 pipe_resource_reference(&drmsurf->back_fb.texture, NULL); 217 218 resource_surface_destroy(drmsurf->rsurf); 219 FREE(drmsurf); 220} 221 222static struct drm_surface * 223drm_display_create_surface(struct native_display *ndpy, 224 const struct native_config *nconf, 225 uint width, uint height) 226{ 227 struct drm_display *drmdpy = drm_display(ndpy); 228 struct drm_config *drmconf = drm_config(nconf); 229 struct drm_surface *drmsurf; 230 231 drmsurf = CALLOC_STRUCT(drm_surface); 232 if (!drmsurf) 233 return NULL; 234 235 drmsurf->drmdpy = drmdpy; 236 drmsurf->color_format = drmconf->base.color_format; 237 drmsurf->width = width; 238 drmsurf->height = height; 239 240 drmsurf->rsurf = resource_surface_create(drmdpy->base.screen, 241 drmsurf->color_format, 242 PIPE_BIND_RENDER_TARGET | 243 PIPE_BIND_SAMPLER_VIEW | 244 PIPE_BIND_DISPLAY_TARGET | 245 PIPE_BIND_SCANOUT); 246 if (!drmsurf->rsurf) { 247 FREE(drmsurf); 248 return NULL; 249 } 250 251 resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height); 252 253 drmsurf->base.destroy = drm_surface_destroy; 254 drmsurf->base.swap_buffers = drm_surface_swap_buffers; 255 drmsurf->base.flush_frontbuffer = drm_surface_flush_frontbuffer; 256 drmsurf->base.present = drm_surface_present; 257 drmsurf->base.validate = drm_surface_validate; 258 drmsurf->base.wait = drm_surface_wait; 259 260 return drmsurf; 261} 262 263/** 264 * Choose a CRTC that supports all given connectors. 265 */ 266static uint32_t 267drm_display_choose_crtc(struct native_display *ndpy, 268 uint32_t *connectors, int num_connectors) 269{ 270 struct drm_display *drmdpy = drm_display(ndpy); 271 int idx; 272 273 for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) { 274 boolean found_crtc = TRUE; 275 int i, j; 276 277 for (i = 0; i < num_connectors; i++) { 278 drmModeConnectorPtr connector; 279 int encoder_idx = -1; 280 281 connector = drmModeGetConnector(drmdpy->fd, connectors[i]); 282 if (!connector) { 283 found_crtc = FALSE; 284 break; 285 } 286 287 /* find an encoder the CRTC supports */ 288 for (j = 0; j < connector->count_encoders; j++) { 289 drmModeEncoderPtr encoder = 290 drmModeGetEncoder(drmdpy->fd, connector->encoders[j]); 291 if (encoder->possible_crtcs & (1 << idx)) { 292 encoder_idx = j; 293 break; 294 } 295 drmModeFreeEncoder(encoder); 296 } 297 298 drmModeFreeConnector(connector); 299 if (encoder_idx < 0) { 300 found_crtc = FALSE; 301 break; 302 } 303 } 304 305 if (found_crtc) 306 break; 307 } 308 309 if (idx >= drmdpy->resources->count_crtcs) { 310 _eglLog(_EGL_WARNING, 311 "failed to find a CRTC that supports the given %d connectors", 312 num_connectors); 313 return 0; 314 } 315 316 return drmdpy->resources->crtcs[idx]; 317} 318 319/** 320 * Remember the original CRTC status and set the CRTC 321 */ 322static boolean 323drm_display_set_crtc(struct native_display *ndpy, int crtc_idx, 324 uint32_t buffer_id, uint32_t x, uint32_t y, 325 uint32_t *connectors, int num_connectors, 326 drmModeModeInfoPtr mode) 327{ 328 struct drm_display *drmdpy = drm_display(ndpy); 329 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx]; 330 uint32_t crtc_id; 331 int err; 332 333 if (drmcrtc->crtc) { 334 crtc_id = drmcrtc->crtc->crtc_id; 335 } 336 else { 337 int count = 0, i; 338 339 /* 340 * Choose the CRTC once. It could be more dynamic, but let's keep it 341 * simple for now. 342 */ 343 crtc_id = drm_display_choose_crtc(&drmdpy->base, 344 connectors, num_connectors); 345 346 /* save the original CRTC status */ 347 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 348 if (!drmcrtc->crtc) 349 return FALSE; 350 351 for (i = 0; i < drmdpy->num_connectors; i++) { 352 struct drm_connector *drmconn = &drmdpy->connectors[i]; 353 drmModeConnectorPtr connector = drmconn->connector; 354 drmModeEncoderPtr encoder; 355 356 encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id); 357 if (encoder) { 358 if (encoder->crtc_id == crtc_id) { 359 drmcrtc->connectors[count++] = connector->connector_id; 360 if (count >= Elements(drmcrtc->connectors)) 361 break; 362 } 363 drmModeFreeEncoder(encoder); 364 } 365 } 366 367 drmcrtc->num_connectors = count; 368 } 369 370 err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y, 371 connectors, num_connectors, mode); 372 if (err) { 373 drmModeFreeCrtc(drmcrtc->crtc); 374 drmcrtc->crtc = NULL; 375 drmcrtc->num_connectors = 0; 376 377 return FALSE; 378 } 379 380 return TRUE; 381} 382 383static boolean 384drm_display_program(struct native_display *ndpy, int crtc_idx, 385 struct native_surface *nsurf, uint x, uint y, 386 const struct native_connector **nconns, int num_nconns, 387 const struct native_mode *nmode) 388{ 389 struct drm_display *drmdpy = drm_display(ndpy); 390 struct drm_surface *drmsurf = drm_surface(nsurf); 391 const struct drm_mode *drmmode = drm_mode(nmode); 392 uint32_t connector_ids[32]; 393 uint32_t buffer_id; 394 drmModeModeInfo mode_tmp, *mode; 395 int i; 396 397 if (num_nconns > Elements(connector_ids)) { 398 _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns); 399 num_nconns = Elements(connector_ids); 400 } 401 402 if (drmsurf) { 403 if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE)) 404 return FALSE; 405 406 buffer_id = drmsurf->front_fb.buffer_id; 407 /* the mode argument of drmModeSetCrtc is not constified */ 408 mode_tmp = drmmode->mode; 409 mode = &mode_tmp; 410 } 411 else { 412 /* disable the CRTC */ 413 buffer_id = 0; 414 mode = NULL; 415 num_nconns = 0; 416 } 417 418 for (i = 0; i < num_nconns; i++) { 419 struct drm_connector *drmconn = drm_connector(nconns[i]); 420 connector_ids[i] = drmconn->connector->connector_id; 421 } 422 423 if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y, 424 connector_ids, num_nconns, mode)) { 425 _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx); 426 427 return FALSE; 428 } 429 430 if (drmdpy->shown_surfaces[crtc_idx]) 431 drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE; 432 drmdpy->shown_surfaces[crtc_idx] = drmsurf; 433 434 /* remember the settings for buffer swapping */ 435 if (drmsurf) { 436 uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id; 437 struct drm_crtc *drmcrtc = &drmsurf->current_crtc; 438 439 if (drmcrtc->crtc) 440 drmModeFreeCrtc(drmcrtc->crtc); 441 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 442 443 assert(num_nconns < Elements(drmcrtc->connectors)); 444 memcpy(drmcrtc->connectors, connector_ids, 445 sizeof(*connector_ids) * num_nconns); 446 drmcrtc->num_connectors = num_nconns; 447 448 drmsurf->is_shown = TRUE; 449 } 450 451 return TRUE; 452} 453 454static const struct native_mode ** 455drm_display_get_modes(struct native_display *ndpy, 456 const struct native_connector *nconn, 457 int *num_modes) 458{ 459 struct drm_display *drmdpy = drm_display(ndpy); 460 struct drm_connector *drmconn = drm_connector(nconn); 461 const struct native_mode **nmodes_return; 462 int count, i; 463 464 /* delete old data */ 465 if (drmconn->connector) { 466 drmModeFreeConnector(drmconn->connector); 467 FREE(drmconn->drm_modes); 468 469 drmconn->connector = NULL; 470 drmconn->drm_modes = NULL; 471 drmconn->num_modes = 0; 472 } 473 474 /* detect again */ 475 drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id); 476 if (!drmconn->connector) 477 return NULL; 478 479 count = drmconn->connector->count_modes; 480 drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes)); 481 if (!drmconn->drm_modes) { 482 drmModeFreeConnector(drmconn->connector); 483 drmconn->connector = NULL; 484 485 return NULL; 486 } 487 488 for (i = 0; i < count; i++) { 489 struct drm_mode *drmmode = &drmconn->drm_modes[i]; 490 drmModeModeInfoPtr mode = &drmconn->connector->modes[i]; 491 492 drmmode->mode = *mode; 493 494 drmmode->base.desc = drmmode->mode.name; 495 drmmode->base.width = drmmode->mode.hdisplay; 496 drmmode->base.height = drmmode->mode.vdisplay; 497 drmmode->base.refresh_rate = drmmode->mode.vrefresh; 498 /* not all kernels have vrefresh = refresh_rate * 1000 */ 499 if (drmmode->base.refresh_rate < 1000) 500 drmmode->base.refresh_rate *= 1000; 501 } 502 503 nmodes_return = MALLOC(count * sizeof(*nmodes_return)); 504 if (nmodes_return) { 505 for (i = 0; i < count; i++) 506 nmodes_return[i] = &drmconn->drm_modes[i].base; 507 if (num_modes) 508 *num_modes = count; 509 } 510 511 return nmodes_return; 512} 513 514static const struct native_connector ** 515drm_display_get_connectors(struct native_display *ndpy, int *num_connectors, 516 int *num_crtc) 517{ 518 struct drm_display *drmdpy = drm_display(ndpy); 519 const struct native_connector **connectors; 520 int i; 521 522 if (!drmdpy->connectors) { 523 drmdpy->connectors = 524 CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors)); 525 if (!drmdpy->connectors) 526 return NULL; 527 528 for (i = 0; i < drmdpy->resources->count_connectors; i++) { 529 struct drm_connector *drmconn = &drmdpy->connectors[i]; 530 531 drmconn->connector_id = drmdpy->resources->connectors[i]; 532 /* drmconn->connector is allocated when the modes are asked */ 533 } 534 535 drmdpy->num_connectors = drmdpy->resources->count_connectors; 536 } 537 538 connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors)); 539 if (connectors) { 540 for (i = 0; i < drmdpy->num_connectors; i++) 541 connectors[i] = &drmdpy->connectors[i].base; 542 if (num_connectors) 543 *num_connectors = drmdpy->num_connectors; 544 } 545 546 if (num_crtc) 547 *num_crtc = drmdpy->resources->count_crtcs; 548 549 return connectors; 550} 551 552static struct native_surface * 553drm_display_create_scanout_surface(struct native_display *ndpy, 554 const struct native_config *nconf, 555 uint width, uint height) 556{ 557 struct drm_surface *drmsurf; 558 559 drmsurf = drm_display_create_surface(ndpy, nconf, width, height); 560 return &drmsurf->base; 561} 562 563static struct native_display_modeset drm_display_modeset = { 564 .get_connectors = drm_display_get_connectors, 565 .get_modes = drm_display_get_modes, 566 .create_scanout_surface = drm_display_create_scanout_surface, 567 .program = drm_display_program 568}; 569 570void 571drm_display_fini_modeset(struct native_display *ndpy) 572{ 573 struct drm_display *drmdpy = drm_display(ndpy); 574 int i; 575 576 if (drmdpy->connectors) { 577 for (i = 0; i < drmdpy->num_connectors; i++) { 578 struct drm_connector *drmconn = &drmdpy->connectors[i]; 579 if (drmconn->connector) { 580 drmModeFreeConnector(drmconn->connector); 581 FREE(drmconn->drm_modes); 582 } 583 } 584 FREE(drmdpy->connectors); 585 } 586 587 if (drmdpy->shown_surfaces) { 588 FREE(drmdpy->shown_surfaces); 589 drmdpy->shown_surfaces = NULL; 590 } 591 592 if (drmdpy->saved_crtcs) { 593 for (i = 0; i < drmdpy->resources->count_crtcs; i++) { 594 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i]; 595 596 if (drmcrtc->crtc) { 597 /* restore crtc */ 598 drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id, 599 drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y, 600 drmcrtc->connectors, drmcrtc->num_connectors, 601 &drmcrtc->crtc->mode); 602 603 drmModeFreeCrtc(drmcrtc->crtc); 604 } 605 } 606 FREE(drmdpy->saved_crtcs); 607 } 608 609 if (drmdpy->resources) { 610 drmModeFreeResources(drmdpy->resources); 611 drmdpy->resources = NULL; 612 } 613 614 drmdpy->base.modeset = NULL; 615} 616 617boolean 618drm_display_init_modeset(struct native_display *ndpy) 619{ 620 struct drm_display *drmdpy = drm_display(ndpy); 621 622 /* resources are fixed, unlike crtc, connector, or encoder */ 623 drmdpy->resources = drmModeGetResources(drmdpy->fd); 624 if (!drmdpy->resources) { 625 _eglLog(_EGL_DEBUG, "Failed to get KMS resources. Disable modeset."); 626 return FALSE; 627 } 628 629 drmdpy->saved_crtcs = 630 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs)); 631 if (!drmdpy->saved_crtcs) { 632 drm_display_fini_modeset(&drmdpy->base); 633 return FALSE; 634 } 635 636 drmdpy->shown_surfaces = 637 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces)); 638 if (!drmdpy->shown_surfaces) { 639 drm_display_fini_modeset(&drmdpy->base); 640 return FALSE; 641 } 642 643 drmdpy->base.modeset = &drm_display_modeset; 644 645 return TRUE; 646} 647