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