of: MSI: Simplify irqdomain lookup
[linux/fpc-iii.git] / drivers / staging / sm750fb / sm750.c
blob860e1c288ad5ee2116b7a99c2ec6863566e1dd48
1 #include <linux/kernel.h>
2 #include <linux/module.h>
3 #include <linux/errno.h>
4 #include <linux/string.h>
5 #include <linux/mm.h>
6 #include <linux/slab.h>
7 #include <linux/delay.h>
8 #include <linux/fb.h>
9 #include <linux/ioport.h>
10 #include <linux/init.h>
11 #include <linux/pci.h>
12 #include <linux/mm_types.h>
13 #include <linux/vmalloc.h>
14 #include <linux/pagemap.h>
15 #include <linux/screen_info.h>
16 #include <linux/vmalloc.h>
17 #include <linux/pagemap.h>
18 #include <linux/console.h>
19 #include <asm/fb.h>
20 #include "sm750.h"
21 #include "sm750_accel.h"
22 #include "sm750_cursor.h"
24 #include "modedb.h"
27 * #ifdef __BIG_ENDIAN
28 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
29 * size_t count, loff_t *ppos);
30 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
31 * size_t count, loff_t *ppos);
32 * #endif
35 /* common var for all device */
36 static int g_hwcursor = 1;
37 static int g_noaccel;
38 static int g_nomtrr;
39 static const char *g_fbmode[] = {NULL, NULL};
40 static const char *g_def_fbmode = "800x600-16@60";
41 static char *g_settings;
42 static int g_dualview;
43 static char *g_option;
45 static const struct fb_videomode lynx750_ext[] = {
46 /* 1024x600-60 VESA [1.71:1] */
47 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
48 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
49 FB_VMODE_NONINTERLACED},
51 /* 1024x600-70 VESA */
52 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
53 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
54 FB_VMODE_NONINTERLACED},
56 /* 1024x600-75 VESA */
57 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
58 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
59 FB_VMODE_NONINTERLACED},
61 /* 1024x600-85 VESA */
62 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
63 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
64 FB_VMODE_NONINTERLACED},
66 /* 720x480 */
67 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
68 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
69 FB_VMODE_NONINTERLACED},
71 /* 1280x720 [1.78:1] */
72 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
73 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74 FB_VMODE_NONINTERLACED},
76 /* 1280x768@60 */
77 {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
78 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79 FB_VMODE_NONINTERLACED},
81 /* 1360 x 768 [1.77083:1] */
82 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
83 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
84 FB_VMODE_NONINTERLACED},
86 /* 1368 x 768 [1.78:1] */
87 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
88 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89 FB_VMODE_NONINTERLACED},
91 /* 1440 x 900 [16:10] */
92 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
93 FB_SYNC_VERT_HIGH_ACT,
94 FB_VMODE_NONINTERLACED},
96 /* 1440x960 [15:10] */
97 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
98 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
99 FB_VMODE_NONINTERLACED},
101 /* 1920x1080 [16:9] */
102 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
103 FB_SYNC_VERT_HIGH_ACT,
104 FB_VMODE_NONINTERLACED},
108 /* no hardware cursor supported under version 2.6.10, kernel bug */
109 static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
111 struct lynxfb_par *par;
112 struct lynxfb_crtc *crtc;
113 struct lynx_cursor *cursor;
115 par = info->par;
116 crtc = &par->crtc;
117 cursor = &crtc->cursor;
119 if (fbcursor->image.width > cursor->maxW ||
120 fbcursor->image.height > cursor->maxH ||
121 fbcursor->image.depth > 1) {
122 return -ENXIO;
125 hw_cursor_disable(cursor);
126 if (fbcursor->set & FB_CUR_SETSIZE)
127 hw_cursor_setSize(cursor,
128 fbcursor->image.width,
129 fbcursor->image.height);
131 if (fbcursor->set & FB_CUR_SETPOS)
132 hw_cursor_setPos(cursor,
133 fbcursor->image.dx - info->var.xoffset,
134 fbcursor->image.dy - info->var.yoffset);
136 if (fbcursor->set & FB_CUR_SETCMAP) {
137 /* get the 16bit color of kernel means */
138 u16 fg, bg;
140 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800)) |
141 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5) |
142 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
144 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800)) |
145 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5) |
146 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
148 hw_cursor_setColor(cursor, fg, bg);
151 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
152 hw_cursor_setData(cursor,
153 fbcursor->rop,
154 fbcursor->image.data,
155 fbcursor->mask);
158 if (fbcursor->enable)
159 hw_cursor_enable(cursor);
161 return 0;
164 static void lynxfb_ops_fillrect(struct fb_info *info,
165 const struct fb_fillrect *region)
167 struct lynxfb_par *par;
168 struct sm750_dev *sm750_dev;
169 unsigned int base, pitch, Bpp, rop;
170 u32 color;
172 if (info->state != FBINFO_STATE_RUNNING)
173 return;
175 par = info->par;
176 sm750_dev = par->dev;
179 * each time 2d function begin to work,below three variable always need
180 * be set, seems we can put them together in some place
182 base = par->crtc.oScreen;
183 pitch = info->fix.line_length;
184 Bpp = info->var.bits_per_pixel >> 3;
186 color = (Bpp == 1) ? region->color :
187 ((u32 *)info->pseudo_palette)[region->color];
188 rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR : HW_ROP2_COPY;
191 * If not use spin_lock,system will die if user load driver
192 * and immediately unload driver frequently (dual)
194 if (sm750_dev->dual)
195 spin_lock(&sm750_dev->slock);
197 sm750_dev->accel.de_fillrect(&sm750_dev->accel,
198 base, pitch, Bpp,
199 region->dx, region->dy,
200 region->width, region->height,
201 color, rop);
202 if (sm750_dev->dual)
203 spin_unlock(&sm750_dev->slock);
206 static void lynxfb_ops_copyarea(struct fb_info *info,
207 const struct fb_copyarea *region)
209 struct lynxfb_par *par;
210 struct sm750_dev *sm750_dev;
211 unsigned int base, pitch, Bpp;
213 par = info->par;
214 sm750_dev = par->dev;
217 * each time 2d function begin to work,below three variable always need
218 * be set, seems we can put them together in some place
220 base = par->crtc.oScreen;
221 pitch = info->fix.line_length;
222 Bpp = info->var.bits_per_pixel >> 3;
225 * If not use spin_lock, system will die if user load driver
226 * and immediately unload driver frequently (dual)
228 if (sm750_dev->dual)
229 spin_lock(&sm750_dev->slock);
231 sm750_dev->accel.de_copyarea(&sm750_dev->accel,
232 base, pitch, region->sx, region->sy,
233 base, pitch, Bpp, region->dx, region->dy,
234 region->width, region->height,
235 HW_ROP2_COPY);
236 if (sm750_dev->dual)
237 spin_unlock(&sm750_dev->slock);
240 static void lynxfb_ops_imageblit(struct fb_info *info,
241 const struct fb_image *image)
243 unsigned int base, pitch, Bpp;
244 unsigned int fgcol, bgcol;
245 struct lynxfb_par *par;
246 struct sm750_dev *sm750_dev;
248 par = info->par;
249 sm750_dev = par->dev;
251 * each time 2d function begin to work,below three variable always need
252 * be set, seems we can put them together in some place
254 base = par->crtc.oScreen;
255 pitch = info->fix.line_length;
256 Bpp = info->var.bits_per_pixel >> 3;
258 /* TODO: Implement hardware acceleration for image->depth > 1 */
259 if (image->depth != 1) {
260 cfb_imageblit(info, image);
261 return;
264 if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
265 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
266 fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
267 bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
268 } else {
269 fgcol = image->fg_color;
270 bgcol = image->bg_color;
274 * If not use spin_lock, system will die if user load driver
275 * and immediately unload driver frequently (dual)
277 if (sm750_dev->dual)
278 spin_lock(&sm750_dev->slock);
280 sm750_dev->accel.de_imageblit(&sm750_dev->accel,
281 image->data, image->width >> 3, 0,
282 base, pitch, Bpp,
283 image->dx, image->dy,
284 image->width, image->height,
285 fgcol, bgcol, HW_ROP2_COPY);
286 if (sm750_dev->dual)
287 spin_unlock(&sm750_dev->slock);
290 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
291 struct fb_info *info)
293 struct lynxfb_par *par;
294 struct lynxfb_crtc *crtc;
296 if (!info)
297 return -EINVAL;
299 par = info->par;
300 crtc = &par->crtc;
301 return hw_sm750_pan_display(crtc, var, info);
304 static int lynxfb_ops_set_par(struct fb_info *info)
306 struct lynxfb_par *par;
307 struct lynxfb_crtc *crtc;
308 struct lynxfb_output *output;
309 struct fb_var_screeninfo *var;
310 struct fb_fix_screeninfo *fix;
311 int ret;
312 unsigned int line_length;
314 if (!info)
315 return -EINVAL;
317 ret = 0;
318 par = info->par;
319 crtc = &par->crtc;
320 output = &par->output;
321 var = &info->var;
322 fix = &info->fix;
324 /* fix structur is not so FIX ... */
325 line_length = var->xres_virtual * var->bits_per_pixel / 8;
326 line_length = ALIGN(line_length, crtc->line_pad);
327 fix->line_length = line_length;
328 pr_info("fix->line_length = %d\n", fix->line_length);
331 * var->red,green,blue,transp are need to be set by driver
332 * and these data should be set before setcolreg routine
335 switch (var->bits_per_pixel) {
336 case 8:
337 fix->visual = FB_VISUAL_PSEUDOCOLOR;
338 var->red.offset = 0;
339 var->red.length = 8;
340 var->green.offset = 0;
341 var->green.length = 8;
342 var->blue.offset = 0;
343 var->blue.length = 8;
344 var->transp.length = 0;
345 var->transp.offset = 0;
346 break;
347 case 16:
348 var->red.offset = 11;
349 var->red.length = 5;
350 var->green.offset = 5;
351 var->green.length = 6;
352 var->blue.offset = 0;
353 var->blue.length = 5;
354 var->transp.length = 0;
355 var->transp.offset = 0;
356 fix->visual = FB_VISUAL_TRUECOLOR;
357 break;
358 case 24:
359 case 32:
360 var->red.offset = 16;
361 var->red.length = 8;
362 var->green.offset = 8;
363 var->green.length = 8;
364 var->blue.offset = 0;
365 var->blue.length = 8;
366 fix->visual = FB_VISUAL_TRUECOLOR;
367 break;
368 default:
369 ret = -EINVAL;
370 break;
372 var->height = var->width = -1;
373 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
375 if (ret) {
376 pr_err("pixel bpp format not satisfied\n.");
377 return ret;
379 ret = hw_sm750_crtc_setMode(crtc, var, fix);
380 if (!ret)
381 ret = hw_sm750_output_setMode(output, var, fix);
382 return ret;
385 static inline unsigned int chan_to_field(unsigned int chan,
386 struct fb_bitfield *bf)
388 chan &= 0xffff;
389 chan >>= 16 - bf->length;
390 return chan << bf->offset;
393 #ifdef CONFIG_PM
394 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
396 struct fb_info *info;
397 struct sm750_dev *sm750_dev;
398 int ret;
400 if (mesg.event == pdev->dev.power.power_state.event)
401 return 0;
403 ret = 0;
404 sm750_dev = pci_get_drvdata(pdev);
405 switch (mesg.event) {
406 case PM_EVENT_FREEZE:
407 case PM_EVENT_PRETHAW:
408 pdev->dev.power.power_state = mesg;
409 return 0;
412 console_lock();
413 if (mesg.event & PM_EVENT_SLEEP) {
414 info = sm750_dev->fbinfo[0];
415 if (info)
416 /* 1 means do suspend */
417 fb_set_suspend(info, 1);
418 info = sm750_dev->fbinfo[1];
419 if (info)
420 /* 1 means do suspend */
421 fb_set_suspend(info, 1);
423 ret = pci_save_state(pdev);
424 if (ret) {
425 pr_err("error:%d occurred in pci_save_state\n", ret);
426 return ret;
429 pci_disable_device(pdev);
430 ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
431 if (ret) {
432 pr_err("error:%d occurred in pci_set_power_state\n", ret);
433 return ret;
437 pdev->dev.power.power_state = mesg;
438 console_unlock();
439 return ret;
442 static int lynxfb_resume(struct pci_dev *pdev)
444 struct fb_info *info;
445 struct sm750_dev *sm750_dev;
447 struct lynxfb_par *par;
448 struct lynxfb_crtc *crtc;
449 struct lynx_cursor *cursor;
451 int ret;
453 ret = 0;
454 sm750_dev = pci_get_drvdata(pdev);
456 console_lock();
458 ret = pci_set_power_state(pdev, PCI_D0);
459 if (ret) {
460 pr_err("error:%d occurred in pci_set_power_state\n", ret);
461 return ret;
464 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
465 pci_restore_state(pdev);
466 ret = pci_enable_device(pdev);
467 if (ret) {
468 pr_err("error:%d occurred in pci_enable_device\n", ret);
469 return ret;
471 pci_set_master(pdev);
474 hw_sm750_inithw(sm750_dev, pdev);
476 info = sm750_dev->fbinfo[0];
478 if (info) {
479 par = info->par;
480 crtc = &par->crtc;
481 cursor = &crtc->cursor;
482 memset_io(cursor->vstart, 0x0, cursor->size);
483 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
484 lynxfb_ops_set_par(info);
485 fb_set_suspend(info, 0);
488 info = sm750_dev->fbinfo[1];
490 if (info) {
491 par = info->par;
492 crtc = &par->crtc;
493 cursor = &crtc->cursor;
494 memset_io(cursor->vstart, 0x0, cursor->size);
495 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
496 lynxfb_ops_set_par(info);
497 fb_set_suspend(info, 0);
500 pdev->dev.power.power_state.event = PM_EVENT_RESUME;
501 console_unlock();
502 return ret;
504 #endif
506 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
507 struct fb_info *info)
509 struct lynxfb_par *par;
510 struct lynxfb_crtc *crtc;
511 struct lynxfb_output *output;
512 resource_size_t request;
514 par = info->par;
515 crtc = &par->crtc;
516 output = &par->output;
518 pr_debug("check var:%dx%d-%d\n",
519 var->xres,
520 var->yres,
521 var->bits_per_pixel);
523 switch (var->bits_per_pixel) {
524 case 8:
525 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
526 var->red.offset = 0;
527 var->red.length = 8;
528 var->green.offset = 0;
529 var->green.length = 8;
530 var->blue.offset = 0;
531 var->blue.length = 8;
532 var->transp.length = 0;
533 var->transp.offset = 0;
534 break;
535 case 16:
536 var->red.offset = 11;
537 var->red.length = 5;
538 var->green.offset = 5;
539 var->green.length = 6;
540 var->blue.offset = 0;
541 var->blue.length = 5;
542 var->transp.length = 0;
543 var->transp.offset = 0;
544 info->fix.visual = FB_VISUAL_TRUECOLOR;
545 break;
546 case 24:
547 case 32:
548 var->red.offset = 16;
549 var->red.length = 8;
550 var->green.offset = 8;
551 var->green.length = 8;
552 var->blue.offset = 0;
553 var->blue.length = 8;
554 info->fix.visual = FB_VISUAL_TRUECOLOR;
555 break;
556 default:
557 pr_err("bpp %d not supported\n", var->bits_per_pixel);
558 return -EINVAL;
560 var->height = var->width = -1;
561 var->accel_flags = 0;/* FB_ACCELF_TEXT; */
563 /* check if current fb's video memory big enought to hold the onscreen*/
564 request = var->xres_virtual * (var->bits_per_pixel >> 3);
565 /* defaulty crtc->channel go with par->index */
567 request = ALIGN(request, crtc->line_pad);
568 request = request * var->yres_virtual;
569 if (crtc->vidmem_size < request) {
570 pr_err("not enough video memory for mode\n");
571 return -ENOMEM;
574 return hw_sm750_crtc_checkMode(crtc, var);
577 static int lynxfb_ops_setcolreg(unsigned regno,
578 unsigned red,
579 unsigned green,
580 unsigned blue,
581 unsigned transp,
582 struct fb_info *info)
584 struct lynxfb_par *par;
585 struct lynxfb_crtc *crtc;
586 struct fb_var_screeninfo *var;
587 int ret;
589 par = info->par;
590 crtc = &par->crtc;
591 var = &info->var;
592 ret = 0;
594 if (regno > 256) {
595 pr_err("regno = %d\n", regno);
596 return -EINVAL;
599 if (info->var.grayscale)
600 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
602 if (var->bits_per_pixel == 8 &&
603 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
604 red >>= 8;
605 green >>= 8;
606 blue >>= 8;
607 ret = hw_sm750_setColReg(crtc, regno, red, green, blue);
608 goto exit;
611 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
612 u32 val;
614 if (var->bits_per_pixel == 16 ||
615 var->bits_per_pixel == 32 ||
616 var->bits_per_pixel == 24) {
617 val = chan_to_field(red, &var->red);
618 val |= chan_to_field(green, &var->green);
619 val |= chan_to_field(blue, &var->blue);
620 par->pseudo_palette[regno] = val;
621 goto exit;
625 ret = -EINVAL;
627 exit:
628 return ret;
631 static int lynxfb_ops_blank(int blank, struct fb_info *info)
633 struct lynxfb_par *par;
634 struct lynxfb_output *output;
636 pr_debug("blank = %d.\n", blank);
637 par = info->par;
638 output = &par->output;
639 return output->proc_setBLANK(output, blank);
642 static int sm750fb_set_drv(struct lynxfb_par *par)
644 int ret;
645 struct sm750_dev *sm750_dev;
646 struct lynxfb_output *output;
647 struct lynxfb_crtc *crtc;
649 ret = 0;
651 sm750_dev = par->dev;
652 output = &par->output;
653 crtc = &par->crtc;
655 crtc->vidmem_size = (sm750_dev->dual) ? sm750_dev->vidmem_size >> 1 :
656 sm750_dev->vidmem_size;
657 /* setup crtc and output member */
658 sm750_dev->hwCursor = g_hwcursor;
660 crtc->line_pad = 16;
661 crtc->xpanstep = 8;
662 crtc->ypanstep = 1;
663 crtc->ywrapstep = 0;
665 output->proc_setBLANK = (sm750_dev->revid == SM750LE_REVISION_ID) ?
666 hw_sm750le_setBLANK : hw_sm750_setBLANK;
667 /* chip specific phase */
668 sm750_dev->accel.de_wait = (sm750_dev->revid == SM750LE_REVISION_ID) ?
669 hw_sm750le_deWait : hw_sm750_deWait;
670 switch (sm750_dev->dataflow) {
671 case sm750_simul_pri:
672 output->paths = sm750_pnc;
673 crtc->channel = sm750_primary;
674 crtc->oScreen = 0;
675 crtc->vScreen = sm750_dev->pvMem;
676 pr_info("use simul primary mode\n");
677 break;
678 case sm750_simul_sec:
679 output->paths = sm750_pnc;
680 crtc->channel = sm750_secondary;
681 crtc->oScreen = 0;
682 crtc->vScreen = sm750_dev->pvMem;
683 break;
684 case sm750_dual_normal:
685 if (par->index == 0) {
686 output->paths = sm750_panel;
687 crtc->channel = sm750_primary;
688 crtc->oScreen = 0;
689 crtc->vScreen = sm750_dev->pvMem;
690 } else {
691 output->paths = sm750_crt;
692 crtc->channel = sm750_secondary;
693 /* not consider of padding stuffs for oScreen,need fix */
694 crtc->oScreen = (sm750_dev->vidmem_size >> 1);
695 crtc->vScreen = sm750_dev->pvMem + crtc->oScreen;
697 break;
698 case sm750_dual_swap:
699 if (par->index == 0) {
700 output->paths = sm750_panel;
701 crtc->channel = sm750_secondary;
702 crtc->oScreen = 0;
703 crtc->vScreen = sm750_dev->pvMem;
704 } else {
705 output->paths = sm750_crt;
706 crtc->channel = sm750_primary;
707 /* not consider of padding stuffs for oScreen,need fix */
708 crtc->oScreen = (sm750_dev->vidmem_size >> 1);
709 crtc->vScreen = sm750_dev->pvMem + crtc->oScreen;
711 break;
712 default:
713 ret = -EINVAL;
716 return ret;
719 static struct fb_ops lynxfb_ops = {
720 .owner = THIS_MODULE,
721 .fb_check_var = lynxfb_ops_check_var,
722 .fb_set_par = lynxfb_ops_set_par,
723 .fb_setcolreg = lynxfb_ops_setcolreg,
724 .fb_blank = lynxfb_ops_blank,
725 .fb_fillrect = cfb_fillrect,
726 .fb_imageblit = cfb_imageblit,
727 .fb_copyarea = cfb_copyarea,
728 /* cursor */
729 .fb_cursor = lynxfb_ops_cursor,
732 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
734 int i;
735 struct lynxfb_par *par;
736 struct sm750_dev *sm750_dev;
737 struct lynxfb_crtc *crtc;
738 struct lynxfb_output *output;
739 struct fb_var_screeninfo *var;
740 struct fb_fix_screeninfo *fix;
742 const struct fb_videomode *pdb[] = {
743 lynx750_ext, NULL, vesa_modes,
745 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
746 static const char *mdb_desc[] = {
747 "driver prepared modes",
748 "kernel prepared default modedb",
749 "kernel HELPERS prepared vesa_modes",
752 static const char *fixId[2] = {
753 "sm750_fb1", "sm750_fb2",
756 int ret, line_length;
758 ret = 0;
759 par = (struct lynxfb_par *)info->par;
760 sm750_dev = par->dev;
761 crtc = &par->crtc;
762 output = &par->output;
763 var = &info->var;
764 fix = &info->fix;
766 /* set index */
767 par->index = index;
768 output->channel = &crtc->channel;
769 sm750fb_set_drv(par);
770 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
773 * set current cursor variable and proc pointer,
774 * must be set after crtc member initialized
776 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
777 crtc->cursor.mmio = sm750_dev->pvReg +
778 0x800f0 + (int)crtc->channel * 0x140;
780 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
781 crtc->cursor.maxH = crtc->cursor.maxW = 64;
782 crtc->cursor.size = crtc->cursor.maxH * crtc->cursor.maxW * 2 / 8;
783 crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
785 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
786 if (!g_hwcursor) {
787 lynxfb_ops.fb_cursor = NULL;
788 hw_cursor_disable(&crtc->cursor);
791 /* set info->fbops, must be set before fb_find_mode */
792 if (!sm750_dev->accel_off) {
793 /* use 2d acceleration */
794 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
795 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
796 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
798 info->fbops = &lynxfb_ops;
800 if (!g_fbmode[index]) {
801 g_fbmode[index] = g_def_fbmode;
802 if (index)
803 g_fbmode[index] = g_fbmode[0];
806 for (i = 0; i < 3; i++) {
808 ret = fb_find_mode(var, info, g_fbmode[index],
809 pdb[i], cdb[i], NULL, 8);
811 if (ret == 1) {
812 pr_info("success! use specified mode:%s in %s\n",
813 g_fbmode[index],
814 mdb_desc[i]);
815 break;
816 } else if (ret == 2) {
817 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
818 g_fbmode[index],
819 mdb_desc[i]);
820 break;
821 } else if (ret == 3) {
822 pr_warn("wanna use default mode\n");
823 /*break;*/
824 } else if (ret == 4) {
825 pr_warn("fall back to any valid mode\n");
826 } else {
827 pr_warn("ret = %d,fb_find_mode failed,with %s\n",
828 ret,
829 mdb_desc[i]);
833 /* some member of info->var had been set by fb_find_mode */
835 pr_info("Member of info->var is :\n\
836 xres=%d\n\
837 yres=%d\n\
838 xres_virtual=%d\n\
839 yres_virtual=%d\n\
840 xoffset=%d\n\
841 yoffset=%d\n\
842 bits_per_pixel=%d\n \
843 ...\n",
844 var->xres,
845 var->yres,
846 var->xres_virtual,
847 var->yres_virtual,
848 var->xoffset,
849 var->yoffset,
850 var->bits_per_pixel);
852 /* set par */
853 par->info = info;
855 /* set info */
856 line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
857 crtc->line_pad);
859 info->pseudo_palette = &par->pseudo_palette[0];
860 info->screen_base = crtc->vScreen;
861 pr_debug("screen_base vaddr = %p\n", info->screen_base);
862 info->screen_size = line_length * var->yres_virtual;
863 info->flags = FBINFO_FLAG_DEFAULT | 0;
865 /* set info->fix */
866 fix->type = FB_TYPE_PACKED_PIXELS;
867 fix->type_aux = 0;
868 fix->xpanstep = crtc->xpanstep;
869 fix->ypanstep = crtc->ypanstep;
870 fix->ywrapstep = crtc->ywrapstep;
871 fix->accel = FB_ACCEL_SMI;
873 strlcpy(fix->id, fixId[index], sizeof(fix->id));
875 fix->smem_start = crtc->oScreen + sm750_dev->vidmem_start;
876 pr_info("fix->smem_start = %lx\n", fix->smem_start);
878 * according to mmap experiment from user space application,
879 * fix->mmio_len should not larger than virtual size
880 * (xres_virtual x yres_virtual x ByPP)
881 * Below line maybe buggy when user mmap fb dev node and write
882 * data into the bound over virtual size
884 fix->smem_len = crtc->vidmem_size;
885 pr_info("fix->smem_len = %x\n", fix->smem_len);
886 info->screen_size = fix->smem_len;
887 fix->line_length = line_length;
888 fix->mmio_start = sm750_dev->vidreg_start;
889 pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
890 fix->mmio_len = sm750_dev->vidreg_size;
891 pr_info("fix->mmio_len = %x\n", fix->mmio_len);
892 switch (var->bits_per_pixel) {
893 case 8:
894 fix->visual = FB_VISUAL_PSEUDOCOLOR;
895 break;
896 case 16:
897 case 32:
898 fix->visual = FB_VISUAL_TRUECOLOR;
899 break;
902 /* set var */
903 var->activate = FB_ACTIVATE_NOW;
904 var->accel_flags = 0;
905 var->vmode = FB_VMODE_NONINTERLACED;
907 pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
908 info->cmap.start, info->cmap.len,
909 info->cmap.red, info->cmap.green, info->cmap.blue,
910 info->cmap.transp);
912 ret = fb_alloc_cmap(&info->cmap, 256, 0);
913 if (ret < 0) {
914 pr_err("Could not allocate memory for cmap.\n");
915 goto exit;
918 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
919 info->cmap.start, info->cmap.len,
920 info->cmap.red, info->cmap.green, info->cmap.blue,
921 info->cmap.transp);
923 exit:
924 lynxfb_ops_check_var(var, info);
925 return ret;
928 /* chip specific g_option configuration routine */
929 static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
931 char *opt;
932 int swap;
934 swap = 0;
936 sm750_dev->initParm.chip_clk = 0;
937 sm750_dev->initParm.mem_clk = 0;
938 sm750_dev->initParm.master_clk = 0;
939 sm750_dev->initParm.powerMode = 0;
940 sm750_dev->initParm.setAllEngOff = 0;
941 sm750_dev->initParm.resetMemory = 1;
943 /* defaultly turn g_hwcursor on for both view */
944 g_hwcursor = 3;
946 if (!src || !*src) {
947 pr_warn("no specific g_option.\n");
948 goto NO_PARAM;
951 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
952 pr_info("opt=%s\n", opt);
953 pr_info("src=%s\n", src);
955 if (!strncmp(opt, "swap", strlen("swap")))
956 swap = 1;
957 else if (!strncmp(opt, "nocrt", strlen("nocrt")))
958 sm750_dev->nocrt = 1;
959 else if (!strncmp(opt, "36bit", strlen("36bit")))
960 sm750_dev->pnltype = sm750_doubleTFT;
961 else if (!strncmp(opt, "18bit", strlen("18bit")))
962 sm750_dev->pnltype = sm750_dualTFT;
963 else if (!strncmp(opt, "24bit", strlen("24bit")))
964 sm750_dev->pnltype = sm750_24TFT;
965 else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
966 g_hwcursor &= ~0x1;
967 else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
968 g_hwcursor &= ~0x2;
969 else if (!strncmp(opt, "nohwc", strlen("nohwc")))
970 g_hwcursor = 0;
971 else {
972 if (!g_fbmode[0]) {
973 g_fbmode[0] = opt;
974 pr_info("find fbmode0 : %s\n", g_fbmode[0]);
975 } else if (!g_fbmode[1]) {
976 g_fbmode[1] = opt;
977 pr_info("find fbmode1 : %s\n", g_fbmode[1]);
978 } else {
979 pr_warn("How many view you wann set?\n");
984 NO_PARAM:
985 if (sm750_dev->revid != SM750LE_REVISION_ID) {
986 if (sm750_dev->dual) {
987 if (swap)
988 sm750_dev->dataflow = sm750_dual_swap;
989 else
990 sm750_dev->dataflow = sm750_dual_normal;
991 } else {
992 if (swap)
993 sm750_dev->dataflow = sm750_simul_sec;
994 else
995 sm750_dev->dataflow = sm750_simul_pri;
997 } else {
998 /* SM750LE only have one crt channel */
999 sm750_dev->dataflow = sm750_simul_sec;
1000 /* sm750le do not have complex attributes */
1001 sm750_dev->nocrt = 0;
1005 static int lynxfb_pci_probe(struct pci_dev *pdev,
1006 const struct pci_device_id *ent)
1008 struct fb_info *info[] = {NULL, NULL};
1009 struct sm750_dev *sm750_dev = NULL;
1010 int fbidx;
1012 /* enable device */
1013 if (pci_enable_device(pdev)) {
1014 pr_err("can not enable device.\n");
1015 goto err_enable;
1018 sm750_dev = kzalloc(sizeof(*sm750_dev), GFP_KERNEL);
1019 if (!sm750_dev) {
1020 pr_err("Could not allocate memory for share.\n");
1021 goto err_share;
1024 sm750_dev->fbinfo[0] = sm750_dev->fbinfo[1] = NULL;
1025 sm750_dev->devid = pdev->device;
1026 sm750_dev->revid = pdev->revision;
1028 pr_info("share->revid = %02x\n", sm750_dev->revid);
1029 sm750_dev->pdev = pdev;
1030 sm750_dev->mtrr_off = g_nomtrr;
1031 sm750_dev->mtrr.vram = 0;
1032 sm750_dev->accel_off = g_noaccel;
1033 sm750_dev->dual = g_dualview;
1034 spin_lock_init(&sm750_dev->slock);
1036 if (!sm750_dev->accel_off) {
1038 * hook deInit and 2d routines, notes that below hw_xxx
1039 * routine can work on most of lynx chips
1040 * if some chip need specific function,
1041 * please hook it in smXXX_set_drv routine
1043 sm750_dev->accel.de_init = hw_de_init;
1044 sm750_dev->accel.de_fillrect = hw_fillrect;
1045 sm750_dev->accel.de_copyarea = hw_copyarea;
1046 sm750_dev->accel.de_imageblit = hw_imageblit;
1047 pr_info("enable 2d acceleration\n");
1048 } else {
1049 pr_info("disable 2d acceleration\n");
1052 /* call chip specific setup routine */
1053 sm750fb_setup(sm750_dev, g_settings);
1055 /* call chip specific mmap routine */
1056 if (hw_sm750_map(sm750_dev, pdev)) {
1057 pr_err("Memory map failed\n");
1058 goto err_map;
1061 if (!sm750_dev->mtrr_off)
1062 sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,
1063 sm750_dev->vidmem_size);
1065 memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);
1067 pr_info("sm%3x mmio address = %p\n", sm750_dev->devid,
1068 sm750_dev->pvReg);
1070 pci_set_drvdata(pdev, sm750_dev);
1072 /* call chipInit routine */
1073 hw_sm750_inithw(sm750_dev, pdev);
1075 /* allocate frame buffer info structor according to g_dualview */
1076 fbidx = 0;
1077 ALLOC_FB:
1078 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
1079 if (!info[fbidx]) {
1080 pr_err("Could not allocate framebuffer #%d.\n", fbidx);
1081 if (fbidx == 0)
1082 goto err_info0_alloc;
1083 else
1084 goto err_info1_alloc;
1085 } else {
1086 struct lynxfb_par *par;
1087 int errno;
1089 pr_info("framebuffer #%d alloc okay\n", fbidx);
1090 sm750_dev->fbinfo[fbidx] = info[fbidx];
1091 par = info[fbidx]->par;
1092 par->dev = sm750_dev;
1094 /* set fb_info structure */
1095 if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
1096 pr_err("Failed to initial fb_info #%d.\n", fbidx);
1097 if (fbidx == 0)
1098 goto err_info0_set;
1099 else
1100 goto err_info1_set;
1103 /* register frame buffer */
1104 pr_info("Ready to register framebuffer #%d.\n", fbidx);
1105 errno = register_framebuffer(info[fbidx]);
1106 if (errno < 0) {
1107 pr_err("Failed to register fb_info #%d. err %d\n",
1108 fbidx,
1109 errno);
1110 if (fbidx == 0)
1111 goto err_register0;
1112 else
1113 goto err_register1;
1115 pr_info("Accomplished register framebuffer #%d.\n", fbidx);
1118 /* no dual view by far */
1119 fbidx++;
1120 if (sm750_dev->dual && fbidx < 2)
1121 goto ALLOC_FB;
1123 return 0;
1125 err_register1:
1126 err_info1_set:
1127 framebuffer_release(info[1]);
1128 err_info1_alloc:
1129 unregister_framebuffer(info[0]);
1130 err_register0:
1131 err_info0_set:
1132 framebuffer_release(info[0]);
1133 err_info0_alloc:
1134 err_map:
1135 kfree(sm750_dev);
1136 err_share:
1137 err_enable:
1138 return -ENODEV;
1141 static void lynxfb_pci_remove(struct pci_dev *pdev)
1143 struct fb_info *info;
1144 struct sm750_dev *sm750_dev;
1145 struct lynxfb_par *par;
1146 int cnt;
1148 cnt = 2;
1149 sm750_dev = pci_get_drvdata(pdev);
1151 while (cnt-- > 0) {
1152 info = sm750_dev->fbinfo[cnt];
1153 if (!info)
1154 continue;
1155 par = info->par;
1157 unregister_framebuffer(info);
1158 /* release frame buffer */
1159 framebuffer_release(info);
1161 arch_phys_wc_del(sm750_dev->mtrr.vram);
1163 iounmap(sm750_dev->pvReg);
1164 iounmap(sm750_dev->pvMem);
1165 kfree(g_settings);
1166 kfree(sm750_dev);
1167 pci_set_drvdata(pdev, NULL);
1170 static int __init lynxfb_setup(char *options)
1172 int len;
1173 char *opt, *tmp;
1175 if (!options || !*options) {
1176 pr_warn("no options.\n");
1177 return 0;
1180 pr_info("options:%s\n", options);
1182 len = strlen(options) + 1;
1183 g_settings = kzalloc(len, GFP_KERNEL);
1184 if (!g_settings)
1185 return -ENOMEM;
1187 tmp = g_settings;
1190 * Notes:
1191 * char * strsep(char **s,const char * ct);
1192 * @s: the string to be searched
1193 * @ct :the characters to search for
1195 * strsep() updates @options to pointer after the first found token
1196 * it also returns the pointer ahead the token.
1198 while ((opt = strsep(&options, ":")) != NULL) {
1199 /* options that mean for any lynx chips are configured here */
1200 if (!strncmp(opt, "noaccel", strlen("noaccel")))
1201 g_noaccel = 1;
1202 else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
1203 g_nomtrr = 1;
1204 else if (!strncmp(opt, "dual", strlen("dual")))
1205 g_dualview = 1;
1206 else {
1207 strcat(tmp, opt);
1208 tmp += strlen(opt);
1209 if (options != NULL)
1210 *tmp++ = ':';
1211 else
1212 *tmp++ = 0;
1216 /* misc g_settings are transport to chip specific routines */
1217 pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1218 return 0;
1221 static struct pci_device_id smi_pci_table[] = {
1222 { PCI_DEVICE(0x126f, 0x0750), },
1223 {0,}
1226 MODULE_DEVICE_TABLE(pci, smi_pci_table);
1228 static struct pci_driver lynxfb_driver = {
1229 .name = "sm750fb",
1230 .id_table = smi_pci_table,
1231 .probe = lynxfb_pci_probe,
1232 .remove = lynxfb_pci_remove,
1233 #ifdef CONFIG_PM
1234 .suspend = lynxfb_suspend,
1235 .resume = lynxfb_resume,
1236 #endif
1239 static int __init lynxfb_init(void)
1241 char *option;
1242 int ret;
1244 #ifdef MODULE
1245 option = g_option;
1246 #else
1247 if (fb_get_options("sm750fb", &option))
1248 return -ENODEV;
1249 #endif
1251 lynxfb_setup(option);
1252 ret = pci_register_driver(&lynxfb_driver);
1253 return ret;
1255 module_init(lynxfb_init);
1257 static void __exit lynxfb_exit(void)
1259 pci_unregister_driver(&lynxfb_driver);
1261 module_exit(lynxfb_exit);
1263 module_param(g_option, charp, S_IRUGO);
1265 MODULE_PARM_DESC(g_option,
1266 "\n\t\tCommon options:\n"
1267 "\t\tnoaccel:disable 2d capabilities\n"
1268 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1269 "\t\tdualview:dual frame buffer feature enabled\n"
1270 "\t\tnohwc:disable hardware cursor\n"
1271 "\t\tUsual example:\n"
1272 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1275 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1276 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1277 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1278 MODULE_LICENSE("GPL v2");