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/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 __devinitdata = {
  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 __devinitdata = {
 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 < 0
 394                    || var->yoffset >= info->var.yres_virtual
 395                    || var->xoffset)
 396                        return -EINVAL;
 397        } else {
 398                if (var->xoffset + info->var.xres > info->var.xres_virtual ||
 399                    var->yoffset + info->var.yres > info->var.yres_virtual)
 400                        return -EINVAL;
 401        }
 402        info->var.xoffset = var->xoffset;
 403        info->var.yoffset = var->yoffset;
 404        if (var->vmode & FB_VMODE_YWRAP)
 405                info->var.vmode |= FB_VMODE_YWRAP;
 406        else
 407                info->var.vmode &= ~FB_VMODE_YWRAP;
 408        return 0;
 409}
 410
 411    /*
 412     *  Most drivers don't need their own mmap function 
 413     */
 414
 415static int vfb_mmap(struct fb_info *info,
 416                    struct vm_area_struct *vma)
 417{
 418        unsigned long start = vma->vm_start;
 419        unsigned long size = vma->vm_end - vma->vm_start;
 420        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 421        unsigned long page, pos;
 422
 423        if (offset + size > info->fix.smem_len) {
 424                return -EINVAL;
 425        }
 426
 427        pos = (unsigned long)info->fix.smem_start + offset;
 428
 429        while (size > 0) {
 430                page = vmalloc_to_pfn((void *)pos);
 431                if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
 432                        return -EAGAIN;
 433                }
 434                start += PAGE_SIZE;
 435                pos += PAGE_SIZE;
 436                if (size > PAGE_SIZE)
 437                        size -= PAGE_SIZE;
 438                else
 439                        size = 0;
 440        }
 441
 442        return 0;
 443
 444}
 445
 446#ifndef MODULE
 447/*
 448 * The virtual framebuffer driver is only enabled if explicitly
 449 * requested by passing 'video=vfb:' (or any actual options).
 450 */
 451static int __init vfb_setup(char *options)
 452{
 453        char *this_opt;
 454
 455        vfb_enable = 0;
 456
 457        if (!options)
 458                return 1;
 459
 460        vfb_enable = 1;
 461
 462        if (!*options)
 463                return 1;
 464
 465        while ((this_opt = strsep(&options, ",")) != NULL) {
 466                if (!*this_opt)
 467                        continue;
 468                /* Test disable for backwards compatibility */
 469                if (!strcmp(this_opt, "disable"))
 470                        vfb_enable = 0;
 471        }
 472        return 1;
 473}
 474#endif  /*  MODULE  */
 475
 476    /*
 477     *  Initialisation
 478     */
 479
 480static int __devinit vfb_probe(struct platform_device *dev)
 481{
 482        struct fb_info *info;
 483        int retval = -ENOMEM;
 484
 485        /*
 486         * For real video cards we use ioremap.
 487         */
 488        if (!(videomemory = rvmalloc(videomemorysize)))
 489                return retval;
 490
 491        /*
 492         * VFB must clear memory to prevent kernel info
 493         * leakage into userspace
 494         * VGA-based drivers MUST NOT clear memory if
 495         * they want to be able to take over vgacon
 496         */
 497        memset(videomemory, 0, videomemorysize);
 498
 499        info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
 500        if (!info)
 501                goto err;
 502
 503        info->screen_base = (char __iomem *)videomemory;
 504        info->fbops = &vfb_ops;
 505
 506        retval = fb_find_mode(&info->var, info, NULL,
 507                              NULL, 0, NULL, 8);
 508
 509        if (!retval || (retval == 4))
 510                info->var = vfb_default;
 511        vfb_fix.smem_start = (unsigned long) videomemory;
 512        vfb_fix.smem_len = videomemorysize;
 513        info->fix = vfb_fix;
 514        info->pseudo_palette = info->par;
 515        info->par = NULL;
 516        info->flags = FBINFO_FLAG_DEFAULT;
 517
 518        retval = fb_alloc_cmap(&info->cmap, 256, 0);
 519        if (retval < 0)
 520                goto err1;
 521
 522        retval = register_framebuffer(info);
 523        if (retval < 0)
 524                goto err2;
 525        platform_set_drvdata(dev, info);
 526
 527        printk(KERN_INFO
 528               "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
 529               info->node, videomemorysize >> 10);
 530        return 0;
 531err2:
 532        fb_dealloc_cmap(&info->cmap);
 533err1:
 534        framebuffer_release(info);
 535err:
 536        rvfree(videomemory, videomemorysize);
 537        return retval;
 538}
 539
 540static int vfb_remove(struct platform_device *dev)
 541{
 542        struct fb_info *info = platform_get_drvdata(dev);
 543
 544        if (info) {
 545                unregister_framebuffer(info);
 546                rvfree(videomemory, videomemorysize);
 547                fb_dealloc_cmap(&info->cmap);
 548                framebuffer_release(info);
 549        }
 550        return 0;
 551}
 552
 553static struct platform_driver vfb_driver = {
 554        .probe  = vfb_probe,
 555        .remove = vfb_remove,
 556        .driver = {
 557                .name   = "vfb",
 558        },
 559};
 560
 561static struct platform_device *vfb_device;
 562
 563static int __init vfb_init(void)
 564{
 565        int ret = 0;
 566
 567#ifndef MODULE
 568        char *option = NULL;
 569
 570        if (fb_get_options("vfb", &option))
 571                return -ENODEV;
 572        vfb_setup(option);
 573#endif
 574
 575        if (!vfb_enable)
 576                return -ENXIO;
 577
 578        ret = platform_driver_register(&vfb_driver);
 579
 580        if (!ret) {
 581                vfb_device = platform_device_alloc("vfb", 0);
 582
 583                if (vfb_device)
 584                        ret = platform_device_add(vfb_device);
 585                else
 586                        ret = -ENOMEM;
 587
 588                if (ret) {
 589                        platform_device_put(vfb_device);
 590                        platform_driver_unregister(&vfb_driver);
 591                }
 592        }
 593
 594        return ret;
 595}
 596
 597module_init(vfb_init);
 598
 599#ifdef MODULE
 600static void __exit vfb_exit(void)
 601{
 602        platform_device_unregister(vfb_device);
 603        platform_driver_unregister(&vfb_driver);
 604}
 605
 606module_exit(vfb_exit);
 607
 608MODULE_LICENSE("GPL");
 609#endif                          /* MODULE */
 610
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.