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