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