1/* 2 * linux/drivers/video/fbgen.c -- Generic routines for frame buffer devices 3 * 4 * Created 3 Jan 1998 by Geert Uytterhoeven 5 * 6 * 2001 - Documented with DocBook 7 * - Brad Douglas <brad@neruo.com> 8 * 9 * This file is subject to the terms and conditions of the GNU General Public 10 * License. See the file COPYING in the main directory of this archive 11 * for more details. 12 */ 13 14#include <linux/module.h> 15#include <linux/string.h> 16#include <linux/tty.h> 17#include <linux/fb.h> 18#include <linux/slab.h> 19 20#include <asm/uaccess.h> 21#include <asm/io.h> 22 23#include <video/fbcon.h> 24 25static int currcon = 0; 26 27 28/* ---- `Generic' versions of the frame buffer device operations ----------- */ 29 30 31/** 32 * fbgen_get_fix - get fixed part of display 33 * @fix: fb_fix_screeninfo structure 34 * @con: virtual console number 35 * @info: frame buffer info structure 36 * 37 * Get the fixed information part of the display and place it 38 * into @fix for virtual console @con on device @info. 39 * 40 * Returns negative errno on error, or zero on success. 41 * 42 */ 43 44int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) 45{ 46 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 47 struct fbgen_hwswitch *fbhw = info2->fbhw; 48 char par[info2->parsize]; 49 50 if (con == -1) 51 fbhw->get_par(&par, info2); 52 else { 53 int err; 54 55 if ((err = fbhw->decode_var(&fb_display[con].var, &par, info2))) 56 return err; 57 } 58 memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 59 return fbhw->encode_fix(fix, &par, info2); 60} 61 62 63/** 64 * fbgen_get_var - get user defined part of display 65 * @var: fb_var_screeninfo structure 66 * @con: virtual console number 67 * @info: frame buffer info structure 68 * 69 * Get the user defined part of the display and place it into @var 70 * for virtual console @con on device @info. 71 * 72 * Returns negative errno on error, or zero for success. 73 * 74 */ 75 76int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) 77{ 78 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 79 struct fbgen_hwswitch *fbhw = info2->fbhw; 80 char par[info2->parsize]; 81 82 if (con == -1) { 83 fbhw->get_par(&par, info2); 84 fbhw->encode_var(var, &par, info2); 85 } else 86 *var = fb_display[con].var; 87 return 0; 88} 89 90 91/** 92 * fbgen_set_var - set the user defined part of display 93 * @var: fb_var_screeninfo user defined part of the display 94 * @con: virtual console number 95 * @info: frame buffer info structure 96 * 97 * Set the user defined part of the display as dictated by @var 98 * for virtual console @con on device @info. 99 * 100 * Returns negative errno on error, or zero for success. 101 * 102 */ 103 104int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) 105{ 106 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 107 int err; 108 int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset; 109 struct fb_bitfield oldred, oldgreen, oldblue; 110 111 if ((err = fbgen_do_set_var(var, con == currcon, info2))) 112 return err; 113 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { 114 oldxres = fb_display[con].var.xres; 115 oldyres = fb_display[con].var.yres; 116 oldxres_virtual = fb_display[con].var.xres_virtual; 117 oldyres_virtual = fb_display[con].var.yres_virtual; 118 oldbpp = fb_display[con].var.bits_per_pixel; 119 oldred = fb_display[con].var.red; 120 oldgreen = fb_display[con].var.green; 121 oldblue = fb_display[con].var.blue; 122 oldyoffset = fb_display[con].var.yoffset; 123 fb_display[con].var = *var; 124 if (oldxres != var->xres || oldyres != var->yres || 125 oldxres_virtual != var->xres_virtual || 126 oldyres_virtual != var->yres_virtual || 127 oldbpp != var->bits_per_pixel || 128 (!(memcmp(&oldred, &(var->red), sizeof(struct fb_bitfield)))) || 129 (!(memcmp(&oldgreen, &(var->green), sizeof(struct fb_bitfield)))) || 130 (!(memcmp(&oldblue, &(var->blue), sizeof(struct fb_bitfield)))) || 131 oldyoffset != var->yoffset) { 132 fbgen_set_disp(con, info2); 133 if (info->changevar) 134 (*info->changevar)(con); 135 if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0))) 136 return err; 137 fbgen_install_cmap(con, info2); 138 } 139 } 140 var->activate = 0; 141 return 0; 142} 143 144 145/** 146 * fbgen_get_cmap - get the colormap 147 * @cmap: frame buffer colormap structure 148 * @kspc: boolean, 0 copy local, 1 put_user() function 149 * @con: virtual console number 150 * @info: frame buffer info structure 151 * 152 * Gets the colormap for virtual console @con and places it into 153 * @cmap for device @info. 154 * 155 * Returns negative errno on error, or zero for success. 156 * 157 */ 158 159int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con, 160 struct fb_info *info) 161{ 162 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 163 struct fbgen_hwswitch *fbhw = info2->fbhw; 164 165 if (con == currcon) /* current console ? */ 166 return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info); 167 else 168 if (fb_display[con].cmap.len) /* non default colormap ? */ 169 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); 170 else { 171 int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256; 172 fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2); 173 } 174 return 0; 175} 176 177 178/** 179 * fbgen_set_cmap - set the colormap 180 * @cmap: frame buffer colormap structure 181 * @kspc: boolean, 0 copy local, 1 get_user() function 182 * @con: virtual console number 183 * @info: frame buffer info structure 184 * 185 * Sets the colormap @cmap for virtual console @con on 186 * device @info. 187 * 188 * Returns negative errno on error, or zero for success. 189 * 190 */ 191 192int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con, 193 struct fb_info *info) 194{ 195 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 196 struct fbgen_hwswitch *fbhw = info2->fbhw; 197 int err; 198 199 if (!fb_display[con].cmap.len) { /* no colormap allocated ? */ 200 int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256; 201 if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0))) 202 return err; 203 } 204 if (con == currcon) /* current console ? */ 205 return fb_set_cmap(cmap, kspc, fbhw->setcolreg, info); 206 else 207 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); 208 return 0; 209} 210 211 212/** 213 * fbgen_pan_display - pan or wrap the display 214 * @var: frame buffer user defined part of display 215 * @con: virtual console number 216 * @info: frame buffer info structure 217 * 218 * Pan or wrap virtual console @con for device @info. 219 * 220 * This call looks only at xoffset, yoffset and the 221 * FB_VMODE_YWRAP flag in @var. 222 * 223 * Returns negative errno on error, or zero for success. 224 * 225 */ 226 227int fbgen_pan_display(struct fb_var_screeninfo *var, int con, 228 struct fb_info *info) 229{ 230 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 231 struct fbgen_hwswitch *fbhw = info2->fbhw; 232 int xoffset = var->xoffset; 233 int yoffset = var->yoffset; 234 int err; 235 236 if (xoffset < 0 || 237 xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual || 238 yoffset < 0 || 239 yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual) 240 return -EINVAL; 241 if (con == currcon) { 242 if (fbhw->pan_display) { 243 if ((err = fbhw->pan_display(var, info2))) 244 return err; 245 } else 246 return -EINVAL; 247 } 248 fb_display[con].var.xoffset = var->xoffset; 249 fb_display[con].var.yoffset = var->yoffset; 250 if (var->vmode & FB_VMODE_YWRAP) 251 fb_display[con].var.vmode |= FB_VMODE_YWRAP; 252 else 253 fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; 254 255 return 0; 256} 257 258 259/* ---- Helper functions --------------------------------------------------- */ 260 261 262/** 263 * fbgen_do_set_var - change the video mode 264 * @var: frame buffer user defined part of display 265 * @isactive: boolean, 0 inactive, 1 active 266 * @info: generic frame buffer info structure 267 * 268 * Change the video mode settings for device @info. If @isactive 269 * is non-zero, the changes will be activated immediately. 270 * 271 * Return negative errno on error, or zero for success. 272 * 273 */ 274 275int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive, 276 struct fb_info_gen *info) 277{ 278 struct fbgen_hwswitch *fbhw = info->fbhw; 279 int err, activate; 280 char par[info->parsize]; 281 282 if ((err = fbhw->decode_var(var, &par, info))) 283 return err; 284 activate = var->activate; 285 if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) 286 fbhw->set_par(&par, info); 287 fbhw->encode_var(var, &par, info); 288 var->activate = activate; 289 return 0; 290} 291 292 293/** 294 * fbgen_set_disp - set generic display 295 * @con: virtual console number 296 * @info: generic frame buffer info structure 297 * 298 * Sets a display on virtual console @con for device @info. 299 * 300 */ 301 302void fbgen_set_disp(int con, struct fb_info_gen *info) 303{ 304 struct fbgen_hwswitch *fbhw = info->fbhw; 305 struct fb_fix_screeninfo fix; 306 char par[info->parsize]; 307 struct display *display; 308 309 if (con >= 0) 310 display = &fb_display[con]; 311 else 312 display = info->info.disp; /* used during initialization */ 313 314 if (con == -1) 315 fbhw->get_par(&par, info); 316 else 317 fbhw->decode_var(&fb_display[con].var, &par, info); 318 memset(&fix, 0, sizeof(struct fb_fix_screeninfo)); 319 fbhw->encode_fix(&fix, &par, info); 320 321 display->visual = fix.visual; 322 display->type = fix.type; 323 display->type_aux = fix.type_aux; 324 display->ypanstep = fix.ypanstep; 325 display->ywrapstep = fix.ywrapstep; 326 display->line_length = fix.line_length; 327 if (info->fbhw->blank || fix.visual == FB_VISUAL_PSEUDOCOLOR || 328 fix.visual == FB_VISUAL_DIRECTCOLOR) 329 display->can_soft_blank = 1; 330 else 331 display->can_soft_blank = 0; 332 fbhw->set_disp(&par, display, info); 333 display->inverse = fix.visual == FB_VISUAL_MONO01; 334} 335 336 337/** 338 * fbgen_install_cmap - install the current colormap 339 * @con: virtual console number 340 * @info: generic frame buffer info structure 341 * 342 * Installs the current colormap for virtual console @con on 343 * device @info. 344 * 345 */ 346 347void fbgen_install_cmap(int con, struct fb_info_gen *info) 348{ 349 struct fbgen_hwswitch *fbhw = info->fbhw; 350 if (con != currcon) 351 return; 352 if (fb_display[con].cmap.len) 353 fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, &info->info); 354 else { 355 int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256; 356 fb_set_cmap(fb_default_cmap(size), 1, fbhw->setcolreg, &info->info); 357 } 358} 359 360 361/** 362 * fbgen_update_var - update user defined part of display 363 * @con: virtual console number 364 * @info: frame buffer info structure 365 * 366 * Updates the user defined part of the display ('var' 367 * structure) on virtual console @con for device @info. 368 * This function is called by fbcon.c. 369 * 370 * Returns negative errno on error, or zero for success. 371 * 372 */ 373 374int fbgen_update_var(int con, struct fb_info *info) 375{ 376 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 377 struct fbgen_hwswitch *fbhw = info2->fbhw; 378 int err; 379 380 if (fbhw->pan_display) { 381 if ((err = fbhw->pan_display(&fb_display[con].var, info2))) 382 return err; 383 } 384 return 0; 385} 386 387 388/** 389 * fbgen_switch - switch to a different virtual console. 390 * @con: virtual console number 391 * @info: frame buffer info structure 392 * 393 * Switch to virtuall console @con on device @info. 394 * 395 * Returns zero. 396 * 397 */ 398 399int fbgen_switch(int con, struct fb_info *info) 400{ 401 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 402 struct fbgen_hwswitch *fbhw = info2->fbhw; 403 404 /* Do we have to save the colormap ? */ 405 if (fb_display[currcon].cmap.len) 406 fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg, 407 &info2->info); 408 fbgen_do_set_var(&fb_display[con].var, 1, info2); 409 currcon = con; 410 /* Install new colormap */ 411 fbgen_install_cmap(con, info2); 412 return 0; 413} 414 415 416/** 417 * fbgen_blank - blank the screen 418 * @blank: boolean, 0 unblank, 1 blank 419 * @info: frame buffer info structure 420 * 421 * Blank the screen on device @info. 422 * 423 */ 424 425void fbgen_blank(int blank, struct fb_info *info) 426{ 427 struct fb_info_gen *info2 = (struct fb_info_gen *)info; 428 struct fbgen_hwswitch *fbhw = info2->fbhw; 429 u16 black[16]; 430 struct fb_cmap cmap; 431 432 if (fbhw->blank && !fbhw->blank(blank, info2)) 433 return; 434 if (blank) { 435 memset(black, 0, 16*sizeof(u16)); 436 cmap.red = black; 437 cmap.green = black; 438 cmap.blue = black; 439 cmap.transp = NULL; 440 cmap.start = 0; 441 cmap.len = 16; 442 fb_set_cmap(&cmap, 1, fbhw->setcolreg, info); 443 } else 444 fbgen_install_cmap(currcon, info2); 445} 446MODULE_LICENSE("GPL"); 447 448 449 /* 450 * Visible symbols for modules 451 */ 452 453EXPORT_SYMBOL(fbgen_get_var); 454EXPORT_SYMBOL(fbgen_get_cmap); 455EXPORT_SYMBOL(fbgen_get_fix); 456EXPORT_SYMBOL(fbgen_set_var); 457EXPORT_SYMBOL(fbgen_set_cmap); 458EXPORT_SYMBOL(fbgen_set_disp); 459EXPORT_SYMBOL(fbgen_install_cmap); 460EXPORT_SYMBOL(fbgen_pan_display); 461EXPORT_SYMBOL(fbgen_update_var); 462EXPORT_SYMBOL(fbgen_do_set_var); 463EXPORT_SYMBOL(fbgen_switch); 464EXPORT_SYMBOL(fbgen_blank); 465