1 #include<linux/kernel.h> 2 #include<linux/module.h> 3 #include<linux/errno.h> 4 #include<linux/string.h> 5 #include<linux/mm.h> 6 #include<linux/slab.h> 7 #include<linux/delay.h> 8 #include<linux/fb.h> 9 #include<linux/ioport.h> 10 #include<linux/init.h> 11 #include<linux/pci.h> 12 #include<linux/mm_types.h> 13 #include<linux/vmalloc.h> 14 #include<linux/pagemap.h> 15 #include<linux/screen_info.h> 16 #include<linux/vmalloc.h> 17 #include<linux/pagemap.h> 18 #include <linux/console.h> 19 #include <asm/fb.h> 20 #include "sm750.h" 21 #include "sm750_hw.h" 22 #include "sm750_accel.h" 23 #include "sm750_cursor.h" 24 25 #include "modedb.h" 26 27 int smi_indent = 0; 28 29 30 /* 31 * #ifdef __BIG_ENDIAN 32 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf, 33 * size_t count, loff_t *ppos); 34 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf, 35 * size_t count, loff_t *ppos); 36 * #endif 37 */ 38 39 typedef void (*PROC_SPEC_SETUP)(struct lynx_share*, char *); 40 typedef int (*PROC_SPEC_MAP)(struct lynx_share*, struct pci_dev*); 41 typedef int (*PROC_SPEC_INITHW)(struct lynx_share*, struct pci_dev*); 42 43 44 /* common var for all device */ 45 static int g_hwcursor = 1; 46 static int g_noaccel; 47 static int g_nomtrr; 48 static const char *g_fbmode[] = {NULL, NULL}; 49 static const char *g_def_fbmode = "800x600-16@60"; 50 static char *g_settings = NULL; 51 static int g_dualview; 52 static char *g_option = NULL; 53 54 55 static const struct fb_videomode lynx750_ext[] = { 56 /* 1024x600-60 VESA [1.71:1] */ 57 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3, 58 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 59 FB_VMODE_NONINTERLACED}, 60 61 /* 1024x600-70 VESA */ 62 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3, 63 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 64 FB_VMODE_NONINTERLACED}, 65 66 /* 1024x600-75 VESA */ 67 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3, 68 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 69 FB_VMODE_NONINTERLACED}, 70 71 /* 1024x600-85 VESA */ 72 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3, 73 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 74 FB_VMODE_NONINTERLACED}, 75 76 /* 720x480 */ 77 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3, 78 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 79 FB_VMODE_NONINTERLACED}, 80 81 /* 1280x720 [1.78:1] */ 82 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3, 83 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 84 FB_VMODE_NONINTERLACED}, 85 86 /* 1280x768@60 */ 87 {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7, 88 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 89 FB_VMODE_NONINTERLACED}, 90 91 /* 1360 x 768 [1.77083:1] */ 92 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3, 93 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 94 FB_VMODE_NONINTERLACED}, 95 96 /* 1368 x 768 [1.78:1] */ 97 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3, 98 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 99 FB_VMODE_NONINTERLACED}, 100 101 /* 1440 x 900 [16:10] */ 102 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3, 103 FB_SYNC_VERT_HIGH_ACT, 104 FB_VMODE_NONINTERLACED}, 105 106 /* 1440x960 [15:10] */ 107 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3, 108 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 109 FB_VMODE_NONINTERLACED}, 110 111 /* 1920x1080 [16:9] */ 112 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3, 113 FB_SYNC_VERT_HIGH_ACT, 114 FB_VMODE_NONINTERLACED}, 115 }; 116 117 118 119 120 /* no hardware cursor supported under version 2.6.10, kernel bug */ 121 static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor) 122 { 123 struct lynxfb_par *par; 124 struct lynxfb_crtc *crtc; 125 struct lynx_cursor *cursor; 126 127 par = info->par; 128 crtc = &par->crtc; 129 cursor = &crtc->cursor; 130 131 if (fbcursor->image.width > cursor->maxW || 132 fbcursor->image.height > cursor->maxH || 133 fbcursor->image.depth > 1) { 134 return -ENXIO; 135 } 136 137 cursor->disable(cursor); 138 if (fbcursor->set & FB_CUR_SETSIZE) 139 cursor->setSize(cursor, 140 fbcursor->image.width, 141 fbcursor->image.height); 142 143 if (fbcursor->set & FB_CUR_SETPOS) 144 cursor->setPos(cursor, 145 fbcursor->image.dx - info->var.xoffset, 146 fbcursor->image.dy - info->var.yoffset); 147 148 if (fbcursor->set & FB_CUR_SETCMAP) { 149 /* get the 16bit color of kernel means */ 150 u16 fg, bg; 151 152 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))| 153 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)| 154 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11); 155 156 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))| 157 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)| 158 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11); 159 160 cursor->setColor(cursor, fg, bg); 161 } 162 163 164 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { 165 cursor->setData(cursor, 166 fbcursor->rop, 167 fbcursor->image.data, 168 fbcursor->mask); 169 } 170 171 if (fbcursor->enable) 172 cursor->enable(cursor); 173 174 return 0; 175 } 176 177 static void lynxfb_ops_fillrect(struct fb_info *info, 178 const struct fb_fillrect *region) 179 { 180 struct lynxfb_par *par; 181 struct lynx_share *share; 182 unsigned int base, pitch, Bpp, rop; 183 u32 color; 184 185 if (info->state != FBINFO_STATE_RUNNING) 186 return; 187 188 par = info->par; 189 share = par->share; 190 191 /* each time 2d function begin to work,below three variable always need 192 * be set, seems we can put them together in some place */ 193 base = par->crtc.oScreen; 194 pitch = info->fix.line_length; 195 Bpp = info->var.bits_per_pixel >> 3; 196 197 color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color]; 198 rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY; 199 200 /* 201 * If not use spin_lock,system will die if user load driver 202 * and immediately unload driver frequently (dual) 203 */ 204 if (share->dual) 205 spin_lock(&share->slock); 206 207 share->accel.de_fillrect(&share->accel, 208 base, pitch, Bpp, 209 region->dx, region->dy, 210 region->width, region->height, 211 color, rop); 212 if (share->dual) 213 spin_unlock(&share->slock); 214 } 215 216 static void lynxfb_ops_copyarea(struct fb_info *info, 217 const struct fb_copyarea *region) 218 { 219 struct lynxfb_par *par; 220 struct lynx_share *share; 221 unsigned int base, pitch, Bpp; 222 223 par = info->par; 224 share = par->share; 225 226 /* each time 2d function begin to work,below three variable always need 227 * be set, seems we can put them together in some place */ 228 base = par->crtc.oScreen; 229 pitch = info->fix.line_length; 230 Bpp = info->var.bits_per_pixel >> 3; 231 232 /* 233 * If not use spin_lock, system will die if user load driver 234 * and immediately unload driver frequently (dual) 235 */ 236 if (share->dual) 237 spin_lock(&share->slock); 238 239 share->accel.de_copyarea(&share->accel, 240 base, pitch, region->sx, region->sy, 241 base, pitch, Bpp, region->dx, region->dy, 242 region->width, region->height, HW_ROP2_COPY); 243 if (share->dual) 244 spin_unlock(&share->slock); 245 } 246 247 static void lynxfb_ops_imageblit(struct fb_info *info, 248 const struct fb_image *image) 249 { 250 unsigned int base, pitch, Bpp; 251 unsigned int fgcol, bgcol; 252 struct lynxfb_par *par; 253 struct lynx_share *share; 254 255 par = info->par; 256 share = par->share; 257 /* each time 2d function begin to work,below three variable always need 258 * be set, seems we can put them together in some place */ 259 base = par->crtc.oScreen; 260 pitch = info->fix.line_length; 261 Bpp = info->var.bits_per_pixel >> 3; 262 263 if (image->depth == 1) { 264 if (info->fix.visual == FB_VISUAL_TRUECOLOR || 265 info->fix.visual == FB_VISUAL_DIRECTCOLOR) { 266 fgcol = ((u32 *)info->pseudo_palette)[image->fg_color]; 267 bgcol = ((u32 *)info->pseudo_palette)[image->bg_color]; 268 } else { 269 fgcol = image->fg_color; 270 bgcol = image->bg_color; 271 } 272 goto _do_work; 273 } 274 /* TODO: Implement hardware acceleration for image->depth > 1 */ 275 cfb_imageblit(info, image); 276 return; 277 278 _do_work: 279 /* 280 * If not use spin_lock, system will die if user load driver 281 * and immediately unload driver frequently (dual) 282 */ 283 if (share->dual) 284 spin_lock(&share->slock); 285 286 share->accel.de_imageblit(&share->accel, 287 image->data, image->width>>3, 0, 288 base, pitch, Bpp, 289 image->dx, image->dy, 290 image->width, image->height, 291 fgcol, bgcol, HW_ROP2_COPY); 292 if (share->dual) 293 spin_unlock(&share->slock); 294 } 295 296 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var, 297 struct fb_info *info) 298 { 299 struct lynxfb_par *par; 300 struct lynxfb_crtc *crtc; 301 int ret; 302 303 304 if (!info) 305 return -EINVAL; 306 307 ret = 0; 308 par = info->par; 309 crtc = &par->crtc; 310 ret = crtc->proc_panDisplay(crtc, var, info); 311 312 return ret; 313 } 314 315 static int lynxfb_ops_set_par(struct fb_info *info) 316 { 317 struct lynxfb_par *par; 318 struct lynx_share *share; 319 struct lynxfb_crtc *crtc; 320 struct lynxfb_output *output; 321 struct fb_var_screeninfo *var; 322 struct fb_fix_screeninfo *fix; 323 int ret; 324 unsigned int line_length; 325 326 if (!info) 327 return -EINVAL; 328 329 ret = 0; 330 par = info->par; 331 share = par->share; 332 crtc = &par->crtc; 333 output = &par->output; 334 var = &info->var; 335 fix = &info->fix; 336 337 /* fix structur is not so FIX ... */ 338 line_length = var->xres_virtual * var->bits_per_pixel / 8; 339 line_length = PADDING(crtc->line_pad, line_length); 340 fix->line_length = line_length; 341 pr_info("fix->line_length = %d\n", fix->line_length); 342 343 /* var->red,green,blue,transp are need to be set by driver 344 * and these data should be set before setcolreg routine 345 * */ 346 347 switch (var->bits_per_pixel) { 348 case 8: 349 fix->visual = FB_VISUAL_PSEUDOCOLOR; 350 var->red.offset = 0; 351 var->red.length = 8; 352 var->green.offset = 0; 353 var->green.length = 8; 354 var->blue.offset = 0; 355 var->blue.length = 8; 356 var->transp.length = 0; 357 var->transp.offset = 0; 358 break; 359 case 16: 360 var->red.offset = 11; 361 var->red.length = 5; 362 var->green.offset = 5; 363 var->green.length = 6; 364 var->blue.offset = 0; 365 var->blue.length = 5; 366 var->transp.length = 0; 367 var->transp.offset = 0; 368 fix->visual = FB_VISUAL_TRUECOLOR; 369 break; 370 case 24: 371 case 32: 372 var->red.offset = 16; 373 var->red.length = 8; 374 var->green.offset = 8; 375 var->green.length = 8; 376 var->blue.offset = 0; 377 var->blue.length = 8; 378 fix->visual = FB_VISUAL_TRUECOLOR; 379 break; 380 default: 381 ret = -EINVAL; 382 break; 383 } 384 var->height = var->width = -1; 385 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/ 386 387 if (ret) { 388 pr_err("pixel bpp format not satisfied\n."); 389 return ret; 390 } 391 ret = crtc->proc_setMode(crtc, var, fix); 392 if (!ret) 393 ret = output->proc_setMode(output, var, fix); 394 return ret; 395 } 396 397 static inline unsigned int chan_to_field(unsigned int chan, 398 struct fb_bitfield *bf) 399 { 400 chan &= 0xffff; 401 chan >>= 16 - bf->length; 402 return chan << bf->offset; 403 } 404 405 #ifdef CONFIG_PM 406 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg) 407 { 408 struct fb_info *info; 409 struct lynx_share *share; 410 int ret; 411 412 if (mesg.event == pdev->dev.power.power_state.event) 413 return 0; 414 415 ret = 0; 416 share = pci_get_drvdata(pdev); 417 switch (mesg.event) { 418 case PM_EVENT_FREEZE: 419 case PM_EVENT_PRETHAW: 420 pdev->dev.power.power_state = mesg; 421 return 0; 422 } 423 424 console_lock(); 425 if (mesg.event & PM_EVENT_SLEEP) { 426 info = share->fbinfo[0]; 427 if (info) 428 /* 1 means do suspend */ 429 fb_set_suspend(info, 1); 430 info = share->fbinfo[1]; 431 if (info) 432 /* 1 means do suspend */ 433 fb_set_suspend(info, 1); 434 435 ret = pci_save_state(pdev); 436 if (ret) { 437 pr_err("error:%d occurred in pci_save_state\n", ret); 438 return ret; 439 } 440 441 /* set chip to sleep mode */ 442 if (share->suspend) 443 (*share->suspend)(share); 444 445 pci_disable_device(pdev); 446 ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg)); 447 if (ret) { 448 pr_err("error:%d occurred in pci_set_power_state\n", ret); 449 return ret; 450 } 451 } 452 453 pdev->dev.power.power_state = mesg; 454 console_unlock(); 455 return ret; 456 } 457 458 static int lynxfb_resume(struct pci_dev *pdev) 459 { 460 struct fb_info *info; 461 struct lynx_share *share; 462 463 struct lynxfb_par *par; 464 struct lynxfb_crtc *crtc; 465 struct lynx_cursor *cursor; 466 467 int ret; 468 469 470 ret = 0; 471 share = pci_get_drvdata(pdev); 472 473 console_lock(); 474 475 ret = pci_set_power_state(pdev, PCI_D0); 476 if (ret) { 477 pr_err("error:%d occurred in pci_set_power_state\n", ret); 478 return ret; 479 } 480 481 482 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) { 483 pci_restore_state(pdev); 484 ret = pci_enable_device(pdev); 485 if (ret) { 486 pr_err("error:%d occurred in pci_enable_device\n", ret); 487 return ret; 488 } 489 pci_set_master(pdev); 490 } 491 if (share->resume) 492 (*share->resume)(share); 493 494 hw_sm750_inithw(share, pdev); 495 496 497 info = share->fbinfo[0]; 498 499 if (info) { 500 par = info->par; 501 crtc = &par->crtc; 502 cursor = &crtc->cursor; 503 memset_io(cursor->vstart, 0x0, cursor->size); 504 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size); 505 lynxfb_ops_set_par(info); 506 fb_set_suspend(info, 0); 507 } 508 509 info = share->fbinfo[1]; 510 511 if (info) { 512 par = info->par; 513 crtc = &par->crtc; 514 cursor = &crtc->cursor; 515 memset_io(cursor->vstart, 0x0, cursor->size); 516 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size); 517 lynxfb_ops_set_par(info); 518 fb_set_suspend(info, 0); 519 } 520 521 522 console_unlock(); 523 return ret; 524 } 525 #endif 526 527 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var, 528 struct fb_info *info) 529 { 530 struct lynxfb_par *par; 531 struct lynxfb_crtc *crtc; 532 struct lynxfb_output *output; 533 struct lynx_share *share; 534 int ret; 535 resource_size_t request; 536 537 538 par = info->par; 539 crtc = &par->crtc; 540 output = &par->output; 541 share = par->share; 542 ret = 0; 543 544 pr_debug("check var:%dx%d-%d\n", 545 var->xres, 546 var->yres, 547 var->bits_per_pixel); 548 549 550 switch (var->bits_per_pixel) { 551 case 8: 552 case 16: 553 case 24: /* support 24 bpp for only lynx712/722/720 */ 554 case 32: 555 break; 556 default: 557 pr_err("bpp %d not supported\n", var->bits_per_pixel); 558 ret = -EINVAL; 559 goto exit; 560 } 561 562 switch (var->bits_per_pixel) { 563 case 8: 564 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 565 var->red.offset = 0; 566 var->red.length = 8; 567 var->green.offset = 0; 568 var->green.length = 8; 569 var->blue.offset = 0; 570 var->blue.length = 8; 571 var->transp.length = 0; 572 var->transp.offset = 0; 573 break; 574 case 16: 575 var->red.offset = 11; 576 var->red.length = 5; 577 var->green.offset = 5; 578 var->green.length = 6; 579 var->blue.offset = 0; 580 var->blue.length = 5; 581 var->transp.length = 0; 582 var->transp.offset = 0; 583 info->fix.visual = FB_VISUAL_TRUECOLOR; 584 break; 585 case 24: 586 case 32: 587 var->red.offset = 16; 588 var->red.length = 8; 589 var->green.offset = 8; 590 var->green.length = 8; 591 var->blue.offset = 0; 592 var->blue.length = 8; 593 info->fix.visual = FB_VISUAL_TRUECOLOR; 594 break; 595 default: 596 ret = -EINVAL; 597 break; 598 } 599 var->height = var->width = -1; 600 var->accel_flags = 0;/* FB_ACCELF_TEXT; */ 601 602 /* check if current fb's video memory big enought to hold the onscreen*/ 603 request = var->xres_virtual * (var->bits_per_pixel >> 3); 604 /* defaulty crtc->channel go with par->index */ 605 606 request = PADDING(crtc->line_pad, request); 607 request = request * var->yres_virtual; 608 if (crtc->vidmem_size < request) { 609 pr_err("not enough video memory for mode\n"); 610 return -ENOMEM; 611 } 612 613 ret = output->proc_checkMode(output, var); 614 if (!ret) 615 ret = crtc->proc_checkMode(crtc, var); 616 exit: 617 return ret; 618 } 619 620 621 static int lynxfb_ops_setcolreg(unsigned regno, 622 unsigned red, 623 unsigned green, 624 unsigned blue, 625 unsigned transp, 626 struct fb_info *info) 627 { 628 struct lynxfb_par *par; 629 struct lynxfb_crtc *crtc; 630 struct fb_var_screeninfo *var; 631 int ret; 632 633 par = info->par; 634 crtc = &par->crtc; 635 var = &info->var; 636 ret = 0; 637 638 if (regno > 256) { 639 pr_err("regno = %d\n", regno); 640 return -EINVAL; 641 } 642 643 if (info->var.grayscale) 644 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 645 646 if (var->bits_per_pixel == 8 && 647 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) { 648 red >>= 8; 649 green >>= 8; 650 blue >>= 8; 651 ret = crtc->proc_setColReg(crtc, regno, red, green, blue); 652 goto exit; 653 } 654 655 656 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) { 657 u32 val; 658 659 if (var->bits_per_pixel == 16 || 660 var->bits_per_pixel == 32 || 661 var->bits_per_pixel == 24) { 662 val = chan_to_field(red, &var->red); 663 val |= chan_to_field(green, &var->green); 664 val |= chan_to_field(blue, &var->blue); 665 par->pseudo_palette[regno] = val; 666 goto exit; 667 } 668 } 669 670 ret = -EINVAL; 671 672 exit: 673 return ret; 674 } 675 676 static int lynxfb_ops_blank(int blank, struct fb_info *info) 677 { 678 struct lynxfb_par *par; 679 struct lynxfb_output *output; 680 681 pr_debug("blank = %d.\n", blank); 682 par = info->par; 683 output = &par->output; 684 return output->proc_setBLANK(output, blank); 685 } 686 687 static int sm750fb_set_drv(struct lynxfb_par *par) 688 { 689 int ret; 690 struct lynx_share *share; 691 struct sm750_share *spec_share; 692 struct lynxfb_output *output; 693 struct lynxfb_crtc *crtc; 694 695 ret = 0; 696 697 share = par->share; 698 spec_share = container_of(share, struct sm750_share, share); 699 output = &par->output; 700 crtc = &par->crtc; 701 702 crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size; 703 /* setup crtc and output member */ 704 spec_share->hwCursor = g_hwcursor; 705 706 crtc->proc_setMode = hw_sm750_crtc_setMode; 707 crtc->proc_checkMode = hw_sm750_crtc_checkMode; 708 crtc->proc_setColReg = hw_sm750_setColReg; 709 crtc->proc_panDisplay = hw_sm750_pan_display; 710 crtc->clear = hw_sm750_crtc_clear; 711 crtc->line_pad = 16; 712 crtc->xpanstep = 8; 713 crtc->ypanstep = 1; 714 crtc->ywrapstep = 0; 715 716 output->proc_setMode = hw_sm750_output_setMode; 717 output->proc_checkMode = hw_sm750_output_checkMode; 718 719 output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK; 720 output->clear = hw_sm750_output_clear; 721 /* chip specific phase */ 722 share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait; 723 switch (spec_share->state.dataflow) { 724 case sm750_simul_pri: 725 output->paths = sm750_pnc; 726 crtc->channel = sm750_primary; 727 crtc->oScreen = 0; 728 crtc->vScreen = share->pvMem; 729 pr_info("use simul primary mode\n"); 730 break; 731 case sm750_simul_sec: 732 output->paths = sm750_pnc; 733 crtc->channel = sm750_secondary; 734 crtc->oScreen = 0; 735 crtc->vScreen = share->pvMem; 736 break; 737 case sm750_dual_normal: 738 if (par->index == 0) { 739 output->paths = sm750_panel; 740 crtc->channel = sm750_primary; 741 crtc->oScreen = 0; 742 crtc->vScreen = share->pvMem; 743 } else { 744 output->paths = sm750_crt; 745 crtc->channel = sm750_secondary; 746 /* not consider of padding stuffs for oScreen,need fix */ 747 crtc->oScreen = (share->vidmem_size >> 1); 748 crtc->vScreen = share->pvMem + crtc->oScreen; 749 } 750 break; 751 case sm750_dual_swap: 752 if (par->index == 0) { 753 output->paths = sm750_panel; 754 crtc->channel = sm750_secondary; 755 crtc->oScreen = 0; 756 crtc->vScreen = share->pvMem; 757 } else { 758 output->paths = sm750_crt; 759 crtc->channel = sm750_primary; 760 /* not consider of padding stuffs for oScreen,need fix */ 761 crtc->oScreen = (share->vidmem_size >> 1); 762 crtc->vScreen = share->pvMem + crtc->oScreen; 763 } 764 break; 765 default: 766 ret = -EINVAL; 767 } 768 769 return ret; 770 } 771 772 static struct fb_ops lynxfb_ops = { 773 .owner = THIS_MODULE, 774 .fb_check_var = lynxfb_ops_check_var, 775 .fb_set_par = lynxfb_ops_set_par, 776 .fb_setcolreg = lynxfb_ops_setcolreg, 777 .fb_blank = lynxfb_ops_blank, 778 .fb_fillrect = cfb_fillrect, 779 .fb_imageblit = cfb_imageblit, 780 .fb_copyarea = cfb_copyarea, 781 /* cursor */ 782 .fb_cursor = lynxfb_ops_cursor, 783 }; 784 785 786 static int lynxfb_set_fbinfo(struct fb_info *info, int index) 787 { 788 int i; 789 struct lynxfb_par *par; 790 struct lynx_share *share; 791 struct lynxfb_crtc *crtc; 792 struct lynxfb_output *output; 793 struct fb_var_screeninfo *var; 794 struct fb_fix_screeninfo *fix; 795 796 const struct fb_videomode *pdb[] = { 797 lynx750_ext, NULL, vesa_modes, 798 }; 799 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE}; 800 static const char *mdb_desc[] = { 801 "driver prepared modes", 802 "kernel prepared default modedb", 803 "kernel HELPERS prepared vesa_modes", 804 }; 805 806 807 static const char *fixId[2] = { 808 "sm750_fb1", "sm750_fb2", 809 }; 810 811 int ret, line_length; 812 813 ret = 0; 814 par = (struct lynxfb_par *)info->par; 815 share = par->share; 816 crtc = &par->crtc; 817 output = &par->output; 818 var = &info->var; 819 fix = &info->fix; 820 821 /* set index */ 822 par->index = index; 823 output->channel = &crtc->channel; 824 sm750fb_set_drv(par); 825 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display; 826 827 828 /* set current cursor variable and proc pointer, 829 * must be set after crtc member initialized */ 830 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024; 831 crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140; 832 833 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio); 834 crtc->cursor.maxH = crtc->cursor.maxW = 64; 835 crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8; 836 crtc->cursor.disable = hw_cursor_disable; 837 crtc->cursor.enable = hw_cursor_enable; 838 crtc->cursor.setColor = hw_cursor_setColor; 839 crtc->cursor.setPos = hw_cursor_setPos; 840 crtc->cursor.setSize = hw_cursor_setSize; 841 crtc->cursor.setData = hw_cursor_setData; 842 crtc->cursor.vstart = share->pvMem + crtc->cursor.offset; 843 844 845 crtc->cursor.share = share; 846 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size); 847 if (!g_hwcursor) { 848 lynxfb_ops.fb_cursor = NULL; 849 crtc->cursor.disable(&crtc->cursor); 850 } 851 852 853 /* set info->fbops, must be set before fb_find_mode */ 854 if (!share->accel_off) { 855 /* use 2d acceleration */ 856 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect; 857 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea; 858 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit; 859 } 860 info->fbops = &lynxfb_ops; 861 862 if (!g_fbmode[index]) { 863 g_fbmode[index] = g_def_fbmode; 864 if (index) 865 g_fbmode[index] = g_fbmode[0]; 866 } 867 868 869 for (i = 0; i < 3; i++) { 870 871 ret = fb_find_mode(var, info, g_fbmode[index], 872 pdb[i], cdb[i], NULL, 8); 873 874 if (ret == 1) { 875 pr_info("success! use specified mode:%s in %s\n", 876 g_fbmode[index], 877 mdb_desc[i]); 878 break; 879 } else if (ret == 2) { 880 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n", 881 g_fbmode[index], 882 mdb_desc[i]); 883 break; 884 } else if (ret == 3) { 885 pr_warn("wanna use default mode\n"); 886 /*break;*/ 887 } else if (ret == 4) { 888 pr_warn("fall back to any valid mode\n"); 889 } else { 890 pr_warn("ret = %d,fb_find_mode failed,with %s\n", 891 ret, 892 mdb_desc[i]); 893 } 894 } 895 896 /* some member of info->var had been set by fb_find_mode */ 897 898 pr_info("Member of info->var is :\n\ 899 xres=%d\n\ 900 yres=%d\n\ 901 xres_virtual=%d\n\ 902 yres_virtual=%d\n\ 903 xoffset=%d\n\ 904 yoffset=%d\n\ 905 bits_per_pixel=%d\n \ 906 ...\n", 907 var->xres, 908 var->yres, 909 var->xres_virtual, 910 var->yres_virtual, 911 var->xoffset, 912 var->yoffset, 913 var->bits_per_pixel); 914 915 /* set par */ 916 par->info = info; 917 918 /* set info */ 919 line_length = PADDING(crtc->line_pad, 920 (var->xres_virtual * var->bits_per_pixel/8)); 921 922 info->pseudo_palette = &par->pseudo_palette[0]; 923 info->screen_base = crtc->vScreen; 924 pr_debug("screen_base vaddr = %p\n", info->screen_base); 925 info->screen_size = line_length * var->yres_virtual; 926 info->flags = FBINFO_FLAG_DEFAULT|0; 927 928 /* set info->fix */ 929 fix->type = FB_TYPE_PACKED_PIXELS; 930 fix->type_aux = 0; 931 fix->xpanstep = crtc->xpanstep; 932 fix->ypanstep = crtc->ypanstep; 933 fix->ywrapstep = crtc->ywrapstep; 934 fix->accel = FB_ACCEL_SMI; 935 936 strlcpy(fix->id, fixId[index], sizeof(fix->id)); 937 938 939 fix->smem_start = crtc->oScreen + share->vidmem_start; 940 pr_info("fix->smem_start = %lx\n", fix->smem_start); 941 /* according to mmap experiment from user space application, 942 * fix->mmio_len should not larger than virtual size 943 * (xres_virtual x yres_virtual x ByPP) 944 * Below line maybe buggy when user mmap fb dev node and write 945 * data into the bound over virtual size 946 * */ 947 fix->smem_len = crtc->vidmem_size; 948 pr_info("fix->smem_len = %x\n", fix->smem_len); 949 info->screen_size = fix->smem_len; 950 fix->line_length = line_length; 951 fix->mmio_start = share->vidreg_start; 952 pr_info("fix->mmio_start = %lx\n", fix->mmio_start); 953 fix->mmio_len = share->vidreg_size; 954 pr_info("fix->mmio_len = %x\n", fix->mmio_len); 955 switch (var->bits_per_pixel) { 956 case 8: 957 fix->visual = FB_VISUAL_PSEUDOCOLOR; 958 break; 959 case 16: 960 case 32: 961 fix->visual = FB_VISUAL_TRUECOLOR; 962 break; 963 } 964 965 /* set var */ 966 var->activate = FB_ACTIVATE_NOW; 967 var->accel_flags = 0; 968 var->vmode = FB_VMODE_NONINTERLACED; 969 970 pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", 971 info->cmap.start, info->cmap.len, 972 info->cmap.red, info->cmap.green, info->cmap.blue, 973 info->cmap.transp); 974 975 ret = fb_alloc_cmap(&info->cmap, 256, 0); 976 if (ret < 0) { 977 pr_err("Could not allocate memory for cmap.\n"); 978 goto exit; 979 } 980 981 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", 982 info->cmap.start, info->cmap.len, 983 info->cmap.red, info->cmap.green, info->cmap.blue, 984 info->cmap.transp); 985 986 exit: 987 lynxfb_ops_check_var(var, info); 988 return ret; 989 } 990 991 /* chip specific g_option configuration routine */ 992 static void sm750fb_setup(struct lynx_share *share, char *src) 993 { 994 struct sm750_share *spec_share; 995 char *opt; 996 #ifdef CAP_EXPENSION 997 char *exp_res; 998 #endif 999 int swap; 1000 1001 1002 spec_share = container_of(share, struct sm750_share, share); 1003 #ifdef CAP_EXPENSIION 1004 exp_res = NULL; 1005 #endif 1006 swap = 0; 1007 1008 spec_share->state.initParm.chip_clk = 0; 1009 spec_share->state.initParm.mem_clk = 0; 1010 spec_share->state.initParm.master_clk = 0; 1011 spec_share->state.initParm.powerMode = 0; 1012 spec_share->state.initParm.setAllEngOff = 0; 1013 spec_share->state.initParm.resetMemory = 1; 1014 1015 /* defaultly turn g_hwcursor on for both view */ 1016 g_hwcursor = 3; 1017 1018 if (!src || !*src) { 1019 pr_warn("no specific g_option.\n"); 1020 goto NO_PARAM; 1021 } 1022 1023 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) { 1024 pr_info("opt=%s\n", opt); 1025 pr_info("src=%s\n", src); 1026 1027 if (!strncmp(opt, "swap", strlen("swap"))) 1028 swap = 1; 1029 else if (!strncmp(opt, "nocrt", strlen("nocrt"))) 1030 spec_share->state.nocrt = 1; 1031 else if (!strncmp(opt, "36bit", strlen("36bit"))) 1032 spec_share->state.pnltype = sm750_doubleTFT; 1033 else if (!strncmp(opt, "18bit", strlen("18bit"))) 1034 spec_share->state.pnltype = sm750_dualTFT; 1035 else if (!strncmp(opt, "24bit", strlen("24bit"))) 1036 spec_share->state.pnltype = sm750_24TFT; 1037 #ifdef CAP_EXPANSION 1038 else if (!strncmp(opt, "exp:", strlen("exp:"))) 1039 exp_res = opt + strlen("exp:"); 1040 #endif 1041 else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) 1042 g_hwcursor &= ~0x1; 1043 else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) 1044 g_hwcursor &= ~0x2; 1045 else if (!strncmp(opt, "nohwc", strlen("nohwc"))) 1046 g_hwcursor = 0; 1047 else { 1048 if (!g_fbmode[0]) { 1049 g_fbmode[0] = opt; 1050 pr_info("find fbmode0 : %s\n", g_fbmode[0]); 1051 } else if (!g_fbmode[1]) { 1052 g_fbmode[1] = opt; 1053 pr_info("find fbmode1 : %s\n", g_fbmode[1]); 1054 } else { 1055 pr_warn("How many view you wann set?\n"); 1056 } 1057 } 1058 } 1059 #ifdef CAP_EXPANSION 1060 if (getExpRes(exp_res, 1061 &spec_share->state.xLCD, 1062 &spec_share->state.yLCD)) { 1063 /* seems exp_res is not valid */ 1064 spec_share->state.xLCD = spec_share->state.yLCD = 0; 1065 } 1066 #endif 1067 1068 NO_PARAM: 1069 if (share->revid != SM750LE_REVISION_ID) { 1070 if (share->dual) { 1071 if (swap) 1072 spec_share->state.dataflow = sm750_dual_swap; 1073 else 1074 spec_share->state.dataflow = sm750_dual_normal; 1075 } else { 1076 if (swap) 1077 spec_share->state.dataflow = sm750_simul_sec; 1078 else 1079 spec_share->state.dataflow = sm750_simul_pri; 1080 } 1081 } else { 1082 /* SM750LE only have one crt channel */ 1083 spec_share->state.dataflow = sm750_simul_sec; 1084 /* sm750le do not have complex attributes */ 1085 spec_share->state.nocrt = 0; 1086 } 1087 } 1088 1089 static int lynxfb_pci_probe(struct pci_dev *pdev, 1090 const struct pci_device_id *ent) 1091 { 1092 struct fb_info *info[] = {NULL, NULL}; 1093 struct lynx_share *share = NULL; 1094 1095 struct sm750_share *spec_share = NULL; 1096 size_t spec_offset = 0; 1097 int fbidx; 1098 1099 1100 /* enable device */ 1101 if (pci_enable_device(pdev)) { 1102 pr_err("can not enable device.\n"); 1103 goto err_enable; 1104 } 1105 1106 /* though offset of share in sm750_share is 0, 1107 * we use this marcro as the same */ 1108 spec_offset = offsetof(struct sm750_share, share); 1109 1110 spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL); 1111 if (!spec_share) { 1112 pr_err("Could not allocate memory for share.\n"); 1113 goto err_share; 1114 } 1115 1116 /* setting share structure */ 1117 share = (struct lynx_share *)(&(spec_share->share)); 1118 share->fbinfo[0] = share->fbinfo[1] = NULL; 1119 share->devid = pdev->device; 1120 share->revid = pdev->revision; 1121 1122 pr_info("share->revid = %02x\n", share->revid); 1123 share->pdev = pdev; 1124 share->mtrr_off = g_nomtrr; 1125 share->mtrr.vram = 0; 1126 share->accel_off = g_noaccel; 1127 share->dual = g_dualview; 1128 spin_lock_init(&share->slock); 1129 1130 if (!share->accel_off) { 1131 /* hook deInit and 2d routines, notes that below hw_xxx 1132 * routine can work on most of lynx chips 1133 * if some chip need specific function, 1134 * please hook it in smXXX_set_drv routine */ 1135 share->accel.de_init = hw_de_init; 1136 share->accel.de_fillrect = hw_fillrect; 1137 share->accel.de_copyarea = hw_copyarea; 1138 share->accel.de_imageblit = hw_imageblit; 1139 pr_info("enable 2d acceleration\n"); 1140 } else { 1141 pr_info("disable 2d acceleration\n"); 1142 } 1143 1144 /* call chip specific setup routine */ 1145 sm750fb_setup(share, g_settings); 1146 1147 /* call chip specific mmap routine */ 1148 if (hw_sm750_map(share, pdev)) { 1149 pr_err("Memory map failed\n"); 1150 goto err_map; 1151 } 1152 1153 if (!share->mtrr_off) 1154 share->mtrr.vram = arch_phys_wc_add(share->vidmem_start, 1155 share->vidmem_size); 1156 1157 memset_io(share->pvMem, 0, share->vidmem_size); 1158 1159 pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg); 1160 1161 pci_set_drvdata(pdev, share); 1162 1163 /* call chipInit routine */ 1164 hw_sm750_inithw(share, pdev); 1165 1166 /* allocate frame buffer info structor according to g_dualview */ 1167 fbidx = 0; 1168 ALLOC_FB: 1169 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev); 1170 if (!info[fbidx]) { 1171 pr_err("Could not allocate framebuffer #%d.\n", fbidx); 1172 if (fbidx == 0) 1173 goto err_info0_alloc; 1174 else 1175 goto err_info1_alloc; 1176 } else { 1177 struct lynxfb_par *par; 1178 int errno; 1179 1180 pr_info("framebuffer #%d alloc okay\n", fbidx); 1181 share->fbinfo[fbidx] = info[fbidx]; 1182 par = info[fbidx]->par; 1183 par->share = share; 1184 1185 /* set fb_info structure */ 1186 if (lynxfb_set_fbinfo(info[fbidx], fbidx)) { 1187 pr_err("Failed to initial fb_info #%d.\n", fbidx); 1188 if (fbidx == 0) 1189 goto err_info0_set; 1190 else 1191 goto err_info1_set; 1192 } 1193 1194 /* register frame buffer */ 1195 pr_info("Ready to register framebuffer #%d.\n", fbidx); 1196 errno = register_framebuffer(info[fbidx]); 1197 if (errno < 0) { 1198 pr_err("Failed to register fb_info #%d. err %d\n", 1199 fbidx, 1200 errno); 1201 if (fbidx == 0) 1202 goto err_register0; 1203 else 1204 goto err_register1; 1205 } 1206 pr_info("Accomplished register framebuffer #%d.\n", fbidx); 1207 } 1208 1209 /* no dual view by far */ 1210 fbidx++; 1211 if (share->dual && fbidx < 2) 1212 goto ALLOC_FB; 1213 1214 return 0; 1215 1216 err_register1: 1217 err_info1_set: 1218 framebuffer_release(info[1]); 1219 err_info1_alloc: 1220 unregister_framebuffer(info[0]); 1221 err_register0: 1222 err_info0_set: 1223 framebuffer_release(info[0]); 1224 err_info0_alloc: 1225 err_map: 1226 kfree(spec_share); 1227 err_share: 1228 err_enable: 1229 return -ENODEV; 1230 } 1231 1232 static void lynxfb_pci_remove(struct pci_dev *pdev) 1233 { 1234 struct fb_info *info; 1235 struct lynx_share *share; 1236 void *spec_share; 1237 struct lynxfb_par *par; 1238 int cnt; 1239 1240 cnt = 2; 1241 share = pci_get_drvdata(pdev); 1242 1243 while (cnt-- > 0) { 1244 info = share->fbinfo[cnt]; 1245 if (!info) 1246 continue; 1247 par = info->par; 1248 1249 unregister_framebuffer(info); 1250 /* clean crtc & output allocations */ 1251 par->crtc.clear(&par->crtc); 1252 par->output.clear(&par->output); 1253 /* release frame buffer */ 1254 framebuffer_release(info); 1255 } 1256 arch_phys_wc_del(share->mtrr.vram); 1257 1258 iounmap(share->pvReg); 1259 iounmap(share->pvMem); 1260 spec_share = container_of(share, struct sm750_share, share); 1261 kfree(g_settings); 1262 kfree(spec_share); 1263 pci_set_drvdata(pdev, NULL); 1264 } 1265 1266 static int __init lynxfb_setup(char *options) 1267 { 1268 int len; 1269 char *opt, *tmp; 1270 1271 1272 if (!options || !*options) { 1273 pr_warn("no options.\n"); 1274 return 0; 1275 } 1276 1277 pr_info("options:%s\n", options); 1278 1279 len = strlen(options) + 1; 1280 g_settings = kzalloc(len, GFP_KERNEL); 1281 if (!g_settings) 1282 return -ENOMEM; 1283 1284 tmp = g_settings; 1285 1286 /* Notes: 1287 char * strsep(char **s,const char * ct); 1288 @s: the string to be searched 1289 @ct :the characters to search for 1290 1291 strsep() updates @options to pointer after the first found token 1292 it also returns the pointer ahead the token. 1293 */ 1294 while ((opt = strsep(&options, ":")) != NULL) { 1295 /* options that mean for any lynx chips are configured here */ 1296 if (!strncmp(opt, "noaccel", strlen("noaccel"))) 1297 g_noaccel = 1; 1298 else if (!strncmp(opt, "nomtrr", strlen("nomtrr"))) 1299 g_nomtrr = 1; 1300 else if (!strncmp(opt, "dual", strlen("dual"))) 1301 g_dualview = 1; 1302 else { 1303 strcat(tmp, opt); 1304 tmp += strlen(opt); 1305 if (options != NULL) 1306 *tmp++ = ':'; 1307 else 1308 *tmp++ = 0; 1309 } 1310 } 1311 1312 /* misc g_settings are transport to chip specific routines */ 1313 pr_info("parameter left for chip specific analysis:%s\n", g_settings); 1314 return 0; 1315 } 1316 1317 static struct pci_device_id smi_pci_table[] = { 1318 { PCI_DEVICE(0x126f, 0x0750), }, 1319 {0,} 1320 }; 1321 1322 MODULE_DEVICE_TABLE(pci, smi_pci_table); 1323 1324 static struct pci_driver lynxfb_driver = { 1325 .name = "sm750fb", 1326 .id_table = smi_pci_table, 1327 .probe = lynxfb_pci_probe, 1328 .remove = lynxfb_pci_remove, 1329 #ifdef CONFIG_PM 1330 .suspend = lynxfb_suspend, 1331 .resume = lynxfb_resume, 1332 #endif 1333 }; 1334 1335 1336 static int __init lynxfb_init(void) 1337 { 1338 char *option; 1339 int ret; 1340 1341 #ifdef MODULE 1342 option = g_option; 1343 #else 1344 if (fb_get_options("sm750fb", &option)) 1345 return -ENODEV; 1346 #endif 1347 1348 lynxfb_setup(option); 1349 ret = pci_register_driver(&lynxfb_driver); 1350 return ret; 1351 } 1352 module_init(lynxfb_init); 1353 1354 static void __exit lynxfb_exit(void) 1355 { 1356 pci_unregister_driver(&lynxfb_driver); 1357 } 1358 module_exit(lynxfb_exit); 1359 1360 module_param(g_option, charp, S_IRUGO); 1361 1362 MODULE_PARM_DESC(g_option, 1363 "\n\t\tCommon options:\n" 1364 "\t\tnoaccel:disable 2d capabilities\n" 1365 "\t\tnomtrr:disable MTRR attribute for video memory\n" 1366 "\t\tdualview:dual frame buffer feature enabled\n" 1367 "\t\tnohwc:disable hardware cursor\n" 1368 "\t\tUsual example:\n" 1369 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n" 1370 ); 1371 1372 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>"); 1373 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>"); 1374 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset"); 1375 MODULE_LICENSE("GPL v2"); 1376