1/* 2 * linux/drivers/video/vfb.c -- Virtual frame buffer device 3 * 4 * Copyright (C) 1997 Geert Uytterhoeven 5 * 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 8 * more details. 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/errno.h> 14#include <linux/string.h> 15#include <linux/mm.h> 16#include <linux/tty.h> 17#include <linux/slab.h> 18#include <linux/vmalloc.h> 19#include <linux/delay.h> 20#include <linux/interrupt.h> 21#include <asm/uaccess.h> 22#include <linux/fb.h> 23#include <linux/init.h> 24 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> 33 34 35 /* 36 * RAM we reserve for the frame buffer. This defines the maximum screen 37 * size 38 * 39 * The default can be overridden if the driver is compiled as a module 40 */ 41 42#define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */ 43 44static u_long videomemory, videomemorysize = VIDEOMEMSIZE; 45MODULE_PARM(videomemorysize, "l"); 46static int currcon = 0; 47static struct display disp; 48static struct fb_info fb_info; 49static struct { u_char red, green, blue, pad; } palette[256]; 50static union { 51#ifdef FBCON_HAS_CFB16 52 u16 cfb16[16]; 53#endif 54#ifdef FBCON_HAS_CFB24 55 u32 cfb24[16]; 56#endif 57#ifdef FBCON_HAS_CFB32 58 u32 cfb32[16]; 59#endif 60} fbcon_cmap; 61static char vfb_name[16] = "Virtual FB"; 62 63static struct fb_var_screeninfo vfb_default = { 64 /* 640x480, 8 bpp */ 65 640, 480, 640, 480, 0, 0, 8, 0, 66 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 67 0, 0, -1, -1, 0, 20000, 64, 64, 32, 32, 64, 2, 68 0, FB_VMODE_NONINTERLACED 69}; 70 71static int vfb_enable = 0; /* disabled by default */ 72 73 74 /* 75 * Interface used by the world 76 */ 77 78int vfb_setup(char*); 79 80static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con, 81 struct fb_info *info); 82static int vfb_get_var(struct fb_var_screeninfo *var, int con, 83 struct fb_info *info); 84static int vfb_set_var(struct fb_var_screeninfo *var, int con, 85 struct fb_info *info); 86static int vfb_pan_display(struct fb_var_screeninfo *var, int con, 87 struct fb_info *info); 88static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, 89 struct fb_info *info); 90static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 91 struct fb_info *info); 92 93 94 /* 95 * Interface to the low level console driver 96 */ 97 98int vfb_init(void); 99static int vfbcon_switch(int con, struct fb_info *info); 100static int vfbcon_updatevar(int con, struct fb_info *info); 101static void vfbcon_blank(int blank, struct fb_info *info); 102 103 104 /* 105 * Internal routines 106 */ 107 108static u_long get_line_length(int xres_virtual, int bpp); 109static void vfb_encode_fix(struct fb_fix_screeninfo *fix, 110 struct fb_var_screeninfo *var); 111static void set_color_bitfields(struct fb_var_screeninfo *var); 112static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, 113 u_int *transp, struct fb_info *info); 114static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 115 u_int transp, struct fb_info *info); 116static void do_install_cmap(int con, struct fb_info *info); 117 118 119static struct fb_ops vfb_ops = { 120 owner: THIS_MODULE, 121 fb_get_fix: vfb_get_fix, 122 fb_get_var: vfb_get_var, 123 fb_set_var: vfb_set_var, 124 fb_get_cmap: vfb_get_cmap, 125 fb_set_cmap: vfb_set_cmap, 126 fb_pan_display: vfb_pan_display, 127}; 128 129 /* 130 * Get the Fixed Part of the Display 131 */ 132 133static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con, 134 struct fb_info *info) 135{ 136 struct fb_var_screeninfo *var; 137 138 if (con == -1) 139 var = &vfb_default; 140 else 141 var = &fb_display[con].var; 142 vfb_encode_fix(fix, var); 143 return 0; 144} 145 146 147 /* 148 * Get the User Defined Part of the Display 149 */ 150 151static int vfb_get_var(struct fb_var_screeninfo *var, int con, 152 struct fb_info *info) 153{ 154 if (con == -1) 155 *var = vfb_default; 156 else 157 *var = fb_display[con].var; 158 set_color_bitfields(var); 159 return 0; 160} 161 162 163 /* 164 * Set the User Defined Part of the Display 165 */ 166 167static int vfb_set_var(struct fb_var_screeninfo *var, int con, 168 struct fb_info *info) 169{ 170 int err, activate = var->activate; 171 int oldxres, oldyres, oldvxres, oldvyres, oldbpp; 172 u_long line_length; 173 174 struct display *display; 175 if (con >= 0) 176 display = &fb_display[con]; 177 else 178 display = &disp; /* used during initialization */ 179 180 /* 181 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! 182 * as FB_VMODE_SMOOTH_XPAN is only used internally 183 */ 184 185 if (var->vmode & FB_VMODE_CONUPDATE) { 186 var->vmode |= FB_VMODE_YWRAP; 187 var->xoffset = display->var.xoffset; 188 var->yoffset = display->var.yoffset; 189 } 190 191 /* 192 * Some very basic checks 193 */ 194 if (!var->xres) 195 var->xres = 1; 196 if (!var->yres) 197 var->yres = 1; 198 if (var->xres > var->xres_virtual) 199 var->xres_virtual = var->xres; 200 if (var->yres > var->yres_virtual) 201 var->yres_virtual = var->yres; 202 if (var->bits_per_pixel <= 1) 203 var->bits_per_pixel = 1; 204 else if (var->bits_per_pixel <= 8) 205 var->bits_per_pixel = 8; 206 else if (var->bits_per_pixel <= 16) 207 var->bits_per_pixel = 16; 208 else 209 return -EINVAL; 210 211 /* 212 * Memory limit 213 */ 214 line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 215 if (line_length*var->yres_virtual > videomemorysize) 216 return -ENOMEM; 217 218 set_color_bitfields(var); 219 220 if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { 221 oldxres = display->var.xres; 222 oldyres = display->var.yres; 223 oldvxres = display->var.xres_virtual; 224 oldvyres = display->var.yres_virtual; 225 oldbpp = display->var.bits_per_pixel; 226 display->var = *var; 227 if (oldxres != var->xres || oldyres != var->yres || 228 oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || 229 oldbpp != var->bits_per_pixel) { 230 struct fb_fix_screeninfo fix; 231 232 vfb_encode_fix(&fix, var); 233 display->screen_base = (char *)videomemory; 234 display->visual = fix.visual; 235 display->type = fix.type; 236 display->type_aux = fix.type_aux; 237 display->ypanstep = fix.ypanstep; 238 display->ywrapstep = fix.ywrapstep; 239 display->line_length = fix.line_length; 240 display->can_soft_blank = 1; 241 display->inverse = 0; 242 switch (var->bits_per_pixel) { 243#ifdef FBCON_HAS_MFB 244 case 1: 245 display->dispsw = &fbcon_mfb; 246 break; 247#endif 248#ifdef FBCON_HAS_CFB2 249 case 2: 250 display->dispsw = &fbcon_cfb2; 251 break; 252#endif 253#ifdef FBCON_HAS_CFB4 254 case 4: 255 display->dispsw = &fbcon_cfb4; 256 break; 257#endif 258#ifdef FBCON_HAS_CFB8 259 case 8: 260 display->dispsw = &fbcon_cfb8; 261 break; 262#endif 263#ifdef FBCON_HAS_CFB16 264 case 16: 265 display->dispsw = &fbcon_cfb16; 266 display->dispsw_data = fbcon_cmap.cfb16; 267 break; 268#endif 269#ifdef FBCON_HAS_CFB24 270 case 24: 271 display->dispsw = &fbcon_cfb24; 272 display->dispsw_data = fbcon_cmap.cfb24; 273 break; 274#endif 275#ifdef FBCON_HAS_CFB32 276 case 32: 277 display->dispsw = &fbcon_cfb32; 278 display->dispsw_data = fbcon_cmap.cfb32; 279 break; 280#endif 281 default: 282 display->dispsw = &fbcon_dummy; 283 break; 284 } 285 if (fb_info.changevar) 286 (*fb_info.changevar)(con); 287 } 288 if (oldbpp != var->bits_per_pixel) { 289 if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) 290 return err; 291 do_install_cmap(con, info); 292 } 293 } 294 return 0; 295} 296 297 298 /* 299 * Pan or Wrap the Display 300 * 301 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag 302 */ 303 304static int vfb_pan_display(struct fb_var_screeninfo *var, int con, 305 struct fb_info *info) 306{ 307 if (var->vmode & FB_VMODE_YWRAP) { 308 if (var->yoffset < 0 || 309 var->yoffset >= fb_display[con].var.yres_virtual || 310 var->xoffset) 311 return -EINVAL; 312 } else { 313 if (var->xoffset+fb_display[con].var.xres > 314 fb_display[con].var.xres_virtual || 315 var->yoffset+fb_display[con].var.yres > 316 fb_display[con].var.yres_virtual) 317 return -EINVAL; 318 } 319 fb_display[con].var.xoffset = var->xoffset; 320 fb_display[con].var.yoffset = var->yoffset; 321 if (var->vmode & FB_VMODE_YWRAP) 322 fb_display[con].var.vmode |= FB_VMODE_YWRAP; 323 else 324 fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; 325 return 0; 326} 327 328 /* 329 * Get the Colormap 330 */ 331 332static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, 333 struct fb_info *info) 334{ 335 if (con == currcon) /* current console? */ 336 return fb_get_cmap(cmap, kspc, vfb_getcolreg, info); 337 else if (fb_display[con].cmap.len) /* non default colormap? */ 338 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); 339 else 340 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 341 cmap, kspc ? 0 : 2); 342 return 0; 343} 344 345 /* 346 * Set the Colormap 347 */ 348 349static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 350 struct fb_info *info) 351{ 352 int err; 353 354 if (!fb_display[con].cmap.len) { /* no colormap allocated? */ 355 if ((err = fb_alloc_cmap(&fb_display[con].cmap, 356 1<<fb_display[con].var.bits_per_pixel, 0))) 357 return err; 358 } 359 if (con == currcon) /* current console? */ 360 return fb_set_cmap(cmap, kspc, vfb_setcolreg, info); 361 else 362 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); 363 return 0; 364} 365 366 367int __init vfb_setup(char *options) 368{ 369 char *this_opt; 370 371 fb_info.fontname[0] = '\0'; 372 373 vfb_enable = 1; 374 375 if (!options || !*options) 376 return 0; 377 378 while ((this_opt = strsep(&options, ",")) != NULL) { 379 if (!strncmp(this_opt, "font:", 5)) 380 strcpy(fb_info.fontname, this_opt+5); 381 } 382 return 0; 383} 384 385 386 /* 387 * Initialisation 388 */ 389 390int __init vfb_init(void) 391{ 392 if (!vfb_enable) 393 return -ENXIO; 394 395 if (!(videomemory = (u_long)vmalloc(videomemorysize))) 396 return -ENOMEM; 397 398 strcpy(fb_info.modename, vfb_name); 399 fb_info.changevar = NULL; 400 fb_info.node = -1; 401 fb_info.fbops = &vfb_ops; 402 fb_info.disp = &disp; 403 fb_info.switch_con = &vfbcon_switch; 404 fb_info.updatevar = &vfbcon_updatevar; 405 fb_info.blank = &vfbcon_blank; 406 fb_info.flags = FBINFO_FLAG_DEFAULT; 407 408 vfb_set_var(&vfb_default, -1, &fb_info); 409 410 if (register_framebuffer(&fb_info) < 0) { 411 vfree((void *)videomemory); 412 return -EINVAL; 413 } 414 415 printk(KERN_INFO "fb%d: Virtual frame buffer device, using %ldK of video memory\n", 416 GET_FB_IDX(fb_info.node), videomemorysize>>10); 417 return 0; 418} 419 420 421static int vfbcon_switch(int con, struct fb_info *info) 422{ 423 /* Do we have to save the colormap? */ 424 if (fb_display[currcon].cmap.len) 425 fb_get_cmap(&fb_display[currcon].cmap, 1, vfb_getcolreg, info); 426 427 currcon = con; 428 /* Install new colormap */ 429 do_install_cmap(con, info); 430 return 0; 431} 432 433 /* 434 * Update the `var' structure (called by fbcon.c) 435 */ 436 437static int vfbcon_updatevar(int con, struct fb_info *info) 438{ 439 /* Nothing */ 440 return 0; 441} 442 443 /* 444 * Blank the display. 445 */ 446 447static void vfbcon_blank(int blank, struct fb_info *info) 448{ 449 /* Nothing */ 450} 451 452static u_long get_line_length(int xres_virtual, int bpp) 453{ 454 u_long length; 455 456 length = xres_virtual*bpp; 457 length = (length+31)&-32; 458 length >>= 3; 459 return(length); 460} 461 462static void vfb_encode_fix(struct fb_fix_screeninfo *fix, 463 struct fb_var_screeninfo *var) 464{ 465 memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 466 strcpy(fix->id, vfb_name); 467 fix->smem_start = videomemory; 468 fix->smem_len = videomemorysize; 469 fix->type = FB_TYPE_PACKED_PIXELS; 470 fix->type_aux = 0; 471 switch (var->bits_per_pixel) { 472 case 1: 473 fix->visual = FB_VISUAL_MONO01; 474 break; 475 case 2: 476 case 4: 477 case 8: 478 fix->visual = FB_VISUAL_PSEUDOCOLOR; 479 break; 480 case 16: 481 case 24: 482 case 32: 483 fix->visual = FB_VISUAL_TRUECOLOR; 484 break; 485 } 486 fix->ywrapstep = 1; 487 fix->xpanstep = 1; 488 fix->ypanstep = 1; 489 fix->line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 490} 491 492static void set_color_bitfields(struct fb_var_screeninfo *var) 493{ 494 switch (var->bits_per_pixel) { 495 case 1: 496 case 8: 497 var->red.offset = 0; 498 var->red.length = 8; 499 var->green.offset = 0; 500 var->green.length = 8; 501 var->blue.offset = 0; 502 var->blue.length = 8; 503 var->transp.offset = 0; 504 var->transp.length = 0; 505 break; 506 case 16: /* RGB 565 */ 507 var->red.offset = 0; 508 var->red.length = 5; 509 var->green.offset = 5; 510 var->green.length = 6; 511 var->blue.offset = 11; 512 var->blue.length = 5; 513 var->transp.offset = 0; 514 var->transp.length = 0; 515 break; 516 case 24: /* RGB 888 */ 517 var->red.offset = 0; 518 var->red.length = 8; 519 var->green.offset = 8; 520 var->green.length = 8; 521 var->blue.offset = 16; 522 var->blue.length = 8; 523 var->transp.offset = 0; 524 var->transp.length = 0; 525 break; 526 case 32: /* RGBA 8888 */ 527 var->red.offset = 0; 528 var->red.length = 8; 529 var->green.offset = 8; 530 var->green.length = 8; 531 var->blue.offset = 16; 532 var->blue.length = 8; 533 var->transp.offset = 24; 534 var->transp.length = 8; 535 break; 536 } 537 var->red.msb_right = 0; 538 var->green.msb_right = 0; 539 var->blue.msb_right = 0; 540 var->transp.msb_right = 0; 541} 542 543 544 /* 545 * Read a single color register and split it into 546 * colors/transparent. Return != 0 for invalid regno. 547 */ 548 549static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, 550 u_int *transp, struct fb_info *info) 551{ 552 if (regno > 255) 553 return 1; 554 *red = (palette[regno].red<<8) | palette[regno].red; 555 *green = (palette[regno].green<<8) | palette[regno].green; 556 *blue = (palette[regno].blue<<8) | palette[regno].blue; 557 *transp = 0; 558 return 0; 559} 560 561 562 /* 563 * Set a single color register. The values supplied are already 564 * rounded down to the hardware's capabilities (according to the 565 * entries in the var structure). Return != 0 for invalid regno. 566 */ 567 568static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 569 u_int transp, struct fb_info *info) 570{ 571 if (regno > 255) 572 return 1; 573 red >>= 8; 574 green >>= 8; 575 blue >>= 8; 576 palette[regno].red = red; 577 palette[regno].green = green; 578 palette[regno].blue = blue; 579 return 0; 580} 581 582 583static void do_install_cmap(int con, struct fb_info *info) 584{ 585 if (con != currcon) 586 return; 587 if (fb_display[con].cmap.len) 588 fb_set_cmap(&fb_display[con].cmap, 1, vfb_setcolreg, info); 589 else 590 fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1, 591 vfb_setcolreg, info); 592} 593 594 595#ifdef MODULE 596MODULE_LICENSE("GPL"); 597 598int init_module(void) 599{ 600 return vfb_init(); 601} 602 603void cleanup_module(void) 604{ 605 unregister_framebuffer(&fb_info); 606 vfree((void *)videomemory); 607} 608 609#endif /* MODULE */ 610