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