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