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        u8 *buffer, *dst;
 701        u8 __iomem *src;
 702        int c, 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 = (u8 __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                fb_memcpy_fromfb(dst, src, c);
 742                dst += c;
 743                src += c;
 744
 745                if (copy_to_user(buf, buffer, c)) {
 746                        err = -EFAULT;
 747                        break;
 748                }
 749                *ppos += c;
 750                buf += c;
 751                cnt += c;
 752                count -= c;
 753        }
 754
 755        kfree(buffer);
 756
 757        return (err) ? err : cnt;
 758}
 759
 760static ssize_t
 761fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 762{
 763        unsigned long p = *ppos;
 764        struct inode *inode = file->f_path.dentry->d_inode;
 765        int fbidx = iminor(inode);
 766        struct fb_info *info = registered_fb[fbidx];
 767        u8 *buffer, *src;
 768        u8 __iomem *dst;
 769        int c, cnt = 0, err = 0;
 770        unsigned long total_size;
 771
 772        if (!info || !info->screen_base)
 773                return -ENODEV;
 774
 775        if (info->state != FBINFO_STATE_RUNNING)
 776                return -EPERM;
 777
 778        if (info->fbops->fb_write)
 779                return info->fbops->fb_write(info, buf, count, ppos);
 780        
 781        total_size = info->screen_size;
 782
 783        if (total_size == 0)
 784                total_size = info->fix.smem_len;
 785
 786        if (p > total_size)
 787                return -EFBIG;
 788
 789        if (count > total_size) {
 790                err = -EFBIG;
 791                count = total_size;
 792        }
 793
 794        if (count + p > total_size) {
 795                if (!err)
 796                        err = -ENOSPC;
 797
 798                count = total_size - p;
 799        }
 800
 801        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
 802                         GFP_KERNEL);
 803        if (!buffer)
 804                return -ENOMEM;
 805
 806        dst = (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                src = buffer;
 814
 815                if (copy_from_user(src, buf, c)) {
 816                        err = -EFAULT;
 817                        break;
 818                }
 819
 820                fb_memcpy_tofb(dst, src, c);
 821                dst += c;
 822                src += c;
 823                *ppos += c;
 824                buf += c;
 825                cnt += c;
 826                count -= c;
 827        }
 828
 829        kfree(buffer);
 830
 831        return (cnt) ? cnt : err;
 832}
 833
 834int
 835fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
 836{
 837        struct fb_fix_screeninfo *fix = &info->fix;
 838        unsigned int yres = info->var.yres;
 839        int err = 0;
 840
 841        if (var->yoffset > 0) {
 842                if (var->vmode & FB_VMODE_YWRAP) {
 843                        if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
 844                                err = -EINVAL;
 845                        else
 846                                yres = 0;
 847                } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
 848                        err = -EINVAL;
 849        }
 850
 851        if (var->xoffset > 0 && (!fix->xpanstep ||
 852                                 (var->xoffset % fix->xpanstep)))
 853                err = -EINVAL;
 854
 855        if (err || !info->fbops->fb_pan_display ||
 856            var->yoffset > info->var.yres_virtual - yres ||
 857            var->xoffset > info->var.xres_virtual - info->var.xres)
 858                return -EINVAL;
 859
 860        if ((err = info->fbops->fb_pan_display(var, info)))
 861                return err;
 862        info->var.xoffset = var->xoffset;
 863        info->var.yoffset = var->yoffset;
 864        if (var->vmode & FB_VMODE_YWRAP)
 865                info->var.vmode |= FB_VMODE_YWRAP;
 866        else
 867                info->var.vmode &= ~FB_VMODE_YWRAP;
 868        return 0;
 869}
 870
 871static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
 872                         u32 activate)
 873{
 874        struct fb_event event;
 875        struct fb_blit_caps caps, fbcaps;
 876        int err = 0;
 877
 878        memset(&caps, 0, sizeof(caps));
 879        memset(&fbcaps, 0, sizeof(fbcaps));
 880        caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
 881        event.info = info;
 882        event.data = &caps;
 883        fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
 884        info->fbops->fb_get_caps(info, &fbcaps, var);
 885
 886        if (((fbcaps.x ^ caps.x) & caps.x) ||
 887            ((fbcaps.y ^ caps.y) & caps.y) ||
 888            (fbcaps.len < caps.len))
 889                err = -EINVAL;
 890
 891        return err;
 892}
 893
 894int
 895fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 896{
 897        int flags = info->flags;
 898        int ret = 0;
 899
 900        if (var->activate & FB_ACTIVATE_INV_MODE) {
 901                struct fb_videomode mode1, mode2;
 902
 903                fb_var_to_videomode(&mode1, var);
 904                fb_var_to_videomode(&mode2, &info->var);
 905                /* make sure we don't delete the videomode of current var */
 906                ret = fb_mode_is_equal(&mode1, &mode2);
 907
 908                if (!ret) {
 909                    struct fb_event event;
 910
 911                    event.info = info;
 912                    event.data = &mode1;
 913                    ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
 914                }
 915
 916                if (!ret)
 917                    fb_delete_videomode(&mode1, &info->modelist);
 918
 919
 920                ret = (ret) ? -EINVAL : 0;
 921                goto done;
 922        }
 923
 924        if ((var->activate & FB_ACTIVATE_FORCE) ||
 925            memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
 926                u32 activate = var->activate;
 927
 928                if (!info->fbops->fb_check_var) {
 929                        *var = info->var;
 930                        goto done;
 931                }
 932
 933                ret = info->fbops->fb_check_var(var, info);
 934
 935                if (ret)
 936                        goto done;
 937
 938                if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 939                        struct fb_var_screeninfo old_var;
 940                        struct fb_videomode mode;
 941
 942                        if (info->fbops->fb_get_caps) {
 943                                ret = fb_check_caps(info, var, activate);
 944
 945                                if (ret)
 946                                        goto done;
 947                        }
 948
 949                        old_var = info->var;
 950                        info->var = *var;
 951
 952                        if (info->fbops->fb_set_par) {
 953                                ret = info->fbops->fb_set_par(info);
 954
 955                                if (ret) {
 956                                        info->var = old_var;
 957                                        printk(KERN_WARNING "detected "
 958                                                "fb_set_par error, "
 959                                                "error code: %d\n", ret);
 960                                        goto done;
 961                                }
 962                        }
 963
 964                        fb_pan_display(info, &info->var);
 965                        fb_set_cmap(&info->cmap, info);
 966                        fb_var_to_videomode(&mode, &info->var);
 967
 968                        if (info->modelist.prev && info->modelist.next &&
 969                            !list_empty(&info->modelist))
 970                                ret = fb_add_videomode(&mode, &info->modelist);
 971
 972                        if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
 973                                struct fb_event event;
 974                                int evnt = (activate & FB_ACTIVATE_ALL) ?
 975                                        FB_EVENT_MODE_CHANGE_ALL :
 976                                        FB_EVENT_MODE_CHANGE;
 977
 978                                info->flags &= ~FBINFO_MISC_USEREVENT;
 979                                event.info = info;
 980                                event.data = &mode;
 981                                fb_notifier_call_chain(evnt, &event);
 982                        }
 983                }
 984        }
 985
 986 done:
 987        return ret;
 988}
 989
 990int
 991fb_blank(struct fb_info *info, int blank)
 992{       
 993        int ret = -EINVAL;
 994
 995        if (blank > FB_BLANK_POWERDOWN)
 996                blank = FB_BLANK_POWERDOWN;
 997
 998        if (info->fbops->fb_blank)
 999                ret = info->fbops->fb_blank(blank, info);
1000
1001        if (!ret) {
1002                struct fb_event event;
1003
1004                event.info = info;
1005                event.data = &blank;
1006                fb_notifier_call_chain(FB_EVENT_BLANK, &event);
1007        }
1008
1009        return ret;
1010}
1011
1012static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
1013                        unsigned long arg)
1014{
1015        struct fb_ops *fb;
1016        struct fb_var_screeninfo var;
1017        struct fb_fix_screeninfo fix;
1018        struct fb_con2fbmap con2fb;
1019        struct fb_cmap cmap_from;
1020        struct fb_cmap_user cmap;
1021        struct fb_event event;
1022        void __user *argp = (void __user *)arg;
1023        long ret = 0;
1024
1025        switch (cmd) {
1026        case FBIOGET_VSCREENINFO:
1027                if (!lock_fb_info(info))
1028                        return -ENODEV;
1029                var = info->var;
1030                unlock_fb_info(info);
1031
1032                ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
1033                break;
1034        case FBIOPUT_VSCREENINFO:
1035                if (copy_from_user(&var, argp, sizeof(var)))
1036                        return -EFAULT;
1037                if (!lock_fb_info(info))
1038                        return -ENODEV;
1039                console_lock();
1040                info->flags |= FBINFO_MISC_USEREVENT;
1041                ret = fb_set_var(info, &var);
1042                info->flags &= ~FBINFO_MISC_USEREVENT;
1043                console_unlock();
1044                unlock_fb_info(info);
1045                if (!ret && copy_to_user(argp, &var, sizeof(var)))
1046                        ret = -EFAULT;
1047                break;
1048        case FBIOGET_FSCREENINFO:
1049                if (!lock_fb_info(info))
1050                        return -ENODEV;
1051                fix = info->fix;
1052                unlock_fb_info(info);
1053
1054                ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
1055                break;
1056        case FBIOPUTCMAP:
1057                if (copy_from_user(&cmap, argp, sizeof(cmap)))
1058                        return -EFAULT;
1059                ret = fb_set_user_cmap(&cmap, info);
1060                break;
1061        case FBIOGETCMAP:
1062                if (copy_from_user(&cmap, argp, sizeof(cmap)))
1063                        return -EFAULT;
1064                if (!lock_fb_info(info))
1065                        return -ENODEV;
1066                cmap_from = info->cmap;
1067                unlock_fb_info(info);
1068                ret = fb_cmap_to_user(&cmap_from, &cmap);
1069                break;
1070        case FBIOPAN_DISPLAY:
1071                if (copy_from_user(&var, argp, sizeof(var)))
1072                        return -EFAULT;
1073                if (!lock_fb_info(info))
1074                        return -ENODEV;
1075                console_lock();
1076                ret = fb_pan_display(info, &var);
1077                console_unlock();
1078                unlock_fb_info(info);
1079                if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
1080                        return -EFAULT;
1081                break;
1082        case FBIO_CURSOR:
1083                ret = -EINVAL;
1084                break;
1085        case FBIOGET_CON2FBMAP:
1086                if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1087                        return -EFAULT;
1088                if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1089                        return -EINVAL;
1090                con2fb.framebuffer = -1;
1091                event.data = &con2fb;
1092                if (!lock_fb_info(info))
1093                        return -ENODEV;
1094                event.info = info;
1095                fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
1096                unlock_fb_info(info);
1097                ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
1098                break;
1099        case FBIOPUT_CON2FBMAP:
1100                if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1101                        return -EFAULT;
1102                if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1103                        return -EINVAL;
1104                if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
1105                        return -EINVAL;
1106                if (!registered_fb[con2fb.framebuffer])
1107                        request_module("fb%d", con2fb.framebuffer);
1108                if (!registered_fb[con2fb.framebuffer]) {
1109                        ret = -EINVAL;
1110                        break;
1111                }
1112                event.data = &con2fb;
1113                if (!lock_fb_info(info))
1114                        return -ENODEV;
1115                event.info = info;
1116                ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
1117                unlock_fb_info(info);
1118                break;
1119        case FBIOBLANK:
1120                if (!lock_fb_info(info))
1121                        return -ENODEV;
1122                console_lock();
1123                info->flags |= FBINFO_MISC_USEREVENT;
1124                ret = fb_blank(info, arg);
1125                info->flags &= ~FBINFO_MISC_USEREVENT;
1126                console_unlock();
1127                unlock_fb_info(info);
1128                break;
1129        default:
1130                if (!lock_fb_info(info))
1131                        return -ENODEV;
1132                fb = info->fbops;
1133                if (fb->fb_ioctl)
1134                        ret = fb->fb_ioctl(info, cmd, arg);
1135                else
1136                        ret = -ENOTTY;
1137                unlock_fb_info(info);
1138        }
1139        return ret;
1140}
1141
1142static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1143{
1144        struct inode *inode = file->f_path.dentry->d_inode;
1145        int fbidx = iminor(inode);
1146        struct fb_info *info = registered_fb[fbidx];
1147
1148        return do_fb_ioctl(info, cmd, arg);
1149}
1150
1151#ifdef CONFIG_COMPAT
1152struct fb_fix_screeninfo32 {
1153        char                    id[16];
1154        compat_caddr_t          smem_start;
1155        u32                     smem_len;
1156        u32                     type;
1157        u32                     type_aux;
1158        u32                     visual;
1159        u16                     xpanstep;
1160        u16                     ypanstep;
1161        u16                     ywrapstep;
1162        u32                     line_length;
1163        compat_caddr_t          mmio_start;
1164        u32                     mmio_len;
1165        u32                     accel;
1166        u16                     reserved[3];
1167};
1168
1169struct fb_cmap32 {
1170        u32                     start;
1171        u32                     len;
1172        compat_caddr_t  red;
1173        compat_caddr_t  green;
1174        compat_caddr_t  blue;
1175        compat_caddr_t  transp;
1176};
1177
1178static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
1179                          unsigned long arg)
1180{
1181        struct fb_cmap_user __user *cmap;
1182        struct fb_cmap32 __user *cmap32;
1183        __u32 data;
1184        int err;
1185
1186        cmap = compat_alloc_user_space(sizeof(*cmap));
1187        cmap32 = compat_ptr(arg);
1188
1189        if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32)))
1190                return -EFAULT;
1191
1192        if (get_user(data, &cmap32->red) ||
1193            put_user(compat_ptr(data), &cmap->red) ||
1194            get_user(data, &cmap32->green) ||
1195            put_user(compat_ptr(data), &cmap->green) ||
1196            get_user(data, &cmap32->blue) ||
1197            put_user(compat_ptr(data), &cmap->blue) ||
1198            get_user(data, &cmap32->transp) ||
1199            put_user(compat_ptr(data), &cmap->transp))
1200                return -EFAULT;
1201
1202        err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
1203
1204        if (!err) {
1205                if (copy_in_user(&cmap32->start,
1206                                 &cmap->start,
1207                                 2 * sizeof(__u32)))
1208                        err = -EFAULT;
1209        }
1210        return err;
1211}
1212
1213static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
1214                                  struct fb_fix_screeninfo32 __user *fix32)
1215{
1216        __u32 data;
1217        int err;
1218
1219        err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
1220
1221        data = (__u32) (unsigned long) fix->smem_start;
1222        err |= put_user(data, &fix32->smem_start);
1223
1224        err |= put_user(fix->smem_len, &fix32->smem_len);
1225        err |= put_user(fix->type, &fix32->type);
1226        err |= put_user(fix->type_aux, &fix32->type_aux);
1227        err |= put_user(fix->visual, &fix32->visual);
1228        err |= put_user(fix->xpanstep, &fix32->xpanstep);
1229        err |= put_user(fix->ypanstep, &fix32->ypanstep);
1230        err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
1231        err |= put_user(fix->line_length, &fix32->line_length);
1232
1233        data = (__u32) (unsigned long) fix->mmio_start;
1234        err |= put_user(data, &fix32->mmio_start);
1235
1236        err |= put_user(fix->mmio_len, &fix32->mmio_len);
1237        err |= put_user(fix->accel, &fix32->accel);
1238        err |= copy_to_user(fix32->reserved, fix->reserved,
1239                            sizeof(fix->reserved));
1240
1241        return err;
1242}
1243
1244static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
1245                              unsigned long arg)
1246{
1247        mm_segment_t old_fs;
1248        struct fb_fix_screeninfo fix;
1249        struct fb_fix_screeninfo32 __user *fix32;
1250        int err;
1251
1252        fix32 = compat_ptr(arg);
1253
1254        old_fs = get_fs();
1255        set_fs(KERNEL_DS);
1256        err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
1257        set_fs(old_fs);
1258
1259        if (!err)
1260                err = do_fscreeninfo_to_user(&fix, fix32);
1261
1262        return err;
1263}
1264
1265static long fb_compat_ioctl(struct file *file, unsigned int cmd,
1266                            unsigned long arg)
1267{
1268        struct inode *inode = file->f_path.dentry->d_inode;
1269        int fbidx = iminor(inode);
1270        struct fb_info *info = registered_fb[fbidx];
1271        struct fb_ops *fb = info->fbops;
1272        long ret = -ENOIOCTLCMD;
1273
1274        switch(cmd) {
1275        case FBIOGET_VSCREENINFO:
1276        case FBIOPUT_VSCREENINFO:
1277        case FBIOPAN_DISPLAY:
1278        case FBIOGET_CON2FBMAP:
1279        case FBIOPUT_CON2FBMAP:
1280                arg = (unsigned long) compat_ptr(arg);
1281        case FBIOBLANK:
1282                ret = do_fb_ioctl(info, cmd, arg);
1283                break;
1284
1285        case FBIOGET_FSCREENINFO:
1286                ret = fb_get_fscreeninfo(info, cmd, arg);
1287                break;
1288
1289        case FBIOGETCMAP:
1290        case FBIOPUTCMAP:
1291                ret = fb_getput_cmap(info, cmd, arg);
1292                break;
1293
1294        default:
1295                if (fb->fb_compat_ioctl)
1296                        ret = fb->fb_compat_ioctl(info, cmd, arg);
1297                break;
1298        }
1299        return ret;
1300}
1301#endif
1302
1303static int
1304fb_mmap(struct file *file, struct vm_area_struct * vma)
1305{
1306        int fbidx = iminor(file->f_path.dentry->d_inode);
1307        struct fb_info *info = registered_fb[fbidx];
1308        struct fb_ops *fb = info->fbops;
1309        unsigned long off;
1310        unsigned long start;
1311        u32 len;
1312
1313        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
1314                return -EINVAL;
1315        off = vma->vm_pgoff << PAGE_SHIFT;
1316        if (!fb)
1317                return -ENODEV;
1318        mutex_lock(&info->mm_lock);
1319        if (fb->fb_mmap) {
1320                int res;
1321                res = fb->fb_mmap(info, vma);
1322                mutex_unlock(&info->mm_lock);
1323                return res;
1324        }
1325
1326        /* frame buffer memory */
1327        start = info->fix.smem_start;
1328        len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
1329        if (off >= len) {
1330                /* memory mapped io */
1331                off -= len;
1332                if (info->var.accel_flags) {
1333                        mutex_unlock(&info->mm_lock);
1334                        return -EINVAL;
1335                }
1336                start = info->fix.mmio_start;
1337                len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
1338        }
1339        mutex_unlock(&info->mm_lock);
1340        start &= PAGE_MASK;
1341        if ((vma->vm_end - vma->vm_start + off) > len)
1342                return -EINVAL;
1343        off += start;
1344        vma->vm_pgoff = off >> PAGE_SHIFT;
1345        /* This is an IO map - tell maydump to skip this VMA */
1346        vma->vm_flags |= VM_IO | VM_RESERVED;
1347        vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
1348        fb_pgprotect(file, vma, off);
1349        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
1350                             vma->vm_end - vma->vm_start, vma->vm_page_prot))
1351                return -EAGAIN;
1352        return 0;
1353}
1354
1355static int
1356fb_open(struct inode *inode, struct file *file)
1357__acquires(&info->lock)
1358__releases(&info->lock)
1359{
1360        int fbidx = iminor(inode);
1361        struct fb_info *info;
1362        int res = 0;
1363
1364        if (fbidx >= FB_MAX)
1365                return -ENODEV;
1366        info = registered_fb[fbidx];
1367        if (!info)
1368                request_module("fb%d", fbidx);
1369        info = registered_fb[fbidx];
1370        if (!info)
1371                return -ENODEV;
1372        mutex_lock(&info->lock);
1373        if (!try_module_get(info->fbops->owner)) {
1374                res = -ENODEV;
1375                goto out;
1376        }
1377        file->private_data = info;
1378        if (info->fbops->fb_open) {
1379                res = info->fbops->fb_open(info,1);
1380                if (res)
1381                        module_put(info->fbops->owner);
1382        }
1383#ifdef CONFIG_FB_DEFERRED_IO
1384        if (info->fbdefio)
1385                fb_deferred_io_open(info, inode, file);
1386#endif
1387out:
1388        mutex_unlock(&info->lock);
1389        return res;
1390}
1391
1392static int 
1393fb_release(struct inode *inode, struct file *file)
1394__acquires(&info->lock)
1395__releases(&info->lock)
1396{
1397        struct fb_info * const info = file->private_data;
1398
1399        mutex_lock(&info->lock);
1400        if (info->fbops->fb_release)
1401                info->fbops->fb_release(info,1);
1402        module_put(info->fbops->owner);
1403        mutex_unlock(&info->lock);
1404        return 0;
1405}
1406
1407static const struct file_operations fb_fops = {
1408        .owner =        THIS_MODULE,
1409        .read =         fb_read,
1410        .write =        fb_write,
1411        .unlocked_ioctl = fb_ioctl,
1412#ifdef CONFIG_COMPAT
1413        .compat_ioctl = fb_compat_ioctl,
1414#endif
1415        .mmap =         fb_mmap,
1416        .open =         fb_open,
1417        .release =      fb_release,
1418#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1419        .get_unmapped_area = get_fb_unmapped_area,
1420#endif
1421#ifdef CONFIG_FB_DEFERRED_IO
1422        .fsync =        fb_deferred_io_fsync,
1423#endif
1424        .llseek =       default_llseek,
1425};
1426
1427struct class *fb_class;
1428EXPORT_SYMBOL(fb_class);
1429
1430static int fb_check_foreignness(struct fb_info *fi)
1431{
1432        const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
1433
1434        fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
1435
1436#ifdef __BIG_ENDIAN
1437        fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
1438#else
1439        fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
1440#endif /* __BIG_ENDIAN */
1441
1442        if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
1443                pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
1444                       "support this framebuffer\n", fi->fix.id);
1445                return -ENOSYS;
1446        } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
1447                pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
1448                       "support this framebuffer\n", fi->fix.id);
1449                return -ENOSYS;
1450        }
1451
1452        return 0;
1453}
1454
1455static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
1456{
1457        /* is the generic aperture base the same as the HW one */
1458        if (gen->base == hw->base)
1459                return true;
1460        /* is the generic aperture base inside the hw base->hw base+size */
1461        if (gen->base > hw->base && gen->base < hw->base + hw->size)
1462                return true;
1463        return false;
1464}
1465
1466static bool fb_do_apertures_overlap(struct apertures_struct *gena,
1467                                    struct apertures_struct *hwa)
1468{
1469        int i, j;
1470        if (!hwa || !gena)
1471                return false;
1472
1473        for (i = 0; i < hwa->count; ++i) {
1474                struct aperture *h = &hwa->ranges[i];
1475                for (j = 0; j < gena->count; ++j) {
1476                        struct aperture *g = &gena->ranges[j];
1477                        printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
1478                                (unsigned long long)g->base,
1479                                (unsigned long long)g->size,
1480                                (unsigned long long)h->base,
1481                                (unsigned long long)h->size);
1482                        if (apertures_overlap(g, h))
1483                                return true;
1484                }
1485        }
1486
1487        return false;
1488}
1489
1490#define VGA_FB_PHYS 0xA0000
1491void remove_conflicting_framebuffers(struct apertures_struct *a,
1492                                     const char *name, bool primary)
1493{
1494        int i;
1495
1496        /* check all firmware fbs and kick off if the base addr overlaps */
1497        for (i = 0 ; i < FB_MAX; i++) {
1498                struct apertures_struct *gen_aper;
1499                if (!registered_fb[i])
1500                        continue;
1501
1502                if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
1503                        continue;
1504
1505                gen_aper = registered_fb[i]->apertures;
1506                if (fb_do_apertures_overlap(gen_aper, a) ||
1507                        (primary && gen_aper && gen_aper->count &&
1508                         gen_aper->ranges[0].base == VGA_FB_PHYS)) {
1509
1510                        printk(KERN_ERR "fb: conflicting fb hw usage "
1511                               "%s vs %s - removing generic driver\n",
1512                               name, registered_fb[i]->fix.id);
1513                        unregister_framebuffer(registered_fb[i]);
1514                }
1515        }
1516}
1517EXPORT_SYMBOL(remove_conflicting_framebuffers);
1518
1519/**
1520 *      register_framebuffer - registers a frame buffer device
1521 *      @fb_info: frame buffer info structure
1522 *
1523 *      Registers a frame buffer device @fb_info.
1524 *
1525 *      Returns negative errno on error, or zero for success.
1526 *
1527 */
1528
1529int
1530register_framebuffer(struct fb_info *fb_info)
1531{
1532        int i;
1533        struct fb_event event;
1534        struct fb_videomode mode;
1535
1536        if (num_registered_fb == FB_MAX)
1537                return -ENXIO;
1538
1539        if (fb_check_foreignness(fb_info))
1540                return -ENOSYS;
1541
1542        remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
1543                                         fb_is_primary_device(fb_info));
1544
1545        num_registered_fb++;
1546        for (i = 0 ; i < FB_MAX; i++)
1547                if (!registered_fb[i])
1548                        break;
1549        fb_info->node = i;
1550        mutex_init(&fb_info->lock);
1551        mutex_init(&fb_info->mm_lock);
1552
1553        fb_info->dev = device_create(fb_class, fb_info->device,
1554                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
1555        if (IS_ERR(fb_info->dev)) {
1556                /* Not fatal */
1557                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1558                fb_info->dev = NULL;
1559        } else
1560                fb_init_device(fb_info);
1561
1562        if (fb_info->pixmap.addr == NULL) {
1563                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
1564                if (fb_info->pixmap.addr) {
1565                        fb_info->pixmap.size = FBPIXMAPSIZE;
1566                        fb_info->pixmap.buf_align = 1;
1567                        fb_info->pixmap.scan_align = 1;
1568                        fb_info->pixmap.access_align = 32;
1569                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
1570                }
1571        }       
1572        fb_info->pixmap.offset = 0;
1573
1574        if (!fb_info->pixmap.blit_x)
1575                fb_info->pixmap.blit_x = ~(u32)0;
1576
1577        if (!fb_info->pixmap.blit_y)
1578                fb_info->pixmap.blit_y = ~(u32)0;
1579
1580        if (!fb_info->modelist.prev || !fb_info->modelist.next)
1581                INIT_LIST_HEAD(&fb_info->modelist);
1582
1583        fb_var_to_videomode(&mode, &fb_info->var);
1584        fb_add_videomode(&mode, &fb_info->modelist);
1585        registered_fb[i] = fb_info;
1586
1587        event.info = fb_info;
1588        if (!lock_fb_info(fb_info))
1589                return -ENODEV;
1590        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
1591        unlock_fb_info(fb_info);
1592        return 0;
1593}
1594
1595
1596/**
1597 *      unregister_framebuffer - releases a frame buffer device
1598 *      @fb_info: frame buffer info structure
1599 *
1600 *      Unregisters a frame buffer device @fb_info.
1601 *
1602 *      Returns negative errno on error, or zero for success.
1603 *
1604 *      This function will also notify the framebuffer console
1605 *      to release the driver.
1606 *
1607 *      This is meant to be called within a driver's module_exit()
1608 *      function. If this is called outside module_exit(), ensure
1609 *      that the driver implements fb_open() and fb_release() to
1610 *      check that no processes are using the device.
1611 */
1612
1613int
1614unregister_framebuffer(struct fb_info *fb_info)
1615{
1616        struct fb_event event;
1617        int i, ret = 0;
1618
1619        i = fb_info->node;
1620        if (!registered_fb[i]) {
1621                ret = -EINVAL;
1622                goto done;
1623        }
1624
1625
1626        if (!lock_fb_info(fb_info))
1627                return -ENODEV;
1628        event.info = fb_info;
1629        ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
1630        unlock_fb_info(fb_info);
1631
1632        if (ret) {
1633                ret = -EINVAL;
1634                goto done;
1635        }
1636
1637        if (fb_info->pixmap.addr &&
1638            (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1639                kfree(fb_info->pixmap.addr);
1640        fb_destroy_modelist(&fb_info->modelist);
1641        registered_fb[i]=NULL;
1642        num_registered_fb--;
1643        fb_cleanup_device(fb_info);
1644        device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1645        event.info = fb_info;
1646        fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1647
1648        /* this may free fb info */
1649        if (fb_info->fbops->fb_destroy)
1650                fb_info->fbops->fb_destroy(fb_info);
1651done:
1652        return ret;
1653}
1654
1655/**
1656 *      fb_set_suspend - low level driver signals suspend
1657 *      @info: framebuffer affected
1658 *      @state: 0 = resuming, !=0 = suspending
1659 *
1660 *      This is meant to be used by low level drivers to
1661 *      signal suspend/resume to the core & clients.
1662 *      It must be called with the console semaphore held
1663 */
1664void fb_set_suspend(struct fb_info *info, int state)
1665{
1666        struct fb_event event;
1667
1668        if (!lock_fb_info(info))
1669                return;
1670        event.info = info;
1671        if (state) {
1672                fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
1673                info->state = FBINFO_STATE_SUSPENDED;
1674        } else {
1675                info->state = FBINFO_STATE_RUNNING;
1676                fb_notifier_call_chain(FB_EVENT_RESUME, &event);
1677        }
1678        unlock_fb_info(info);
1679}
1680
1681/**
1682 *      fbmem_init - init frame buffer subsystem
1683 *
1684 *      Initialize the frame buffer subsystem.
1685 *
1686 *      NOTE: This function is _only_ to be called by drivers/char/mem.c.
1687 *
1688 */
1689
1690static int __init
1691fbmem_init(void)
1692{
1693        proc_create("fb", 0, NULL, &fb_proc_fops);
1694
1695        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
1696                printk("unable to get major %d for fb devs\n", FB_MAJOR);
1697
1698        fb_class = class_create(THIS_MODULE, "graphics");
1699        if (IS_ERR(fb_class)) {
1700                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
1701                fb_class = NULL;
1702        }
1703        return 0;
1704}
1705
1706#ifdef MODULE
1707module_init(fbmem_init);
1708static void __exit
1709fbmem_exit(void)
1710{
1711        remove_proc_entry("fb", NULL);
1712        class_destroy(fb_class);
1713        unregister_chrdev(FB_MAJOR, "fb");
1714}
1715
1716module_exit(fbmem_exit);
1717MODULE_LICENSE("GPL");
1718MODULE_DESCRIPTION("Framebuffer base");
1719#else
1720subsys_initcall(fbmem_init);
1721#endif
1722
1723int fb_new_modelist(struct fb_info *info)
1724{
1725        struct fb_event event;
1726        struct fb_var_screeninfo var = info->var;
1727        struct list_head *pos, *n;
1728        struct fb_modelist *modelist;
1729        struct fb_videomode *m, mode;
1730        int err = 1;
1731
1732        list_for_each_safe(pos, n, &info->modelist) {
1733                modelist = list_entry(pos, struct fb_modelist, list);
1734                m = &modelist->mode;
1735                fb_videomode_to_var(&var, m);
1736                var.activate = FB_ACTIVATE_TEST;
1737                err = fb_set_var(info, &var);
1738                fb_var_to_videomode(&mode, &var);
1739                if (err || !fb_mode_is_equal(m, &mode)) {
1740                        list_del(pos);
1741                        kfree(pos);
1742                }
1743        }
1744
1745        err = 1;
1746
1747        if (!list_empty(&info->modelist)) {
1748                if (!lock_fb_info(info))
1749                        return -ENODEV;
1750                event.info = info;
1751                err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
1752                unlock_fb_info(info);
1753        }
1754
1755        return err;
1756}
1757
1758static char *video_options[FB_MAX] __read_mostly;
1759static int ofonly __read_mostly;
1760
1761/**
1762 * fb_get_options - get kernel boot parameters
1763 * @name:   framebuffer name as it would appear in
1764 *          the boot parameter line
1765 *          (video=<name>:<options>)
1766 * @option: the option will be stored here
1767 *
1768 * NOTE: Needed to maintain backwards compatibility
1769 */
1770int fb_get_options(char *name, char **option)
1771{
1772        char *opt, *options = NULL;
1773        int retval = 0;
1774        int name_len = strlen(name), i;
1775
1776        if (name_len && ofonly && strncmp(name, "offb", 4))
1777                retval = 1;
1778
1779        if (name_len && !retval) {
1780                for (i = 0; i < FB_MAX; i++) {
1781                        if (video_options[i] == NULL)
1782                                continue;
1783                        if (!video_options[i][0])
1784                                continue;
1785                        opt = video_options[i];
1786                        if (!strncmp(name, opt, name_len) &&
1787                            opt[name_len] == ':')
1788                                options = opt + name_len + 1;
1789                }
1790        }
1791        if (options && !strncmp(options, "off", 3))
1792                retval = 1;
1793
1794        if (option)
1795                *option = options;
1796
1797        return retval;
1798}
1799
1800#ifndef MODULE
1801/**
1802 *      video_setup - process command line options
1803 *      @options: string of options
1804 *
1805 *      Process command line options for frame buffer subsystem.
1806 *
1807 *      NOTE: This function is a __setup and __init function.
1808 *            It only stores the options.  Drivers have to call
1809 *            fb_get_options() as necessary.
1810 *
1811 *      Returns zero.
1812 *
1813 */
1814static int __init video_setup(char *options)
1815{
1816        int i, global = 0;
1817
1818        if (!options || !*options)
1819                global = 1;
1820
1821        if (!global && !strncmp(options, "ofonly", 6)) {
1822                ofonly = 1;
1823                global = 1;
1824        }
1825
1826        if (!global && !strchr(options, ':')) {
1827                fb_mode_option = options;
1828                global = 1;
1829        }
1830
1831        if (!global) {
1832                for (i = 0; i < FB_MAX; i++) {
1833                        if (video_options[i] == NULL) {
1834                                video_options[i] = options;
1835                                break;
1836                        }
1837
1838                }
1839        }
1840
1841        return 1;
1842}
1843__setup("video=", video_setup);
1844#endif
1845
1846    /*
1847     *  Visible symbols for modules
1848     */
1849
1850EXPORT_SYMBOL(register_framebuffer);
1851EXPORT_SYMBOL(unregister_framebuffer);
1852EXPORT_SYMBOL(num_registered_fb);
1853EXPORT_SYMBOL(registered_fb);
1854EXPORT_SYMBOL(fb_show_logo);
1855EXPORT_SYMBOL(fb_set_var);
1856EXPORT_SYMBOL(fb_blank);
1857EXPORT_SYMBOL(fb_pan_display);
1858EXPORT_SYMBOL(fb_get_buffer_offset);
1859EXPORT_SYMBOL(fb_set_suspend);
1860EXPORT_SYMBOL(fb_get_options);
1861
1862MODULE_LICENSE("GPL");
1863