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