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