brintos

brintos / linux-shallow public Read only

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