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>
25 #include "sm750_accel.h"
26 #include "sm750_cursor.h"
35 ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
36 size_t count, loff_t *ppos);
37 ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
38 size_t count, loff_t *ppos);
42 typedef void (*PROC_SPEC_SETUP
)(struct lynx_share
*,char *);
43 typedef int (*PROC_SPEC_MAP
)(struct lynx_share
*,struct pci_dev
*);
44 typedef int (*PROC_SPEC_INITHW
)(struct lynx_share
*,struct pci_dev
*);
47 /* common var for all device */
48 static int g_hwcursor
= 1;
49 static int g_noaccel
= 0;
51 static int g_nomtrr
= 0;
53 static const char * g_fbmode
[] = {NULL
,NULL
};
54 static const char * g_def_fbmode
= "800x600-16@60";
55 static char * g_settings
= NULL
;
56 static int g_dualview
= 0;
58 static char * g_option
= NULL
;
61 /* if not use spin_lock,system will die if user load driver
62 * and immediatly unload driver frequently (dual)*/
63 static inline void myspin_lock(spinlock_t
* sl
){
64 struct lynx_share
* share
;
65 share
= container_of(sl
,struct lynx_share
,slock
);
71 static inline void myspin_unlock(spinlock_t
* sl
){
72 struct lynx_share
* share
;
73 share
= container_of(sl
,struct lynx_share
,slock
);
78 static const struct fb_videomode lynx750_ext
[] = {
79 /* 1024x600-60 VESA [1.71:1] */
80 {NULL
, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
81 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
83 /* 1024x600-70 VESA */
84 {NULL
, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
85 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
87 /* 1024x600-75 VESA */
88 {NULL
, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
89 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
91 /* 1024x600-85 VESA */
92 {NULL
, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
93 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
96 {NULL
, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
97 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
99 /* 1280x720 [1.78:1] */
100 {NULL
, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
101 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
,FB_VMODE_NONINTERLACED
},
104 {NULL
,60,1280,768,12579,192,64,20,3,128,7,
105 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
,FB_VMODE_NONINTERLACED
},
107 {NULL
,60,1360,768,11804,208,64,23,1,144,3,
108 FB_SYNC_HOR_HIGH_ACT
|FB_VMODE_NONINTERLACED
},
110 /* 1360 x 768 [1.77083:1] */
111 {NULL
, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
112 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
114 /* 1368 x 768 [1.78:1] */
115 {NULL
, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
116 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
118 /* 1440 x 900 [16:10] */
119 {NULL
, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
120 FB_SYNC_VERT_HIGH_ACT
,FB_VMODE_NONINTERLACED
},
122 /* 1440x960 [15:10] */
123 {NULL
, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
124 FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
, FB_VMODE_NONINTERLACED
},
126 /* 1920x1080 [16:9] */
127 {NULL
, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
128 FB_SYNC_VERT_HIGH_ACT
,FB_VMODE_NONINTERLACED
},
134 /* no hardware cursor supported under version 2.6.10, kernel bug */
135 static int lynxfb_ops_cursor(struct fb_info
* info
,struct fb_cursor
* fbcursor
)
137 struct lynxfb_par
* par
;
138 struct lynxfb_crtc
* crtc
;
139 struct lynx_cursor
* cursor
;
143 cursor
= &crtc
->cursor
;
145 if(fbcursor
->image
.width
> cursor
->maxW
||
146 fbcursor
->image
.height
> cursor
->maxH
||
147 fbcursor
->image
.depth
> 1){
151 cursor
->disable(cursor
);
152 if(fbcursor
->set
& FB_CUR_SETSIZE
){
153 cursor
->setSize(cursor
,fbcursor
->image
.width
,fbcursor
->image
.height
);
156 if(fbcursor
->set
& FB_CUR_SETPOS
){
157 cursor
->setPos(cursor
,fbcursor
->image
.dx
- info
->var
.xoffset
,
158 fbcursor
->image
.dy
- info
->var
.yoffset
);
161 if(fbcursor
->set
& FB_CUR_SETCMAP
){
162 /* get the 16bit color of kernel means */
164 fg
= ((info
->cmap
.red
[fbcursor
->image
.fg_color
] & 0xf800))|
165 ((info
->cmap
.green
[fbcursor
->image
.fg_color
] & 0xfc00) >> 5)|
166 ((info
->cmap
.blue
[fbcursor
->image
.fg_color
] & 0xf800) >> 11);
168 bg
= ((info
->cmap
.red
[fbcursor
->image
.bg_color
] & 0xf800))|
169 ((info
->cmap
.green
[fbcursor
->image
.bg_color
] & 0xfc00) >> 5)|
170 ((info
->cmap
.blue
[fbcursor
->image
.bg_color
] & 0xf800) >> 11);
172 cursor
->setColor(cursor
,fg
,bg
);
176 if(fbcursor
->set
& (FB_CUR_SETSHAPE
| FB_CUR_SETIMAGE
))
178 cursor
->setData(cursor
,
180 fbcursor
->image
.data
,
184 if(fbcursor
->enable
){
185 cursor
->enable(cursor
);
191 static void lynxfb_ops_fillrect(struct fb_info
* info
,const struct fb_fillrect
* region
)
193 struct lynxfb_par
* par
;
194 struct lynx_share
* share
;
195 unsigned int base
,pitch
,Bpp
,rop
;
198 if(info
->state
!= FBINFO_STATE_RUNNING
){
205 /* each time 2d function begin to work,below three variable always need
206 * be set, seems we can put them together in some place */
207 base
= par
->crtc
.oScreen
;
208 pitch
= info
->fix
.line_length
;
209 Bpp
= info
->var
.bits_per_pixel
>> 3;
211 color
= (Bpp
== 1)?region
->color
:((u32
*)info
->pseudo_palette
)[region
->color
];
212 rop
= ( region
->rop
!= ROP_COPY
) ? HW_ROP2_XOR
:HW_ROP2_COPY
;
214 myspin_lock(&share
->slock
);
215 share
->accel
.de_fillrect(&share
->accel
,
217 region
->dx
,region
->dy
,
218 region
->width
,region
->height
,
220 myspin_unlock(&share
->slock
);
223 static void lynxfb_ops_copyarea(struct fb_info
* info
,const struct fb_copyarea
* region
)
225 struct lynxfb_par
* par
;
226 struct lynx_share
* share
;
227 unsigned int base
,pitch
,Bpp
;
232 /* each time 2d function begin to work,below three variable always need
233 * be set, seems we can put them together in some place */
234 base
= par
->crtc
.oScreen
;
235 pitch
= info
->fix
.line_length
;
236 Bpp
= info
->var
.bits_per_pixel
>> 3;
238 myspin_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
);
243 myspin_unlock(&share
->slock
);
246 static void lynxfb_ops_imageblit(struct fb_info
*info
,const struct fb_image
* image
)
248 unsigned int base
,pitch
,Bpp
;
249 unsigned int fgcol
,bgcol
;
250 struct lynxfb_par
* par
;
251 struct lynx_share
* share
;
255 /* each time 2d function begin to work,below three variable always need
256 * be set, seems we can put them together in some place */
257 base
= par
->crtc
.oScreen
;
258 pitch
= info
->fix
.line_length
;
259 Bpp
= info
->var
.bits_per_pixel
>> 3;
261 if(image
->depth
== 1){
262 if(info
->fix
.visual
== FB_VISUAL_TRUECOLOR
||
263 info
->fix
.visual
== FB_VISUAL_DIRECTCOLOR
)
265 fgcol
= ((u32
*)info
->pseudo_palette
)[image
->fg_color
];
266 bgcol
= ((u32
*)info
->pseudo_palette
)[image
->bg_color
];
270 fgcol
= image
->fg_color
;
271 bgcol
= image
->bg_color
;
277 myspin_lock(&share
->slock
);
278 share
->accel
.de_imageblit(&share
->accel
,
279 image
->data
,image
->width
>>3,0,
282 image
->width
,image
->height
,
283 fgcol
,bgcol
,HW_ROP2_COPY
);
284 myspin_unlock(&share
->slock
);
287 static int lynxfb_ops_pan_display(struct fb_var_screeninfo
*var
,
288 struct fb_info
*info
)
290 struct lynxfb_par
* par
;
291 struct lynxfb_crtc
* crtc
;
301 ret
= crtc
->proc_panDisplay(crtc
, var
, info
);
310 static int lynxfb_suspend(struct pci_dev
* pdev
,pm_message_t mesg
)
312 struct fb_info
* info
;
313 struct lynx_share
* share
;
317 if(mesg
.event
== pdev
->dev
.power
.power_state
.event
)
321 share
= pci_get_drvdata(pdev
);
322 switch (mesg
.event
) {
323 case PM_EVENT_FREEZE
:
324 case PM_EVENT_PRETHAW
:
325 pdev
->dev
.power
.power_state
= mesg
;
330 if (mesg
.event
& PM_EVENT_SLEEP
) {
331 info
= share
->fbinfo
[0];
333 fb_set_suspend(info
, 1);/* 1 means do suspend*/
335 info
= share
->fbinfo
[1];
337 fb_set_suspend(info
, 1);/* 1 means do suspend*/
339 ret
= pci_save_state(pdev
);
341 pr_err("error:%d occured in pci_save_state\n",ret
);
345 /* set chip to sleep mode */
347 (*share
->suspend
)(share
);
349 pci_disable_device(pdev
);
350 ret
= pci_set_power_state(pdev
,pci_choose_state(pdev
,mesg
));
352 pr_err("error:%d occured in pci_set_power_state\n",ret
);
357 pdev
->dev
.power
.power_state
= mesg
;
362 static int lynxfb_ops_set_par(struct fb_info
* info
)
364 struct lynxfb_par
* par
;
365 struct lynx_share
* share
;
366 struct lynxfb_crtc
* crtc
;
367 struct lynxfb_output
* output
;
368 struct fb_var_screeninfo
* var
;
369 struct fb_fix_screeninfo
* fix
;
371 unsigned int line_length
;
381 output
= &par
->output
;
385 /* fix structur is not so FIX ... */
386 line_length
= var
->xres_virtual
* var
->bits_per_pixel
/ 8;
387 line_length
= PADDING(crtc
->line_pad
,line_length
);
388 fix
->line_length
= line_length
;
389 pr_err("fix->line_length = %d\n",fix
->line_length
);
391 /* var->red,green,blue,transp are need to be set by driver
392 * and these data should be set before setcolreg routine
395 switch(var
->bits_per_pixel
){
397 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
400 var
->green
.offset
= 0;
401 var
->green
.length
= 8;
402 var
->blue
.offset
= 0;
403 var
->blue
.length
= 8;
404 var
->transp
.length
= 0;
405 var
->transp
.offset
= 0;
408 var
->red
.offset
= 11;
410 var
->green
.offset
= 5;
411 var
->green
.length
= 6;
412 var
->blue
.offset
= 0;
413 var
->blue
.length
= 5;
414 var
->transp
.length
= 0;
415 var
->transp
.offset
= 0;
416 fix
->visual
= FB_VISUAL_TRUECOLOR
;
420 var
->red
.offset
= 16;
422 var
->green
.offset
= 8;
423 var
->green
.length
= 8;
424 var
->blue
.offset
= 0 ;
425 var
->blue
.length
= 8;
426 fix
->visual
= FB_VISUAL_TRUECOLOR
;
432 var
->height
= var
->width
= -1;
433 var
->accel_flags
= 0;/*FB_ACCELF_TEXT;*/
436 pr_err("pixel bpp format not satisfied\n.");
439 ret
= crtc
->proc_setMode(crtc
,var
,fix
);
441 ret
= output
->proc_setMode(output
,var
,fix
);
444 static inline unsigned int chan_to_field(unsigned int chan
,struct fb_bitfield
* bf
)
447 chan
>>= 16 - bf
->length
;
448 return chan
<< bf
->offset
;
452 static int lynxfb_resume(struct pci_dev
* pdev
)
454 struct fb_info
* info
;
455 struct lynx_share
* share
;
457 struct lynxfb_par
* par
;
458 struct lynxfb_crtc
* crtc
;
459 struct lynx_cursor
* cursor
;
465 share
= pci_get_drvdata(pdev
);
469 if((ret
= pci_set_power_state(pdev
, PCI_D0
)) != 0){
470 pr_err("error:%d occured in pci_set_power_state\n",ret
);
475 if(pdev
->dev
.power
.power_state
.event
!= PM_EVENT_FREEZE
){
476 pci_restore_state(pdev
);
477 if ((ret
= pci_enable_device(pdev
)) != 0){
478 pr_err("error:%d occured in pci_enable_device\n",ret
);
481 pci_set_master(pdev
);
484 (*share
->resume
)(share
);
486 hw_sm750_inithw(share
,pdev
);
489 info
= share
->fbinfo
[0];
494 cursor
= &crtc
->cursor
;
495 memset(cursor
->vstart
, 0x0, cursor
->size
);
496 memset(crtc
->vScreen
,0x0,crtc
->vidmem_size
);
497 lynxfb_ops_set_par(info
);
498 fb_set_suspend(info
, 0);
501 info
= share
->fbinfo
[1];
506 cursor
= &crtc
->cursor
;
507 memset(cursor
->vstart
, 0x0, cursor
->size
);
508 memset(crtc
->vScreen
,0x0,crtc
->vidmem_size
);
509 lynxfb_ops_set_par(info
);
510 fb_set_suspend(info
, 0);
519 static int lynxfb_ops_mmap(struct fb_info
* info
, struct vm_area_struct
* vma
)
530 if (vma
->vm_pgoff
> (~0UL >> PAGE_SHIFT
))
532 off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
533 printk("lynxfb mmap pgoff: %x\n", vma
->vm_pgoff
);
534 printk("lynxfb mmap off 1: %x\n", off
);
536 /* frame buffer memory */
537 start
= info
->fix
.smem_start
;
538 len
= PAGE_ALIGN((start
& ~PAGE_MASK
) + info
->fix
.smem_len
);
540 printk("lynxfb mmap start 1: %x\n", start
);
541 printk("lynxfb mmap len 1: %x\n", len
);
544 /* memory mapped io */
546 printk("lynxfb mmap off 2: %x\n", off
);
547 if (info
->var
.accel_flags
) {
548 printk("lynxfb mmap accel flags true");
551 start
= info
->fix
.mmio_start
;
552 len
= PAGE_ALIGN((start
& ~PAGE_MASK
) + info
->fix
.mmio_len
);
554 printk("lynxfb mmap start 2: %x\n", start
);
555 printk("lynxfb mmap len 2: %x\n", len
);
558 printk("lynxfb mmap start 3: %x\n", start
);
559 printk("lynxfb mmap vm start: %x\n", vma
->vm_start
);
560 printk("lynxfb mmap vm end: %x\n", vma
->vm_end
);
561 printk("lynxfb mmap len: %x\n", len
);
562 printk("lynxfb mmap off: %x\n", off
);
563 if ((vma
->vm_end
- vma
->vm_start
+ off
) > len
)
568 printk("lynxfb mmap off 3: %x\n", off
);
569 vma
->vm_pgoff
= off
>> PAGE_SHIFT
;
570 /* This is an IO map - tell maydump to skip this VMA */
571 vma
->vm_flags
|= VM_IO
| VM_DONTEXPAND
| VM_DONTDUMP
;
572 vma
->vm_page_prot
= vm_get_page_prot(vma
->vm_flags
);
573 fb_pgprotect(file
, vma
, off
);
574 printk("lynxfb mmap off 4: %x\n", off
);
575 printk("lynxfb mmap pgprot: %x\n", vma
->vm_page_prot
);
576 if (io_remap_pfn_range(vma
, vma
->vm_start
, off
>> PAGE_SHIFT
,
577 vma
->vm_end
- vma
->vm_start
, vma
->vm_page_prot
))
582 static int lynxfb_ops_check_var(struct fb_var_screeninfo
* var
,struct fb_info
* info
)
584 struct lynxfb_par
* par
;
585 struct lynxfb_crtc
* crtc
;
586 struct lynxfb_output
* output
;
587 struct lynx_share
* share
;
589 resource_size_t request
;
594 output
= &par
->output
;
598 pr_debug("check var:%dx%d-%d\n",
601 var
->bits_per_pixel
);
604 switch(var
->bits_per_pixel
){
607 case 24: /* support 24 bpp for only lynx712/722/720 */
611 pr_err("bpp %d not supported\n",var
->bits_per_pixel
);
616 switch(var
->bits_per_pixel
){
618 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
621 var
->green
.offset
= 0;
622 var
->green
.length
= 8;
623 var
->blue
.offset
= 0;
624 var
->blue
.length
= 8;
625 var
->transp
.length
= 0;
626 var
->transp
.offset
= 0;
629 var
->red
.offset
= 11;
631 var
->green
.offset
= 5;
632 var
->green
.length
= 6;
633 var
->blue
.offset
= 0;
634 var
->blue
.length
= 5;
635 var
->transp
.length
= 0;
636 var
->transp
.offset
= 0;
637 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
641 var
->red
.offset
= 16;
643 var
->green
.offset
= 8;
644 var
->green
.length
= 8;
645 var
->blue
.offset
= 0 ;
646 var
->blue
.length
= 8;
647 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
653 var
->height
= var
->width
= -1;
654 var
->accel_flags
= 0;/*FB_ACCELF_TEXT;*/
656 /* check if current fb's video memory big enought to hold the onscreen */
657 request
= var
->xres_virtual
* (var
->bits_per_pixel
>> 3);
658 /* defaulty crtc->channel go with par->index */
660 request
= PADDING(crtc
->line_pad
,request
);
661 request
= request
* var
->yres_virtual
;
662 if(crtc
->vidmem_size
< request
){
663 pr_err("not enough video memory for mode\n");
667 ret
= output
->proc_checkMode(output
,var
);
669 ret
= crtc
->proc_checkMode(crtc
,var
);
675 static int lynxfb_ops_setcolreg(unsigned regno
,unsigned red
,
676 unsigned green
,unsigned blue
,
677 unsigned transp
,struct fb_info
* info
)
679 struct lynxfb_par
* par
;
680 struct lynxfb_crtc
* crtc
;
681 struct fb_var_screeninfo
* var
;
689 //pr_debug("regno=%d,red=%d,green=%d,blue=%d\n",regno,red,green,blue);
691 pr_err("regno = %d\n",regno
);
695 if(info
->var
.grayscale
)
696 red
= green
= blue
= (red
* 77 + green
* 151 + blue
* 28) >> 8;
698 if(var
->bits_per_pixel
== 8 && info
->fix
.visual
== FB_VISUAL_PSEUDOCOLOR
)
703 ret
= crtc
->proc_setColReg(crtc
,regno
,red
,green
,blue
);
708 if(info
->fix
.visual
== FB_VISUAL_TRUECOLOR
&& regno
< 256 )
711 if(var
->bits_per_pixel
== 16 ||
712 var
->bits_per_pixel
== 32 ||
713 var
->bits_per_pixel
== 24)
715 val
= chan_to_field(red
,&var
->red
);
716 val
|= chan_to_field(green
,&var
->green
);
717 val
|= chan_to_field(blue
,&var
->blue
);
718 par
->pseudo_palette
[regno
] = val
;
729 static int lynxfb_ops_blank(int blank
,struct fb_info
* info
)
731 struct lynxfb_par
* par
;
732 struct lynxfb_output
* output
;
734 pr_debug("blank = %d.\n",blank
);
736 output
= &par
->output
;
737 return output
->proc_setBLANK(output
,blank
);
740 static int sm750fb_set_drv(struct lynxfb_par
* par
)
743 struct lynx_share
* share
;
744 struct sm750_share
* spec_share
;
745 struct lynxfb_output
* output
;
746 struct lynxfb_crtc
* crtc
;
751 spec_share
= container_of(share
,struct sm750_share
,share
);
752 output
= &par
->output
;
755 crtc
->vidmem_size
= (share
->dual
)?share
->vidmem_size
>>1:share
->vidmem_size
;
756 /* setup crtc and output member */
757 spec_share
->hwCursor
= g_hwcursor
;
759 crtc
->proc_setMode
= hw_sm750_crtc_setMode
;
760 crtc
->proc_checkMode
= hw_sm750_crtc_checkMode
;
761 crtc
->proc_setColReg
= hw_sm750_setColReg
;
762 crtc
->proc_panDisplay
= hw_sm750_pan_display
;
763 crtc
->clear
= hw_sm750_crtc_clear
;
765 //crtc->xpanstep = crtc->ypanstep = crtc->ywrapstep = 0;
770 output
->proc_setMode
= hw_sm750_output_setMode
;
771 output
->proc_checkMode
= hw_sm750_output_checkMode
;
773 output
->proc_setBLANK
= (share
->revid
== SM750LE_REVISION_ID
)?hw_sm750le_setBLANK
:hw_sm750_setBLANK
;
774 output
->clear
= hw_sm750_output_clear
;
775 /* chip specific phase */
776 share
->accel
.de_wait
= (share
->revid
== SM750LE_REVISION_ID
)?hw_sm750le_deWait
: hw_sm750_deWait
;
777 switch (spec_share
->state
.dataflow
)
779 case sm750_simul_pri
:
780 output
->paths
= sm750_pnc
;
781 crtc
->channel
= sm750_primary
;
783 crtc
->vScreen
= share
->pvMem
;
784 pr_info("use simul primary mode\n");
786 case sm750_simul_sec
:
787 output
->paths
= sm750_pnc
;
788 crtc
->channel
= sm750_secondary
;
790 crtc
->vScreen
= share
->pvMem
;
792 case sm750_dual_normal
:
794 output
->paths
= sm750_panel
;
795 crtc
->channel
= sm750_primary
;
797 crtc
->vScreen
= share
->pvMem
;
799 output
->paths
= sm750_crt
;
800 crtc
->channel
= sm750_secondary
;
801 /* not consider of padding stuffs for oScreen,need fix*/
802 crtc
->oScreen
= (share
->vidmem_size
>> 1);
803 crtc
->vScreen
= share
->pvMem
+ crtc
->oScreen
;
806 case sm750_dual_swap
:
808 output
->paths
= sm750_panel
;
809 crtc
->channel
= sm750_secondary
;
811 crtc
->vScreen
= share
->pvMem
;
813 output
->paths
= sm750_crt
;
814 crtc
->channel
= sm750_primary
;
815 /* not consider of padding stuffs for oScreen,need fix*/
816 crtc
->oScreen
= (share
->vidmem_size
>> 1);
817 crtc
->vScreen
= share
->pvMem
+ crtc
->oScreen
;
827 static struct fb_ops lynxfb_ops
={
828 .owner
= THIS_MODULE
,
829 .fb_check_var
= lynxfb_ops_check_var
,
830 .fb_set_par
= lynxfb_ops_set_par
,
831 .fb_setcolreg
= lynxfb_ops_setcolreg
,
832 .fb_blank
= lynxfb_ops_blank
,
833 /*.fb_mmap = lynxfb_ops_mmap,*/
834 /* will be hooked by hardware */
835 .fb_fillrect
= cfb_fillrect
,
836 .fb_imageblit
= cfb_imageblit
,
837 .fb_copyarea
= cfb_copyarea
,
839 .fb_cursor
= lynxfb_ops_cursor
,
843 static int lynxfb_set_fbinfo(struct fb_info
* info
,int index
)
846 struct lynxfb_par
* par
;
847 struct lynx_share
* share
;
848 struct lynxfb_crtc
* crtc
;
849 struct lynxfb_output
* output
;
850 struct fb_var_screeninfo
* var
;
851 struct fb_fix_screeninfo
* fix
;
853 const struct fb_videomode
* pdb
[] = {
854 lynx750_ext
, NULL
,vesa_modes
,
856 int cdb
[] = {ARRAY_SIZE(lynx750_ext
),0,VESA_MODEDB_SIZE
};
857 static const char * mdb_desc
[] ={
858 "driver prepared modes",
859 "kernel prepared default modedb",
860 "kernel HELPERS prepared vesa_modes",
864 static const char * fixId
[2]=
866 "sm750_fb1","sm750_fb2",
872 par
= (struct lynxfb_par
*)info
->par
;
875 output
= &par
->output
;
881 output
->channel
= &crtc
->channel
;
882 sm750fb_set_drv(par
);
883 lynxfb_ops
.fb_pan_display
= lynxfb_ops_pan_display
;
886 /* set current cursor variable and proc pointer,
887 * must be set after crtc member initialized */
888 crtc
->cursor
.offset
= crtc
->oScreen
+ crtc
->vidmem_size
- 1024;
889 crtc
->cursor
.mmio
= share
->pvReg
+ 0x800f0 + (int)crtc
->channel
* 0x140;
891 pr_info("crtc->cursor.mmio = %p\n",crtc
->cursor
.mmio
);
892 crtc
->cursor
.maxH
= crtc
->cursor
.maxW
= 64;
893 crtc
->cursor
.size
= crtc
->cursor
.maxH
*crtc
->cursor
.maxW
*2/8;
894 crtc
->cursor
.disable
= hw_cursor_disable
;
895 crtc
->cursor
.enable
= hw_cursor_enable
;
896 crtc
->cursor
.setColor
= hw_cursor_setColor
;
897 crtc
->cursor
.setPos
= hw_cursor_setPos
;
898 crtc
->cursor
.setSize
= hw_cursor_setSize
;
899 crtc
->cursor
.setData
= hw_cursor_setData
;
900 crtc
->cursor
.vstart
= share
->pvMem
+ crtc
->cursor
.offset
;
903 crtc
->cursor
.share
= share
;
904 memset(crtc
->cursor
.vstart
, 0, crtc
->cursor
.size
);
906 lynxfb_ops
.fb_cursor
= NULL
;
907 crtc
->cursor
.disable(&crtc
->cursor
);
911 /* set info->fbops, must be set before fb_find_mode */
912 if(!share
->accel_off
){
913 /* use 2d acceleration */
914 lynxfb_ops
.fb_fillrect
= lynxfb_ops_fillrect
;
915 lynxfb_ops
.fb_copyarea
= lynxfb_ops_copyarea
;
916 lynxfb_ops
.fb_imageblit
= lynxfb_ops_imageblit
;
918 info
->fbops
= &lynxfb_ops
;
920 if(!g_fbmode
[index
]){
921 g_fbmode
[index
] = g_def_fbmode
;
923 g_fbmode
[index
] = g_fbmode
[0];
929 ret
= fb_find_mode(var
,info
,g_fbmode
[index
],
930 pdb
[i
],cdb
[i
],NULL
,8);
933 pr_info("success! use specified mode:%s in %s\n",
938 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
943 pr_warn("wanna use default mode\n");
946 pr_warn("fall back to any valid mode\n");
948 pr_warn("ret = %d,fb_find_mode failed,with %s\n",ret
,mdb_desc
[i
]);
952 /* some member of info->var had been set by fb_find_mode */
954 pr_info("Member of info->var is :\n\
961 bits_per_pixel=%d\n \
962 ...\n",var
->xres
,var
->yres
,var
->xres_virtual
,var
->yres_virtual
,
963 var
->xoffset
,var
->yoffset
,var
->bits_per_pixel
);
969 line_length
= PADDING(crtc
->line_pad
,
970 (var
->xres_virtual
* var
->bits_per_pixel
/8));
972 info
->pseudo_palette
= &par
->pseudo_palette
[0];
973 info
->screen_base
= crtc
->vScreen
;
974 pr_debug("screen_base vaddr = %p\n",info
->screen_base
);
975 info
->screen_size
= line_length
* var
->yres_virtual
;
976 info
->flags
= FBINFO_FLAG_DEFAULT
|0;
979 fix
->type
= FB_TYPE_PACKED_PIXELS
;
981 fix
->xpanstep
= crtc
->xpanstep
;
982 fix
->ypanstep
= crtc
->ypanstep
;
983 fix
->ywrapstep
= crtc
->ywrapstep
;
984 fix
->accel
= FB_ACCEL_SMI
;
986 strlcpy(fix
->id
,fixId
[index
],sizeof(fix
->id
));
989 fix
->smem_start
= crtc
->oScreen
+ share
->vidmem_start
;
990 pr_info("fix->smem_start = %lx\n",fix
->smem_start
);
991 /* according to mmap experiment from user space application,
992 * fix->mmio_len should not larger than virtual size
993 * (xres_virtual x yres_virtual x ByPP)
994 * Below line maybe buggy when user mmap fb dev node and write
995 * data into the bound over virtual size
997 fix
->smem_len
= crtc
->vidmem_size
;
998 pr_info("fix->smem_len = %x\n",fix
->smem_len
);
999 info
->screen_size
= fix
->smem_len
;
1000 fix
->line_length
= line_length
;
1001 fix
->mmio_start
= share
->vidreg_start
;
1002 pr_info("fix->mmio_start = %lx\n",fix
->mmio_start
);
1003 fix
->mmio_len
= share
->vidreg_size
;
1004 pr_info("fix->mmio_len = %x\n",fix
->mmio_len
);
1005 switch(var
->bits_per_pixel
)
1008 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
1012 fix
->visual
= FB_VISUAL_TRUECOLOR
;
1017 var
->activate
= FB_ACTIVATE_NOW
;
1018 var
->accel_flags
= 0;
1019 var
->vmode
= FB_VMODE_NONINTERLACED
;
1021 pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
1022 info
->cmap
.start
,info
->cmap
.len
,
1023 info
->cmap
.red
,info
->cmap
.green
,info
->cmap
.blue
,
1026 if((ret
= fb_alloc_cmap(&info
->cmap
,256,0)) < 0){
1027 pr_err("Could not allcate memory for cmap.\n");
1031 pr_debug("#2 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
1032 info
->cmap
.start
,info
->cmap
.len
,
1033 info
->cmap
.red
,info
->cmap
.green
,info
->cmap
.blue
,
1037 lynxfb_ops_check_var(var
,info
);
1038 // lynxfb_ops_set_par(info);
1042 /* chip specific g_option configuration routine */
1043 static void sm750fb_setup(struct lynx_share
* share
,char * src
)
1045 struct sm750_share
* spec_share
;
1047 #ifdef CAP_EXPENSION
1053 spec_share
= container_of(share
,struct sm750_share
,share
);
1054 #ifdef CAP_EXPENSIION
1059 spec_share
->state
.initParm
.chip_clk
= 0;
1060 spec_share
->state
.initParm
.mem_clk
= 0;
1061 spec_share
->state
.initParm
.master_clk
= 0;
1062 spec_share
->state
.initParm
.powerMode
= 0;
1063 spec_share
->state
.initParm
.setAllEngOff
= 0;
1064 spec_share
->state
.initParm
.resetMemory
= 1;
1066 /*defaultly turn g_hwcursor on for both view */
1070 pr_warn("no specific g_option.\n");
1074 while((opt
= strsep(&src
,":")) != NULL
&& *opt
!= NULL
){
1075 pr_err("opt=%s\n",opt
);
1076 pr_err("src=%s\n",src
);
1078 if(!strncmp(opt
,"swap",strlen("swap")))
1080 else if(!strncmp(opt
,"nocrt",strlen("nocrt")))
1081 spec_share
->state
.nocrt
= 1;
1082 else if(!strncmp(opt
,"36bit",strlen("36bit")))
1083 spec_share
->state
.pnltype
= sm750_doubleTFT
;
1084 else if(!strncmp(opt
,"18bit",strlen("18bit")))
1085 spec_share
->state
.pnltype
= sm750_dualTFT
;
1086 else if(!strncmp(opt
,"24bit",strlen("24bit")))
1087 spec_share
->state
.pnltype
= sm750_24TFT
;
1088 #ifdef CAP_EXPANSION
1089 else if(!strncmp(opt
,"exp:",strlen("exp:")))
1090 exp_res
= opt
+ strlen("exp:");
1092 else if(!strncmp(opt
,"nohwc0",strlen("nohwc0")))
1094 else if(!strncmp(opt
,"nohwc1",strlen("nohwc1")))
1096 else if(!strncmp(opt
,"nohwc",strlen("nohwc")))
1102 pr_info("find fbmode0 : %s\n",g_fbmode
[0]);
1103 }else if(!g_fbmode
[1]){
1105 pr_info("find fbmode1 : %s\n",g_fbmode
[1]);
1107 pr_warn("How many view you wann set?\n");
1111 #ifdef CAP_EXPANSION
1112 if(getExpRes(exp_res
,&spec_share
->state
.xLCD
,&spec_share
->state
.yLCD
))
1114 /* seems exp_res is not valid*/
1115 spec_share
->state
.xLCD
= spec_share
->state
.yLCD
= 0;
1120 if(share
->revid
!= SM750LE_REVISION_ID
){
1124 spec_share
->state
.dataflow
= sm750_dual_swap
;
1126 spec_share
->state
.dataflow
= sm750_dual_normal
;
1129 spec_share
->state
.dataflow
= sm750_simul_sec
;
1131 spec_share
->state
.dataflow
= sm750_simul_pri
;
1134 /* SM750LE only have one crt channel */
1135 spec_share
->state
.dataflow
= sm750_simul_sec
;
1136 /* sm750le do not have complex attributes*/
1137 spec_share
->state
.nocrt
= 0;
1141 static int lynxfb_pci_probe(struct pci_dev
* pdev
,
1142 const struct pci_device_id
* ent
)
1144 struct fb_info
* info
[] = {NULL
,NULL
};
1145 struct lynx_share
* share
= NULL
;
1147 struct sm750_share
*spec_share
= NULL
;
1148 size_t spec_offset
= 0;
1153 if(pci_enable_device(pdev
)){
1154 pr_err("can not enable device.\n");
1158 /* though offset of share in sm750_share is 0,
1159 * we use this marcro as the same */
1160 spec_offset
= offsetof(struct sm750_share
,share
);
1162 spec_share
= kzalloc(sizeof(*spec_share
),GFP_KERNEL
);
1164 pr_err("Could not allocate memory for share.\n");
1168 /* setting share structure */
1169 share
= (struct lynx_share
* )(&(spec_share
->share
));
1170 share
->fbinfo
[0] = share
->fbinfo
[1] = NULL
;
1171 share
->devid
= pdev
->device
;
1172 share
->revid
= pdev
->revision
;
1174 pr_info("share->revid = %02x\n",share
->revid
);
1177 share
->mtrr_off
= g_nomtrr
;
1178 share
->mtrr
.vram
= 0;
1179 share
->mtrr
.vram_added
= 0;
1181 share
->accel_off
= g_noaccel
;
1182 share
->dual
= g_dualview
;
1183 spin_lock_init(&share
->slock
);
1185 if(!share
->accel_off
){
1186 /* hook deInit and 2d routines, notes that below hw_xxx
1187 * routine can work on most of lynx chips
1188 * if some chip need specific function,please hook it in smXXX_set_drv
1190 share
->accel
.de_init
= hw_de_init
;
1191 share
->accel
.de_fillrect
= hw_fillrect
;
1192 share
->accel
.de_copyarea
= hw_copyarea
;
1193 share
->accel
.de_imageblit
= hw_imageblit
;
1194 pr_info("enable 2d acceleration\n");
1196 pr_info("disable 2d acceleration\n");
1199 /* call chip specific setup routine */
1200 sm750fb_setup(share
,g_settings
);
1202 /* call chip specific mmap routine */
1203 if(hw_sm750_map(share
,pdev
)){
1204 pr_err("Memory map failed\n");
1209 if(!share
->mtrr_off
){
1210 pr_info("enable mtrr\n");
1211 share
->mtrr
.vram
= mtrr_add(share
->vidmem_start
,
1213 MTRR_TYPE_WRCOMB
,1);
1215 if(share
->mtrr
.vram
< 0){
1216 /* don't block driver with the failure of MTRR */
1217 pr_err("Unable to setup MTRR.\n");
1219 share
->mtrr
.vram_added
= 1;
1220 pr_info("MTRR added succesfully\n");
1225 memset(share
->pvMem
,0,share
->vidmem_size
);
1227 pr_info("sm%3x mmio address = %p\n",share
->devid
,share
->pvReg
);
1229 pci_set_drvdata(pdev
,share
);
1231 /* call chipInit routine */
1232 hw_sm750_inithw(share
,pdev
);
1234 /* allocate frame buffer info structor according to g_dualview */
1237 info
[fbidx
] = framebuffer_alloc(sizeof(struct lynxfb_par
),&pdev
->dev
);
1240 pr_err("Could not allocate framebuffer #%d.\n",fbidx
);
1242 goto err_info0_alloc
;
1244 goto err_info1_alloc
;
1248 struct lynxfb_par
* par
;
1249 pr_info("framebuffer #%d alloc okay\n",fbidx
);
1250 share
->fbinfo
[fbidx
] = info
[fbidx
];
1251 par
= info
[fbidx
]->par
;
1254 /* set fb_info structure */
1255 if(lynxfb_set_fbinfo(info
[fbidx
],fbidx
)){
1256 pr_err("Failed to initial fb_info #%d.\n",fbidx
);
1263 /* register frame buffer*/
1264 pr_info("Ready to register framebuffer #%d.\n",fbidx
);
1265 int errno
= register_framebuffer(info
[fbidx
]);
1267 pr_err("Failed to register fb_info #%d. err %d\n",fbidx
, errno
);
1273 pr_info("Accomplished register framebuffer #%d.\n",fbidx
);
1276 /* no dual view by far */
1278 if(share
->dual
&& fbidx
< 2)
1285 framebuffer_release(info
[1]);
1287 unregister_framebuffer(info
[0]);
1290 framebuffer_release(info
[0]);
1299 static void __exit
lynxfb_pci_remove(struct pci_dev
* pdev
)
1301 struct fb_info
* info
;
1302 struct lynx_share
* share
;
1304 struct lynxfb_par
* par
;
1308 share
= pci_get_drvdata(pdev
);
1311 info
= share
->fbinfo
[cnt
];
1316 unregister_framebuffer(info
);
1317 /* clean crtc & output allocations*/
1318 par
->crtc
.clear(&par
->crtc
);
1319 par
->output
.clear(&par
->output
);
1320 /* release frame buffer*/
1321 framebuffer_release(info
);
1324 if(share
->mtrr
.vram_added
)
1325 mtrr_del(share
->mtrr
.vram
,share
->vidmem_start
,share
->vidmem_size
);
1327 // pci_release_regions(pdev);
1329 iounmap(share
->pvReg
);
1330 iounmap(share
->pvMem
);
1331 spec_share
= container_of(share
,struct sm750_share
,share
);
1334 pci_set_drvdata(pdev
,NULL
);
1337 static int __init
lynxfb_setup(char * options
)
1343 if(!options
|| !*options
){
1344 pr_warn("no options.\n");
1348 pr_info("options:%s\n",options
);
1350 len
= strlen(options
) + 1;
1351 g_settings
= kmalloc(len
,GFP_KERNEL
);
1355 memset(g_settings
,0,len
);
1359 char * strsep(char **s,const char * ct);
1360 @s: the string to be searched
1361 @ct :the characters to search for
1363 strsep() updates @options to pointer after the first found token
1364 it also returns the pointer ahead the token.
1366 while((opt
= strsep(&options
,":"))!=NULL
)
1368 /* options that mean for any lynx chips are configured here */
1369 if(!strncmp(opt
,"noaccel",strlen("noaccel")))
1372 else if(!strncmp(opt
,"nomtrr",strlen("nomtrr")))
1375 else if(!strncmp(opt
,"dual",strlen("dual")))
1388 /* misc g_settings are transport to chip specific routines */
1389 pr_info("parameter left for chip specific analysis:%s\n",g_settings
);
1393 static struct pci_device_id smi_pci_table
[] = {
1394 { PCI_DEVICE(0x126f, 0x0750), },
1398 MODULE_DEVICE_TABLE(pci
,smi_pci_table
);
1400 static struct pci_driver lynxfb_driver
= {
1402 .id_table
= smi_pci_table
,
1403 .probe
= lynxfb_pci_probe
,
1404 .remove
= lynxfb_pci_remove
,
1406 .suspend
= lynxfb_suspend
,
1407 .resume
= lynxfb_resume
,
1412 static int __init
lynxfb_init(void)
1420 if(fb_get_options("sm750fb",&option
))
1424 lynxfb_setup(option
);
1425 ret
= pci_register_driver(&lynxfb_driver
);
1428 module_init(lynxfb_init
);
1430 static void __exit
lynxfb_exit(void)
1432 pci_unregister_driver(&lynxfb_driver
);
1434 module_exit(lynxfb_exit
);
1436 module_param(g_option
,charp
,S_IRUGO
);
1438 MODULE_PARM_DESC(g_option
,
1439 "\n\t\tCommon options:\n"
1440 "\t\tnoaccel:disable 2d capabilities\n"
1441 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1442 "\t\tdualview:dual frame buffer feature enabled\n"
1443 "\t\tnohwc:disable hardware cursor\n"
1444 "\t\tUsual example:\n"
1445 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1448 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1449 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1450 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1451 MODULE_LICENSE("GPL v2");