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