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 	int ret = -EINVAL;
1050
1051 	if (blank > FB_BLANK_POWERDOWN)
1052 		blank = FB_BLANK_POWERDOWN;
1053
1054	if (info->fbops->fb_blank)
1055 		ret = info->fbops->fb_blank(blank, info);
1056
1057 	if (!ret) {
1058		struct fb_event event;
1059
1060		event.info = info;
1061		event.data = &blank;
1062		fb_notifier_call_chain(FB_EVENT_BLANK, &event);
1063	}
1064
1065 	return ret;
1066}
1067
1068static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
1069			unsigned long arg)
1070{
1071	struct fb_ops *fb;
1072	struct fb_var_screeninfo var;
1073	struct fb_fix_screeninfo fix;
1074	struct fb_con2fbmap con2fb;
1075	struct fb_cmap cmap_from;
1076	struct fb_cmap_user cmap;
1077	struct fb_event event;
1078	void __user *argp = (void __user *)arg;
1079	long ret = 0;
1080
1081	switch (cmd) {
1082	case FBIOGET_VSCREENINFO:
1083		if (!lock_fb_info(info))
1084			return -ENODEV;
1085		var = info->var;
1086		unlock_fb_info(info);
1087
1088		ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
1089		break;
1090	case FBIOPUT_VSCREENINFO:
1091		if (copy_from_user(&var, argp, sizeof(var)))
1092			return -EFAULT;
1093		if (!lock_fb_info(info))
1094			return -ENODEV;
1095		console_lock();
1096		info->flags |= FBINFO_MISC_USEREVENT;
1097		ret = fb_set_var(info, &var);
1098		info->flags &= ~FBINFO_MISC_USEREVENT;
1099		console_unlock();
1100		unlock_fb_info(info);
1101		if (!ret && copy_to_user(argp, &var, sizeof(var)))
1102			ret = -EFAULT;
1103		break;
1104	case FBIOGET_FSCREENINFO:
1105		if (!lock_fb_info(info))
1106			return -ENODEV;
1107		fix = info->fix;
1108		unlock_fb_info(info);
1109
1110		ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
1111		break;
1112	case FBIOPUTCMAP:
1113		if (copy_from_user(&cmap, argp, sizeof(cmap)))
1114			return -EFAULT;
1115		ret = fb_set_user_cmap(&cmap, info);
1116		break;
1117	case FBIOGETCMAP:
1118		if (copy_from_user(&cmap, argp, sizeof(cmap)))
1119			return -EFAULT;
1120		if (!lock_fb_info(info))
1121			return -ENODEV;
1122		cmap_from = info->cmap;
1123		unlock_fb_info(info);
1124		ret = fb_cmap_to_user(&cmap_from, &cmap);
1125		break;
1126	case FBIOPAN_DISPLAY:
1127		if (copy_from_user(&var, argp, sizeof(var)))
1128			return -EFAULT;
1129		if (!lock_fb_info(info))
1130			return -ENODEV;
1131		console_lock();
1132		ret = fb_pan_display(info, &var);
1133		console_unlock();
1134		unlock_fb_info(info);
1135		if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
1136			return -EFAULT;
1137		break;
1138	case FBIO_CURSOR:
1139		ret = -EINVAL;
1140		break;
1141	case FBIOGET_CON2FBMAP:
1142		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1143			return -EFAULT;
1144		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1145			return -EINVAL;
1146		con2fb.framebuffer = -1;
1147		event.data = &con2fb;
1148		if (!lock_fb_info(info))
1149			return -ENODEV;
1150		event.info = info;
1151		fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
1152		unlock_fb_info(info);
1153		ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
1154		break;
1155	case FBIOPUT_CON2FBMAP:
1156		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1157			return -EFAULT;
1158		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1159			return -EINVAL;
1160		if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
1161			return -EINVAL;
1162		if (!registered_fb[con2fb.framebuffer])
1163			request_module("fb%d", con2fb.framebuffer);
1164		if (!registered_fb[con2fb.framebuffer]) {
1165			ret = -EINVAL;
1166			break;
1167		}
1168		event.data = &con2fb;
1169		if (!lock_fb_info(info))
1170			return -ENODEV;
1171		event.info = info;
1172		ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
1173		unlock_fb_info(info);
1174		break;
1175	case FBIOBLANK:
1176		if (!lock_fb_info(info))
1177			return -ENODEV;
1178		console_lock();
1179		info->flags |= FBINFO_MISC_USEREVENT;
1180		ret = fb_blank(info, arg);
1181		info->flags &= ~FBINFO_MISC_USEREVENT;
1182		console_unlock();
1183		unlock_fb_info(info);
1184		break;
1185	default:
1186		if (!lock_fb_info(info))
1187			return -ENODEV;
1188		fb = info->fbops;
1189		if (fb->fb_ioctl)
1190			ret = fb->fb_ioctl(info, cmd, arg);
1191		else
1192			ret = -ENOTTY;
1193		unlock_fb_info(info);
1194	}
1195	return ret;
1196}
1197
1198static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1199{
1200	struct fb_info *info = file_fb_info(file);
1201
1202	if (!info)
1203		return -ENODEV;
1204	return do_fb_ioctl(info, cmd, arg);
1205}
1206
1207#ifdef CONFIG_COMPAT
1208struct fb_fix_screeninfo32 {
1209	char			id[16];
1210	compat_caddr_t		smem_start;
1211	u32			smem_len;
1212	u32			type;
1213	u32			type_aux;
1214	u32			visual;
1215	u16			xpanstep;
1216	u16			ypanstep;
1217	u16			ywrapstep;
1218	u32			line_length;
1219	compat_caddr_t		mmio_start;
1220	u32			mmio_len;
1221	u32			accel;
1222	u16			reserved[3];
1223};
1224
1225struct fb_cmap32 {
1226	u32			start;
1227	u32			len;
1228	compat_caddr_t	red;
1229	compat_caddr_t	green;
1230	compat_caddr_t	blue;
1231	compat_caddr_t	transp;
1232};
1233
1234static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
1235			  unsigned long arg)
1236{
1237	struct fb_cmap_user __user *cmap;
1238	struct fb_cmap32 __user *cmap32;
1239	__u32 data;
1240	int err;
1241
1242	cmap = compat_alloc_user_space(sizeof(*cmap));
1243	cmap32 = compat_ptr(arg);
1244
1245	if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32)))
1246		return -EFAULT;
1247
1248	if (get_user(data, &cmap32->red) ||
1249	    put_user(compat_ptr(data), &cmap->red) ||
1250	    get_user(data, &cmap32->green) ||
1251	    put_user(compat_ptr(data), &cmap->green) ||
1252	    get_user(data, &cmap32->blue) ||
1253	    put_user(compat_ptr(data), &cmap->blue) ||
1254	    get_user(data, &cmap32->transp) ||
1255	    put_user(compat_ptr(data), &cmap->transp))
1256		return -EFAULT;
1257
1258	err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
1259
1260	if (!err) {
1261		if (copy_in_user(&cmap32->start,
1262				 &cmap->start,
1263				 2 * sizeof(__u32)))
1264			err = -EFAULT;
1265	}
1266	return err;
1267}
1268
1269static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
1270				  struct fb_fix_screeninfo32 __user *fix32)
1271{
1272	__u32 data;
1273	int err;
1274
1275	err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
1276
1277	data = (__u32) (unsigned long) fix->smem_start;
1278	err |= put_user(data, &fix32->smem_start);
1279
1280	err |= put_user(fix->smem_len, &fix32->smem_len);
1281	err |= put_user(fix->type, &fix32->type);
1282	err |= put_user(fix->type_aux, &fix32->type_aux);
1283	err |= put_user(fix->visual, &fix32->visual);
1284	err |= put_user(fix->xpanstep, &fix32->xpanstep);
1285	err |= put_user(fix->ypanstep, &fix32->ypanstep);
1286	err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
1287	err |= put_user(fix->line_length, &fix32->line_length);
1288
1289	data = (__u32) (unsigned long) fix->mmio_start;
1290	err |= put_user(data, &fix32->mmio_start);
1291
1292	err |= put_user(fix->mmio_len, &fix32->mmio_len);
1293	err |= put_user(fix->accel, &fix32->accel);
1294	err |= copy_to_user(fix32->reserved, fix->reserved,
1295			    sizeof(fix->reserved));
1296
1297	return err;
1298}
1299
1300static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
1301			      unsigned long arg)
1302{
1303	mm_segment_t old_fs;
1304	struct fb_fix_screeninfo fix;
1305	struct fb_fix_screeninfo32 __user *fix32;
1306	int err;
1307
1308	fix32 = compat_ptr(arg);
1309
1310	old_fs = get_fs();
1311	set_fs(KERNEL_DS);
1312	err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
1313	set_fs(old_fs);
1314
1315	if (!err)
1316		err = do_fscreeninfo_to_user(&fix, fix32);
1317
1318	return err;
1319}
1320
1321static long fb_compat_ioctl(struct file *file, unsigned int cmd,
1322			    unsigned long arg)
1323{
1324	struct fb_info *info = file_fb_info(file);
1325	struct fb_ops *fb;
1326	long ret = -ENOIOCTLCMD;
1327
1328	if (!info)
1329		return -ENODEV;
1330	fb = info->fbops;
1331	switch(cmd) {
1332	case FBIOGET_VSCREENINFO:
1333	case FBIOPUT_VSCREENINFO:
1334	case FBIOPAN_DISPLAY:
1335	case FBIOGET_CON2FBMAP:
1336	case FBIOPUT_CON2FBMAP:
1337		arg = (unsigned long) compat_ptr(arg);
1338	case FBIOBLANK:
1339		ret = do_fb_ioctl(info, cmd, arg);
1340		break;
1341
1342	case FBIOGET_FSCREENINFO:
1343		ret = fb_get_fscreeninfo(info, cmd, arg);
1344		break;
1345
1346	case FBIOGETCMAP:
1347	case FBIOPUTCMAP:
1348		ret = fb_getput_cmap(info, cmd, arg);
1349		break;
1350
1351	default:
1352		if (fb->fb_compat_ioctl)
1353			ret = fb->fb_compat_ioctl(info, cmd, arg);
1354		break;
1355	}
1356	return ret;
1357}
1358#endif
1359
1360static int
1361fb_mmap(struct file *file, struct vm_area_struct * vma)
1362{
1363	struct fb_info *info = file_fb_info(file);
1364	struct fb_ops *fb;
1365	unsigned long off;
1366	unsigned long start;
1367	u32 len;
1368
1369	if (!info)
1370		return -ENODEV;
1371	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
1372		return -EINVAL;
1373	off = vma->vm_pgoff << PAGE_SHIFT;
1374	fb = info->fbops;
1375	if (!fb)
1376		return -ENODEV;
1377	mutex_lock(&info->mm_lock);
1378	if (fb->fb_mmap) {
1379		int res;
1380		res = fb->fb_mmap(info, vma);
1381		mutex_unlock(&info->mm_lock);
1382		return res;
1383	}
1384
1385	/* frame buffer memory */
1386	start = info->fix.smem_start;
1387	len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
1388	if (off >= len) {
1389		/* memory mapped io */
1390		off -= len;
1391		if (info->var.accel_flags) {
1392			mutex_unlock(&info->mm_lock);
1393			return -EINVAL;
1394		}
1395		start = info->fix.mmio_start;
1396		len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
1397	}
1398	mutex_unlock(&info->mm_lock);
1399	start &= PAGE_MASK;
1400	if ((vma->vm_end - vma->vm_start + off) > len)
1401		return -EINVAL;
1402	off += start;
1403	vma->vm_pgoff = off >> PAGE_SHIFT;
1404	/* This is an IO map - tell maydump to skip this VMA */
1405	vma->vm_flags |= VM_IO | VM_RESERVED;
1406	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
1407	fb_pgprotect(file, vma, off);
1408	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
1409			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
1410		return -EAGAIN;
1411	return 0;
1412}
1413
1414static int
1415fb_open(struct inode *inode, struct file *file)
1416__acquires(&info->lock)
1417__releases(&info->lock)
1418{
1419	int fbidx = iminor(inode);
1420	struct fb_info *info;
1421	int res = 0;
1422
1423	info = get_fb_info(fbidx);
1424	if (!info) {
1425		request_module("fb%d", fbidx);
1426		info = get_fb_info(fbidx);
1427		if (!info)
1428			return -ENODEV;
1429	}
1430	if (IS_ERR(info))
1431		return PTR_ERR(info);
1432
1433	mutex_lock(&info->lock);
1434	if (!try_module_get(info->fbops->owner)) {
1435		res = -ENODEV;
1436		goto out;
1437	}
1438	file->private_data = info;
1439	if (info->fbops->fb_open) {
1440		res = info->fbops->fb_open(info,1);
1441		if (res)
1442			module_put(info->fbops->owner);
1443	}
1444#ifdef CONFIG_FB_DEFERRED_IO
1445	if (info->fbdefio)
1446		fb_deferred_io_open(info, inode, file);
1447#endif
1448out:
1449	mutex_unlock(&info->lock);
1450	if (res)
1451		put_fb_info(info);
1452	return res;
1453}
1454
1455static int
1456fb_release(struct inode *inode, struct file *file)
1457__acquires(&info->lock)
1458__releases(&info->lock)
1459{
1460	struct fb_info * const info = file->private_data;
1461
1462	mutex_lock(&info->lock);
1463	if (info->fbops->fb_release)
1464		info->fbops->fb_release(info,1);
1465	module_put(info->fbops->owner);
1466	mutex_unlock(&info->lock);
1467	put_fb_info(info);
1468	return 0;
1469}
1470
1471static const struct file_operations fb_fops = {
1472	.owner =	THIS_MODULE,
1473	.read =		fb_read,
1474	.write =	fb_write,
1475	.unlocked_ioctl = fb_ioctl,
1476#ifdef CONFIG_COMPAT
1477	.compat_ioctl = fb_compat_ioctl,
1478#endif
1479	.mmap =		fb_mmap,
1480	.open =		fb_open,
1481	.release =	fb_release,
1482#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1483	.get_unmapped_area = get_fb_unmapped_area,
1484#endif
1485#ifdef CONFIG_FB_DEFERRED_IO
1486	.fsync =	fb_deferred_io_fsync,
1487#endif
1488	.llseek =	default_llseek,
1489};
1490
1491struct class *fb_class;
1492EXPORT_SYMBOL(fb_class);
1493
1494static int fb_check_foreignness(struct fb_info *fi)
1495{
1496	const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
1497
1498	fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
1499
1500#ifdef __BIG_ENDIAN
1501	fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
1502#else
1503	fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
1504#endif /* __BIG_ENDIAN */
1505
1506	if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
1507		pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
1508		       "support this framebuffer\n", fi->fix.id);
1509		return -ENOSYS;
1510	} else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
1511		pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
1512		       "support this framebuffer\n", fi->fix.id);
1513		return -ENOSYS;
1514	}
1515
1516	return 0;
1517}
1518
1519static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
1520{
1521	/* is the generic aperture base the same as the HW one */
1522	if (gen->base == hw->base)
1523		return true;
1524	/* is the generic aperture base inside the hw base->hw base+size */
1525	if (gen->base > hw->base && gen->base < hw->base + hw->size)
1526		return true;
1527	return false;
1528}
1529
1530static bool fb_do_apertures_overlap(struct apertures_struct *gena,
1531				    struct apertures_struct *hwa)
1532{
1533	int i, j;
1534	if (!hwa || !gena)
1535		return false;
1536
1537	for (i = 0; i < hwa->count; ++i) {
1538		struct aperture *h = &hwa->ranges[i];
1539		for (j = 0; j < gena->count; ++j) {
1540			struct aperture *g = &gena->ranges[j];
1541			printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
1542				(unsigned long long)g->base,
1543				(unsigned long long)g->size,
1544				(unsigned long long)h->base,
1545				(unsigned long long)h->size);
1546			if (apertures_overlap(g, h))
1547				return true;
1548		}
1549	}
1550
1551	return false;
1552}
1553
1554static int do_unregister_framebuffer(struct fb_info *fb_info);
1555
1556#define VGA_FB_PHYS 0xA0000
1557static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
1558				     const char *name, bool primary)
1559{
1560	int i;
1561
1562	/* check all firmware fbs and kick off if the base addr overlaps */
1563	for (i = 0 ; i < FB_MAX; i++) {
1564		struct apertures_struct *gen_aper;
1565		if (!registered_fb[i])
1566			continue;
1567
1568		if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
1569			continue;
1570
1571		gen_aper = registered_fb[i]->apertures;
1572		if (fb_do_apertures_overlap(gen_aper, a) ||
1573			(primary && gen_aper && gen_aper->count &&
1574			 gen_aper->ranges[0].base == VGA_FB_PHYS)) {
1575
1576			printk(KERN_INFO "fb: conflicting fb hw usage "
1577			       "%s vs %s - removing generic driver\n",
1578			       name, registered_fb[i]->fix.id);
1579			do_unregister_framebuffer(registered_fb[i]);
1580		}
1581	}
1582}
1583
1584static int do_register_framebuffer(struct fb_info *fb_info)
1585{
1586	int i;
1587	struct fb_event event;
1588	struct fb_videomode mode;
1589
1590	if (fb_check_foreignness(fb_info))
1591		return -ENOSYS;
1592
1593	do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
1594					 fb_is_primary_device(fb_info));
1595
1596	if (num_registered_fb == FB_MAX)
1597		return -ENXIO;
1598
1599	num_registered_fb++;
1600	for (i = 0 ; i < FB_MAX; i++)
1601		if (!registered_fb[i])
1602			break;
1603	fb_info->node = i;
1604	atomic_set(&fb_info->count, 1);
1605	mutex_init(&fb_info->lock);
1606	mutex_init(&fb_info->mm_lock);
1607
1608	fb_info->dev = device_create(fb_class, fb_info->device,
1609				     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
1610	if (IS_ERR(fb_info->dev)) {
1611		/* Not fatal */
1612		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1613		fb_info->dev = NULL;
1614	} else
1615		fb_init_device(fb_info);
1616
1617	if (fb_info->pixmap.addr == NULL) {
1618		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
1619		if (fb_info->pixmap.addr) {
1620			fb_info->pixmap.size = FBPIXMAPSIZE;
1621			fb_info->pixmap.buf_align = 1;
1622			fb_info->pixmap.scan_align = 1;
1623			fb_info->pixmap.access_align = 32;
1624			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
1625		}
1626	}
1627	fb_info->pixmap.offset = 0;
1628
1629	if (!fb_info->pixmap.blit_x)
1630		fb_info->pixmap.blit_x = ~(u32)0;
1631
1632	if (!fb_info->pixmap.blit_y)
1633		fb_info->pixmap.blit_y = ~(u32)0;
1634
1635	if (!fb_info->modelist.prev || !fb_info->modelist.next)
1636		INIT_LIST_HEAD(&fb_info->modelist);
1637
1638	fb_var_to_videomode(&mode, &fb_info->var);
1639	fb_add_videomode(&mode, &fb_info->modelist);
1640	registered_fb[i] = fb_info;
1641
1642	event.info = fb_info;
1643	if (!lock_fb_info(fb_info))
1644		return -ENODEV;
1645	fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
1646	unlock_fb_info(fb_info);
1647	return 0;
1648}
1649
1650static int do_unregister_framebuffer(struct fb_info *fb_info)
1651{
1652	struct fb_event event;
1653	int i, ret = 0;
1654
1655	i = fb_info->node;
1656	if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
1657		return -EINVAL;
1658
1659	if (!lock_fb_info(fb_info))
1660		return -ENODEV;
1661	event.info = fb_info;
1662	ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
1663	unlock_fb_info(fb_info);
1664
1665	if (ret)
1666		return -EINVAL;
1667
1668	if (fb_info->pixmap.addr &&
1669	    (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1670		kfree(fb_info->pixmap.addr);
1671	fb_destroy_modelist(&fb_info->modelist);
1672	registered_fb[i] = NULL;
1673	num_registered_fb--;
1674	fb_cleanup_device(fb_info);
1675	device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1676	event.info = fb_info;
1677	fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1678
1679	/* this may free fb info */
1680	put_fb_info(fb_info);
1681	return 0;
1682}
1683
1684void remove_conflicting_framebuffers(struct apertures_struct *a,
1685				     const char *name, bool primary)
1686{
1687	mutex_lock(&registration_lock);
1688	do_remove_conflicting_framebuffers(a, name, primary);
1689	mutex_unlock(&registration_lock);
1690}
1691EXPORT_SYMBOL(remove_conflicting_framebuffers);
1692
1693/**
1694 *	register_framebuffer - registers a frame buffer device
1695 *	@fb_info: frame buffer info structure
1696 *
1697 *	Registers a frame buffer device @fb_info.
1698 *
1699 *	Returns negative errno on error, or zero for success.
1700 *
1701 */
1702int
1703register_framebuffer(struct fb_info *fb_info)
1704{
1705	int ret;
1706
1707	mutex_lock(&registration_lock);
1708	ret = do_register_framebuffer(fb_info);
1709	mutex_unlock(&registration_lock);
1710
1711	return ret;
1712}
1713
1714/**
1715 *	unregister_framebuffer - releases a frame buffer device
1716 *	@fb_info: frame buffer info structure
1717 *
1718 *	Unregisters a frame buffer device @fb_info.
1719 *
1720 *	Returns negative errno on error, or zero for success.
1721 *
1722 *      This function will also notify the framebuffer console
1723 *      to release the driver.
1724 *
1725 *      This is meant to be called within a driver's module_exit()
1726 *      function. If this is called outside module_exit(), ensure
1727 *      that the driver implements fb_open() and fb_release() to
1728 *      check that no processes are using the device.
1729 */
1730int
1731unregister_framebuffer(struct fb_info *fb_info)
1732{
1733	int ret;
1734
1735	mutex_lock(&registration_lock);
1736	ret = do_unregister_framebuffer(fb_info);
1737	mutex_unlock(&registration_lock);
1738
1739	return ret;
1740}
1741
1742/**
1743 *	fb_set_suspend - low level driver signals suspend
1744 *	@info: framebuffer affected
1745 *	@state: 0 = resuming, !=0 = suspending
1746 *
1747 *	This is meant to be used by low level drivers to
1748 * 	signal suspend/resume to the core & clients.
1749 *	It must be called with the console semaphore held
1750 */
1751void fb_set_suspend(struct fb_info *info, int state)
1752{
1753	struct fb_event event;
1754
1755	event.info = info;
1756	if (state) {
1757		fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
1758		info->state = FBINFO_STATE_SUSPENDED;
1759	} else {
1760		info->state = FBINFO_STATE_RUNNING;
1761		fb_notifier_call_chain(FB_EVENT_RESUME, &event);
1762	}
1763}
1764
1765/**
1766 *	fbmem_init - init frame buffer subsystem
1767 *
1768 *	Initialize the frame buffer subsystem.
1769 *
1770 *	NOTE: This function is _only_ to be called by drivers/char/mem.c.
1771 *
1772 */
1773
1774static int __init
1775fbmem_init(void)
1776{
1777	proc_create("fb", 0, NULL, &fb_proc_fops);
1778
1779	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
1780		printk("unable to get major %d for fb devs\n", FB_MAJOR);
1781
1782	fb_class = class_create(THIS_MODULE, "graphics");
1783	if (IS_ERR(fb_class)) {
1784		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
1785		fb_class = NULL;
1786	}
1787	return 0;
1788}
1789
1790#ifdef MODULE
1791module_init(fbmem_init);
1792static void __exit
1793fbmem_exit(void)
1794{
1795	remove_proc_entry("fb", NULL);
1796	class_destroy(fb_class);
1797	unregister_chrdev(FB_MAJOR, "fb");
1798}
1799
1800module_exit(fbmem_exit);
1801MODULE_LICENSE("GPL");
1802MODULE_DESCRIPTION("Framebuffer base");
1803#else
1804subsys_initcall(fbmem_init);
1805#endif
1806
1807int fb_new_modelist(struct fb_info *info)
1808{
1809	struct fb_event event;
1810	struct fb_var_screeninfo var = info->var;
1811	struct list_head *pos, *n;
1812	struct fb_modelist *modelist;
1813	struct fb_videomode *m, mode;
1814	int err = 1;
1815
1816	list_for_each_safe(pos, n, &info->modelist) {
1817		modelist = list_entry(pos, struct fb_modelist, list);
1818		m = &modelist->mode;
1819		fb_videomode_to_var(&var, m);
1820		var.activate = FB_ACTIVATE_TEST;
1821		err = fb_set_var(info, &var);
1822		fb_var_to_videomode(&mode, &var);
1823		if (err || !fb_mode_is_equal(m, &mode)) {
1824			list_del(pos);
1825			kfree(pos);
1826		}
1827	}
1828
1829	err = 1;
1830
1831	if (!list_empty(&info->modelist)) {
1832		if (!lock_fb_info(info))
1833			return -ENODEV;
1834		event.info = info;
1835		err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
1836		unlock_fb_info(info);
1837	}
1838
1839	return err;
1840}
1841
1842static char *video_options[FB_MAX] __read_mostly;
1843static int ofonly __read_mostly;
1844
1845/**
1846 * fb_get_options - get kernel boot parameters
1847 * @name:   framebuffer name as it would appear in
1848 *          the boot parameter line
1849 *          (video=<name>:<options>)
1850 * @option: the option will be stored here
1851 *
1852 * NOTE: Needed to maintain backwards compatibility
1853 */
1854int fb_get_options(char *name, char **option)
1855{
1856	char *opt, *options = NULL;
1857	int retval = 0;
1858	int name_len = strlen(name), i;
1859
1860	if (name_len && ofonly && strncmp(name, "offb", 4))
1861		retval = 1;
1862
1863	if (name_len && !retval) {
1864		for (i = 0; i < FB_MAX; i++) {
1865			if (video_options[i] == NULL)
1866				continue;
1867			if (!video_options[i][0])
1868				continue;
1869			opt = video_options[i];
1870			if (!strncmp(name, opt, name_len) &&
1871			    opt[name_len] == ':')
1872				options = opt + name_len + 1;
1873		}
1874	}
1875	if (options && !strncmp(options, "off", 3))
1876		retval = 1;
1877
1878	if (option)
1879		*option = options;
1880
1881	return retval;
1882}
1883
1884#ifndef MODULE
1885/**
1886 *	video_setup - process command line options
1887 *	@options: string of options
1888 *
1889 *	Process command line options for frame buffer subsystem.
1890 *
1891 *	NOTE: This function is a __setup and __init function.
1892 *            It only stores the options.  Drivers have to call
1893 *            fb_get_options() as necessary.
1894 *
1895 *	Returns zero.
1896 *
1897 */
1898static int __init video_setup(char *options)
1899{
1900	int i, global = 0;
1901
1902	if (!options || !*options)
1903 		global = 1;
1904
1905 	if (!global && !strncmp(options, "ofonly", 6)) {
1906 		ofonly = 1;
1907 		global = 1;
1908 	}
1909
1910 	if (!global && !strchr(options, ':')) {
1911 		fb_mode_option = options;
1912 		global = 1;
1913 	}
1914
1915 	if (!global) {
1916 		for (i = 0; i < FB_MAX; i++) {
1917 			if (video_options[i] == NULL) {
1918 				video_options[i] = options;
1919 				break;
1920 			}
1921
1922		}
1923	}
1924
1925	return 1;
1926}
1927__setup("video=", video_setup);
1928#endif
1929
1930    /*
1931     *  Visible symbols for modules
1932     */
1933
1934EXPORT_SYMBOL(register_framebuffer);
1935EXPORT_SYMBOL(unregister_framebuffer);
1936EXPORT_SYMBOL(num_registered_fb);
1937EXPORT_SYMBOL(registered_fb);
1938EXPORT_SYMBOL(fb_show_logo);
1939EXPORT_SYMBOL(fb_set_var);
1940EXPORT_SYMBOL(fb_blank);
1941EXPORT_SYMBOL(fb_pan_display);
1942EXPORT_SYMBOL(fb_get_buffer_offset);
1943EXPORT_SYMBOL(fb_set_suspend);
1944EXPORT_SYMBOL(fb_get_options);
1945
1946MODULE_LICENSE("GPL");
1947