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