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/sysrq.h> 31 #include <linux/fb.h> 32 #include "drmP.h" 33 #include "drm_crtc.h" 34 #include "drm_fb_helper.h" 35 #include "drm_crtc_helper.h" 36 37 MODULE_AUTHOR("David Airlie, Jesse Barnes"); 38 MODULE_DESCRIPTION("DRM KMS helper"); 39 MODULE_LICENSE("GPL and additional rights"); 40 41 static LIST_HEAD(kernel_fb_helper_list); 42 43 int drm_fb_helper_add_connector(struct drm_connector *connector) 44 { 45 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); 46 if (!connector->fb_helper_private) 47 return -ENOMEM; 48 49 return 0; 50 } 51 EXPORT_SYMBOL(drm_fb_helper_add_connector); 52 53 static int my_atoi(const char *name) 54 { 55 int val = 0; 56 57 for (;; name++) { 58 switch (*name) { 59 case '0' ... '9': 60 val = 10*val+(*name-'0'); 61 break; 62 default: 63 return val; 64 } 65 } 66 } 67 68 /** 69 * drm_fb_helper_connector_parse_command_line - parse command line for connector 70 * @connector - connector to parse line for 71 * @mode_option - per connector mode option 72 * 73 * This parses the connector specific then generic command lines for 74 * modes and options to configure the connector. 75 * 76 * This uses the same parameters as the fb modedb.c, except for extra 77 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] 78 * 79 * enable/enable Digital/disable bit at the end 80 */ 81 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector, 82 const char *mode_option) 83 { 84 const char *name; 85 unsigned int namelen; 86 int res_specified = 0, bpp_specified = 0, refresh_specified = 0; 87 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; 88 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; 89 int i; 90 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; 91 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; 92 struct drm_fb_helper_cmdline_mode *cmdline_mode; 93 94 if (!fb_help_conn) 95 return false; 96 97 cmdline_mode = &fb_help_conn->cmdline_mode; 98 if (!mode_option) 99 mode_option = fb_mode_option; 100 101 if (!mode_option) { 102 cmdline_mode->specified = false; 103 return false; 104 } 105 106 name = mode_option; 107 namelen = strlen(name); 108 for (i = namelen-1; i >= 0; i--) { 109 switch (name[i]) { 110 case '@': 111 namelen = i; 112 if (!refresh_specified && !bpp_specified && 113 !yres_specified) { 114 refresh = my_atoi(&name[i+1]); 115 refresh_specified = 1; 116 if (cvt || rb) 117 cvt = 0; 118 } else 119 goto done; 120 break; 121 case '-': 122 namelen = i; 123 if (!bpp_specified && !yres_specified) { 124 bpp = my_atoi(&name[i+1]); 125 bpp_specified = 1; 126 if (cvt || rb) 127 cvt = 0; 128 } else 129 goto done; 130 break; 131 case 'x': 132 if (!yres_specified) { 133 yres = my_atoi(&name[i+1]); 134 yres_specified = 1; 135 } else 136 goto done; 137 case '0' ... '9': 138 break; 139 case 'M': 140 if (!yres_specified) 141 cvt = 1; 142 break; 143 case 'R': 144 if (!cvt) 145 rb = 1; 146 break; 147 case 'm': 148 if (!cvt) 149 margins = 1; 150 break; 151 case 'i': 152 if (!cvt) 153 interlace = 1; 154 break; 155 case 'e': 156 force = DRM_FORCE_ON; 157 break; 158 case 'D': 159 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) || 160 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) 161 force = DRM_FORCE_ON; 162 else 163 force = DRM_FORCE_ON_DIGITAL; 164 break; 165 case 'd': 166 force = DRM_FORCE_OFF; 167 break; 168 default: 169 goto done; 170 } 171 } 172 if (i < 0 && yres_specified) { 173 xres = my_atoi(name); 174 res_specified = 1; 175 } 176 done: 177 178 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 179 drm_get_connector_name(connector), xres, yres, 180 (refresh) ? refresh : 60, (rb) ? " reduced blanking" : 181 "", (margins) ? " with margins" : "", (interlace) ? 182 " interlaced" : ""); 183 184 if (force) { 185 const char *s; 186 switch (force) { 187 case DRM_FORCE_OFF: s = "OFF"; break; 188 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; 189 default: 190 case DRM_FORCE_ON: s = "ON"; break; 191 } 192 193 DRM_INFO("forcing %s connector %s\n", 194 drm_get_connector_name(connector), s); 195 connector->force = force; 196 } 197 198 if (res_specified) { 199 cmdline_mode->specified = true; 200 cmdline_mode->xres = xres; 201 cmdline_mode->yres = yres; 202 } 203 204 if (refresh_specified) { 205 cmdline_mode->refresh_specified = true; 206 cmdline_mode->refresh = refresh; 207 } 208 209 if (bpp_specified) { 210 cmdline_mode->bpp_specified = true; 211 cmdline_mode->bpp = bpp; 212 } 213 cmdline_mode->rb = rb ? true : false; 214 cmdline_mode->cvt = cvt ? true : false; 215 cmdline_mode->interlace = interlace ? true : false; 216 217 return true; 218 } 219 220 int drm_fb_helper_parse_command_line(struct drm_device *dev) 221 { 222 struct drm_connector *connector; 223 224 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 225 char *option = NULL; 226 227 /* do something on return - turn off connector maybe */ 228 if (fb_get_options(drm_get_connector_name(connector), &option)) 229 continue; 230 231 drm_fb_helper_connector_parse_command_line(connector, option); 232 } 233 return 0; 234 } 235 236 bool drm_fb_helper_force_kernel_mode(void) 237 { 238 int i = 0; 239 bool ret, error = false; 240 struct drm_fb_helper *helper; 241 242 if (list_empty(&kernel_fb_helper_list)) 243 return false; 244 245 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 246 for (i = 0; i < helper->crtc_count; i++) { 247 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; 248 ret = drm_crtc_helper_set_config(mode_set); 249 if (ret) 250 error = true; 251 } 252 } 253 return error; 254 } 255 256 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, 257 void *panic_str) 258 { 259 DRM_ERROR("panic occurred, switching back to text console\n"); 260 return drm_fb_helper_force_kernel_mode(); 261 return 0; 262 } 263 EXPORT_SYMBOL(drm_fb_helper_panic); 264 265 static struct notifier_block paniced = { 266 .notifier_call = drm_fb_helper_panic, 267 }; 268 269 /** 270 * drm_fb_helper_restore - restore the framebuffer console (kernel) config 271 * 272 * Restore's the kernel's fbcon mode, used for lastclose & panic paths. 273 */ 274 void drm_fb_helper_restore(void) 275 { 276 bool ret; 277 ret = drm_fb_helper_force_kernel_mode(); 278 if (ret == true) 279 DRM_ERROR("Failed to restore crtc configuration\n"); 280 } 281 EXPORT_SYMBOL(drm_fb_helper_restore); 282 283 #ifdef CONFIG_MAGIC_SYSRQ 284 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 285 { 286 drm_fb_helper_restore(); 287 } 288 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 289 290 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3) 291 { 292 schedule_work(&drm_fb_helper_restore_work); 293 } 294 295 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 296 .handler = drm_fb_helper_sysrq, 297 .help_msg = "force-fb(V)", 298 .action_msg = "Restore framebuffer console", 299 }; 300 #endif 301 302 static void drm_fb_helper_on(struct fb_info *info) 303 { 304 struct drm_fb_helper *fb_helper = info->par; 305 struct drm_device *dev = fb_helper->dev; 306 struct drm_crtc *crtc; 307 struct drm_encoder *encoder; 308 int i; 309 310 /* 311 * For each CRTC in this fb, turn the crtc on then, 312 * find all associated encoders and turn them on. 313 */ 314 for (i = 0; i < fb_helper->crtc_count; i++) { 315 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 316 struct drm_crtc_helper_funcs *crtc_funcs = 317 crtc->helper_private; 318 319 /* Only mess with CRTCs in this fb */ 320 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || 321 !crtc->enabled) 322 continue; 323 324 mutex_lock(&dev->mode_config.mutex); 325 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); 326 mutex_unlock(&dev->mode_config.mutex); 327 328 /* Found a CRTC on this fb, now find encoders */ 329 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 330 if (encoder->crtc == crtc) { 331 struct drm_encoder_helper_funcs *encoder_funcs; 332 333 encoder_funcs = encoder->helper_private; 334 mutex_lock(&dev->mode_config.mutex); 335 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); 336 mutex_unlock(&dev->mode_config.mutex); 337 } 338 } 339 } 340 } 341 } 342 343 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) 344 { 345 struct drm_fb_helper *fb_helper = info->par; 346 struct drm_device *dev = fb_helper->dev; 347 struct drm_crtc *crtc; 348 struct drm_encoder *encoder; 349 int i; 350 351 /* 352 * For each CRTC in this fb, find all associated encoders 353 * and turn them off, then turn off the CRTC. 354 */ 355 for (i = 0; i < fb_helper->crtc_count; i++) { 356 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 357 struct drm_crtc_helper_funcs *crtc_funcs = 358 crtc->helper_private; 359 360 /* Only mess with CRTCs in this fb */ 361 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || 362 !crtc->enabled) 363 continue; 364 365 /* Found a CRTC on this fb, now find encoders */ 366 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 367 if (encoder->crtc == crtc) { 368 struct drm_encoder_helper_funcs *encoder_funcs; 369 370 encoder_funcs = encoder->helper_private; 371 mutex_lock(&dev->mode_config.mutex); 372 encoder_funcs->dpms(encoder, dpms_mode); 373 mutex_unlock(&dev->mode_config.mutex); 374 } 375 } 376 mutex_lock(&dev->mode_config.mutex); 377 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); 378 mutex_unlock(&dev->mode_config.mutex); 379 } 380 } 381 } 382 383 int drm_fb_helper_blank(int blank, struct fb_info *info) 384 { 385 switch (blank) { 386 /* Display: On; HSync: On, VSync: On */ 387 case FB_BLANK_UNBLANK: 388 drm_fb_helper_on(info); 389 break; 390 /* Display: Off; HSync: On, VSync: On */ 391 case FB_BLANK_NORMAL: 392 drm_fb_helper_off(info, DRM_MODE_DPMS_ON); 393 break; 394 /* Display: Off; HSync: Off, VSync: On */ 395 case FB_BLANK_HSYNC_SUSPEND: 396 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); 397 break; 398 /* Display: Off; HSync: On, VSync: Off */ 399 case FB_BLANK_VSYNC_SUSPEND: 400 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); 401 break; 402 /* Display: Off; HSync: Off, VSync: Off */ 403 case FB_BLANK_POWERDOWN: 404 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); 405 break; 406 } 407 return 0; 408 } 409 EXPORT_SYMBOL(drm_fb_helper_blank); 410 411 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) 412 { 413 int i; 414 415 for (i = 0; i < helper->crtc_count; i++) 416 kfree(helper->crtc_info[i].mode_set.connectors); 417 kfree(helper->crtc_info); 418 } 419 420 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count) 421 { 422 struct drm_device *dev = helper->dev; 423 struct drm_crtc *crtc; 424 int ret = 0; 425 int i; 426 427 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); 428 if (!helper->crtc_info) 429 return -ENOMEM; 430 431 helper->crtc_count = crtc_count; 432 433 for (i = 0; i < crtc_count; i++) { 434 helper->crtc_info[i].mode_set.connectors = 435 kcalloc(max_conn_count, 436 sizeof(struct drm_connector *), 437 GFP_KERNEL); 438 439 if (!helper->crtc_info[i].mode_set.connectors) { 440 ret = -ENOMEM; 441 goto out_free; 442 } 443 helper->crtc_info[i].mode_set.num_connectors = 0; 444 } 445 446 i = 0; 447 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 448 helper->crtc_info[i].crtc_id = crtc->base.id; 449 helper->crtc_info[i].mode_set.crtc = crtc; 450 i++; 451 } 452 helper->conn_limit = max_conn_count; 453 return 0; 454 out_free: 455 drm_fb_helper_crtc_free(helper); 456 return -ENOMEM; 457 } 458 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); 459 460 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 461 u16 blue, u16 regno, struct fb_info *info) 462 { 463 struct drm_fb_helper *fb_helper = info->par; 464 struct drm_framebuffer *fb = fb_helper->fb; 465 int pindex; 466 467 if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 468 u32 *palette; 469 u32 value; 470 /* place color in psuedopalette */ 471 if (regno > 16) 472 return -EINVAL; 473 palette = (u32 *)info->pseudo_palette; 474 red >>= (16 - info->var.red.length); 475 green >>= (16 - info->var.green.length); 476 blue >>= (16 - info->var.blue.length); 477 value = (red << info->var.red.offset) | 478 (green << info->var.green.offset) | 479 (blue << info->var.blue.offset); 480 palette[regno] = value; 481 return 0; 482 } 483 484 pindex = regno; 485 486 if (fb->bits_per_pixel == 16) { 487 pindex = regno << 3; 488 489 if (fb->depth == 16 && regno > 63) 490 return -EINVAL; 491 if (fb->depth == 15 && regno > 31) 492 return -EINVAL; 493 494 if (fb->depth == 16) { 495 u16 r, g, b; 496 int i; 497 if (regno < 32) { 498 for (i = 0; i < 8; i++) 499 fb_helper->funcs->gamma_set(crtc, red, 500 green, blue, pindex + i); 501 } 502 503 fb_helper->funcs->gamma_get(crtc, &r, 504 &g, &b, 505 pindex >> 1); 506 507 for (i = 0; i < 4; i++) 508 fb_helper->funcs->gamma_set(crtc, r, 509 green, b, 510 (pindex >> 1) + i); 511 } 512 } 513 514 if (fb->depth != 16) 515 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); 516 return 0; 517 } 518 519 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 520 { 521 struct drm_fb_helper *fb_helper = info->par; 522 struct drm_device *dev = fb_helper->dev; 523 u16 *red, *green, *blue, *transp; 524 struct drm_crtc *crtc; 525 int i, rc = 0; 526 int start; 527 528 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 529 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 530 for (i = 0; i < fb_helper->crtc_count; i++) { 531 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) 532 break; 533 } 534 if (i == fb_helper->crtc_count) 535 continue; 536 537 red = cmap->red; 538 green = cmap->green; 539 blue = cmap->blue; 540 transp = cmap->transp; 541 start = cmap->start; 542 543 for (i = 0; i < cmap->len; i++) { 544 u16 hred, hgreen, hblue, htransp = 0xffff; 545 546 hred = *red++; 547 hgreen = *green++; 548 hblue = *blue++; 549 550 if (transp) 551 htransp = *transp++; 552 553 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); 554 if (rc) 555 return rc; 556 } 557 crtc_funcs->load_lut(crtc); 558 } 559 return rc; 560 } 561 EXPORT_SYMBOL(drm_fb_helper_setcmap); 562 563 int drm_fb_helper_setcolreg(unsigned regno, 564 unsigned red, 565 unsigned green, 566 unsigned blue, 567 unsigned transp, 568 struct fb_info *info) 569 { 570 struct drm_fb_helper *fb_helper = info->par; 571 struct drm_device *dev = fb_helper->dev; 572 struct drm_crtc *crtc; 573 int i; 574 int ret; 575 576 if (regno > 255) 577 return 1; 578 579 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 580 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 581 for (i = 0; i < fb_helper->crtc_count; i++) { 582 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) 583 break; 584 } 585 if (i == fb_helper->crtc_count) 586 continue; 587 588 ret = setcolreg(crtc, red, green, blue, regno, info); 589 if (ret) 590 return ret; 591 592 crtc_funcs->load_lut(crtc); 593 } 594 return 0; 595 } 596 EXPORT_SYMBOL(drm_fb_helper_setcolreg); 597 598 int drm_fb_helper_check_var(struct fb_var_screeninfo *var, 599 struct fb_info *info) 600 { 601 struct drm_fb_helper *fb_helper = info->par; 602 struct drm_framebuffer *fb = fb_helper->fb; 603 int depth; 604 605 if (var->pixclock != 0) 606 return -EINVAL; 607 608 /* Need to resize the fb object !!! */ 609 if (var->xres > fb->width || var->yres > fb->height) { 610 DRM_ERROR("Requested width/height is greater than current fb " 611 "object %dx%d > %dx%d\n", var->xres, var->yres, 612 fb->width, fb->height); 613 DRM_ERROR("Need resizing code.\n"); 614 return -EINVAL; 615 } 616 617 switch (var->bits_per_pixel) { 618 case 16: 619 depth = (var->green.length == 6) ? 16 : 15; 620 break; 621 case 32: 622 depth = (var->transp.length > 0) ? 32 : 24; 623 break; 624 default: 625 depth = var->bits_per_pixel; 626 break; 627 } 628 629 switch (depth) { 630 case 8: 631 var->red.offset = 0; 632 var->green.offset = 0; 633 var->blue.offset = 0; 634 var->red.length = 8; 635 var->green.length = 8; 636 var->blue.length = 8; 637 var->transp.length = 0; 638 var->transp.offset = 0; 639 break; 640 case 15: 641 var->red.offset = 10; 642 var->green.offset = 5; 643 var->blue.offset = 0; 644 var->red.length = 5; 645 var->green.length = 5; 646 var->blue.length = 5; 647 var->transp.length = 1; 648 var->transp.offset = 15; 649 break; 650 case 16: 651 var->red.offset = 11; 652 var->green.offset = 5; 653 var->blue.offset = 0; 654 var->red.length = 5; 655 var->green.length = 6; 656 var->blue.length = 5; 657 var->transp.length = 0; 658 var->transp.offset = 0; 659 break; 660 case 24: 661 var->red.offset = 16; 662 var->green.offset = 8; 663 var->blue.offset = 0; 664 var->red.length = 8; 665 var->green.length = 8; 666 var->blue.length = 8; 667 var->transp.length = 0; 668 var->transp.offset = 0; 669 break; 670 case 32: 671 var->red.offset = 16; 672 var->green.offset = 8; 673 var->blue.offset = 0; 674 var->red.length = 8; 675 var->green.length = 8; 676 var->blue.length = 8; 677 var->transp.length = 8; 678 var->transp.offset = 24; 679 break; 680 default: 681 return -EINVAL; 682 } 683 return 0; 684 } 685 EXPORT_SYMBOL(drm_fb_helper_check_var); 686 687 /* this will let fbcon do the mode init */ 688 int drm_fb_helper_set_par(struct fb_info *info) 689 { 690 struct drm_fb_helper *fb_helper = info->par; 691 struct drm_device *dev = fb_helper->dev; 692 struct fb_var_screeninfo *var = &info->var; 693 struct drm_crtc *crtc; 694 int ret; 695 int i; 696 697 if (var->pixclock != 0) { 698 DRM_ERROR("PIXEL CLCOK SET\n"); 699 return -EINVAL; 700 } 701 702 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 703 704 for (i = 0; i < fb_helper->crtc_count; i++) { 705 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) 706 break; 707 } 708 if (i == fb_helper->crtc_count) 709 continue; 710 711 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { 712 mutex_lock(&dev->mode_config.mutex); 713 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 714 mutex_unlock(&dev->mode_config.mutex); 715 if (ret) 716 return ret; 717 } 718 } 719 return 0; 720 } 721 EXPORT_SYMBOL(drm_fb_helper_set_par); 722 723 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 724 struct fb_info *info) 725 { 726 struct drm_fb_helper *fb_helper = info->par; 727 struct drm_device *dev = fb_helper->dev; 728 struct drm_mode_set *modeset; 729 struct drm_crtc *crtc; 730 int ret = 0; 731 int i; 732 733 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 734 for (i = 0; i < fb_helper->crtc_count; i++) { 735 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) 736 break; 737 } 738 739 if (i == fb_helper->crtc_count) 740 continue; 741 742 modeset = &fb_helper->crtc_info[i].mode_set; 743 744 modeset->x = var->xoffset; 745 modeset->y = var->yoffset; 746 747 if (modeset->num_connectors) { 748 mutex_lock(&dev->mode_config.mutex); 749 ret = crtc->funcs->set_config(modeset); 750 mutex_unlock(&dev->mode_config.mutex); 751 if (!ret) { 752 info->var.xoffset = var->xoffset; 753 info->var.yoffset = var->yoffset; 754 } 755 } 756 } 757 return ret; 758 } 759 EXPORT_SYMBOL(drm_fb_helper_pan_display); 760 761 int drm_fb_helper_single_fb_probe(struct drm_device *dev, 762 int preferred_bpp, 763 int (*fb_create)(struct drm_device *dev, 764 uint32_t fb_width, 765 uint32_t fb_height, 766 uint32_t surface_width, 767 uint32_t surface_height, 768 uint32_t surface_depth, 769 uint32_t surface_bpp, 770 struct drm_framebuffer **fb_ptr)) 771 { 772 struct drm_crtc *crtc; 773 struct drm_connector *connector; 774 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; 775 unsigned int surface_width = 0, surface_height = 0; 776 int new_fb = 0; 777 int crtc_count = 0; 778 int ret, i, conn_count = 0; 779 struct fb_info *info; 780 struct drm_framebuffer *fb; 781 struct drm_mode_set *modeset = NULL; 782 struct drm_fb_helper *fb_helper; 783 uint32_t surface_depth = 24, surface_bpp = 32; 784 785 /* if driver picks 8 or 16 by default use that 786 for both depth/bpp */ 787 if (preferred_bpp != surface_bpp) { 788 surface_depth = surface_bpp = preferred_bpp; 789 } 790 /* first up get a count of crtcs now in use and new min/maxes width/heights */ 791 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 792 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; 793 794 struct drm_fb_helper_cmdline_mode *cmdline_mode; 795 796 if (!fb_help_conn) 797 continue; 798 799 cmdline_mode = &fb_help_conn->cmdline_mode; 800 801 if (cmdline_mode->bpp_specified) { 802 switch (cmdline_mode->bpp) { 803 case 8: 804 surface_depth = surface_bpp = 8; 805 break; 806 case 15: 807 surface_depth = 15; 808 surface_bpp = 16; 809 break; 810 case 16: 811 surface_depth = surface_bpp = 16; 812 break; 813 case 24: 814 surface_depth = surface_bpp = 24; 815 break; 816 case 32: 817 surface_depth = 24; 818 surface_bpp = 32; 819 break; 820 } 821 break; 822 } 823 } 824 825 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 826 if (drm_helper_crtc_in_use(crtc)) { 827 if (crtc->desired_mode) { 828 if (crtc->desired_mode->hdisplay < fb_width) 829 fb_width = crtc->desired_mode->hdisplay; 830 831 if (crtc->desired_mode->vdisplay < fb_height) 832 fb_height = crtc->desired_mode->vdisplay; 833 834 if (crtc->desired_mode->hdisplay > surface_width) 835 surface_width = crtc->desired_mode->hdisplay; 836 837 if (crtc->desired_mode->vdisplay > surface_height) 838 surface_height = crtc->desired_mode->vdisplay; 839 } 840 crtc_count++; 841 } 842 } 843 844 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { 845 /* hmm everyone went away - assume VGA cable just fell out 846 and will come back later. */ 847 return 0; 848 } 849 850 /* do we have an fb already? */ 851 if (list_empty(&dev->mode_config.fb_kernel_list)) { 852 ret = (*fb_create)(dev, fb_width, fb_height, surface_width, 853 surface_height, surface_depth, surface_bpp, 854 &fb); 855 if (ret) 856 return -EINVAL; 857 new_fb = 1; 858 } else { 859 fb = list_first_entry(&dev->mode_config.fb_kernel_list, 860 struct drm_framebuffer, filp_head); 861 862 /* if someone hotplugs something bigger than we have already allocated, we are pwned. 863 As really we can't resize an fbdev that is in the wild currently due to fbdev 864 not really being designed for the lower layers moving stuff around under it. 865 - so in the grand style of things - punt. */ 866 if ((fb->width < surface_width) || 867 (fb->height < surface_height)) { 868 DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); 869 return -EINVAL; 870 } 871 } 872 873 info = fb->fbdev; 874 fb_helper = info->par; 875 876 crtc_count = 0; 877 /* okay we need to setup new connector sets in the crtcs */ 878 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 879 modeset = &fb_helper->crtc_info[crtc_count].mode_set; 880 modeset->fb = fb; 881 conn_count = 0; 882 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 883 if (connector->encoder) 884 if (connector->encoder->crtc == modeset->crtc) { 885 modeset->connectors[conn_count] = connector; 886 conn_count++; 887 if (conn_count > fb_helper->conn_limit) 888 BUG(); 889 } 890 } 891 892 for (i = conn_count; i < fb_helper->conn_limit; i++) 893 modeset->connectors[i] = NULL; 894 895 modeset->crtc = crtc; 896 crtc_count++; 897 898 modeset->num_connectors = conn_count; 899 if (modeset->crtc->desired_mode) { 900 if (modeset->mode) 901 drm_mode_destroy(dev, modeset->mode); 902 modeset->mode = drm_mode_duplicate(dev, 903 modeset->crtc->desired_mode); 904 } 905 } 906 fb_helper->crtc_count = crtc_count; 907 fb_helper->fb = fb; 908 909 if (new_fb) { 910 info->var.pixclock = 0; 911 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0); 912 if (ret) 913 return ret; 914 if (register_framebuffer(info) < 0) { 915 fb_dealloc_cmap(&info->cmap); 916 return -EINVAL; 917 } 918 } else { 919 drm_fb_helper_set_par(info); 920 } 921 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, 922 info->fix.id); 923 924 /* Switch back to kernel console on panic */ 925 /* multi card linked list maybe */ 926 if (list_empty(&kernel_fb_helper_list)) { 927 printk(KERN_INFO "registered panic notifier\n"); 928 atomic_notifier_chain_register(&panic_notifier_list, 929 &paniced); 930 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 931 } 932 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 933 return 0; 934 } 935 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); 936 937 void drm_fb_helper_free(struct drm_fb_helper *helper) 938 { 939 list_del(&helper->kernel_fb_list); 940 if (list_empty(&kernel_fb_helper_list)) { 941 printk(KERN_INFO "unregistered panic notifier\n"); 942 atomic_notifier_chain_unregister(&panic_notifier_list, 943 &paniced); 944 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 945 } 946 drm_fb_helper_crtc_free(helper); 947 fb_dealloc_cmap(&helper->fb->fbdev->cmap); 948 } 949 EXPORT_SYMBOL(drm_fb_helper_free); 950 951 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 952 uint32_t depth) 953 { 954 info->fix.type = FB_TYPE_PACKED_PIXELS; 955 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : 956 FB_VISUAL_TRUECOLOR; 957 info->fix.type_aux = 0; 958 info->fix.xpanstep = 1; /* doing it in hw */ 959 info->fix.ypanstep = 1; /* doing it in hw */ 960 info->fix.ywrapstep = 0; 961 info->fix.accel = FB_ACCEL_NONE; 962 info->fix.type_aux = 0; 963 964 info->fix.line_length = pitch; 965 return; 966 } 967 EXPORT_SYMBOL(drm_fb_helper_fill_fix); 968 969 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb, 970 uint32_t fb_width, uint32_t fb_height) 971 { 972 info->pseudo_palette = fb->pseudo_palette; 973 info->var.xres_virtual = fb->width; 974 info->var.yres_virtual = fb->height; 975 info->var.bits_per_pixel = fb->bits_per_pixel; 976 info->var.xoffset = 0; 977 info->var.yoffset = 0; 978 info->var.activate = FB_ACTIVATE_NOW; 979 info->var.height = -1; 980 info->var.width = -1; 981 982 switch (fb->depth) { 983 case 8: 984 info->var.red.offset = 0; 985 info->var.green.offset = 0; 986 info->var.blue.offset = 0; 987 info->var.red.length = 8; /* 8bit DAC */ 988 info->var.green.length = 8; 989 info->var.blue.length = 8; 990 info->var.transp.offset = 0; 991 info->var.transp.length = 0; 992 break; 993 case 15: 994 info->var.red.offset = 10; 995 info->var.green.offset = 5; 996 info->var.blue.offset = 0; 997 info->var.red.length = 5; 998 info->var.green.length = 5; 999 info->var.blue.length = 5; 1000 info->var.transp.offset = 15; 1001 info->var.transp.length = 1; 1002 break; 1003 case 16: 1004 info->var.red.offset = 11; 1005 info->var.green.offset = 5; 1006 info->var.blue.offset = 0; 1007 info->var.red.length = 5; 1008 info->var.green.length = 6; 1009 info->var.blue.length = 5; 1010 info->var.transp.offset = 0; 1011 break; 1012 case 24: 1013 info->var.red.offset = 16; 1014 info->var.green.offset = 8; 1015 info->var.blue.offset = 0; 1016 info->var.red.length = 8; 1017 info->var.green.length = 8; 1018 info->var.blue.length = 8; 1019 info->var.transp.offset = 0; 1020 info->var.transp.length = 0; 1021 break; 1022 case 32: 1023 info->var.red.offset = 16; 1024 info->var.green.offset = 8; 1025 info->var.blue.offset = 0; 1026 info->var.red.length = 8; 1027 info->var.green.length = 8; 1028 info->var.blue.length = 8; 1029 info->var.transp.offset = 24; 1030 info->var.transp.length = 8; 1031 break; 1032 default: 1033 break; 1034 } 1035 1036 info->var.xres = fb_width; 1037 info->var.yres = fb_height; 1038 } 1039 EXPORT_SYMBOL(drm_fb_helper_fill_var); 1040