modeset.c revision 828d944fd6670b44b2dd640a92bc0f9fe983a520
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.present = drm_surface_present; 255 drmsurf->base.validate = drm_surface_validate; 256 drmsurf->base.wait = drm_surface_wait; 257 258 return drmsurf; 259} 260 261/** 262 * Choose a CRTC that supports all given connectors. 263 */ 264static uint32_t 265drm_display_choose_crtc(struct native_display *ndpy, 266 uint32_t *connectors, int num_connectors) 267{ 268 struct drm_display *drmdpy = drm_display(ndpy); 269 int idx; 270 271 for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) { 272 boolean found_crtc = TRUE; 273 int i, j; 274 275 for (i = 0; i < num_connectors; i++) { 276 drmModeConnectorPtr connector; 277 int encoder_idx = -1; 278 279 connector = drmModeGetConnector(drmdpy->fd, connectors[i]); 280 if (!connector) { 281 found_crtc = FALSE; 282 break; 283 } 284 285 /* find an encoder the CRTC supports */ 286 for (j = 0; j < connector->count_encoders; j++) { 287 drmModeEncoderPtr encoder = 288 drmModeGetEncoder(drmdpy->fd, connector->encoders[j]); 289 if (encoder->possible_crtcs & (1 << idx)) { 290 encoder_idx = j; 291 break; 292 } 293 drmModeFreeEncoder(encoder); 294 } 295 296 drmModeFreeConnector(connector); 297 if (encoder_idx < 0) { 298 found_crtc = FALSE; 299 break; 300 } 301 } 302 303 if (found_crtc) 304 break; 305 } 306 307 if (idx >= drmdpy->resources->count_crtcs) { 308 _eglLog(_EGL_WARNING, 309 "failed to find a CRTC that supports the given %d connectors", 310 num_connectors); 311 return 0; 312 } 313 314 return drmdpy->resources->crtcs[idx]; 315} 316 317/** 318 * Remember the original CRTC status and set the CRTC 319 */ 320static boolean 321drm_display_set_crtc(struct native_display *ndpy, int crtc_idx, 322 uint32_t buffer_id, uint32_t x, uint32_t y, 323 uint32_t *connectors, int num_connectors, 324 drmModeModeInfoPtr mode) 325{ 326 struct drm_display *drmdpy = drm_display(ndpy); 327 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx]; 328 uint32_t crtc_id; 329 int err; 330 331 if (drmcrtc->crtc) { 332 crtc_id = drmcrtc->crtc->crtc_id; 333 } 334 else { 335 int count = 0, i; 336 337 /* 338 * Choose the CRTC once. It could be more dynamic, but let's keep it 339 * simple for now. 340 */ 341 crtc_id = drm_display_choose_crtc(&drmdpy->base, 342 connectors, num_connectors); 343 344 /* save the original CRTC status */ 345 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 346 if (!drmcrtc->crtc) 347 return FALSE; 348 349 for (i = 0; i < drmdpy->num_connectors; i++) { 350 struct drm_connector *drmconn = &drmdpy->connectors[i]; 351 drmModeConnectorPtr connector = drmconn->connector; 352 drmModeEncoderPtr encoder; 353 354 encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id); 355 if (encoder) { 356 if (encoder->crtc_id == crtc_id) { 357 drmcrtc->connectors[count++] = connector->connector_id; 358 if (count >= Elements(drmcrtc->connectors)) 359 break; 360 } 361 drmModeFreeEncoder(encoder); 362 } 363 } 364 365 drmcrtc->num_connectors = count; 366 } 367 368 err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y, 369 connectors, num_connectors, mode); 370 if (err) { 371 drmModeFreeCrtc(drmcrtc->crtc); 372 drmcrtc->crtc = NULL; 373 drmcrtc->num_connectors = 0; 374 375 return FALSE; 376 } 377 378 return TRUE; 379} 380 381static boolean 382drm_display_program(struct native_display *ndpy, int crtc_idx, 383 struct native_surface *nsurf, uint x, uint y, 384 const struct native_connector **nconns, int num_nconns, 385 const struct native_mode *nmode) 386{ 387 struct drm_display *drmdpy = drm_display(ndpy); 388 struct drm_surface *drmsurf = drm_surface(nsurf); 389 const struct drm_mode *drmmode = drm_mode(nmode); 390 uint32_t connector_ids[32]; 391 uint32_t buffer_id; 392 drmModeModeInfo mode_tmp, *mode; 393 int i; 394 395 if (num_nconns > Elements(connector_ids)) { 396 _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns); 397 num_nconns = Elements(connector_ids); 398 } 399 400 if (drmsurf) { 401 if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE)) 402 return FALSE; 403 404 buffer_id = drmsurf->front_fb.buffer_id; 405 /* the mode argument of drmModeSetCrtc is not constified */ 406 mode_tmp = drmmode->mode; 407 mode = &mode_tmp; 408 } 409 else { 410 /* disable the CRTC */ 411 buffer_id = 0; 412 mode = NULL; 413 num_nconns = 0; 414 } 415 416 for (i = 0; i < num_nconns; i++) { 417 struct drm_connector *drmconn = drm_connector(nconns[i]); 418 connector_ids[i] = drmconn->connector->connector_id; 419 } 420 421 if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y, 422 connector_ids, num_nconns, mode)) { 423 _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx); 424 425 return FALSE; 426 } 427 428 if (drmdpy->shown_surfaces[crtc_idx]) 429 drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE; 430 drmdpy->shown_surfaces[crtc_idx] = drmsurf; 431 432 /* remember the settings for buffer swapping */ 433 if (drmsurf) { 434 uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id; 435 struct drm_crtc *drmcrtc = &drmsurf->current_crtc; 436 437 if (drmcrtc->crtc) 438 drmModeFreeCrtc(drmcrtc->crtc); 439 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id); 440 441 assert(num_nconns < Elements(drmcrtc->connectors)); 442 memcpy(drmcrtc->connectors, connector_ids, 443 sizeof(*connector_ids) * num_nconns); 444 drmcrtc->num_connectors = num_nconns; 445 446 drmsurf->is_shown = TRUE; 447 } 448 449 return TRUE; 450} 451 452static const struct native_mode ** 453drm_display_get_modes(struct native_display *ndpy, 454 const struct native_connector *nconn, 455 int *num_modes) 456{ 457 struct drm_display *drmdpy = drm_display(ndpy); 458 struct drm_connector *drmconn = drm_connector(nconn); 459 const struct native_mode **nmodes_return; 460 int count, i; 461 462 /* delete old data */ 463 if (drmconn->connector) { 464 drmModeFreeConnector(drmconn->connector); 465 FREE(drmconn->drm_modes); 466 467 drmconn->connector = NULL; 468 drmconn->drm_modes = NULL; 469 drmconn->num_modes = 0; 470 } 471 472 /* detect again */ 473 drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id); 474 if (!drmconn->connector) 475 return NULL; 476 477 count = drmconn->connector->count_modes; 478 drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes)); 479 if (!drmconn->drm_modes) { 480 drmModeFreeConnector(drmconn->connector); 481 drmconn->connector = NULL; 482 483 return NULL; 484 } 485 486 for (i = 0; i < count; i++) { 487 struct drm_mode *drmmode = &drmconn->drm_modes[i]; 488 drmModeModeInfoPtr mode = &drmconn->connector->modes[i]; 489 490 drmmode->mode = *mode; 491 492 drmmode->base.desc = drmmode->mode.name; 493 drmmode->base.width = drmmode->mode.hdisplay; 494 drmmode->base.height = drmmode->mode.vdisplay; 495 drmmode->base.refresh_rate = drmmode->mode.vrefresh; 496 /* not all kernels have vrefresh = refresh_rate * 1000 */ 497 if (drmmode->base.refresh_rate < 1000) 498 drmmode->base.refresh_rate *= 1000; 499 } 500 501 nmodes_return = MALLOC(count * sizeof(*nmodes_return)); 502 if (nmodes_return) { 503 for (i = 0; i < count; i++) 504 nmodes_return[i] = &drmconn->drm_modes[i].base; 505 if (num_modes) 506 *num_modes = count; 507 } 508 509 return nmodes_return; 510} 511 512static const struct native_connector ** 513drm_display_get_connectors(struct native_display *ndpy, int *num_connectors, 514 int *num_crtc) 515{ 516 struct drm_display *drmdpy = drm_display(ndpy); 517 const struct native_connector **connectors; 518 int i; 519 520 if (!drmdpy->connectors) { 521 drmdpy->connectors = 522 CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors)); 523 if (!drmdpy->connectors) 524 return NULL; 525 526 for (i = 0; i < drmdpy->resources->count_connectors; i++) { 527 struct drm_connector *drmconn = &drmdpy->connectors[i]; 528 529 drmconn->connector_id = drmdpy->resources->connectors[i]; 530 /* drmconn->connector is allocated when the modes are asked */ 531 } 532 533 drmdpy->num_connectors = drmdpy->resources->count_connectors; 534 } 535 536 connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors)); 537 if (connectors) { 538 for (i = 0; i < drmdpy->num_connectors; i++) 539 connectors[i] = &drmdpy->connectors[i].base; 540 if (num_connectors) 541 *num_connectors = drmdpy->num_connectors; 542 } 543 544 if (num_crtc) 545 *num_crtc = drmdpy->resources->count_crtcs; 546 547 return connectors; 548} 549 550static struct native_surface * 551drm_display_create_scanout_surface(struct native_display *ndpy, 552 const struct native_config *nconf, 553 uint width, uint height) 554{ 555 struct drm_surface *drmsurf; 556 557 drmsurf = drm_display_create_surface(ndpy, nconf, width, height); 558 return &drmsurf->base; 559} 560 561static struct native_display_modeset drm_display_modeset = { 562 .get_connectors = drm_display_get_connectors, 563 .get_modes = drm_display_get_modes, 564 .create_scanout_surface = drm_display_create_scanout_surface, 565 .program = drm_display_program 566}; 567 568void 569drm_display_fini_modeset(struct native_display *ndpy) 570{ 571 struct drm_display *drmdpy = drm_display(ndpy); 572 int i; 573 574 if (drmdpy->connectors) { 575 for (i = 0; i < drmdpy->num_connectors; i++) { 576 struct drm_connector *drmconn = &drmdpy->connectors[i]; 577 if (drmconn->connector) { 578 drmModeFreeConnector(drmconn->connector); 579 FREE(drmconn->drm_modes); 580 } 581 } 582 FREE(drmdpy->connectors); 583 } 584 585 if (drmdpy->shown_surfaces) { 586 FREE(drmdpy->shown_surfaces); 587 drmdpy->shown_surfaces = NULL; 588 } 589 590 if (drmdpy->saved_crtcs) { 591 for (i = 0; i < drmdpy->resources->count_crtcs; i++) { 592 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i]; 593 594 if (drmcrtc->crtc) { 595 /* restore crtc */ 596 drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id, 597 drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y, 598 drmcrtc->connectors, drmcrtc->num_connectors, 599 &drmcrtc->crtc->mode); 600 601 drmModeFreeCrtc(drmcrtc->crtc); 602 } 603 } 604 FREE(drmdpy->saved_crtcs); 605 } 606 607 if (drmdpy->resources) { 608 drmModeFreeResources(drmdpy->resources); 609 drmdpy->resources = NULL; 610 } 611 612 drmdpy->base.modeset = NULL; 613} 614 615boolean 616drm_display_init_modeset(struct native_display *ndpy) 617{ 618 struct drm_display *drmdpy = drm_display(ndpy); 619 620 /* resources are fixed, unlike crtc, connector, or encoder */ 621 drmdpy->resources = drmModeGetResources(drmdpy->fd); 622 if (!drmdpy->resources) { 623 _eglLog(_EGL_DEBUG, "Failed to get KMS resources. Disable modeset."); 624 return FALSE; 625 } 626 627 drmdpy->saved_crtcs = 628 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs)); 629 if (!drmdpy->saved_crtcs) { 630 drm_display_fini_modeset(&drmdpy->base); 631 return FALSE; 632 } 633 634 drmdpy->shown_surfaces = 635 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces)); 636 if (!drmdpy->shown_surfaces) { 637 drm_display_fini_modeset(&drmdpy->base); 638 return FALSE; 639 } 640 641 drmdpy->base.modeset = &drm_display_modeset; 642 643 return TRUE; 644} 645