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