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