Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12-rc6 1370 lines 34 kB view raw
1/* 2 * linux/drivers/video/fbmem.c 3 * 4 * Copyright (C) 1994 Martin Schaller 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/config.h> 15#include <linux/module.h> 16 17#include <linux/types.h> 18#include <linux/errno.h> 19#include <linux/sched.h> 20#include <linux/smp_lock.h> 21#include <linux/kernel.h> 22#include <linux/major.h> 23#include <linux/slab.h> 24#include <linux/mm.h> 25#include <linux/mman.h> 26#include <linux/tty.h> 27#include <linux/init.h> 28#include <linux/linux_logo.h> 29#include <linux/proc_fs.h> 30#include <linux/console.h> 31#ifdef CONFIG_KMOD 32#include <linux/kmod.h> 33#endif 34#include <linux/devfs_fs_kernel.h> 35#include <linux/err.h> 36#include <linux/kernel.h> 37#include <linux/device.h> 38#include <linux/efi.h> 39 40#if defined(__mc68000__) || defined(CONFIG_APUS) 41#include <asm/setup.h> 42#endif 43 44#include <asm/io.h> 45#include <asm/uaccess.h> 46#include <asm/page.h> 47#include <asm/pgtable.h> 48 49#include <linux/fb.h> 50 51 /* 52 * Frame buffer device initialization and setup routines 53 */ 54 55#define FBPIXMAPSIZE (1024 * 8) 56 57static struct notifier_block *fb_notifier_list; 58struct fb_info *registered_fb[FB_MAX]; 59int num_registered_fb; 60 61/* 62 * Helpers 63 */ 64 65int fb_get_color_depth(struct fb_var_screeninfo *var) 66{ 67 if (var->green.length == var->blue.length && 68 var->green.length == var->red.length && 69 !var->green.offset && !var->blue.offset && 70 !var->red.offset) 71 return var->green.length; 72 else 73 return (var->green.length + var->red.length + 74 var->blue.length); 75} 76EXPORT_SYMBOL(fb_get_color_depth); 77 78/* 79 * Drawing helpers. 80 */ 81void fb_iomove_buf_aligned(struct fb_info *info, struct fb_pixmap *buf, 82 u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, 83 u32 height) 84{ 85 int i; 86 87 for (i = height; i--; ) { 88 buf->outbuf(info, dst, src, s_pitch); 89 src += s_pitch; 90 dst += d_pitch; 91 } 92} 93 94void fb_sysmove_buf_aligned(struct fb_info *info, struct fb_pixmap *buf, 95 u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, 96 u32 height) 97{ 98 int i, j; 99 100 for (i = height; i--; ) { 101 for (j = 0; j < s_pitch; j++) 102 dst[j] = src[j]; 103 src += s_pitch; 104 dst += d_pitch; 105 } 106} 107 108void fb_iomove_buf_unaligned(struct fb_info *info, struct fb_pixmap *buf, 109 u8 *dst, u32 d_pitch, u8 *src, u32 idx, 110 u32 height, u32 shift_high, u32 shift_low, 111 u32 mod) 112{ 113 u8 mask = (u8) (0xfff << shift_high), tmp; 114 int i, j; 115 116 for (i = height; i--; ) { 117 for (j = 0; j < idx; j++) { 118 tmp = buf->inbuf(info, dst+j); 119 tmp &= mask; 120 tmp |= *src >> shift_low; 121 buf->outbuf(info, dst+j, &tmp, 1); 122 tmp = *src << shift_high; 123 buf->outbuf(info, dst+j+1, &tmp, 1); 124 src++; 125 } 126 tmp = buf->inbuf(info, dst+idx); 127 tmp &= mask; 128 tmp |= *src >> shift_low; 129 buf->outbuf(info, dst+idx, &tmp, 1); 130 if (shift_high < mod) { 131 tmp = *src << shift_high; 132 buf->outbuf(info, dst+idx+1, &tmp, 1); 133 } 134 src++; 135 dst += d_pitch; 136 } 137} 138 139void fb_sysmove_buf_unaligned(struct fb_info *info, struct fb_pixmap *buf, 140 u8 *dst, u32 d_pitch, u8 *src, u32 idx, 141 u32 height, u32 shift_high, u32 shift_low, 142 u32 mod) 143{ 144 u8 mask = (u8) (0xfff << shift_high), tmp; 145 int i, j; 146 147 for (i = height; i--; ) { 148 for (j = 0; j < idx; j++) { 149 tmp = dst[j]; 150 tmp &= mask; 151 tmp |= *src >> shift_low; 152 dst[j] = tmp; 153 tmp = *src << shift_high; 154 dst[j+1] = tmp; 155 src++; 156 } 157 tmp = dst[idx]; 158 tmp &= mask; 159 tmp |= *src >> shift_low; 160 dst[idx] = tmp; 161 if (shift_high < mod) { 162 tmp = *src << shift_high; 163 dst[idx+1] = tmp; 164 } 165 src++; 166 dst += d_pitch; 167 } 168} 169 170/* 171 * we need to lock this section since fb_cursor 172 * may use fb_imageblit() 173 */ 174char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size) 175{ 176 u32 align = buf->buf_align - 1, offset; 177 char *addr = buf->addr; 178 179 /* If IO mapped, we need to sync before access, no sharing of 180 * the pixmap is done 181 */ 182 if (buf->flags & FB_PIXMAP_IO) { 183 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) 184 info->fbops->fb_sync(info); 185 return addr; 186 } 187 188 /* See if we fit in the remaining pixmap space */ 189 offset = buf->offset + align; 190 offset &= ~align; 191 if (offset + size > buf->size) { 192 /* We do not fit. In order to be able to re-use the buffer, 193 * we must ensure no asynchronous DMA'ing or whatever operation 194 * is in progress, we sync for that. 195 */ 196 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) 197 info->fbops->fb_sync(info); 198 offset = 0; 199 } 200 buf->offset = offset + size; 201 addr += offset; 202 203 return addr; 204} 205 206#ifdef CONFIG_LOGO 207#include <linux/linux_logo.h> 208 209static inline unsigned safe_shift(unsigned d, int n) 210{ 211 return n < 0 ? d >> -n : d << n; 212} 213 214static void fb_set_logocmap(struct fb_info *info, 215 const struct linux_logo *logo) 216{ 217 struct fb_cmap palette_cmap; 218 u16 palette_green[16]; 219 u16 palette_blue[16]; 220 u16 palette_red[16]; 221 int i, j, n; 222 const unsigned char *clut = logo->clut; 223 224 palette_cmap.start = 0; 225 palette_cmap.len = 16; 226 palette_cmap.red = palette_red; 227 palette_cmap.green = palette_green; 228 palette_cmap.blue = palette_blue; 229 palette_cmap.transp = NULL; 230 231 for (i = 0; i < logo->clutsize; i += n) { 232 n = logo->clutsize - i; 233 /* palette_cmap provides space for only 16 colors at once */ 234 if (n > 16) 235 n = 16; 236 palette_cmap.start = 32 + i; 237 palette_cmap.len = n; 238 for (j = 0; j < n; ++j) { 239 palette_cmap.red[j] = clut[0] << 8 | clut[0]; 240 palette_cmap.green[j] = clut[1] << 8 | clut[1]; 241 palette_cmap.blue[j] = clut[2] << 8 | clut[2]; 242 clut += 3; 243 } 244 fb_set_cmap(&palette_cmap, info); 245 } 246} 247 248static void fb_set_logo_truepalette(struct fb_info *info, 249 const struct linux_logo *logo, 250 u32 *palette) 251{ 252 unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; 253 unsigned char redmask, greenmask, bluemask; 254 int redshift, greenshift, blueshift; 255 int i; 256 const unsigned char *clut = logo->clut; 257 258 /* 259 * We have to create a temporary palette since console palette is only 260 * 16 colors long. 261 */ 262 /* Bug: Doesn't obey msb_right ... (who needs that?) */ 263 redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; 264 greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; 265 bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; 266 redshift = info->var.red.offset - (8 - info->var.red.length); 267 greenshift = info->var.green.offset - (8 - info->var.green.length); 268 blueshift = info->var.blue.offset - (8 - info->var.blue.length); 269 270 for ( i = 0; i < logo->clutsize; i++) { 271 palette[i+32] = (safe_shift((clut[0] & redmask), redshift) | 272 safe_shift((clut[1] & greenmask), greenshift) | 273 safe_shift((clut[2] & bluemask), blueshift)); 274 clut += 3; 275 } 276} 277 278static void fb_set_logo_directpalette(struct fb_info *info, 279 const struct linux_logo *logo, 280 u32 *palette) 281{ 282 int redshift, greenshift, blueshift; 283 int i; 284 285 redshift = info->var.red.offset; 286 greenshift = info->var.green.offset; 287 blueshift = info->var.blue.offset; 288 289 for (i = 32; i < logo->clutsize; i++) 290 palette[i] = i << redshift | i << greenshift | i << blueshift; 291} 292 293static void fb_set_logo(struct fb_info *info, 294 const struct linux_logo *logo, u8 *dst, 295 int depth) 296{ 297 int i, j, k, fg = 1; 298 const u8 *src = logo->data; 299 u8 d, xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0; 300 301 if (fb_get_color_depth(&info->var) == 3) 302 fg = 7; 303 304 switch (depth) { 305 case 4: 306 for (i = 0; i < logo->height; i++) 307 for (j = 0; j < logo->width; src++) { 308 *dst++ = *src >> 4; 309 j++; 310 if (j < logo->width) { 311 *dst++ = *src & 0x0f; 312 j++; 313 } 314 } 315 break; 316 case 1: 317 for (i = 0; i < logo->height; i++) { 318 for (j = 0; j < logo->width; src++) { 319 d = *src ^ xor; 320 for (k = 7; k >= 0; k--) { 321 *dst++ = ((d >> k) & 1) ? fg : 0; 322 j++; 323 } 324 } 325 } 326 break; 327 } 328} 329 330/* 331 * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors), 332 * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on 333 * the visual format and color depth of the framebuffer, the DAC, the 334 * pseudo_palette, and the logo data will be adjusted accordingly. 335 * 336 * Case 1 - linux_logo_clut224: 337 * Color exceeds the number of console colors (16), thus we set the hardware DAC 338 * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set. 339 * 340 * For visuals that require color info from the pseudo_palette, we also construct 341 * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags 342 * will be set. 343 * 344 * Case 2 - linux_logo_vga16: 345 * The number of colors just matches the console colors, thus there is no need 346 * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie, 347 * each byte contains color information for two pixels (upper and lower nibble). 348 * To be consistent with fb_imageblit() usage, we therefore separate the two 349 * nibbles into separate bytes. The "depth" flag will be set to 4. 350 * 351 * Case 3 - linux_logo_mono: 352 * This is similar with Case 2. Each byte contains information for 8 pixels. 353 * We isolate each bit and expand each into a byte. The "depth" flag will 354 * be set to 1. 355 */ 356static struct logo_data { 357 int depth; 358 int needs_directpalette; 359 int needs_truepalette; 360 int needs_cmapreset; 361 const struct linux_logo *logo; 362} fb_logo; 363 364int fb_prepare_logo(struct fb_info *info) 365{ 366 int depth = fb_get_color_depth(&info->var); 367 368 memset(&fb_logo, 0, sizeof(struct logo_data)); 369 370 if (info->flags & FBINFO_MISC_TILEBLITTING) 371 return 0; 372 373 if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { 374 depth = info->var.blue.length; 375 if (info->var.red.length < depth) 376 depth = info->var.red.length; 377 if (info->var.green.length < depth) 378 depth = info->var.green.length; 379 } 380 381 if (depth >= 8) { 382 switch (info->fix.visual) { 383 case FB_VISUAL_TRUECOLOR: 384 fb_logo.needs_truepalette = 1; 385 break; 386 case FB_VISUAL_DIRECTCOLOR: 387 fb_logo.needs_directpalette = 1; 388 fb_logo.needs_cmapreset = 1; 389 break; 390 case FB_VISUAL_PSEUDOCOLOR: 391 fb_logo.needs_cmapreset = 1; 392 break; 393 } 394 } 395 396 /* Return if no suitable logo was found */ 397 fb_logo.logo = fb_find_logo(depth); 398 399 if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) { 400 fb_logo.logo = NULL; 401 return 0; 402 } 403 /* What depth we asked for might be different from what we get */ 404 if (fb_logo.logo->type == LINUX_LOGO_CLUT224) 405 fb_logo.depth = 8; 406 else if (fb_logo.logo->type == LINUX_LOGO_VGA16) 407 fb_logo.depth = 4; 408 else 409 fb_logo.depth = 1; 410 return fb_logo.logo->height; 411} 412 413int fb_show_logo(struct fb_info *info) 414{ 415 u32 *palette = NULL, *saved_pseudo_palette = NULL; 416 unsigned char *logo_new = NULL; 417 struct fb_image image; 418 int x; 419 420 /* Return if the frame buffer is not mapped or suspended */ 421 if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING) 422 return 0; 423 424 image.depth = 8; 425 image.data = fb_logo.logo->data; 426 427 if (fb_logo.needs_cmapreset) 428 fb_set_logocmap(info, fb_logo.logo); 429 430 if (fb_logo.needs_truepalette || 431 fb_logo.needs_directpalette) { 432 palette = kmalloc(256 * 4, GFP_KERNEL); 433 if (palette == NULL) 434 return 0; 435 436 if (fb_logo.needs_truepalette) 437 fb_set_logo_truepalette(info, fb_logo.logo, palette); 438 else 439 fb_set_logo_directpalette(info, fb_logo.logo, palette); 440 441 saved_pseudo_palette = info->pseudo_palette; 442 info->pseudo_palette = palette; 443 } 444 445 if (fb_logo.depth <= 4) { 446 logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, 447 GFP_KERNEL); 448 if (logo_new == NULL) { 449 kfree(palette); 450 if (saved_pseudo_palette) 451 info->pseudo_palette = saved_pseudo_palette; 452 return 0; 453 } 454 image.data = logo_new; 455 fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); 456 } 457 458 image.width = fb_logo.logo->width; 459 image.height = fb_logo.logo->height; 460 image.dy = 0; 461 462 for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) && 463 x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) { 464 image.dx = x; 465 info->fbops->fb_imageblit(info, &image); 466 } 467 468 kfree(palette); 469 if (saved_pseudo_palette != NULL) 470 info->pseudo_palette = saved_pseudo_palette; 471 kfree(logo_new); 472 return fb_logo.logo->height; 473} 474#else 475int fb_prepare_logo(struct fb_info *info) { return 0; } 476int fb_show_logo(struct fb_info *info) { return 0; } 477#endif /* CONFIG_LOGO */ 478 479static int fbmem_read_proc(char *buf, char **start, off_t offset, 480 int len, int *eof, void *private) 481{ 482 struct fb_info **fi; 483 int clen; 484 485 clen = 0; 486 for (fi = registered_fb; fi < &registered_fb[FB_MAX] && len < 4000; fi++) 487 if (*fi) 488 clen += sprintf(buf + clen, "%d %s\n", 489 (*fi)->node, 490 (*fi)->fix.id); 491 *start = buf + offset; 492 if (clen > offset) 493 clen -= offset; 494 else 495 clen = 0; 496 return clen < len ? clen : len; 497} 498 499static ssize_t 500fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 501{ 502 unsigned long p = *ppos; 503 struct inode *inode = file->f_dentry->d_inode; 504 int fbidx = iminor(inode); 505 struct fb_info *info = registered_fb[fbidx]; 506 u32 *buffer, *dst; 507 u32 __iomem *src; 508 int c, i, cnt = 0, err = 0; 509 unsigned long total_size; 510 511 if (!info || ! info->screen_base) 512 return -ENODEV; 513 514 if (info->state != FBINFO_STATE_RUNNING) 515 return -EPERM; 516 517 if (info->fbops->fb_read) 518 return info->fbops->fb_read(file, buf, count, ppos); 519 520 total_size = info->screen_size; 521 if (total_size == 0) 522 total_size = info->fix.smem_len; 523 524 if (p >= total_size) 525 return 0; 526 if (count >= total_size) 527 count = total_size; 528 if (count + p > total_size) 529 count = total_size - p; 530 531 cnt = 0; 532 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, 533 GFP_KERNEL); 534 if (!buffer) 535 return -ENOMEM; 536 537 src = (u32 __iomem *) (info->screen_base + p); 538 539 if (info->fbops->fb_sync) 540 info->fbops->fb_sync(info); 541 542 while (count) { 543 c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 544 dst = buffer; 545 for (i = c >> 2; i--; ) 546 *dst++ = fb_readl(src++); 547 if (c & 3) { 548 u8 *dst8 = (u8 *) dst; 549 u8 __iomem *src8 = (u8 __iomem *) src; 550 551 for (i = c & 3; i--;) 552 *dst8++ = fb_readb(src8++); 553 554 src = (u32 __iomem *) src8; 555 } 556 557 if (copy_to_user(buf, buffer, c)) { 558 err = -EFAULT; 559 break; 560 } 561 *ppos += c; 562 buf += c; 563 cnt += c; 564 count -= c; 565 } 566 567 kfree(buffer); 568 return (err) ? err : cnt; 569} 570 571static ssize_t 572fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 573{ 574 unsigned long p = *ppos; 575 struct inode *inode = file->f_dentry->d_inode; 576 int fbidx = iminor(inode); 577 struct fb_info *info = registered_fb[fbidx]; 578 u32 *buffer, *src; 579 u32 __iomem *dst; 580 int c, i, cnt = 0, err; 581 unsigned long total_size; 582 583 if (!info || !info->screen_base) 584 return -ENODEV; 585 586 if (info->state != FBINFO_STATE_RUNNING) 587 return -EPERM; 588 589 if (info->fbops->fb_write) 590 return info->fbops->fb_write(file, buf, count, ppos); 591 592 total_size = info->screen_size; 593 if (total_size == 0) 594 total_size = info->fix.smem_len; 595 596 if (p > total_size) 597 return -ENOSPC; 598 if (count >= total_size) 599 count = total_size; 600 err = 0; 601 if (count + p > total_size) { 602 count = total_size - p; 603 err = -ENOSPC; 604 } 605 cnt = 0; 606 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, 607 GFP_KERNEL); 608 if (!buffer) 609 return -ENOMEM; 610 611 dst = (u32 __iomem *) (info->screen_base + p); 612 613 if (info->fbops->fb_sync) 614 info->fbops->fb_sync(info); 615 616 while (count) { 617 c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 618 src = buffer; 619 if (copy_from_user(src, buf, c)) { 620 err = -EFAULT; 621 break; 622 } 623 for (i = c >> 2; i--; ) 624 fb_writel(*src++, dst++); 625 if (c & 3) { 626 u8 *src8 = (u8 *) src; 627 u8 __iomem *dst8 = (u8 __iomem *) dst; 628 629 for (i = c & 3; i--; ) 630 fb_writeb(*src8++, dst8++); 631 632 dst = (u32 __iomem *) dst8; 633 } 634 *ppos += c; 635 buf += c; 636 cnt += c; 637 count -= c; 638 } 639 kfree(buffer); 640 641 return (err) ? err : cnt; 642} 643 644#ifdef CONFIG_KMOD 645static void try_to_load(int fb) 646{ 647 request_module("fb%d", fb); 648} 649#endif /* CONFIG_KMOD */ 650 651int 652fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) 653{ 654 int xoffset = var->xoffset; 655 int yoffset = var->yoffset; 656 int err; 657 658 if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display || 659 xoffset + info->var.xres > info->var.xres_virtual || 660 yoffset + info->var.yres > info->var.yres_virtual) 661 return -EINVAL; 662 if ((err = info->fbops->fb_pan_display(var, info))) 663 return err; 664 info->var.xoffset = var->xoffset; 665 info->var.yoffset = var->yoffset; 666 if (var->vmode & FB_VMODE_YWRAP) 667 info->var.vmode |= FB_VMODE_YWRAP; 668 else 669 info->var.vmode &= ~FB_VMODE_YWRAP; 670 return 0; 671} 672 673int 674fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) 675{ 676 int err; 677 678 if (var->activate & FB_ACTIVATE_INV_MODE) { 679 struct fb_videomode mode1, mode2; 680 int ret = 0; 681 682 fb_var_to_videomode(&mode1, var); 683 fb_var_to_videomode(&mode2, &info->var); 684 /* make sure we don't delete the videomode of current var */ 685 ret = fb_mode_is_equal(&mode1, &mode2); 686 687 if (!ret) { 688 struct fb_event event; 689 690 event.info = info; 691 event.data = &mode1; 692 ret = notifier_call_chain(&fb_notifier_list, 693 FB_EVENT_MODE_DELETE, &event); 694 } 695 696 if (!ret) 697 fb_delete_videomode(&mode1, &info->modelist); 698 699 return ret; 700 } 701 702 if ((var->activate & FB_ACTIVATE_FORCE) || 703 memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { 704 if (!info->fbops->fb_check_var) { 705 *var = info->var; 706 return 0; 707 } 708 709 if ((err = info->fbops->fb_check_var(var, info))) 710 return err; 711 712 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { 713 struct fb_videomode mode; 714 int err = 0; 715 716 info->var = *var; 717 if (info->fbops->fb_set_par) 718 info->fbops->fb_set_par(info); 719 720 fb_pan_display(info, &info->var); 721 722 fb_set_cmap(&info->cmap, info); 723 724 fb_var_to_videomode(&mode, &info->var); 725 726 if (info->modelist.prev && info->modelist.next && 727 !list_empty(&info->modelist)) 728 err = fb_add_videomode(&mode, &info->modelist); 729 730 if (!err && info->flags & FBINFO_MISC_USEREVENT) { 731 struct fb_event event; 732 733 info->flags &= ~FBINFO_MISC_USEREVENT; 734 event.info = info; 735 notifier_call_chain(&fb_notifier_list, 736 FB_EVENT_MODE_CHANGE, 737 &event); 738 } 739 } 740 } 741 return 0; 742} 743 744int 745fb_blank(struct fb_info *info, int blank) 746{ 747 int ret = -EINVAL; 748 749 if (blank > FB_BLANK_POWERDOWN) 750 blank = FB_BLANK_POWERDOWN; 751 752 if (info->fbops->fb_blank) 753 ret = info->fbops->fb_blank(blank, info); 754 755 if (!ret) { 756 struct fb_event event; 757 758 event.info = info; 759 event.data = &blank; 760 notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); 761 } 762 763 return ret; 764} 765 766static int 767fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 768 unsigned long arg) 769{ 770 int fbidx = iminor(inode); 771 struct fb_info *info = registered_fb[fbidx]; 772 struct fb_ops *fb = info->fbops; 773 struct fb_var_screeninfo var; 774 struct fb_fix_screeninfo fix; 775 struct fb_con2fbmap con2fb; 776 struct fb_cmap_user cmap; 777 struct fb_event event; 778 void __user *argp = (void __user *)arg; 779 int i; 780 781 if (!fb) 782 return -ENODEV; 783 switch (cmd) { 784 case FBIOGET_VSCREENINFO: 785 return copy_to_user(argp, &info->var, 786 sizeof(var)) ? -EFAULT : 0; 787 case FBIOPUT_VSCREENINFO: 788 if (copy_from_user(&var, argp, sizeof(var))) 789 return -EFAULT; 790 acquire_console_sem(); 791 info->flags |= FBINFO_MISC_USEREVENT; 792 i = fb_set_var(info, &var); 793 info->flags &= ~FBINFO_MISC_USEREVENT; 794 release_console_sem(); 795 if (i) return i; 796 if (copy_to_user(argp, &var, sizeof(var))) 797 return -EFAULT; 798 return 0; 799 case FBIOGET_FSCREENINFO: 800 return copy_to_user(argp, &info->fix, 801 sizeof(fix)) ? -EFAULT : 0; 802 case FBIOPUTCMAP: 803 if (copy_from_user(&cmap, argp, sizeof(cmap))) 804 return -EFAULT; 805 return (fb_set_user_cmap(&cmap, info)); 806 case FBIOGETCMAP: 807 if (copy_from_user(&cmap, argp, sizeof(cmap))) 808 return -EFAULT; 809 return fb_cmap_to_user(&info->cmap, &cmap); 810 case FBIOPAN_DISPLAY: 811 if (copy_from_user(&var, argp, sizeof(var))) 812 return -EFAULT; 813 acquire_console_sem(); 814 i = fb_pan_display(info, &var); 815 release_console_sem(); 816 if (i) 817 return i; 818 if (copy_to_user(argp, &var, sizeof(var))) 819 return -EFAULT; 820 return 0; 821 case FBIO_CURSOR: 822 return -EINVAL; 823 case FBIOGET_CON2FBMAP: 824 if (copy_from_user(&con2fb, argp, sizeof(con2fb))) 825 return -EFAULT; 826 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) 827 return -EINVAL; 828 con2fb.framebuffer = -1; 829 event.info = info; 830 event.data = &con2fb; 831 notifier_call_chain(&fb_notifier_list, 832 FB_EVENT_GET_CONSOLE_MAP, &event); 833 return copy_to_user(argp, &con2fb, 834 sizeof(con2fb)) ? -EFAULT : 0; 835 case FBIOPUT_CON2FBMAP: 836 if (copy_from_user(&con2fb, argp, sizeof(con2fb))) 837 return - EFAULT; 838 if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES) 839 return -EINVAL; 840 if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) 841 return -EINVAL; 842#ifdef CONFIG_KMOD 843 if (!registered_fb[con2fb.framebuffer]) 844 try_to_load(con2fb.framebuffer); 845#endif /* CONFIG_KMOD */ 846 if (!registered_fb[con2fb.framebuffer]) 847 return -EINVAL; 848 event.info = info; 849 event.data = &con2fb; 850 return notifier_call_chain(&fb_notifier_list, 851 FB_EVENT_SET_CONSOLE_MAP, 852 &event); 853 case FBIOBLANK: 854 acquire_console_sem(); 855 info->flags |= FBINFO_MISC_USEREVENT; 856 i = fb_blank(info, arg); 857 info->flags &= ~FBINFO_MISC_USEREVENT; 858 release_console_sem(); 859 return i; 860 default: 861 if (fb->fb_ioctl == NULL) 862 return -EINVAL; 863 return fb->fb_ioctl(inode, file, cmd, arg, info); 864 } 865} 866 867#ifdef CONFIG_COMPAT 868static long 869fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 870{ 871 int fbidx = iminor(file->f_dentry->d_inode); 872 struct fb_info *info = registered_fb[fbidx]; 873 struct fb_ops *fb = info->fbops; 874 long ret; 875 876 if (fb->fb_compat_ioctl == NULL) 877 return -ENOIOCTLCMD; 878 lock_kernel(); 879 ret = fb->fb_compat_ioctl(file, cmd, arg, info); 880 unlock_kernel(); 881 return ret; 882} 883#endif 884 885static int 886fb_mmap(struct file *file, struct vm_area_struct * vma) 887{ 888 int fbidx = iminor(file->f_dentry->d_inode); 889 struct fb_info *info = registered_fb[fbidx]; 890 struct fb_ops *fb = info->fbops; 891 unsigned long off; 892#if !defined(__sparc__) || defined(__sparc_v9__) 893 unsigned long start; 894 u32 len; 895#endif 896 897 if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 898 return -EINVAL; 899 off = vma->vm_pgoff << PAGE_SHIFT; 900 if (!fb) 901 return -ENODEV; 902 if (fb->fb_mmap) { 903 int res; 904 lock_kernel(); 905 res = fb->fb_mmap(info, file, vma); 906 unlock_kernel(); 907 return res; 908 } 909 910#if defined(__sparc__) && !defined(__sparc_v9__) 911 /* Should never get here, all fb drivers should have their own 912 mmap routines */ 913 return -EINVAL; 914#else 915 /* !sparc32... */ 916 lock_kernel(); 917 918 /* frame buffer memory */ 919 start = info->fix.smem_start; 920 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); 921 if (off >= len) { 922 /* memory mapped io */ 923 off -= len; 924 if (info->var.accel_flags) { 925 unlock_kernel(); 926 return -EINVAL; 927 } 928 start = info->fix.mmio_start; 929 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); 930 } 931 unlock_kernel(); 932 start &= PAGE_MASK; 933 if ((vma->vm_end - vma->vm_start + off) > len) 934 return -EINVAL; 935 off += start; 936 vma->vm_pgoff = off >> PAGE_SHIFT; 937 /* This is an IO map - tell maydump to skip this VMA */ 938 vma->vm_flags |= VM_IO | VM_RESERVED; 939#if defined(__sparc_v9__) 940 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, 941 vma->vm_end - vma->vm_start, vma->vm_page_prot)) 942 return -EAGAIN; 943#else 944#if defined(__mc68000__) 945#if defined(CONFIG_SUN3) 946 pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; 947#elif defined(CONFIG_MMU) 948 if (CPU_IS_020_OR_030) 949 pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; 950 if (CPU_IS_040_OR_060) { 951 pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; 952 /* Use no-cache mode, serialized */ 953 pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; 954 } 955#endif 956#elif defined(__powerpc__) 957 vma->vm_page_prot = phys_mem_access_prot(file, off, 958 vma->vm_end - vma->vm_start, 959 vma->vm_page_prot); 960#elif defined(__alpha__) 961 /* Caching is off in the I/O space quadrant by design. */ 962#elif defined(__i386__) || defined(__x86_64__) 963 if (boot_cpu_data.x86 > 3) 964 pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; 965#elif defined(__mips__) 966 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 967#elif defined(__hppa__) 968 pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; 969#elif defined(__arm__) || defined(__sh__) || defined(__m32r__) 970 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 971#elif defined(__ia64__) 972 if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) 973 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 974 else 975 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 976#else 977#warning What do we have to do here?? 978#endif 979 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, 980 vma->vm_end - vma->vm_start, vma->vm_page_prot)) 981 return -EAGAIN; 982#endif /* !__sparc_v9__ */ 983 return 0; 984#endif /* !sparc32 */ 985} 986 987static int 988fb_open(struct inode *inode, struct file *file) 989{ 990 int fbidx = iminor(inode); 991 struct fb_info *info; 992 int res = 0; 993 994 if (fbidx >= FB_MAX) 995 return -ENODEV; 996#ifdef CONFIG_KMOD 997 if (!(info = registered_fb[fbidx])) 998 try_to_load(fbidx); 999#endif /* CONFIG_KMOD */ 1000 if (!(info = registered_fb[fbidx])) 1001 return -ENODEV; 1002 if (!try_module_get(info->fbops->owner)) 1003 return -ENODEV; 1004 if (info->fbops->fb_open) { 1005 res = info->fbops->fb_open(info,1); 1006 if (res) 1007 module_put(info->fbops->owner); 1008 } 1009 return res; 1010} 1011 1012static int 1013fb_release(struct inode *inode, struct file *file) 1014{ 1015 int fbidx = iminor(inode); 1016 struct fb_info *info; 1017 1018 lock_kernel(); 1019 info = registered_fb[fbidx]; 1020 if (info->fbops->fb_release) 1021 info->fbops->fb_release(info,1); 1022 module_put(info->fbops->owner); 1023 unlock_kernel(); 1024 return 0; 1025} 1026 1027static struct file_operations fb_fops = { 1028 .owner = THIS_MODULE, 1029 .read = fb_read, 1030 .write = fb_write, 1031 .ioctl = fb_ioctl, 1032#ifdef CONFIG_COMPAT 1033 .compat_ioctl = fb_compat_ioctl, 1034#endif 1035 .mmap = fb_mmap, 1036 .open = fb_open, 1037 .release = fb_release, 1038#ifdef HAVE_ARCH_FB_UNMAPPED_AREA 1039 .get_unmapped_area = get_fb_unmapped_area, 1040#endif 1041}; 1042 1043static struct class_simple *fb_class; 1044 1045/** 1046 * register_framebuffer - registers a frame buffer device 1047 * @fb_info: frame buffer info structure 1048 * 1049 * Registers a frame buffer device @fb_info. 1050 * 1051 * Returns negative errno on error, or zero for success. 1052 * 1053 */ 1054 1055int 1056register_framebuffer(struct fb_info *fb_info) 1057{ 1058 int i; 1059 struct fb_event event; 1060 1061 if (num_registered_fb == FB_MAX) 1062 return -ENXIO; 1063 num_registered_fb++; 1064 for (i = 0 ; i < FB_MAX; i++) 1065 if (!registered_fb[i]) 1066 break; 1067 fb_info->node = i; 1068 1069 fb_info->class_device = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), 1070 fb_info->device, "fb%d", i); 1071 if (IS_ERR(fb_info->class_device)) { 1072 /* Not fatal */ 1073 printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device)); 1074 fb_info->class_device = NULL; 1075 } else 1076 fb_init_class_device(fb_info); 1077 1078 if (fb_info->pixmap.addr == NULL) { 1079 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); 1080 if (fb_info->pixmap.addr) { 1081 fb_info->pixmap.size = FBPIXMAPSIZE; 1082 fb_info->pixmap.buf_align = 1; 1083 fb_info->pixmap.scan_align = 1; 1084 fb_info->pixmap.access_align = 4; 1085 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; 1086 } 1087 } 1088 fb_info->pixmap.offset = 0; 1089 1090 if (!fb_info->modelist.prev || 1091 !fb_info->modelist.next || 1092 list_empty(&fb_info->modelist)) { 1093 struct fb_videomode mode; 1094 1095 INIT_LIST_HEAD(&fb_info->modelist); 1096 fb_var_to_videomode(&mode, &fb_info->var); 1097 fb_add_videomode(&mode, &fb_info->modelist); 1098 } 1099 1100 registered_fb[i] = fb_info; 1101 1102 devfs_mk_cdev(MKDEV(FB_MAJOR, i), 1103 S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); 1104 event.info = fb_info; 1105 notifier_call_chain(&fb_notifier_list, 1106 FB_EVENT_FB_REGISTERED, &event); 1107 return 0; 1108} 1109 1110 1111/** 1112 * unregister_framebuffer - releases a frame buffer device 1113 * @fb_info: frame buffer info structure 1114 * 1115 * Unregisters a frame buffer device @fb_info. 1116 * 1117 * Returns negative errno on error, or zero for success. 1118 * 1119 */ 1120 1121int 1122unregister_framebuffer(struct fb_info *fb_info) 1123{ 1124 int i; 1125 1126 i = fb_info->node; 1127 if (!registered_fb[i]) 1128 return -EINVAL; 1129 devfs_remove("fb/%d", i); 1130 1131 if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1132 kfree(fb_info->pixmap.addr); 1133 fb_destroy_modelist(&fb_info->modelist); 1134 registered_fb[i]=NULL; 1135 num_registered_fb--; 1136 fb_cleanup_class_device(fb_info); 1137 class_simple_device_remove(MKDEV(FB_MAJOR, i)); 1138 return 0; 1139} 1140 1141/** 1142 * fb_register_client - register a client notifier 1143 * @nb: notifier block to callback on events 1144 */ 1145int fb_register_client(struct notifier_block *nb) 1146{ 1147 return notifier_chain_register(&fb_notifier_list, nb); 1148} 1149 1150/** 1151 * fb_unregister_client - unregister a client notifier 1152 * @nb: notifier block to callback on events 1153 */ 1154int fb_unregister_client(struct notifier_block *nb) 1155{ 1156 return notifier_chain_unregister(&fb_notifier_list, nb); 1157} 1158 1159/** 1160 * fb_set_suspend - low level driver signals suspend 1161 * @info: framebuffer affected 1162 * @state: 0 = resuming, !=0 = suspending 1163 * 1164 * This is meant to be used by low level drivers to 1165 * signal suspend/resume to the core & clients. 1166 * It must be called with the console semaphore held 1167 */ 1168void fb_set_suspend(struct fb_info *info, int state) 1169{ 1170 struct fb_event event; 1171 1172 event.info = info; 1173 if (state) { 1174 notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); 1175 info->state = FBINFO_STATE_SUSPENDED; 1176 } else { 1177 info->state = FBINFO_STATE_RUNNING; 1178 notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); 1179 } 1180} 1181 1182/** 1183 * fbmem_init - init frame buffer subsystem 1184 * 1185 * Initialize the frame buffer subsystem. 1186 * 1187 * NOTE: This function is _only_ to be called by drivers/char/mem.c. 1188 * 1189 */ 1190 1191static int __init 1192fbmem_init(void) 1193{ 1194 create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL); 1195 1196 devfs_mk_dir("fb"); 1197 if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) 1198 printk("unable to get major %d for fb devs\n", FB_MAJOR); 1199 1200 fb_class = class_simple_create(THIS_MODULE, "graphics"); 1201 if (IS_ERR(fb_class)) { 1202 printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class)); 1203 fb_class = NULL; 1204 } 1205 return 0; 1206} 1207 1208#ifdef MODULE 1209module_init(fbmem_init); 1210static void __exit 1211fbmem_exit(void) 1212{ 1213 class_simple_destroy(fb_class); 1214} 1215 1216module_exit(fbmem_exit); 1217MODULE_LICENSE("GPL"); 1218MODULE_DESCRIPTION("Framebuffer base"); 1219#else 1220subsys_initcall(fbmem_init); 1221#endif 1222 1223int fb_new_modelist(struct fb_info *info) 1224{ 1225 struct fb_event event; 1226 struct fb_var_screeninfo var = info->var; 1227 struct list_head *pos, *n; 1228 struct fb_modelist *modelist; 1229 struct fb_videomode *m, mode; 1230 int err = 1; 1231 1232 list_for_each_safe(pos, n, &info->modelist) { 1233 modelist = list_entry(pos, struct fb_modelist, list); 1234 m = &modelist->mode; 1235 fb_videomode_to_var(&var, m); 1236 var.activate = FB_ACTIVATE_TEST; 1237 err = fb_set_var(info, &var); 1238 fb_var_to_videomode(&mode, &var); 1239 if (err || !fb_mode_is_equal(m, &mode)) { 1240 list_del(pos); 1241 kfree(pos); 1242 } 1243 } 1244 1245 err = 1; 1246 1247 if (!list_empty(&info->modelist)) { 1248 event.info = info; 1249 err = notifier_call_chain(&fb_notifier_list, 1250 FB_EVENT_NEW_MODELIST, 1251 &event); 1252 } 1253 1254 return err; 1255} 1256 1257static char *video_options[FB_MAX]; 1258static int ofonly; 1259 1260extern const char *global_mode_option; 1261 1262/** 1263 * fb_get_options - get kernel boot parameters 1264 * @name: framebuffer name as it would appear in 1265 * the boot parameter line 1266 * (video=<name>:<options>) 1267 * @option: the option will be stored here 1268 * 1269 * NOTE: Needed to maintain backwards compatibility 1270 */ 1271int fb_get_options(char *name, char **option) 1272{ 1273 char *opt, *options = NULL; 1274 int opt_len, retval = 0; 1275 int name_len = strlen(name), i; 1276 1277 if (name_len && ofonly && strncmp(name, "offb", 4)) 1278 retval = 1; 1279 1280 if (name_len && !retval) { 1281 for (i = 0; i < FB_MAX; i++) { 1282 if (video_options[i] == NULL) 1283 continue; 1284 opt_len = strlen(video_options[i]); 1285 if (!opt_len) 1286 continue; 1287 opt = video_options[i]; 1288 if (!strncmp(name, opt, name_len) && 1289 opt[name_len] == ':') 1290 options = opt + name_len + 1; 1291 } 1292 } 1293 if (options && !strncmp(options, "off", 3)) 1294 retval = 1; 1295 1296 if (option) 1297 *option = options; 1298 1299 return retval; 1300} 1301 1302/** 1303 * video_setup - process command line options 1304 * @options: string of options 1305 * 1306 * Process command line options for frame buffer subsystem. 1307 * 1308 * NOTE: This function is a __setup and __init function. 1309 * It only stores the options. Drivers have to call 1310 * fb_get_options() as necessary. 1311 * 1312 * Returns zero. 1313 * 1314 */ 1315static int __init video_setup(char *options) 1316{ 1317 int i, global = 0; 1318 1319 if (!options || !*options) 1320 global = 1; 1321 1322 if (!global && !strncmp(options, "ofonly", 6)) { 1323 ofonly = 1; 1324 global = 1; 1325 } 1326 1327 if (!global && !strstr(options, "fb:")) { 1328 global_mode_option = options; 1329 global = 1; 1330 } 1331 1332 if (!global) { 1333 for (i = 0; i < FB_MAX; i++) { 1334 if (video_options[i] == NULL) { 1335 video_options[i] = options; 1336 break; 1337 } 1338 1339 } 1340 } 1341 1342 return 0; 1343} 1344__setup("video=", video_setup); 1345 1346 /* 1347 * Visible symbols for modules 1348 */ 1349 1350EXPORT_SYMBOL(register_framebuffer); 1351EXPORT_SYMBOL(unregister_framebuffer); 1352EXPORT_SYMBOL(num_registered_fb); 1353EXPORT_SYMBOL(registered_fb); 1354EXPORT_SYMBOL(fb_prepare_logo); 1355EXPORT_SYMBOL(fb_show_logo); 1356EXPORT_SYMBOL(fb_set_var); 1357EXPORT_SYMBOL(fb_blank); 1358EXPORT_SYMBOL(fb_pan_display); 1359EXPORT_SYMBOL(fb_get_buffer_offset); 1360EXPORT_SYMBOL(fb_iomove_buf_unaligned); 1361EXPORT_SYMBOL(fb_iomove_buf_aligned); 1362EXPORT_SYMBOL(fb_sysmove_buf_unaligned); 1363EXPORT_SYMBOL(fb_sysmove_buf_aligned); 1364EXPORT_SYMBOL(fb_set_suspend); 1365EXPORT_SYMBOL(fb_register_client); 1366EXPORT_SYMBOL(fb_unregister_client); 1367EXPORT_SYMBOL(fb_get_options); 1368EXPORT_SYMBOL(fb_new_modelist); 1369 1370MODULE_LICENSE("GPL");