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