162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/vfb.c -- Virtual frame buffer device 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2002 James Simmons 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 962306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 1062306a36Sopenharmony_ci * more details. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/mm.h> 1862306a36Sopenharmony_ci#include <linux/vmalloc.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/fb.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * RAM we reserve for the frame buffer. This defines the maximum screen 2862306a36Sopenharmony_ci * size 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * The default can be overridden if the driver is compiled as a module 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void *videomemory; 3662306a36Sopenharmony_cistatic u_long videomemorysize = VIDEOMEMSIZE; 3762306a36Sopenharmony_cimodule_param(videomemorysize, ulong, 0); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(videomemorysize, "RAM available to frame buffer (in bytes)"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic char *mode_option = NULL; 4162306a36Sopenharmony_cimodule_param(mode_option, charp, 0); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Preferred video mode (e.g. 640x480-8@60)"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const struct fb_videomode vfb_default = { 4562306a36Sopenharmony_ci .xres = 640, 4662306a36Sopenharmony_ci .yres = 480, 4762306a36Sopenharmony_ci .pixclock = 20000, 4862306a36Sopenharmony_ci .left_margin = 64, 4962306a36Sopenharmony_ci .right_margin = 64, 5062306a36Sopenharmony_ci .upper_margin = 32, 5162306a36Sopenharmony_ci .lower_margin = 32, 5262306a36Sopenharmony_ci .hsync_len = 64, 5362306a36Sopenharmony_ci .vsync_len = 2, 5462306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct fb_fix_screeninfo vfb_fix = { 5862306a36Sopenharmony_ci .id = "Virtual FB", 5962306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 6062306a36Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 6162306a36Sopenharmony_ci .xpanstep = 1, 6262306a36Sopenharmony_ci .ypanstep = 1, 6362306a36Sopenharmony_ci .ywrapstep = 1, 6462306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic bool vfb_enable __initdata = 0; /* disabled by default */ 6862306a36Sopenharmony_cimodule_param(vfb_enable, bool, 0); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(vfb_enable, "Enable Virtual FB driver"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int vfb_check_var(struct fb_var_screeninfo *var, 7262306a36Sopenharmony_ci struct fb_info *info); 7362306a36Sopenharmony_cistatic int vfb_set_par(struct fb_info *info); 7462306a36Sopenharmony_cistatic int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 7562306a36Sopenharmony_ci u_int transp, struct fb_info *info); 7662306a36Sopenharmony_cistatic int vfb_pan_display(struct fb_var_screeninfo *var, 7762306a36Sopenharmony_ci struct fb_info *info); 7862306a36Sopenharmony_cistatic int vfb_mmap(struct fb_info *info, 7962306a36Sopenharmony_ci struct vm_area_struct *vma); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic const struct fb_ops vfb_ops = { 8262306a36Sopenharmony_ci .owner = THIS_MODULE, 8362306a36Sopenharmony_ci .fb_read = fb_sys_read, 8462306a36Sopenharmony_ci .fb_write = fb_sys_write, 8562306a36Sopenharmony_ci .fb_check_var = vfb_check_var, 8662306a36Sopenharmony_ci .fb_set_par = vfb_set_par, 8762306a36Sopenharmony_ci .fb_setcolreg = vfb_setcolreg, 8862306a36Sopenharmony_ci .fb_pan_display = vfb_pan_display, 8962306a36Sopenharmony_ci .fb_fillrect = sys_fillrect, 9062306a36Sopenharmony_ci .fb_copyarea = sys_copyarea, 9162306a36Sopenharmony_ci .fb_imageblit = sys_imageblit, 9262306a36Sopenharmony_ci .fb_mmap = vfb_mmap, 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Internal routines 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic u_long get_line_length(int xres_virtual, int bpp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u_long length; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci length = xres_virtual * bpp; 10462306a36Sopenharmony_ci length = (length + 31) & ~31; 10562306a36Sopenharmony_ci length >>= 3; 10662306a36Sopenharmony_ci return (length); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Setting the video mode has been split into two parts. 11162306a36Sopenharmony_ci * First part, xxxfb_check_var, must not write anything 11262306a36Sopenharmony_ci * to hardware, it should only verify and adjust var. 11362306a36Sopenharmony_ci * This means it doesn't alter par but it does use hardware 11462306a36Sopenharmony_ci * data from it to check this var. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int vfb_check_var(struct fb_var_screeninfo *var, 11862306a36Sopenharmony_ci struct fb_info *info) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u_long line_length; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! 12462306a36Sopenharmony_ci * as FB_VMODE_SMOOTH_XPAN is only used internally 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (var->vmode & FB_VMODE_CONUPDATE) { 12862306a36Sopenharmony_ci var->vmode |= FB_VMODE_YWRAP; 12962306a36Sopenharmony_ci var->xoffset = info->var.xoffset; 13062306a36Sopenharmony_ci var->yoffset = info->var.yoffset; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * Some very basic checks 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (!var->xres) 13762306a36Sopenharmony_ci var->xres = 1; 13862306a36Sopenharmony_ci if (!var->yres) 13962306a36Sopenharmony_ci var->yres = 1; 14062306a36Sopenharmony_ci if (var->xres > var->xres_virtual) 14162306a36Sopenharmony_ci var->xres_virtual = var->xres; 14262306a36Sopenharmony_ci if (var->yres > var->yres_virtual) 14362306a36Sopenharmony_ci var->yres_virtual = var->yres; 14462306a36Sopenharmony_ci if (var->bits_per_pixel <= 1) 14562306a36Sopenharmony_ci var->bits_per_pixel = 1; 14662306a36Sopenharmony_ci else if (var->bits_per_pixel <= 8) 14762306a36Sopenharmony_ci var->bits_per_pixel = 8; 14862306a36Sopenharmony_ci else if (var->bits_per_pixel <= 16) 14962306a36Sopenharmony_ci var->bits_per_pixel = 16; 15062306a36Sopenharmony_ci else if (var->bits_per_pixel <= 24) 15162306a36Sopenharmony_ci var->bits_per_pixel = 24; 15262306a36Sopenharmony_ci else if (var->bits_per_pixel <= 32) 15362306a36Sopenharmony_ci var->bits_per_pixel = 32; 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (var->xres_virtual < var->xoffset + var->xres) 15862306a36Sopenharmony_ci var->xres_virtual = var->xoffset + var->xres; 15962306a36Sopenharmony_ci if (var->yres_virtual < var->yoffset + var->yres) 16062306a36Sopenharmony_ci var->yres_virtual = var->yoffset + var->yres; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Memory limit 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci line_length = 16662306a36Sopenharmony_ci get_line_length(var->xres_virtual, var->bits_per_pixel); 16762306a36Sopenharmony_ci if (line_length * var->yres_virtual > videomemorysize) 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Now that we checked it we alter var. The reason being is that the video 17262306a36Sopenharmony_ci * mode passed in might not work but slight changes to it might make it 17362306a36Sopenharmony_ci * work. This way we let the user know what is acceptable. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci switch (var->bits_per_pixel) { 17662306a36Sopenharmony_ci case 1: 17762306a36Sopenharmony_ci case 8: 17862306a36Sopenharmony_ci var->red.offset = 0; 17962306a36Sopenharmony_ci var->red.length = 8; 18062306a36Sopenharmony_ci var->green.offset = 0; 18162306a36Sopenharmony_ci var->green.length = 8; 18262306a36Sopenharmony_ci var->blue.offset = 0; 18362306a36Sopenharmony_ci var->blue.length = 8; 18462306a36Sopenharmony_ci var->transp.offset = 0; 18562306a36Sopenharmony_ci var->transp.length = 0; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case 16: /* RGBA 5551 */ 18862306a36Sopenharmony_ci if (var->transp.length) { 18962306a36Sopenharmony_ci var->red.offset = 0; 19062306a36Sopenharmony_ci var->red.length = 5; 19162306a36Sopenharmony_ci var->green.offset = 5; 19262306a36Sopenharmony_ci var->green.length = 5; 19362306a36Sopenharmony_ci var->blue.offset = 10; 19462306a36Sopenharmony_ci var->blue.length = 5; 19562306a36Sopenharmony_ci var->transp.offset = 15; 19662306a36Sopenharmony_ci var->transp.length = 1; 19762306a36Sopenharmony_ci } else { /* RGB 565 */ 19862306a36Sopenharmony_ci var->red.offset = 0; 19962306a36Sopenharmony_ci var->red.length = 5; 20062306a36Sopenharmony_ci var->green.offset = 5; 20162306a36Sopenharmony_ci var->green.length = 6; 20262306a36Sopenharmony_ci var->blue.offset = 11; 20362306a36Sopenharmony_ci var->blue.length = 5; 20462306a36Sopenharmony_ci var->transp.offset = 0; 20562306a36Sopenharmony_ci var->transp.length = 0; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci case 24: /* RGB 888 */ 20962306a36Sopenharmony_ci var->red.offset = 0; 21062306a36Sopenharmony_ci var->red.length = 8; 21162306a36Sopenharmony_ci var->green.offset = 8; 21262306a36Sopenharmony_ci var->green.length = 8; 21362306a36Sopenharmony_ci var->blue.offset = 16; 21462306a36Sopenharmony_ci var->blue.length = 8; 21562306a36Sopenharmony_ci var->transp.offset = 0; 21662306a36Sopenharmony_ci var->transp.length = 0; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case 32: /* RGBA 8888 */ 21962306a36Sopenharmony_ci var->red.offset = 0; 22062306a36Sopenharmony_ci var->red.length = 8; 22162306a36Sopenharmony_ci var->green.offset = 8; 22262306a36Sopenharmony_ci var->green.length = 8; 22362306a36Sopenharmony_ci var->blue.offset = 16; 22462306a36Sopenharmony_ci var->blue.length = 8; 22562306a36Sopenharmony_ci var->transp.offset = 24; 22662306a36Sopenharmony_ci var->transp.length = 8; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci var->red.msb_right = 0; 23062306a36Sopenharmony_ci var->green.msb_right = 0; 23162306a36Sopenharmony_ci var->blue.msb_right = 0; 23262306a36Sopenharmony_ci var->transp.msb_right = 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* This routine actually sets the video mode. It's in here where we 23862306a36Sopenharmony_ci * the hardware state info->par and fix which can be affected by the 23962306a36Sopenharmony_ci * change in par. For this driver it doesn't do much. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic int vfb_set_par(struct fb_info *info) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 24462306a36Sopenharmony_ci case 1: 24562306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_MONO01; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case 8: 24862306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case 16: 25162306a36Sopenharmony_ci case 24: 25262306a36Sopenharmony_ci case 32: 25362306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci info->fix.line_length = get_line_length(info->var.xres_virtual, 25862306a36Sopenharmony_ci info->var.bits_per_pixel); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Set a single color register. The values supplied are already 26562306a36Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 26662306a36Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 27062306a36Sopenharmony_ci u_int transp, struct fb_info *info) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci if (regno >= 256) /* no. of hw registers */ 27362306a36Sopenharmony_ci return 1; 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Program hardware... do anything you want with transp 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* grayscale works only partially under directcolor */ 27962306a36Sopenharmony_ci if (info->var.grayscale) { 28062306a36Sopenharmony_ci /* grayscale = 0.30*R + 0.59*G + 0.11*B */ 28162306a36Sopenharmony_ci red = green = blue = 28262306a36Sopenharmony_ci (red * 77 + green * 151 + blue * 28) >> 8; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Directcolor: 28662306a36Sopenharmony_ci * var->{color}.offset contains start of bitfield 28762306a36Sopenharmony_ci * var->{color}.length contains length of bitfield 28862306a36Sopenharmony_ci * {hardwarespecific} contains width of RAMDAC 28962306a36Sopenharmony_ci * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset) 29062306a36Sopenharmony_ci * RAMDAC[X] is programmed to (red, green, blue) 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Pseudocolor: 29362306a36Sopenharmony_ci * var->{color}.offset is 0 unless the palette index takes less than 29462306a36Sopenharmony_ci * bits_per_pixel bits and is stored in the upper 29562306a36Sopenharmony_ci * bits of the pixel value 29662306a36Sopenharmony_ci * var->{color}.length is set so that 1 << length is the number of available 29762306a36Sopenharmony_ci * palette entries 29862306a36Sopenharmony_ci * cmap is not used 29962306a36Sopenharmony_ci * RAMDAC[X] is programmed to (red, green, blue) 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Truecolor: 30262306a36Sopenharmony_ci * does not use DAC. Usually 3 are present. 30362306a36Sopenharmony_ci * var->{color}.offset contains start of bitfield 30462306a36Sopenharmony_ci * var->{color}.length contains length of bitfield 30562306a36Sopenharmony_ci * cmap is programmed to (red << red.offset) | (green << green.offset) | 30662306a36Sopenharmony_ci * (blue << blue.offset) | (transp << transp.offset) 30762306a36Sopenharmony_ci * RAMDAC does not exist 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) 31062306a36Sopenharmony_ci switch (info->fix.visual) { 31162306a36Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 31262306a36Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 31362306a36Sopenharmony_ci red = CNVT_TOHW(red, info->var.red.length); 31462306a36Sopenharmony_ci green = CNVT_TOHW(green, info->var.green.length); 31562306a36Sopenharmony_ci blue = CNVT_TOHW(blue, info->var.blue.length); 31662306a36Sopenharmony_ci transp = CNVT_TOHW(transp, info->var.transp.length); 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci case FB_VISUAL_DIRECTCOLOR: 31962306a36Sopenharmony_ci red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */ 32062306a36Sopenharmony_ci green = CNVT_TOHW(green, 8); 32162306a36Sopenharmony_ci blue = CNVT_TOHW(blue, 8); 32262306a36Sopenharmony_ci /* hey, there is bug in transp handling... */ 32362306a36Sopenharmony_ci transp = CNVT_TOHW(transp, 8); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci#undef CNVT_TOHW 32762306a36Sopenharmony_ci /* Truecolor has hardware independent palette */ 32862306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 32962306a36Sopenharmony_ci u32 v; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (regno >= 16) 33262306a36Sopenharmony_ci return 1; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci v = (red << info->var.red.offset) | 33562306a36Sopenharmony_ci (green << info->var.green.offset) | 33662306a36Sopenharmony_ci (blue << info->var.blue.offset) | 33762306a36Sopenharmony_ci (transp << info->var.transp.offset); 33862306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 33962306a36Sopenharmony_ci case 8: 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case 16: 34262306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = v; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case 24: 34562306a36Sopenharmony_ci case 32: 34662306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = v; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Pan or Wrap the Display 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int vfb_pan_display(struct fb_var_screeninfo *var, 36162306a36Sopenharmony_ci struct fb_info *info) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 36462306a36Sopenharmony_ci if (var->yoffset >= info->var.yres_virtual || 36562306a36Sopenharmony_ci var->xoffset) 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci if (var->xoffset + info->var.xres > info->var.xres_virtual || 36962306a36Sopenharmony_ci var->yoffset + info->var.yres > info->var.yres_virtual) 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci info->var.xoffset = var->xoffset; 37362306a36Sopenharmony_ci info->var.yoffset = var->yoffset; 37462306a36Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) 37562306a36Sopenharmony_ci info->var.vmode |= FB_VMODE_YWRAP; 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci info->var.vmode &= ~FB_VMODE_YWRAP; 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Most drivers don't need their own mmap function 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int vfb_mmap(struct fb_info *info, 38662306a36Sopenharmony_ci struct vm_area_struct *vma) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci#ifndef MODULE 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * The virtual framebuffer driver is only enabled if explicitly 39462306a36Sopenharmony_ci * requested by passing 'video=vfb:' (or any actual options). 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_cistatic int __init vfb_setup(char *options) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci char *this_opt; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci vfb_enable = 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!options) 40362306a36Sopenharmony_ci return 1; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci vfb_enable = 1; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (!*options) 40862306a36Sopenharmony_ci return 1; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 41162306a36Sopenharmony_ci if (!*this_opt) 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci /* Test disable for backwards compatibility */ 41462306a36Sopenharmony_ci if (!strcmp(this_opt, "disable")) 41562306a36Sopenharmony_ci vfb_enable = 0; 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci mode_option = this_opt; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci return 1; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci#endif /* MODULE */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * Initialisation 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int vfb_probe(struct platform_device *dev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct fb_info *info; 43062306a36Sopenharmony_ci unsigned int size = PAGE_ALIGN(videomemorysize); 43162306a36Sopenharmony_ci int retval = -ENOMEM; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * For real video cards we use ioremap. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (!(videomemory = vmalloc_32_user(size))) 43762306a36Sopenharmony_ci return retval; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev); 44062306a36Sopenharmony_ci if (!info) 44162306a36Sopenharmony_ci goto err; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci info->screen_buffer = videomemory; 44462306a36Sopenharmony_ci info->fbops = &vfb_ops; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!fb_find_mode(&info->var, info, mode_option, 44762306a36Sopenharmony_ci NULL, 0, &vfb_default, 8)){ 44862306a36Sopenharmony_ci fb_err(info, "Unable to find usable video mode.\n"); 44962306a36Sopenharmony_ci retval = -EINVAL; 45062306a36Sopenharmony_ci goto err1; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci vfb_fix.smem_start = (unsigned long) videomemory; 45462306a36Sopenharmony_ci vfb_fix.smem_len = videomemorysize; 45562306a36Sopenharmony_ci info->fix = vfb_fix; 45662306a36Sopenharmony_ci info->pseudo_palette = info->par; 45762306a36Sopenharmony_ci info->par = NULL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 256, 0); 46062306a36Sopenharmony_ci if (retval < 0) 46162306a36Sopenharmony_ci goto err1; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci retval = register_framebuffer(info); 46462306a36Sopenharmony_ci if (retval < 0) 46562306a36Sopenharmony_ci goto err2; 46662306a36Sopenharmony_ci platform_set_drvdata(dev, info); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci vfb_set_par(info); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci fb_info(info, "Virtual frame buffer device, using %ldK of video memory\n", 47162306a36Sopenharmony_ci videomemorysize >> 10); 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_cierr2: 47462306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 47562306a36Sopenharmony_cierr1: 47662306a36Sopenharmony_ci framebuffer_release(info); 47762306a36Sopenharmony_cierr: 47862306a36Sopenharmony_ci vfree(videomemory); 47962306a36Sopenharmony_ci return retval; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void vfb_remove(struct platform_device *dev) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (info) { 48762306a36Sopenharmony_ci unregister_framebuffer(info); 48862306a36Sopenharmony_ci vfree(videomemory); 48962306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 49062306a36Sopenharmony_ci framebuffer_release(info); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic struct platform_driver vfb_driver = { 49562306a36Sopenharmony_ci .probe = vfb_probe, 49662306a36Sopenharmony_ci .remove_new = vfb_remove, 49762306a36Sopenharmony_ci .driver = { 49862306a36Sopenharmony_ci .name = "vfb", 49962306a36Sopenharmony_ci }, 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic struct platform_device *vfb_device; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int __init vfb_init(void) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int ret = 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci#ifndef MODULE 50962306a36Sopenharmony_ci char *option = NULL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (fb_get_options("vfb", &option)) 51262306a36Sopenharmony_ci return -ENODEV; 51362306a36Sopenharmony_ci vfb_setup(option); 51462306a36Sopenharmony_ci#endif 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!vfb_enable) 51762306a36Sopenharmony_ci return -ENXIO; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ret = platform_driver_register(&vfb_driver); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!ret) { 52262306a36Sopenharmony_ci vfb_device = platform_device_alloc("vfb", 0); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (vfb_device) 52562306a36Sopenharmony_ci ret = platform_device_add(vfb_device); 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci ret = -ENOMEM; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (ret) { 53062306a36Sopenharmony_ci platform_device_put(vfb_device); 53162306a36Sopenharmony_ci platform_driver_unregister(&vfb_driver); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cimodule_init(vfb_init); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci#ifdef MODULE 54162306a36Sopenharmony_cistatic void __exit vfb_exit(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci platform_device_unregister(vfb_device); 54462306a36Sopenharmony_ci platform_driver_unregister(&vfb_driver); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cimodule_exit(vfb_exit); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 55062306a36Sopenharmony_ci#endif /* MODULE */ 551