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