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