1 /* 2 * Copyright (c) 2006-2009 Red Hat Inc. 3 * Copyright (c) 2006-2008 Intel Corporation 4 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 5 * 6 * DRM framebuffer helper functions 7 * 8 * Permission to use, copy, modify, distribute, and sell this software and its 9 * documentation for any purpose is hereby granted without fee, provided that 10 * the above copyright notice appear in all copies and that both that copyright 11 * notice and this permission notice appear in supporting documentation, and 12 * that the name of the copyright holders not be used in advertising or 13 * publicity pertaining to distribution of the software without specific, 14 * written prior permission. The copyright holders make no representations 15 * about the suitability of this software for any purpose. It is provided "as 16 * is" without express or implied warranty. 17 * 18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24 * OF THIS SOFTWARE. 25 * 26 * Authors: 27 * Dave Airlie <airlied@linux.ie> 28 * Jesse Barnes <jesse.barnes@intel.com> 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <dev/drm2/drmP.h> 35 #include <dev/drm2/drm_crtc.h> 36 #include <dev/drm2/drm_fb_helper.h> 37 #include <dev/drm2/drm_crtc_helper.h> 38 39 #include <sys/kdb.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 43 struct vt_kms_softc { 44 struct drm_fb_helper *fb_helper; 45 struct task fb_mode_task; 46 }; 47 48 static fb_enter_t vt_kms_postswitch; 49 static void vt_restore_fbdev_mode(void *, int); 50 51 /* Call restore out of vt(9) locks. */ 52 static void 53 vt_restore_fbdev_mode(void *arg, int pending) 54 { 55 struct drm_fb_helper *fb_helper; 56 struct vt_kms_softc *sc; 57 58 sc = (struct vt_kms_softc *)arg; 59 fb_helper = sc->fb_helper; 60 sx_xlock(&fb_helper->dev->mode_config.mutex); 61 drm_fb_helper_restore_fbdev_mode(fb_helper); 62 sx_xunlock(&fb_helper->dev->mode_config.mutex); 63 } 64 65 static int 66 vt_kms_postswitch(void *arg) 67 { 68 struct vt_kms_softc *sc; 69 70 sc = (struct vt_kms_softc *)arg; 71 72 if (!kdb_active && panicstr == NULL) 73 taskqueue_enqueue_fast(taskqueue_thread, &sc->fb_mode_task); 74 else 75 drm_fb_helper_restore_fbdev_mode(sc->fb_helper); 76 77 return (0); 78 } 79 80 static DRM_LIST_HEAD(kernel_fb_helper_list); 81 82 /* simple single crtc case helper function */ 83 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 84 { 85 struct drm_device *dev = fb_helper->dev; 86 struct drm_connector *connector; 87 88 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 89 struct drm_fb_helper_connector *fb_helper_connector; 90 91 fb_helper_connector = malloc( 92 sizeof(struct drm_fb_helper_connector), DRM_MEM_KMS, 93 M_WAITOK | M_ZERO); 94 95 fb_helper_connector->connector = connector; 96 fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; 97 } 98 return 0; 99 } 100 101 static int 102 fb_get_options(const char *connector_name, char **option) 103 { 104 char tunable[64]; 105 106 /* 107 * A user may use loader tunables to set a specific mode for the 108 * console. Tunables are read in the following order: 109 * 1. kern.vt.fb.modes.$connector_name 110 * 2. kern.vt.fb.default_mode 111 * 112 * Example of a mode specific to the LVDS connector: 113 * kern.vt.fb.modes.LVDS="1024x768" 114 * 115 * Example of a mode applied to all connectors not having a 116 * connector-specific mode: 117 * kern.vt.fb.default_mode="640x480" 118 */ 119 snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s", 120 connector_name); 121 DRM_INFO("Connector %s: get mode from tunables:\n", connector_name); 122 DRM_INFO(" - %s\n", tunable); 123 DRM_INFO(" - kern.vt.fb.default_mode\n"); 124 *option = kern_getenv(tunable); 125 if (*option == NULL) 126 *option = kern_getenv("kern.vt.fb.default_mode"); 127 128 return (*option != NULL ? 0 : 1); 129 } 130 131 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) 132 { 133 struct drm_fb_helper_connector *fb_helper_conn; 134 int i; 135 136 for (i = 0; i < fb_helper->connector_count; i++) { 137 struct drm_cmdline_mode *mode; 138 struct drm_connector *connector; 139 char *option = NULL; 140 141 fb_helper_conn = fb_helper->connector_info[i]; 142 connector = fb_helper_conn->connector; 143 mode = &fb_helper_conn->cmdline_mode; 144 145 /* do something on return - turn off connector maybe */ 146 if (fb_get_options(drm_get_connector_name(connector), &option)) 147 continue; 148 149 if (drm_mode_parse_command_line_for_connector(option, 150 connector, 151 mode)) { 152 if (mode->force) { 153 const char *s; 154 switch (mode->force) { 155 case DRM_FORCE_OFF: 156 s = "OFF"; 157 break; 158 case DRM_FORCE_ON_DIGITAL: 159 s = "ON - dig"; 160 break; 161 default: 162 case DRM_FORCE_ON: 163 s = "ON"; 164 break; 165 } 166 167 DRM_INFO("forcing %s connector %s\n", 168 drm_get_connector_name(connector), s); 169 connector->force = mode->force; 170 } 171 172 DRM_INFO("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 173 drm_get_connector_name(connector), 174 mode->xres, mode->yres, 175 mode->refresh_specified ? mode->refresh : 60, 176 mode->rb ? " reduced blanking" : "", 177 mode->margins ? " with margins" : "", 178 mode->interlace ? " interlaced" : ""); 179 } 180 181 freeenv(option); 182 183 } 184 return 0; 185 } 186 187 #if 0 188 static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) 189 { 190 uint16_t *r_base, *g_base, *b_base; 191 int i; 192 193 r_base = crtc->gamma_store; 194 g_base = r_base + crtc->gamma_size; 195 b_base = g_base + crtc->gamma_size; 196 197 for (i = 0; i < crtc->gamma_size; i++) 198 helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); 199 } 200 201 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 202 { 203 uint16_t *r_base, *g_base, *b_base; 204 205 if (crtc->funcs->gamma_set == NULL) 206 return; 207 208 r_base = crtc->gamma_store; 209 g_base = r_base + crtc->gamma_size; 210 b_base = g_base + crtc->gamma_size; 211 212 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 213 } 214 #endif 215 216 #if 0 217 int drm_fb_helper_debug_enter(struct fb_info *info) 218 { 219 struct drm_fb_helper *helper = info->par; 220 struct drm_crtc_helper_funcs *funcs; 221 int i; 222 223 if (list_empty(&kernel_fb_helper_list)) 224 return false; 225 226 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 227 for (i = 0; i < helper->crtc_count; i++) { 228 struct drm_mode_set *mode_set = 229 &helper->crtc_info[i].mode_set; 230 231 if (!mode_set->crtc->enabled) 232 continue; 233 234 funcs = mode_set->crtc->helper_private; 235 drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); 236 funcs->mode_set_base_atomic(mode_set->crtc, 237 mode_set->fb, 238 mode_set->x, 239 mode_set->y, 240 ENTER_ATOMIC_MODE_SET); 241 } 242 } 243 244 return 0; 245 } 246 #endif 247 248 #if 0 249 /* Find the real fb for a given fb helper CRTC */ 250 static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) 251 { 252 struct drm_device *dev = crtc->dev; 253 struct drm_crtc *c; 254 255 list_for_each_entry(c, &dev->mode_config.crtc_list, head) { 256 if (crtc->base.id == c->base.id) 257 return c->fb; 258 } 259 260 return NULL; 261 } 262 #endif 263 264 #if 0 265 int drm_fb_helper_debug_leave(struct fb_info *info) 266 { 267 struct drm_fb_helper *helper = info->par; 268 struct drm_crtc *crtc; 269 struct drm_crtc_helper_funcs *funcs; 270 struct drm_framebuffer *fb; 271 int i; 272 273 for (i = 0; i < helper->crtc_count; i++) { 274 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; 275 crtc = mode_set->crtc; 276 funcs = crtc->helper_private; 277 fb = drm_mode_config_fb(crtc); 278 279 if (!crtc->enabled) 280 continue; 281 282 if (!fb) { 283 DRM_ERROR("no fb to restore??\n"); 284 continue; 285 } 286 287 drm_fb_helper_restore_lut_atomic(mode_set->crtc); 288 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 289 crtc->y, LEAVE_ATOMIC_MODE_SET); 290 } 291 292 return 0; 293 } 294 #endif 295 296 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) 297 { 298 bool error = false; 299 int i, ret; 300 for (i = 0; i < fb_helper->crtc_count; i++) { 301 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; 302 ret = drm_crtc_helper_set_config(mode_set); 303 if (ret) 304 error = true; 305 } 306 return error; 307 } 308 309 #if 0 310 bool drm_fb_helper_force_kernel_mode(void) 311 { 312 bool ret, error = false; 313 struct drm_fb_helper *helper; 314 315 if (list_empty(&kernel_fb_helper_list)) 316 return false; 317 318 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 319 if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) 320 continue; 321 322 ret = drm_fb_helper_restore_fbdev_mode(helper); 323 if (ret) 324 error = true; 325 } 326 return error; 327 } 328 #endif 329 330 #if 0 331 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, 332 void *panic_str) 333 { 334 printf("panic occurred, switching back to text console\n"); 335 return drm_fb_helper_force_kernel_mode(); 336 return 0; 337 } 338 339 static struct notifier_block paniced = { 340 .notifier_call = drm_fb_helper_panic, 341 }; 342 343 /** 344 * drm_fb_helper_restore - restore the framebuffer console (kernel) config 345 * 346 * Restore's the kernel's fbcon mode, used for lastclose & panic paths. 347 */ 348 void drm_fb_helper_restore(void) 349 { 350 bool ret; 351 ret = drm_fb_helper_force_kernel_mode(); 352 if (ret == true) 353 DRM_ERROR("Failed to restore crtc configuration\n"); 354 } 355 356 #ifdef CONFIG_MAGIC_SYSRQ 357 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 358 { 359 drm_fb_helper_restore(); 360 } 361 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 362 363 static void drm_fb_helper_sysrq(int dummy1) 364 { 365 schedule_work(&drm_fb_helper_restore_work); 366 } 367 368 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 369 .handler = drm_fb_helper_sysrq, 370 .help_msg = "force-fb(V)", 371 .action_msg = "Restore framebuffer console", 372 }; 373 #else 374 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 375 #endif 376 #endif 377 378 #if 0 379 static void drm_fb_helper_on(struct fb_info *info) 380 { 381 struct drm_fb_helper *fb_helper = info->par; 382 struct drm_device *dev = fb_helper->dev; 383 struct drm_crtc *crtc; 384 struct drm_crtc_helper_funcs *crtc_funcs; 385 struct drm_connector *connector; 386 struct drm_encoder *encoder; 387 int i, j; 388 389 /* 390 * For each CRTC in this fb, turn the crtc on then, 391 * find all associated encoders and turn them on. 392 */ 393 sx_xlock(&dev->mode_config.mutex); 394 for (i = 0; i < fb_helper->crtc_count; i++) { 395 crtc = fb_helper->crtc_info[i].mode_set.crtc; 396 crtc_funcs = crtc->helper_private; 397 398 if (!crtc->enabled) 399 continue; 400 401 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); 402 403 /* Walk the connectors & encoders on this fb turning them on */ 404 for (j = 0; j < fb_helper->connector_count; j++) { 405 connector = fb_helper->connector_info[j]->connector; 406 connector->dpms = DRM_MODE_DPMS_ON; 407 drm_connector_property_set_value(connector, 408 dev->mode_config.dpms_property, 409 DRM_MODE_DPMS_ON); 410 } 411 /* Found a CRTC on this fb, now find encoders */ 412 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 413 if (encoder->crtc == crtc) { 414 struct drm_encoder_helper_funcs *encoder_funcs; 415 416 encoder_funcs = encoder->helper_private; 417 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); 418 } 419 } 420 } 421 sx_xunlock(&dev->mode_config.mutex); 422 } 423 #endif 424 425 #if 0 426 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) 427 { 428 struct drm_fb_helper *fb_helper = info->par; 429 struct drm_device *dev = fb_helper->dev; 430 struct drm_crtc *crtc; 431 struct drm_crtc_helper_funcs *crtc_funcs; 432 struct drm_connector *connector; 433 struct drm_encoder *encoder; 434 int i, j; 435 436 /* 437 * For each CRTC in this fb, find all associated encoders 438 * and turn them off, then turn off the CRTC. 439 */ 440 sx_xlock(&dev->mode_config.mutex); 441 for (i = 0; i < fb_helper->crtc_count; i++) { 442 crtc = fb_helper->crtc_info[i].mode_set.crtc; 443 crtc_funcs = crtc->helper_private; 444 445 if (!crtc->enabled) 446 continue; 447 448 /* Walk the connectors on this fb and mark them off */ 449 for (j = 0; j < fb_helper->connector_count; j++) { 450 connector = fb_helper->connector_info[j]->connector; 451 connector->dpms = dpms_mode; 452 drm_connector_property_set_value(connector, 453 dev->mode_config.dpms_property, 454 dpms_mode); 455 } 456 /* Found a CRTC on this fb, now find encoders */ 457 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 458 if (encoder->crtc == crtc) { 459 struct drm_encoder_helper_funcs *encoder_funcs; 460 461 encoder_funcs = encoder->helper_private; 462 encoder_funcs->dpms(encoder, dpms_mode); 463 } 464 } 465 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); 466 } 467 sx_xunlock(&dev->mode_config.mutex); 468 } 469 #endif 470 471 #if 0 472 int drm_fb_helper_blank(int blank, struct fb_info *info) 473 { 474 switch (blank) { 475 /* Display: On; HSync: On, VSync: On */ 476 case FB_BLANK_UNBLANK: 477 drm_fb_helper_on(info); 478 break; 479 /* Display: Off; HSync: On, VSync: On */ 480 case FB_BLANK_NORMAL: 481 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); 482 break; 483 /* Display: Off; HSync: Off, VSync: On */ 484 case FB_BLANK_HSYNC_SUSPEND: 485 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); 486 break; 487 /* Display: Off; HSync: On, VSync: Off */ 488 case FB_BLANK_VSYNC_SUSPEND: 489 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); 490 break; 491 /* Display: Off; HSync: Off, VSync: Off */ 492 case FB_BLANK_POWERDOWN: 493 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); 494 break; 495 } 496 return 0; 497 } 498 #endif 499 500 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) 501 { 502 int i; 503 504 for (i = 0; i < helper->connector_count; i++) 505 free(helper->connector_info[i], DRM_MEM_KMS); 506 free(helper->connector_info, DRM_MEM_KMS); 507 for (i = 0; i < helper->crtc_count; i++) { 508 free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS); 509 if (helper->crtc_info[i].mode_set.mode) 510 drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode); 511 } 512 free(helper->crtc_info, DRM_MEM_KMS); 513 } 514 515 int drm_fb_helper_init(struct drm_device *dev, 516 struct drm_fb_helper *fb_helper, 517 int crtc_count, int max_conn_count) 518 { 519 struct drm_crtc *crtc; 520 int i; 521 522 fb_helper->dev = dev; 523 524 INIT_LIST_HEAD(&fb_helper->kernel_fb_list); 525 526 fb_helper->crtc_info = malloc(crtc_count * 527 sizeof(struct drm_fb_helper_crtc), DRM_MEM_KMS, M_WAITOK | M_ZERO); 528 529 fb_helper->crtc_count = crtc_count; 530 fb_helper->connector_info = malloc(dev->mode_config.num_connector * 531 sizeof(struct drm_fb_helper_connector *), DRM_MEM_KMS, 532 M_WAITOK | M_ZERO); 533 fb_helper->connector_count = 0; 534 535 for (i = 0; i < crtc_count; i++) { 536 fb_helper->crtc_info[i].mode_set.connectors = 537 malloc(max_conn_count * sizeof(struct drm_connector *), 538 DRM_MEM_KMS, M_WAITOK | M_ZERO); 539 540 fb_helper->crtc_info[i].mode_set.num_connectors = 0; 541 } 542 543 i = 0; 544 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 545 fb_helper->crtc_info[i].crtc_id = crtc->base.id; 546 fb_helper->crtc_info[i].mode_set.crtc = crtc; 547 i++; 548 } 549 fb_helper->conn_limit = max_conn_count; 550 return 0; 551 } 552 553 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 554 { 555 if (!list_empty(&fb_helper->kernel_fb_list)) { 556 list_del(&fb_helper->kernel_fb_list); 557 if (list_empty(&kernel_fb_helper_list)) { 558 #if 0 559 printk(KERN_INFO "drm: unregistered panic notifier\n"); 560 atomic_notifier_chain_unregister(&panic_notifier_list, 561 &paniced); 562 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 563 #endif 564 } 565 } 566 567 drm_fb_helper_crtc_free(fb_helper); 568 569 } 570 571 #if 0 572 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 573 u16 blue, u16 regno, struct fb_info *info) 574 { 575 struct drm_fb_helper *fb_helper = info->par; 576 struct drm_framebuffer *fb = fb_helper->fb; 577 int pindex; 578 579 if (info->fix.visual == FB_VISUAL_trueCOLOR) { 580 u32 *palette; 581 u32 value; 582 /* place color in psuedopalette */ 583 if (regno > 16) 584 return -EINVAL; 585 palette = (u32 *)info->pseudo_palette; 586 red >>= (16 - info->var.red.length); 587 green >>= (16 - info->var.green.length); 588 blue >>= (16 - info->var.blue.length); 589 value = (red << info->var.red.offset) | 590 (green << info->var.green.offset) | 591 (blue << info->var.blue.offset); 592 if (info->var.transp.length > 0) { 593 u32 mask = (1 << info->var.transp.length) - 1; 594 mask <<= info->var.transp.offset; 595 value |= mask; 596 } 597 palette[regno] = value; 598 return 0; 599 } 600 601 pindex = regno; 602 603 if (fb->bits_per_pixel == 16) { 604 pindex = regno << 3; 605 606 if (fb->depth == 16 && regno > 63) 607 return -EINVAL; 608 if (fb->depth == 15 && regno > 31) 609 return -EINVAL; 610 611 if (fb->depth == 16) { 612 u16 r, g, b; 613 int i; 614 if (regno < 32) { 615 for (i = 0; i < 8; i++) 616 fb_helper->funcs->gamma_set(crtc, red, 617 green, blue, pindex + i); 618 } 619 620 fb_helper->funcs->gamma_get(crtc, &r, 621 &g, &b, 622 pindex >> 1); 623 624 for (i = 0; i < 4; i++) 625 fb_helper->funcs->gamma_set(crtc, r, 626 green, b, 627 (pindex >> 1) + i); 628 } 629 } 630 631 if (fb->depth != 16) 632 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); 633 return 0; 634 } 635 #endif 636 637 #if 0 638 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 639 { 640 struct drm_fb_helper *fb_helper = info->par; 641 struct drm_crtc_helper_funcs *crtc_funcs; 642 u16 *red, *green, *blue, *transp; 643 struct drm_crtc *crtc; 644 int i, j, rc = 0; 645 int start; 646 647 for (i = 0; i < fb_helper->crtc_count; i++) { 648 crtc = fb_helper->crtc_info[i].mode_set.crtc; 649 crtc_funcs = crtc->helper_private; 650 651 red = cmap->red; 652 green = cmap->green; 653 blue = cmap->blue; 654 transp = cmap->transp; 655 start = cmap->start; 656 657 for (j = 0; j < cmap->len; j++) { 658 u16 hred, hgreen, hblue, htransp = 0xffff; 659 660 hred = *red++; 661 hgreen = *green++; 662 hblue = *blue++; 663 664 if (transp) 665 htransp = *transp++; 666 667 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); 668 if (rc) 669 return rc; 670 } 671 crtc_funcs->load_lut(crtc); 672 } 673 return rc; 674 } 675 #endif 676 677 #if 0 678 int drm_fb_helper_check_var(struct fb_var_screeninfo *var, 679 struct fb_info *info) 680 { 681 struct drm_fb_helper *fb_helper = info->par; 682 struct drm_framebuffer *fb = fb_helper->fb; 683 int depth; 684 685 if (var->pixclock != 0 || in_dbg_master()) 686 return -EINVAL; 687 688 /* Need to resize the fb object !!! */ 689 if (var->bits_per_pixel > fb->bits_per_pixel || 690 var->xres > fb->width || var->yres > fb->height || 691 var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 692 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " 693 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 694 var->xres, var->yres, var->bits_per_pixel, 695 var->xres_virtual, var->yres_virtual, 696 fb->width, fb->height, fb->bits_per_pixel); 697 return -EINVAL; 698 } 699 700 switch (var->bits_per_pixel) { 701 case 16: 702 depth = (var->green.length == 6) ? 16 : 15; 703 break; 704 case 32: 705 depth = (var->transp.length > 0) ? 32 : 24; 706 break; 707 default: 708 depth = var->bits_per_pixel; 709 break; 710 } 711 712 switch (depth) { 713 case 8: 714 var->red.offset = 0; 715 var->green.offset = 0; 716 var->blue.offset = 0; 717 var->red.length = 8; 718 var->green.length = 8; 719 var->blue.length = 8; 720 var->transp.length = 0; 721 var->transp.offset = 0; 722 break; 723 case 15: 724 var->red.offset = 10; 725 var->green.offset = 5; 726 var->blue.offset = 0; 727 var->red.length = 5; 728 var->green.length = 5; 729 var->blue.length = 5; 730 var->transp.length = 1; 731 var->transp.offset = 15; 732 break; 733 case 16: 734 var->red.offset = 11; 735 var->green.offset = 5; 736 var->blue.offset = 0; 737 var->red.length = 5; 738 var->green.length = 6; 739 var->blue.length = 5; 740 var->transp.length = 0; 741 var->transp.offset = 0; 742 break; 743 case 24: 744 var->red.offset = 16; 745 var->green.offset = 8; 746 var->blue.offset = 0; 747 var->red.length = 8; 748 var->green.length = 8; 749 var->blue.length = 8; 750 var->transp.length = 0; 751 var->transp.offset = 0; 752 break; 753 case 32: 754 var->red.offset = 16; 755 var->green.offset = 8; 756 var->blue.offset = 0; 757 var->red.length = 8; 758 var->green.length = 8; 759 var->blue.length = 8; 760 var->transp.length = 8; 761 var->transp.offset = 24; 762 break; 763 default: 764 return -EINVAL; 765 } 766 return 0; 767 } 768 #endif 769 770 #if 0 771 /* this will let fbcon do the mode init */ 772 int drm_fb_helper_set_par(struct fb_info *info) 773 { 774 struct drm_fb_helper *fb_helper = info->par; 775 struct drm_device *dev = fb_helper->dev; 776 struct fb_var_screeninfo *var = &info->var; 777 struct drm_crtc *crtc; 778 int ret; 779 int i; 780 781 if (var->pixclock != 0) { 782 DRM_ERROR("PIXEL CLOCK SET\n"); 783 return -EINVAL; 784 } 785 786 mutex_lock(&dev->mode_config.mutex); 787 for (i = 0; i < fb_helper->crtc_count; i++) { 788 crtc = fb_helper->crtc_info[i].mode_set.crtc; 789 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 790 if (ret) { 791 mutex_unlock(&dev->mode_config.mutex); 792 return ret; 793 } 794 } 795 mutex_unlock(&dev->mode_config.mutex); 796 797 if (fb_helper->delayed_hotplug) { 798 fb_helper->delayed_hotplug = false; 799 drm_fb_helper_hotplug_event(fb_helper); 800 } 801 return 0; 802 } 803 #endif 804 805 #if 0 806 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 807 struct fb_info *info) 808 { 809 struct drm_fb_helper *fb_helper = info->par; 810 struct drm_device *dev = fb_helper->dev; 811 struct drm_mode_set *modeset; 812 struct drm_crtc *crtc; 813 int ret = 0; 814 int i; 815 816 mutex_lock(&dev->mode_config.mutex); 817 for (i = 0; i < fb_helper->crtc_count; i++) { 818 crtc = fb_helper->crtc_info[i].mode_set.crtc; 819 820 modeset = &fb_helper->crtc_info[i].mode_set; 821 822 modeset->x = var->xoffset; 823 modeset->y = var->yoffset; 824 825 if (modeset->num_connectors) { 826 ret = crtc->funcs->set_config(modeset); 827 if (!ret) { 828 info->var.xoffset = var->xoffset; 829 info->var.yoffset = var->yoffset; 830 } 831 } 832 } 833 mutex_unlock(&dev->mode_config.mutex); 834 return ret; 835 } 836 #endif 837 838 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, 839 int preferred_bpp) 840 { 841 int new_fb = 0; 842 int crtc_count = 0; 843 int i; 844 struct fb_info *info; 845 struct drm_fb_helper_surface_size sizes; 846 int gamma_size = 0; 847 struct vt_kms_softc *sc; 848 device_t kdev; 849 850 memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); 851 sizes.surface_depth = 24; 852 sizes.surface_bpp = 32; 853 sizes.fb_width = (unsigned)-1; 854 sizes.fb_height = (unsigned)-1; 855 856 /* if driver picks 8 or 16 by default use that 857 for both depth/bpp */ 858 if (preferred_bpp != sizes.surface_bpp) { 859 sizes.surface_depth = sizes.surface_bpp = preferred_bpp; 860 } 861 /* first up get a count of crtcs now in use and new min/maxes width/heights */ 862 for (i = 0; i < fb_helper->connector_count; i++) { 863 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; 864 struct drm_cmdline_mode *cmdline_mode; 865 866 cmdline_mode = &fb_helper_conn->cmdline_mode; 867 868 if (cmdline_mode->bpp_specified) { 869 switch (cmdline_mode->bpp) { 870 case 8: 871 sizes.surface_depth = sizes.surface_bpp = 8; 872 break; 873 case 15: 874 sizes.surface_depth = 15; 875 sizes.surface_bpp = 16; 876 break; 877 case 16: 878 sizes.surface_depth = sizes.surface_bpp = 16; 879 break; 880 case 24: 881 sizes.surface_depth = sizes.surface_bpp = 24; 882 break; 883 case 32: 884 sizes.surface_depth = 24; 885 sizes.surface_bpp = 32; 886 break; 887 } 888 break; 889 } 890 } 891 892 crtc_count = 0; 893 for (i = 0; i < fb_helper->crtc_count; i++) { 894 struct drm_display_mode *desired_mode; 895 desired_mode = fb_helper->crtc_info[i].desired_mode; 896 897 if (desired_mode) { 898 if (gamma_size == 0) 899 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 900 if (desired_mode->hdisplay < sizes.fb_width) 901 sizes.fb_width = desired_mode->hdisplay; 902 if (desired_mode->vdisplay < sizes.fb_height) 903 sizes.fb_height = desired_mode->vdisplay; 904 if (desired_mode->hdisplay > sizes.surface_width) 905 sizes.surface_width = desired_mode->hdisplay; 906 if (desired_mode->vdisplay > sizes.surface_height) 907 sizes.surface_height = desired_mode->vdisplay; 908 crtc_count++; 909 } 910 } 911 912 if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { 913 /* hmm everyone went away - assume VGA cable just fell out 914 and will come back later. */ 915 DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); 916 sizes.fb_width = sizes.surface_width = 1024; 917 sizes.fb_height = sizes.surface_height = 768; 918 } 919 920 /* push down into drivers */ 921 new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 922 if (new_fb < 0) 923 return new_fb; 924 925 sc = malloc(sizeof(struct vt_kms_softc), DRM_MEM_KMS, 926 M_WAITOK | M_ZERO); 927 sc->fb_helper = fb_helper; 928 TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc); 929 930 info = fb_helper->fbdev; 931 932 info->fb_name = device_get_nameunit(fb_helper->dev->device); 933 info->fb_depth = fb_helper->fb->bits_per_pixel; 934 info->fb_height = fb_helper->fb->height; 935 info->fb_width = fb_helper->fb->width; 936 info->fb_stride = fb_helper->fb->pitches[0]; 937 info->fb_priv = sc; 938 info->enter = &vt_kms_postswitch; 939 940 /* set the fb pointer */ 941 for (i = 0; i < fb_helper->crtc_count; i++) { 942 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; 943 } 944 945 if (new_fb) { 946 device_t fbd; 947 int ret; 948 949 kdev = fb_helper->dev->device; 950 fbd = device_add_child(kdev, "fbd", device_get_unit(kdev)); 951 if (fbd != NULL) 952 ret = device_probe_and_attach(fbd); 953 else 954 ret = ENODEV; 955 #ifdef DEV_VT 956 if (ret != 0) 957 DRM_ERROR("Failed to attach fbd device: %d\n", ret); 958 #endif 959 } 960 return 0; 961 } 962 963 #if 0 964 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 965 uint32_t depth) 966 { 967 info->fix.type = FB_TYPE_PACKED_PIXELS; 968 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : 969 FB_VISUAL_trueCOLOR; 970 info->fix.mmio_start = 0; 971 info->fix.mmio_len = 0; 972 info->fix.type_aux = 0; 973 info->fix.xpanstep = 1; /* doing it in hw */ 974 info->fix.ypanstep = 1; /* doing it in hw */ 975 info->fix.ywrapstep = 0; 976 info->fix.accel = FB_ACCEL_NONE; 977 info->fix.type_aux = 0; 978 979 info->fix.line_length = pitch; 980 return; 981 } 982 983 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, 984 uint32_t fb_width, uint32_t fb_height) 985 { 986 struct drm_framebuffer *fb = fb_helper->fb; 987 info->pseudo_palette = fb_helper->pseudo_palette; 988 info->var.xres_virtual = fb->width; 989 info->var.yres_virtual = fb->height; 990 info->var.bits_per_pixel = fb->bits_per_pixel; 991 info->var.accel_flags = FB_ACCELF_TEXT; 992 info->var.xoffset = 0; 993 info->var.yoffset = 0; 994 info->var.activate = FB_ACTIVATE_NOW; 995 info->var.height = -1; 996 info->var.width = -1; 997 998 switch (fb->depth) { 999 case 8: 1000 info->var.red.offset = 0; 1001 info->var.green.offset = 0; 1002 info->var.blue.offset = 0; 1003 info->var.red.length = 8; /* 8bit DAC */ 1004 info->var.green.length = 8; 1005 info->var.blue.length = 8; 1006 info->var.transp.offset = 0; 1007 info->var.transp.length = 0; 1008 break; 1009 case 15: 1010 info->var.red.offset = 10; 1011 info->var.green.offset = 5; 1012 info->var.blue.offset = 0; 1013 info->var.red.length = 5; 1014 info->var.green.length = 5; 1015 info->var.blue.length = 5; 1016 info->var.transp.offset = 15; 1017 info->var.transp.length = 1; 1018 break; 1019 case 16: 1020 info->var.red.offset = 11; 1021 info->var.green.offset = 5; 1022 info->var.blue.offset = 0; 1023 info->var.red.length = 5; 1024 info->var.green.length = 6; 1025 info->var.blue.length = 5; 1026 info->var.transp.offset = 0; 1027 break; 1028 case 24: 1029 info->var.red.offset = 16; 1030 info->var.green.offset = 8; 1031 info->var.blue.offset = 0; 1032 info->var.red.length = 8; 1033 info->var.green.length = 8; 1034 info->var.blue.length = 8; 1035 info->var.transp.offset = 0; 1036 info->var.transp.length = 0; 1037 break; 1038 case 32: 1039 info->var.red.offset = 16; 1040 info->var.green.offset = 8; 1041 info->var.blue.offset = 0; 1042 info->var.red.length = 8; 1043 info->var.green.length = 8; 1044 info->var.blue.length = 8; 1045 info->var.transp.offset = 24; 1046 info->var.transp.length = 8; 1047 break; 1048 default: 1049 break; 1050 } 1051 1052 info->var.xres = fb_width; 1053 info->var.yres = fb_height; 1054 } 1055 #endif 1056 1057 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, 1058 uint32_t maxX, 1059 uint32_t maxY) 1060 { 1061 struct drm_connector *connector; 1062 int count = 0; 1063 int i; 1064 1065 for (i = 0; i < fb_helper->connector_count; i++) { 1066 connector = fb_helper->connector_info[i]->connector; 1067 count += connector->funcs->fill_modes(connector, maxX, maxY); 1068 } 1069 1070 return count; 1071 } 1072 1073 static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) 1074 { 1075 struct drm_display_mode *mode; 1076 1077 list_for_each_entry(mode, &fb_connector->connector->modes, head) { 1078 if (drm_mode_width(mode) > width || 1079 drm_mode_height(mode) > height) 1080 continue; 1081 if (mode->type & DRM_MODE_TYPE_PREFERRED) 1082 return mode; 1083 } 1084 return NULL; 1085 } 1086 1087 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) 1088 { 1089 struct drm_cmdline_mode *cmdline_mode; 1090 cmdline_mode = &fb_connector->cmdline_mode; 1091 return cmdline_mode->specified; 1092 } 1093 1094 static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, 1095 int width, int height) 1096 { 1097 struct drm_cmdline_mode *cmdline_mode; 1098 struct drm_display_mode *mode = NULL; 1099 1100 cmdline_mode = &fb_helper_conn->cmdline_mode; 1101 if (cmdline_mode->specified == false) 1102 return (NULL); 1103 1104 /* attempt to find a matching mode in the list of modes 1105 * we have gotten so far, if not add a CVT mode that conforms 1106 */ 1107 if (cmdline_mode->rb || cmdline_mode->margins) 1108 goto create_mode; 1109 1110 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1111 /* check width/height */ 1112 if (mode->hdisplay != cmdline_mode->xres || 1113 mode->vdisplay != cmdline_mode->yres) 1114 continue; 1115 1116 if (cmdline_mode->refresh_specified) { 1117 if (mode->vrefresh != cmdline_mode->refresh) 1118 continue; 1119 } 1120 1121 if (cmdline_mode->interlace) { 1122 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 1123 continue; 1124 } 1125 return mode; 1126 } 1127 1128 create_mode: 1129 if (cmdline_mode->cvt) 1130 mode = drm_cvt_mode(fb_helper_conn->connector->dev, 1131 cmdline_mode->xres, cmdline_mode->yres, 1132 cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, 1133 cmdline_mode->rb, cmdline_mode->interlace, 1134 cmdline_mode->margins); 1135 else 1136 mode = drm_gtf_mode(fb_helper_conn->connector->dev, 1137 cmdline_mode->xres, cmdline_mode->yres, 1138 cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, 1139 cmdline_mode->interlace, 1140 cmdline_mode->margins); 1141 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 1142 list_add(&mode->head, &fb_helper_conn->connector->modes); 1143 return mode; 1144 } 1145 1146 static bool drm_connector_enabled(struct drm_connector *connector, bool strict) 1147 { 1148 bool enable; 1149 1150 if (strict) { 1151 enable = connector->status == connector_status_connected; 1152 } else { 1153 enable = connector->status != connector_status_disconnected; 1154 } 1155 return enable; 1156 } 1157 1158 static void drm_enable_connectors(struct drm_fb_helper *fb_helper, 1159 bool *enabled) 1160 { 1161 bool any_enabled = false; 1162 struct drm_connector *connector; 1163 int i = 0; 1164 1165 for (i = 0; i < fb_helper->connector_count; i++) { 1166 connector = fb_helper->connector_info[i]->connector; 1167 enabled[i] = drm_connector_enabled(connector, true); 1168 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, 1169 enabled[i] ? "yes" : "no"); 1170 any_enabled |= enabled[i]; 1171 } 1172 1173 if (any_enabled) 1174 return; 1175 1176 for (i = 0; i < fb_helper->connector_count; i++) { 1177 connector = fb_helper->connector_info[i]->connector; 1178 enabled[i] = drm_connector_enabled(connector, false); 1179 } 1180 } 1181 1182 static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1183 struct drm_display_mode **modes, 1184 bool *enabled, int width, int height) 1185 { 1186 int count, i, j; 1187 bool can_clone = false; 1188 struct drm_fb_helper_connector *fb_helper_conn; 1189 struct drm_display_mode *dmt_mode, *mode; 1190 1191 /* only contemplate cloning in the single crtc case */ 1192 if (fb_helper->crtc_count > 1) 1193 return false; 1194 1195 count = 0; 1196 for (i = 0; i < fb_helper->connector_count; i++) { 1197 if (enabled[i]) 1198 count++; 1199 } 1200 1201 /* only contemplate cloning if more than one connector is enabled */ 1202 if (count <= 1) 1203 return false; 1204 1205 /* check the command line or if nothing common pick 1024x768 */ 1206 can_clone = true; 1207 for (i = 0; i < fb_helper->connector_count; i++) { 1208 if (!enabled[i]) 1209 continue; 1210 fb_helper_conn = fb_helper->connector_info[i]; 1211 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1212 if (!modes[i]) { 1213 can_clone = false; 1214 break; 1215 } 1216 for (j = 0; j < i; j++) { 1217 if (!enabled[j]) 1218 continue; 1219 if (!drm_mode_equal(modes[j], modes[i])) 1220 can_clone = false; 1221 } 1222 } 1223 1224 if (can_clone) { 1225 DRM_DEBUG_KMS("can clone using command line\n"); 1226 return true; 1227 } 1228 1229 /* try and find a 1024x768 mode on each connector */ 1230 can_clone = true; 1231 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); 1232 1233 for (i = 0; i < fb_helper->connector_count; i++) { 1234 1235 if (!enabled[i]) 1236 continue; 1237 1238 fb_helper_conn = fb_helper->connector_info[i]; 1239 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1240 if (drm_mode_equal(mode, dmt_mode)) 1241 modes[i] = mode; 1242 } 1243 if (!modes[i]) 1244 can_clone = false; 1245 } 1246 1247 if (can_clone) { 1248 DRM_DEBUG_KMS("can clone using 1024x768\n"); 1249 return true; 1250 } 1251 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); 1252 return false; 1253 } 1254 1255 static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1256 struct drm_display_mode **modes, 1257 bool *enabled, int width, int height) 1258 { 1259 struct drm_fb_helper_connector *fb_helper_conn; 1260 int i; 1261 1262 for (i = 0; i < fb_helper->connector_count; i++) { 1263 fb_helper_conn = fb_helper->connector_info[i]; 1264 1265 if (enabled[i] == false) 1266 continue; 1267 1268 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1269 fb_helper_conn->connector->base.id); 1270 1271 /* got for command line mode first */ 1272 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1273 if (!modes[i]) { 1274 DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1275 fb_helper_conn->connector->base.id); 1276 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1277 } 1278 /* No preferred modes, pick one off the list */ 1279 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { 1280 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) 1281 break; 1282 } 1283 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1284 "none"); 1285 } 1286 return true; 1287 } 1288 1289 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, 1290 struct drm_fb_helper_crtc **best_crtcs, 1291 struct drm_display_mode **modes, 1292 int n, int width, int height) 1293 { 1294 int c, o; 1295 struct drm_device *dev = fb_helper->dev; 1296 struct drm_connector *connector; 1297 struct drm_connector_helper_funcs *connector_funcs; 1298 struct drm_encoder *encoder; 1299 struct drm_fb_helper_crtc *best_crtc; 1300 int my_score, best_score, score; 1301 struct drm_fb_helper_crtc **crtcs, *crtc; 1302 struct drm_fb_helper_connector *fb_helper_conn; 1303 1304 if (n == fb_helper->connector_count) 1305 return 0; 1306 1307 fb_helper_conn = fb_helper->connector_info[n]; 1308 connector = fb_helper_conn->connector; 1309 1310 best_crtcs[n] = NULL; 1311 best_crtc = NULL; 1312 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); 1313 if (modes[n] == NULL) 1314 return best_score; 1315 1316 crtcs = malloc(dev->mode_config.num_connector * 1317 sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, 1318 M_WAITOK | M_ZERO); 1319 1320 my_score = 1; 1321 if (connector->status == connector_status_connected) 1322 my_score++; 1323 if (drm_has_cmdline_mode(fb_helper_conn)) 1324 my_score++; 1325 if (drm_has_preferred_mode(fb_helper_conn, width, height)) 1326 my_score++; 1327 1328 connector_funcs = connector->helper_private; 1329 encoder = connector_funcs->best_encoder(connector); 1330 if (!encoder) 1331 goto out; 1332 1333 /* select a crtc for this connector and then attempt to configure 1334 remaining connectors */ 1335 for (c = 0; c < fb_helper->crtc_count; c++) { 1336 crtc = &fb_helper->crtc_info[c]; 1337 1338 if ((encoder->possible_crtcs & (1 << c)) == 0) { 1339 continue; 1340 } 1341 1342 for (o = 0; o < n; o++) 1343 if (best_crtcs[o] == crtc) 1344 break; 1345 1346 if (o < n) { 1347 /* ignore cloning unless only a single crtc */ 1348 if (fb_helper->crtc_count > 1) 1349 continue; 1350 1351 if (!drm_mode_equal(modes[o], modes[n])) 1352 continue; 1353 } 1354 1355 crtcs[n] = crtc; 1356 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); 1357 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, 1358 width, height); 1359 if (score > best_score) { 1360 best_crtc = crtc; 1361 best_score = score; 1362 memcpy(best_crtcs, crtcs, 1363 dev->mode_config.num_connector * 1364 sizeof(struct drm_fb_helper_crtc *)); 1365 } 1366 } 1367 out: 1368 free(crtcs, DRM_MEM_KMS); 1369 return best_score; 1370 } 1371 1372 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) 1373 { 1374 struct drm_device *dev = fb_helper->dev; 1375 struct drm_fb_helper_crtc **crtcs; 1376 struct drm_display_mode **modes; 1377 struct drm_encoder *encoder; 1378 struct drm_mode_set *modeset; 1379 bool *enabled; 1380 int width, height; 1381 int i, ret; 1382 1383 DRM_DEBUG_KMS("\n"); 1384 1385 width = dev->mode_config.max_width; 1386 height = dev->mode_config.max_height; 1387 1388 /* clean out all the encoder/crtc combos */ 1389 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 1390 encoder->crtc = NULL; 1391 } 1392 1393 crtcs = malloc(dev->mode_config.num_connector * 1394 sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, 1395 M_WAITOK | M_ZERO); 1396 modes = malloc(dev->mode_config.num_connector * 1397 sizeof(struct drm_display_mode *), DRM_MEM_KMS, 1398 M_WAITOK | M_ZERO); 1399 enabled = malloc(dev->mode_config.num_connector * 1400 sizeof(bool), DRM_MEM_KMS, M_WAITOK | M_ZERO); 1401 1402 drm_enable_connectors(fb_helper, enabled); 1403 1404 ret = drm_target_cloned(fb_helper, modes, enabled, width, height); 1405 if (!ret) { 1406 ret = drm_target_preferred(fb_helper, modes, enabled, width, height); 1407 if (!ret) 1408 DRM_ERROR("Unable to find initial modes\n"); 1409 } 1410 1411 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); 1412 1413 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); 1414 1415 /* need to set the modesets up here for use later */ 1416 /* fill out the connector<->crtc mappings into the modesets */ 1417 for (i = 0; i < fb_helper->crtc_count; i++) { 1418 modeset = &fb_helper->crtc_info[i].mode_set; 1419 modeset->num_connectors = 0; 1420 } 1421 1422 for (i = 0; i < fb_helper->connector_count; i++) { 1423 struct drm_display_mode *mode = modes[i]; 1424 struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1425 modeset = &fb_crtc->mode_set; 1426 1427 if (mode && fb_crtc) { 1428 DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1429 mode->name, fb_crtc->mode_set.crtc->base.id); 1430 fb_crtc->desired_mode = mode; 1431 if (modeset->mode) 1432 drm_mode_destroy(dev, modeset->mode); 1433 modeset->mode = drm_mode_duplicate(dev, 1434 fb_crtc->desired_mode); 1435 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1436 } 1437 } 1438 1439 free(crtcs, DRM_MEM_KMS); 1440 free(modes, DRM_MEM_KMS); 1441 free(enabled, DRM_MEM_KMS); 1442 } 1443 1444 /** 1445 * drm_helper_initial_config - setup a sane initial connector configuration 1446 * @dev: DRM device 1447 * 1448 * LOCKING: 1449 * Called at init time, must take mode config lock. 1450 * 1451 * Scan the CRTCs and connectors and try to put together an initial setup. 1452 * At the moment, this is a cloned configuration across all heads with 1453 * a new framebuffer object as the backing store. 1454 * 1455 * RETURNS: 1456 * Zero if everything went ok, nonzero otherwise. 1457 */ 1458 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 1459 { 1460 struct drm_device *dev = fb_helper->dev; 1461 int count = 0; 1462 1463 /* disable all the possible outputs/crtcs before entering KMS mode */ 1464 drm_helper_disable_unused_functions(fb_helper->dev); 1465 1466 drm_fb_helper_parse_command_line(fb_helper); 1467 1468 count = drm_fb_helper_probe_connector_modes(fb_helper, 1469 dev->mode_config.max_width, 1470 dev->mode_config.max_height); 1471 /* 1472 * we shouldn't end up with no modes here. 1473 */ 1474 if (count == 0) { 1475 printf("No connectors reported connected with modes\n"); 1476 } 1477 drm_setup_crtcs(fb_helper); 1478 1479 return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1480 } 1481 1482 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 1483 { 1484 struct drm_device *dev = fb_helper->dev; 1485 int count = 0; 1486 u32 max_width, max_height, bpp_sel; 1487 bool bound = false, crtcs_bound = false; 1488 struct drm_crtc *crtc; 1489 1490 if (!fb_helper->fb) 1491 return 0; 1492 1493 sx_xlock(&dev->mode_config.mutex); 1494 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1495 if (crtc->fb) 1496 crtcs_bound = true; 1497 if (crtc->fb == fb_helper->fb) 1498 bound = true; 1499 } 1500 1501 if (!bound && crtcs_bound) { 1502 fb_helper->delayed_hotplug = true; 1503 sx_xunlock(&dev->mode_config.mutex); 1504 return 0; 1505 } 1506 DRM_DEBUG_KMS("\n"); 1507 1508 max_width = fb_helper->fb->width; 1509 max_height = fb_helper->fb->height; 1510 bpp_sel = fb_helper->fb->bits_per_pixel; 1511 1512 count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, 1513 max_height); 1514 drm_setup_crtcs(fb_helper); 1515 sx_xunlock(&dev->mode_config.mutex); 1516 1517 return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1518 } 1519 1520