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