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