Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

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