1 /*
2  * linux/drivers/video/fbgen.c -- Generic routines for frame buffer devices
3  *
4  *  Created 3 Jan 1998 by Geert Uytterhoeven
5  *
6  *	2001 - Documented with DocBook
7  *	- Brad Douglas <brad@neruo.com>
8  *
9  * This file is subject to the terms and conditions of the GNU General Public
10  * License.  See the file COPYING in the main directory of this archive
11  * for more details.
12  */
13 
14 #include <linux/module.h>
15 #include <linux/string.h>
16 #include <linux/tty.h>
17 #include <linux/fb.h>
18 #include <linux/slab.h>
19 
20 #include <asm/uaccess.h>
21 #include <asm/io.h>
22 
23 #include <video/fbcon.h>
24 
25 static int currcon = 0;
26 
27 
28 /* ---- `Generic' versions of the frame buffer device operations ----------- */
29 
30 
31 /**
32  *	fbgen_get_fix - get fixed part of display
33  *	@fix: fb_fix_screeninfo structure
34  *	@con: virtual console number
35  *	@info: frame buffer info structure
36  *
37  *	Get the fixed information part of the display and place it
38  *	into @fix for virtual console @con on device @info.
39  *
40  *	Returns negative errno on error, or zero on success.
41  *
42  */
43 
fbgen_get_fix(struct fb_fix_screeninfo * fix,int con,struct fb_info * info)44 int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
45 {
46     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
47     struct fbgen_hwswitch *fbhw = info2->fbhw;
48     char par[info2->parsize];
49 
50     if (con == -1)
51 	fbhw->get_par(&par, info2);
52     else {
53 	int err;
54 
55 	if ((err = fbhw->decode_var(&fb_display[con].var, &par, info2)))
56 	    return err;
57     }
58     memset(fix, 0, sizeof(struct fb_fix_screeninfo));
59     return fbhw->encode_fix(fix, &par, info2);
60 }
61 
62 
63 /**
64  *	fbgen_get_var - get user defined part of display
65  *	@var: fb_var_screeninfo structure
66  *	@con: virtual console number
67  *	@info: frame buffer info structure
68  *
69  *	Get the user defined part of the display and place it into @var
70  *	for virtual console @con on device @info.
71  *
72  *	Returns negative errno on error, or zero for success.
73  *
74  */
75 
fbgen_get_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)76 int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
77 {
78     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
79     struct fbgen_hwswitch *fbhw = info2->fbhw;
80     char par[info2->parsize];
81 
82     if (con == -1) {
83 	fbhw->get_par(&par, info2);
84 	fbhw->encode_var(var, &par, info2);
85     } else
86 	*var = fb_display[con].var;
87     return 0;
88 }
89 
90 
91 /**
92  *	fbgen_set_var - set the user defined part of display
93  *	@var: fb_var_screeninfo user defined part of the display
94  *	@con: virtual console number
95  *	@info: frame buffer info structure
96  *
97  *	Set the user defined part of the display as dictated by @var
98  *	for virtual console @con on device @info.
99  *
100  *	Returns negative errno on error, or zero for success.
101  *
102  */
103 
fbgen_set_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)104 int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
105 {
106     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
107     int err;
108     int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset;
109     struct fb_bitfield oldred, oldgreen, oldblue;
110 
111     if ((err = fbgen_do_set_var(var, con == currcon, info2)))
112 	return err;
113     if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
114 	oldxres = fb_display[con].var.xres;
115 	oldyres = fb_display[con].var.yres;
116 	oldxres_virtual = fb_display[con].var.xres_virtual;
117 	oldyres_virtual = fb_display[con].var.yres_virtual;
118 	oldbpp = fb_display[con].var.bits_per_pixel;
119 	oldred = fb_display[con].var.red;
120 	oldgreen = fb_display[con].var.green;
121 	oldblue = fb_display[con].var.blue;
122 	oldyoffset = fb_display[con].var.yoffset;
123 	fb_display[con].var = *var;
124 	if (oldxres != var->xres || oldyres != var->yres ||
125 	    oldxres_virtual != var->xres_virtual ||
126 	    oldyres_virtual != var->yres_virtual ||
127 	    oldbpp != var->bits_per_pixel ||
128 	    (!(memcmp(&oldred, &(var->red), sizeof(struct fb_bitfield)))) ||
129 	    (!(memcmp(&oldgreen, &(var->green), sizeof(struct fb_bitfield)))) ||
130 	    (!(memcmp(&oldblue, &(var->blue), sizeof(struct fb_bitfield)))) ||
131 	    oldyoffset != var->yoffset) {
132 	    fbgen_set_disp(con, info2);
133 	    if (info->changevar)
134 		(*info->changevar)(con);
135 	    if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0)))
136 		return err;
137 	    fbgen_install_cmap(con, info2);
138 	}
139     }
140     var->activate = 0;
141     return 0;
142 }
143 
144 
145 /**
146  *	fbgen_get_cmap - get the colormap
147  *	@cmap: frame buffer colormap structure
148  *	@kspc: boolean, 0 copy local, 1 put_user() function
149  *	@con: virtual console number
150  *	@info: frame buffer info structure
151  *
152  *	Gets the colormap for virtual console @con and places it into
153  *	@cmap for device @info.
154  *
155  *	Returns negative errno on error, or zero for success.
156  *
157  */
158 
fbgen_get_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)159 int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
160 		   struct fb_info *info)
161 {
162     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
163     struct fbgen_hwswitch *fbhw = info2->fbhw;
164 
165     if (con == currcon)			/* current console ? */
166 	return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info);
167     else
168 	if (fb_display[con].cmap.len)	/* non default colormap ? */
169 	    fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
170 	else {
171 	    int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
172 	    fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
173 	}
174     return 0;
175 }
176 
177 
178 /**
179  *	fbgen_set_cmap - set the colormap
180  *	@cmap: frame buffer colormap structure
181  *	@kspc: boolean, 0 copy local, 1 get_user() function
182  *	@con: virtual console number
183  *	@info: frame buffer info structure
184  *
185  *	Sets the colormap @cmap for virtual console @con on
186  *	device @info.
187  *
188  *	Returns negative errno on error, or zero for success.
189  *
190  */
191 
fbgen_set_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)192 int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
193 		   struct fb_info *info)
194 {
195     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
196     struct fbgen_hwswitch *fbhw = info2->fbhw;
197     int err;
198 
199     if (!fb_display[con].cmap.len) {	/* no colormap allocated ? */
200 	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
201 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
202 	    return err;
203     }
204     if (con == currcon)			/* current console ? */
205 	return fb_set_cmap(cmap, kspc, fbhw->setcolreg, info);
206     else
207 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
208     return 0;
209 }
210 
211 
212 /**
213  *	fbgen_pan_display - pan or wrap the display
214  *	@var: frame buffer user defined part of display
215  *	@con: virtual console number
216  *	@info: frame buffer info structure
217  *
218  *	Pan or wrap virtual console @con for device @info.
219  *
220  *	This call looks only at xoffset, yoffset and the
221  *	FB_VMODE_YWRAP flag in @var.
222  *
223  *	Returns negative errno on error, or zero for success.
224  *
225  */
226 
fbgen_pan_display(struct fb_var_screeninfo * var,int con,struct fb_info * info)227 int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
228 		      struct fb_info *info)
229 {
230     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
231     struct fbgen_hwswitch *fbhw = info2->fbhw;
232     int xoffset = var->xoffset;
233     int yoffset = var->yoffset;
234     int err;
235 
236     if (xoffset < 0 ||
237 	xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
238 	yoffset < 0 ||
239 	yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
240 	return -EINVAL;
241     if (con == currcon) {
242 	if (fbhw->pan_display) {
243 	    if ((err = fbhw->pan_display(var, info2)))
244 		return err;
245 	} else
246 	    return -EINVAL;
247     }
248     fb_display[con].var.xoffset = var->xoffset;
249     fb_display[con].var.yoffset = var->yoffset;
250     if (var->vmode & FB_VMODE_YWRAP)
251 	fb_display[con].var.vmode |= FB_VMODE_YWRAP;
252     else
253 	fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
254 
255     return 0;
256 }
257 
258 
259 /* ---- Helper functions --------------------------------------------------- */
260 
261 
262 /**
263  *	fbgen_do_set_var - change the video mode
264  *	@var: frame buffer user defined part of display
265  *	@isactive: boolean, 0 inactive, 1 active
266  *	@info: generic frame buffer info structure
267  *
268  *	Change the video mode settings for device @info.  If @isactive
269  *	is non-zero, the changes will be activated immediately.
270  *
271  *	Return negative errno on error, or zero for success.
272  *
273  */
274 
fbgen_do_set_var(struct fb_var_screeninfo * var,int isactive,struct fb_info_gen * info)275 int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
276 		     struct fb_info_gen *info)
277 {
278     struct fbgen_hwswitch *fbhw = info->fbhw;
279     int err, activate;
280     char par[info->parsize];
281 
282     if ((err = fbhw->decode_var(var, &par, info)))
283 	return err;
284     activate = var->activate;
285     if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
286 	fbhw->set_par(&par, info);
287     fbhw->encode_var(var, &par, info);
288     var->activate = activate;
289     return 0;
290 }
291 
292 
293 /**
294  *	fbgen_set_disp - set generic display
295  *	@con: virtual console number
296  *	@info: generic frame buffer info structure
297  *
298  *	Sets a display on virtual console @con for device @info.
299  *
300  */
301 
fbgen_set_disp(int con,struct fb_info_gen * info)302 void fbgen_set_disp(int con, struct fb_info_gen *info)
303 {
304     struct fbgen_hwswitch *fbhw = info->fbhw;
305     struct fb_fix_screeninfo fix;
306     char par[info->parsize];
307     struct display *display;
308 
309     if (con >= 0)
310 	display = &fb_display[con];
311     else
312 	display = info->info.disp;	/* used during initialization */
313 
314     if (con == -1)
315 	fbhw->get_par(&par, info);
316     else
317 	fbhw->decode_var(&fb_display[con].var, &par, info);
318     memset(&fix, 0, sizeof(struct fb_fix_screeninfo));
319     fbhw->encode_fix(&fix, &par, info);
320 
321     display->visual = fix.visual;
322     display->type = fix.type;
323     display->type_aux = fix.type_aux;
324     display->ypanstep = fix.ypanstep;
325     display->ywrapstep = fix.ywrapstep;
326     display->line_length = fix.line_length;
327     if (info->fbhw->blank || fix.visual == FB_VISUAL_PSEUDOCOLOR ||
328 	fix.visual == FB_VISUAL_DIRECTCOLOR)
329 	display->can_soft_blank = 1;
330     else
331 	display->can_soft_blank = 0;
332     fbhw->set_disp(&par, display, info);
333 #if 0 /* FIXME: generic inverse is not supported yet */
334     display->inverse = (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
335 #else
336     display->inverse = fix.visual == FB_VISUAL_MONO01;
337 #endif
338 }
339 
340 
341 /**
342  *	fbgen_install_cmap - install the current colormap
343  *	@con: virtual console number
344  *	@info: generic frame buffer info structure
345  *
346  *	Installs the current colormap for virtual console @con on
347  *	device @info.
348  *
349  */
350 
fbgen_install_cmap(int con,struct fb_info_gen * info)351 void fbgen_install_cmap(int con, struct fb_info_gen *info)
352 {
353     struct fbgen_hwswitch *fbhw = info->fbhw;
354     if (con != currcon)
355 	return;
356     if (fb_display[con].cmap.len)
357 	fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, &info->info);
358     else {
359 	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
360 	fb_set_cmap(fb_default_cmap(size), 1, fbhw->setcolreg, &info->info);
361     }
362 }
363 
364 
365 /**
366  *	fbgen_update_var - update user defined part of display
367  *	@con: virtual console number
368  *	@info: frame buffer info structure
369  *
370  *	Updates the user defined part of the display ('var'
371  *	structure) on virtual console @con for device @info.
372  *	This function is called by fbcon.c.
373  *
374  *	Returns negative errno on error, or zero for success.
375  *
376  */
377 
fbgen_update_var(int con,struct fb_info * info)378 int fbgen_update_var(int con, struct fb_info *info)
379 {
380     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
381     struct fbgen_hwswitch *fbhw = info2->fbhw;
382     int err;
383 
384     if (fbhw->pan_display) {
385         if ((err = fbhw->pan_display(&fb_display[con].var, info2)))
386             return err;
387     }
388     return 0;
389 }
390 
391 
392 /**
393  *	fbgen_switch - switch to a different virtual console.
394  *	@con: virtual console number
395  *	@info: frame buffer info structure
396  *
397  *	Switch to virtuall console @con on device @info.
398  *
399  *	Returns zero.
400  *
401  */
402 
fbgen_switch(int con,struct fb_info * info)403 int fbgen_switch(int con, struct fb_info *info)
404 {
405     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
406     struct fbgen_hwswitch *fbhw = info2->fbhw;
407 
408     /* Do we have to save the colormap ? */
409     if (fb_display[currcon].cmap.len)
410 	fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
411 		    &info2->info);
412     fbgen_do_set_var(&fb_display[con].var, 1, info2);
413     currcon = con;
414     /* Install new colormap */
415     fbgen_install_cmap(con, info2);
416     return 0;
417 }
418 
419 
420 /**
421  *	fbgen_blank - blank the screen
422  *	@blank: boolean, 0 unblank, 1 blank
423  *	@info: frame buffer info structure
424  *
425  *	Blank the screen on device @info.
426  *
427  */
428 
fbgen_blank(int blank,struct fb_info * info)429 void fbgen_blank(int blank, struct fb_info *info)
430 {
431     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
432     struct fbgen_hwswitch *fbhw = info2->fbhw;
433     u16 black[16];
434     struct fb_cmap cmap;
435 
436     if (fbhw->blank && !fbhw->blank(blank, info2))
437 	return;
438     if (blank) {
439 	memset(black, 0, 16*sizeof(u16));
440 	cmap.red = black;
441 	cmap.green = black;
442 	cmap.blue = black;
443 	cmap.transp = NULL;
444 	cmap.start = 0;
445 	cmap.len = 16;
446 	fb_set_cmap(&cmap, 1, fbhw->setcolreg, info);
447     } else
448 	fbgen_install_cmap(currcon, info2);
449 }
450 MODULE_LICENSE("GPL");
451 
452 
453     /*
454      *  Visible symbols for modules
455      */
456 
457 EXPORT_SYMBOL(fbgen_get_var);
458 EXPORT_SYMBOL(fbgen_get_cmap);
459 EXPORT_SYMBOL(fbgen_get_fix);
460 EXPORT_SYMBOL(fbgen_set_var);
461 EXPORT_SYMBOL(fbgen_set_cmap);
462 EXPORT_SYMBOL(fbgen_set_disp);
463 EXPORT_SYMBOL(fbgen_install_cmap);
464 EXPORT_SYMBOL(fbgen_pan_display);
465 EXPORT_SYMBOL(fbgen_update_var);
466 EXPORT_SYMBOL(fbgen_do_set_var);
467 EXPORT_SYMBOL(fbgen_switch);
468 EXPORT_SYMBOL(fbgen_blank);
469