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