18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/vfb.c -- Virtual frame buffer device 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2002 James Simmons 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 108c2ecf20Sopenharmony_ci * more details. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/fb.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci /* 278c2ecf20Sopenharmony_ci * RAM we reserve for the frame buffer. This defines the maximum screen 288c2ecf20Sopenharmony_ci * size 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * The default can be overridden if the driver is compiled as a module 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void *videomemory; 368c2ecf20Sopenharmony_cistatic u_long videomemorysize = VIDEOMEMSIZE; 378c2ecf20Sopenharmony_cimodule_param(videomemorysize, ulong, 0); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(videomemorysize, "RAM available to frame buffer (in bytes)"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic char *mode_option = NULL; 418c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Preferred video mode (e.g. 640x480-8@60)"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct fb_videomode vfb_default = { 458c2ecf20Sopenharmony_ci .xres = 640, 468c2ecf20Sopenharmony_ci .yres = 480, 478c2ecf20Sopenharmony_ci .pixclock = 20000, 488c2ecf20Sopenharmony_ci .left_margin = 64, 498c2ecf20Sopenharmony_ci .right_margin = 64, 508c2ecf20Sopenharmony_ci .upper_margin = 32, 518c2ecf20Sopenharmony_ci .lower_margin = 32, 528c2ecf20Sopenharmony_ci .hsync_len = 64, 538c2ecf20Sopenharmony_ci .vsync_len = 2, 548c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo vfb_fix = { 588c2ecf20Sopenharmony_ci .id = "Virtual FB", 598c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 608c2ecf20Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 618c2ecf20Sopenharmony_ci .xpanstep = 1, 628c2ecf20Sopenharmony_ci .ypanstep = 1, 638c2ecf20Sopenharmony_ci .ywrapstep = 1, 648c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic bool vfb_enable __initdata = 0; /* disabled by default */ 688c2ecf20Sopenharmony_cimodule_param(vfb_enable, bool, 0); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vfb_enable, "Enable Virtual FB driver"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int vfb_check_var(struct fb_var_screeninfo *var, 728c2ecf20Sopenharmony_ci struct fb_info *info); 738c2ecf20Sopenharmony_cistatic int vfb_set_par(struct fb_info *info); 748c2ecf20Sopenharmony_cistatic int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 758c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info); 768c2ecf20Sopenharmony_cistatic int vfb_pan_display(struct fb_var_screeninfo *var, 778c2ecf20Sopenharmony_ci struct fb_info *info); 788c2ecf20Sopenharmony_cistatic int vfb_mmap(struct fb_info *info, 798c2ecf20Sopenharmony_ci struct vm_area_struct *vma); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const struct fb_ops vfb_ops = { 828c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 838c2ecf20Sopenharmony_ci .fb_write = fb_sys_write, 848c2ecf20Sopenharmony_ci .fb_check_var = vfb_check_var, 858c2ecf20Sopenharmony_ci .fb_set_par = vfb_set_par, 868c2ecf20Sopenharmony_ci .fb_setcolreg = vfb_setcolreg, 878c2ecf20Sopenharmony_ci .fb_pan_display = vfb_pan_display, 888c2ecf20Sopenharmony_ci .fb_fillrect = sys_fillrect, 898c2ecf20Sopenharmony_ci .fb_copyarea = sys_copyarea, 908c2ecf20Sopenharmony_ci .fb_imageblit = sys_imageblit, 918c2ecf20Sopenharmony_ci .fb_mmap = vfb_mmap, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Internal routines 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic u_long get_line_length(int xres_virtual, int bpp) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci u_long length; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci length = xres_virtual * bpp; 1038c2ecf20Sopenharmony_ci length = (length + 31) & ~31; 1048c2ecf20Sopenharmony_ci length >>= 3; 1058c2ecf20Sopenharmony_ci return (length); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Setting the video mode has been split into two parts. 1108c2ecf20Sopenharmony_ci * First part, xxxfb_check_var, must not write anything 1118c2ecf20Sopenharmony_ci * to hardware, it should only verify and adjust var. 1128c2ecf20Sopenharmony_ci * This means it doesn't alter par but it does use hardware 1138c2ecf20Sopenharmony_ci * data from it to check this var. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int vfb_check_var(struct fb_var_screeninfo *var, 1178c2ecf20Sopenharmony_ci struct fb_info *info) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u_long line_length; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! 1238c2ecf20Sopenharmony_ci * as FB_VMODE_SMOOTH_XPAN is only used internally 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_CONUPDATE) { 1278c2ecf20Sopenharmony_ci var->vmode |= FB_VMODE_YWRAP; 1288c2ecf20Sopenharmony_ci var->xoffset = info->var.xoffset; 1298c2ecf20Sopenharmony_ci var->yoffset = info->var.yoffset; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * Some very basic checks 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (!var->xres) 1368c2ecf20Sopenharmony_ci var->xres = 1; 1378c2ecf20Sopenharmony_ci if (!var->yres) 1388c2ecf20Sopenharmony_ci var->yres = 1; 1398c2ecf20Sopenharmony_ci if (var->xres > var->xres_virtual) 1408c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 1418c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual) 1428c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 1438c2ecf20Sopenharmony_ci if (var->bits_per_pixel <= 1) 1448c2ecf20Sopenharmony_ci var->bits_per_pixel = 1; 1458c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 8) 1468c2ecf20Sopenharmony_ci var->bits_per_pixel = 8; 1478c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 16) 1488c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1498c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 24) 1508c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 1518c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 32) 1528c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xoffset + var->xres) 1578c2ecf20Sopenharmony_ci var->xres_virtual = var->xoffset + var->xres; 1588c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yoffset + var->yres) 1598c2ecf20Sopenharmony_ci var->yres_virtual = var->yoffset + var->yres; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Memory limit 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci line_length = 1658c2ecf20Sopenharmony_ci get_line_length(var->xres_virtual, var->bits_per_pixel); 1668c2ecf20Sopenharmony_ci if (line_length * var->yres_virtual > videomemorysize) 1678c2ecf20Sopenharmony_ci return -ENOMEM; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Now that we checked it we alter var. The reason being is that the video 1718c2ecf20Sopenharmony_ci * mode passed in might not work but slight changes to it might make it 1728c2ecf20Sopenharmony_ci * work. This way we let the user know what is acceptable. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 1758c2ecf20Sopenharmony_ci case 1: 1768c2ecf20Sopenharmony_ci case 8: 1778c2ecf20Sopenharmony_ci var->red.offset = 0; 1788c2ecf20Sopenharmony_ci var->red.length = 8; 1798c2ecf20Sopenharmony_ci var->green.offset = 0; 1808c2ecf20Sopenharmony_ci var->green.length = 8; 1818c2ecf20Sopenharmony_ci var->blue.offset = 0; 1828c2ecf20Sopenharmony_ci var->blue.length = 8; 1838c2ecf20Sopenharmony_ci var->transp.offset = 0; 1848c2ecf20Sopenharmony_ci var->transp.length = 0; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case 16: /* RGBA 5551 */ 1878c2ecf20Sopenharmony_ci if (var->transp.length) { 1888c2ecf20Sopenharmony_ci var->red.offset = 0; 1898c2ecf20Sopenharmony_ci var->red.length = 5; 1908c2ecf20Sopenharmony_ci var->green.offset = 5; 1918c2ecf20Sopenharmony_ci var->green.length = 5; 1928c2ecf20Sopenharmony_ci var->blue.offset = 10; 1938c2ecf20Sopenharmony_ci var->blue.length = 5; 1948c2ecf20Sopenharmony_ci var->transp.offset = 15; 1958c2ecf20Sopenharmony_ci var->transp.length = 1; 1968c2ecf20Sopenharmony_ci } else { /* RGB 565 */ 1978c2ecf20Sopenharmony_ci var->red.offset = 0; 1988c2ecf20Sopenharmony_ci var->red.length = 5; 1998c2ecf20Sopenharmony_ci var->green.offset = 5; 2008c2ecf20Sopenharmony_ci var->green.length = 6; 2018c2ecf20Sopenharmony_ci var->blue.offset = 11; 2028c2ecf20Sopenharmony_ci var->blue.length = 5; 2038c2ecf20Sopenharmony_ci var->transp.offset = 0; 2048c2ecf20Sopenharmony_ci var->transp.length = 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case 24: /* RGB 888 */ 2088c2ecf20Sopenharmony_ci var->red.offset = 0; 2098c2ecf20Sopenharmony_ci var->red.length = 8; 2108c2ecf20Sopenharmony_ci var->green.offset = 8; 2118c2ecf20Sopenharmony_ci var->green.length = 8; 2128c2ecf20Sopenharmony_ci var->blue.offset = 16; 2138c2ecf20Sopenharmony_ci var->blue.length = 8; 2148c2ecf20Sopenharmony_ci var->transp.offset = 0; 2158c2ecf20Sopenharmony_ci var->transp.length = 0; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case 32: /* RGBA 8888 */ 2188c2ecf20Sopenharmony_ci var->red.offset = 0; 2198c2ecf20Sopenharmony_ci var->red.length = 8; 2208c2ecf20Sopenharmony_ci var->green.offset = 8; 2218c2ecf20Sopenharmony_ci var->green.length = 8; 2228c2ecf20Sopenharmony_ci var->blue.offset = 16; 2238c2ecf20Sopenharmony_ci var->blue.length = 8; 2248c2ecf20Sopenharmony_ci var->transp.offset = 24; 2258c2ecf20Sopenharmony_ci var->transp.length = 8; 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci var->red.msb_right = 0; 2298c2ecf20Sopenharmony_ci var->green.msb_right = 0; 2308c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 2318c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* This routine actually sets the video mode. It's in here where we 2378c2ecf20Sopenharmony_ci * the hardware state info->par and fix which can be affected by the 2388c2ecf20Sopenharmony_ci * change in par. For this driver it doesn't do much. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic int vfb_set_par(struct fb_info *info) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 2438c2ecf20Sopenharmony_ci case 1: 2448c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_MONO01; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case 8: 2478c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case 16: 2508c2ecf20Sopenharmony_ci case 24: 2518c2ecf20Sopenharmony_ci case 32: 2528c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci info->fix.line_length = get_line_length(info->var.xres_virtual, 2578c2ecf20Sopenharmony_ci info->var.bits_per_pixel); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Set a single color register. The values supplied are already 2648c2ecf20Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 2658c2ecf20Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 2698c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci if (regno >= 256) /* no. of hw registers */ 2728c2ecf20Sopenharmony_ci return 1; 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * Program hardware... do anything you want with transp 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* grayscale works only partially under directcolor */ 2788c2ecf20Sopenharmony_ci if (info->var.grayscale) { 2798c2ecf20Sopenharmony_ci /* grayscale = 0.30*R + 0.59*G + 0.11*B */ 2808c2ecf20Sopenharmony_ci red = green = blue = 2818c2ecf20Sopenharmony_ci (red * 77 + green * 151 + blue * 28) >> 8; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Directcolor: 2858c2ecf20Sopenharmony_ci * var->{color}.offset contains start of bitfield 2868c2ecf20Sopenharmony_ci * var->{color}.length contains length of bitfield 2878c2ecf20Sopenharmony_ci * {hardwarespecific} contains width of RAMDAC 2888c2ecf20Sopenharmony_ci * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset) 2898c2ecf20Sopenharmony_ci * RAMDAC[X] is programmed to (red, green, blue) 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Pseudocolor: 2928c2ecf20Sopenharmony_ci * var->{color}.offset is 0 unless the palette index takes less than 2938c2ecf20Sopenharmony_ci * bits_per_pixel bits and is stored in the upper 2948c2ecf20Sopenharmony_ci * bits of the pixel value 2958c2ecf20Sopenharmony_ci * var->{color}.length is set so that 1 << length is the number of available 2968c2ecf20Sopenharmony_ci * palette entries 2978c2ecf20Sopenharmony_ci * cmap is not used 2988c2ecf20Sopenharmony_ci * RAMDAC[X] is programmed to (red, green, blue) 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Truecolor: 3018c2ecf20Sopenharmony_ci * does not use DAC. Usually 3 are present. 3028c2ecf20Sopenharmony_ci * var->{color}.offset contains start of bitfield 3038c2ecf20Sopenharmony_ci * var->{color}.length contains length of bitfield 3048c2ecf20Sopenharmony_ci * cmap is programmed to (red << red.offset) | (green << green.offset) | 3058c2ecf20Sopenharmony_ci * (blue << blue.offset) | (transp << transp.offset) 3068c2ecf20Sopenharmony_ci * RAMDAC does not exist 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) 3098c2ecf20Sopenharmony_ci switch (info->fix.visual) { 3108c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 3118c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 3128c2ecf20Sopenharmony_ci red = CNVT_TOHW(red, info->var.red.length); 3138c2ecf20Sopenharmony_ci green = CNVT_TOHW(green, info->var.green.length); 3148c2ecf20Sopenharmony_ci blue = CNVT_TOHW(blue, info->var.blue.length); 3158c2ecf20Sopenharmony_ci transp = CNVT_TOHW(transp, info->var.transp.length); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case FB_VISUAL_DIRECTCOLOR: 3188c2ecf20Sopenharmony_ci red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */ 3198c2ecf20Sopenharmony_ci green = CNVT_TOHW(green, 8); 3208c2ecf20Sopenharmony_ci blue = CNVT_TOHW(blue, 8); 3218c2ecf20Sopenharmony_ci /* hey, there is bug in transp handling... */ 3228c2ecf20Sopenharmony_ci transp = CNVT_TOHW(transp, 8); 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci#undef CNVT_TOHW 3268c2ecf20Sopenharmony_ci /* Truecolor has hardware independent palette */ 3278c2ecf20Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 3288c2ecf20Sopenharmony_ci u32 v; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (regno >= 16) 3318c2ecf20Sopenharmony_ci return 1; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci v = (red << info->var.red.offset) | 3348c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 3358c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 3368c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 3378c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 3388c2ecf20Sopenharmony_ci case 8: 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case 16: 3418c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = v; 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case 24: 3448c2ecf20Sopenharmony_ci case 32: 3458c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = v; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Pan or Wrap the Display 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int vfb_pan_display(struct fb_var_screeninfo *var, 3608c2ecf20Sopenharmony_ci struct fb_info *info) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 3638c2ecf20Sopenharmony_ci if (var->yoffset >= info->var.yres_virtual || 3648c2ecf20Sopenharmony_ci var->xoffset) 3658c2ecf20Sopenharmony_ci return -EINVAL; 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci if (var->xoffset + info->var.xres > info->var.xres_virtual || 3688c2ecf20Sopenharmony_ci var->yoffset + info->var.yres > info->var.yres_virtual) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci info->var.xoffset = var->xoffset; 3728c2ecf20Sopenharmony_ci info->var.yoffset = var->yoffset; 3738c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) 3748c2ecf20Sopenharmony_ci info->var.vmode |= FB_VMODE_YWRAP; 3758c2ecf20Sopenharmony_ci else 3768c2ecf20Sopenharmony_ci info->var.vmode &= ~FB_VMODE_YWRAP; 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * Most drivers don't need their own mmap function 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int vfb_mmap(struct fb_info *info, 3858c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci#ifndef MODULE 3918c2ecf20Sopenharmony_ci/* 3928c2ecf20Sopenharmony_ci * The virtual framebuffer driver is only enabled if explicitly 3938c2ecf20Sopenharmony_ci * requested by passing 'video=vfb:' (or any actual options). 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_cistatic int __init vfb_setup(char *options) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci char *this_opt; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci vfb_enable = 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!options) 4028c2ecf20Sopenharmony_ci return 1; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci vfb_enable = 1; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!*options) 4078c2ecf20Sopenharmony_ci return 1; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 4108c2ecf20Sopenharmony_ci if (!*this_opt) 4118c2ecf20Sopenharmony_ci continue; 4128c2ecf20Sopenharmony_ci /* Test disable for backwards compatibility */ 4138c2ecf20Sopenharmony_ci if (!strcmp(this_opt, "disable")) 4148c2ecf20Sopenharmony_ci vfb_enable = 0; 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci mode_option = this_opt; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci return 1; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci#endif /* MODULE */ 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* 4238c2ecf20Sopenharmony_ci * Initialisation 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int vfb_probe(struct platform_device *dev) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct fb_info *info; 4298c2ecf20Sopenharmony_ci unsigned int size = PAGE_ALIGN(videomemorysize); 4308c2ecf20Sopenharmony_ci int retval = -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * For real video cards we use ioremap. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if (!(videomemory = vmalloc_32_user(size))) 4368c2ecf20Sopenharmony_ci return retval; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev); 4398c2ecf20Sopenharmony_ci if (!info) 4408c2ecf20Sopenharmony_ci goto err; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci info->screen_base = (char __iomem *)videomemory; 4438c2ecf20Sopenharmony_ci info->fbops = &vfb_ops; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!fb_find_mode(&info->var, info, mode_option, 4468c2ecf20Sopenharmony_ci NULL, 0, &vfb_default, 8)){ 4478c2ecf20Sopenharmony_ci fb_err(info, "Unable to find usable video mode.\n"); 4488c2ecf20Sopenharmony_ci retval = -EINVAL; 4498c2ecf20Sopenharmony_ci goto err1; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci vfb_fix.smem_start = (unsigned long) videomemory; 4538c2ecf20Sopenharmony_ci vfb_fix.smem_len = videomemorysize; 4548c2ecf20Sopenharmony_ci info->fix = vfb_fix; 4558c2ecf20Sopenharmony_ci info->pseudo_palette = info->par; 4568c2ecf20Sopenharmony_ci info->par = NULL; 4578c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 256, 0); 4608c2ecf20Sopenharmony_ci if (retval < 0) 4618c2ecf20Sopenharmony_ci goto err1; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci retval = register_framebuffer(info); 4648c2ecf20Sopenharmony_ci if (retval < 0) 4658c2ecf20Sopenharmony_ci goto err2; 4668c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci vfb_set_par(info); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci fb_info(info, "Virtual frame buffer device, using %ldK of video memory\n", 4718c2ecf20Sopenharmony_ci videomemorysize >> 10); 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_cierr2: 4748c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 4758c2ecf20Sopenharmony_cierr1: 4768c2ecf20Sopenharmony_ci framebuffer_release(info); 4778c2ecf20Sopenharmony_cierr: 4788c2ecf20Sopenharmony_ci vfree(videomemory); 4798c2ecf20Sopenharmony_ci return retval; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int vfb_remove(struct platform_device *dev) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (info) { 4878c2ecf20Sopenharmony_ci unregister_framebuffer(info); 4888c2ecf20Sopenharmony_ci vfree(videomemory); 4898c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 4908c2ecf20Sopenharmony_ci framebuffer_release(info); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic struct platform_driver vfb_driver = { 4968c2ecf20Sopenharmony_ci .probe = vfb_probe, 4978c2ecf20Sopenharmony_ci .remove = vfb_remove, 4988c2ecf20Sopenharmony_ci .driver = { 4998c2ecf20Sopenharmony_ci .name = "vfb", 5008c2ecf20Sopenharmony_ci }, 5018c2ecf20Sopenharmony_ci}; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic struct platform_device *vfb_device; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int __init vfb_init(void) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci int ret = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci#ifndef MODULE 5108c2ecf20Sopenharmony_ci char *option = NULL; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (fb_get_options("vfb", &option)) 5138c2ecf20Sopenharmony_ci return -ENODEV; 5148c2ecf20Sopenharmony_ci vfb_setup(option); 5158c2ecf20Sopenharmony_ci#endif 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!vfb_enable) 5188c2ecf20Sopenharmony_ci return -ENXIO; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ret = platform_driver_register(&vfb_driver); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!ret) { 5238c2ecf20Sopenharmony_ci vfb_device = platform_device_alloc("vfb", 0); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (vfb_device) 5268c2ecf20Sopenharmony_ci ret = platform_device_add(vfb_device); 5278c2ecf20Sopenharmony_ci else 5288c2ecf20Sopenharmony_ci ret = -ENOMEM; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (ret) { 5318c2ecf20Sopenharmony_ci platform_device_put(vfb_device); 5328c2ecf20Sopenharmony_ci platform_driver_unregister(&vfb_driver); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cimodule_init(vfb_init); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci#ifdef MODULE 5428c2ecf20Sopenharmony_cistatic void __exit vfb_exit(void) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci platform_device_unregister(vfb_device); 5458c2ecf20Sopenharmony_ci platform_driver_unregister(&vfb_driver); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cimodule_exit(vfb_exit); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5518c2ecf20Sopenharmony_ci#endif /* MODULE */ 552