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