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