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 /* 1360 x 768 [1.77083:1] */
92 {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
93 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
94 FB_VMODE_NONINTERLACED},
96 /* 1368 x 768 [1.78:1] */
97 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
98 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
99 FB_VMODE_NONINTERLACED},
101 /* 1440 x 900 [16:10] */
102 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
103 FB_SYNC_VERT_HIGH_ACT,
104 FB_VMODE_NONINTERLACED},
106 /* 1440x960 [15:10] */
107 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
108 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
109 FB_VMODE_NONINTERLACED},
111 /* 1920x1080 [16:9] */
112 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
113 FB_SYNC_VERT_HIGH_ACT,
114 FB_VMODE_NONINTERLACED},
120 /* no hardware cursor supported under version 2.6.10, kernel bug */
121 static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
123 struct lynxfb_par *par;
124 struct lynxfb_crtc *crtc;
125 struct lynx_cursor *cursor;
129 cursor = &crtc->cursor;
131 if (fbcursor->image.width > cursor->maxW ||
132 fbcursor->image.height > cursor->maxH ||
133 fbcursor->image.depth > 1) {
137 cursor->disable(cursor);
138 if (fbcursor->set & FB_CUR_SETSIZE)
139 cursor->setSize(cursor,
140 fbcursor->image.width,
141 fbcursor->image.height);
143 if (fbcursor->set & FB_CUR_SETPOS)
144 cursor->setPos(cursor,
145 fbcursor->image.dx - info->var.xoffset,
146 fbcursor->image.dy - info->var.yoffset);
148 if (fbcursor->set & FB_CUR_SETCMAP) {
149 /* get the 16bit color of kernel means */
152 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
153 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
154 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
156 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
157 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
158 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
160 cursor->setColor(cursor, fg, bg);
164 if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
165 cursor->setData(cursor,
167 fbcursor->image.data,
171 if (fbcursor->enable)
172 cursor->enable(cursor);
177 static void lynxfb_ops_fillrect(struct fb_info *info,
178 const struct fb_fillrect *region)
180 struct lynxfb_par *par;
181 struct lynx_share *share;
182 unsigned int base, pitch, Bpp, rop;
185 if (info->state != FBINFO_STATE_RUNNING)
191 /* each time 2d function begin to work,below three variable always need
192 * be set, seems we can put them together in some place */
193 base = par->crtc.oScreen;
194 pitch = info->fix.line_length;
195 Bpp = info->var.bits_per_pixel >> 3;
197 color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color];
198 rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY;
201 * If not use spin_lock,system will die if user load driver
202 * and immediately unload driver frequently (dual)
205 spin_lock(&share->slock);
207 share->accel.de_fillrect(&share->accel,
209 region->dx, region->dy,
210 region->width, region->height,
213 spin_unlock(&share->slock);
216 static void lynxfb_ops_copyarea(struct fb_info *info,
217 const struct fb_copyarea *region)
219 struct lynxfb_par *par;
220 struct lynx_share *share;
221 unsigned int base, pitch, Bpp;
226 /* each time 2d function begin to work,below three variable always need
227 * be set, seems we can put them together in some place */
228 base = par->crtc.oScreen;
229 pitch = info->fix.line_length;
230 Bpp = info->var.bits_per_pixel >> 3;
233 * If not use spin_lock, system will die if user load driver
234 * and immediately unload driver frequently (dual)
237 spin_lock(&share->slock);
239 share->accel.de_copyarea(&share->accel,
240 base, pitch, region->sx, region->sy,
241 base, pitch, Bpp, region->dx, region->dy,
242 region->width, region->height, HW_ROP2_COPY);
244 spin_unlock(&share->slock);
247 static void lynxfb_ops_imageblit(struct fb_info *info,
248 const struct fb_image *image)
250 unsigned int base, pitch, Bpp;
251 unsigned int fgcol, bgcol;
252 struct lynxfb_par *par;
253 struct lynx_share *share;
257 /* each time 2d function begin to work,below three variable always need
258 * be set, seems we can put them together in some place */
259 base = par->crtc.oScreen;
260 pitch = info->fix.line_length;
261 Bpp = info->var.bits_per_pixel >> 3;
263 if (image->depth == 1) {
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];
269 fgcol = image->fg_color;
270 bgcol = image->bg_color;
274 /* TODO: Implement hardware acceleration for image->depth > 1 */
275 cfb_imageblit(info, image);
280 * If not use spin_lock, system will die if user load driver
281 * and immediately unload driver frequently (dual)
284 spin_lock(&share->slock);
286 share->accel.de_imageblit(&share->accel,
287 image->data, image->width>>3, 0,
289 image->dx, image->dy,
290 image->width, image->height,
291 fgcol, bgcol, HW_ROP2_COPY);
293 spin_unlock(&share->slock);
296 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
297 struct fb_info *info)
299 struct lynxfb_par *par;
300 struct lynxfb_crtc *crtc;
310 ret = crtc->proc_panDisplay(crtc, var, info);
315 static int lynxfb_ops_set_par(struct fb_info *info)
317 struct lynxfb_par *par;
318 struct lynx_share *share;
319 struct lynxfb_crtc *crtc;
320 struct lynxfb_output *output;
321 struct fb_var_screeninfo *var;
322 struct fb_fix_screeninfo *fix;
324 unsigned int line_length;
333 output = &par->output;
337 /* fix structur is not so FIX ... */
338 line_length = var->xres_virtual * var->bits_per_pixel / 8;
339 line_length = PADDING(crtc->line_pad, line_length);
340 fix->line_length = line_length;
341 pr_err("fix->line_length = %d\n", fix->line_length);
343 /* var->red,green,blue,transp are need to be set by driver
344 * and these data should be set before setcolreg routine
347 switch (var->bits_per_pixel) {
349 fix->visual = FB_VISUAL_PSEUDOCOLOR;
352 var->green.offset = 0;
353 var->green.length = 8;
354 var->blue.offset = 0;
355 var->blue.length = 8;
356 var->transp.length = 0;
357 var->transp.offset = 0;
360 var->red.offset = 11;
362 var->green.offset = 5;
363 var->green.length = 6;
364 var->blue.offset = 0;
365 var->blue.length = 5;
366 var->transp.length = 0;
367 var->transp.offset = 0;
368 fix->visual = FB_VISUAL_TRUECOLOR;
372 var->red.offset = 16;
374 var->green.offset = 8;
375 var->green.length = 8;
376 var->blue.offset = 0;
377 var->blue.length = 8;
378 fix->visual = FB_VISUAL_TRUECOLOR;
384 var->height = var->width = -1;
385 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
388 pr_err("pixel bpp format not satisfied\n.");
391 ret = crtc->proc_setMode(crtc, var, fix);
393 ret = output->proc_setMode(output, var, fix);
397 static inline unsigned int chan_to_field(unsigned int chan,
398 struct fb_bitfield *bf)
401 chan >>= 16 - bf->length;
402 return chan << bf->offset;
406 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
408 struct fb_info *info;
409 struct lynx_share *share;
412 if (mesg.event == pdev->dev.power.power_state.event)
416 share = pci_get_drvdata(pdev);
417 switch (mesg.event) {
418 case PM_EVENT_FREEZE:
419 case PM_EVENT_PRETHAW:
420 pdev->dev.power.power_state = mesg;
425 if (mesg.event & PM_EVENT_SLEEP) {
426 info = share->fbinfo[0];
428 /* 1 means do suspend */
429 fb_set_suspend(info, 1);
430 info = share->fbinfo[1];
432 /* 1 means do suspend */
433 fb_set_suspend(info, 1);
435 ret = pci_save_state(pdev);
437 pr_err("error:%d occurred in pci_save_state\n", ret);
441 /* set chip to sleep mode */
443 (*share->suspend)(share);
445 pci_disable_device(pdev);
446 ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
448 pr_err("error:%d occurred in pci_set_power_state\n", ret);
453 pdev->dev.power.power_state = mesg;
458 static int lynxfb_resume(struct pci_dev *pdev)
460 struct fb_info *info;
461 struct lynx_share *share;
463 struct lynxfb_par *par;
464 struct lynxfb_crtc *crtc;
465 struct lynx_cursor *cursor;
471 share = pci_get_drvdata(pdev);
475 ret = pci_set_power_state(pdev, PCI_D0);
477 pr_err("error:%d occurred in pci_set_power_state\n", ret);
482 if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
483 pci_restore_state(pdev);
484 ret = pci_enable_device(pdev);
486 pr_err("error:%d occurred in pci_enable_device\n", ret);
489 pci_set_master(pdev);
492 (*share->resume)(share);
494 hw_sm750_inithw(share, pdev);
497 info = share->fbinfo[0];
502 cursor = &crtc->cursor;
503 memset_io(cursor->vstart, 0x0, cursor->size);
504 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
505 lynxfb_ops_set_par(info);
506 fb_set_suspend(info, 0);
509 info = share->fbinfo[1];
514 cursor = &crtc->cursor;
515 memset_io(cursor->vstart, 0x0, cursor->size);
516 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
517 lynxfb_ops_set_par(info);
518 fb_set_suspend(info, 0);
527 static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
528 struct fb_info *info)
530 struct lynxfb_par *par;
531 struct lynxfb_crtc *crtc;
532 struct lynxfb_output *output;
533 struct lynx_share *share;
535 resource_size_t request;
540 output = &par->output;
544 pr_debug("check var:%dx%d-%d\n",
547 var->bits_per_pixel);
550 switch (var->bits_per_pixel) {
553 case 24: /* support 24 bpp for only lynx712/722/720 */
557 pr_err("bpp %d not supported\n", var->bits_per_pixel);
562 switch (var->bits_per_pixel) {
564 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
567 var->green.offset = 0;
568 var->green.length = 8;
569 var->blue.offset = 0;
570 var->blue.length = 8;
571 var->transp.length = 0;
572 var->transp.offset = 0;
575 var->red.offset = 11;
577 var->green.offset = 5;
578 var->green.length = 6;
579 var->blue.offset = 0;
580 var->blue.length = 5;
581 var->transp.length = 0;
582 var->transp.offset = 0;
583 info->fix.visual = FB_VISUAL_TRUECOLOR;
587 var->red.offset = 16;
589 var->green.offset = 8;
590 var->green.length = 8;
591 var->blue.offset = 0;
592 var->blue.length = 8;
593 info->fix.visual = FB_VISUAL_TRUECOLOR;
599 var->height = var->width = -1;
600 var->accel_flags = 0;/* FB_ACCELF_TEXT; */
602 /* check if current fb's video memory big enought to hold the onscreen*/
603 request = var->xres_virtual * (var->bits_per_pixel >> 3);
604 /* defaulty crtc->channel go with par->index */
606 request = PADDING(crtc->line_pad, request);
607 request = request * var->yres_virtual;
608 if (crtc->vidmem_size < request) {
609 pr_err("not enough video memory for mode\n");
613 ret = output->proc_checkMode(output, var);
615 ret = crtc->proc_checkMode(crtc, var);
621 static int lynxfb_ops_setcolreg(unsigned regno,
626 struct fb_info *info)
628 struct lynxfb_par *par;
629 struct lynxfb_crtc *crtc;
630 struct fb_var_screeninfo *var;
639 pr_err("regno = %d\n", regno);
643 if (info->var.grayscale)
644 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
646 if (var->bits_per_pixel == 8 &&
647 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
651 ret = crtc->proc_setColReg(crtc, regno, red, green, blue);
656 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
659 if (var->bits_per_pixel == 16 ||
660 var->bits_per_pixel == 32 ||
661 var->bits_per_pixel == 24) {
662 val = chan_to_field(red, &var->red);
663 val |= chan_to_field(green, &var->green);
664 val |= chan_to_field(blue, &var->blue);
665 par->pseudo_palette[regno] = val;
676 static int lynxfb_ops_blank(int blank, struct fb_info *info)
678 struct lynxfb_par *par;
679 struct lynxfb_output *output;
681 pr_debug("blank = %d.\n", blank);
683 output = &par->output;
684 return output->proc_setBLANK(output, blank);
687 static int sm750fb_set_drv(struct lynxfb_par *par)
690 struct lynx_share *share;
691 struct sm750_share *spec_share;
692 struct lynxfb_output *output;
693 struct lynxfb_crtc *crtc;
698 spec_share = container_of(share, struct sm750_share, share);
699 output = &par->output;
702 crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
703 /* setup crtc and output member */
704 spec_share->hwCursor = g_hwcursor;
706 crtc->proc_setMode = hw_sm750_crtc_setMode;
707 crtc->proc_checkMode = hw_sm750_crtc_checkMode;
708 crtc->proc_setColReg = hw_sm750_setColReg;
709 crtc->proc_panDisplay = hw_sm750_pan_display;
710 crtc->clear = hw_sm750_crtc_clear;
716 output->proc_setMode = hw_sm750_output_setMode;
717 output->proc_checkMode = hw_sm750_output_checkMode;
719 output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
720 output->clear = hw_sm750_output_clear;
721 /* chip specific phase */
722 share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait;
723 switch (spec_share->state.dataflow) {
724 case sm750_simul_pri:
725 output->paths = sm750_pnc;
726 crtc->channel = sm750_primary;
728 crtc->vScreen = share->pvMem;
729 pr_info("use simul primary mode\n");
731 case sm750_simul_sec:
732 output->paths = sm750_pnc;
733 crtc->channel = sm750_secondary;
735 crtc->vScreen = share->pvMem;
737 case sm750_dual_normal:
738 if (par->index == 0) {
739 output->paths = sm750_panel;
740 crtc->channel = sm750_primary;
742 crtc->vScreen = share->pvMem;
744 output->paths = sm750_crt;
745 crtc->channel = sm750_secondary;
746 /* not consider of padding stuffs for oScreen,need fix */
747 crtc->oScreen = (share->vidmem_size >> 1);
748 crtc->vScreen = share->pvMem + crtc->oScreen;
751 case sm750_dual_swap:
752 if (par->index == 0) {
753 output->paths = sm750_panel;
754 crtc->channel = sm750_secondary;
756 crtc->vScreen = share->pvMem;
758 output->paths = sm750_crt;
759 crtc->channel = sm750_primary;
760 /* not consider of padding stuffs for oScreen,need fix */
761 crtc->oScreen = (share->vidmem_size >> 1);
762 crtc->vScreen = share->pvMem + crtc->oScreen;
772 static struct fb_ops lynxfb_ops = {
773 .owner = THIS_MODULE,
774 .fb_check_var = lynxfb_ops_check_var,
775 .fb_set_par = lynxfb_ops_set_par,
776 .fb_setcolreg = lynxfb_ops_setcolreg,
777 .fb_blank = lynxfb_ops_blank,
778 .fb_fillrect = cfb_fillrect,
779 .fb_imageblit = cfb_imageblit,
780 .fb_copyarea = cfb_copyarea,
782 .fb_cursor = lynxfb_ops_cursor,
786 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
789 struct lynxfb_par *par;
790 struct lynx_share *share;
791 struct lynxfb_crtc *crtc;
792 struct lynxfb_output *output;
793 struct fb_var_screeninfo *var;
794 struct fb_fix_screeninfo *fix;
796 const struct fb_videomode *pdb[] = {
797 lynx750_ext, NULL, vesa_modes,
799 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
800 static const char *mdb_desc[] = {
801 "driver prepared modes",
802 "kernel prepared default modedb",
803 "kernel HELPERS prepared vesa_modes",
807 static const char *fixId[2] = {
808 "sm750_fb1", "sm750_fb2",
811 int ret, line_length;
814 par = (struct lynxfb_par *)info->par;
817 output = &par->output;
823 output->channel = &crtc->channel;
824 sm750fb_set_drv(par);
825 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
828 /* set current cursor variable and proc pointer,
829 * must be set after crtc member initialized */
830 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
831 crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
833 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
834 crtc->cursor.maxH = crtc->cursor.maxW = 64;
835 crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
836 crtc->cursor.disable = hw_cursor_disable;
837 crtc->cursor.enable = hw_cursor_enable;
838 crtc->cursor.setColor = hw_cursor_setColor;
839 crtc->cursor.setPos = hw_cursor_setPos;
840 crtc->cursor.setSize = hw_cursor_setSize;
841 crtc->cursor.setData = hw_cursor_setData;
842 crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
845 crtc->cursor.share = share;
846 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
848 lynxfb_ops.fb_cursor = NULL;
849 crtc->cursor.disable(&crtc->cursor);
853 /* set info->fbops, must be set before fb_find_mode */
854 if (!share->accel_off) {
855 /* use 2d acceleration */
856 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
857 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
858 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
860 info->fbops = &lynxfb_ops;
862 if (!g_fbmode[index]) {
863 g_fbmode[index] = g_def_fbmode;
865 g_fbmode[index] = g_fbmode[0];
869 for (i = 0; i < 3; i++) {
871 ret = fb_find_mode(var, info, g_fbmode[index],
872 pdb[i], cdb[i], NULL, 8);
875 pr_info("success! use specified mode:%s in %s\n",
879 } else if (ret == 2) {
880 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
884 } else if (ret == 3) {
885 pr_warn("wanna use default mode\n");
887 } else if (ret == 4) {
888 pr_warn("fall back to any valid mode\n");
890 pr_warn("ret = %d,fb_find_mode failed,with %s\n",
896 /* some member of info->var had been set by fb_find_mode */
898 pr_info("Member of info->var is :\n\
905 bits_per_pixel=%d\n \
913 var->bits_per_pixel);
919 line_length = PADDING(crtc->line_pad,
920 (var->xres_virtual * var->bits_per_pixel/8));
922 info->pseudo_palette = &par->pseudo_palette[0];
923 info->screen_base = crtc->vScreen;
924 pr_debug("screen_base vaddr = %p\n", info->screen_base);
925 info->screen_size = line_length * var->yres_virtual;
926 info->flags = FBINFO_FLAG_DEFAULT|0;
929 fix->type = FB_TYPE_PACKED_PIXELS;
931 fix->xpanstep = crtc->xpanstep;
932 fix->ypanstep = crtc->ypanstep;
933 fix->ywrapstep = crtc->ywrapstep;
934 fix->accel = FB_ACCEL_SMI;
936 strlcpy(fix->id, fixId[index], sizeof(fix->id));
939 fix->smem_start = crtc->oScreen + share->vidmem_start;
940 pr_info("fix->smem_start = %lx\n", fix->smem_start);
941 /* according to mmap experiment from user space application,
942 * fix->mmio_len should not larger than virtual size
943 * (xres_virtual x yres_virtual x ByPP)
944 * Below line maybe buggy when user mmap fb dev node and write
945 * data into the bound over virtual size
947 fix->smem_len = crtc->vidmem_size;
948 pr_info("fix->smem_len = %x\n", fix->smem_len);
949 info->screen_size = fix->smem_len;
950 fix->line_length = line_length;
951 fix->mmio_start = share->vidreg_start;
952 pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
953 fix->mmio_len = share->vidreg_size;
954 pr_info("fix->mmio_len = %x\n", fix->mmio_len);
955 switch (var->bits_per_pixel) {
957 fix->visual = FB_VISUAL_PSEUDOCOLOR;
961 fix->visual = FB_VISUAL_TRUECOLOR;
966 var->activate = FB_ACTIVATE_NOW;
967 var->accel_flags = 0;
968 var->vmode = FB_VMODE_NONINTERLACED;
970 pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
971 info->cmap.start, info->cmap.len,
972 info->cmap.red, info->cmap.green, info->cmap.blue,
975 ret = fb_alloc_cmap(&info->cmap, 256, 0);
977 pr_err("Could not allocate memory for cmap.\n");
981 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
982 info->cmap.start, info->cmap.len,
983 info->cmap.red, info->cmap.green, info->cmap.blue,
987 lynxfb_ops_check_var(var, info);
991 /* chip specific g_option configuration routine */
992 static void sm750fb_setup(struct lynx_share *share, char *src)
994 struct sm750_share *spec_share;
1002 spec_share = container_of(share, struct sm750_share, share);
1003 #ifdef CAP_EXPENSIION
1008 spec_share->state.initParm.chip_clk = 0;
1009 spec_share->state.initParm.mem_clk = 0;
1010 spec_share->state.initParm.master_clk = 0;
1011 spec_share->state.initParm.powerMode = 0;
1012 spec_share->state.initParm.setAllEngOff = 0;
1013 spec_share->state.initParm.resetMemory = 1;
1015 /* defaultly turn g_hwcursor on for both view */
1018 if (!src || !*src) {
1019 pr_warn("no specific g_option.\n");
1023 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
1024 pr_err("opt=%s\n", opt);
1025 pr_err("src=%s\n", src);
1027 if (!strncmp(opt, "swap", strlen("swap")))
1029 else if (!strncmp(opt, "nocrt", strlen("nocrt")))
1030 spec_share->state.nocrt = 1;
1031 else if (!strncmp(opt, "36bit", strlen("36bit")))
1032 spec_share->state.pnltype = sm750_doubleTFT;
1033 else if (!strncmp(opt, "18bit", strlen("18bit")))
1034 spec_share->state.pnltype = sm750_dualTFT;
1035 else if (!strncmp(opt, "24bit", strlen("24bit")))
1036 spec_share->state.pnltype = sm750_24TFT;
1037 #ifdef CAP_EXPANSION
1038 else if (!strncmp(opt, "exp:", strlen("exp:")))
1039 exp_res = opt + strlen("exp:");
1041 else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
1043 else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
1045 else if (!strncmp(opt, "nohwc", strlen("nohwc")))
1050 pr_info("find fbmode0 : %s\n", g_fbmode[0]);
1051 } else if (!g_fbmode[1]) {
1053 pr_info("find fbmode1 : %s\n", g_fbmode[1]);
1055 pr_warn("How many view you wann set?\n");
1059 #ifdef CAP_EXPANSION
1060 if (getExpRes(exp_res,
1061 &spec_share->state.xLCD,
1062 &spec_share->state.yLCD)) {
1063 /* seems exp_res is not valid */
1064 spec_share->state.xLCD = spec_share->state.yLCD = 0;
1069 if (share->revid != SM750LE_REVISION_ID) {
1072 spec_share->state.dataflow = sm750_dual_swap;
1074 spec_share->state.dataflow = sm750_dual_normal;
1077 spec_share->state.dataflow = sm750_simul_sec;
1079 spec_share->state.dataflow = sm750_simul_pri;
1082 /* SM750LE only have one crt channel */
1083 spec_share->state.dataflow = sm750_simul_sec;
1084 /* sm750le do not have complex attributes */
1085 spec_share->state.nocrt = 0;
1089 static int lynxfb_pci_probe(struct pci_dev *pdev,
1090 const struct pci_device_id * ent)
1092 struct fb_info *info[] = {NULL, NULL};
1093 struct lynx_share *share = NULL;
1095 struct sm750_share *spec_share = NULL;
1096 size_t spec_offset = 0;
1101 if (pci_enable_device(pdev)) {
1102 pr_err("can not enable device.\n");
1106 /* though offset of share in sm750_share is 0,
1107 * we use this marcro as the same */
1108 spec_offset = offsetof(struct sm750_share, share);
1110 spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL);
1112 pr_err("Could not allocate memory for share.\n");
1116 /* setting share structure */
1117 share = (struct lynx_share *)(&(spec_share->share));
1118 share->fbinfo[0] = share->fbinfo[1] = NULL;
1119 share->devid = pdev->device;
1120 share->revid = pdev->revision;
1122 pr_info("share->revid = %02x\n", share->revid);
1124 share->mtrr_off = g_nomtrr;
1125 share->mtrr.vram = 0;
1126 share->accel_off = g_noaccel;
1127 share->dual = g_dualview;
1128 spin_lock_init(&share->slock);
1130 if (!share->accel_off) {
1131 /* hook deInit and 2d routines, notes that below hw_xxx
1132 * routine can work on most of lynx chips
1133 * if some chip need specific function,
1134 * please hook it in smXXX_set_drv routine */
1135 share->accel.de_init = hw_de_init;
1136 share->accel.de_fillrect = hw_fillrect;
1137 share->accel.de_copyarea = hw_copyarea;
1138 share->accel.de_imageblit = hw_imageblit;
1139 pr_info("enable 2d acceleration\n");
1141 pr_info("disable 2d acceleration\n");
1144 /* call chip specific setup routine */
1145 sm750fb_setup(share, g_settings);
1147 /* call chip specific mmap routine */
1148 if (hw_sm750_map(share, pdev)) {
1149 pr_err("Memory map failed\n");
1153 if (!share->mtrr_off)
1154 share->mtrr.vram = arch_phys_wc_add(share->vidmem_start,
1155 share->vidmem_size);
1157 memset_io(share->pvMem, 0, share->vidmem_size);
1159 pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg);
1161 pci_set_drvdata(pdev, share);
1163 /* call chipInit routine */
1164 hw_sm750_inithw(share, pdev);
1166 /* allocate frame buffer info structor according to g_dualview */
1169 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
1171 pr_err("Could not allocate framebuffer #%d.\n", fbidx);
1173 goto err_info0_alloc;
1175 goto err_info1_alloc;
1177 struct lynxfb_par *par;
1180 pr_info("framebuffer #%d alloc okay\n", fbidx);
1181 share->fbinfo[fbidx] = info[fbidx];
1182 par = info[fbidx]->par;
1185 /* set fb_info structure */
1186 if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
1187 pr_err("Failed to initial fb_info #%d.\n", fbidx);
1194 /* register frame buffer */
1195 pr_info("Ready to register framebuffer #%d.\n", fbidx);
1196 errno = register_framebuffer(info[fbidx]);
1198 pr_err("Failed to register fb_info #%d. err %d\n",
1206 pr_info("Accomplished register framebuffer #%d.\n", fbidx);
1209 /* no dual view by far */
1211 if (share->dual && fbidx < 2)
1218 framebuffer_release(info[1]);
1220 unregister_framebuffer(info[0]);
1223 framebuffer_release(info[0]);
1232 static void lynxfb_pci_remove(struct pci_dev *pdev)
1234 struct fb_info *info;
1235 struct lynx_share *share;
1237 struct lynxfb_par *par;
1241 share = pci_get_drvdata(pdev);
1244 info = share->fbinfo[cnt];
1249 unregister_framebuffer(info);
1250 /* clean crtc & output allocations */
1251 par->crtc.clear(&par->crtc);
1252 par->output.clear(&par->output);
1253 /* release frame buffer */
1254 framebuffer_release(info);
1256 arch_phys_wc_del(share->mtrr.vram);
1258 iounmap(share->pvReg);
1259 iounmap(share->pvMem);
1260 spec_share = container_of(share, struct sm750_share, share);
1263 pci_set_drvdata(pdev, NULL);
1266 static int __init lynxfb_setup(char *options)
1272 if (!options || !*options) {
1273 pr_warn("no options.\n");
1277 pr_info("options:%s\n", options);
1279 len = strlen(options) + 1;
1280 g_settings = kzalloc(len, GFP_KERNEL);
1287 char * strsep(char **s,const char * ct);
1288 @s: the string to be searched
1289 @ct :the characters to search for
1291 strsep() updates @options to pointer after the first found token
1292 it also returns the pointer ahead the token.
1294 while ((opt = strsep(&options, ":")) != NULL) {
1295 /* options that mean for any lynx chips are configured here */
1296 if (!strncmp(opt, "noaccel", strlen("noaccel")))
1298 else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
1300 else if (!strncmp(opt, "dual", strlen("dual")))
1305 if (options != NULL)
1312 /* misc g_settings are transport to chip specific routines */
1313 pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1317 static struct pci_device_id smi_pci_table[] = {
1318 { PCI_DEVICE(0x126f, 0x0750), },
1322 MODULE_DEVICE_TABLE(pci, smi_pci_table);
1324 static struct pci_driver lynxfb_driver = {
1326 .id_table = smi_pci_table,
1327 .probe = lynxfb_pci_probe,
1328 .remove = lynxfb_pci_remove,
1330 .suspend = lynxfb_suspend,
1331 .resume = lynxfb_resume,
1336 static int __init lynxfb_init(void)
1344 if (fb_get_options("sm750fb", &option))
1348 lynxfb_setup(option);
1349 ret = pci_register_driver(&lynxfb_driver);
1352 module_init(lynxfb_init);
1354 static void __exit lynxfb_exit(void)
1356 pci_unregister_driver(&lynxfb_driver);
1358 module_exit(lynxfb_exit);
1360 module_param(g_option, charp, S_IRUGO);
1362 MODULE_PARM_DESC(g_option,
1363 "\n\t\tCommon options:\n"
1364 "\t\tnoaccel:disable 2d capabilities\n"
1365 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1366 "\t\tdualview:dual frame buffer feature enabled\n"
1367 "\t\tnohwc:disable hardware cursor\n"
1368 "\t\tUsual example:\n"
1369 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1372 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1373 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1374 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1375 MODULE_LICENSE("GPL v2");