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