1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/kernel.h> 3 #include <linux/module.h> 4 #include <linux/errno.h> 5 #include <linux/string.h> 6 #include <linux/mm.h> 7 #include <linux/slab.h> 8 #include <linux/delay.h> 9 #include <linux/fb.h> 10 #include <linux/ioport.h> 11 #include <linux/init.h> 12 #include <linux/pci.h> 13 #include <linux/mm_types.h> 14 #include <linux/vmalloc.h> 15 #include <linux/pagemap.h> 16 #include <linux/screen_info.h> 17 #include <linux/console.h> 18 #include <asm/fb.h> 19 #include "sm750.h" 20 #include "sm750_accel.h" 21 #include "sm750_cursor.h" 22 23 /* 24 * #ifdef __BIG_ENDIAN 25 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf, 26 * size_t count, loff_t *ppos); 27 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf, 28 * size_t count, loff_t *ppos); 29 * #endif 30 */ 31 32 /* common var for all device */ 33 static int g_hwcursor = 1; 34 static int g_noaccel; 35 static int g_nomtrr; 36 static const char *g_fbmode[] = {NULL, NULL}; 37 static const char *g_def_fbmode = "1024x768-32@60"; 38 static char *g_settings; 39 static int g_dualview; 40 static char *g_option; 41 42 static const struct fb_videomode lynx750_ext[] = { 43 /* 1024x600-60 VESA [1.71:1] */ 44 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3, 45 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 46 FB_VMODE_NONINTERLACED}, 47 48 /* 1024x600-70 VESA */ 49 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3, 50 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 51 FB_VMODE_NONINTERLACED}, 52 53 /* 1024x600-75 VESA */ 54 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3, 55 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 56 FB_VMODE_NONINTERLACED}, 57 58 /* 1024x600-85 VESA */ 59 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3, 60 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 61 FB_VMODE_NONINTERLACED}, 62 63 /* 720x480 */ 64 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3, 65 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 66 FB_VMODE_NONINTERLACED}, 67 68 /* 1280x720 [1.78:1] */ 69 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3, 70 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 71 FB_VMODE_NONINTERLACED}, 72 73 /* 1280x768@60 */ 74 {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7, 75 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 76 FB_VMODE_NONINTERLACED}, 77 78 /* 1360 x 768 [1.77083:1] */ 79 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3, 80 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 81 FB_VMODE_NONINTERLACED}, 82 83 /* 1368 x 768 [1.78:1] */ 84 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3, 85 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 86 FB_VMODE_NONINTERLACED}, 87 88 /* 1440 x 900 [16:10] */ 89 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3, 90 FB_SYNC_VERT_HIGH_ACT, 91 FB_VMODE_NONINTERLACED}, 92 93 /* 1440x960 [15:10] */ 94 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3, 95 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 96 FB_VMODE_NONINTERLACED}, 97 98 /* 1920x1080 [16:9] */ 99 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3, 100 FB_SYNC_VERT_HIGH_ACT, 101 FB_VMODE_NONINTERLACED}, 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 sm750_hw_cursor_disable(cursor); 122 if (fbcursor->set & FB_CUR_SETSIZE) 123 sm750_hw_cursor_setSize(cursor, 124 fbcursor->image.width, 125 fbcursor->image.height); 126 127 if (fbcursor->set & FB_CUR_SETPOS) 128 sm750_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 sm750_hw_cursor_setColor(cursor, fg, bg); 145 } 146 147 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { 148 sm750_hw_cursor_setData(cursor, 149 fbcursor->rop, 150 fbcursor->image.data, 151 fbcursor->mask); 152 } 153 154 if (fbcursor->enable) 155 sm750_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 * since they fb_count could change during the lifetime of 190 * this lock, we are holding it for all cases. 191 */ 192 spin_lock(&sm750_dev->slock); 193 194 sm750_dev->accel.de_fillrect(&sm750_dev->accel, 195 base, pitch, Bpp, 196 region->dx, region->dy, 197 region->width, region->height, 198 color, rop); 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 * since they fb_count could change during the lifetime of 224 * this lock, we are holding it for all cases. 225 */ 226 spin_lock(&sm750_dev->slock); 227 228 sm750_dev->accel.de_copyarea(&sm750_dev->accel, 229 base, pitch, region->sx, region->sy, 230 base, pitch, Bpp, region->dx, region->dy, 231 region->width, region->height, 232 HW_ROP2_COPY); 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 * since they fb_count could change during the lifetime of 273 * this lock, we are holding it for all cases. 274 */ 275 spin_lock(&sm750_dev->slock); 276 277 sm750_dev->accel.de_imageblit(&sm750_dev->accel, 278 image->data, image->width >> 3, 0, 279 base, pitch, Bpp, 280 image->dx, image->dy, 281 image->width, image->height, 282 fgcol, bgcol, HW_ROP2_COPY); 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 goto lynxfb_suspend_err; 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 goto lynxfb_suspend_err; 432 } 433 } 434 435 pdev->dev.power.power_state = mesg; 436 437 lynxfb_suspend_err: 438 console_unlock(); 439 return ret; 440 } 441 442 static 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 dev_err(&pdev->dev, 461 "error:%d occurred in pci_set_power_state\n", ret); 462 goto lynxfb_resume_err; 463 } 464 465 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) { 466 pci_restore_state(pdev); 467 ret = pci_enable_device(pdev); 468 if (ret) { 469 dev_err(&pdev->dev, 470 "error:%d occurred in pci_enable_device\n", 471 ret); 472 goto lynxfb_resume_err; 473 } 474 pci_set_master(pdev); 475 } 476 477 hw_sm750_inithw(sm750_dev, pdev); 478 479 info = sm750_dev->fbinfo[0]; 480 481 if (info) { 482 par = info->par; 483 crtc = &par->crtc; 484 cursor = &crtc->cursor; 485 memset_io(cursor->vstart, 0x0, cursor->size); 486 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size); 487 lynxfb_ops_set_par(info); 488 fb_set_suspend(info, 0); 489 } 490 491 info = sm750_dev->fbinfo[1]; 492 493 if (info) { 494 par = info->par; 495 crtc = &par->crtc; 496 cursor = &crtc->cursor; 497 memset_io(cursor->vstart, 0x0, cursor->size); 498 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size); 499 lynxfb_ops_set_par(info); 500 fb_set_suspend(info, 0); 501 } 502 503 pdev->dev.power.power_state.event = PM_EVENT_RESUME; 504 505 lynxfb_resume_err: 506 console_unlock(); 507 return ret; 508 } 509 #endif 510 511 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var, 512 struct fb_info *info) 513 { 514 struct lynxfb_par *par; 515 struct lynxfb_crtc *crtc; 516 resource_size_t request; 517 518 par = info->par; 519 crtc = &par->crtc; 520 521 pr_debug("check var:%dx%d-%d\n", 522 var->xres, 523 var->yres, 524 var->bits_per_pixel); 525 526 switch (var->bits_per_pixel) { 527 case 8: 528 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 529 var->red.offset = 0; 530 var->red.length = 8; 531 var->green.offset = 0; 532 var->green.length = 8; 533 var->blue.offset = 0; 534 var->blue.length = 8; 535 var->transp.length = 0; 536 var->transp.offset = 0; 537 break; 538 case 16: 539 var->red.offset = 11; 540 var->red.length = 5; 541 var->green.offset = 5; 542 var->green.length = 6; 543 var->blue.offset = 0; 544 var->blue.length = 5; 545 var->transp.length = 0; 546 var->transp.offset = 0; 547 info->fix.visual = FB_VISUAL_TRUECOLOR; 548 break; 549 case 24: 550 case 32: 551 var->red.offset = 16; 552 var->red.length = 8; 553 var->green.offset = 8; 554 var->green.length = 8; 555 var->blue.offset = 0; 556 var->blue.length = 8; 557 info->fix.visual = FB_VISUAL_TRUECOLOR; 558 break; 559 default: 560 pr_err("bpp %d not supported\n", var->bits_per_pixel); 561 return -EINVAL; 562 } 563 var->height = var->width = -1; 564 var->accel_flags = 0;/* FB_ACCELF_TEXT; */ 565 566 /* check if current fb's video memory big enought to hold the onscreen*/ 567 request = var->xres_virtual * (var->bits_per_pixel >> 3); 568 /* defaulty crtc->channel go with par->index */ 569 570 request = ALIGN(request, crtc->line_pad); 571 request = request * var->yres_virtual; 572 if (crtc->vidmem_size < request) { 573 pr_err("not enough video memory for mode\n"); 574 return -ENOMEM; 575 } 576 577 return hw_sm750_crtc_checkMode(crtc, var); 578 } 579 580 static int lynxfb_ops_setcolreg(unsigned int regno, 581 unsigned int red, 582 unsigned int green, 583 unsigned int blue, 584 unsigned int transp, 585 struct fb_info *info) 586 { 587 struct lynxfb_par *par; 588 struct lynxfb_crtc *crtc; 589 struct fb_var_screeninfo *var; 590 int ret; 591 592 par = info->par; 593 crtc = &par->crtc; 594 var = &info->var; 595 ret = 0; 596 597 if (regno > 256) { 598 pr_err("regno = %d\n", regno); 599 return -EINVAL; 600 } 601 602 if (info->var.grayscale) 603 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 604 605 if (var->bits_per_pixel == 8 && 606 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) { 607 red >>= 8; 608 green >>= 8; 609 blue >>= 8; 610 ret = hw_sm750_setColReg(crtc, regno, red, green, blue); 611 goto exit; 612 } 613 614 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) { 615 u32 val; 616 617 if (var->bits_per_pixel == 16 || 618 var->bits_per_pixel == 32 || 619 var->bits_per_pixel == 24) { 620 val = chan_to_field(red, &var->red); 621 val |= chan_to_field(green, &var->green); 622 val |= chan_to_field(blue, &var->blue); 623 par->pseudo_palette[regno] = val; 624 goto exit; 625 } 626 } 627 628 ret = -EINVAL; 629 630 exit: 631 return ret; 632 } 633 634 static int lynxfb_ops_blank(int blank, struct fb_info *info) 635 { 636 struct lynxfb_par *par; 637 struct lynxfb_output *output; 638 639 pr_debug("blank = %d.\n", blank); 640 par = info->par; 641 output = &par->output; 642 return output->proc_setBLANK(output, blank); 643 } 644 645 static int sm750fb_set_drv(struct lynxfb_par *par) 646 { 647 int ret; 648 struct sm750_dev *sm750_dev; 649 struct lynxfb_output *output; 650 struct lynxfb_crtc *crtc; 651 652 ret = 0; 653 654 sm750_dev = par->dev; 655 output = &par->output; 656 crtc = &par->crtc; 657 658 crtc->vidmem_size = sm750_dev->vidmem_size; 659 if (sm750_dev->fb_count > 1) 660 crtc->vidmem_size >>= 1; 661 662 /* setup crtc and output member */ 663 sm750_dev->hwCursor = g_hwcursor; 664 665 crtc->line_pad = 16; 666 crtc->xpanstep = 8; 667 crtc->ypanstep = 1; 668 crtc->ywrapstep = 0; 669 670 output->proc_setBLANK = (sm750_dev->revid == SM750LE_REVISION_ID) ? 671 hw_sm750le_setBLANK : hw_sm750_setBLANK; 672 /* chip specific phase */ 673 sm750_dev->accel.de_wait = (sm750_dev->revid == SM750LE_REVISION_ID) ? 674 hw_sm750le_deWait : hw_sm750_deWait; 675 switch (sm750_dev->dataflow) { 676 case sm750_simul_pri: 677 output->paths = sm750_pnc; 678 crtc->channel = sm750_primary; 679 crtc->oScreen = 0; 680 crtc->vScreen = sm750_dev->pvMem; 681 pr_info("use simul primary mode\n"); 682 break; 683 case sm750_simul_sec: 684 output->paths = sm750_pnc; 685 crtc->channel = sm750_secondary; 686 crtc->oScreen = 0; 687 crtc->vScreen = sm750_dev->pvMem; 688 break; 689 case sm750_dual_normal: 690 if (par->index == 0) { 691 output->paths = sm750_panel; 692 crtc->channel = sm750_primary; 693 crtc->oScreen = 0; 694 crtc->vScreen = sm750_dev->pvMem; 695 } else { 696 output->paths = sm750_crt; 697 crtc->channel = sm750_secondary; 698 /* not consider of padding stuffs for oScreen,need fix */ 699 crtc->oScreen = sm750_dev->vidmem_size >> 1; 700 crtc->vScreen = sm750_dev->pvMem + crtc->oScreen; 701 } 702 break; 703 case sm750_dual_swap: 704 if (par->index == 0) { 705 output->paths = sm750_panel; 706 crtc->channel = sm750_secondary; 707 crtc->oScreen = 0; 708 crtc->vScreen = sm750_dev->pvMem; 709 } else { 710 output->paths = sm750_crt; 711 crtc->channel = sm750_primary; 712 /* not consider of padding stuffs for oScreen,need fix */ 713 crtc->oScreen = sm750_dev->vidmem_size >> 1; 714 crtc->vScreen = sm750_dev->pvMem + crtc->oScreen; 715 } 716 break; 717 default: 718 ret = -EINVAL; 719 } 720 721 return ret; 722 } 723 724 static struct fb_ops lynxfb_ops = { 725 .owner = THIS_MODULE, 726 .fb_check_var = lynxfb_ops_check_var, 727 .fb_set_par = lynxfb_ops_set_par, 728 .fb_setcolreg = lynxfb_ops_setcolreg, 729 .fb_blank = lynxfb_ops_blank, 730 .fb_fillrect = cfb_fillrect, 731 .fb_imageblit = cfb_imageblit, 732 .fb_copyarea = cfb_copyarea, 733 /* cursor */ 734 .fb_cursor = lynxfb_ops_cursor, 735 }; 736 737 static int lynxfb_set_fbinfo(struct fb_info *info, int index) 738 { 739 int i; 740 struct lynxfb_par *par; 741 struct sm750_dev *sm750_dev; 742 struct lynxfb_crtc *crtc; 743 struct lynxfb_output *output; 744 struct fb_var_screeninfo *var; 745 struct fb_fix_screeninfo *fix; 746 747 const struct fb_videomode *pdb[] = { 748 lynx750_ext, NULL, vesa_modes, 749 }; 750 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE}; 751 static const char * const mdb_desc[] = { 752 "driver prepared modes", 753 "kernel prepared default modedb", 754 "kernel HELPERS prepared vesa_modes", 755 }; 756 757 static const char *fixId[2] = { 758 "sm750_fb1", "sm750_fb2", 759 }; 760 761 int ret, line_length; 762 763 ret = 0; 764 par = (struct lynxfb_par *)info->par; 765 sm750_dev = par->dev; 766 crtc = &par->crtc; 767 output = &par->output; 768 var = &info->var; 769 fix = &info->fix; 770 771 /* set index */ 772 par->index = index; 773 output->channel = &crtc->channel; 774 sm750fb_set_drv(par); 775 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display; 776 777 /* 778 * set current cursor variable and proc pointer, 779 * must be set after crtc member initialized 780 */ 781 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024; 782 crtc->cursor.mmio = sm750_dev->pvReg + 783 0x800f0 + (int)crtc->channel * 0x140; 784 785 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio); 786 crtc->cursor.maxH = crtc->cursor.maxW = 64; 787 crtc->cursor.size = crtc->cursor.maxH * crtc->cursor.maxW * 2 / 8; 788 crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset; 789 790 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size); 791 if (!g_hwcursor) { 792 lynxfb_ops.fb_cursor = NULL; 793 sm750_hw_cursor_disable(&crtc->cursor); 794 } 795 796 /* set info->fbops, must be set before fb_find_mode */ 797 if (!sm750_dev->accel_off) { 798 /* use 2d acceleration */ 799 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect; 800 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea; 801 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit; 802 } 803 info->fbops = &lynxfb_ops; 804 805 if (!g_fbmode[index]) { 806 g_fbmode[index] = g_def_fbmode; 807 if (index) 808 g_fbmode[index] = g_fbmode[0]; 809 } 810 811 for (i = 0; i < 3; i++) { 812 ret = fb_find_mode(var, info, g_fbmode[index], 813 pdb[i], cdb[i], NULL, 8); 814 815 if (ret == 1) { 816 pr_info("success! use specified mode:%s in %s\n", 817 g_fbmode[index], 818 mdb_desc[i]); 819 break; 820 } else if (ret == 2) { 821 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n", 822 g_fbmode[index], 823 mdb_desc[i]); 824 break; 825 } else if (ret == 3) { 826 pr_warn("wanna use default mode\n"); 827 /*break;*/ 828 } else if (ret == 4) { 829 pr_warn("fall back to any valid mode\n"); 830 } else { 831 pr_warn("ret = %d,fb_find_mode failed,with %s\n", 832 ret, 833 mdb_desc[i]); 834 } 835 } 836 837 /* some member of info->var had been set by fb_find_mode */ 838 839 pr_info("Member of info->var is :\n" 840 "xres=%d\n" 841 "yres=%d\n" 842 "xres_virtual=%d\n" 843 "yres_virtual=%d\n" 844 "xoffset=%d\n" 845 "yoffset=%d\n" 846 "bits_per_pixel=%d\n" 847 " ...\n", 848 var->xres, 849 var->yres, 850 var->xres_virtual, 851 var->yres_virtual, 852 var->xoffset, 853 var->yoffset, 854 var->bits_per_pixel); 855 856 /* set par */ 857 par->info = info; 858 859 /* set info */ 860 line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8), 861 crtc->line_pad); 862 863 info->pseudo_palette = &par->pseudo_palette[0]; 864 info->screen_base = crtc->vScreen; 865 pr_debug("screen_base vaddr = %p\n", info->screen_base); 866 info->screen_size = line_length * var->yres_virtual; 867 info->flags = FBINFO_FLAG_DEFAULT | 0; 868 869 /* set info->fix */ 870 fix->type = FB_TYPE_PACKED_PIXELS; 871 fix->type_aux = 0; 872 fix->xpanstep = crtc->xpanstep; 873 fix->ypanstep = crtc->ypanstep; 874 fix->ywrapstep = crtc->ywrapstep; 875 fix->accel = FB_ACCEL_SMI; 876 877 strlcpy(fix->id, fixId[index], sizeof(fix->id)); 878 879 fix->smem_start = crtc->oScreen + sm750_dev->vidmem_start; 880 pr_info("fix->smem_start = %lx\n", fix->smem_start); 881 /* 882 * according to mmap experiment from user space application, 883 * fix->mmio_len should not larger than virtual size 884 * (xres_virtual x yres_virtual x ByPP) 885 * Below line maybe buggy when user mmap fb dev node and write 886 * data into the bound over virtual size 887 */ 888 fix->smem_len = crtc->vidmem_size; 889 pr_info("fix->smem_len = %x\n", fix->smem_len); 890 info->screen_size = fix->smem_len; 891 fix->line_length = line_length; 892 fix->mmio_start = sm750_dev->vidreg_start; 893 pr_info("fix->mmio_start = %lx\n", fix->mmio_start); 894 fix->mmio_len = sm750_dev->vidreg_size; 895 pr_info("fix->mmio_len = %x\n", fix->mmio_len); 896 switch (var->bits_per_pixel) { 897 case 8: 898 fix->visual = FB_VISUAL_PSEUDOCOLOR; 899 break; 900 case 16: 901 case 32: 902 fix->visual = FB_VISUAL_TRUECOLOR; 903 break; 904 } 905 906 /* set var */ 907 var->activate = FB_ACTIVATE_NOW; 908 var->accel_flags = 0; 909 var->vmode = FB_VMODE_NONINTERLACED; 910 911 pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", 912 info->cmap.start, info->cmap.len, 913 info->cmap.red, info->cmap.green, info->cmap.blue, 914 info->cmap.transp); 915 916 ret = fb_alloc_cmap(&info->cmap, 256, 0); 917 if (ret < 0) { 918 pr_err("Could not allocate memory for cmap.\n"); 919 goto exit; 920 } 921 922 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", 923 info->cmap.start, info->cmap.len, 924 info->cmap.red, info->cmap.green, info->cmap.blue, 925 info->cmap.transp); 926 927 exit: 928 lynxfb_ops_check_var(var, info); 929 return ret; 930 } 931 932 /* chip specific g_option configuration routine */ 933 static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src) 934 { 935 char *opt; 936 int swap; 937 938 swap = 0; 939 940 sm750_dev->initParm.chip_clk = 0; 941 sm750_dev->initParm.mem_clk = 0; 942 sm750_dev->initParm.master_clk = 0; 943 sm750_dev->initParm.powerMode = 0; 944 sm750_dev->initParm.setAllEngOff = 0; 945 sm750_dev->initParm.resetMemory = 1; 946 947 /* defaultly turn g_hwcursor on for both view */ 948 g_hwcursor = 3; 949 950 if (!src || !*src) { 951 dev_warn(&sm750_dev->pdev->dev, "no specific g_option.\n"); 952 goto NO_PARAM; 953 } 954 955 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) { 956 dev_info(&sm750_dev->pdev->dev, "opt=%s\n", opt); 957 dev_info(&sm750_dev->pdev->dev, "src=%s\n", src); 958 959 if (!strncmp(opt, "swap", strlen("swap"))) { 960 swap = 1; 961 } else if (!strncmp(opt, "nocrt", strlen("nocrt"))) { 962 sm750_dev->nocrt = 1; 963 } else if (!strncmp(opt, "36bit", strlen("36bit"))) { 964 sm750_dev->pnltype = sm750_doubleTFT; 965 } else if (!strncmp(opt, "18bit", strlen("18bit"))) { 966 sm750_dev->pnltype = sm750_dualTFT; 967 } else if (!strncmp(opt, "24bit", strlen("24bit"))) { 968 sm750_dev->pnltype = sm750_24TFT; 969 } else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) { 970 g_hwcursor &= ~0x1; 971 } else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) { 972 g_hwcursor &= ~0x2; 973 } else if (!strncmp(opt, "nohwc", strlen("nohwc"))) { 974 g_hwcursor = 0; 975 } else { 976 if (!g_fbmode[0]) { 977 g_fbmode[0] = opt; 978 dev_info(&sm750_dev->pdev->dev, 979 "find fbmode0 : %s\n", g_fbmode[0]); 980 } else if (!g_fbmode[1]) { 981 g_fbmode[1] = opt; 982 dev_info(&sm750_dev->pdev->dev, 983 "find fbmode1 : %s\n", g_fbmode[1]); 984 } else { 985 dev_warn(&sm750_dev->pdev->dev, "How many view you wann set?\n"); 986 } 987 } 988 } 989 990 NO_PARAM: 991 if (sm750_dev->revid != SM750LE_REVISION_ID) { 992 if (sm750_dev->fb_count > 1) { 993 if (swap) 994 sm750_dev->dataflow = sm750_dual_swap; 995 else 996 sm750_dev->dataflow = sm750_dual_normal; 997 } else { 998 if (swap) 999 sm750_dev->dataflow = sm750_simul_sec; 1000 else 1001 sm750_dev->dataflow = sm750_simul_pri; 1002 } 1003 } else { 1004 /* SM750LE only have one crt channel */ 1005 sm750_dev->dataflow = sm750_simul_sec; 1006 /* sm750le do not have complex attributes */ 1007 sm750_dev->nocrt = 0; 1008 } 1009 } 1010 1011 static void sm750fb_framebuffer_release(struct sm750_dev *sm750_dev) 1012 { 1013 struct fb_info *fb_info; 1014 1015 while (sm750_dev->fb_count) { 1016 fb_info = sm750_dev->fbinfo[sm750_dev->fb_count - 1]; 1017 unregister_framebuffer(fb_info); 1018 framebuffer_release(fb_info); 1019 sm750_dev->fb_count--; 1020 } 1021 } 1022 1023 static int sm750fb_framebuffer_alloc(struct sm750_dev *sm750_dev, int fbidx) 1024 { 1025 struct fb_info *fb_info; 1026 struct lynxfb_par *par; 1027 int err; 1028 1029 fb_info = framebuffer_alloc(sizeof(struct lynxfb_par), 1030 &sm750_dev->pdev->dev); 1031 if (!fb_info) 1032 return -ENOMEM; 1033 1034 sm750_dev->fbinfo[fbidx] = fb_info; 1035 par = fb_info->par; 1036 par->dev = sm750_dev; 1037 1038 err = lynxfb_set_fbinfo(fb_info, fbidx); 1039 if (err) 1040 goto release_fb; 1041 1042 err = register_framebuffer(fb_info); 1043 if (err < 0) 1044 goto release_fb; 1045 1046 sm750_dev->fb_count++; 1047 1048 return 0; 1049 1050 release_fb: 1051 framebuffer_release(fb_info); 1052 return err; 1053 } 1054 1055 static int lynxfb_kick_out_firmware_fb(struct pci_dev *pdev) 1056 { 1057 struct apertures_struct *ap; 1058 bool primary = false; 1059 1060 ap = alloc_apertures(1); 1061 if (!ap) 1062 return -ENOMEM; 1063 1064 ap->ranges[0].base = pci_resource_start(pdev, 0); 1065 ap->ranges[0].size = pci_resource_len(pdev, 0); 1066 #ifdef CONFIG_X86 1067 primary = pdev->resource[PCI_ROM_RESOURCE].flags & 1068 IORESOURCE_ROM_SHADOW; 1069 #endif 1070 remove_conflicting_framebuffers(ap, "sm750_fb1", primary); 1071 kfree(ap); 1072 return 0; 1073 } 1074 1075 static int lynxfb_pci_probe(struct pci_dev *pdev, 1076 const struct pci_device_id *ent) 1077 { 1078 struct sm750_dev *sm750_dev = NULL; 1079 int max_fb; 1080 int fbidx; 1081 int err; 1082 1083 err = lynxfb_kick_out_firmware_fb(pdev); 1084 if (err) 1085 return err; 1086 1087 /* enable device */ 1088 err = pcim_enable_device(pdev); 1089 if (err) 1090 return err; 1091 1092 err = -ENOMEM; 1093 sm750_dev = devm_kzalloc(&pdev->dev, sizeof(*sm750_dev), GFP_KERNEL); 1094 if (!sm750_dev) 1095 return err; 1096 1097 sm750_dev->fbinfo[0] = sm750_dev->fbinfo[1] = NULL; 1098 sm750_dev->devid = pdev->device; 1099 sm750_dev->revid = pdev->revision; 1100 sm750_dev->pdev = pdev; 1101 sm750_dev->mtrr_off = g_nomtrr; 1102 sm750_dev->mtrr.vram = 0; 1103 sm750_dev->accel_off = g_noaccel; 1104 spin_lock_init(&sm750_dev->slock); 1105 1106 if (!sm750_dev->accel_off) { 1107 /* 1108 * hook deInit and 2d routines, notes that below hw_xxx 1109 * routine can work on most of lynx chips 1110 * if some chip need specific function, 1111 * please hook it in smXXX_set_drv routine 1112 */ 1113 sm750_dev->accel.de_init = sm750_hw_de_init; 1114 sm750_dev->accel.de_fillrect = sm750_hw_fillrect; 1115 sm750_dev->accel.de_copyarea = sm750_hw_copyarea; 1116 sm750_dev->accel.de_imageblit = sm750_hw_imageblit; 1117 } 1118 1119 /* call chip specific setup routine */ 1120 sm750fb_setup(sm750_dev, g_settings); 1121 1122 /* call chip specific mmap routine */ 1123 err = hw_sm750_map(sm750_dev, pdev); 1124 if (err) 1125 return err; 1126 1127 if (!sm750_dev->mtrr_off) 1128 sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start, 1129 sm750_dev->vidmem_size); 1130 1131 memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size); 1132 1133 pci_set_drvdata(pdev, sm750_dev); 1134 1135 /* call chipInit routine */ 1136 hw_sm750_inithw(sm750_dev, pdev); 1137 1138 /* allocate frame buffer info structures according to g_dualview */ 1139 max_fb = g_dualview ? 2 : 1; 1140 for (fbidx = 0; fbidx < max_fb; fbidx++) { 1141 err = sm750fb_framebuffer_alloc(sm750_dev, fbidx); 1142 if (err) 1143 goto release_fb; 1144 } 1145 1146 return 0; 1147 1148 release_fb: 1149 sm750fb_framebuffer_release(sm750_dev); 1150 return err; 1151 } 1152 1153 static void lynxfb_pci_remove(struct pci_dev *pdev) 1154 { 1155 struct sm750_dev *sm750_dev; 1156 1157 sm750_dev = pci_get_drvdata(pdev); 1158 1159 sm750fb_framebuffer_release(sm750_dev); 1160 arch_phys_wc_del(sm750_dev->mtrr.vram); 1161 1162 iounmap(sm750_dev->pvReg); 1163 iounmap(sm750_dev->pvMem); 1164 kfree(g_settings); 1165 } 1166 1167 static int __init lynxfb_setup(char *options) 1168 { 1169 int len; 1170 char *opt, *tmp; 1171 1172 if (!options || !*options) { 1173 pr_warn("no options.\n"); 1174 return 0; 1175 } 1176 1177 pr_info("options:%s\n", options); 1178 1179 len = strlen(options) + 1; 1180 g_settings = kzalloc(len, GFP_KERNEL); 1181 if (!g_settings) 1182 return -ENOMEM; 1183 1184 tmp = g_settings; 1185 1186 /* 1187 * Notes: 1188 * char * strsep(char **s,const char * ct); 1189 * @s: the string to be searched 1190 * @ct :the characters to search for 1191 * 1192 * strsep() updates @options to pointer after the first found token 1193 * it also returns the pointer ahead the token. 1194 */ 1195 while ((opt = strsep(&options, ":")) != NULL) { 1196 /* options that mean for any lynx chips are configured here */ 1197 if (!strncmp(opt, "noaccel", strlen("noaccel"))) { 1198 g_noaccel = 1; 1199 } else if (!strncmp(opt, "nomtrr", strlen("nomtrr"))) { 1200 g_nomtrr = 1; 1201 } else if (!strncmp(opt, "dual", strlen("dual"))) { 1202 g_dualview = 1; 1203 } else { 1204 strcat(tmp, opt); 1205 tmp += strlen(opt); 1206 if (options) 1207 *tmp++ = ':'; 1208 else 1209 *tmp++ = 0; 1210 } 1211 } 1212 1213 /* misc g_settings are transport to chip specific routines */ 1214 pr_info("parameter left for chip specific analysis:%s\n", g_settings); 1215 return 0; 1216 } 1217 1218 static const struct pci_device_id smi_pci_table[] = { 1219 { PCI_DEVICE(0x126f, 0x0750), }, 1220 {0,} 1221 }; 1222 1223 MODULE_DEVICE_TABLE(pci, smi_pci_table); 1224 1225 static struct pci_driver lynxfb_driver = { 1226 .name = "sm750fb", 1227 .id_table = smi_pci_table, 1228 .probe = lynxfb_pci_probe, 1229 .remove = lynxfb_pci_remove, 1230 #ifdef CONFIG_PM 1231 .suspend = lynxfb_suspend, 1232 .resume = lynxfb_resume, 1233 #endif 1234 }; 1235 1236 static int __init lynxfb_init(void) 1237 { 1238 char *option; 1239 1240 #ifdef MODULE 1241 option = g_option; 1242 #else 1243 if (fb_get_options("sm750fb", &option)) 1244 return -ENODEV; 1245 #endif 1246 1247 lynxfb_setup(option); 1248 return pci_register_driver(&lynxfb_driver); 1249 } 1250 module_init(lynxfb_init); 1251 1252 static void __exit lynxfb_exit(void) 1253 { 1254 pci_unregister_driver(&lynxfb_driver); 1255 } 1256 module_exit(lynxfb_exit); 1257 1258 module_param(g_option, charp, 0444); 1259 1260 MODULE_PARM_DESC(g_option, 1261 "\n\t\tCommon options:\n" 1262 "\t\tnoaccel:disable 2d capabilities\n" 1263 "\t\tnomtrr:disable MTRR attribute for video memory\n" 1264 "\t\tdualview:dual frame buffer feature enabled\n" 1265 "\t\tnohwc:disable hardware cursor\n" 1266 "\t\tUsual example:\n" 1267 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n" 1268 ); 1269 1270 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>"); 1271 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>"); 1272 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset"); 1273 MODULE_LICENSE("Dual BSD/GPL"); 1274