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