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