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