Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.32-rc4 1839 lines 44 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/module.h> 15 16#include <linux/compat.h> 17#include <linux/types.h> 18#include <linux/errno.h> 19#include <linux/kernel.h> 20#include <linux/major.h> 21#include <linux/slab.h> 22#include <linux/mm.h> 23#include <linux/mman.h> 24#include <linux/vt.h> 25#include <linux/init.h> 26#include <linux/linux_logo.h> 27#include <linux/proc_fs.h> 28#include <linux/seq_file.h> 29#include <linux/console.h> 30#include <linux/kmod.h> 31#include <linux/err.h> 32#include <linux/device.h> 33#include <linux/efi.h> 34#include <linux/fb.h> 35 36#include <asm/fb.h> 37 38 39 /* 40 * Frame buffer device initialization and setup routines 41 */ 42 43#define FBPIXMAPSIZE (1024 * 8) 44 45struct fb_info *registered_fb[FB_MAX] __read_mostly; 46int num_registered_fb __read_mostly; 47 48int lock_fb_info(struct fb_info *info) 49{ 50 mutex_lock(&info->lock); 51 if (!info->fbops) { 52 mutex_unlock(&info->lock); 53 return 0; 54 } 55 return 1; 56} 57EXPORT_SYMBOL(lock_fb_info); 58 59/* 60 * Helpers 61 */ 62 63int fb_get_color_depth(struct fb_var_screeninfo *var, 64 struct fb_fix_screeninfo *fix) 65{ 66 int depth = 0; 67 68 if (fix->visual == FB_VISUAL_MONO01 || 69 fix->visual == FB_VISUAL_MONO10) 70 depth = 1; 71 else { 72 if (var->green.length == var->blue.length && 73 var->green.length == var->red.length && 74 var->green.offset == var->blue.offset && 75 var->green.offset == var->red.offset) 76 depth = var->green.length; 77 else 78 depth = var->green.length + var->red.length + 79 var->blue.length; 80 } 81 82 return depth; 83} 84EXPORT_SYMBOL(fb_get_color_depth); 85 86/* 87 * Data padding functions. 88 */ 89void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height) 90{ 91 __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height); 92} 93EXPORT_SYMBOL(fb_pad_aligned_buffer); 94 95void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height, 96 u32 shift_high, u32 shift_low, u32 mod) 97{ 98 u8 mask = (u8) (0xfff << shift_high), tmp; 99 int i, j; 100 101 for (i = height; i--; ) { 102 for (j = 0; j < idx; j++) { 103 tmp = dst[j]; 104 tmp &= mask; 105 tmp |= *src >> shift_low; 106 dst[j] = tmp; 107 tmp = *src << shift_high; 108 dst[j+1] = tmp; 109 src++; 110 } 111 tmp = dst[idx]; 112 tmp &= mask; 113 tmp |= *src >> shift_low; 114 dst[idx] = tmp; 115 if (shift_high < mod) { 116 tmp = *src << shift_high; 117 dst[idx+1] = tmp; 118 } 119 src++; 120 dst += d_pitch; 121 } 122} 123EXPORT_SYMBOL(fb_pad_unaligned_buffer); 124 125/* 126 * we need to lock this section since fb_cursor 127 * may use fb_imageblit() 128 */ 129char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size) 130{ 131 u32 align = buf->buf_align - 1, offset; 132 char *addr = buf->addr; 133 134 /* If IO mapped, we need to sync before access, no sharing of 135 * the pixmap is done 136 */ 137 if (buf->flags & FB_PIXMAP_IO) { 138 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) 139 info->fbops->fb_sync(info); 140 return addr; 141 } 142 143 /* See if we fit in the remaining pixmap space */ 144 offset = buf->offset + align; 145 offset &= ~align; 146 if (offset + size > buf->size) { 147 /* We do not fit. In order to be able to re-use the buffer, 148 * we must ensure no asynchronous DMA'ing or whatever operation 149 * is in progress, we sync for that. 150 */ 151 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) 152 info->fbops->fb_sync(info); 153 offset = 0; 154 } 155 buf->offset = offset + size; 156 addr += offset; 157 158 return addr; 159} 160 161#ifdef CONFIG_LOGO 162 163static inline unsigned safe_shift(unsigned d, int n) 164{ 165 return n < 0 ? d >> -n : d << n; 166} 167 168static void fb_set_logocmap(struct fb_info *info, 169 const struct linux_logo *logo) 170{ 171 struct fb_cmap palette_cmap; 172 u16 palette_green[16]; 173 u16 palette_blue[16]; 174 u16 palette_red[16]; 175 int i, j, n; 176 const unsigned char *clut = logo->clut; 177 178 palette_cmap.start = 0; 179 palette_cmap.len = 16; 180 palette_cmap.red = palette_red; 181 palette_cmap.green = palette_green; 182 palette_cmap.blue = palette_blue; 183 palette_cmap.transp = NULL; 184 185 for (i = 0; i < logo->clutsize; i += n) { 186 n = logo->clutsize - i; 187 /* palette_cmap provides space for only 16 colors at once */ 188 if (n > 16) 189 n = 16; 190 palette_cmap.start = 32 + i; 191 palette_cmap.len = n; 192 for (j = 0; j < n; ++j) { 193 palette_cmap.red[j] = clut[0] << 8 | clut[0]; 194 palette_cmap.green[j] = clut[1] << 8 | clut[1]; 195 palette_cmap.blue[j] = clut[2] << 8 | clut[2]; 196 clut += 3; 197 } 198 fb_set_cmap(&palette_cmap, info); 199 } 200} 201 202static void fb_set_logo_truepalette(struct fb_info *info, 203 const struct linux_logo *logo, 204 u32 *palette) 205{ 206 static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; 207 unsigned char redmask, greenmask, bluemask; 208 int redshift, greenshift, blueshift; 209 int i; 210 const unsigned char *clut = logo->clut; 211 212 /* 213 * We have to create a temporary palette since console palette is only 214 * 16 colors long. 215 */ 216 /* Bug: Doesn't obey msb_right ... (who needs that?) */ 217 redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; 218 greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; 219 bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; 220 redshift = info->var.red.offset - (8 - info->var.red.length); 221 greenshift = info->var.green.offset - (8 - info->var.green.length); 222 blueshift = info->var.blue.offset - (8 - info->var.blue.length); 223 224 for ( i = 0; i < logo->clutsize; i++) { 225 palette[i+32] = (safe_shift((clut[0] & redmask), redshift) | 226 safe_shift((clut[1] & greenmask), greenshift) | 227 safe_shift((clut[2] & bluemask), blueshift)); 228 clut += 3; 229 } 230} 231 232static void fb_set_logo_directpalette(struct fb_info *info, 233 const struct linux_logo *logo, 234 u32 *palette) 235{ 236 int redshift, greenshift, blueshift; 237 int i; 238 239 redshift = info->var.red.offset; 240 greenshift = info->var.green.offset; 241 blueshift = info->var.blue.offset; 242 243 for (i = 32; i < 32 + logo->clutsize; i++) 244 palette[i] = i << redshift | i << greenshift | i << blueshift; 245} 246 247static void fb_set_logo(struct fb_info *info, 248 const struct linux_logo *logo, u8 *dst, 249 int depth) 250{ 251 int i, j, k; 252 const u8 *src = logo->data; 253 u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0; 254 u8 fg = 1, d; 255 256 switch (fb_get_color_depth(&info->var, &info->fix)) { 257 case 1: 258 fg = 1; 259 break; 260 case 2: 261 fg = 3; 262 break; 263 default: 264 fg = 7; 265 break; 266 } 267 268 if (info->fix.visual == FB_VISUAL_MONO01 || 269 info->fix.visual == FB_VISUAL_MONO10) 270 fg = ~((u8) (0xfff << info->var.green.length)); 271 272 switch (depth) { 273 case 4: 274 for (i = 0; i < logo->height; i++) 275 for (j = 0; j < logo->width; src++) { 276 *dst++ = *src >> 4; 277 j++; 278 if (j < logo->width) { 279 *dst++ = *src & 0x0f; 280 j++; 281 } 282 } 283 break; 284 case 1: 285 for (i = 0; i < logo->height; i++) { 286 for (j = 0; j < logo->width; src++) { 287 d = *src ^ xor; 288 for (k = 7; k >= 0; k--) { 289 *dst++ = ((d >> k) & 1) ? fg : 0; 290 j++; 291 } 292 } 293 } 294 break; 295 } 296} 297 298/* 299 * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors), 300 * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on 301 * the visual format and color depth of the framebuffer, the DAC, the 302 * pseudo_palette, and the logo data will be adjusted accordingly. 303 * 304 * Case 1 - linux_logo_clut224: 305 * Color exceeds the number of console colors (16), thus we set the hardware DAC 306 * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set. 307 * 308 * For visuals that require color info from the pseudo_palette, we also construct 309 * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags 310 * will be set. 311 * 312 * Case 2 - linux_logo_vga16: 313 * The number of colors just matches the console colors, thus there is no need 314 * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie, 315 * each byte contains color information for two pixels (upper and lower nibble). 316 * To be consistent with fb_imageblit() usage, we therefore separate the two 317 * nibbles into separate bytes. The "depth" flag will be set to 4. 318 * 319 * Case 3 - linux_logo_mono: 320 * This is similar with Case 2. Each byte contains information for 8 pixels. 321 * We isolate each bit and expand each into a byte. The "depth" flag will 322 * be set to 1. 323 */ 324static struct logo_data { 325 int depth; 326 int needs_directpalette; 327 int needs_truepalette; 328 int needs_cmapreset; 329 const struct linux_logo *logo; 330} fb_logo __read_mostly; 331 332static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height) 333{ 334 u32 size = width * height, i; 335 336 out += size - 1; 337 338 for (i = size; i--; ) 339 *out-- = *in++; 340} 341 342static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height) 343{ 344 int i, j, h = height - 1; 345 346 for (i = 0; i < height; i++) 347 for (j = 0; j < width; j++) 348 out[height * j + h - i] = *in++; 349} 350 351static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height) 352{ 353 int i, j, w = width - 1; 354 355 for (i = 0; i < height; i++) 356 for (j = 0; j < width; j++) 357 out[height * (w - j) + i] = *in++; 358} 359 360static void fb_rotate_logo(struct fb_info *info, u8 *dst, 361 struct fb_image *image, int rotate) 362{ 363 u32 tmp; 364 365 if (rotate == FB_ROTATE_UD) { 366 fb_rotate_logo_ud(image->data, dst, image->width, 367 image->height); 368 image->dx = info->var.xres - image->width - image->dx; 369 image->dy = info->var.yres - image->height - image->dy; 370 } else if (rotate == FB_ROTATE_CW) { 371 fb_rotate_logo_cw(image->data, dst, image->width, 372 image->height); 373 tmp = image->width; 374 image->width = image->height; 375 image->height = tmp; 376 tmp = image->dy; 377 image->dy = image->dx; 378 image->dx = info->var.xres - image->width - tmp; 379 } else if (rotate == FB_ROTATE_CCW) { 380 fb_rotate_logo_ccw(image->data, dst, image->width, 381 image->height); 382 tmp = image->width; 383 image->width = image->height; 384 image->height = tmp; 385 tmp = image->dx; 386 image->dx = image->dy; 387 image->dy = info->var.yres - image->height - tmp; 388 } 389 390 image->data = dst; 391} 392 393static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, 394 int rotate, unsigned int num) 395{ 396 unsigned int x; 397 398 if (rotate == FB_ROTATE_UR) { 399 for (x = 0; 400 x < num && image->dx + image->width <= info->var.xres; 401 x++) { 402 info->fbops->fb_imageblit(info, image); 403 image->dx += image->width + 8; 404 } 405 } else if (rotate == FB_ROTATE_UD) { 406 for (x = 0; x < num && image->dx >= 0; x++) { 407 info->fbops->fb_imageblit(info, image); 408 image->dx -= image->width + 8; 409 } 410 } else if (rotate == FB_ROTATE_CW) { 411 for (x = 0; 412 x < num && image->dy + image->height <= info->var.yres; 413 x++) { 414 info->fbops->fb_imageblit(info, image); 415 image->dy += image->height + 8; 416 } 417 } else if (rotate == FB_ROTATE_CCW) { 418 for (x = 0; x < num && image->dy >= 0; x++) { 419 info->fbops->fb_imageblit(info, image); 420 image->dy -= image->height + 8; 421 } 422 } 423} 424 425static int fb_show_logo_line(struct fb_info *info, int rotate, 426 const struct linux_logo *logo, int y, 427 unsigned int n) 428{ 429 u32 *palette = NULL, *saved_pseudo_palette = NULL; 430 unsigned char *logo_new = NULL, *logo_rotate = NULL; 431 struct fb_image image; 432 433 /* Return if the frame buffer is not mapped or suspended */ 434 if (logo == NULL || info->state != FBINFO_STATE_RUNNING || 435 info->flags & FBINFO_MODULE) 436 return 0; 437 438 image.depth = 8; 439 image.data = logo->data; 440 441 if (fb_logo.needs_cmapreset) 442 fb_set_logocmap(info, logo); 443 444 if (fb_logo.needs_truepalette || 445 fb_logo.needs_directpalette) { 446 palette = kmalloc(256 * 4, GFP_KERNEL); 447 if (palette == NULL) 448 return 0; 449 450 if (fb_logo.needs_truepalette) 451 fb_set_logo_truepalette(info, logo, palette); 452 else 453 fb_set_logo_directpalette(info, logo, palette); 454 455 saved_pseudo_palette = info->pseudo_palette; 456 info->pseudo_palette = palette; 457 } 458 459 if (fb_logo.depth <= 4) { 460 logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); 461 if (logo_new == NULL) { 462 kfree(palette); 463 if (saved_pseudo_palette) 464 info->pseudo_palette = saved_pseudo_palette; 465 return 0; 466 } 467 image.data = logo_new; 468 fb_set_logo(info, logo, logo_new, fb_logo.depth); 469 } 470 471 image.dx = 0; 472 image.dy = y; 473 image.width = logo->width; 474 image.height = logo->height; 475 476 if (rotate) { 477 logo_rotate = kmalloc(logo->width * 478 logo->height, GFP_KERNEL); 479 if (logo_rotate) 480 fb_rotate_logo(info, logo_rotate, &image, rotate); 481 } 482 483 fb_do_show_logo(info, &image, rotate, n); 484 485 kfree(palette); 486 if (saved_pseudo_palette != NULL) 487 info->pseudo_palette = saved_pseudo_palette; 488 kfree(logo_new); 489 kfree(logo_rotate); 490 return logo->height; 491} 492 493 494#ifdef CONFIG_FB_LOGO_EXTRA 495 496#define FB_LOGO_EX_NUM_MAX 10 497static struct logo_data_extra { 498 const struct linux_logo *logo; 499 unsigned int n; 500} fb_logo_ex[FB_LOGO_EX_NUM_MAX]; 501static unsigned int fb_logo_ex_num; 502 503void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n) 504{ 505 if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX) 506 return; 507 508 fb_logo_ex[fb_logo_ex_num].logo = logo; 509 fb_logo_ex[fb_logo_ex_num].n = n; 510 fb_logo_ex_num++; 511} 512 513static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height, 514 unsigned int yres) 515{ 516 unsigned int i; 517 518 /* FIXME: logo_ex supports only truecolor fb. */ 519 if (info->fix.visual != FB_VISUAL_TRUECOLOR) 520 fb_logo_ex_num = 0; 521 522 for (i = 0; i < fb_logo_ex_num; i++) { 523 if (fb_logo_ex[i].logo->type != fb_logo.logo->type) { 524 fb_logo_ex[i].logo = NULL; 525 continue; 526 } 527 height += fb_logo_ex[i].logo->height; 528 if (height > yres) { 529 height -= fb_logo_ex[i].logo->height; 530 fb_logo_ex_num = i; 531 break; 532 } 533 } 534 return height; 535} 536 537static int fb_show_extra_logos(struct fb_info *info, int y, int rotate) 538{ 539 unsigned int i; 540 541 for (i = 0; i < fb_logo_ex_num; i++) 542 y += fb_show_logo_line(info, rotate, 543 fb_logo_ex[i].logo, y, fb_logo_ex[i].n); 544 545 return y; 546} 547 548#else /* !CONFIG_FB_LOGO_EXTRA */ 549 550static inline int fb_prepare_extra_logos(struct fb_info *info, 551 unsigned int height, 552 unsigned int yres) 553{ 554 return height; 555} 556 557static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate) 558{ 559 return y; 560} 561 562#endif /* CONFIG_FB_LOGO_EXTRA */ 563 564 565int fb_prepare_logo(struct fb_info *info, int rotate) 566{ 567 int depth = fb_get_color_depth(&info->var, &info->fix); 568 unsigned int yres; 569 570 memset(&fb_logo, 0, sizeof(struct logo_data)); 571 572 if (info->flags & FBINFO_MISC_TILEBLITTING || 573 info->flags & FBINFO_MODULE) 574 return 0; 575 576 if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { 577 depth = info->var.blue.length; 578 if (info->var.red.length < depth) 579 depth = info->var.red.length; 580 if (info->var.green.length < depth) 581 depth = info->var.green.length; 582 } 583 584 if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) { 585 /* assume console colormap */ 586 depth = 4; 587 } 588 589 /* Return if no suitable logo was found */ 590 fb_logo.logo = fb_find_logo(depth); 591 592 if (!fb_logo.logo) { 593 return 0; 594 } 595 596 if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD) 597 yres = info->var.yres; 598 else 599 yres = info->var.xres; 600 601 if (fb_logo.logo->height > yres) { 602 fb_logo.logo = NULL; 603 return 0; 604 } 605 606 /* What depth we asked for might be different from what we get */ 607 if (fb_logo.logo->type == LINUX_LOGO_CLUT224) 608 fb_logo.depth = 8; 609 else if (fb_logo.logo->type == LINUX_LOGO_VGA16) 610 fb_logo.depth = 4; 611 else 612 fb_logo.depth = 1; 613 614 615 if (fb_logo.depth > 4 && depth > 4) { 616 switch (info->fix.visual) { 617 case FB_VISUAL_TRUECOLOR: 618 fb_logo.needs_truepalette = 1; 619 break; 620 case FB_VISUAL_DIRECTCOLOR: 621 fb_logo.needs_directpalette = 1; 622 fb_logo.needs_cmapreset = 1; 623 break; 624 case FB_VISUAL_PSEUDOCOLOR: 625 fb_logo.needs_cmapreset = 1; 626 break; 627 } 628 } 629 630 return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); 631} 632 633int fb_show_logo(struct fb_info *info, int rotate) 634{ 635 int y; 636 637 y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, 638 num_online_cpus()); 639 y = fb_show_extra_logos(info, y, rotate); 640 641 return y; 642} 643#else 644int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; } 645int fb_show_logo(struct fb_info *info, int rotate) { return 0; } 646#endif /* CONFIG_LOGO */ 647 648static void *fb_seq_start(struct seq_file *m, loff_t *pos) 649{ 650 return (*pos < FB_MAX) ? pos : NULL; 651} 652 653static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) 654{ 655 (*pos)++; 656 return (*pos < FB_MAX) ? pos : NULL; 657} 658 659static void fb_seq_stop(struct seq_file *m, void *v) 660{ 661} 662 663static int fb_seq_show(struct seq_file *m, void *v) 664{ 665 int i = *(loff_t *)v; 666 struct fb_info *fi = registered_fb[i]; 667 668 if (fi) 669 seq_printf(m, "%d %s\n", fi->node, fi->fix.id); 670 return 0; 671} 672 673static const struct seq_operations proc_fb_seq_ops = { 674 .start = fb_seq_start, 675 .next = fb_seq_next, 676 .stop = fb_seq_stop, 677 .show = fb_seq_show, 678}; 679 680static int proc_fb_open(struct inode *inode, struct file *file) 681{ 682 return seq_open(file, &proc_fb_seq_ops); 683} 684 685static const struct file_operations fb_proc_fops = { 686 .owner = THIS_MODULE, 687 .open = proc_fb_open, 688 .read = seq_read, 689 .llseek = seq_lseek, 690 .release = seq_release, 691}; 692 693static ssize_t 694fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 695{ 696 unsigned long p = *ppos; 697 struct inode *inode = file->f_path.dentry->d_inode; 698 int fbidx = iminor(inode); 699 struct fb_info *info = registered_fb[fbidx]; 700 u32 *buffer, *dst; 701 u32 __iomem *src; 702 int c, i, cnt = 0, err = 0; 703 unsigned long total_size; 704 705 if (!info || ! info->screen_base) 706 return -ENODEV; 707 708 if (info->state != FBINFO_STATE_RUNNING) 709 return -EPERM; 710 711 if (info->fbops->fb_read) 712 return info->fbops->fb_read(info, buf, count, ppos); 713 714 total_size = info->screen_size; 715 716 if (total_size == 0) 717 total_size = info->fix.smem_len; 718 719 if (p >= total_size) 720 return 0; 721 722 if (count >= total_size) 723 count = total_size; 724 725 if (count + p > total_size) 726 count = total_size - p; 727 728 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, 729 GFP_KERNEL); 730 if (!buffer) 731 return -ENOMEM; 732 733 src = (u32 __iomem *) (info->screen_base + p); 734 735 if (info->fbops->fb_sync) 736 info->fbops->fb_sync(info); 737 738 while (count) { 739 c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 740 dst = buffer; 741 for (i = c >> 2; i--; ) 742 *dst++ = fb_readl(src++); 743 if (c & 3) { 744 u8 *dst8 = (u8 *) dst; 745 u8 __iomem *src8 = (u8 __iomem *) src; 746 747 for (i = c & 3; i--;) 748 *dst8++ = fb_readb(src8++); 749 750 src = (u32 __iomem *) src8; 751 } 752 753 if (copy_to_user(buf, buffer, c)) { 754 err = -EFAULT; 755 break; 756 } 757 *ppos += c; 758 buf += c; 759 cnt += c; 760 count -= c; 761 } 762 763 kfree(buffer); 764 765 return (err) ? err : cnt; 766} 767 768static ssize_t 769fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 770{ 771 unsigned long p = *ppos; 772 struct inode *inode = file->f_path.dentry->d_inode; 773 int fbidx = iminor(inode); 774 struct fb_info *info = registered_fb[fbidx]; 775 u32 *buffer, *src; 776 u32 __iomem *dst; 777 int c, i, cnt = 0, err = 0; 778 unsigned long total_size; 779 780 if (!info || !info->screen_base) 781 return -ENODEV; 782 783 if (info->state != FBINFO_STATE_RUNNING) 784 return -EPERM; 785 786 if (info->fbops->fb_write) 787 return info->fbops->fb_write(info, buf, count, ppos); 788 789 total_size = info->screen_size; 790 791 if (total_size == 0) 792 total_size = info->fix.smem_len; 793 794 if (p > total_size) 795 return -EFBIG; 796 797 if (count > total_size) { 798 err = -EFBIG; 799 count = total_size; 800 } 801 802 if (count + p > total_size) { 803 if (!err) 804 err = -ENOSPC; 805 806 count = total_size - p; 807 } 808 809 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, 810 GFP_KERNEL); 811 if (!buffer) 812 return -ENOMEM; 813 814 dst = (u32 __iomem *) (info->screen_base + p); 815 816 if (info->fbops->fb_sync) 817 info->fbops->fb_sync(info); 818 819 while (count) { 820 c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 821 src = buffer; 822 823 if (copy_from_user(src, buf, c)) { 824 err = -EFAULT; 825 break; 826 } 827 828 for (i = c >> 2; i--; ) 829 fb_writel(*src++, dst++); 830 831 if (c & 3) { 832 u8 *src8 = (u8 *) src; 833 u8 __iomem *dst8 = (u8 __iomem *) dst; 834 835 for (i = c & 3; i--; ) 836 fb_writeb(*src8++, dst8++); 837 838 dst = (u32 __iomem *) dst8; 839 } 840 841 *ppos += c; 842 buf += c; 843 cnt += c; 844 count -= c; 845 } 846 847 kfree(buffer); 848 849 return (cnt) ? cnt : err; 850} 851 852int 853fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) 854{ 855 struct fb_fix_screeninfo *fix = &info->fix; 856 unsigned int yres = info->var.yres; 857 int err = 0; 858 859 if (var->yoffset > 0) { 860 if (var->vmode & FB_VMODE_YWRAP) { 861 if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep)) 862 err = -EINVAL; 863 else 864 yres = 0; 865 } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep)) 866 err = -EINVAL; 867 } 868 869 if (var->xoffset > 0 && (!fix->xpanstep || 870 (var->xoffset % fix->xpanstep))) 871 err = -EINVAL; 872 873 if (err || !info->fbops->fb_pan_display || 874 var->yoffset > info->var.yres_virtual - yres || 875 var->xoffset > info->var.xres_virtual - info->var.xres) 876 return -EINVAL; 877 878 if ((err = info->fbops->fb_pan_display(var, info))) 879 return err; 880 info->var.xoffset = var->xoffset; 881 info->var.yoffset = var->yoffset; 882 if (var->vmode & FB_VMODE_YWRAP) 883 info->var.vmode |= FB_VMODE_YWRAP; 884 else 885 info->var.vmode &= ~FB_VMODE_YWRAP; 886 return 0; 887} 888 889static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var, 890 u32 activate) 891{ 892 struct fb_event event; 893 struct fb_blit_caps caps, fbcaps; 894 int err = 0; 895 896 memset(&caps, 0, sizeof(caps)); 897 memset(&fbcaps, 0, sizeof(fbcaps)); 898 caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0; 899 event.info = info; 900 event.data = &caps; 901 fb_notifier_call_chain(FB_EVENT_GET_REQ, &event); 902 info->fbops->fb_get_caps(info, &fbcaps, var); 903 904 if (((fbcaps.x ^ caps.x) & caps.x) || 905 ((fbcaps.y ^ caps.y) & caps.y) || 906 (fbcaps.len < caps.len)) 907 err = -EINVAL; 908 909 return err; 910} 911 912int 913fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) 914{ 915 int flags = info->flags; 916 int ret = 0; 917 918 if (var->activate & FB_ACTIVATE_INV_MODE) { 919 struct fb_videomode mode1, mode2; 920 921 fb_var_to_videomode(&mode1, var); 922 fb_var_to_videomode(&mode2, &info->var); 923 /* make sure we don't delete the videomode of current var */ 924 ret = fb_mode_is_equal(&mode1, &mode2); 925 926 if (!ret) { 927 struct fb_event event; 928 929 event.info = info; 930 event.data = &mode1; 931 ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event); 932 } 933 934 if (!ret) 935 fb_delete_videomode(&mode1, &info->modelist); 936 937 938 ret = (ret) ? -EINVAL : 0; 939 goto done; 940 } 941 942 if ((var->activate & FB_ACTIVATE_FORCE) || 943 memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { 944 u32 activate = var->activate; 945 946 if (!info->fbops->fb_check_var) { 947 *var = info->var; 948 goto done; 949 } 950 951 ret = info->fbops->fb_check_var(var, info); 952 953 if (ret) 954 goto done; 955 956 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { 957 struct fb_var_screeninfo old_var; 958 struct fb_videomode mode; 959 960 if (info->fbops->fb_get_caps) { 961 ret = fb_check_caps(info, var, activate); 962 963 if (ret) 964 goto done; 965 } 966 967 old_var = info->var; 968 info->var = *var; 969 970 if (info->fbops->fb_set_par) { 971 ret = info->fbops->fb_set_par(info); 972 973 if (ret) { 974 info->var = old_var; 975 printk(KERN_WARNING "detected " 976 "fb_set_par error, " 977 "error code: %d\n", ret); 978 goto done; 979 } 980 } 981 982 fb_pan_display(info, &info->var); 983 fb_set_cmap(&info->cmap, info); 984 fb_var_to_videomode(&mode, &info->var); 985 986 if (info->modelist.prev && info->modelist.next && 987 !list_empty(&info->modelist)) 988 ret = fb_add_videomode(&mode, &info->modelist); 989 990 if (!ret && (flags & FBINFO_MISC_USEREVENT)) { 991 struct fb_event event; 992 int evnt = (activate & FB_ACTIVATE_ALL) ? 993 FB_EVENT_MODE_CHANGE_ALL : 994 FB_EVENT_MODE_CHANGE; 995 996 info->flags &= ~FBINFO_MISC_USEREVENT; 997 event.info = info; 998 event.data = &mode; 999 fb_notifier_call_chain(evnt, &event); 1000 } 1001 } 1002 } 1003 1004 done: 1005 return ret; 1006} 1007 1008int 1009fb_blank(struct fb_info *info, int blank) 1010{ 1011 int ret = -EINVAL; 1012 1013 if (blank > FB_BLANK_POWERDOWN) 1014 blank = FB_BLANK_POWERDOWN; 1015 1016 if (info->fbops->fb_blank) 1017 ret = info->fbops->fb_blank(blank, info); 1018 1019 if (!ret) { 1020 struct fb_event event; 1021 1022 event.info = info; 1023 event.data = &blank; 1024 fb_notifier_call_chain(FB_EVENT_BLANK, &event); 1025 } 1026 1027 return ret; 1028} 1029 1030static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, 1031 unsigned long arg) 1032{ 1033 struct fb_ops *fb; 1034 struct fb_var_screeninfo var; 1035 struct fb_fix_screeninfo fix; 1036 struct fb_con2fbmap con2fb; 1037 struct fb_cmap cmap_from; 1038 struct fb_cmap_user cmap; 1039 struct fb_event event; 1040 void __user *argp = (void __user *)arg; 1041 long ret = 0; 1042 1043 switch (cmd) { 1044 case FBIOGET_VSCREENINFO: 1045 if (!lock_fb_info(info)) 1046 return -ENODEV; 1047 var = info->var; 1048 unlock_fb_info(info); 1049 1050 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; 1051 break; 1052 case FBIOPUT_VSCREENINFO: 1053 if (copy_from_user(&var, argp, sizeof(var))) 1054 return -EFAULT; 1055 if (!lock_fb_info(info)) 1056 return -ENODEV; 1057 acquire_console_sem(); 1058 info->flags |= FBINFO_MISC_USEREVENT; 1059 ret = fb_set_var(info, &var); 1060 info->flags &= ~FBINFO_MISC_USEREVENT; 1061 release_console_sem(); 1062 unlock_fb_info(info); 1063 if (!ret && copy_to_user(argp, &var, sizeof(var))) 1064 ret = -EFAULT; 1065 break; 1066 case FBIOGET_FSCREENINFO: 1067 if (!lock_fb_info(info)) 1068 return -ENODEV; 1069 fix = info->fix; 1070 unlock_fb_info(info); 1071 1072 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; 1073 break; 1074 case FBIOPUTCMAP: 1075 if (copy_from_user(&cmap, argp, sizeof(cmap))) 1076 return -EFAULT; 1077 ret = fb_set_user_cmap(&cmap, info); 1078 break; 1079 case FBIOGETCMAP: 1080 if (copy_from_user(&cmap, argp, sizeof(cmap))) 1081 return -EFAULT; 1082 if (!lock_fb_info(info)) 1083 return -ENODEV; 1084 cmap_from = info->cmap; 1085 unlock_fb_info(info); 1086 ret = fb_cmap_to_user(&cmap_from, &cmap); 1087 break; 1088 case FBIOPAN_DISPLAY: 1089 if (copy_from_user(&var, argp, sizeof(var))) 1090 return -EFAULT; 1091 if (!lock_fb_info(info)) 1092 return -ENODEV; 1093 acquire_console_sem(); 1094 ret = fb_pan_display(info, &var); 1095 release_console_sem(); 1096 unlock_fb_info(info); 1097 if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) 1098 return -EFAULT; 1099 break; 1100 case FBIO_CURSOR: 1101 ret = -EINVAL; 1102 break; 1103 case FBIOGET_CON2FBMAP: 1104 if (copy_from_user(&con2fb, argp, sizeof(con2fb))) 1105 return -EFAULT; 1106 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) 1107 return -EINVAL; 1108 con2fb.framebuffer = -1; 1109 event.data = &con2fb; 1110 if (!lock_fb_info(info)) 1111 return -ENODEV; 1112 event.info = info; 1113 fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event); 1114 unlock_fb_info(info); 1115 ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; 1116 break; 1117 case FBIOPUT_CON2FBMAP: 1118 if (copy_from_user(&con2fb, argp, sizeof(con2fb))) 1119 return -EFAULT; 1120 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) 1121 return -EINVAL; 1122 if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) 1123 return -EINVAL; 1124 if (!registered_fb[con2fb.framebuffer]) 1125 request_module("fb%d", con2fb.framebuffer); 1126 if (!registered_fb[con2fb.framebuffer]) { 1127 ret = -EINVAL; 1128 break; 1129 } 1130 event.data = &con2fb; 1131 if (!lock_fb_info(info)) 1132 return -ENODEV; 1133 event.info = info; 1134 ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); 1135 unlock_fb_info(info); 1136 break; 1137 case FBIOBLANK: 1138 if (!lock_fb_info(info)) 1139 return -ENODEV; 1140 acquire_console_sem(); 1141 info->flags |= FBINFO_MISC_USEREVENT; 1142 ret = fb_blank(info, arg); 1143 info->flags &= ~FBINFO_MISC_USEREVENT; 1144 release_console_sem(); 1145 unlock_fb_info(info); 1146 break; 1147 default: 1148 if (!lock_fb_info(info)) 1149 return -ENODEV; 1150 fb = info->fbops; 1151 if (fb->fb_ioctl) 1152 ret = fb->fb_ioctl(info, cmd, arg); 1153 else 1154 ret = -ENOTTY; 1155 unlock_fb_info(info); 1156 } 1157 return ret; 1158} 1159 1160static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1161{ 1162 struct inode *inode = file->f_path.dentry->d_inode; 1163 int fbidx = iminor(inode); 1164 struct fb_info *info = registered_fb[fbidx]; 1165 1166 return do_fb_ioctl(info, cmd, arg); 1167} 1168 1169#ifdef CONFIG_COMPAT 1170struct fb_fix_screeninfo32 { 1171 char id[16]; 1172 compat_caddr_t smem_start; 1173 u32 smem_len; 1174 u32 type; 1175 u32 type_aux; 1176 u32 visual; 1177 u16 xpanstep; 1178 u16 ypanstep; 1179 u16 ywrapstep; 1180 u32 line_length; 1181 compat_caddr_t mmio_start; 1182 u32 mmio_len; 1183 u32 accel; 1184 u16 reserved[3]; 1185}; 1186 1187struct fb_cmap32 { 1188 u32 start; 1189 u32 len; 1190 compat_caddr_t red; 1191 compat_caddr_t green; 1192 compat_caddr_t blue; 1193 compat_caddr_t transp; 1194}; 1195 1196static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, 1197 unsigned long arg) 1198{ 1199 struct fb_cmap_user __user *cmap; 1200 struct fb_cmap32 __user *cmap32; 1201 __u32 data; 1202 int err; 1203 1204 cmap = compat_alloc_user_space(sizeof(*cmap)); 1205 cmap32 = compat_ptr(arg); 1206 1207 if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) 1208 return -EFAULT; 1209 1210 if (get_user(data, &cmap32->red) || 1211 put_user(compat_ptr(data), &cmap->red) || 1212 get_user(data, &cmap32->green) || 1213 put_user(compat_ptr(data), &cmap->green) || 1214 get_user(data, &cmap32->blue) || 1215 put_user(compat_ptr(data), &cmap->blue) || 1216 get_user(data, &cmap32->transp) || 1217 put_user(compat_ptr(data), &cmap->transp)) 1218 return -EFAULT; 1219 1220 err = do_fb_ioctl(info, cmd, (unsigned long) cmap); 1221 1222 if (!err) { 1223 if (copy_in_user(&cmap32->start, 1224 &cmap->start, 1225 2 * sizeof(__u32))) 1226 err = -EFAULT; 1227 } 1228 return err; 1229} 1230 1231static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, 1232 struct fb_fix_screeninfo32 __user *fix32) 1233{ 1234 __u32 data; 1235 int err; 1236 1237 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); 1238 1239 data = (__u32) (unsigned long) fix->smem_start; 1240 err |= put_user(data, &fix32->smem_start); 1241 1242 err |= put_user(fix->smem_len, &fix32->smem_len); 1243 err |= put_user(fix->type, &fix32->type); 1244 err |= put_user(fix->type_aux, &fix32->type_aux); 1245 err |= put_user(fix->visual, &fix32->visual); 1246 err |= put_user(fix->xpanstep, &fix32->xpanstep); 1247 err |= put_user(fix->ypanstep, &fix32->ypanstep); 1248 err |= put_user(fix->ywrapstep, &fix32->ywrapstep); 1249 err |= put_user(fix->line_length, &fix32->line_length); 1250 1251 data = (__u32) (unsigned long) fix->mmio_start; 1252 err |= put_user(data, &fix32->mmio_start); 1253 1254 err |= put_user(fix->mmio_len, &fix32->mmio_len); 1255 err |= put_user(fix->accel, &fix32->accel); 1256 err |= copy_to_user(fix32->reserved, fix->reserved, 1257 sizeof(fix->reserved)); 1258 1259 return err; 1260} 1261 1262static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, 1263 unsigned long arg) 1264{ 1265 mm_segment_t old_fs; 1266 struct fb_fix_screeninfo fix; 1267 struct fb_fix_screeninfo32 __user *fix32; 1268 int err; 1269 1270 fix32 = compat_ptr(arg); 1271 1272 old_fs = get_fs(); 1273 set_fs(KERNEL_DS); 1274 err = do_fb_ioctl(info, cmd, (unsigned long) &fix); 1275 set_fs(old_fs); 1276 1277 if (!err) 1278 err = do_fscreeninfo_to_user(&fix, fix32); 1279 1280 return err; 1281} 1282 1283static long fb_compat_ioctl(struct file *file, unsigned int cmd, 1284 unsigned long arg) 1285{ 1286 struct inode *inode = file->f_path.dentry->d_inode; 1287 int fbidx = iminor(inode); 1288 struct fb_info *info = registered_fb[fbidx]; 1289 struct fb_ops *fb = info->fbops; 1290 long ret = -ENOIOCTLCMD; 1291 1292 switch(cmd) { 1293 case FBIOGET_VSCREENINFO: 1294 case FBIOPUT_VSCREENINFO: 1295 case FBIOPAN_DISPLAY: 1296 case FBIOGET_CON2FBMAP: 1297 case FBIOPUT_CON2FBMAP: 1298 arg = (unsigned long) compat_ptr(arg); 1299 case FBIOBLANK: 1300 ret = do_fb_ioctl(info, cmd, arg); 1301 break; 1302 1303 case FBIOGET_FSCREENINFO: 1304 ret = fb_get_fscreeninfo(info, cmd, arg); 1305 break; 1306 1307 case FBIOGETCMAP: 1308 case FBIOPUTCMAP: 1309 ret = fb_getput_cmap(info, cmd, arg); 1310 break; 1311 1312 default: 1313 if (fb->fb_compat_ioctl) 1314 ret = fb->fb_compat_ioctl(info, cmd, arg); 1315 break; 1316 } 1317 return ret; 1318} 1319#endif 1320 1321static int 1322fb_mmap(struct file *file, struct vm_area_struct * vma) 1323{ 1324 int fbidx = iminor(file->f_path.dentry->d_inode); 1325 struct fb_info *info = registered_fb[fbidx]; 1326 struct fb_ops *fb = info->fbops; 1327 unsigned long off; 1328 unsigned long start; 1329 u32 len; 1330 1331 if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 1332 return -EINVAL; 1333 off = vma->vm_pgoff << PAGE_SHIFT; 1334 if (!fb) 1335 return -ENODEV; 1336 mutex_lock(&info->mm_lock); 1337 if (fb->fb_mmap) { 1338 int res; 1339 res = fb->fb_mmap(info, vma); 1340 mutex_unlock(&info->mm_lock); 1341 return res; 1342 } 1343 1344 /* frame buffer memory */ 1345 start = info->fix.smem_start; 1346 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); 1347 if (off >= len) { 1348 /* memory mapped io */ 1349 off -= len; 1350 if (info->var.accel_flags) { 1351 mutex_unlock(&info->mm_lock); 1352 return -EINVAL; 1353 } 1354 start = info->fix.mmio_start; 1355 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); 1356 } 1357 mutex_unlock(&info->mm_lock); 1358 start &= PAGE_MASK; 1359 if ((vma->vm_end - vma->vm_start + off) > len) 1360 return -EINVAL; 1361 off += start; 1362 vma->vm_pgoff = off >> PAGE_SHIFT; 1363 /* This is an IO map - tell maydump to skip this VMA */ 1364 vma->vm_flags |= VM_IO | VM_RESERVED; 1365 fb_pgprotect(file, vma, off); 1366 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, 1367 vma->vm_end - vma->vm_start, vma->vm_page_prot)) 1368 return -EAGAIN; 1369 return 0; 1370} 1371 1372static int 1373fb_open(struct inode *inode, struct file *file) 1374__acquires(&info->lock) 1375__releases(&info->lock) 1376{ 1377 int fbidx = iminor(inode); 1378 struct fb_info *info; 1379 int res = 0; 1380 1381 if (fbidx >= FB_MAX) 1382 return -ENODEV; 1383 info = registered_fb[fbidx]; 1384 if (!info) 1385 request_module("fb%d", fbidx); 1386 info = registered_fb[fbidx]; 1387 if (!info) 1388 return -ENODEV; 1389 mutex_lock(&info->lock); 1390 if (!try_module_get(info->fbops->owner)) { 1391 res = -ENODEV; 1392 goto out; 1393 } 1394 file->private_data = info; 1395 if (info->fbops->fb_open) { 1396 res = info->fbops->fb_open(info,1); 1397 if (res) 1398 module_put(info->fbops->owner); 1399 } 1400#ifdef CONFIG_FB_DEFERRED_IO 1401 if (info->fbdefio) 1402 fb_deferred_io_open(info, inode, file); 1403#endif 1404out: 1405 mutex_unlock(&info->lock); 1406 return res; 1407} 1408 1409static int 1410fb_release(struct inode *inode, struct file *file) 1411__acquires(&info->lock) 1412__releases(&info->lock) 1413{ 1414 struct fb_info * const info = file->private_data; 1415 1416 mutex_lock(&info->lock); 1417 if (info->fbops->fb_release) 1418 info->fbops->fb_release(info,1); 1419 module_put(info->fbops->owner); 1420 mutex_unlock(&info->lock); 1421 return 0; 1422} 1423 1424static const struct file_operations fb_fops = { 1425 .owner = THIS_MODULE, 1426 .read = fb_read, 1427 .write = fb_write, 1428 .unlocked_ioctl = fb_ioctl, 1429#ifdef CONFIG_COMPAT 1430 .compat_ioctl = fb_compat_ioctl, 1431#endif 1432 .mmap = fb_mmap, 1433 .open = fb_open, 1434 .release = fb_release, 1435#ifdef HAVE_ARCH_FB_UNMAPPED_AREA 1436 .get_unmapped_area = get_fb_unmapped_area, 1437#endif 1438#ifdef CONFIG_FB_DEFERRED_IO 1439 .fsync = fb_deferred_io_fsync, 1440#endif 1441}; 1442 1443struct class *fb_class; 1444EXPORT_SYMBOL(fb_class); 1445 1446static int fb_check_foreignness(struct fb_info *fi) 1447{ 1448 const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN; 1449 1450 fi->flags &= ~FBINFO_FOREIGN_ENDIAN; 1451 1452#ifdef __BIG_ENDIAN 1453 fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH; 1454#else 1455 fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0; 1456#endif /* __BIG_ENDIAN */ 1457 1458 if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) { 1459 pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to " 1460 "support this framebuffer\n", fi->fix.id); 1461 return -ENOSYS; 1462 } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) { 1463 pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to " 1464 "support this framebuffer\n", fi->fix.id); 1465 return -ENOSYS; 1466 } 1467 1468 return 0; 1469} 1470 1471static bool fb_do_apertures_overlap(struct fb_info *gen, struct fb_info *hw) 1472{ 1473 /* is the generic aperture base the same as the HW one */ 1474 if (gen->aperture_base == hw->aperture_base) 1475 return true; 1476 /* is the generic aperture base inside the hw base->hw base+size */ 1477 if (gen->aperture_base > hw->aperture_base && gen->aperture_base <= hw->aperture_base + hw->aperture_size) 1478 return true; 1479 return false; 1480} 1481/** 1482 * register_framebuffer - registers a frame buffer device 1483 * @fb_info: frame buffer info structure 1484 * 1485 * Registers a frame buffer device @fb_info. 1486 * 1487 * Returns negative errno on error, or zero for success. 1488 * 1489 */ 1490 1491int 1492register_framebuffer(struct fb_info *fb_info) 1493{ 1494 int i; 1495 struct fb_event event; 1496 struct fb_videomode mode; 1497 1498 if (num_registered_fb == FB_MAX) 1499 return -ENXIO; 1500 1501 if (fb_check_foreignness(fb_info)) 1502 return -ENOSYS; 1503 1504 /* check all firmware fbs and kick off if the base addr overlaps */ 1505 for (i = 0 ; i < FB_MAX; i++) { 1506 if (!registered_fb[i]) 1507 continue; 1508 1509 if (registered_fb[i]->flags & FBINFO_MISC_FIRMWARE) { 1510 if (fb_do_apertures_overlap(registered_fb[i], fb_info)) { 1511 printk(KERN_ERR "fb: conflicting fb hw usage " 1512 "%s vs %s - removing generic driver\n", 1513 fb_info->fix.id, 1514 registered_fb[i]->fix.id); 1515 unregister_framebuffer(registered_fb[i]); 1516 break; 1517 } 1518 } 1519 } 1520 1521 num_registered_fb++; 1522 for (i = 0 ; i < FB_MAX; i++) 1523 if (!registered_fb[i]) 1524 break; 1525 fb_info->node = i; 1526 mutex_init(&fb_info->lock); 1527 mutex_init(&fb_info->mm_lock); 1528 1529 fb_info->dev = device_create(fb_class, fb_info->device, 1530 MKDEV(FB_MAJOR, i), NULL, "fb%d", i); 1531 if (IS_ERR(fb_info->dev)) { 1532 /* Not fatal */ 1533 printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); 1534 fb_info->dev = NULL; 1535 } else 1536 fb_init_device(fb_info); 1537 1538 if (fb_info->pixmap.addr == NULL) { 1539 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); 1540 if (fb_info->pixmap.addr) { 1541 fb_info->pixmap.size = FBPIXMAPSIZE; 1542 fb_info->pixmap.buf_align = 1; 1543 fb_info->pixmap.scan_align = 1; 1544 fb_info->pixmap.access_align = 32; 1545 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; 1546 } 1547 } 1548 fb_info->pixmap.offset = 0; 1549 1550 if (!fb_info->pixmap.blit_x) 1551 fb_info->pixmap.blit_x = ~(u32)0; 1552 1553 if (!fb_info->pixmap.blit_y) 1554 fb_info->pixmap.blit_y = ~(u32)0; 1555 1556 if (!fb_info->modelist.prev || !fb_info->modelist.next) 1557 INIT_LIST_HEAD(&fb_info->modelist); 1558 1559 fb_var_to_videomode(&mode, &fb_info->var); 1560 fb_add_videomode(&mode, &fb_info->modelist); 1561 registered_fb[i] = fb_info; 1562 1563 event.info = fb_info; 1564 if (!lock_fb_info(fb_info)) 1565 return -ENODEV; 1566 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); 1567 unlock_fb_info(fb_info); 1568 return 0; 1569} 1570 1571 1572/** 1573 * unregister_framebuffer - releases a frame buffer device 1574 * @fb_info: frame buffer info structure 1575 * 1576 * Unregisters a frame buffer device @fb_info. 1577 * 1578 * Returns negative errno on error, or zero for success. 1579 * 1580 * This function will also notify the framebuffer console 1581 * to release the driver. 1582 * 1583 * This is meant to be called within a driver's module_exit() 1584 * function. If this is called outside module_exit(), ensure 1585 * that the driver implements fb_open() and fb_release() to 1586 * check that no processes are using the device. 1587 */ 1588 1589int 1590unregister_framebuffer(struct fb_info *fb_info) 1591{ 1592 struct fb_event event; 1593 int i, ret = 0; 1594 1595 i = fb_info->node; 1596 if (!registered_fb[i]) { 1597 ret = -EINVAL; 1598 goto done; 1599 } 1600 1601 1602 if (!lock_fb_info(fb_info)) 1603 return -ENODEV; 1604 event.info = fb_info; 1605 ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); 1606 unlock_fb_info(fb_info); 1607 1608 if (ret) { 1609 ret = -EINVAL; 1610 goto done; 1611 } 1612 1613 if (fb_info->pixmap.addr && 1614 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1615 kfree(fb_info->pixmap.addr); 1616 fb_destroy_modelist(&fb_info->modelist); 1617 registered_fb[i]=NULL; 1618 num_registered_fb--; 1619 fb_cleanup_device(fb_info); 1620 device_destroy(fb_class, MKDEV(FB_MAJOR, i)); 1621 event.info = fb_info; 1622 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); 1623 1624 /* this may free fb info */ 1625 if (fb_info->fbops->fb_destroy) 1626 fb_info->fbops->fb_destroy(fb_info); 1627done: 1628 return ret; 1629} 1630 1631/** 1632 * fb_set_suspend - low level driver signals suspend 1633 * @info: framebuffer affected 1634 * @state: 0 = resuming, !=0 = suspending 1635 * 1636 * This is meant to be used by low level drivers to 1637 * signal suspend/resume to the core & clients. 1638 * It must be called with the console semaphore held 1639 */ 1640void fb_set_suspend(struct fb_info *info, int state) 1641{ 1642 struct fb_event event; 1643 1644 if (!lock_fb_info(info)) 1645 return; 1646 event.info = info; 1647 if (state) { 1648 fb_notifier_call_chain(FB_EVENT_SUSPEND, &event); 1649 info->state = FBINFO_STATE_SUSPENDED; 1650 } else { 1651 info->state = FBINFO_STATE_RUNNING; 1652 fb_notifier_call_chain(FB_EVENT_RESUME, &event); 1653 } 1654 unlock_fb_info(info); 1655} 1656 1657/** 1658 * fbmem_init - init frame buffer subsystem 1659 * 1660 * Initialize the frame buffer subsystem. 1661 * 1662 * NOTE: This function is _only_ to be called by drivers/char/mem.c. 1663 * 1664 */ 1665 1666static int __init 1667fbmem_init(void) 1668{ 1669 proc_create("fb", 0, NULL, &fb_proc_fops); 1670 1671 if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) 1672 printk("unable to get major %d for fb devs\n", FB_MAJOR); 1673 1674 fb_class = class_create(THIS_MODULE, "graphics"); 1675 if (IS_ERR(fb_class)) { 1676 printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class)); 1677 fb_class = NULL; 1678 } 1679 return 0; 1680} 1681 1682#ifdef MODULE 1683module_init(fbmem_init); 1684static void __exit 1685fbmem_exit(void) 1686{ 1687 remove_proc_entry("fb", NULL); 1688 class_destroy(fb_class); 1689 unregister_chrdev(FB_MAJOR, "fb"); 1690} 1691 1692module_exit(fbmem_exit); 1693MODULE_LICENSE("GPL"); 1694MODULE_DESCRIPTION("Framebuffer base"); 1695#else 1696subsys_initcall(fbmem_init); 1697#endif 1698 1699int fb_new_modelist(struct fb_info *info) 1700{ 1701 struct fb_event event; 1702 struct fb_var_screeninfo var = info->var; 1703 struct list_head *pos, *n; 1704 struct fb_modelist *modelist; 1705 struct fb_videomode *m, mode; 1706 int err = 1; 1707 1708 list_for_each_safe(pos, n, &info->modelist) { 1709 modelist = list_entry(pos, struct fb_modelist, list); 1710 m = &modelist->mode; 1711 fb_videomode_to_var(&var, m); 1712 var.activate = FB_ACTIVATE_TEST; 1713 err = fb_set_var(info, &var); 1714 fb_var_to_videomode(&mode, &var); 1715 if (err || !fb_mode_is_equal(m, &mode)) { 1716 list_del(pos); 1717 kfree(pos); 1718 } 1719 } 1720 1721 err = 1; 1722 1723 if (!list_empty(&info->modelist)) { 1724 if (!lock_fb_info(info)) 1725 return -ENODEV; 1726 event.info = info; 1727 err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); 1728 unlock_fb_info(info); 1729 } 1730 1731 return err; 1732} 1733 1734static char *video_options[FB_MAX] __read_mostly; 1735static int ofonly __read_mostly; 1736 1737/** 1738 * fb_get_options - get kernel boot parameters 1739 * @name: framebuffer name as it would appear in 1740 * the boot parameter line 1741 * (video=<name>:<options>) 1742 * @option: the option will be stored here 1743 * 1744 * NOTE: Needed to maintain backwards compatibility 1745 */ 1746int fb_get_options(char *name, char **option) 1747{ 1748 char *opt, *options = NULL; 1749 int opt_len, retval = 0; 1750 int name_len = strlen(name), i; 1751 1752 if (name_len && ofonly && strncmp(name, "offb", 4)) 1753 retval = 1; 1754 1755 if (name_len && !retval) { 1756 for (i = 0; i < FB_MAX; i++) { 1757 if (video_options[i] == NULL) 1758 continue; 1759 opt_len = strlen(video_options[i]); 1760 if (!opt_len) 1761 continue; 1762 opt = video_options[i]; 1763 if (!strncmp(name, opt, name_len) && 1764 opt[name_len] == ':') 1765 options = opt + name_len + 1; 1766 } 1767 } 1768 if (options && !strncmp(options, "off", 3)) 1769 retval = 1; 1770 1771 if (option) 1772 *option = options; 1773 1774 return retval; 1775} 1776 1777#ifndef MODULE 1778/** 1779 * video_setup - process command line options 1780 * @options: string of options 1781 * 1782 * Process command line options for frame buffer subsystem. 1783 * 1784 * NOTE: This function is a __setup and __init function. 1785 * It only stores the options. Drivers have to call 1786 * fb_get_options() as necessary. 1787 * 1788 * Returns zero. 1789 * 1790 */ 1791static int __init video_setup(char *options) 1792{ 1793 int i, global = 0; 1794 1795 if (!options || !*options) 1796 global = 1; 1797 1798 if (!global && !strncmp(options, "ofonly", 6)) { 1799 ofonly = 1; 1800 global = 1; 1801 } 1802 1803 if (!global && !strchr(options, ':')) { 1804 fb_mode_option = options; 1805 global = 1; 1806 } 1807 1808 if (!global) { 1809 for (i = 0; i < FB_MAX; i++) { 1810 if (video_options[i] == NULL) { 1811 video_options[i] = options; 1812 break; 1813 } 1814 1815 } 1816 } 1817 1818 return 1; 1819} 1820__setup("video=", video_setup); 1821#endif 1822 1823 /* 1824 * Visible symbols for modules 1825 */ 1826 1827EXPORT_SYMBOL(register_framebuffer); 1828EXPORT_SYMBOL(unregister_framebuffer); 1829EXPORT_SYMBOL(num_registered_fb); 1830EXPORT_SYMBOL(registered_fb); 1831EXPORT_SYMBOL(fb_show_logo); 1832EXPORT_SYMBOL(fb_set_var); 1833EXPORT_SYMBOL(fb_blank); 1834EXPORT_SYMBOL(fb_pan_display); 1835EXPORT_SYMBOL(fb_get_buffer_offset); 1836EXPORT_SYMBOL(fb_set_suspend); 1837EXPORT_SYMBOL(fb_get_options); 1838 1839MODULE_LICENSE("GPL");