2 * linux/drivers/video/vfb.c -- Virtual frame buffer device
4 * Copyright (C) 2002 James Simmons
6 * Copyright (C) 1997 Geert Uytterhoeven
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
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
18 #include <linux/slab.h>
19 #include <linux/vmalloc.h>
20 #include <linux/delay.h>
21 #include <linux/interrupt.h>
22 #include <linux/platform_device.h>
25 #include <linux/init.h>
28 * RAM we reserve for the frame buffer. This defines the maximum screen
31 * The default can be overridden if the driver is compiled as a module
34 #define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */
36 static void *videomemory
;
37 static u_long videomemorysize
= VIDEOMEMSIZE
;
38 module_param(videomemorysize
, ulong
, 0);
40 /**********************************************************************
44 **********************************************************************/
45 static void *rvmalloc(unsigned long size
)
50 size
= PAGE_ALIGN(size
);
51 mem
= vmalloc_32(size
);
55 memset(mem
, 0, size
); /* Clear the ram out, no junk to the user */
56 adr
= (unsigned long) mem
;
58 SetPageReserved(vmalloc_to_page((void *)adr
));
66 static void rvfree(void *mem
, unsigned long size
)
73 adr
= (unsigned long) mem
;
74 while ((long) size
> 0) {
75 ClearPageReserved(vmalloc_to_page((void *)adr
));
82 static struct fb_var_screeninfo vfb_default __initdata
= {
91 .activate
= FB_ACTIVATE_TEST
,
101 .vmode
= FB_VMODE_NONINTERLACED
,
104 static struct fb_fix_screeninfo vfb_fix __initdata
= {
106 .type
= FB_TYPE_PACKED_PIXELS
,
107 .visual
= FB_VISUAL_PSEUDOCOLOR
,
111 .accel
= FB_ACCEL_NONE
,
114 static int vfb_enable __initdata
= 0; /* disabled by default */
115 module_param(vfb_enable
, bool, 0);
117 static int vfb_check_var(struct fb_var_screeninfo
*var
,
118 struct fb_info
*info
);
119 static int vfb_set_par(struct fb_info
*info
);
120 static int vfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
121 u_int transp
, struct fb_info
*info
);
122 static int vfb_pan_display(struct fb_var_screeninfo
*var
,
123 struct fb_info
*info
);
124 static int vfb_mmap(struct fb_info
*info
,
125 struct vm_area_struct
*vma
);
127 static struct fb_ops vfb_ops
= {
128 .fb_read
= fb_sys_read
,
129 .fb_write
= fb_sys_write
,
130 .fb_check_var
= vfb_check_var
,
131 .fb_set_par
= vfb_set_par
,
132 .fb_setcolreg
= vfb_setcolreg
,
133 .fb_pan_display
= vfb_pan_display
,
134 .fb_fillrect
= sys_fillrect
,
135 .fb_copyarea
= sys_copyarea
,
136 .fb_imageblit
= sys_imageblit
,
144 static u_long
get_line_length(int xres_virtual
, int bpp
)
148 length
= xres_virtual
* bpp
;
149 length
= (length
+ 31) & ~31;
155 * Setting the video mode has been split into two parts.
156 * First part, xxxfb_check_var, must not write anything
157 * to hardware, it should only verify and adjust var.
158 * This means it doesn't alter par but it does use hardware
159 * data from it to check this var.
162 static int vfb_check_var(struct fb_var_screeninfo
*var
,
163 struct fb_info
*info
)
168 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
169 * as FB_VMODE_SMOOTH_XPAN is only used internally
172 if (var
->vmode
& FB_VMODE_CONUPDATE
) {
173 var
->vmode
|= FB_VMODE_YWRAP
;
174 var
->xoffset
= info
->var
.xoffset
;
175 var
->yoffset
= info
->var
.yoffset
;
179 * Some very basic checks
185 if (var
->xres
> var
->xres_virtual
)
186 var
->xres_virtual
= var
->xres
;
187 if (var
->yres
> var
->yres_virtual
)
188 var
->yres_virtual
= var
->yres
;
189 if (var
->bits_per_pixel
<= 1)
190 var
->bits_per_pixel
= 1;
191 else if (var
->bits_per_pixel
<= 8)
192 var
->bits_per_pixel
= 8;
193 else if (var
->bits_per_pixel
<= 16)
194 var
->bits_per_pixel
= 16;
195 else if (var
->bits_per_pixel
<= 24)
196 var
->bits_per_pixel
= 24;
197 else if (var
->bits_per_pixel
<= 32)
198 var
->bits_per_pixel
= 32;
202 if (var
->xres_virtual
< var
->xoffset
+ var
->xres
)
203 var
->xres_virtual
= var
->xoffset
+ var
->xres
;
204 if (var
->yres_virtual
< var
->yoffset
+ var
->yres
)
205 var
->yres_virtual
= var
->yoffset
+ var
->yres
;
211 get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
212 if (line_length
* var
->yres_virtual
> videomemorysize
)
216 * Now that we checked it we alter var. The reason being is that the video
217 * mode passed in might not work but slight changes to it might make it
218 * work. This way we let the user know what is acceptable.
220 switch (var
->bits_per_pixel
) {
225 var
->green
.offset
= 0;
226 var
->green
.length
= 8;
227 var
->blue
.offset
= 0;
228 var
->blue
.length
= 8;
229 var
->transp
.offset
= 0;
230 var
->transp
.length
= 0;
232 case 16: /* RGBA 5551 */
233 if (var
->transp
.length
) {
236 var
->green
.offset
= 5;
237 var
->green
.length
= 5;
238 var
->blue
.offset
= 10;
239 var
->blue
.length
= 5;
240 var
->transp
.offset
= 15;
241 var
->transp
.length
= 1;
242 } else { /* RGB 565 */
245 var
->green
.offset
= 5;
246 var
->green
.length
= 6;
247 var
->blue
.offset
= 11;
248 var
->blue
.length
= 5;
249 var
->transp
.offset
= 0;
250 var
->transp
.length
= 0;
253 case 24: /* RGB 888 */
256 var
->green
.offset
= 8;
257 var
->green
.length
= 8;
258 var
->blue
.offset
= 16;
259 var
->blue
.length
= 8;
260 var
->transp
.offset
= 0;
261 var
->transp
.length
= 0;
263 case 32: /* RGBA 8888 */
266 var
->green
.offset
= 8;
267 var
->green
.length
= 8;
268 var
->blue
.offset
= 16;
269 var
->blue
.length
= 8;
270 var
->transp
.offset
= 24;
271 var
->transp
.length
= 8;
274 var
->red
.msb_right
= 0;
275 var
->green
.msb_right
= 0;
276 var
->blue
.msb_right
= 0;
277 var
->transp
.msb_right
= 0;
282 /* This routine actually sets the video mode. It's in here where we
283 * the hardware state info->par and fix which can be affected by the
284 * change in par. For this driver it doesn't do much.
286 static int vfb_set_par(struct fb_info
*info
)
288 info
->fix
.line_length
= get_line_length(info
->var
.xres_virtual
,
289 info
->var
.bits_per_pixel
);
294 * Set a single color register. The values supplied are already
295 * rounded down to the hardware's capabilities (according to the
296 * entries in the var structure). Return != 0 for invalid regno.
299 static int vfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
300 u_int transp
, struct fb_info
*info
)
302 if (regno
>= 256) /* no. of hw registers */
305 * Program hardware... do anything you want with transp
308 /* grayscale works only partially under directcolor */
309 if (info
->var
.grayscale
) {
310 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
312 (red
* 77 + green
* 151 + blue
* 28) >> 8;
316 * var->{color}.offset contains start of bitfield
317 * var->{color}.length contains length of bitfield
318 * {hardwarespecific} contains width of RAMDAC
319 * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
320 * RAMDAC[X] is programmed to (red, green, blue)
323 * uses offset = 0 && length = RAMDAC register width.
324 * var->{color}.offset is 0
325 * var->{color}.length contains widht of DAC
327 * RAMDAC[X] is programmed to (red, green, blue)
329 * does not use DAC. Usually 3 are present.
330 * var->{color}.offset contains start of bitfield
331 * var->{color}.length contains length of bitfield
332 * cmap is programmed to (red << red.offset) | (green << green.offset) |
333 * (blue << blue.offset) | (transp << transp.offset)
334 * RAMDAC does not exist
336 #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
337 switch (info
->fix
.visual
) {
338 case FB_VISUAL_TRUECOLOR
:
339 case FB_VISUAL_PSEUDOCOLOR
:
340 red
= CNVT_TOHW(red
, info
->var
.red
.length
);
341 green
= CNVT_TOHW(green
, info
->var
.green
.length
);
342 blue
= CNVT_TOHW(blue
, info
->var
.blue
.length
);
343 transp
= CNVT_TOHW(transp
, info
->var
.transp
.length
);
345 case FB_VISUAL_DIRECTCOLOR
:
346 red
= CNVT_TOHW(red
, 8); /* expect 8 bit DAC */
347 green
= CNVT_TOHW(green
, 8);
348 blue
= CNVT_TOHW(blue
, 8);
349 /* hey, there is bug in transp handling... */
350 transp
= CNVT_TOHW(transp
, 8);
354 /* Truecolor has hardware independent palette */
355 if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
361 v
= (red
<< info
->var
.red
.offset
) |
362 (green
<< info
->var
.green
.offset
) |
363 (blue
<< info
->var
.blue
.offset
) |
364 (transp
<< info
->var
.transp
.offset
);
365 switch (info
->var
.bits_per_pixel
) {
369 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
373 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
382 * Pan or Wrap the Display
384 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
387 static int vfb_pan_display(struct fb_var_screeninfo
*var
,
388 struct fb_info
*info
)
390 if (var
->vmode
& FB_VMODE_YWRAP
) {
392 || var
->yoffset
>= info
->var
.yres_virtual
396 if (var
->xoffset
+ var
->xres
> info
->var
.xres_virtual
||
397 var
->yoffset
+ var
->yres
> info
->var
.yres_virtual
)
400 info
->var
.xoffset
= var
->xoffset
;
401 info
->var
.yoffset
= var
->yoffset
;
402 if (var
->vmode
& FB_VMODE_YWRAP
)
403 info
->var
.vmode
|= FB_VMODE_YWRAP
;
405 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
410 * Most drivers don't need their own mmap function
413 static int vfb_mmap(struct fb_info
*info
,
414 struct vm_area_struct
*vma
)
416 unsigned long start
= vma
->vm_start
;
417 unsigned long size
= vma
->vm_end
- vma
->vm_start
;
418 unsigned long offset
= vma
->vm_pgoff
<< PAGE_SHIFT
;
419 unsigned long page
, pos
;
421 if (offset
+ size
> info
->fix
.smem_len
) {
425 pos
= (unsigned long)info
->fix
.smem_start
+ offset
;
428 page
= vmalloc_to_pfn((void *)pos
);
429 if (remap_pfn_range(vma
, start
, page
, PAGE_SIZE
, PAGE_SHARED
)) {
434 if (size
> PAGE_SIZE
)
440 vma
->vm_flags
|= VM_RESERVED
; /* avoid to swap out this VMA */
447 * The virtual framebuffer driver is only enabled if explicitly
448 * requested by passing 'video=vfb:' (or any actual options).
450 static int __init
vfb_setup(char *options
)
464 while ((this_opt
= strsep(&options
, ",")) != NULL
) {
467 /* Test disable for backwards compatibility */
468 if (!strcmp(this_opt
, "disable"))
479 static int __init
vfb_probe(struct platform_device
*dev
)
481 struct fb_info
*info
;
482 int retval
= -ENOMEM
;
485 * For real video cards we use ioremap.
487 if (!(videomemory
= rvmalloc(videomemorysize
)))
491 * VFB must clear memory to prevent kernel info
492 * leakage into userspace
493 * VGA-based drivers MUST NOT clear memory if
494 * they want to be able to take over vgacon
496 memset(videomemory
, 0, videomemorysize
);
498 info
= framebuffer_alloc(sizeof(u32
) * 256, &dev
->dev
);
502 info
->screen_base
= (char __iomem
*)videomemory
;
503 info
->fbops
= &vfb_ops
;
505 retval
= fb_find_mode(&info
->var
, info
, NULL
,
508 if (!retval
|| (retval
== 4))
509 info
->var
= vfb_default
;
510 vfb_fix
.smem_start
= (unsigned long) videomemory
;
511 vfb_fix
.smem_len
= videomemorysize
;
513 info
->pseudo_palette
= info
->par
;
515 info
->flags
= FBINFO_FLAG_DEFAULT
;
517 retval
= fb_alloc_cmap(&info
->cmap
, 256, 0);
521 retval
= register_framebuffer(info
);
524 platform_set_drvdata(dev
, info
);
527 "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
528 info
->node
, videomemorysize
>> 10);
531 fb_dealloc_cmap(&info
->cmap
);
533 framebuffer_release(info
);
535 rvfree(videomemory
, videomemorysize
);
539 static int vfb_remove(struct platform_device
*dev
)
541 struct fb_info
*info
= platform_get_drvdata(dev
);
544 unregister_framebuffer(info
);
545 rvfree(videomemory
, videomemorysize
);
546 fb_dealloc_cmap(&info
->cmap
);
547 framebuffer_release(info
);
552 static struct platform_driver vfb_driver
= {
554 .remove
= vfb_remove
,
560 static struct platform_device
*vfb_device
;
562 static int __init
vfb_init(void)
569 if (fb_get_options("vfb", &option
))
577 ret
= platform_driver_register(&vfb_driver
);
580 vfb_device
= platform_device_alloc("vfb", 0);
583 ret
= platform_device_add(vfb_device
);
588 platform_device_put(vfb_device
);
589 platform_driver_unregister(&vfb_driver
);
596 module_init(vfb_init
);
599 static void __exit
vfb_exit(void)
601 platform_device_unregister(vfb_device
);
602 platform_driver_unregister(&vfb_driver
);
605 module_exit(vfb_exit
);
607 MODULE_LICENSE("GPL");