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