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: releng/10.3/sys/dev/drm2/drm_fb_helper.c 282199 2015-04-28 19:35:05Z dumbbell $"); 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 41MODULE_AUTHOR("David Airlie, Jesse Barnes"); 42MODULE_DESCRIPTION("DRM KMS helper"); 43MODULE_LICENSE("GPL and additional rights"); 44 45static DRM_LIST_HEAD(kernel_fb_helper_list); 46 47#include <sys/kdb.h> 48#include <sys/param.h> 49#include <sys/systm.h> 50 51struct 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. */ 57static void 58vt_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 70static int 71vt_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_fast(taskqueue_thread, &sc->fb_mode_task); 79 else 80 drm_fb_helper_restore_fbdev_mode(sc->fb_helper); 81 82 return (0); 83} 84 85struct fb_info * 86framebuffer_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 102void 103framebuffer_release(struct fb_info *info) 104{ 105 106 free(info->fb_priv, DRM_MEM_KMS); 107 free(info, DRM_MEM_KMS); 108} 109 110static int 111fb_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 = getenv(tunable); 134 if (*option == NULL) 135 *option = 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 */ 150int 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; 168fail: 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} 176EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); 177 178static 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) 234static 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 247static 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 261int 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} 290EXPORT_SYMBOL(drm_fb_helper_debug_enter); 291 292/* Find the real fb for a given fb helper CRTC */ 293static 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 306int 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} 335EXPORT_SYMBOL(drm_fb_helper_debug_leave); 336#endif /* FREEBSD_NOTYET */ 337 338bool 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} 350EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); 351 352static 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) 372int 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} 385EXPORT_SYMBOL(drm_fb_helper_panic); 386 387static 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 */ 397void 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} 404EXPORT_SYMBOL(drm_fb_helper_restore); 405 406#ifdef __linux__ 407#ifdef CONFIG_MAGIC_SYSRQ 408static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 409{ 410 drm_fb_helper_restore(); 411} 412static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 413 414static void drm_fb_helper_sysrq(int dummy1) 415{ 416 schedule_work(&drm_fb_helper_restore_work); 417} 418 419static 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 425static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 426#endif 427#endif 428 429#if 0 && defined(FREEBSD_NOTYET) 430static 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 459int 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} 485EXPORT_SYMBOL(drm_fb_helper_blank); 486#endif /* FREEBSD_NOTYET */ 487 488static 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 503int 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; 546out_free: 547 drm_fb_helper_crtc_free(fb_helper); 548 return -ENOMEM; 549} 550EXPORT_SYMBOL(drm_fb_helper_init); 551 552void 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} 569EXPORT_SYMBOL(drm_fb_helper_fini); 570 571#if 0 && defined(FREEBSD_NOTYET) 572static 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 636int 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} 673EXPORT_SYMBOL(drm_fb_helper_setcmap); 674 675int 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} 765EXPORT_SYMBOL(drm_fb_helper_check_var); 766 767/* this will let fbcon do the mode init */ 768int 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} 799EXPORT_SYMBOL(drm_fb_helper_set_par); 800 801int 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} 831EXPORT_SYMBOL(drm_fb_helper_pan_display); 832#endif /* FREEBSD_NOTYET */ 833 834int 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 /* set the fb pointer */ 925 for (i = 0; i < fb_helper->crtc_count; i++) 926 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; 927 928#if defined(__FreeBSD__) 929 if (new_fb) { 930 device_t fbd; 931 int ret; 932 933 kdev = fb_helper->dev->dev; 934 fbd = device_add_child(kdev, "fbd", device_get_unit(kdev)); 935 if (fbd != NULL) 936 ret = device_probe_and_attach(fbd); 937 else 938 ret = ENODEV; 939#ifdef DEV_VT 940 if (ret != 0) 941 DRM_ERROR("Failed to attach fbd device: %d\n", ret); 942#endif 943 } 944#else 945 if (new_fb) { 946 info->var.pixclock = 0; 947 if (register_framebuffer(info) < 0) 948 return -EINVAL; 949 950 dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", 951 info->node, info->fix.id); 952 953 } else { 954 drm_fb_helper_set_par(info); 955 } 956#endif 957 958#if 0 && defined(FREEBSD_NOTYET) 959 /* Switch back to kernel console on panic */ 960 /* multi card linked list maybe */ 961 if (list_empty(&kernel_fb_helper_list)) { 962 dev_info(fb_helper->dev->dev, "registered panic notifier\n"); 963 atomic_notifier_chain_register(&panic_notifier_list, 964 &paniced); 965 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 966 } 967#endif /* FREEBSD_NOTYET */ 968 if (new_fb) 969 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 970 971 return 0; 972} 973EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); 974 975void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 976 uint32_t depth) 977{ 978 info->fb_stride = pitch; 979 980 return; 981} 982EXPORT_SYMBOL(drm_fb_helper_fill_fix); 983 984void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, 985 uint32_t fb_width, uint32_t fb_height) 986{ 987 struct drm_framebuffer *fb = fb_helper->fb; 988 struct vt_kms_softc *sc; 989 990 info->fb_name = device_get_nameunit(fb_helper->dev->dev); 991 info->fb_width = fb->width; 992 info->fb_height = fb->height; 993 info->fb_depth = fb->bits_per_pixel; 994 995 sc = (struct vt_kms_softc *)info->fb_priv; 996 sc->fb_helper = fb_helper; 997} 998EXPORT_SYMBOL(drm_fb_helper_fill_var); 999 1000static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, 1001 uint32_t maxX, 1002 uint32_t maxY) 1003{ 1004 struct drm_connector *connector; 1005 int count = 0; 1006 int i; 1007 1008 for (i = 0; i < fb_helper->connector_count; i++) { 1009 connector = fb_helper->connector_info[i]->connector; 1010 count += connector->funcs->fill_modes(connector, maxX, maxY); 1011 } 1012 1013 return count; 1014} 1015 1016static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) 1017{ 1018 struct drm_display_mode *mode; 1019 1020 list_for_each_entry(mode, &fb_connector->connector->modes, head) { 1021 if (drm_mode_width(mode) > width || 1022 drm_mode_height(mode) > height) 1023 continue; 1024 if (mode->type & DRM_MODE_TYPE_PREFERRED) 1025 return mode; 1026 } 1027 return NULL; 1028} 1029 1030static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) 1031{ 1032 struct drm_cmdline_mode *cmdline_mode; 1033 cmdline_mode = &fb_connector->cmdline_mode; 1034 return cmdline_mode->specified; 1035} 1036 1037static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, 1038 int width, int height) 1039{ 1040 struct drm_cmdline_mode *cmdline_mode; 1041 struct drm_display_mode *mode = NULL; 1042 1043 cmdline_mode = &fb_helper_conn->cmdline_mode; 1044 if (cmdline_mode->specified == false) 1045 return mode; 1046 1047 /* attempt to find a matching mode in the list of modes 1048 * we have gotten so far, if not add a CVT mode that conforms 1049 */ 1050 if (cmdline_mode->rb || cmdline_mode->margins) 1051 goto create_mode; 1052 1053 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1054 /* check width/height */ 1055 if (mode->hdisplay != cmdline_mode->xres || 1056 mode->vdisplay != cmdline_mode->yres) 1057 continue; 1058 1059 if (cmdline_mode->refresh_specified) { 1060 if (mode->vrefresh != cmdline_mode->refresh) 1061 continue; 1062 } 1063 1064 if (cmdline_mode->interlace) { 1065 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 1066 continue; 1067 } 1068 return mode; 1069 } 1070 1071create_mode: 1072 mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, 1073 cmdline_mode); 1074 list_add(&mode->head, &fb_helper_conn->connector->modes); 1075 return mode; 1076} 1077 1078static bool drm_connector_enabled(struct drm_connector *connector, bool strict) 1079{ 1080 bool enable; 1081 1082 if (strict) 1083 enable = connector->status == connector_status_connected; 1084 else 1085 enable = connector->status != connector_status_disconnected; 1086 1087 return enable; 1088} 1089 1090static void drm_enable_connectors(struct drm_fb_helper *fb_helper, 1091 bool *enabled) 1092{ 1093 bool any_enabled = false; 1094 struct drm_connector *connector; 1095 int i = 0; 1096 1097 for (i = 0; i < fb_helper->connector_count; i++) { 1098 connector = fb_helper->connector_info[i]->connector; 1099 enabled[i] = drm_connector_enabled(connector, true); 1100 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, 1101 enabled[i] ? "yes" : "no"); 1102 any_enabled |= enabled[i]; 1103 } 1104 1105 if (any_enabled) 1106 return; 1107 1108 for (i = 0; i < fb_helper->connector_count; i++) { 1109 connector = fb_helper->connector_info[i]->connector; 1110 enabled[i] = drm_connector_enabled(connector, false); 1111 } 1112} 1113 1114static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1115 struct drm_display_mode **modes, 1116 bool *enabled, int width, int height) 1117{ 1118 int count, i, j; 1119 bool can_clone = false; 1120 struct drm_fb_helper_connector *fb_helper_conn; 1121 struct drm_display_mode *dmt_mode, *mode; 1122 1123 /* only contemplate cloning in the single crtc case */ 1124 if (fb_helper->crtc_count > 1) 1125 return false; 1126 1127 count = 0; 1128 for (i = 0; i < fb_helper->connector_count; i++) { 1129 if (enabled[i]) 1130 count++; 1131 } 1132 1133 /* only contemplate cloning if more than one connector is enabled */ 1134 if (count <= 1) 1135 return false; 1136 1137 /* check the command line or if nothing common pick 1024x768 */ 1138 can_clone = true; 1139 for (i = 0; i < fb_helper->connector_count; i++) { 1140 if (!enabled[i]) 1141 continue; 1142 fb_helper_conn = fb_helper->connector_info[i]; 1143 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1144 if (!modes[i]) { 1145 can_clone = false; 1146 break; 1147 } 1148 for (j = 0; j < i; j++) { 1149 if (!enabled[j]) 1150 continue; 1151 if (!drm_mode_equal(modes[j], modes[i])) 1152 can_clone = false; 1153 } 1154 } 1155 1156 if (can_clone) { 1157 DRM_DEBUG_KMS("can clone using command line\n"); 1158 return true; 1159 } 1160 1161 /* try and find a 1024x768 mode on each connector */ 1162 can_clone = true; 1163 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); 1164 1165 for (i = 0; i < fb_helper->connector_count; i++) { 1166 1167 if (!enabled[i]) 1168 continue; 1169 1170 fb_helper_conn = fb_helper->connector_info[i]; 1171 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1172 if (drm_mode_equal(mode, dmt_mode)) 1173 modes[i] = mode; 1174 } 1175 if (!modes[i]) 1176 can_clone = false; 1177 } 1178 1179 if (can_clone) { 1180 DRM_DEBUG_KMS("can clone using 1024x768\n"); 1181 return true; 1182 } 1183 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); 1184 return false; 1185} 1186 1187static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1188 struct drm_display_mode **modes, 1189 bool *enabled, int width, int height) 1190{ 1191 struct drm_fb_helper_connector *fb_helper_conn; 1192 int i; 1193 1194 for (i = 0; i < fb_helper->connector_count; i++) { 1195 fb_helper_conn = fb_helper->connector_info[i]; 1196 1197 if (enabled[i] == false) 1198 continue; 1199 1200 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1201 fb_helper_conn->connector->base.id); 1202 1203 /* got for command line mode first */ 1204 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1205 if (!modes[i]) { 1206 DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1207 fb_helper_conn->connector->base.id); 1208 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1209 } 1210 /* No preferred modes, pick one off the list */ 1211 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { 1212 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) 1213 break; 1214 } 1215 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1216 "none"); 1217 } 1218 return true; 1219} 1220 1221static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, 1222 struct drm_fb_helper_crtc **best_crtcs, 1223 struct drm_display_mode **modes, 1224 int n, int width, int height) 1225{ 1226 int c, o; 1227 struct drm_device *dev = fb_helper->dev; 1228 struct drm_connector *connector; 1229 struct drm_connector_helper_funcs *connector_funcs; 1230 struct drm_encoder *encoder; 1231 struct drm_fb_helper_crtc *best_crtc; 1232 int my_score, best_score, score; 1233 struct drm_fb_helper_crtc **crtcs, *crtc; 1234 struct drm_fb_helper_connector *fb_helper_conn; 1235 1236 if (n == fb_helper->connector_count) 1237 return 0; 1238 1239 fb_helper_conn = fb_helper->connector_info[n]; 1240 connector = fb_helper_conn->connector; 1241 1242 best_crtcs[n] = NULL; 1243 best_crtc = NULL; 1244 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); 1245 if (modes[n] == NULL) 1246 return best_score; 1247 1248 crtcs = malloc(dev->mode_config.num_connector * 1249 sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1250 if (!crtcs) 1251 return best_score; 1252 1253 my_score = 1; 1254 if (connector->status == connector_status_connected) 1255 my_score++; 1256 if (drm_has_cmdline_mode(fb_helper_conn)) 1257 my_score++; 1258 if (drm_has_preferred_mode(fb_helper_conn, width, height)) 1259 my_score++; 1260 1261 connector_funcs = connector->helper_private; 1262 encoder = connector_funcs->best_encoder(connector); 1263 if (!encoder) 1264 goto out; 1265 1266 /* select a crtc for this connector and then attempt to configure 1267 remaining connectors */ 1268 for (c = 0; c < fb_helper->crtc_count; c++) { 1269 crtc = &fb_helper->crtc_info[c]; 1270 1271 if ((encoder->possible_crtcs & (1 << c)) == 0) 1272 continue; 1273 1274 for (o = 0; o < n; o++) 1275 if (best_crtcs[o] == crtc) 1276 break; 1277 1278 if (o < n) { 1279 /* ignore cloning unless only a single crtc */ 1280 if (fb_helper->crtc_count > 1) 1281 continue; 1282 1283 if (!drm_mode_equal(modes[o], modes[n])) 1284 continue; 1285 } 1286 1287 crtcs[n] = crtc; 1288 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); 1289 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, 1290 width, height); 1291 if (score > best_score) { 1292 best_crtc = crtc; 1293 best_score = score; 1294 memcpy(best_crtcs, crtcs, 1295 dev->mode_config.num_connector * 1296 sizeof(struct drm_fb_helper_crtc *)); 1297 } 1298 } 1299out: 1300 free(crtcs, DRM_MEM_KMS); 1301 return best_score; 1302} 1303 1304static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) 1305{ 1306 struct drm_device *dev = fb_helper->dev; 1307 struct drm_fb_helper_crtc **crtcs; 1308 struct drm_display_mode **modes; 1309 struct drm_mode_set *modeset; 1310 bool *enabled; 1311 int width, height; 1312 int i, ret; 1313 1314 DRM_DEBUG_KMS("\n"); 1315 1316 width = dev->mode_config.max_width; 1317 height = dev->mode_config.max_height; 1318 1319 crtcs = malloc(dev->mode_config.num_connector * 1320 sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1321 modes = malloc(dev->mode_config.num_connector * 1322 sizeof(struct drm_display_mode *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1323 enabled = malloc(dev->mode_config.num_connector * 1324 sizeof(bool), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1325 if (!crtcs || !modes || !enabled) { 1326 DRM_ERROR("Memory allocation failed\n"); 1327 goto out; 1328 } 1329 1330 1331 drm_enable_connectors(fb_helper, enabled); 1332 1333 ret = drm_target_cloned(fb_helper, modes, enabled, width, height); 1334 if (!ret) { 1335 ret = drm_target_preferred(fb_helper, modes, enabled, width, height); 1336 if (!ret) 1337 DRM_ERROR("Unable to find initial modes\n"); 1338 } 1339 1340 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); 1341 1342 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); 1343 1344 /* need to set the modesets up here for use later */ 1345 /* fill out the connector<->crtc mappings into the modesets */ 1346 for (i = 0; i < fb_helper->crtc_count; i++) { 1347 modeset = &fb_helper->crtc_info[i].mode_set; 1348 modeset->num_connectors = 0; 1349 } 1350 1351 for (i = 0; i < fb_helper->connector_count; i++) { 1352 struct drm_display_mode *mode = modes[i]; 1353 struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1354 modeset = &fb_crtc->mode_set; 1355 1356 if (mode && fb_crtc) { 1357 DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1358 mode->name, fb_crtc->mode_set.crtc->base.id); 1359 fb_crtc->desired_mode = mode; 1360 if (modeset->mode) 1361 drm_mode_destroy(dev, modeset->mode); 1362 modeset->mode = drm_mode_duplicate(dev, 1363 fb_crtc->desired_mode); 1364 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1365 } 1366 } 1367 1368out: 1369 free(crtcs, DRM_MEM_KMS); 1370 free(modes, DRM_MEM_KMS); 1371 free(enabled, DRM_MEM_KMS); 1372} 1373 1374/** 1375 * drm_helper_initial_config - setup a sane initial connector configuration 1376 * @fb_helper: fb_helper device struct 1377 * @bpp_sel: bpp value to use for the framebuffer configuration 1378 * 1379 * LOCKING: 1380 * Called at init time by the driver to set up the @fb_helper initial 1381 * configuration, must take the mode config lock. 1382 * 1383 * Scans the CRTCs and connectors and tries to put together an initial setup. 1384 * At the moment, this is a cloned configuration across all heads with 1385 * a new framebuffer object as the backing store. 1386 * 1387 * RETURNS: 1388 * Zero if everything went ok, nonzero otherwise. 1389 */ 1390bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 1391{ 1392 struct drm_device *dev = fb_helper->dev; 1393 int count = 0; 1394 1395 /* disable all the possible outputs/crtcs before entering KMS mode */ 1396 drm_helper_disable_unused_functions(fb_helper->dev); 1397 1398 drm_fb_helper_parse_command_line(fb_helper); 1399 1400 count = drm_fb_helper_probe_connector_modes(fb_helper, 1401 dev->mode_config.max_width, 1402 dev->mode_config.max_height); 1403 /* 1404 * we shouldn't end up with no modes here. 1405 */ 1406 if (count == 0) 1407 dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n"); 1408 1409 drm_setup_crtcs(fb_helper); 1410 1411 return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1412} 1413EXPORT_SYMBOL(drm_fb_helper_initial_config); 1414 1415/** 1416 * drm_fb_helper_hotplug_event - respond to a hotplug notification by 1417 * probing all the outputs attached to the fb 1418 * @fb_helper: the drm_fb_helper 1419 * 1420 * LOCKING: 1421 * Called at runtime, must take mode config lock. 1422 * 1423 * Scan the connectors attached to the fb_helper and try to put together a 1424 * setup after *notification of a change in output configuration. 1425 * 1426 * RETURNS: 1427 * 0 on success and a non-zero error code otherwise. 1428 */ 1429int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 1430{ 1431 struct drm_device *dev = fb_helper->dev; 1432 int count = 0; 1433 u32 max_width, max_height, bpp_sel; 1434 int bound = 0, crtcs_bound = 0; 1435 struct drm_crtc *crtc; 1436 1437 if (!fb_helper->fb) 1438 return 0; 1439 1440 sx_xlock(&dev->mode_config.mutex); 1441 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1442 if (crtc->fb) 1443 crtcs_bound++; 1444 if (crtc->fb == fb_helper->fb) 1445 bound++; 1446 } 1447 1448 if (bound < crtcs_bound) { 1449 fb_helper->delayed_hotplug = true; 1450 sx_xunlock(&dev->mode_config.mutex); 1451 return 0; 1452 } 1453 DRM_DEBUG_KMS("\n"); 1454 1455 max_width = fb_helper->fb->width; 1456 max_height = fb_helper->fb->height; 1457 bpp_sel = fb_helper->fb->bits_per_pixel; 1458 1459 count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, 1460 max_height); 1461 drm_setup_crtcs(fb_helper); 1462 sx_xunlock(&dev->mode_config.mutex); 1463 1464 return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1465} 1466EXPORT_SYMBOL(drm_fb_helper_hotplug_event); 1467