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