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