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