1 #include<linux/kernel.h>
2 #include<linux/module.h>
3 #include<linux/errno.h>
4 #include<linux/string.h>
7 #include<linux/delay.h>
9 #include<linux/ioport.h>
10 #include<linux/init.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>
22 #include "sm750_accel.h"
23 #include "sm750_cursor.h"
32 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
33 * size_t count, loff_t *ppos);
34 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
35 * size_t count, loff_t *ppos);
39 typedef void (*PROC_SPEC_SETUP)(struct lynx_share*, char *);
40 typedef int (*PROC_SPEC_MAP)(struct lynx_share*, struct pci_dev*);
41 typedef int (*PROC_SPEC_INITHW)(struct lynx_share*, struct pci_dev*);
44 /* common var for all device */
45 static int g_hwcursor = 1;
48 static const char *g_fbmode[] = {NULL, NULL};
49 static const char *g_def_fbmode = "800x600-16@60";
50 static char *g_settings = NULL;
51 static int g_dualview;
52 static char *g_option = NULL;
55 static const struct fb_videomode lynx750_ext[] = {
56 /* 1024x600-60 VESA [1.71:1] */
57 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
58 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
59 FB_VMODE_NONINTERLACED},
61 /* 1024x600-70 VESA */
62 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
63 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
64 FB_VMODE_NONINTERLACED},
66 /* 1024x600-75 VESA */
67 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
68 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
69 FB_VMODE_NONINTERLACED},
71 /* 1024x600-85 VESA */
72 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
73 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74 FB_VMODE_NONINTERLACED},
77 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
78 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79 FB_VMODE_NONINTERLACED},
81 /* 1280x720 [1.78:1] */
82 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
83 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
84 FB_VMODE_NONINTERLACED},
87 {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
88 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89 FB_VMODE_NONINTERLACED},
91 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
92 FB_SYNC_HOR_HIGH_ACT|FB_VMODE_NONINTERLACED},
94 /* 1360 x 768 [1.77083:1] */
95 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
96 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
97 FB_VMODE_NONINTERLACED},
99 /* 1368 x 768 [1.78:1] */
100 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
101 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
102 FB_VMODE_NONINTERLACED},
104 /* 1440 x 900 [16:10] */
105 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
106 FB_SYNC_VERT_HIGH_ACT,
107 FB_VMODE_NONINTERLACED},
109 /* 1440x960 [15:10] */
110 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
111 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
112 FB_VMODE_NONINTERLACED},
114 /* 1920x1080 [16:9] */
115 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
116 FB_SYNC_VERT_HIGH_ACT,
117 FB_VMODE_NONINTERLACED},
123 /* no hardware cursor supported under version 2.6.10, kernel bug */
124 static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
126 struct lynxfb_par *par;
127 struct lynxfb_crtc *crtc;
128 struct lynx_cursor *cursor;
132 cursor = &crtc->cursor;
134 if (fbcursor->image.width > cursor->maxW ||
135 fbcursor->image.height > cursor->maxH ||
136 fbcursor->image.depth > 1) {
140 cursor->disable(cursor);
141 if (fbcursor->set & FB_CUR_SETSIZE)
142 cursor->setSize(cursor,
143 fbcursor->image.width,
144 fbcursor->image.height);
146 if (fbcursor->set & FB_CUR_SETPOS)
147 cursor->setPos(cursor,
148 fbcursor->image.dx - info->var.xoffset,
149 fbcursor->image.dy - info->var.yoffset);
151 if (fbcursor->set & FB_CUR_SETCMAP) {
152 /* get the 16bit color of kernel means */
155 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
156 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
157 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
159 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
160 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
161 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
163 cursor->setColor(cursor, fg, bg);
167 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
168 cursor->setData(cursor,
170 fbcursor->image.data,
174 if (fbcursor->enable)
175 cursor->enable(cursor);
180 static void lynxfb_ops_fillrect(struct fb_info *info,
181 const struct fb_fillrect *region)
183 struct lynxfb_par *par;
184 struct lynx_share *share;
185 unsigned int base, pitch, Bpp, rop;
188 if (info->state != FBINFO_STATE_RUNNING)
194 /* each time 2d function begin to work,below three variable always need
195 * be set, seems we can put them together in some place */
196 base = par->crtc.oScreen;
197 pitch = info->fix.line_length;
198 Bpp = info->var.bits_per_pixel >> 3;
200 color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color];
201 rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY;
204 * If not use spin_lock,system will die if user load driver
205 * and immediately unload driver frequently (dual)
208 spin_lock(&share->slock);
210 share->accel.de_fillrect(&share->accel,
212 region->dx, region->dy,
213 region->width, region->height,
216 spin_unlock(&share->slock);
219 static void lynxfb_ops_copyarea(struct fb_info *info,
220 const struct fb_copyarea *region)
222 struct lynxfb_par *par;
223 struct lynx_share *share;
224 unsigned int base, pitch, Bpp;
229 /* each time 2d function begin to work,below three variable always need
230 * be set, seems we can put them together in some place */
231 base = par->crtc.oScreen;
232 pitch = info->fix.line_length;
233 Bpp = info->var.bits_per_pixel >> 3;
236 * If not use spin_lock, system will die if user load driver
237 * and immediately unload driver frequently (dual)
240 spin_lock(&share->slock);
242 share->accel.de_copyarea(&share->accel,
243 base, pitch, region->sx, region->sy,
244 base, pitch, Bpp, region->dx, region->dy,
245 region->width, region->height, HW_ROP2_COPY);
247 spin_unlock(&share->slock);
250 static void lynxfb_ops_imageblit(struct fb_info *info,
251 const struct fb_image *image)
253 unsigned int base, pitch, Bpp;
254 unsigned int fgcol, bgcol;
255 struct lynxfb_par *par;
256 struct lynx_share *share;
260 /* each time 2d function begin to work,below three variable always need
261 * be set, seems we can put them together in some place */
262 base = par->crtc.oScreen;
263 pitch = info->fix.line_length;
264 Bpp = info->var.bits_per_pixel >> 3;
266 if (image->depth == 1) {
267 if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
268 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
269 fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
270 bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
272 fgcol = image->fg_color;
273 bgcol = image->bg_color;
277 /* TODO: Implement hardware acceleration for image->depth > 1 */
278 cfb_imageblit(info, image);
283 * If not use spin_lock, system will die if user load driver
284 * and immediately unload driver frequently (dual)
287 spin_lock(&share->slock);
289 share->accel.de_imageblit(&share->accel,
290 image->data, image->width>>3, 0,
292 image->dx, image->dy,
293 image->width, image->height,
294 fgcol, bgcol, HW_ROP2_COPY);
296 spin_unlock(&share->slock);
299 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
300 struct fb_info *info)
302 struct lynxfb_par *par;
303 struct lynxfb_crtc *crtc;
313 ret = crtc->proc_panDisplay(crtc, var, info);
318 static int lynxfb_ops_set_par(struct fb_info *info)
320 struct lynxfb_par *par;
321 struct lynx_share *share;
322 struct lynxfb_crtc *crtc;
323 struct lynxfb_output *output;
324 struct fb_var_screeninfo *var;
325 struct fb_fix_screeninfo *fix;
327 unsigned int line_length;
336 output = &par->output;
340 /* fix structur is not so FIX ... */
341 line_length = var->xres_virtual * var->bits_per_pixel / 8;
342 line_length = PADDING(crtc->line_pad, line_length);
343 fix->line_length = line_length;
344 pr_err("fix->line_length = %d\n", fix->line_length);
346 /* var->red,green,blue,transp are need to be set by driver
347 * and these data should be set before setcolreg routine
350 switch (var->bits_per_pixel) {
352 fix->visual = FB_VISUAL_PSEUDOCOLOR;
355 var->green.offset = 0;
356 var->green.length = 8;
357 var->blue.offset = 0;
358 var->blue.length = 8;
359 var->transp.length = 0;
360 var->transp.offset = 0;
363 var->red.offset = 11;
365 var->green.offset = 5;
366 var->green.length = 6;
367 var->blue.offset = 0;
368 var->blue.length = 5;
369 var->transp.length = 0;
370 var->transp.offset = 0;
371 fix->visual = FB_VISUAL_TRUECOLOR;
375 var->red.offset = 16;
377 var->green.offset = 8;
378 var->green.length = 8;
379 var->blue.offset = 0;
380 var->blue.length = 8;
381 fix->visual = FB_VISUAL_TRUECOLOR;
387 var->height = var->width = -1;
388 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
391 pr_err("pixel bpp format not satisfied\n.");
394 ret = crtc->proc_setMode(crtc, var, fix);
396 ret = output->proc_setMode(output, var, fix);
400 static inline unsigned int chan_to_field(unsigned int chan,
401 struct fb_bitfield *bf)
404 chan >>= 16 - bf->length;
405 return chan << bf->offset;
409 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
411 struct fb_info *info;
412 struct lynx_share *share;
415 if (mesg.event == pdev->dev.power.power_state.event)
419 share = pci_get_drvdata(pdev);
420 switch (mesg.event) {
421 case PM_EVENT_FREEZE:
422 case PM_EVENT_PRETHAW:
423 pdev->dev.power.power_state = mesg;
428 if (mesg.event & PM_EVENT_SLEEP) {
429 info = share->fbinfo[0];
431 /* 1 means do suspend */
432 fb_set_suspend(info, 1);
433 info = share->fbinfo[1];
435 /* 1 means do suspend */
436 fb_set_suspend(info, 1);
438 ret = pci_save_state(pdev);
440 pr_err("error:%d occurred in pci_save_state\n", ret);
444 /* set chip to sleep mode */
446 (*share->suspend)(share);
448 pci_disable_device(pdev);
449 ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
451 pr_err("error:%d occurred in pci_set_power_state\n", ret);
456 pdev->dev.power.power_state = mesg;
461 static int lynxfb_resume(struct pci_dev *pdev)
463 struct fb_info *info;
464 struct lynx_share *share;
466 struct lynxfb_par *par;
467 struct lynxfb_crtc *crtc;
468 struct lynx_cursor *cursor;
474 share = pci_get_drvdata(pdev);
478 ret = pci_set_power_state(pdev, PCI_D0);
480 pr_err("error:%d occurred in pci_set_power_state\n", ret);
485 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
486 pci_restore_state(pdev);
487 ret = pci_enable_device(pdev);
489 pr_err("error:%d occurred in pci_enable_device\n", ret);
492 pci_set_master(pdev);
495 (*share->resume)(share);
497 hw_sm750_inithw(share, pdev);
500 info = share->fbinfo[0];
505 cursor = &crtc->cursor;
506 memset_io(cursor->vstart, 0x0, cursor->size);
507 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
508 lynxfb_ops_set_par(info);
509 fb_set_suspend(info, 0);
512 info = share->fbinfo[1];
517 cursor = &crtc->cursor;
518 memset_io(cursor->vstart, 0x0, cursor->size);
519 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
520 lynxfb_ops_set_par(info);
521 fb_set_suspend(info, 0);
530 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
531 struct fb_info *info)
533 struct lynxfb_par *par;
534 struct lynxfb_crtc *crtc;
535 struct lynxfb_output *output;
536 struct lynx_share *share;
538 resource_size_t request;
543 output = &par->output;
547 pr_debug("check var:%dx%d-%d\n",
550 var->bits_per_pixel);
553 switch (var->bits_per_pixel) {
556 case 24: /* support 24 bpp for only lynx712/722/720 */
560 pr_err("bpp %d not supported\n", var->bits_per_pixel);
565 switch (var->bits_per_pixel) {
567 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
570 var->green.offset = 0;
571 var->green.length = 8;
572 var->blue.offset = 0;
573 var->blue.length = 8;
574 var->transp.length = 0;
575 var->transp.offset = 0;
578 var->red.offset = 11;
580 var->green.offset = 5;
581 var->green.length = 6;
582 var->blue.offset = 0;
583 var->blue.length = 5;
584 var->transp.length = 0;
585 var->transp.offset = 0;
586 info->fix.visual = FB_VISUAL_TRUECOLOR;
590 var->red.offset = 16;
592 var->green.offset = 8;
593 var->green.length = 8;
594 var->blue.offset = 0;
595 var->blue.length = 8;
596 info->fix.visual = FB_VISUAL_TRUECOLOR;
602 var->height = var->width = -1;
603 var->accel_flags = 0;/* FB_ACCELF_TEXT; */
605 /* check if current fb's video memory big enought to hold the onscreen*/
606 request = var->xres_virtual * (var->bits_per_pixel >> 3);
607 /* defaulty crtc->channel go with par->index */
609 request = PADDING(crtc->line_pad, request);
610 request = request * var->yres_virtual;
611 if (crtc->vidmem_size < request) {
612 pr_err("not enough video memory for mode\n");
616 ret = output->proc_checkMode(output, var);
618 ret = crtc->proc_checkMode(crtc, var);
624 static int lynxfb_ops_setcolreg(unsigned regno,
629 struct fb_info *info)
631 struct lynxfb_par *par;
632 struct lynxfb_crtc *crtc;
633 struct fb_var_screeninfo *var;
642 pr_err("regno = %d\n", regno);
646 if (info->var.grayscale)
647 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
649 if (var->bits_per_pixel == 8 &&
650 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
654 ret = crtc->proc_setColReg(crtc, regno, red, green, blue);
659 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
662 if (var->bits_per_pixel == 16 ||
663 var->bits_per_pixel == 32 ||
664 var->bits_per_pixel == 24) {
665 val = chan_to_field(red, &var->red);
666 val |= chan_to_field(green, &var->green);
667 val |= chan_to_field(blue, &var->blue);
668 par->pseudo_palette[regno] = val;
679 static int lynxfb_ops_blank(int blank, struct fb_info *info)
681 struct lynxfb_par *par;
682 struct lynxfb_output *output;
684 pr_debug("blank = %d.\n", blank);
686 output = &par->output;
687 return output->proc_setBLANK(output, blank);
690 static int sm750fb_set_drv(struct lynxfb_par *par)
693 struct lynx_share *share;
694 struct sm750_share *spec_share;
695 struct lynxfb_output *output;
696 struct lynxfb_crtc *crtc;
701 spec_share = container_of(share, struct sm750_share, share);
702 output = &par->output;
705 crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
706 /* setup crtc and output member */
707 spec_share->hwCursor = g_hwcursor;
709 crtc->proc_setMode = hw_sm750_crtc_setMode;
710 crtc->proc_checkMode = hw_sm750_crtc_checkMode;
711 crtc->proc_setColReg = hw_sm750_setColReg;
712 crtc->proc_panDisplay = hw_sm750_pan_display;
713 crtc->clear = hw_sm750_crtc_clear;
719 output->proc_setMode = hw_sm750_output_setMode;
720 output->proc_checkMode = hw_sm750_output_checkMode;
722 output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
723 output->clear = hw_sm750_output_clear;
724 /* chip specific phase */
725 share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait;
726 switch (spec_share->state.dataflow) {
727 case sm750_simul_pri:
728 output->paths = sm750_pnc;
729 crtc->channel = sm750_primary;
731 crtc->vScreen = share->pvMem;
732 pr_info("use simul primary mode\n");
734 case sm750_simul_sec:
735 output->paths = sm750_pnc;
736 crtc->channel = sm750_secondary;
738 crtc->vScreen = share->pvMem;
740 case sm750_dual_normal:
741 if (par->index == 0) {
742 output->paths = sm750_panel;
743 crtc->channel = sm750_primary;
745 crtc->vScreen = share->pvMem;
747 output->paths = sm750_crt;
748 crtc->channel = sm750_secondary;
749 /* not consider of padding stuffs for oScreen,need fix */
750 crtc->oScreen = (share->vidmem_size >> 1);
751 crtc->vScreen = share->pvMem + crtc->oScreen;
754 case sm750_dual_swap:
755 if (par->index == 0) {
756 output->paths = sm750_panel;
757 crtc->channel = sm750_secondary;
759 crtc->vScreen = share->pvMem;
761 output->paths = sm750_crt;
762 crtc->channel = sm750_primary;
763 /* not consider of padding stuffs for oScreen,need fix */
764 crtc->oScreen = (share->vidmem_size >> 1);
765 crtc->vScreen = share->pvMem + crtc->oScreen;
775 static struct fb_ops lynxfb_ops = {
776 .owner = THIS_MODULE,
777 .fb_check_var = lynxfb_ops_check_var,
778 .fb_set_par = lynxfb_ops_set_par,
779 .fb_setcolreg = lynxfb_ops_setcolreg,
780 .fb_blank = lynxfb_ops_blank,
781 .fb_fillrect = cfb_fillrect,
782 .fb_imageblit = cfb_imageblit,
783 .fb_copyarea = cfb_copyarea,
785 .fb_cursor = lynxfb_ops_cursor,
789 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
792 struct lynxfb_par *par;
793 struct lynx_share *share;
794 struct lynxfb_crtc *crtc;
795 struct lynxfb_output *output;
796 struct fb_var_screeninfo *var;
797 struct fb_fix_screeninfo *fix;
799 const struct fb_videomode *pdb[] = {
800 lynx750_ext, NULL, vesa_modes,
802 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
803 static const char *mdb_desc[] = {
804 "driver prepared modes",
805 "kernel prepared default modedb",
806 "kernel HELPERS prepared vesa_modes",
810 static const char *fixId[2] = {
811 "sm750_fb1", "sm750_fb2",
814 int ret, line_length;
817 par = (struct lynxfb_par *)info->par;
820 output = &par->output;
826 output->channel = &crtc->channel;
827 sm750fb_set_drv(par);
828 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
831 /* set current cursor variable and proc pointer,
832 * must be set after crtc member initialized */
833 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
834 crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
836 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
837 crtc->cursor.maxH = crtc->cursor.maxW = 64;
838 crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
839 crtc->cursor.disable = hw_cursor_disable;
840 crtc->cursor.enable = hw_cursor_enable;
841 crtc->cursor.setColor = hw_cursor_setColor;
842 crtc->cursor.setPos = hw_cursor_setPos;
843 crtc->cursor.setSize = hw_cursor_setSize;
844 crtc->cursor.setData = hw_cursor_setData;
845 crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
848 crtc->cursor.share = share;
849 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
851 lynxfb_ops.fb_cursor = NULL;
852 crtc->cursor.disable(&crtc->cursor);
856 /* set info->fbops, must be set before fb_find_mode */
857 if (!share->accel_off) {
858 /* use 2d acceleration */
859 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
860 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
861 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
863 info->fbops = &lynxfb_ops;
865 if (!g_fbmode[index]) {
866 g_fbmode[index] = g_def_fbmode;
868 g_fbmode[index] = g_fbmode[0];
872 for (i = 0; i < 3; i++) {
874 ret = fb_find_mode(var, info, g_fbmode[index],
875 pdb[i], cdb[i], NULL, 8);
878 pr_info("success! use specified mode:%s in %s\n",
882 } else if (ret == 2) {
883 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
887 } else if (ret == 3) {
888 pr_warn("wanna use default mode\n");
890 } else if (ret == 4) {
891 pr_warn("fall back to any valid mode\n");
893 pr_warn("ret = %d,fb_find_mode failed,with %s\n",
899 /* some member of info->var had been set by fb_find_mode */
901 pr_info("Member of info->var is :\n\
908 bits_per_pixel=%d\n \
916 var->bits_per_pixel);
922 line_length = PADDING(crtc->line_pad,
923 (var->xres_virtual * var->bits_per_pixel/8));
925 info->pseudo_palette = &par->pseudo_palette[0];
926 info->screen_base = crtc->vScreen;
927 pr_debug("screen_base vaddr = %p\n", info->screen_base);
928 info->screen_size = line_length * var->yres_virtual;
929 info->flags = FBINFO_FLAG_DEFAULT|0;
932 fix->type = FB_TYPE_PACKED_PIXELS;
934 fix->xpanstep = crtc->xpanstep;
935 fix->ypanstep = crtc->ypanstep;
936 fix->ywrapstep = crtc->ywrapstep;
937 fix->accel = FB_ACCEL_SMI;
939 strlcpy(fix->id, fixId[index], sizeof(fix->id));
942 fix->smem_start = crtc->oScreen + share->vidmem_start;
943 pr_info("fix->smem_start = %lx\n", fix->smem_start);
944 /* according to mmap experiment from user space application,
945 * fix->mmio_len should not larger than virtual size
946 * (xres_virtual x yres_virtual x ByPP)
947 * Below line maybe buggy when user mmap fb dev node and write
948 * data into the bound over virtual size
950 fix->smem_len = crtc->vidmem_size;
951 pr_info("fix->smem_len = %x\n", fix->smem_len);
952 info->screen_size = fix->smem_len;
953 fix->line_length = line_length;
954 fix->mmio_start = share->vidreg_start;
955 pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
956 fix->mmio_len = share->vidreg_size;
957 pr_info("fix->mmio_len = %x\n", fix->mmio_len);
958 switch (var->bits_per_pixel) {
960 fix->visual = FB_VISUAL_PSEUDOCOLOR;
964 fix->visual = FB_VISUAL_TRUECOLOR;
969 var->activate = FB_ACTIVATE_NOW;
970 var->accel_flags = 0;
971 var->vmode = FB_VMODE_NONINTERLACED;
973 pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
974 info->cmap.start, info->cmap.len,
975 info->cmap.red, info->cmap.green, info->cmap.blue,
978 ret = fb_alloc_cmap(&info->cmap, 256, 0);
980 pr_err("Could not allcate memory for cmap.\n");
984 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
985 info->cmap.start, info->cmap.len,
986 info->cmap.red, info->cmap.green, info->cmap.blue,
990 lynxfb_ops_check_var(var, info);
994 /* chip specific g_option configuration routine */
995 static void sm750fb_setup(struct lynx_share *share, char *src)
997 struct sm750_share *spec_share;
1005 spec_share = container_of(share, struct sm750_share, share);
1006 #ifdef CAP_EXPENSIION
1011 spec_share->state.initParm.chip_clk = 0;
1012 spec_share->state.initParm.mem_clk = 0;
1013 spec_share->state.initParm.master_clk = 0;
1014 spec_share->state.initParm.powerMode = 0;
1015 spec_share->state.initParm.setAllEngOff = 0;
1016 spec_share->state.initParm.resetMemory = 1;
1018 /* defaultly turn g_hwcursor on for both view */
1021 if (!src || !*src) {
1022 pr_warn("no specific g_option.\n");
1026 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
1027 pr_err("opt=%s\n", opt);
1028 pr_err("src=%s\n", src);
1030 if (!strncmp(opt, "swap", strlen("swap")))
1032 else if (!strncmp(opt, "nocrt", strlen("nocrt")))
1033 spec_share->state.nocrt = 1;
1034 else if (!strncmp(opt, "36bit", strlen("36bit")))
1035 spec_share->state.pnltype = sm750_doubleTFT;
1036 else if (!strncmp(opt, "18bit", strlen("18bit")))
1037 spec_share->state.pnltype = sm750_dualTFT;
1038 else if (!strncmp(opt, "24bit", strlen("24bit")))
1039 spec_share->state.pnltype = sm750_24TFT;
1040 #ifdef CAP_EXPANSION
1041 else if (!strncmp(opt, "exp:", strlen("exp:")))
1042 exp_res = opt + strlen("exp:");
1044 else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
1046 else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
1048 else if (!strncmp(opt, "nohwc", strlen("nohwc")))
1053 pr_info("find fbmode0 : %s\n", g_fbmode[0]);
1054 } else if (!g_fbmode[1]) {
1056 pr_info("find fbmode1 : %s\n", g_fbmode[1]);
1058 pr_warn("How many view you wann set?\n");
1062 #ifdef CAP_EXPANSION
1063 if (getExpRes(exp_res,
1064 &spec_share->state.xLCD,
1065 &spec_share->state.yLCD)) {
1066 /* seems exp_res is not valid */
1067 spec_share->state.xLCD = spec_share->state.yLCD = 0;
1072 if (share->revid != SM750LE_REVISION_ID) {
1075 spec_share->state.dataflow = sm750_dual_swap;
1077 spec_share->state.dataflow = sm750_dual_normal;
1080 spec_share->state.dataflow = sm750_simul_sec;
1082 spec_share->state.dataflow = sm750_simul_pri;
1085 /* SM750LE only have one crt channel */
1086 spec_share->state.dataflow = sm750_simul_sec;
1087 /* sm750le do not have complex attributes */
1088 spec_share->state.nocrt = 0;
1092 static int lynxfb_pci_probe(struct pci_dev *pdev,
1093 const struct pci_device_id * ent)
1095 struct fb_info *info[] = {NULL, NULL};
1096 struct lynx_share *share = NULL;
1098 struct sm750_share *spec_share = NULL;
1099 size_t spec_offset = 0;
1104 if (pci_enable_device(pdev)) {
1105 pr_err("can not enable device.\n");
1109 /* though offset of share in sm750_share is 0,
1110 * we use this marcro as the same */
1111 spec_offset = offsetof(struct sm750_share, share);
1113 spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL);
1115 pr_err("Could not allocate memory for share.\n");
1119 /* setting share structure */
1120 share = (struct lynx_share *)(&(spec_share->share));
1121 share->fbinfo[0] = share->fbinfo[1] = NULL;
1122 share->devid = pdev->device;
1123 share->revid = pdev->revision;
1125 pr_info("share->revid = %02x\n", share->revid);
1127 share->mtrr_off = g_nomtrr;
1128 share->mtrr.vram = 0;
1129 share->accel_off = g_noaccel;
1130 share->dual = g_dualview;
1131 spin_lock_init(&share->slock);
1133 if (!share->accel_off) {
1134 /* hook deInit and 2d routines, notes that below hw_xxx
1135 * routine can work on most of lynx chips
1136 * if some chip need specific function,
1137 * please hook it in smXXX_set_drv routine */
1138 share->accel.de_init = hw_de_init;
1139 share->accel.de_fillrect = hw_fillrect;
1140 share->accel.de_copyarea = hw_copyarea;
1141 share->accel.de_imageblit = hw_imageblit;
1142 pr_info("enable 2d acceleration\n");
1144 pr_info("disable 2d acceleration\n");
1147 /* call chip specific setup routine */
1148 sm750fb_setup(share, g_settings);
1150 /* call chip specific mmap routine */
1151 if (hw_sm750_map(share, pdev)) {
1152 pr_err("Memory map failed\n");
1156 if (!share->mtrr_off)
1157 share->mtrr.vram = arch_phys_wc_add(share->vidmem_start,
1158 share->vidmem_size);
1160 memset_io(share->pvMem, 0, share->vidmem_size);
1162 pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg);
1164 pci_set_drvdata(pdev, share);
1166 /* call chipInit routine */
1167 hw_sm750_inithw(share, pdev);
1169 /* allocate frame buffer info structor according to g_dualview */
1172 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
1174 pr_err("Could not allocate framebuffer #%d.\n", fbidx);
1176 goto err_info0_alloc;
1178 goto err_info1_alloc;
1180 struct lynxfb_par *par;
1183 pr_info("framebuffer #%d alloc okay\n", fbidx);
1184 share->fbinfo[fbidx] = info[fbidx];
1185 par = info[fbidx]->par;
1188 /* set fb_info structure */
1189 if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
1190 pr_err("Failed to initial fb_info #%d.\n", fbidx);
1197 /* register frame buffer */
1198 pr_info("Ready to register framebuffer #%d.\n", fbidx);
1199 errno = register_framebuffer(info[fbidx]);
1201 pr_err("Failed to register fb_info #%d. err %d\n",
1209 pr_info("Accomplished register framebuffer #%d.\n", fbidx);
1212 /* no dual view by far */
1214 if (share->dual && fbidx < 2)
1221 framebuffer_release(info[1]);
1223 unregister_framebuffer(info[0]);
1226 framebuffer_release(info[0]);
1235 static void lynxfb_pci_remove(struct pci_dev *pdev)
1237 struct fb_info *info;
1238 struct lynx_share *share;
1240 struct lynxfb_par *par;
1244 share = pci_get_drvdata(pdev);
1247 info = share->fbinfo[cnt];
1252 unregister_framebuffer(info);
1253 /* clean crtc & output allocations */
1254 par->crtc.clear(&par->crtc);
1255 par->output.clear(&par->output);
1256 /* release frame buffer */
1257 framebuffer_release(info);
1259 arch_phys_wc_del(share->mtrr.vram);
1261 iounmap(share->pvReg);
1262 iounmap(share->pvMem);
1263 spec_share = container_of(share, struct sm750_share, share);
1266 pci_set_drvdata(pdev, NULL);
1269 static int __init lynxfb_setup(char *options)
1275 if (!options || !*options) {
1276 pr_warn("no options.\n");
1280 pr_info("options:%s\n", options);
1282 len = strlen(options) + 1;
1283 g_settings = kzalloc(len, GFP_KERNEL);
1290 char * strsep(char **s,const char * ct);
1291 @s: the string to be searched
1292 @ct :the characters to search for
1294 strsep() updates @options to pointer after the first found token
1295 it also returns the pointer ahead the token.
1297 while ((opt = strsep(&options, ":")) != NULL) {
1298 /* options that mean for any lynx chips are configured here */
1299 if (!strncmp(opt, "noaccel", strlen("noaccel")))
1301 else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
1303 else if (!strncmp(opt, "dual", strlen("dual")))
1308 if (options != NULL)
1315 /* misc g_settings are transport to chip specific routines */
1316 pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1320 static struct pci_device_id smi_pci_table[] = {
1321 { PCI_DEVICE(0x126f, 0x0750), },
1325 MODULE_DEVICE_TABLE(pci, smi_pci_table);
1327 static struct pci_driver lynxfb_driver = {
1329 .id_table = smi_pci_table,
1330 .probe = lynxfb_pci_probe,
1331 .remove = lynxfb_pci_remove,
1333 .suspend = lynxfb_suspend,
1334 .resume = lynxfb_resume,
1339 static int __init lynxfb_init(void)
1347 if (fb_get_options("sm750fb", &option))
1351 lynxfb_setup(option);
1352 ret = pci_register_driver(&lynxfb_driver);
1355 module_init(lynxfb_init);
1357 static void __exit lynxfb_exit(void)
1359 pci_unregister_driver(&lynxfb_driver);
1361 module_exit(lynxfb_exit);
1363 module_param(g_option, charp, S_IRUGO);
1365 MODULE_PARM_DESC(g_option,
1366 "\n\t\tCommon options:\n"
1367 "\t\tnoaccel:disable 2d capabilities\n"
1368 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1369 "\t\tdualview:dual frame buffer feature enabled\n"
1370 "\t\tnohwc:disable hardware cursor\n"
1371 "\t\tUsual example:\n"
1372 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1375 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1376 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1377 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1378 MODULE_LICENSE("GPL v2");