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