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