2 * linux/drivers/video/vfb.c -- Virtual frame buffer device
4 * Copyright (C) 1997 Geert Uytterhoeven
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/string.h>
16 #include <linux/tty.h>
17 #include <linux/malloc.h>
18 #include <linux/vmalloc.h>
19 #include <linux/delay.h>
20 #include <linux/interrupt.h>
21 #include <asm/uaccess.h>
23 #include <linux/init.h>
25 #include <video/fbcon.h>
26 #include <video/fbcon-mfb.h>
27 #include <video/fbcon-cfb2.h>
28 #include <video/fbcon-cfb4.h>
29 #include <video/fbcon-cfb8.h>
30 #include <video/fbcon-cfb16.h>
31 #include <video/fbcon-cfb24.h>
32 #include <video/fbcon-cfb32.h>
35 #define arraysize(x) (sizeof(x)/sizeof(*(x)))
39 * RAM we reserve for the frame buffer. This defines the maximum screen
42 * The default can be overridden if the driver is compiled as a module
45 #define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */
47 static u_long videomemory
, videomemorysize
= VIDEOMEMSIZE
;
48 MODULE_PARM(videomemorysize
, "l");
49 static int currcon
= 0;
50 static struct display disp
;
51 static struct fb_info fb_info
;
52 static struct { u_char red
, green
, blue
, pad
; } palette
[256];
54 #ifdef FBCON_HAS_CFB16
57 #ifdef FBCON_HAS_CFB24
60 #ifdef FBCON_HAS_CFB32
64 static char vfb_name
[16] = "Virtual FB";
66 static struct fb_var_screeninfo vfb_default
= {
68 640, 480, 640, 480, 0, 0, 8, 0,
69 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
70 0, 0, -1, -1, 0, 20000, 64, 64, 32, 32, 64, 2,
71 0, FB_VMODE_NONINTERLACED
74 static int vfb_enable
= 0; /* disabled by default */
78 * Interface used by the world
83 static int vfb_open(struct fb_info
*info
, int user
);
84 static int vfb_release(struct fb_info
*info
, int user
);
85 static int vfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
86 struct fb_info
*info
);
87 static int vfb_get_var(struct fb_var_screeninfo
*var
, int con
,
88 struct fb_info
*info
);
89 static int vfb_set_var(struct fb_var_screeninfo
*var
, int con
,
90 struct fb_info
*info
);
91 static int vfb_pan_display(struct fb_var_screeninfo
*var
, int con
,
92 struct fb_info
*info
);
93 static int vfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
94 struct fb_info
*info
);
95 static int vfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
96 struct fb_info
*info
);
97 static int vfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
98 u_long arg
, int con
, struct fb_info
*info
);
102 * Interface to the low level console driver
106 static int vfbcon_switch(int con
, struct fb_info
*info
);
107 static int vfbcon_updatevar(int con
, struct fb_info
*info
);
108 static void vfbcon_blank(int blank
, struct fb_info
*info
);
115 static u_long
get_line_length(int xres_virtual
, int bpp
);
116 static void vfb_encode_fix(struct fb_fix_screeninfo
*fix
,
117 struct fb_var_screeninfo
*var
);
118 static void set_color_bitfields(struct fb_var_screeninfo
*var
);
119 static int vfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
120 u_int
*transp
, struct fb_info
*info
);
121 static int vfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
122 u_int transp
, struct fb_info
*info
);
123 static void do_install_cmap(int con
, struct fb_info
*info
);
126 static struct fb_ops vfb_ops
= {
127 vfb_open
, vfb_release
, vfb_get_fix
, vfb_get_var
, vfb_set_var
, vfb_get_cmap
,
128 vfb_set_cmap
, vfb_pan_display
, vfb_ioctl
133 * Open/Release the frame buffer device
136 static int vfb_open(struct fb_info
*info
, int user
)
139 * Nothing, only a usage count for the moment
146 static int vfb_release(struct fb_info
*info
, int user
)
154 * Get the Fixed Part of the Display
157 static int vfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
158 struct fb_info
*info
)
160 struct fb_var_screeninfo
*var
;
165 var
= &fb_display
[con
].var
;
166 vfb_encode_fix(fix
, var
);
172 * Get the User Defined Part of the Display
175 static int vfb_get_var(struct fb_var_screeninfo
*var
, int con
,
176 struct fb_info
*info
)
181 *var
= fb_display
[con
].var
;
182 set_color_bitfields(var
);
188 * Set the User Defined Part of the Display
191 static int vfb_set_var(struct fb_var_screeninfo
*var
, int con
,
192 struct fb_info
*info
)
194 int err
, activate
= var
->activate
;
195 int oldxres
, oldyres
, oldvxres
, oldvyres
, oldbpp
;
198 struct display
*display
;
200 display
= &fb_display
[con
];
202 display
= &disp
; /* used during initialization */
205 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
206 * as FB_VMODE_SMOOTH_XPAN is only used internally
209 if (var
->vmode
& FB_VMODE_CONUPDATE
) {
210 var
->vmode
|= FB_VMODE_YWRAP
;
211 var
->xoffset
= display
->var
.xoffset
;
212 var
->yoffset
= display
->var
.yoffset
;
216 * Some very basic checks
222 if (var
->xres
> var
->xres_virtual
)
223 var
->xres_virtual
= var
->xres
;
224 if (var
->yres
> var
->yres_virtual
)
225 var
->yres_virtual
= var
->yres
;
226 if (var
->bits_per_pixel
<= 1)
227 var
->bits_per_pixel
= 1;
228 else if (var
->bits_per_pixel
<= 8)
229 var
->bits_per_pixel
= 8;
230 else if (var
->bits_per_pixel
<= 16)
231 var
->bits_per_pixel
= 16;
233 /* fbcon doesn't support this (yet) */
234 else if (var
->bits_per_pixel
<= 24)
235 var
->bits_per_pixel
= 24;
236 else if (var
->bits_per_pixel
<= 32)
237 var
->bits_per_pixel
= 32;
245 line_length
= get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
246 if (line_length
*var
->yres_virtual
> videomemorysize
)
249 set_color_bitfields(var
);
251 if ((activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
252 oldxres
= display
->var
.xres
;
253 oldyres
= display
->var
.yres
;
254 oldvxres
= display
->var
.xres_virtual
;
255 oldvyres
= display
->var
.yres_virtual
;
256 oldbpp
= display
->var
.bits_per_pixel
;
258 if (oldxres
!= var
->xres
|| oldyres
!= var
->yres
||
259 oldvxres
!= var
->xres_virtual
|| oldvyres
!= var
->yres_virtual
||
260 oldbpp
!= var
->bits_per_pixel
) {
261 struct fb_fix_screeninfo fix
;
263 vfb_encode_fix(&fix
, var
);
264 display
->screen_base
= (char *)videomemory
;
265 display
->visual
= fix
.visual
;
266 display
->type
= fix
.type
;
267 display
->type_aux
= fix
.type_aux
;
268 display
->ypanstep
= fix
.ypanstep
;
269 display
->ywrapstep
= fix
.ywrapstep
;
270 display
->line_length
= fix
.line_length
;
271 display
->can_soft_blank
= 1;
272 display
->inverse
= 0;
273 switch (var
->bits_per_pixel
) {
276 display
->dispsw
= &fbcon_mfb
;
279 #ifdef FBCON_HAS_CFB2
281 display
->dispsw
= &fbcon_cfb2
;
284 #ifdef FBCON_HAS_CFB4
286 display
->dispsw
= &fbcon_cfb4
;
289 #ifdef FBCON_HAS_CFB8
291 display
->dispsw
= &fbcon_cfb8
;
294 #ifdef FBCON_HAS_CFB16
296 display
->dispsw
= &fbcon_cfb16
;
297 display
->dispsw_data
= fbcon_cmap
.cfb16
;
300 #ifdef FBCON_HAS_CFB24
302 display
->dispsw
= &fbcon_cfb24
;
303 display
->dispsw_data
= fbcon_cmap
.cfb24
;
306 #ifdef FBCON_HAS_CFB32
308 display
->dispsw
= &fbcon_cfb32
;
309 display
->dispsw_data
= fbcon_cmap
.cfb32
;
313 display
->dispsw
= &fbcon_dummy
;
316 if (fb_info
.changevar
)
317 (*fb_info
.changevar
)(con
);
319 if (oldbpp
!= var
->bits_per_pixel
) {
320 if ((err
= fb_alloc_cmap(&display
->cmap
, 0, 0)))
322 do_install_cmap(con
, info
);
330 * Pan or Wrap the Display
332 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
335 static int vfb_pan_display(struct fb_var_screeninfo
*var
, int con
,
336 struct fb_info
*info
)
338 if (var
->vmode
& FB_VMODE_YWRAP
) {
339 if (var
->yoffset
< 0 ||
340 var
->yoffset
>= fb_display
[con
].var
.yres_virtual
||
344 if (var
->xoffset
+fb_display
[con
].var
.xres
>
345 fb_display
[con
].var
.xres_virtual
||
346 var
->yoffset
+fb_display
[con
].var
.yres
>
347 fb_display
[con
].var
.yres_virtual
)
350 fb_display
[con
].var
.xoffset
= var
->xoffset
;
351 fb_display
[con
].var
.yoffset
= var
->yoffset
;
352 if (var
->vmode
& FB_VMODE_YWRAP
)
353 fb_display
[con
].var
.vmode
|= FB_VMODE_YWRAP
;
355 fb_display
[con
].var
.vmode
&= ~FB_VMODE_YWRAP
;
363 static int vfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
364 struct fb_info
*info
)
366 if (con
== currcon
) /* current console? */
367 return fb_get_cmap(cmap
, kspc
, vfb_getcolreg
, info
);
368 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
369 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
371 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
380 static int vfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
381 struct fb_info
*info
)
385 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
386 if ((err
= fb_alloc_cmap(&fb_display
[con
].cmap
,
387 1<<fb_display
[con
].var
.bits_per_pixel
, 0)))
390 if (con
== currcon
) /* current console? */
391 return fb_set_cmap(cmap
, kspc
, vfb_setcolreg
, info
);
393 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
399 * Virtual Frame Buffer Specific ioctls
402 static int vfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
403 u_long arg
, int con
, struct fb_info
*info
)
409 int __init
vfb_setup(char *options
)
413 fb_info
.fontname
[0] = '\0';
417 if (!options
|| !*options
)
420 for (this_opt
= strtok(options
, ","); this_opt
;
421 this_opt
= strtok(NULL
, ",")) {
422 if (!strncmp(this_opt
, "font:", 5))
423 strcpy(fb_info
.fontname
, this_opt
+5);
433 int __init
vfb_init(void)
438 if (!(videomemory
= (u_long
)vmalloc(videomemorysize
)))
441 strcpy(fb_info
.modename
, vfb_name
);
442 fb_info
.changevar
= NULL
;
444 fb_info
.fbops
= &vfb_ops
;
445 fb_info
.disp
= &disp
;
446 fb_info
.switch_con
= &vfbcon_switch
;
447 fb_info
.updatevar
= &vfbcon_updatevar
;
448 fb_info
.blank
= &vfbcon_blank
;
449 fb_info
.flags
= FBINFO_FLAG_DEFAULT
;
451 vfb_set_var(&vfb_default
, -1, &fb_info
);
453 if (register_framebuffer(&fb_info
) < 0) {
454 vfree((void *)videomemory
);
458 printk(KERN_INFO
"fb%d: Virtual frame buffer device, using %ldK of video memory\n",
459 GET_FB_IDX(fb_info
.node
), videomemorysize
>>10);
464 static int vfbcon_switch(int con
, struct fb_info
*info
)
466 /* Do we have to save the colormap? */
467 if (fb_display
[currcon
].cmap
.len
)
468 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, vfb_getcolreg
, info
);
471 /* Install new colormap */
472 do_install_cmap(con
, info
);
477 * Update the `var' structure (called by fbcon.c)
480 static int vfbcon_updatevar(int con
, struct fb_info
*info
)
490 static void vfbcon_blank(int blank
, struct fb_info
*info
)
495 static u_long
get_line_length(int xres_virtual
, int bpp
)
499 length
= (xres_virtual
+bpp
-1)/bpp
;
500 length
= (length
+31)&-32;
505 static void vfb_encode_fix(struct fb_fix_screeninfo
*fix
,
506 struct fb_var_screeninfo
*var
)
508 memset(fix
, 0, sizeof(struct fb_fix_screeninfo
));
509 strcpy(fix
->id
, vfb_name
);
510 fix
->smem_start
= videomemory
;
511 fix
->smem_len
= videomemorysize
;
512 fix
->type
= FB_TYPE_PACKED_PIXELS
;
514 switch (var
->bits_per_pixel
) {
516 fix
->visual
= FB_VISUAL_MONO01
;
521 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
526 fix
->visual
= FB_VISUAL_TRUECOLOR
;
532 fix
->line_length
= get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
535 static void set_color_bitfields(struct fb_var_screeninfo
*var
)
537 switch (var
->bits_per_pixel
) {
542 var
->green
.offset
= 0;
543 var
->green
.length
= 8;
544 var
->blue
.offset
= 0;
545 var
->blue
.length
= 8;
546 var
->transp
.offset
= 0;
547 var
->transp
.length
= 0;
549 case 16: /* RGB 565 */
552 var
->green
.offset
= 5;
553 var
->green
.length
= 6;
554 var
->blue
.offset
= 11;
555 var
->blue
.length
= 5;
556 var
->transp
.offset
= 0;
557 var
->transp
.length
= 0;
559 case 24: /* RGB 888 */
562 var
->green
.offset
= 8;
563 var
->green
.length
= 8;
564 var
->blue
.offset
= 16;
565 var
->blue
.length
= 8;
566 var
->transp
.offset
= 0;
567 var
->transp
.length
= 0;
569 case 32: /* RGBA 8888 */
572 var
->green
.offset
= 8;
573 var
->green
.length
= 8;
574 var
->blue
.offset
= 16;
575 var
->blue
.length
= 8;
576 var
->transp
.offset
= 24;
577 var
->transp
.length
= 8;
580 var
->red
.msb_right
= 0;
581 var
->green
.msb_right
= 0;
582 var
->blue
.msb_right
= 0;
583 var
->transp
.msb_right
= 0;
588 * Read a single color register and split it into
589 * colors/transparent. Return != 0 for invalid regno.
592 static int vfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
593 u_int
*transp
, struct fb_info
*info
)
597 *red
= (palette
[regno
].red
<<8) | palette
[regno
].red
;
598 *green
= (palette
[regno
].green
<<8) | palette
[regno
].green
;
599 *blue
= (palette
[regno
].blue
<<8) | palette
[regno
].blue
;
606 * Set a single color register. The values supplied are already
607 * rounded down to the hardware's capabilities (according to the
608 * entries in the var structure). Return != 0 for invalid regno.
611 static int vfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
612 u_int transp
, struct fb_info
*info
)
619 palette
[regno
].red
= red
;
620 palette
[regno
].green
= green
;
621 palette
[regno
].blue
= blue
;
626 static void do_install_cmap(int con
, struct fb_info
*info
)
630 if (fb_display
[con
].cmap
.len
)
631 fb_set_cmap(&fb_display
[con
].cmap
, 1, vfb_setcolreg
, info
);
633 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), 1,
634 vfb_setcolreg
, info
);
639 int init_module(void)
644 void cleanup_module(void)
646 unregister_framebuffer(&fb_info
);
647 vfree((void *)videomemory
);