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