patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / video / vfb.c
1 /*
2  *  linux/drivers/video/vfb.c -- Virtual frame buffer device
3  *
4  *      Copyright (C) 2002 James Simmons
5  *
6  *      Copyright (C) 1997 Geert Uytterhoeven
7  *
8  *  This file is subject to the terms and conditions of the GNU General Public
9  *  License. See the file COPYING in the main directory of this archive for
10  *  more details.
11  */
12
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
17 #include <linux/mm.h>
18 #include <linux/tty.h>
19 #include <linux/slab.h>
20 #include <linux/vmalloc.h>
21 #include <linux/delay.h>
22 #include <linux/interrupt.h>
23 #include <asm/uaccess.h>
24 #include <linux/fb.h>
25 #include <linux/init.h>
26
27     /*
28      *  RAM we reserve for the frame buffer. This defines the maximum screen
29      *  size
30      *
31      *  The default can be overridden if the driver is compiled as a module
32      */
33
34 #define VIDEOMEMSIZE    (1*1024*1024)   /* 1 MB */
35
36 static void *videomemory;
37 static u_long videomemorysize = VIDEOMEMSIZE;
38 MODULE_PARM(videomemorysize, "l");
39
40 static struct fb_var_screeninfo vfb_default __initdata = {
41         .xres =         640,
42         .yres =         480,
43         .xres_virtual = 640,
44         .yres_virtual = 480,
45         .bits_per_pixel = 8,
46         .red =          { 0, 8, 0 },
47         .green =        { 0, 8, 0 },
48         .blue =         { 0, 8, 0 },
49         .activate =     FB_ACTIVATE_TEST,
50         .height =       -1,
51         .width =        -1,
52         .pixclock =     20000,
53         .left_margin =  64,
54         .right_margin = 64,
55         .upper_margin = 32,
56         .lower_margin = 32,
57         .hsync_len =    64,
58         .vsync_len =    2,
59         .vmode =        FB_VMODE_NONINTERLACED,
60 };
61
62 static struct fb_fix_screeninfo vfb_fix __initdata = {
63         .id =           "Virtual FB",
64         .type =         FB_TYPE_PACKED_PIXELS,
65         .visual =       FB_VISUAL_PSEUDOCOLOR,
66         .xpanstep =     1,
67         .ypanstep =     1,
68         .ywrapstep =    1,
69         .accel =        FB_ACCEL_NONE,
70 };
71
72 static int vfb_enable __initdata = 0;   /* disabled by default */
73 MODULE_PARM(vfb_enable, "i");
74
75     /*
76      *  Interface used by the world
77      */
78 int vfb_init(void);
79 int vfb_setup(char *);
80
81 static int vfb_check_var(struct fb_var_screeninfo *var,
82                          struct fb_info *info);
83 static int vfb_set_par(struct fb_info *info);
84 static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
85                          u_int transp, struct fb_info *info);
86 static int vfb_pan_display(struct fb_var_screeninfo *var,
87                            struct fb_info *info);
88 static int vfb_mmap(struct fb_info *info, struct file *file,
89                     struct vm_area_struct *vma);
90
91 static struct fb_ops vfb_ops = {
92         .fb_check_var   = vfb_check_var,
93         .fb_set_par     = vfb_set_par,
94         .fb_setcolreg   = vfb_setcolreg,
95         .fb_pan_display = vfb_pan_display,
96         .fb_fillrect    = cfb_fillrect,
97         .fb_copyarea    = cfb_copyarea,
98         .fb_imageblit   = cfb_imageblit,
99         .fb_cursor      = soft_cursor,
100         .fb_mmap        = vfb_mmap,
101 };
102
103     /*
104      *  Internal routines
105      */
106
107 static u_long get_line_length(int xres_virtual, int bpp)
108 {
109         u_long length;
110
111         length = xres_virtual * bpp;
112         length = (length + 31) & ~31;
113         length >>= 3;
114         return (length);
115 }
116
117     /*
118      *  Setting the video mode has been split into two parts.
119      *  First part, xxxfb_check_var, must not write anything
120      *  to hardware, it should only verify and adjust var.
121      *  This means it doesn't alter par but it does use hardware
122      *  data from it to check this var. 
123      */
124
125 static int vfb_check_var(struct fb_var_screeninfo *var,
126                          struct fb_info *info)
127 {
128         u_long line_length;
129
130         /*
131          *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
132          *  as FB_VMODE_SMOOTH_XPAN is only used internally
133          */
134
135         if (var->vmode & FB_VMODE_CONUPDATE) {
136                 var->vmode |= FB_VMODE_YWRAP;
137                 var->xoffset = info->var.xoffset;
138                 var->yoffset = info->var.yoffset;
139         }
140
141         /*
142          *  Some very basic checks
143          */
144         if (!var->xres)
145                 var->xres = 1;
146         if (!var->yres)
147                 var->yres = 1;
148         if (var->xres > var->xres_virtual)
149                 var->xres_virtual = var->xres;
150         if (var->yres > var->yres_virtual)
151                 var->yres_virtual = var->yres;
152         if (var->bits_per_pixel <= 1)
153                 var->bits_per_pixel = 1;
154         else if (var->bits_per_pixel <= 8)
155                 var->bits_per_pixel = 8;
156         else if (var->bits_per_pixel <= 16)
157                 var->bits_per_pixel = 16;
158         else if (var->bits_per_pixel <= 24)
159                 var->bits_per_pixel = 24;
160         else if (var->bits_per_pixel <= 32)
161                 var->bits_per_pixel = 32;
162         else
163                 return -EINVAL;
164
165         if (var->xres_virtual < var->xoffset + var->xres)
166                 var->xres_virtual = var->xoffset + var->xres;
167         if (var->yres_virtual < var->yoffset + var->yres)
168                 var->yres_virtual = var->yoffset + var->yres;
169
170         /*
171          *  Memory limit
172          */
173         line_length =
174             get_line_length(var->xres_virtual, var->bits_per_pixel);
175         if (line_length * var->yres_virtual > videomemorysize)
176                 return -ENOMEM;
177
178         /*
179          * Now that we checked it we alter var. The reason being is that the video
180          * mode passed in might not work but slight changes to it might make it 
181          * work. This way we let the user know what is acceptable.
182          */
183         switch (var->bits_per_pixel) {
184         case 1:
185         case 8:
186                 var->red.offset = 0;
187                 var->red.length = 8;
188                 var->green.offset = 0;
189                 var->green.length = 8;
190                 var->blue.offset = 0;
191                 var->blue.length = 8;
192                 var->transp.offset = 0;
193                 var->transp.length = 0;
194                 break;
195         case 16:                /* RGBA 5551 */
196                 if (var->transp.length) {
197                         var->red.offset = 0;
198                         var->red.length = 5;
199                         var->green.offset = 5;
200                         var->green.length = 5;
201                         var->blue.offset = 10;
202                         var->blue.length = 5;
203                         var->transp.offset = 15;
204                         var->transp.length = 1;
205                 } else {        /* RGB 565 */
206                         var->red.offset = 0;
207                         var->red.length = 5;
208                         var->green.offset = 5;
209                         var->green.length = 6;
210                         var->blue.offset = 11;
211                         var->blue.length = 5;
212                         var->transp.offset = 0;
213                         var->transp.length = 0;
214                 }
215                 break;
216         case 24:                /* RGB 888 */
217                 var->red.offset = 0;
218                 var->red.length = 8;
219                 var->green.offset = 8;
220                 var->green.length = 8;
221                 var->blue.offset = 16;
222                 var->blue.length = 8;
223                 var->transp.offset = 0;
224                 var->transp.length = 0;
225                 break;
226         case 32:                /* RGBA 8888 */
227                 var->red.offset = 0;
228                 var->red.length = 8;
229                 var->green.offset = 8;
230                 var->green.length = 8;
231                 var->blue.offset = 16;
232                 var->blue.length = 8;
233                 var->transp.offset = 24;
234                 var->transp.length = 8;
235                 break;
236         }
237         var->red.msb_right = 0;
238         var->green.msb_right = 0;
239         var->blue.msb_right = 0;
240         var->transp.msb_right = 0;
241
242         return 0;
243 }
244
245 /* This routine actually sets the video mode. It's in here where we
246  * the hardware state info->par and fix which can be affected by the 
247  * change in par. For this driver it doesn't do much. 
248  */
249 static int vfb_set_par(struct fb_info *info)
250 {
251         info->fix.line_length = get_line_length(info->var.xres_virtual,
252                                                 info->var.bits_per_pixel);
253         return 0;
254 }
255
256     /*
257      *  Set a single color register. The values supplied are already
258      *  rounded down to the hardware's capabilities (according to the
259      *  entries in the var structure). Return != 0 for invalid regno.
260      */
261
262 static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
263                          u_int transp, struct fb_info *info)
264 {
265         if (regno >= 256)       /* no. of hw registers */
266                 return 1;
267         /*
268          * Program hardware... do anything you want with transp
269          */
270
271         /* grayscale works only partially under directcolor */
272         if (info->var.grayscale) {
273                 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
274                 red = green = blue =
275                     (red * 77 + green * 151 + blue * 28) >> 8;
276         }
277
278         /* Directcolor:
279          *   var->{color}.offset contains start of bitfield
280          *   var->{color}.length contains length of bitfield
281          *   {hardwarespecific} contains width of RAMDAC
282          *   cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
283          *   RAMDAC[X] is programmed to (red, green, blue)
284          * 
285          * Pseudocolor:
286          *    uses offset = 0 && length = RAMDAC register width.
287          *    var->{color}.offset is 0
288          *    var->{color}.length contains widht of DAC
289          *    cmap is not used
290          *    RAMDAC[X] is programmed to (red, green, blue)
291          * Truecolor:
292          *    does not use DAC. Usually 3 are present.
293          *    var->{color}.offset contains start of bitfield
294          *    var->{color}.length contains length of bitfield
295          *    cmap is programmed to (red << red.offset) | (green << green.offset) |
296          *                      (blue << blue.offset) | (transp << transp.offset)
297          *    RAMDAC does not exist
298          */
299 #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
300         switch (info->fix.visual) {
301         case FB_VISUAL_TRUECOLOR:
302         case FB_VISUAL_PSEUDOCOLOR:
303                 red = CNVT_TOHW(red, info->var.red.length);
304                 green = CNVT_TOHW(green, info->var.green.length);
305                 blue = CNVT_TOHW(blue, info->var.blue.length);
306                 transp = CNVT_TOHW(transp, info->var.transp.length);
307                 break;
308         case FB_VISUAL_DIRECTCOLOR:
309                 red = CNVT_TOHW(red, 8);        /* expect 8 bit DAC */
310                 green = CNVT_TOHW(green, 8);
311                 blue = CNVT_TOHW(blue, 8);
312                 /* hey, there is bug in transp handling... */
313                 transp = CNVT_TOHW(transp, 8);
314                 break;
315         }
316 #undef CNVT_TOHW
317         /* Truecolor has hardware independent palette */
318         if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
319                 u32 v;
320
321                 if (regno >= 16)
322                         return 1;
323
324                 v = (red << info->var.red.offset) |
325                     (green << info->var.green.offset) |
326                     (blue << info->var.blue.offset) |
327                     (transp << info->var.transp.offset);
328                 switch (info->var.bits_per_pixel) {
329                 case 8:
330                         break;
331                 case 16:
332                         ((u32 *) (info->pseudo_palette))[regno] = v;
333                         break;
334                 case 24:
335                 case 32:
336                         ((u32 *) (info->pseudo_palette))[regno] = v;
337                         break;
338                 }
339                 return 0;
340         }
341         return 0;
342 }
343
344     /*
345      *  Pan or Wrap the Display
346      *
347      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
348      */
349
350 static int vfb_pan_display(struct fb_var_screeninfo *var,
351                            struct fb_info *info)
352 {
353         if (var->vmode & FB_VMODE_YWRAP) {
354                 if (var->yoffset < 0
355                     || var->yoffset >= info->var.yres_virtual
356                     || var->xoffset)
357                         return -EINVAL;
358         } else {
359                 if (var->xoffset + var->xres > info->var.xres_virtual ||
360                     var->yoffset + var->yres > info->var.yres_virtual)
361                         return -EINVAL;
362         }
363         info->var.xoffset = var->xoffset;
364         info->var.yoffset = var->yoffset;
365         if (var->vmode & FB_VMODE_YWRAP)
366                 info->var.vmode |= FB_VMODE_YWRAP;
367         else
368                 info->var.vmode &= ~FB_VMODE_YWRAP;
369         return 0;
370 }
371
372     /*
373      *  Most drivers don't need their own mmap function 
374      */
375
376 static int vfb_mmap(struct fb_info *info, struct file *file,
377                     struct vm_area_struct *vma)
378 {
379         return -EINVAL;
380 }
381
382 int __init vfb_setup(char *options)
383 {
384         char *this_opt;
385
386         vfb_enable = 1;
387
388         if (!options || !*options)
389                 return 1;
390
391         while ((this_opt = strsep(&options, ",")) != NULL) {
392                 if (!*this_opt)
393                         continue;
394                 if (!strncmp(this_opt, "disable", 7))
395                         vfb_enable = 0;
396         }
397         return 1;
398 }
399
400     /*
401      *  Initialisation
402      */
403
404 static void vfb_platform_release(struct device *device)
405 {
406         // This is called when the reference count goes to zero.
407 }
408
409 static int __init vfb_probe(struct device *device)
410 {
411         struct platform_device *dev = to_platform_device(device);
412         struct fb_info *info;
413         int retval = -ENOMEM;
414
415         /*
416          * For real video cards we use ioremap.
417          */
418         if (!(videomemory = vmalloc(videomemorysize)))
419                 return retval;
420
421         /*
422          * VFB must clear memory to prevent kernel info
423          * leakage into userspace
424          * VGA-based drivers MUST NOT clear memory if
425          * they want to be able to take over vgacon
426          */
427         memset(videomemory, 0, videomemorysize);
428
429         info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
430         if (!info)
431                 goto err;
432
433         info->screen_base = videomemory;
434         info->fbops = &vfb_ops;
435
436         retval = fb_find_mode(&info->var, info, NULL,
437                               NULL, 0, NULL, 8);
438
439         if (!retval || (retval == 4))
440                 info->var = vfb_default;
441         info->fix = vfb_fix;
442         info->pseudo_palette = info->par;
443         info->par = NULL;
444         info->flags = FBINFO_FLAG_DEFAULT;
445
446         retval = fb_alloc_cmap(&info->cmap, 256, 0);
447         if (retval < 0)
448                 goto err1;
449
450         retval = register_framebuffer(info);
451         if (retval < 0)
452                 goto err2;
453         dev_set_drvdata(&dev->dev, info);
454
455         printk(KERN_INFO
456                "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
457                info->node, videomemorysize >> 10);
458         return 0;
459 err2:
460         fb_dealloc_cmap(&info->cmap);
461 err1:
462         framebuffer_release(info);
463 err:
464         vfree(videomemory);
465         return retval;
466 }
467
468 static int vfb_remove(struct device *device)
469 {
470         struct fb_info *info = dev_get_drvdata(device);
471
472         if (info) {
473                 unregister_framebuffer(info);
474                 vfree(videomemory);
475                 framebuffer_release(info);
476         }
477         return 0;
478 }
479
480 static struct device_driver vfb_driver = {
481         .name   = "vfb",
482         .bus    = &platform_bus_type,
483         .probe  = vfb_probe,
484         .remove = vfb_remove,
485 };
486
487 static struct platform_device vfb_device = {
488         .name   = "vfb",
489         .id     = 0,
490         .dev    = {
491                 .release = vfb_platform_release,
492         }
493 };
494
495 int __init vfb_init(void)
496 {
497         int ret = 0;
498
499         if (!vfb_enable)
500                 return -ENXIO;
501
502         ret = driver_register(&vfb_driver);
503
504         if (!ret) {
505                 ret = platform_device_register(&vfb_device);
506                 if (ret)
507                         driver_unregister(&vfb_driver);
508         }
509         return ret;
510 }
511
512 #ifdef MODULE
513 static void __exit vfb_exit(void)
514 {
515         platform_device_unregister(&vfb_device);
516         driver_unregister(&vfb_driver);
517 }
518
519 module_init(vfb_init);
520 module_exit(vfb_exit);
521
522 MODULE_LICENSE("GPL");
523 #endif                          /* MODULE */