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