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