linux/drivers/video/vfb.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/video/vfb.c -- Virtual frame buffer device
   3 *
   4 *      Copyright (C) 2002 James Simmons
   5 *
   6 *      Copyright (C) 1997 Geert Uytterhoeven
   7 *
   8 *  This file is subject to the terms and conditions of the GNU General Public
   9 *  License. See the file COPYING in the main directory of this archive for
  10 *  more details.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/errno.h>
  16#include <linux/string.h>
  17#include <linux/mm.h>
  18#include <linux/slab.h>
  19#include <linux/vmalloc.h>
  20#include <linux/delay.h>
  21#include <linux/interrupt.h>
  22#include <linux/platform_device.h>
  23
  24#include <linux/fb.h>
  25#include <linux/init.h>
  26
  27    /*
  28     *  RAM we reserve for the frame buffer. This defines the maximum screen
  29     *  size
  30     *
  31     *  The default can be overridden if the driver is compiled as a module
  32     */
  33
  34#define VIDEOMEMSIZE    (1*1024*1024)   /* 1 MB */
  35
  36static void *videomemory;
  37static u_long videomemorysize = VIDEOMEMSIZE;
  38module_param(videomemorysize, ulong, 0);
  39
  40/**********************************************************************
  41 *
  42 * Memory management
  43 *
  44 **********************************************************************/
  45static void *rvmalloc(unsigned long size)
  46{
  47        void *mem;
  48        unsigned long adr;
  49
  50        size = PAGE_ALIGN(size);
  51        mem = vmalloc_32(size);
  52        if (!mem)
  53                return NULL;
  54
  55        memset(mem, 0, size); /* Clear the ram out, no junk to the user */
  56        adr = (unsigned long) mem;
  57        while (size > 0) {
  58                SetPageReserved(vmalloc_to_page((void *)adr));
  59                adr += PAGE_SIZE;
  60                size -= PAGE_SIZE;
  61        }
  62
  63        return mem;
  64}
  65
  66static void rvfree(void *mem, unsigned long size)
  67{
  68        unsigned long adr;
  69
  70        if (!mem)
  71                return;
  72
  73        adr = (unsigned long) mem;
  74        while ((long) size > 0) {
  75                ClearPageReserved(vmalloc_to_page((void *)adr));
  76                adr += PAGE_SIZE;
  77                size -= PAGE_SIZE;
  78        }
  79        vfree(mem);
  80}
  81
  82static struct fb_var_screeninfo vfb_default __initdata = {
  83        .xres =         640,
  84        .yres =         480,
  85        .xres_virtual = 640,
  86        .yres_virtual = 480,
  87        .bits_per_pixel = 8,
  88        .red =          { 0, 8, 0 },
  89        .green =        { 0, 8, 0 },
  90        .blue =         { 0, 8, 0 },
  91        .activate =     FB_ACTIVATE_TEST,
  92        .height =       -1,
  93        .width =        -1,
  94        .pixclock =     20000,
  95        .left_margin =  64,
  96        .right_margin = 64,
  97        .upper_margin = 32,
  98        .lower_margin = 32,
  99        .hsync_len =    64,
 100        .vsync_len =    2,
 101        .vmode =        FB_VMODE_NONINTERLACED,
 102};
 103
 104static struct fb_fix_screeninfo vfb_fix __initdata = {
 105        .id =           "Virtual FB",
 106        .type =         FB_TYPE_PACKED_PIXELS,
 107        .visual =       FB_VISUAL_PSEUDOCOLOR,
 108        .xpanstep =     1,
 109        .ypanstep =     1,
 110        .ywrapstep =    1,
 111        .accel =        FB_ACCEL_NONE,
 112};
 113
 114static int vfb_enable __initdata = 0;   /* disabled by default */
 115module_param(vfb_enable, bool, 0);
 116
 117static int vfb_check_var(struct fb_var_screeninfo *var,
 118                         struct fb_info *info);
 119static int vfb_set_par(struct fb_info *info);
 120static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 121                         u_int transp, struct fb_info *info);
 122static int vfb_pan_display(struct fb_var_screeninfo *var,
 123                           struct fb_info *info);
 124static int vfb_mmap(struct fb_info *info,
 125                    struct vm_area_struct *vma);
 126
 127static struct fb_ops vfb_ops = {
 128        .fb_read        = fb_sys_read,
 129        .fb_write       = fb_sys_write,
 130        .fb_check_var   = vfb_check_var,
 131        .fb_set_par     = vfb_set_par,
 132        .fb_setcolreg   = vfb_setcolreg,
 133        .fb_pan_display = vfb_pan_display,
 134        .fb_fillrect    = sys_fillrect,
 135        .fb_copyarea    = sys_copyarea,
 136        .fb_imageblit   = sys_imageblit,
 137        .fb_mmap        = vfb_mmap,
 138};
 139
 140    /*
 141     *  Internal routines
 142     */
 143
 144static u_long get_line_length(int xres_virtual, int bpp)
 145{
 146        u_long length;
 147
 148        length = xres_virtual * bpp;
 149        length = (length + 31) & ~31;
 150        length >>= 3;
 151        return (length);
 152}
 153
 154    /*
 155     *  Setting the video mode has been split into two parts.
 156     *  First part, xxxfb_check_var, must not write anything
 157     *  to hardware, it should only verify and adjust var.
 158     *  This means it doesn't alter par but it does use hardware
 159     *  data from it to check this var. 
 160     */
 161
 162static int vfb_check_var(struct fb_var_screeninfo *var,
 163                         struct fb_info *info)
 164{
 165        u_long line_length;
 166
 167        /*
 168         *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
 169         *  as FB_VMODE_SMOOTH_XPAN is only used internally
 170         */
 171
 172        if (var->vmode & FB_VMODE_CONUPDATE) {
 173                var->vmode |= FB_VMODE_YWRAP;
 174                var->xoffset = info->var.xoffset;
 175                var->yoffset = info->var.yoffset;
 176        }
 177
 178        /*
 179         *  Some very basic checks
 180         */
 181        if (!var->xres)
 182                var->xres = 1;
 183        if (!var->yres)
 184                var->yres = 1;
 185        if (var->xres > var->xres_virtual)
 186                var->xres_virtual = var->xres;
 187        if (var->yres > var->yres_virtual)
 188                var->yres_virtual = var->yres;
 189        if (var->bits_per_pixel <= 1)
 190                var->bits_per_pixel = 1;
 191        else if (var->bits_per_pixel <= 8)
 192                var->bits_per_pixel = 8;
 193        else if (var->bits_per_pixel <= 16)
 194                var->bits_per_pixel = 16;
 195        else if (var->bits_per_pixel <= 24)
 196                var->bits_per_pixel = 24;
 197        else if (var->bits_per_pixel <= 32)
 198                var->bits_per_pixel = 32;
 199        else
 200                return -EINVAL;
 201
 202        if (var->xres_virtual < var->xoffset + var->xres)
 203                var->xres_virtual = var->xoffset + var->xres;
 204        if (var->yres_virtual < var->yoffset + var->yres)
 205                var->yres_virtual = var->yoffset + var->yres;
 206
 207        /*
 208         *  Memory limit
 209         */
 210        line_length =
 211            get_line_length(var->xres_virtual, var->bits_per_pixel);
 212        if (line_length * var->yres_virtual > videomemorysize)
 213                return -ENOMEM;
 214
 215        /*
 216         * Now that we checked it we alter var. The reason being is that the video
 217         * mode passed in might not work but slight changes to it might make it 
 218         * work. This way we let the user know what is acceptable.
 219         */
 220        switch (var->bits_per_pixel) {
 221        case 1:
 222        case 8:
 223                var->red.offset = 0;
 224                var->red.length = 8;
 225                var->green.offset = 0;
 226                var->green.length = 8;
 227                var->blue.offset = 0;
 228                var->blue.length = 8;
 229                var->transp.offset = 0;
 230                var->transp.length = 0;
 231                break;
 232        case 16:                /* RGBA 5551 */
 233                if (var->transp.length) {
 234                        var->red.offset = 0;
 235                        var->red.length = 5;
 236                        var->green.offset = 5;
 237                        var->green.length = 5;
 238                        var->blue.offset = 10;
 239                        var->blue.length = 5;
 240                        var->transp.offset = 15;
 241                        var->transp.length = 1;
 242                } else {        /* RGB 565 */
 243                        var->red.offset = 0;
 244                        var->red.length = 5;
 245                        var->green.offset = 5;
 246                        var->green.length = 6;
 247                        var->blue.offset = 11;
 248                        var->blue.length = 5;
 249                        var->transp.offset = 0;
 250                        var->transp.length = 0;
 251                }
 252                break;
 253        case 24:                /* RGB 888 */
 254                var->red.offset = 0;
 255                var->red.length = 8;
 256                var->green.offset = 8;
 257                var->green.length = 8;
 258                var->blue.offset = 16;
 259                var->blue.length = 8;
 260                var->transp.offset = 0;
 261                var->transp.length = 0;
 262                break;
 263        case 32:                /* RGBA 8888 */
 264                var->red.offset = 0;
 265                var->red.length = 8;
 266                var->green.offset = 8;
 267                var->green.length = 8;
 268                var->blue.offset = 16;
 269                var->blue.length = 8;
 270                var->transp.offset = 24;
 271                var->transp.length = 8;
 272                break;
 273        }
 274        var->red.msb_right = 0;
 275        var->green.msb_right = 0;
 276        var->blue.msb_right = 0;
 277        var->transp.msb_right = 0;
 278
 279        return 0;
 280}
 281
 282/* This routine actually sets the video mode. It's in here where we
 283 * the hardware state info->par and fix which can be affected by the 
 284 * change in par. For this driver it doesn't do much. 
 285 */
 286static int vfb_set_par(struct fb_info *info)
 287{
 288        info->fix.line_length = get_line_length(info->var.xres_virtual,
 289                                                info->var.bits_per_pixel);
 290        return 0;
 291}
 292
 293    /*
 294     *  Set a single color register. The values supplied are already
 295     *  rounded down to the hardware's capabilities (according to the
 296     *  entries in the var structure). Return != 0 for invalid regno.
 297     */
 298
 299static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 300                         u_int transp, struct fb_info *info)
 301{
 302        if (regno >= 256)       /* no. of hw registers */
 303                return 1;
 304        /*
 305         * Program hardware... do anything you want with transp
 306         */
 307
 308        /* grayscale works only partially under directcolor */
 309        if (info->var.grayscale) {
 310                /* grayscale = 0.30*R + 0.59*G + 0.11*B */
 311                red = green = blue =
 312                    (red * 77 + green * 151 + blue * 28) >> 8;
 313        }
 314
 315        /* Directcolor:
 316         *   var->{color}.offset contains start of bitfield
 317         *   var->{color}.length contains length of bitfield
 318         *   {hardwarespecific} contains width of RAMDAC
 319         *   cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
 320         *   RAMDAC[X] is programmed to (red, green, blue)
 321         *
 322         * Pseudocolor:
 323         *    var->{color}.offset is 0 unless the palette index takes less than
 324         *                        bits_per_pixel bits and is stored in the upper
 325         *                        bits of the pixel value
 326         *    var->{color}.length is set so that 1 << length is the number of available
 327         *                        palette entries
 328         *    cmap is not used
 329         *    RAMDAC[X] is programmed to (red, green, blue)
 330         *
 331         * Truecolor:
 332         *    does not use DAC. Usually 3 are present.
 333         *    var->{color}.offset contains start of bitfield
 334         *    var->{color}.length contains length of bitfield
 335         *    cmap is programmed to (red << red.offset) | (green << green.offset) |
 336         *                      (blue << blue.offset) | (transp << transp.offset)
 337         *    RAMDAC does not exist
 338         */
 339#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
 340        switch (info->fix.visual) {
 341        case FB_VISUAL_TRUECOLOR:
 342        case FB_VISUAL_PSEUDOCOLOR:
 343                red = CNVT_TOHW(red, info->var.red.length);
 344                green = CNVT_TOHW(green, info->var.green.length);
 345                blue = CNVT_TOHW(blue, info->var.blue.length);
 346                transp = CNVT_TOHW(transp, info->var.transp.length);
 347                break;
 348        case FB_VISUAL_DIRECTCOLOR:
 349                red = CNVT_TOHW(red, 8);        /* expect 8 bit DAC */
 350                green = CNVT_TOHW(green, 8);
 351                blue = CNVT_TOHW(blue, 8);
 352                /* hey, there is bug in transp handling... */
 353                transp = CNVT_TOHW(transp, 8);
 354                break;
 355        }
 356#undef CNVT_TOHW
 357        /* Truecolor has hardware independent palette */
 358        if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
 359                u32 v;
 360
 361                if (regno >= 16)
 362                        return 1;
 363
 364                v = (red << info->var.red.offset) |
 365                    (green << info->var.green.offset) |
 366                    (blue << info->var.blue.offset) |
 367                    (transp << info->var.transp.offset);
 368                switch (info->var.bits_per_pixel) {
 369                case 8:
 370                        break;
 371                case 16:
 372                        ((u32 *) (info->pseudo_palette))[regno] = v;
 373                        break;
 374                case 24:
 375                case 32:
 376                        ((u32 *) (info->pseudo_palette))[regno] = v;
 377                        break;
 378                }
 379                return 0;
 380        }
 381        return 0;
 382}
 383
 384    /*
 385     *  Pan or Wrap the Display
 386     *
 387     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
 388     */
 389
 390static int vfb_pan_display(struct fb_var_screeninfo *var,
 391                           struct fb_info *info)
 392{
 393        if (var->vmode & FB_VMODE_YWRAP) {
 394                if (var->yoffset < 0
 395                    || var->yoffset >= info->var.yres_virtual
 396                    || var->xoffset)
 397                        return -EINVAL;
 398        } else {
 399                if (var->xoffset + var->xres > info->var.xres_virtual ||
 400                    var->yoffset + var->yres > info->var.yres_virtual)
 401                        return -EINVAL;
 402        }
 403        info->var.xoffset = var->xoffset;
 404        info->var.yoffset = var->yoffset;
 405        if (var->vmode & FB_VMODE_YWRAP)
 406                info->var.vmode |= FB_VMODE_YWRAP;
 407        else
 408                info->var.vmode &= ~FB_VMODE_YWRAP;
 409        return 0;
 410}
 411
 412    /*
 413     *  Most drivers don't need their own mmap function 
 414     */
 415
 416static int vfb_mmap(struct fb_info *info,
 417                    struct vm_area_struct *vma)
 418{
 419        unsigned long start = vma->vm_start;
 420        unsigned long size = vma->vm_end - vma->vm_start;
 421        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 422        unsigned long page, pos;
 423
 424        if (offset + size > info->fix.smem_len) {
 425                return -EINVAL;
 426        }
 427
 428        pos = (unsigned long)info->fix.smem_start + offset;
 429
 430        while (size > 0) {
 431                page = vmalloc_to_pfn((void *)pos);
 432                if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
 433                        return -EAGAIN;
 434                }
 435                start += PAGE_SIZE;
 436                pos += PAGE_SIZE;
 437                if (size > PAGE_SIZE)
 438                        size -= PAGE_SIZE;
 439                else
 440                        size = 0;
 441        }
 442
 443        vma->vm_flags |= VM_RESERVED;   /* avoid to swap out this VMA */
 444        return 0;
 445
 446}
 447
 448#ifndef MODULE
 449/*
 450 * The virtual framebuffer driver is only enabled if explicitly
 451 * requested by passing 'video=vfb:' (or any actual options).
 452 */
 453static int __init vfb_setup(char *options)
 454{
 455        char *this_opt;
 456
 457        vfb_enable = 0;
 458
 459        if (!options)
 460                return 1;
 461
 462        vfb_enable = 1;
 463
 464        if (!*options)
 465                return 1;
 466
 467        while ((this_opt = strsep(&options, ",")) != NULL) {
 468                if (!*this_opt)
 469                        continue;
 470                /* Test disable for backwards compatibility */
 471                if (!strcmp(this_opt, "disable"))
 472                        vfb_enable = 0;
 473        }
 474        return 1;
 475}
 476#endif  /*  MODULE  */
 477
 478    /*
 479     *  Initialisation
 480     */
 481
 482static int __init vfb_probe(struct platform_device *dev)
 483{
 484        struct fb_info *info;
 485        int retval = -ENOMEM;
 486
 487        /*
 488         * For real video cards we use ioremap.
 489         */
 490        if (!(videomemory = rvmalloc(videomemorysize)))
 491                return retval;
 492
 493        /*
 494         * VFB must clear memory to prevent kernel info
 495         * leakage into userspace
 496         * VGA-based drivers MUST NOT clear memory if
 497         * they want to be able to take over vgacon
 498         */
 499        memset(videomemory, 0, videomemorysize);
 500
 501        info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
 502        if (!info)
 503                goto err;
 504
 505        info->screen_base = (char __iomem *)videomemory;
 506        info->fbops = &vfb_ops;
 507
 508        retval = fb_find_mode(&info->var, info, NULL,
 509                              NULL, 0, NULL, 8);
 510
 511        if (!retval || (retval == 4))
 512                info->var = vfb_default;
 513        vfb_fix.smem_start = (unsigned long) videomemory;
 514        vfb_fix.smem_len = videomemorysize;
 515        info->fix = vfb_fix;
 516        info->pseudo_palette = info->par;
 517        info->par = NULL;
 518        info->flags = FBINFO_FLAG_DEFAULT;
 519
 520        retval = fb_alloc_cmap(&info->cmap, 256, 0);
 521        if (retval < 0)
 522                goto err1;
 523
 524        retval = register_framebuffer(info);
 525        if (retval < 0)
 526                goto err2;
 527        platform_set_drvdata(dev, info);
 528
 529        printk(KERN_INFO
 530               "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
 531               info->node, videomemorysize >> 10);
 532        return 0;
 533err2:
 534        fb_dealloc_cmap(&info->cmap);
 535err1:
 536        framebuffer_release(info);
 537err:
 538        rvfree(videomemory, videomemorysize);
 539        return retval;
 540}
 541
 542static int vfb_remove(struct platform_device *dev)
 543{
 544        struct fb_info *info = platform_get_drvdata(dev);
 545
 546        if (info) {
 547                unregister_framebuffer(info);
 548                rvfree(videomemory, videomemorysize);
 549                fb_dealloc_cmap(&info->cmap);
 550                framebuffer_release(info);
 551        }
 552        return 0;
 553}
 554
 555static struct platform_driver vfb_driver = {
 556        .probe  = vfb_probe,
 557        .remove = vfb_remove,
 558        .driver = {
 559                .name   = "vfb",
 560        },
 561};
 562
 563static struct platform_device *vfb_device;
 564
 565static int __init vfb_init(void)
 566{
 567        int ret = 0;
 568
 569#ifndef MODULE
 570        char *option = NULL;
 571
 572        if (fb_get_options("vfb", &option))
 573                return -ENODEV;
 574        vfb_setup(option);
 575#endif
 576
 577        if (!vfb_enable)
 578                return -ENXIO;
 579
 580        ret = platform_driver_register(&vfb_driver);
 581
 582        if (!ret) {
 583                vfb_device = platform_device_alloc("vfb", 0);
 584
 585                if (vfb_device)
 586                        ret = platform_device_add(vfb_device);
 587                else
 588                        ret = -ENOMEM;
 589
 590                if (ret) {
 591                        platform_device_put(vfb_device);
 592                        platform_driver_unregister(&vfb_driver);
 593                }
 594        }
 595
 596        return ret;
 597}
 598
 599module_init(vfb_init);
 600
 601#ifdef MODULE
 602static void __exit vfb_exit(void)
 603{
 604        platform_device_unregister(vfb_device);
 605        platform_driver_unregister(&vfb_driver);
 606}
 607
 608module_exit(vfb_exit);
 609
 610MODULE_LICENSE("GPL");
 611#endif                          /* MODULE */
 612