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