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
25static 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
44int 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
76int 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
104int 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
159int 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
192int 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
227int 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
275int 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
302void 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    display->inverse = fix.visual == FB_VISUAL_MONO01;
334}
335
336
337/**
338 *	fbgen_install_cmap - install the current colormap
339 *	@con: virtual console number
340 *	@info: generic frame buffer info structure
341 *
342 *	Installs the current colormap for virtual console @con on
343 *	device @info.
344 *
345 */
346
347void fbgen_install_cmap(int con, struct fb_info_gen *info)
348{
349    struct fbgen_hwswitch *fbhw = info->fbhw;
350    if (con != currcon)
351	return;
352    if (fb_display[con].cmap.len)
353	fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, &info->info);
354    else {
355	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
356	fb_set_cmap(fb_default_cmap(size), 1, fbhw->setcolreg, &info->info);
357    }
358}
359
360
361/**
362 *	fbgen_update_var - update user defined part of display
363 *	@con: virtual console number
364 *	@info: frame buffer info structure
365 *
366 *	Updates the user defined part of the display ('var'
367 *	structure) on virtual console @con for device @info.
368 *	This function is called by fbcon.c.
369 *
370 *	Returns negative errno on error, or zero for success.
371 *
372 */
373
374int fbgen_update_var(int con, struct fb_info *info)
375{
376    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
377    struct fbgen_hwswitch *fbhw = info2->fbhw;
378    int err;
379
380    if (fbhw->pan_display) {
381        if ((err = fbhw->pan_display(&fb_display[con].var, info2)))
382            return err;
383    }
384    return 0;
385}
386
387
388/**
389 *	fbgen_switch - switch to a different virtual console.
390 *	@con: virtual console number
391 *	@info: frame buffer info structure
392 *
393 *	Switch to virtuall console @con on device @info.
394 *
395 *	Returns zero.
396 *
397 */
398
399int fbgen_switch(int con, struct fb_info *info)
400{
401    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
402    struct fbgen_hwswitch *fbhw = info2->fbhw;
403
404    /* Do we have to save the colormap ? */
405    if (fb_display[currcon].cmap.len)
406	fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
407		    &info2->info);
408    fbgen_do_set_var(&fb_display[con].var, 1, info2);
409    currcon = con;
410    /* Install new colormap */
411    fbgen_install_cmap(con, info2);
412    return 0;
413}
414
415
416/**
417 *	fbgen_blank - blank the screen
418 *	@blank: boolean, 0 unblank, 1 blank
419 *	@info: frame buffer info structure
420 *
421 *	Blank the screen on device @info.
422 *
423 */
424
425void fbgen_blank(int blank, struct fb_info *info)
426{
427    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
428    struct fbgen_hwswitch *fbhw = info2->fbhw;
429    u16 black[16];
430    struct fb_cmap cmap;
431
432    if (fbhw->blank && !fbhw->blank(blank, info2))
433	return;
434    if (blank) {
435	memset(black, 0, 16*sizeof(u16));
436	cmap.red = black;
437	cmap.green = black;
438	cmap.blue = black;
439	cmap.transp = NULL;
440	cmap.start = 0;
441	cmap.len = 16;
442	fb_set_cmap(&cmap, 1, fbhw->setcolreg, info);
443    } else
444	fbgen_install_cmap(currcon, info2);
445}
446MODULE_LICENSE("GPL");
447
448
449    /*
450     *  Visible symbols for modules
451     */
452
453EXPORT_SYMBOL(fbgen_get_var);
454EXPORT_SYMBOL(fbgen_get_cmap);
455EXPORT_SYMBOL(fbgen_get_fix);
456EXPORT_SYMBOL(fbgen_set_var);
457EXPORT_SYMBOL(fbgen_set_cmap);
458EXPORT_SYMBOL(fbgen_set_disp);
459EXPORT_SYMBOL(fbgen_install_cmap);
460EXPORT_SYMBOL(fbgen_pan_display);
461EXPORT_SYMBOL(fbgen_update_var);
462EXPORT_SYMBOL(fbgen_do_set_var);
463EXPORT_SYMBOL(fbgen_switch);
464EXPORT_SYMBOL(fbgen_blank);
465