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