87b50fce5dd99e8eadba71ef3dc6cab9b11b9796
[deliverable/linux.git] / drivers / staging / sm750fb / sm750.c
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 #ifdef CONFIG_MTRR
20 #include <asm/mtrr.h>
21 #endif
22 #include <asm/fb.h>
23 #include "sm750.h"
24 #include "sm750_hw.h"
25 #include "sm750_accel.h"
26 #include "sm750_cursor.h"
27
28 #include "modedb.h"
29
30 int smi_indent = 0;
31
32
33 /*
34 * #ifdef __BIG_ENDIAN
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);
39 * #endif
40 */
41
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*);
45
46
47 /* common var for all device */
48 static int g_hwcursor = 1;
49 static int g_noaccel;
50 #ifdef CONFIG_MTRR
51 static int g_nomtrr;
52 #endif
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;
57 static char * g_option = NULL;
58
59 static const struct fb_videomode lynx750_ext[] = {
60 /* 1024x600-60 VESA [1.71:1] */
61 {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
62 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
63
64 /* 1024x600-70 VESA */
65 {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
66 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
67
68 /* 1024x600-75 VESA */
69 {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
70 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
71
72 /* 1024x600-85 VESA */
73 {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
74 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
75
76 /* 720x480 */
77 {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
78 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
79
80 /* 1280x720 [1.78:1] */
81 {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
82 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
83
84 /* 1280x768@60 */
85 {NULL,60,1280,768,12579,192,64,20,3,128,7,
86 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
87
88 {NULL,60,1360,768,11804,208,64,23,1,144,3,
89 FB_SYNC_HOR_HIGH_ACT|FB_VMODE_NONINTERLACED},
90
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, FB_VMODE_NONINTERLACED},
94
95 /* 1368 x 768 [1.78:1] */
96 {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
97 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
98
99 /* 1440 x 900 [16:10] */
100 {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
101 FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
102
103 /* 1440x960 [15:10] */
104 {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
105 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
106
107 /* 1920x1080 [16:9] */
108 {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
109 FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
110 };
111
112
113
114
115 /* no hardware cursor supported under version 2.6.10, kernel bug */
116 static int lynxfb_ops_cursor(struct fb_info* info,struct fb_cursor* fbcursor)
117 {
118 struct lynxfb_par * par;
119 struct lynxfb_crtc * crtc;
120 struct lynx_cursor * cursor;
121
122 par = info->par;
123 crtc = &par->crtc;
124 cursor = &crtc->cursor;
125
126 if(fbcursor->image.width > cursor->maxW ||
127 fbcursor->image.height > cursor->maxH ||
128 fbcursor->image.depth > 1){
129 return -ENXIO;
130 }
131
132 cursor->disable(cursor);
133 if(fbcursor->set & FB_CUR_SETSIZE){
134 cursor->setSize(cursor,fbcursor->image.width,fbcursor->image.height);
135 }
136
137 if(fbcursor->set & FB_CUR_SETPOS){
138 cursor->setPos(cursor,fbcursor->image.dx - info->var.xoffset,
139 fbcursor->image.dy - info->var.yoffset);
140 }
141
142 if(fbcursor->set & FB_CUR_SETCMAP){
143 /* get the 16bit color of kernel means */
144 u16 fg,bg;
145 fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
146 ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
147 ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
148
149 bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
150 ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
151 ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
152
153 cursor->setColor(cursor,fg,bg);
154 }
155
156
157 if(fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE))
158 {
159 cursor->setData(cursor,
160 fbcursor->rop,
161 fbcursor->image.data,
162 fbcursor->mask);
163 }
164
165 if(fbcursor->enable){
166 cursor->enable(cursor);
167 }
168
169 return 0;
170 }
171
172 static void lynxfb_ops_fillrect(struct fb_info* info,const struct fb_fillrect* region)
173 {
174 struct lynxfb_par * par;
175 struct lynx_share * share;
176 unsigned int base,pitch,Bpp,rop;
177 u32 color;
178
179 if(info->state != FBINFO_STATE_RUNNING){
180 return;
181 }
182
183 par = info->par;
184 share = par->share;
185
186 /* each time 2d function begin to work,below three variable always need
187 * be set, seems we can put them together in some place */
188 base = par->crtc.oScreen;
189 pitch = info->fix.line_length;
190 Bpp = info->var.bits_per_pixel >> 3;
191
192 color = (Bpp == 1)?region->color:((u32*)info->pseudo_palette)[region->color];
193 rop = ( region->rop != ROP_COPY ) ? HW_ROP2_XOR:HW_ROP2_COPY;
194
195 /*
196 * If not use spin_lock,system will die if user load driver
197 * and immediatly unload driver frequently (dual)
198 */
199 if (share->dual)
200 spin_lock(&share->slock);
201
202 share->accel.de_fillrect(&share->accel,
203 base,pitch,Bpp,
204 region->dx,region->dy,
205 region->width,region->height,
206 color,rop);
207 if (share->dual)
208 spin_unlock(&share->slock);
209 }
210
211 static void lynxfb_ops_copyarea(struct fb_info * info,const struct fb_copyarea * region)
212 {
213 struct lynxfb_par * par;
214 struct lynx_share * share;
215 unsigned int base,pitch,Bpp;
216
217 par = info->par;
218 share = par->share;
219
220 /* each time 2d function begin to work,below three variable always need
221 * be set, seems we can put them together in some place */
222 base = par->crtc.oScreen;
223 pitch = info->fix.line_length;
224 Bpp = info->var.bits_per_pixel >> 3;
225
226 /*
227 * If not use spin_lock, system will die if user load driver
228 * and immediatly unload driver frequently (dual)
229 */
230 if (share->dual)
231 spin_lock(&share->slock);
232
233 share->accel.de_copyarea(&share->accel,
234 base,pitch,region->sx,region->sy,
235 base,pitch,Bpp,region->dx,region->dy,
236 region->width,region->height,HW_ROP2_COPY);
237 if (share->dual)
238 spin_unlock(&share->slock);
239 }
240
241 static void lynxfb_ops_imageblit(struct fb_info*info,const struct fb_image* image)
242 {
243 unsigned int base,pitch,Bpp;
244 unsigned int fgcol,bgcol;
245 struct lynxfb_par * par;
246 struct lynx_share * share;
247
248 par = info->par;
249 share = par->share;
250 /* each time 2d function begin to work,below three variable always need
251 * be set, seems we can put them together in some place */
252 base = par->crtc.oScreen;
253 pitch = info->fix.line_length;
254 Bpp = info->var.bits_per_pixel >> 3;
255
256 if(image->depth == 1){
257 if(info->fix.visual == FB_VISUAL_TRUECOLOR ||
258 info->fix.visual == FB_VISUAL_DIRECTCOLOR)
259 {
260 fgcol = ((u32*)info->pseudo_palette)[image->fg_color];
261 bgcol = ((u32*)info->pseudo_palette)[image->bg_color];
262 }
263 else
264 {
265 fgcol = image->fg_color;
266 bgcol = image->bg_color;
267 }
268 goto _do_work;
269 }
270 return;
271 _do_work:
272 /*
273 * If not use spin_lock, system will die if user load driver
274 * and immediatly unload driver frequently (dual)
275 */
276 if (share->dual)
277 spin_lock(&share->slock);
278
279 share->accel.de_imageblit(&share->accel,
280 image->data,image->width>>3,0,
281 base,pitch,Bpp,
282 image->dx,image->dy,
283 image->width,image->height,
284 fgcol,bgcol,HW_ROP2_COPY);
285 if (share->dual)
286 spin_unlock(&share->slock);
287 }
288
289 static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
290 struct fb_info *info)
291 {
292 struct lynxfb_par * par;
293 struct lynxfb_crtc * crtc;
294 int ret;
295
296
297 if(!info)
298 return -EINVAL;
299
300 ret = 0;
301 par = info->par;
302 crtc = &par->crtc;
303 ret = crtc->proc_panDisplay(crtc, var, info);
304
305 return ret;
306 }
307
308 static int lynxfb_ops_set_par(struct fb_info * info)
309 {
310 struct lynxfb_par * par;
311 struct lynx_share * share;
312 struct lynxfb_crtc * crtc;
313 struct lynxfb_output * output;
314 struct fb_var_screeninfo * var;
315 struct fb_fix_screeninfo * fix;
316 int ret;
317 unsigned int line_length;
318
319 if(!info)
320 return -EINVAL;
321
322 ret = 0;
323 par = info->par;
324 share = par->share;
325 crtc = &par->crtc;
326 output = &par->output;
327 var = &info->var;
328 fix = &info->fix;
329
330 /* fix structur is not so FIX ... */
331 line_length = var->xres_virtual * var->bits_per_pixel / 8;
332 line_length = PADDING(crtc->line_pad,line_length);
333 fix->line_length = line_length;
334 pr_err("fix->line_length = %d\n",fix->line_length);
335
336 /* var->red,green,blue,transp are need to be set by driver
337 * and these data should be set before setcolreg routine
338 * */
339
340 switch(var->bits_per_pixel){
341 case 8:
342 fix->visual = FB_VISUAL_PSEUDOCOLOR;
343 var->red.offset = 0;
344 var->red.length = 8;
345 var->green.offset = 0;
346 var->green.length = 8;
347 var->blue.offset = 0;
348 var->blue.length = 8;
349 var->transp.length = 0;
350 var->transp.offset = 0;
351 break;
352 case 16:
353 var->red.offset = 11;
354 var->red.length = 5;
355 var->green.offset = 5;
356 var->green.length = 6;
357 var->blue.offset = 0;
358 var->blue.length = 5;
359 var->transp.length = 0;
360 var->transp.offset = 0;
361 fix->visual = FB_VISUAL_TRUECOLOR;
362 break;
363 case 24:
364 case 32:
365 var->red.offset = 16;
366 var->red.length = 8;
367 var->green.offset = 8;
368 var->green.length = 8;
369 var->blue.offset = 0 ;
370 var->blue.length = 8;
371 fix->visual = FB_VISUAL_TRUECOLOR;
372 break;
373 default:
374 ret = -EINVAL;
375 break;
376 }
377 var->height = var->width = -1;
378 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
379
380 if(ret){
381 pr_err("pixel bpp format not satisfied\n.");
382 return ret;
383 }
384 ret = crtc->proc_setMode(crtc,var,fix);
385 if(!ret)
386 ret = output->proc_setMode(output,var,fix);
387 return ret;
388 }
389
390 static inline unsigned int chan_to_field(unsigned int chan,struct fb_bitfield * bf)
391 {
392 chan &= 0xffff;
393 chan >>= 16 - bf->length;
394 return chan << bf->offset;
395 }
396
397 #ifdef CONFIG_PM
398 static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
399 {
400 struct fb_info *info;
401 struct lynx_share *share;
402 int ret;
403
404 if (mesg.event == pdev->dev.power.power_state.event)
405 return 0;
406
407 ret = 0;
408 share = pci_get_drvdata(pdev);
409 switch (mesg.event) {
410 case PM_EVENT_FREEZE:
411 case PM_EVENT_PRETHAW:
412 pdev->dev.power.power_state = mesg;
413 return 0;
414 }
415
416 console_lock();
417 if (mesg.event & PM_EVENT_SLEEP) {
418 info = share->fbinfo[0];
419 if (info)
420 /* 1 means do suspend*/
421 fb_set_suspend(info, 1);
422 info = share->fbinfo[1];
423 if (info)
424 /* 1 means do suspend*/
425 fb_set_suspend(info, 1);
426
427 ret = pci_save_state(pdev);
428 if (ret) {
429 pr_err("error:%d occurred in pci_save_state\n", ret);
430 return ret;
431 }
432
433 /* set chip to sleep mode*/
434 if (share->suspend)
435 (*share->suspend)(share);
436
437 pci_disable_device(pdev);
438 ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
439 if (ret) {
440 pr_err("error:%d occurred in pci_set_power_state\n", ret);
441 return ret;
442 }
443 }
444
445 pdev->dev.power.power_state = mesg;
446 console_unlock();
447 return ret;
448 }
449
450 static int lynxfb_resume(struct pci_dev* pdev)
451 {
452 struct fb_info * info;
453 struct lynx_share * share;
454
455 struct lynxfb_par * par;
456 struct lynxfb_crtc * crtc;
457 struct lynx_cursor * cursor;
458
459 int ret;
460
461
462 ret = 0;
463 share = pci_get_drvdata(pdev);
464
465 console_lock();
466
467 if((ret = pci_set_power_state(pdev, PCI_D0)) != 0){
468 pr_err("error:%d occured in pci_set_power_state\n",ret);
469 return ret;
470 }
471
472
473 if(pdev->dev.power.power_state.event != PM_EVENT_FREEZE){
474 pci_restore_state(pdev);
475 if ((ret = pci_enable_device(pdev)) != 0){
476 pr_err("error:%d occured in pci_enable_device\n",ret);
477 return ret;
478 }
479 pci_set_master(pdev);
480 }
481 if(share->resume)
482 (*share->resume)(share);
483
484 hw_sm750_inithw(share,pdev);
485
486
487 info = share->fbinfo[0];
488
489 if(info){
490 par = info->par;
491 crtc = &par->crtc;
492 cursor = &crtc->cursor;
493 memset_io(cursor->vstart, 0x0, cursor->size);
494 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
495 lynxfb_ops_set_par(info);
496 fb_set_suspend(info, 0);
497 }
498
499 info = share->fbinfo[1];
500
501 if(info){
502 par = info->par;
503 crtc = &par->crtc;
504 cursor = &crtc->cursor;
505 memset_io(cursor->vstart, 0x0, cursor->size);
506 memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
507 lynxfb_ops_set_par(info);
508 fb_set_suspend(info, 0);
509 }
510
511
512 console_unlock();
513 return ret;
514 }
515 #endif
516
517 static int lynxfb_ops_check_var(struct fb_var_screeninfo* var,struct fb_info* info)
518 {
519 struct lynxfb_par * par;
520 struct lynxfb_crtc * crtc;
521 struct lynxfb_output * output;
522 struct lynx_share * share;
523 int ret;
524 resource_size_t request;
525
526
527 par = info->par;
528 crtc = &par->crtc;
529 output = &par->output;
530 share = par->share;
531 ret = 0;
532
533 pr_debug("check var:%dx%d-%d\n",
534 var->xres,
535 var->yres,
536 var->bits_per_pixel);
537
538
539 switch(var->bits_per_pixel){
540 case 8:
541 case 16:
542 case 24: /* support 24 bpp for only lynx712/722/720 */
543 case 32:
544 break;
545 default:
546 pr_err("bpp %d not supported\n",var->bits_per_pixel);
547 ret = -EINVAL;
548 goto exit;
549 }
550
551 switch(var->bits_per_pixel){
552 case 8:
553 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
554 var->red.offset = 0;
555 var->red.length = 8;
556 var->green.offset = 0;
557 var->green.length = 8;
558 var->blue.offset = 0;
559 var->blue.length = 8;
560 var->transp.length = 0;
561 var->transp.offset = 0;
562 break;
563 case 16:
564 var->red.offset = 11;
565 var->red.length = 5;
566 var->green.offset = 5;
567 var->green.length = 6;
568 var->blue.offset = 0;
569 var->blue.length = 5;
570 var->transp.length = 0;
571 var->transp.offset = 0;
572 info->fix.visual = FB_VISUAL_TRUECOLOR;
573 break;
574 case 24:
575 case 32:
576 var->red.offset = 16;
577 var->red.length = 8;
578 var->green.offset = 8;
579 var->green.length = 8;
580 var->blue.offset = 0 ;
581 var->blue.length = 8;
582 info->fix.visual = FB_VISUAL_TRUECOLOR;
583 break;
584 default:
585 ret = -EINVAL;
586 break;
587 }
588 var->height = var->width = -1;
589 var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
590
591 /* check if current fb's video memory big enought to hold the onscreen */
592 request = var->xres_virtual * (var->bits_per_pixel >> 3);
593 /* defaulty crtc->channel go with par->index */
594
595 request = PADDING(crtc->line_pad,request);
596 request = request * var->yres_virtual;
597 if(crtc->vidmem_size < request){
598 pr_err("not enough video memory for mode\n");
599 return -ENOMEM;
600 }
601
602 ret = output->proc_checkMode(output,var);
603 if(!ret)
604 ret = crtc->proc_checkMode(crtc,var);
605 exit:
606 return ret;
607 }
608
609
610 static int lynxfb_ops_setcolreg(unsigned regno,unsigned red,
611 unsigned green,unsigned blue,
612 unsigned transp,struct fb_info * info)
613 {
614 struct lynxfb_par * par;
615 struct lynxfb_crtc * crtc;
616 struct fb_var_screeninfo * var;
617 int ret;
618
619 par = info->par;
620 crtc = &par->crtc;
621 var = &info->var;
622 ret = 0;
623
624 //pr_debug("regno=%d,red=%d,green=%d,blue=%d\n",regno,red,green,blue);
625 if(regno > 256){
626 pr_err("regno = %d\n",regno);
627 return -EINVAL;
628 }
629
630 if(info->var.grayscale)
631 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
632
633 if(var->bits_per_pixel == 8 && info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
634 {
635 red >>= 8;
636 green >>= 8;
637 blue >>= 8;
638 ret = crtc->proc_setColReg(crtc,regno,red,green,blue);
639 goto exit;
640 }
641
642
643 if(info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256 )
644 {
645 u32 val;
646 if(var->bits_per_pixel == 16 ||
647 var->bits_per_pixel == 32 ||
648 var->bits_per_pixel == 24)
649 {
650 val = chan_to_field(red,&var->red);
651 val |= chan_to_field(green,&var->green);
652 val |= chan_to_field(blue,&var->blue);
653 par->pseudo_palette[regno] = val;
654 goto exit;
655 }
656 }
657
658 ret = -EINVAL;
659
660 exit:
661 return ret;
662 }
663
664 static int lynxfb_ops_blank(int blank,struct fb_info* info)
665 {
666 struct lynxfb_par * par;
667 struct lynxfb_output * output;
668
669 pr_debug("blank = %d.\n",blank);
670 par = info->par;
671 output = &par->output;
672 return output->proc_setBLANK(output,blank);
673 }
674
675 static int sm750fb_set_drv(struct lynxfb_par * par)
676 {
677 int ret;
678 struct lynx_share * share;
679 struct sm750_share * spec_share;
680 struct lynxfb_output * output;
681 struct lynxfb_crtc * crtc;
682
683 ret = 0;
684
685 share = par->share;
686 spec_share = container_of(share,struct sm750_share,share);
687 output = &par->output;
688 crtc = &par->crtc;
689
690 crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
691 /* setup crtc and output member */
692 spec_share->hwCursor = g_hwcursor;
693
694 crtc->proc_setMode = hw_sm750_crtc_setMode;
695 crtc->proc_checkMode = hw_sm750_crtc_checkMode;
696 crtc->proc_setColReg = hw_sm750_setColReg;
697 crtc->proc_panDisplay = hw_sm750_pan_display;
698 crtc->clear = hw_sm750_crtc_clear;
699 crtc->line_pad = 16;
700 //crtc->xpanstep = crtc->ypanstep = crtc->ywrapstep = 0;
701 crtc->xpanstep = 8;
702 crtc->ypanstep = 1;
703 crtc->ywrapstep = 0;
704
705 output->proc_setMode = hw_sm750_output_setMode;
706 output->proc_checkMode = hw_sm750_output_checkMode;
707
708 output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
709 output->clear = hw_sm750_output_clear;
710 /* chip specific phase */
711 share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait: hw_sm750_deWait;
712 switch (spec_share->state.dataflow)
713 {
714 case sm750_simul_pri:
715 output->paths = sm750_pnc;
716 crtc->channel = sm750_primary;
717 crtc->oScreen = 0;
718 crtc->vScreen = share->pvMem;
719 pr_info("use simul primary mode\n");
720 break;
721 case sm750_simul_sec:
722 output->paths = sm750_pnc;
723 crtc->channel = sm750_secondary;
724 crtc->oScreen = 0;
725 crtc->vScreen = share->pvMem;
726 break;
727 case sm750_dual_normal:
728 if(par->index == 0){
729 output->paths = sm750_panel;
730 crtc->channel = sm750_primary;
731 crtc->oScreen = 0;
732 crtc->vScreen = share->pvMem;
733 }else{
734 output->paths = sm750_crt;
735 crtc->channel = sm750_secondary;
736 /* not consider of padding stuffs for oScreen,need fix*/
737 crtc->oScreen = (share->vidmem_size >> 1);
738 crtc->vScreen = share->pvMem + crtc->oScreen;
739 }
740 break;
741 case sm750_dual_swap:
742 if(par->index == 0){
743 output->paths = sm750_panel;
744 crtc->channel = sm750_secondary;
745 crtc->oScreen = 0;
746 crtc->vScreen = share->pvMem;
747 }else{
748 output->paths = sm750_crt;
749 crtc->channel = sm750_primary;
750 /* not consider of padding stuffs for oScreen,need fix*/
751 crtc->oScreen = (share->vidmem_size >> 1);
752 crtc->vScreen = share->pvMem + crtc->oScreen;
753 }
754 break;
755 default:
756 ret = -EINVAL;
757 }
758
759 return ret;
760 }
761
762 static struct fb_ops lynxfb_ops={
763 .owner = THIS_MODULE,
764 .fb_check_var = lynxfb_ops_check_var,
765 .fb_set_par = lynxfb_ops_set_par,
766 .fb_setcolreg = lynxfb_ops_setcolreg,
767 .fb_blank = lynxfb_ops_blank,
768 .fb_fillrect = cfb_fillrect,
769 .fb_imageblit = cfb_imageblit,
770 .fb_copyarea = cfb_copyarea,
771 /* cursor */
772 .fb_cursor = lynxfb_ops_cursor,
773 };
774
775
776 static int lynxfb_set_fbinfo(struct fb_info* info,int index)
777 {
778 int i;
779 struct lynxfb_par * par;
780 struct lynx_share * share;
781 struct lynxfb_crtc * crtc;
782 struct lynxfb_output * output;
783 struct fb_var_screeninfo * var;
784 struct fb_fix_screeninfo * fix;
785
786 const struct fb_videomode * pdb[] = {
787 lynx750_ext, NULL,vesa_modes,
788 };
789 int cdb[] = {ARRAY_SIZE(lynx750_ext),0,VESA_MODEDB_SIZE};
790 static const char * mdb_desc[] ={
791 "driver prepared modes",
792 "kernel prepared default modedb",
793 "kernel HELPERS prepared vesa_modes",
794 };
795
796
797 static const char * fixId[2]=
798 {
799 "sm750_fb1","sm750_fb2",
800 };
801
802 int ret,line_length;
803
804 ret = 0;
805 par = (struct lynxfb_par *)info->par;
806 share = par->share;
807 crtc = &par->crtc;
808 output = &par->output;
809 var = &info->var;
810 fix = &info->fix;
811
812 /* set index */
813 par->index = index;
814 output->channel = &crtc->channel;
815 sm750fb_set_drv(par);
816 lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
817
818
819 /* set current cursor variable and proc pointer,
820 * must be set after crtc member initialized */
821 crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
822 crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
823
824 pr_info("crtc->cursor.mmio = %p\n",crtc->cursor.mmio);
825 crtc->cursor.maxH = crtc->cursor.maxW = 64;
826 crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
827 crtc->cursor.disable = hw_cursor_disable;
828 crtc->cursor.enable = hw_cursor_enable;
829 crtc->cursor.setColor = hw_cursor_setColor;
830 crtc->cursor.setPos = hw_cursor_setPos;
831 crtc->cursor.setSize = hw_cursor_setSize;
832 crtc->cursor.setData = hw_cursor_setData;
833 crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
834
835
836 crtc->cursor.share = share;
837 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
838 if(!g_hwcursor){
839 lynxfb_ops.fb_cursor = NULL;
840 crtc->cursor.disable(&crtc->cursor);
841 }
842
843
844 /* set info->fbops, must be set before fb_find_mode */
845 if(!share->accel_off){
846 /* use 2d acceleration */
847 lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
848 lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
849 lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
850 }
851 info->fbops = &lynxfb_ops;
852
853 if(!g_fbmode[index]){
854 g_fbmode[index] = g_def_fbmode;
855 if(index)
856 g_fbmode[index] = g_fbmode[0];
857 }
858
859
860 for(i=0;i<3;i++){
861
862 ret = fb_find_mode(var,info,g_fbmode[index],
863 pdb[i],cdb[i],NULL,8);
864
865 if(ret == 1){
866 pr_info("success! use specified mode:%s in %s\n",
867 g_fbmode[index],
868 mdb_desc[i]);
869 break;
870 }else if(ret == 2){
871 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
872 g_fbmode[index],
873 mdb_desc[i]);
874 break;
875 }else if(ret == 3){
876 pr_warn("wanna use default mode\n");
877 // break;
878 }else if(ret == 4){
879 pr_warn("fall back to any valid mode\n");
880 }else{
881 pr_warn("ret = %d,fb_find_mode failed,with %s\n",ret,mdb_desc[i]);
882 }
883 }
884
885 /* some member of info->var had been set by fb_find_mode */
886
887 pr_info("Member of info->var is :\n\
888 xres=%d\n\
889 yres=%d\n\
890 xres_virtual=%d\n\
891 yres_virtual=%d\n\
892 xoffset=%d\n\
893 yoffset=%d\n\
894 bits_per_pixel=%d\n \
895 ...\n",var->xres,var->yres,var->xres_virtual,var->yres_virtual,
896 var->xoffset,var->yoffset,var->bits_per_pixel);
897
898 /* set par */
899 par->info = info;
900
901 /* set info */
902 line_length = PADDING(crtc->line_pad,
903 (var->xres_virtual * var->bits_per_pixel/8));
904
905 info->pseudo_palette = &par->pseudo_palette[0];
906 info->screen_base = crtc->vScreen;
907 pr_debug("screen_base vaddr = %p\n",info->screen_base);
908 info->screen_size = line_length * var->yres_virtual;
909 info->flags = FBINFO_FLAG_DEFAULT|0;
910
911 /* set info->fix */
912 fix->type = FB_TYPE_PACKED_PIXELS;
913 fix->type_aux = 0;
914 fix->xpanstep = crtc->xpanstep;
915 fix->ypanstep = crtc->ypanstep;
916 fix->ywrapstep = crtc->ywrapstep;
917 fix->accel = FB_ACCEL_SMI;
918
919 strlcpy(fix->id,fixId[index],sizeof(fix->id));
920
921
922 fix->smem_start = crtc->oScreen + share->vidmem_start;
923 pr_info("fix->smem_start = %lx\n",fix->smem_start);
924 /* according to mmap experiment from user space application,
925 * fix->mmio_len should not larger than virtual size
926 * (xres_virtual x yres_virtual x ByPP)
927 * Below line maybe buggy when user mmap fb dev node and write
928 * data into the bound over virtual size
929 * */
930 fix->smem_len = crtc->vidmem_size;
931 pr_info("fix->smem_len = %x\n",fix->smem_len);
932 info->screen_size = fix->smem_len;
933 fix->line_length = line_length;
934 fix->mmio_start = share->vidreg_start;
935 pr_info("fix->mmio_start = %lx\n",fix->mmio_start);
936 fix->mmio_len = share->vidreg_size;
937 pr_info("fix->mmio_len = %x\n",fix->mmio_len);
938 switch(var->bits_per_pixel)
939 {
940 case 8:
941 fix->visual = FB_VISUAL_PSEUDOCOLOR;
942 break;
943 case 16:
944 case 32:
945 fix->visual = FB_VISUAL_TRUECOLOR;
946 break;
947 }
948
949 /* set var */
950 var->activate = FB_ACTIVATE_NOW;
951 var->accel_flags = 0;
952 var->vmode = FB_VMODE_NONINTERLACED;
953
954 pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
955 info->cmap.start,info->cmap.len,
956 info->cmap.red,info->cmap.green,info->cmap.blue,
957 info->cmap.transp);
958
959 if((ret = fb_alloc_cmap(&info->cmap,256,0)) < 0){
960 pr_err("Could not allcate memory for cmap.\n");
961 goto exit;
962 }
963
964 pr_debug("#2 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
965 info->cmap.start,info->cmap.len,
966 info->cmap.red,info->cmap.green,info->cmap.blue,
967 info->cmap.transp);
968
969 exit:
970 lynxfb_ops_check_var(var,info);
971 // lynxfb_ops_set_par(info);
972 return ret;
973 }
974
975 /* chip specific g_option configuration routine */
976 static void sm750fb_setup(struct lynx_share * share,char * src)
977 {
978 struct sm750_share * spec_share;
979 char * opt;
980 #ifdef CAP_EXPENSION
981 char * exp_res;
982 #endif
983 int swap;
984
985
986 spec_share = container_of(share,struct sm750_share,share);
987 #ifdef CAP_EXPENSIION
988 exp_res = NULL;
989 #endif
990 swap = 0;
991
992 spec_share->state.initParm.chip_clk = 0;
993 spec_share->state.initParm.mem_clk = 0;
994 spec_share->state.initParm.master_clk = 0;
995 spec_share->state.initParm.powerMode = 0;
996 spec_share->state.initParm.setAllEngOff = 0;
997 spec_share->state.initParm.resetMemory = 1;
998
999 /*defaultly turn g_hwcursor on for both view */
1000 g_hwcursor = 3;
1001
1002 if(!src || !*src){
1003 pr_warn("no specific g_option.\n");
1004 goto NO_PARAM;
1005 }
1006
1007 while((opt = strsep(&src,":")) != NULL && *opt != 0){
1008 pr_err("opt=%s\n",opt);
1009 pr_err("src=%s\n",src);
1010
1011 if(!strncmp(opt,"swap",strlen("swap")))
1012 swap = 1;
1013 else if(!strncmp(opt,"nocrt",strlen("nocrt")))
1014 spec_share->state.nocrt = 1;
1015 else if(!strncmp(opt,"36bit",strlen("36bit")))
1016 spec_share->state.pnltype = sm750_doubleTFT;
1017 else if(!strncmp(opt,"18bit",strlen("18bit")))
1018 spec_share->state.pnltype = sm750_dualTFT;
1019 else if(!strncmp(opt,"24bit",strlen("24bit")))
1020 spec_share->state.pnltype = sm750_24TFT;
1021 #ifdef CAP_EXPANSION
1022 else if(!strncmp(opt,"exp:",strlen("exp:")))
1023 exp_res = opt + strlen("exp:");
1024 #endif
1025 else if(!strncmp(opt,"nohwc0",strlen("nohwc0")))
1026 g_hwcursor &= ~0x1;
1027 else if(!strncmp(opt,"nohwc1",strlen("nohwc1")))
1028 g_hwcursor &= ~0x2;
1029 else if(!strncmp(opt,"nohwc",strlen("nohwc")))
1030 g_hwcursor = 0;
1031 else
1032 {
1033 if(!g_fbmode[0]){
1034 g_fbmode[0] = opt;
1035 pr_info("find fbmode0 : %s\n",g_fbmode[0]);
1036 }else if(!g_fbmode[1]){
1037 g_fbmode[1] = opt;
1038 pr_info("find fbmode1 : %s\n",g_fbmode[1]);
1039 }else{
1040 pr_warn("How many view you wann set?\n");
1041 }
1042 }
1043 }
1044 #ifdef CAP_EXPANSION
1045 if(getExpRes(exp_res,&spec_share->state.xLCD,&spec_share->state.yLCD))
1046 {
1047 /* seems exp_res is not valid*/
1048 spec_share->state.xLCD = spec_share->state.yLCD = 0;
1049 }
1050 #endif
1051
1052 NO_PARAM:
1053 if(share->revid != SM750LE_REVISION_ID){
1054 if(share->dual)
1055 {
1056 if(swap)
1057 spec_share->state.dataflow = sm750_dual_swap;
1058 else
1059 spec_share->state.dataflow = sm750_dual_normal;
1060 }else{
1061 if(swap)
1062 spec_share->state.dataflow = sm750_simul_sec;
1063 else
1064 spec_share->state.dataflow = sm750_simul_pri;
1065 }
1066 }else{
1067 /* SM750LE only have one crt channel */
1068 spec_share->state.dataflow = sm750_simul_sec;
1069 /* sm750le do not have complex attributes*/
1070 spec_share->state.nocrt = 0;
1071 }
1072 }
1073
1074 static int lynxfb_pci_probe(struct pci_dev * pdev,
1075 const struct pci_device_id * ent)
1076 {
1077 struct fb_info * info[] = {NULL,NULL};
1078 struct lynx_share * share = NULL;
1079
1080 struct sm750_share *spec_share = NULL;
1081 size_t spec_offset = 0;
1082 int fbidx;
1083
1084
1085 /* enable device */
1086 if(pci_enable_device(pdev)){
1087 pr_err("can not enable device.\n");
1088 goto err_enable;
1089 }
1090
1091 /* though offset of share in sm750_share is 0,
1092 * we use this marcro as the same */
1093 spec_offset = offsetof(struct sm750_share,share);
1094
1095 spec_share = kzalloc(sizeof(*spec_share),GFP_KERNEL);
1096 if(!spec_share){
1097 pr_err("Could not allocate memory for share.\n");
1098 goto err_share;
1099 }
1100
1101 /* setting share structure */
1102 share = (struct lynx_share * )(&(spec_share->share));
1103 share->fbinfo[0] = share->fbinfo[1] = NULL;
1104 share->devid = pdev->device;
1105 share->revid = pdev->revision;
1106
1107 pr_info("share->revid = %02x\n",share->revid);
1108 share->pdev = pdev;
1109 #ifdef CONFIG_MTRR
1110 share->mtrr_off = g_nomtrr;
1111 share->mtrr.vram = 0;
1112 share->mtrr.vram_added = 0;
1113 #endif
1114 share->accel_off = g_noaccel;
1115 share->dual = g_dualview;
1116 spin_lock_init(&share->slock);
1117
1118 if(!share->accel_off){
1119 /* hook deInit and 2d routines, notes that below hw_xxx
1120 * routine can work on most of lynx chips
1121 * if some chip need specific function,please hook it in smXXX_set_drv
1122 * routine */
1123 share->accel.de_init = hw_de_init;
1124 share->accel.de_fillrect = hw_fillrect;
1125 share->accel.de_copyarea = hw_copyarea;
1126 share->accel.de_imageblit = hw_imageblit;
1127 pr_info("enable 2d acceleration\n");
1128 }else{
1129 pr_info("disable 2d acceleration\n");
1130 }
1131
1132 /* call chip specific setup routine */
1133 sm750fb_setup(share,g_settings);
1134
1135 /* call chip specific mmap routine */
1136 if(hw_sm750_map(share,pdev)){
1137 pr_err("Memory map failed\n");
1138 goto err_map;
1139 }
1140
1141 #ifdef CONFIG_MTRR
1142 if(!share->mtrr_off){
1143 pr_info("enable mtrr\n");
1144 share->mtrr.vram = mtrr_add(share->vidmem_start,
1145 share->vidmem_size,
1146 MTRR_TYPE_WRCOMB,1);
1147
1148 if(share->mtrr.vram < 0){
1149 /* don't block driver with the failure of MTRR */
1150 pr_err("Unable to setup MTRR.\n");
1151 }else{
1152 share->mtrr.vram_added = 1;
1153 pr_info("MTRR added succesfully\n");
1154 }
1155 }
1156 #endif
1157
1158 memset_io(share->pvMem, 0, share->vidmem_size);
1159
1160 pr_info("sm%3x mmio address = %p\n",share->devid,share->pvReg);
1161
1162 pci_set_drvdata(pdev,share);
1163
1164 /* call chipInit routine */
1165 hw_sm750_inithw(share,pdev);
1166
1167 /* allocate frame buffer info structor according to g_dualview */
1168 fbidx = 0;
1169 ALLOC_FB:
1170 info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par),&pdev->dev);
1171 if(!info[fbidx])
1172 {
1173 pr_err("Could not allocate framebuffer #%d.\n",fbidx);
1174 if(fbidx == 0)
1175 goto err_info0_alloc;
1176 else
1177 goto err_info1_alloc;
1178 }
1179 else
1180 {
1181 struct lynxfb_par * par;
1182 int errno;
1183 pr_info("framebuffer #%d alloc okay\n",fbidx);
1184 share->fbinfo[fbidx] = info[fbidx];
1185 par = info[fbidx]->par;
1186 par->share = share;
1187
1188 /* set fb_info structure */
1189 if(lynxfb_set_fbinfo(info[fbidx],fbidx)){
1190 pr_err("Failed to initial fb_info #%d.\n",fbidx);
1191 if(fbidx == 0)
1192 goto err_info0_set;
1193 else
1194 goto err_info1_set;
1195 }
1196
1197 /* register frame buffer*/
1198 pr_info("Ready to register framebuffer #%d.\n",fbidx);
1199 errno = register_framebuffer(info[fbidx]);
1200 if (errno < 0) {
1201 pr_err("Failed to register fb_info #%d. err %d\n",fbidx, errno);
1202 if(fbidx == 0)
1203 goto err_register0;
1204 else
1205 goto err_register1;
1206 }
1207 pr_info("Accomplished register framebuffer #%d.\n",fbidx);
1208 }
1209
1210 /* no dual view by far */
1211 fbidx++;
1212 if(share->dual && fbidx < 2)
1213 goto ALLOC_FB;
1214
1215 return 0;
1216
1217 err_register1:
1218 err_info1_set:
1219 framebuffer_release(info[1]);
1220 err_info1_alloc:
1221 unregister_framebuffer(info[0]);
1222 err_register0:
1223 err_info0_set:
1224 framebuffer_release(info[0]);
1225 err_info0_alloc:
1226 err_map:
1227 kfree(spec_share);
1228 err_share:
1229 err_enable:
1230 return -ENODEV;
1231 }
1232
1233 static void __exit lynxfb_pci_remove(struct pci_dev * pdev)
1234 {
1235 struct fb_info * info;
1236 struct lynx_share * share;
1237 void * spec_share;
1238 struct lynxfb_par * par;
1239 int cnt;
1240
1241 cnt = 2;
1242 share = pci_get_drvdata(pdev);
1243
1244 while(cnt-- > 0){
1245 info = share->fbinfo[cnt];
1246 if(!info)
1247 continue;
1248 par = info->par;
1249
1250 unregister_framebuffer(info);
1251 /* clean crtc & output allocations*/
1252 par->crtc.clear(&par->crtc);
1253 par->output.clear(&par->output);
1254 /* release frame buffer*/
1255 framebuffer_release(info);
1256 }
1257 #ifdef CONFIG_MTRR
1258 if(share->mtrr.vram_added)
1259 mtrr_del(share->mtrr.vram,share->vidmem_start,share->vidmem_size);
1260 #endif
1261 // pci_release_regions(pdev);
1262
1263 iounmap(share->pvReg);
1264 iounmap(share->pvMem);
1265 spec_share = container_of(share,struct sm750_share,share);
1266 kfree(g_settings);
1267 kfree(spec_share);
1268 pci_set_drvdata(pdev,NULL);
1269 }
1270
1271 static int __init lynxfb_setup(char * options)
1272 {
1273 int len;
1274 char * opt,*tmp;
1275
1276
1277 if(!options || !*options){
1278 pr_warn("no options.\n");
1279 return 0;
1280 }
1281
1282 pr_info("options:%s\n",options);
1283
1284 len = strlen(options) + 1;
1285 g_settings = kzalloc(len, GFP_KERNEL);
1286 if(!g_settings)
1287 return -ENOMEM;
1288
1289 tmp = g_settings;
1290
1291 /* Notes:
1292 char * strsep(char **s,const char * ct);
1293 @s: the string to be searched
1294 @ct :the characters to search for
1295
1296 strsep() updates @options to pointer after the first found token
1297 it also returns the pointer ahead the token.
1298 */
1299 while((opt = strsep(&options,":"))!=NULL)
1300 {
1301 /* options that mean for any lynx chips are configured here */
1302 if(!strncmp(opt,"noaccel",strlen("noaccel")))
1303 g_noaccel = 1;
1304 #ifdef CONFIG_MTRR
1305 else if(!strncmp(opt,"nomtrr",strlen("nomtrr")))
1306 g_nomtrr = 1;
1307 #endif
1308 else if(!strncmp(opt,"dual",strlen("dual")))
1309 g_dualview = 1;
1310 else
1311 {
1312 strcat(tmp,opt);
1313 tmp += strlen(opt);
1314 if(options != NULL)
1315 *tmp++ = ':';
1316 else
1317 *tmp++ = 0;
1318 }
1319 }
1320
1321 /* misc g_settings are transport to chip specific routines */
1322 pr_info("parameter left for chip specific analysis:%s\n",g_settings);
1323 return 0;
1324 }
1325
1326 static struct pci_device_id smi_pci_table[] = {
1327 { PCI_DEVICE(0x126f, 0x0750), },
1328 {0,}
1329 };
1330
1331 MODULE_DEVICE_TABLE(pci,smi_pci_table);
1332
1333 static struct pci_driver lynxfb_driver = {
1334 .name = "sm750fb",
1335 .id_table = smi_pci_table,
1336 .probe = lynxfb_pci_probe,
1337 .remove = lynxfb_pci_remove,
1338 #ifdef CONFIG_PM
1339 .suspend = lynxfb_suspend,
1340 .resume = lynxfb_resume,
1341 #endif
1342 };
1343
1344
1345 static int __init lynxfb_init(void)
1346 {
1347 char *option ;
1348 int ret;
1349
1350 #ifdef MODULE
1351 option = g_option;
1352 #else
1353 if(fb_get_options("sm750fb",&option))
1354 return -ENODEV;
1355 #endif
1356
1357 lynxfb_setup(option);
1358 ret = pci_register_driver(&lynxfb_driver);
1359 return ret;
1360 }
1361 module_init(lynxfb_init);
1362
1363 static void __exit lynxfb_exit(void)
1364 {
1365 pci_unregister_driver(&lynxfb_driver);
1366 }
1367 module_exit(lynxfb_exit);
1368
1369 module_param(g_option,charp,S_IRUGO);
1370
1371 MODULE_PARM_DESC(g_option,
1372 "\n\t\tCommon options:\n"
1373 "\t\tnoaccel:disable 2d capabilities\n"
1374 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1375 "\t\tdualview:dual frame buffer feature enabled\n"
1376 "\t\tnohwc:disable hardware cursor\n"
1377 "\t\tUsual example:\n"
1378 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1379 );
1380
1381 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1382 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1383 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1384 MODULE_LICENSE("GPL v2");
This page took 0.066502 seconds and 4 git commands to generate.