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