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