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