1 /*
2 * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
3 *
4 * Copyright (C) 1995 Geert Uytterhoeven
5 *
6 *
7 * This file is based on the original Amiga console driver (amicon.c):
8 *
9 * Copyright (C) 1993 Hamish Macdonald
10 * Greg Harp
11 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
12 *
13 * with work by William Rucklidge (wjr@cs.cornell.edu)
14 * Geert Uytterhoeven
15 * Jes Sorensen (jds@kom.auc.dk)
16 * Martin Apel
17 *
18 * and on the original Atari console driver (atacon.c):
19 *
20 * Copyright (C) 1993 Bjoern Brauel
21 * Roman Hodek
22 *
23 * with work by Guenther Kelleter
24 * Martin Schaller
25 * Andreas Schwab
26 *
27 * Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
28 * Smart redraw scrolling, arbitrary font width support, 512char font support
29 * and software scrollback added by
30 * Jakub Jelinek (jj@ultra.linux.cz)
31 *
32 * Random hacking by Martin Mares <mj@ucw.cz>
33 *
34 * 2001 - Documented with DocBook
35 * - Brad Douglas <brad@neruo.com>
36 *
37 * The low level operations for the various display memory organizations are
38 * now in separate source files.
39 *
40 * Currently the following organizations are supported:
41 *
42 * o afb Amiga bitplanes
43 * o cfb{2,4,8,16,24,32} Packed pixels
44 * o ilbm Amiga interleaved bitplanes
45 * o iplan2p[248] Atari interleaved bitplanes
46 * o mfb Monochrome
47 * o vga VGA characters/attributes
48 *
49 * To do:
50 *
51 * - Implement 16 plane mode (iplan2p16)
52 *
53 *
54 * This file is subject to the terms and conditions of the GNU General Public
55 * License. See the file COPYING in the main directory of this archive for
56 * more details.
57 */
58
59 #include <linux/export.h>
60 #include <linux/module.h>
61 #include <linux/types.h>
62 #include <linux/fs.h>
63 #include <linux/kernel.h>
64 #include <linux/delay.h> /* MSch: for IRQ probe */
65 #include <linux/console.h>
66 #include <linux/string.h>
67 #include <linux/kd.h>
68 #include <linux/panic.h>
69 #include <linux/printk.h>
70 #include <linux/slab.h>
71 #include <linux/fb.h>
72 #include <linux/fbcon.h>
73 #include <linux/vt_kern.h>
74 #include <linux/selection.h>
75 #include <linux/font.h>
76 #include <linux/smp.h>
77 #include <linux/init.h>
78 #include <linux/interrupt.h>
79 #include <linux/crc32.h> /* For counting font checksums */
80 #include <linux/uaccess.h>
81 #include <asm/irq.h>
82
83 #include "fbcon.h"
84 #include "fb_internal.h"
85
86 /*
87 * FIXME: Locking
88 *
89 * - fbcon state itself is protected by the console_lock, and the code does a
90 * pretty good job at making sure that lock is held everywhere it's needed.
91 *
92 * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
93 * means concurrent access to the same fbdev from both fbcon and userspace
94 * will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
95 * of fb_lock/unlock protected sections, since otherwise we'll recurse and
96 * deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
97 * fbmem.c cannot use locking asserts, and there's lots of callers which get
98 * the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
99 */
100
101 enum {
102 FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */
103 FBCON_LOGO_DRAW = -2, /* draw the logo to a console */
104 FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
105 };
106
107 static struct fbcon_display fb_display[MAX_NR_CONSOLES];
108
109 static struct fb_info *fbcon_registered_fb[FB_MAX];
110 static int fbcon_num_registered_fb;
111
112 #define fbcon_for_each_registered_fb(i) \
113 for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \
114 if (!fbcon_registered_fb[i]) {} else
115
116 static signed char con2fb_map[MAX_NR_CONSOLES];
117 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
118
fbcon_info_from_console(int console)119 static struct fb_info *fbcon_info_from_console(int console)
120 {
121 signed char fb;
122 WARN_CONSOLE_UNLOCKED();
123
124 fb = con2fb_map[console];
125 if (fb < 0 || fb >= ARRAY_SIZE(fbcon_registered_fb))
126 return NULL;
127
128 return fbcon_registered_fb[fb];
129 }
130
131 static int logo_lines;
132 /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
133 enums. */
134 static int logo_shown = FBCON_LOGO_CANSHOW;
135 /* console mappings */
136 static unsigned int first_fb_vc;
137 static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
138 static bool fbcon_is_default = true;
139 static int primary_device = -1;
140 static bool fbcon_has_console_bind;
141
142 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
143 static int map_override;
144
fbcon_map_override(void)145 static inline void fbcon_map_override(void)
146 {
147 map_override = 1;
148 }
149 #else
fbcon_map_override(void)150 static inline void fbcon_map_override(void)
151 {
152 }
153 #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
154
155 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
156 static bool deferred_takeover = true;
157 #else
158 #define deferred_takeover false
159 #endif
160
161 /* font data */
162 static char fontname[40];
163
164 /* current fb_info */
165 static int info_idx = -1;
166
167 /* console rotation */
168 static int initial_rotation = -1;
169 static int margin_color;
170
171 static const struct consw fb_con;
172
173 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
174
175 static bool fbcon_cursor_blink = true;
176
177 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
178
179 /*
180 * Interface used by the world
181 */
182
183 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
184 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
185
186 /*
187 * Internal routines
188 */
189 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
190 int unit);
191 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
192 int line, int count, int dy);
193 static void fbcon_modechanged(struct fb_info *info);
194 static void fbcon_set_all_vcs(struct fb_info *info);
195
196 static struct device *fbcon_device;
197
198 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
fbcon_set_rotation(struct fb_info * info)199 static inline void fbcon_set_rotation(struct fb_info *info)
200 {
201 struct fbcon_ops *ops = info->fbcon_par;
202
203 if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
204 ops->p->con_rotate < 4)
205 ops->rotate = ops->p->con_rotate;
206 else
207 ops->rotate = 0;
208 }
209
fbcon_rotate(struct fb_info * info,u32 rotate)210 static void fbcon_rotate(struct fb_info *info, u32 rotate)
211 {
212 struct fbcon_ops *ops= info->fbcon_par;
213 struct fb_info *fb_info;
214
215 if (!ops || ops->currcon == -1)
216 return;
217
218 fb_info = fbcon_info_from_console(ops->currcon);
219
220 if (info == fb_info) {
221 struct fbcon_display *p = &fb_display[ops->currcon];
222
223 if (rotate < 4)
224 p->con_rotate = rotate;
225 else
226 p->con_rotate = 0;
227
228 fbcon_modechanged(info);
229 }
230 }
231
fbcon_rotate_all(struct fb_info * info,u32 rotate)232 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
233 {
234 struct fbcon_ops *ops = info->fbcon_par;
235 struct vc_data *vc;
236 struct fbcon_display *p;
237 int i;
238
239 if (!ops || ops->currcon < 0 || rotate > 3)
240 return;
241
242 for (i = first_fb_vc; i <= last_fb_vc; i++) {
243 vc = vc_cons[i].d;
244 if (!vc || vc->vc_mode != KD_TEXT ||
245 fbcon_info_from_console(i) != info)
246 continue;
247
248 p = &fb_display[vc->vc_num];
249 p->con_rotate = rotate;
250 }
251
252 fbcon_set_all_vcs(info);
253 }
254 #else
fbcon_set_rotation(struct fb_info * info)255 static inline void fbcon_set_rotation(struct fb_info *info)
256 {
257 struct fbcon_ops *ops = info->fbcon_par;
258
259 ops->rotate = FB_ROTATE_UR;
260 }
261
fbcon_rotate(struct fb_info * info,u32 rotate)262 static void fbcon_rotate(struct fb_info *info, u32 rotate)
263 {
264 return;
265 }
266
fbcon_rotate_all(struct fb_info * info,u32 rotate)267 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
268 {
269 return;
270 }
271 #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
272
fbcon_get_rotate(struct fb_info * info)273 static int fbcon_get_rotate(struct fb_info *info)
274 {
275 struct fbcon_ops *ops = info->fbcon_par;
276
277 return (ops) ? ops->rotate : 0;
278 }
279
fbcon_skip_panic(struct fb_info * info)280 static bool fbcon_skip_panic(struct fb_info *info)
281 {
282 return (info->skip_panic && unlikely(panic_in_progress()));
283 }
284
fbcon_is_active(struct vc_data * vc,struct fb_info * info)285 static inline bool fbcon_is_active(struct vc_data *vc, struct fb_info *info)
286 {
287 struct fbcon_ops *ops = info->fbcon_par;
288
289 return info->state == FBINFO_STATE_RUNNING &&
290 vc->vc_mode == KD_TEXT && !ops->graphics && !fbcon_skip_panic(info);
291 }
292
get_color(struct vc_data * vc,struct fb_info * info,u16 c,bool is_fg)293 static int get_color(struct vc_data *vc, struct fb_info *info,
294 u16 c, bool is_fg)
295 {
296 int depth = fb_get_color_depth(&info->var, &info->fix);
297 int color = 0;
298
299 if (console_blanked) {
300 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
301
302 c = vc->vc_video_erase_char & charmask;
303 }
304
305 if (depth != 1)
306 color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
307 : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
308
309 switch (depth) {
310 case 1:
311 {
312 int col = mono_col(info);
313 /* 0 or 1 */
314 int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
315 int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
316
317 if (console_blanked)
318 fg = bg;
319
320 color = (is_fg) ? fg : bg;
321 break;
322 }
323 case 2:
324 /*
325 * Scale down 16-colors to 4 colors. Default 4-color palette
326 * is grayscale. However, simply dividing the values by 4
327 * will not work, as colors 1, 2 and 3 will be scaled-down
328 * to zero rendering them invisible. So empirically convert
329 * colors to a sane 4-level grayscale.
330 */
331 switch (color) {
332 case 0:
333 color = 0; /* black */
334 break;
335 case 1 ... 6:
336 color = 2; /* white */
337 break;
338 case 7 ... 8:
339 color = 1; /* gray */
340 break;
341 default:
342 color = 3; /* intense white */
343 break;
344 }
345 break;
346 case 3:
347 /*
348 * Last 8 entries of default 16-color palette is a more intense
349 * version of the first 8 (i.e., same chrominance, different
350 * luminance).
351 */
352 color &= 7;
353 break;
354 }
355
356
357 return color;
358 }
359
get_fg_color(struct vc_data * vc,struct fb_info * info,u16 c)360 static int get_fg_color(struct vc_data *vc, struct fb_info *info, u16 c)
361 {
362 return get_color(vc, info, c, true);
363 }
364
get_bg_color(struct vc_data * vc,struct fb_info * info,u16 c)365 static int get_bg_color(struct vc_data *vc, struct fb_info *info, u16 c)
366 {
367 return get_color(vc, info, c, false);
368 }
369
fb_flashcursor(struct work_struct * work)370 static void fb_flashcursor(struct work_struct *work)
371 {
372 struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
373 struct fb_info *info;
374 struct vc_data *vc = NULL;
375 int c;
376 bool enable;
377 int ret;
378
379 /* FIXME: we should sort out the unbind locking instead */
380 /* instead we just fail to flash the cursor if we can't get
381 * the lock instead of blocking fbcon deinit */
382 ret = console_trylock();
383 if (ret == 0)
384 return;
385
386 /* protected by console_lock */
387 info = ops->info;
388
389 if (ops->currcon != -1)
390 vc = vc_cons[ops->currcon].d;
391
392 if (!vc || !con_is_visible(vc) ||
393 fbcon_info_from_console(vc->vc_num) != info ||
394 vc->vc_deccm != 1) {
395 console_unlock();
396 return;
397 }
398
399 c = scr_readw((u16 *) vc->vc_pos);
400 enable = ops->cursor_flash && !ops->cursor_state.enable;
401 ops->cursor(vc, info, enable,
402 get_fg_color(vc, info, c),
403 get_bg_color(vc, info, c));
404 console_unlock();
405
406 queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
407 ops->cur_blink_jiffies);
408 }
409
fbcon_add_cursor_work(struct fb_info * info)410 static void fbcon_add_cursor_work(struct fb_info *info)
411 {
412 struct fbcon_ops *ops = info->fbcon_par;
413
414 if (fbcon_cursor_blink)
415 queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
416 ops->cur_blink_jiffies);
417 }
418
fbcon_del_cursor_work(struct fb_info * info)419 static void fbcon_del_cursor_work(struct fb_info *info)
420 {
421 struct fbcon_ops *ops = info->fbcon_par;
422
423 cancel_delayed_work_sync(&ops->cursor_work);
424 }
425
426 #ifndef MODULE
fb_console_setup(char * this_opt)427 static int __init fb_console_setup(char *this_opt)
428 {
429 char *options;
430 int i, j;
431
432 if (!this_opt || !*this_opt)
433 return 1;
434
435 while ((options = strsep(&this_opt, ",")) != NULL) {
436 if (!strncmp(options, "font:", 5)) {
437 strscpy(fontname, options + 5, sizeof(fontname));
438 continue;
439 }
440
441 if (!strncmp(options, "scrollback:", 11)) {
442 pr_warn("Ignoring scrollback size option\n");
443 continue;
444 }
445
446 if (!strncmp(options, "map:", 4)) {
447 options += 4;
448 if (*options) {
449 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
450 if (!options[j])
451 j = 0;
452 con2fb_map_boot[i] =
453 (options[j++]-'0') % FB_MAX;
454 }
455
456 fbcon_map_override();
457 }
458 continue;
459 }
460
461 if (!strncmp(options, "vc:", 3)) {
462 options += 3;
463 if (*options)
464 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
465 if (first_fb_vc >= MAX_NR_CONSOLES)
466 first_fb_vc = 0;
467 if (*options++ == '-')
468 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
469 if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
470 last_fb_vc = MAX_NR_CONSOLES - 1;
471 fbcon_is_default = false;
472 continue;
473 }
474
475 if (!strncmp(options, "rotate:", 7)) {
476 options += 7;
477 if (*options)
478 initial_rotation = simple_strtoul(options, &options, 0);
479 if (initial_rotation > 3)
480 initial_rotation = 0;
481 continue;
482 }
483
484 if (!strncmp(options, "margin:", 7)) {
485 options += 7;
486 if (*options)
487 margin_color = simple_strtoul(options, &options, 0);
488 continue;
489 }
490 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
491 if (!strcmp(options, "nodefer")) {
492 deferred_takeover = false;
493 continue;
494 }
495 #endif
496
497 #ifdef CONFIG_LOGO
498 if (!strncmp(options, "logo-pos:", 9)) {
499 options += 9;
500 if (!strcmp(options, "center"))
501 fb_center_logo = true;
502 continue;
503 }
504
505 if (!strncmp(options, "logo-count:", 11)) {
506 options += 11;
507 if (*options)
508 fb_logo_count = simple_strtol(options, &options, 0);
509 continue;
510 }
511 #endif
512 }
513 return 1;
514 }
515
516 __setup("fbcon=", fb_console_setup);
517 #endif
518
search_fb_in_map(int idx)519 static int search_fb_in_map(int idx)
520 {
521 int i, retval = 0;
522
523 for (i = first_fb_vc; i <= last_fb_vc; i++) {
524 if (con2fb_map[i] == idx) {
525 retval = 1;
526 break;
527 }
528 }
529 return retval;
530 }
531
search_for_mapped_con(void)532 static int search_for_mapped_con(void)
533 {
534 int i, retval = 0;
535
536 for (i = first_fb_vc; i <= last_fb_vc; i++) {
537 if (con2fb_map[i] != -1) {
538 retval = 1;
539 break;
540 }
541 }
542 return retval;
543 }
544
do_fbcon_takeover(int show_logo)545 static int do_fbcon_takeover(int show_logo)
546 {
547 int err, i;
548
549 if (!fbcon_num_registered_fb)
550 return -ENODEV;
551
552 if (!show_logo)
553 logo_shown = FBCON_LOGO_DONTSHOW;
554
555 for (i = first_fb_vc; i <= last_fb_vc; i++)
556 con2fb_map[i] = info_idx;
557
558 err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
559 fbcon_is_default);
560
561 if (err) {
562 for (i = first_fb_vc; i <= last_fb_vc; i++)
563 con2fb_map[i] = -1;
564 info_idx = -1;
565 } else {
566 fbcon_has_console_bind = true;
567 }
568
569 return err;
570 }
571
572 #ifdef MODULE
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)573 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
574 int cols, int rows, int new_cols, int new_rows)
575 {
576 logo_shown = FBCON_LOGO_DONTSHOW;
577 }
578 #else
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)579 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
580 int cols, int rows, int new_cols, int new_rows)
581 {
582 /* Need to make room for the logo */
583 struct fbcon_ops *ops = info->fbcon_par;
584 int cnt, erase = vc->vc_video_erase_char, step;
585 unsigned short *save = NULL, *r, *q;
586 int logo_height;
587
588 if (info->fbops->owner) {
589 logo_shown = FBCON_LOGO_DONTSHOW;
590 return;
591 }
592
593 /*
594 * remove underline attribute from erase character
595 * if black and white framebuffer.
596 */
597 if (fb_get_color_depth(&info->var, &info->fix) == 1)
598 erase &= ~0x400;
599 logo_height = fb_prepare_logo(info, ops->rotate);
600 logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
601 q = (unsigned short *) (vc->vc_origin +
602 vc->vc_size_row * rows);
603 step = logo_lines * cols;
604 for (r = q - logo_lines * cols; r < q; r++)
605 if (scr_readw(r) != vc->vc_video_erase_char)
606 break;
607 if (r != q && new_rows >= rows + logo_lines) {
608 save = kmalloc(array3_size(logo_lines, new_cols, 2),
609 GFP_KERNEL);
610 if (save) {
611 int i = min(cols, new_cols);
612 scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
613 r = q - step;
614 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
615 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
616 r = q;
617 }
618 }
619 if (r == q) {
620 /* We can scroll screen down */
621 r = q - step - cols;
622 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
623 scr_memcpyw(r + step, r, vc->vc_size_row);
624 r -= cols;
625 }
626 if (!save) {
627 int lines;
628 if (vc->state.y + logo_lines >= rows)
629 lines = rows - vc->state.y - 1;
630 else
631 lines = logo_lines;
632 vc->state.y += lines;
633 vc->vc_pos += lines * vc->vc_size_row;
634 }
635 }
636 scr_memsetw((unsigned short *) vc->vc_origin,
637 erase,
638 vc->vc_size_row * logo_lines);
639
640 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
641 fbcon_clear_margins(vc, 0);
642 update_screen(vc);
643 }
644
645 if (save) {
646 q = (unsigned short *) (vc->vc_origin +
647 vc->vc_size_row *
648 rows);
649 scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
650 vc->state.y += logo_lines;
651 vc->vc_pos += logo_lines * vc->vc_size_row;
652 kfree(save);
653 }
654
655 if (logo_shown == FBCON_LOGO_DONTSHOW)
656 return;
657
658 if (logo_lines > vc->vc_bottom) {
659 logo_shown = FBCON_LOGO_CANSHOW;
660 pr_info("fbcon: disable boot-logo (boot-logo bigger than screen).\n");
661 } else {
662 logo_shown = FBCON_LOGO_DRAW;
663 vc->vc_top = logo_lines;
664 }
665 }
666 #endif /* MODULE */
667
668 #ifdef CONFIG_FB_TILEBLITTING
set_blitting_type(struct vc_data * vc,struct fb_info * info)669 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
670 {
671 struct fbcon_ops *ops = info->fbcon_par;
672
673 ops->p = &fb_display[vc->vc_num];
674
675 if ((info->flags & FBINFO_MISC_TILEBLITTING))
676 fbcon_set_tileops(vc, info);
677 else {
678 fbcon_set_rotation(info);
679 fbcon_set_bitops(ops);
680 }
681 }
682
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)683 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
684 {
685 int err = 0;
686
687 if (info->flags & FBINFO_MISC_TILEBLITTING &&
688 info->tileops->fb_get_tilemax(info) < charcount)
689 err = 1;
690
691 return err;
692 }
693 #else
set_blitting_type(struct vc_data * vc,struct fb_info * info)694 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
695 {
696 struct fbcon_ops *ops = info->fbcon_par;
697
698 info->flags &= ~FBINFO_MISC_TILEBLITTING;
699 ops->p = &fb_display[vc->vc_num];
700 fbcon_set_rotation(info);
701 fbcon_set_bitops(ops);
702 }
703
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)704 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
705 {
706 return 0;
707 }
708
709 #endif /* CONFIG_MISC_TILEBLITTING */
710
fbcon_release(struct fb_info * info)711 static void fbcon_release(struct fb_info *info)
712 {
713 lock_fb_info(info);
714 if (info->fbops->fb_release)
715 info->fbops->fb_release(info, 0);
716 unlock_fb_info(info);
717
718 module_put(info->fbops->owner);
719
720 if (info->fbcon_par) {
721 struct fbcon_ops *ops = info->fbcon_par;
722
723 fbcon_del_cursor_work(info);
724 kfree(ops->cursor_state.mask);
725 kfree(ops->cursor_data);
726 kfree(ops->cursor_src);
727 kfree(ops->fontbuffer);
728 kfree(info->fbcon_par);
729 info->fbcon_par = NULL;
730 }
731 }
732
fbcon_open(struct fb_info * info)733 static int fbcon_open(struct fb_info *info)
734 {
735 struct fbcon_ops *ops;
736
737 if (!try_module_get(info->fbops->owner))
738 return -ENODEV;
739
740 lock_fb_info(info);
741 if (info->fbops->fb_open &&
742 info->fbops->fb_open(info, 0)) {
743 unlock_fb_info(info);
744 module_put(info->fbops->owner);
745 return -ENODEV;
746 }
747 unlock_fb_info(info);
748
749 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
750 if (!ops) {
751 fbcon_release(info);
752 return -ENOMEM;
753 }
754
755 INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor);
756 ops->info = info;
757 info->fbcon_par = ops;
758 ops->cur_blink_jiffies = HZ / 5;
759
760 return 0;
761 }
762
con2fb_acquire_newinfo(struct vc_data * vc,struct fb_info * info,int unit)763 static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
764 int unit)
765 {
766 int err;
767
768 err = fbcon_open(info);
769 if (err)
770 return err;
771
772 if (vc)
773 set_blitting_type(vc, info);
774
775 return err;
776 }
777
con2fb_release_oldinfo(struct vc_data * vc,struct fb_info * oldinfo,struct fb_info * newinfo)778 static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
779 struct fb_info *newinfo)
780 {
781 int ret;
782
783 fbcon_release(oldinfo);
784
785 /*
786 If oldinfo and newinfo are driving the same hardware,
787 the fb_release() method of oldinfo may attempt to
788 restore the hardware state. This will leave the
789 newinfo in an undefined state. Thus, a call to
790 fb_set_par() may be needed for the newinfo.
791 */
792 if (newinfo && newinfo->fbops->fb_set_par) {
793 ret = newinfo->fbops->fb_set_par(newinfo);
794
795 if (ret)
796 printk(KERN_ERR "con2fb_release_oldinfo: "
797 "detected unhandled fb_set_par error, "
798 "error code %d\n", ret);
799 }
800 }
801
con2fb_init_display(struct vc_data * vc,struct fb_info * info,int unit,int show_logo)802 static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
803 int unit, int show_logo)
804 {
805 struct fbcon_ops *ops = info->fbcon_par;
806 int ret;
807
808 ops->currcon = fg_console;
809
810 if (info->fbops->fb_set_par && !ops->initialized) {
811 ret = info->fbops->fb_set_par(info);
812
813 if (ret)
814 printk(KERN_ERR "con2fb_init_display: detected "
815 "unhandled fb_set_par error, "
816 "error code %d\n", ret);
817 }
818
819 ops->initialized = true;
820 ops->graphics = 0;
821 fbcon_set_disp(info, &info->var, unit);
822
823 if (show_logo) {
824 struct vc_data *fg_vc = vc_cons[fg_console].d;
825 struct fb_info *fg_info =
826 fbcon_info_from_console(fg_console);
827
828 fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
829 fg_vc->vc_rows, fg_vc->vc_cols,
830 fg_vc->vc_rows);
831 }
832
833 if (fg_console != unit)
834 update_screen(vc_cons[fg_console].d);
835 }
836
837 /**
838 * set_con2fb_map - map console to frame buffer device
839 * @unit: virtual console number to map
840 * @newidx: frame buffer index to map virtual console to
841 * @user: user request
842 *
843 * Maps a virtual console @unit to a frame buffer device
844 * @newidx.
845 *
846 * This should be called with the console lock held.
847 */
set_con2fb_map(int unit,int newidx,int user)848 static int set_con2fb_map(int unit, int newidx, int user)
849 {
850 struct vc_data *vc = vc_cons[unit].d;
851 int oldidx = con2fb_map[unit];
852 struct fb_info *info = fbcon_registered_fb[newidx];
853 struct fb_info *oldinfo = NULL;
854 int err = 0, show_logo;
855
856 WARN_CONSOLE_UNLOCKED();
857
858 if (oldidx == newidx)
859 return 0;
860
861 if (!info)
862 return -EINVAL;
863
864 if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
865 info_idx = newidx;
866 return do_fbcon_takeover(0);
867 }
868
869 if (oldidx != -1)
870 oldinfo = fbcon_registered_fb[oldidx];
871
872 if (!search_fb_in_map(newidx)) {
873 err = con2fb_acquire_newinfo(vc, info, unit);
874 if (err)
875 return err;
876
877 fbcon_add_cursor_work(info);
878 } else if (vc) {
879 set_blitting_type(vc, info);
880 }
881
882 con2fb_map[unit] = newidx;
883
884 /*
885 * If old fb is not mapped to any of the consoles,
886 * fbcon should release it.
887 */
888 if (oldinfo && !search_fb_in_map(oldidx))
889 con2fb_release_oldinfo(vc, oldinfo, info);
890
891 show_logo = (fg_console == 0 && !user &&
892 logo_shown != FBCON_LOGO_DONTSHOW);
893
894 con2fb_map_boot[unit] = newidx;
895 con2fb_init_display(vc, info, unit, show_logo);
896
897 if (!search_fb_in_map(info_idx))
898 info_idx = newidx;
899
900 return err;
901 }
902
903 /*
904 * Low Level Operations
905 */
906 /* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
var_to_display(struct fbcon_display * disp,struct fb_var_screeninfo * var,struct fb_info * info)907 static int var_to_display(struct fbcon_display *disp,
908 struct fb_var_screeninfo *var,
909 struct fb_info *info)
910 {
911 disp->xres_virtual = var->xres_virtual;
912 disp->yres_virtual = var->yres_virtual;
913 disp->bits_per_pixel = var->bits_per_pixel;
914 disp->grayscale = var->grayscale;
915 disp->nonstd = var->nonstd;
916 disp->accel_flags = var->accel_flags;
917 disp->height = var->height;
918 disp->width = var->width;
919 disp->red = var->red;
920 disp->green = var->green;
921 disp->blue = var->blue;
922 disp->transp = var->transp;
923 disp->rotate = var->rotate;
924 disp->mode = fb_match_mode(var, &info->modelist);
925 if (disp->mode == NULL)
926 /* This should not happen */
927 return -EINVAL;
928 return 0;
929 }
930
display_to_var(struct fb_var_screeninfo * var,struct fbcon_display * disp)931 static void display_to_var(struct fb_var_screeninfo *var,
932 struct fbcon_display *disp)
933 {
934 fb_videomode_to_var(var, disp->mode);
935 var->xres_virtual = disp->xres_virtual;
936 var->yres_virtual = disp->yres_virtual;
937 var->bits_per_pixel = disp->bits_per_pixel;
938 var->grayscale = disp->grayscale;
939 var->nonstd = disp->nonstd;
940 var->accel_flags = disp->accel_flags;
941 var->height = disp->height;
942 var->width = disp->width;
943 var->red = disp->red;
944 var->green = disp->green;
945 var->blue = disp->blue;
946 var->transp = disp->transp;
947 var->rotate = disp->rotate;
948 }
949
fbcon_startup(void)950 static const char *fbcon_startup(void)
951 {
952 static const char display_desc[] = "frame buffer device";
953 struct fbcon_display *p = &fb_display[fg_console];
954 struct vc_data *vc = vc_cons[fg_console].d;
955 const struct font_desc *font = NULL;
956 struct fb_info *info = NULL;
957 struct fbcon_ops *ops;
958 int rows, cols;
959
960 /*
961 * If fbcon_num_registered_fb is zero, this is a call for the dummy part.
962 * The frame buffer devices weren't initialized yet.
963 */
964 if (!fbcon_num_registered_fb || info_idx == -1)
965 return display_desc;
966 /*
967 * Instead of blindly using fbcon_registered_fb[0], we use info_idx, set by
968 * fbcon_fb_registered();
969 */
970 info = fbcon_registered_fb[info_idx];
971 if (!info)
972 return NULL;
973
974 if (fbcon_open(info))
975 return NULL;
976
977 ops = info->fbcon_par;
978 ops->currcon = -1;
979 ops->graphics = 1;
980 ops->cur_rotate = -1;
981
982 p->con_rotate = initial_rotation;
983 if (p->con_rotate == -1)
984 p->con_rotate = info->fbcon_rotate_hint;
985 if (p->con_rotate == -1)
986 p->con_rotate = FB_ROTATE_UR;
987
988 set_blitting_type(vc, info);
989
990 /* Setup default font */
991 if (!p->fontdata) {
992 if (!fontname[0] || !(font = find_font(fontname)))
993 font = get_default_font(info->var.xres,
994 info->var.yres,
995 info->pixmap.blit_x,
996 info->pixmap.blit_y);
997 vc->vc_font.width = font->width;
998 vc->vc_font.height = font->height;
999 vc->vc_font.data = (void *)(p->fontdata = font->data);
1000 vc->vc_font.charcount = font->charcount;
1001 }
1002
1003 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1004 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1005 cols /= vc->vc_font.width;
1006 rows /= vc->vc_font.height;
1007 vc_resize(vc, cols, rows);
1008
1009 pr_debug("mode: %s\n", info->fix.id);
1010 pr_debug("visual: %d\n", info->fix.visual);
1011 pr_debug("res: %dx%d-%d\n", info->var.xres,
1012 info->var.yres,
1013 info->var.bits_per_pixel);
1014
1015 fbcon_add_cursor_work(info);
1016 return display_desc;
1017 }
1018
fbcon_init(struct vc_data * vc,bool init)1019 static void fbcon_init(struct vc_data *vc, bool init)
1020 {
1021 struct fb_info *info;
1022 struct fbcon_ops *ops;
1023 struct vc_data **default_mode = vc->vc_display_fg;
1024 struct vc_data *svc = *default_mode;
1025 struct fbcon_display *t, *p = &fb_display[vc->vc_num];
1026 int logo = 1, new_rows, new_cols, rows, cols;
1027 int ret;
1028
1029 if (WARN_ON(info_idx == -1))
1030 return;
1031
1032 if (con2fb_map[vc->vc_num] == -1)
1033 con2fb_map[vc->vc_num] = info_idx;
1034
1035 info = fbcon_info_from_console(vc->vc_num);
1036
1037 if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1038 logo_shown = FBCON_LOGO_DONTSHOW;
1039
1040 if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1041 (info->fix.type == FB_TYPE_TEXT))
1042 logo = 0;
1043
1044 if (var_to_display(p, &info->var, info))
1045 return;
1046
1047 if (!info->fbcon_par)
1048 con2fb_acquire_newinfo(vc, info, vc->vc_num);
1049
1050 /* If we are not the first console on this
1051 fb, copy the font from that console */
1052 t = &fb_display[fg_console];
1053 if (!p->fontdata) {
1054 if (t->fontdata) {
1055 struct vc_data *fvc = vc_cons[fg_console].d;
1056
1057 vc->vc_font.data = (void *)(p->fontdata =
1058 fvc->vc_font.data);
1059 vc->vc_font.width = fvc->vc_font.width;
1060 vc->vc_font.height = fvc->vc_font.height;
1061 vc->vc_font.charcount = fvc->vc_font.charcount;
1062 p->userfont = t->userfont;
1063
1064 if (p->userfont)
1065 REFCOUNT(p->fontdata)++;
1066 } else {
1067 const struct font_desc *font = NULL;
1068
1069 if (!fontname[0] || !(font = find_font(fontname)))
1070 font = get_default_font(info->var.xres,
1071 info->var.yres,
1072 info->pixmap.blit_x,
1073 info->pixmap.blit_y);
1074 vc->vc_font.width = font->width;
1075 vc->vc_font.height = font->height;
1076 vc->vc_font.data = (void *)(p->fontdata = font->data);
1077 vc->vc_font.charcount = font->charcount;
1078 }
1079 }
1080
1081 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1082 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1083 if (vc->vc_font.charcount == 256) {
1084 vc->vc_hi_font_mask = 0;
1085 } else {
1086 vc->vc_hi_font_mask = 0x100;
1087 if (vc->vc_can_do_color)
1088 vc->vc_complement_mask <<= 1;
1089 }
1090
1091 if (!*svc->uni_pagedict_loc)
1092 con_set_default_unimap(svc);
1093 if (!*vc->uni_pagedict_loc)
1094 con_copy_unimap(vc, svc);
1095
1096 ops = info->fbcon_par;
1097 ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1098
1099 p->con_rotate = initial_rotation;
1100 if (p->con_rotate == -1)
1101 p->con_rotate = info->fbcon_rotate_hint;
1102 if (p->con_rotate == -1)
1103 p->con_rotate = FB_ROTATE_UR;
1104
1105 set_blitting_type(vc, info);
1106
1107 cols = vc->vc_cols;
1108 rows = vc->vc_rows;
1109 new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1110 new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1111 new_cols /= vc->vc_font.width;
1112 new_rows /= vc->vc_font.height;
1113
1114 /*
1115 * We must always set the mode. The mode of the previous console
1116 * driver could be in the same resolution but we are using different
1117 * hardware so we have to initialize the hardware.
1118 *
1119 * We need to do it in fbcon_init() to prevent screen corruption.
1120 */
1121 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1122 if (info->fbops->fb_set_par && !ops->initialized) {
1123 ret = info->fbops->fb_set_par(info);
1124
1125 if (ret)
1126 printk(KERN_ERR "fbcon_init: detected "
1127 "unhandled fb_set_par error, "
1128 "error code %d\n", ret);
1129 }
1130
1131 ops->initialized = true;
1132 }
1133
1134 ops->graphics = 0;
1135
1136 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
1137 if ((info->flags & FBINFO_HWACCEL_COPYAREA) &&
1138 !(info->flags & FBINFO_HWACCEL_DISABLED))
1139 p->scrollmode = SCROLL_MOVE;
1140 else /* default to something safe */
1141 p->scrollmode = SCROLL_REDRAW;
1142 #endif
1143
1144 /*
1145 * ++guenther: console.c:vc_allocate() relies on initializing
1146 * vc_{cols,rows}, but we must not set those if we are only
1147 * resizing the console.
1148 */
1149 if (init) {
1150 vc->vc_cols = new_cols;
1151 vc->vc_rows = new_rows;
1152 } else
1153 vc_resize(vc, new_cols, new_rows);
1154
1155 if (logo)
1156 fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
1157
1158 if (ops->rotate_font && ops->rotate_font(info, vc)) {
1159 ops->rotate = FB_ROTATE_UR;
1160 set_blitting_type(vc, info);
1161 }
1162
1163 ops->p = &fb_display[fg_console];
1164 }
1165
fbcon_free_font(struct fbcon_display * p)1166 static void fbcon_free_font(struct fbcon_display *p)
1167 {
1168 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1169 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1170 p->fontdata = NULL;
1171 p->userfont = 0;
1172 }
1173
1174 static void set_vc_hi_font(struct vc_data *vc, bool set);
1175
fbcon_release_all(void)1176 static void fbcon_release_all(void)
1177 {
1178 struct fb_info *info;
1179 int i, j, mapped;
1180
1181 fbcon_for_each_registered_fb(i) {
1182 mapped = 0;
1183 info = fbcon_registered_fb[i];
1184
1185 for (j = first_fb_vc; j <= last_fb_vc; j++) {
1186 if (con2fb_map[j] == i) {
1187 mapped = 1;
1188 con2fb_map[j] = -1;
1189 }
1190 }
1191
1192 if (mapped)
1193 fbcon_release(info);
1194 }
1195 }
1196
fbcon_deinit(struct vc_data * vc)1197 static void fbcon_deinit(struct vc_data *vc)
1198 {
1199 struct fbcon_display *p = &fb_display[vc->vc_num];
1200 struct fb_info *info;
1201 struct fbcon_ops *ops;
1202 int idx;
1203
1204 fbcon_free_font(p);
1205 idx = con2fb_map[vc->vc_num];
1206
1207 if (idx == -1)
1208 goto finished;
1209
1210 info = fbcon_registered_fb[idx];
1211
1212 if (!info)
1213 goto finished;
1214
1215 ops = info->fbcon_par;
1216
1217 if (!ops)
1218 goto finished;
1219
1220 if (con_is_visible(vc))
1221 fbcon_del_cursor_work(info);
1222
1223 ops->initialized = false;
1224 finished:
1225
1226 fbcon_free_font(p);
1227 vc->vc_font.data = NULL;
1228
1229 if (vc->vc_hi_font_mask && vc->vc_screenbuf)
1230 set_vc_hi_font(vc, false);
1231
1232 if (!con_is_bound(&fb_con))
1233 fbcon_release_all();
1234
1235 if (vc->vc_num == logo_shown)
1236 logo_shown = FBCON_LOGO_CANSHOW;
1237
1238 return;
1239 }
1240
1241 /* ====================================================================== */
1242
1243 /* fbcon_XXX routines - interface used by the world
1244 *
1245 * This system is now divided into two levels because of complications
1246 * caused by hardware scrolling. Top level functions:
1247 *
1248 * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
1249 *
1250 * handles y values in range [0, scr_height-1] that correspond to real
1251 * screen positions. y_wrap shift means that first line of bitmap may be
1252 * anywhere on this display. These functions convert lineoffsets to
1253 * bitmap offsets and deal with the wrap-around case by splitting blits.
1254 *
1255 * fbcon_bmove_physical_8() -- These functions fast implementations
1256 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
1257 * fbcon_putc_physical_8() -- (font width != 8) may be added later
1258 *
1259 * WARNING:
1260 *
1261 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
1262 * Implies should only really hardware scroll in rows. Only reason for
1263 * restriction is simplicity & efficiency at the moment.
1264 */
1265
__fbcon_clear(struct vc_data * vc,unsigned int sy,unsigned int sx,unsigned int height,unsigned int width)1266 static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
1267 unsigned int height, unsigned int width)
1268 {
1269 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1270 struct fbcon_ops *ops = info->fbcon_par;
1271 int fg, bg;
1272 struct fbcon_display *p = &fb_display[vc->vc_num];
1273 u_int y_break;
1274
1275 if (!fbcon_is_active(vc, info))
1276 return;
1277
1278 if (!height || !width)
1279 return;
1280
1281 if (sy < vc->vc_top && vc->vc_top == logo_lines) {
1282 vc->vc_top = 0;
1283 /*
1284 * If the font dimensions are not an integral of the display
1285 * dimensions then the ops->clear below won't end up clearing
1286 * the margins. Call clear_margins here in case the logo
1287 * bitmap stretched into the margin area.
1288 */
1289 fbcon_clear_margins(vc, 0);
1290 }
1291
1292 fg = get_color(vc, info, vc->vc_video_erase_char, 1);
1293 bg = get_color(vc, info, vc->vc_video_erase_char, 0);
1294 /* Split blits that cross physical y_wrap boundary */
1295
1296 y_break = p->vrows - p->yscroll;
1297 if (sy < y_break && sy + height - 1 >= y_break) {
1298 u_int b = y_break - sy;
1299 ops->clear(vc, info, real_y(p, sy), sx, b, width, fg, bg);
1300 ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
1301 width, fg, bg);
1302 } else
1303 ops->clear(vc, info, real_y(p, sy), sx, height, width, fg, bg);
1304 }
1305
fbcon_clear(struct vc_data * vc,unsigned int sy,unsigned int sx,unsigned int width)1306 static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
1307 unsigned int width)
1308 {
1309 __fbcon_clear(vc, sy, sx, 1, width);
1310 }
1311
fbcon_putcs(struct vc_data * vc,const u16 * s,unsigned int count,unsigned int ypos,unsigned int xpos)1312 static void fbcon_putcs(struct vc_data *vc, const u16 *s, unsigned int count,
1313 unsigned int ypos, unsigned int xpos)
1314 {
1315 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1316 struct fbcon_display *p = &fb_display[vc->vc_num];
1317 struct fbcon_ops *ops = info->fbcon_par;
1318
1319 if (fbcon_is_active(vc, info))
1320 ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
1321 get_fg_color(vc, info, scr_readw(s)),
1322 get_bg_color(vc, info, scr_readw(s)));
1323 }
1324
fbcon_clear_margins(struct vc_data * vc,int bottom_only)1325 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
1326 {
1327 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1328 struct fbcon_ops *ops = info->fbcon_par;
1329
1330 if (fbcon_is_active(vc, info))
1331 ops->clear_margins(vc, info, margin_color, bottom_only);
1332 }
1333
fbcon_cursor(struct vc_data * vc,bool enable)1334 static void fbcon_cursor(struct vc_data *vc, bool enable)
1335 {
1336 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1337 struct fbcon_ops *ops = info->fbcon_par;
1338 int c = scr_readw((u16 *) vc->vc_pos);
1339
1340 ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1341
1342 if (!fbcon_is_active(vc, info) || vc->vc_deccm != 1)
1343 return;
1344
1345 if (vc->vc_cursor_type & CUR_SW)
1346 fbcon_del_cursor_work(info);
1347 else
1348 fbcon_add_cursor_work(info);
1349
1350 ops->cursor_flash = enable;
1351
1352 if (!ops->cursor)
1353 return;
1354
1355 ops->cursor(vc, info, enable,
1356 get_fg_color(vc, info, c),
1357 get_bg_color(vc, info, c));
1358 }
1359
1360 static int scrollback_phys_max = 0;
1361 static int scrollback_max = 0;
1362 static int scrollback_current = 0;
1363
fbcon_set_disp(struct fb_info * info,struct fb_var_screeninfo * var,int unit)1364 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1365 int unit)
1366 {
1367 struct fbcon_display *p, *t;
1368 struct vc_data **default_mode, *vc;
1369 struct vc_data *svc;
1370 struct fbcon_ops *ops = info->fbcon_par;
1371 int rows, cols;
1372 unsigned long ret = 0;
1373
1374 p = &fb_display[unit];
1375
1376 if (var_to_display(p, var, info))
1377 return;
1378
1379 vc = vc_cons[unit].d;
1380
1381 if (!vc)
1382 return;
1383
1384 default_mode = vc->vc_display_fg;
1385 svc = *default_mode;
1386 t = &fb_display[svc->vc_num];
1387
1388 if (!vc->vc_font.data) {
1389 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1390 vc->vc_font.width = (*default_mode)->vc_font.width;
1391 vc->vc_font.height = (*default_mode)->vc_font.height;
1392 vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
1393 p->userfont = t->userfont;
1394 if (p->userfont)
1395 REFCOUNT(p->fontdata)++;
1396 }
1397
1398 var->activate = FB_ACTIVATE_NOW;
1399 info->var.activate = var->activate;
1400 var->yoffset = info->var.yoffset;
1401 var->xoffset = info->var.xoffset;
1402 fb_set_var(info, var);
1403 ops->var = info->var;
1404 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1405 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1406 if (vc->vc_font.charcount == 256) {
1407 vc->vc_hi_font_mask = 0;
1408 } else {
1409 vc->vc_hi_font_mask = 0x100;
1410 if (vc->vc_can_do_color)
1411 vc->vc_complement_mask <<= 1;
1412 }
1413
1414 if (!*svc->uni_pagedict_loc)
1415 con_set_default_unimap(svc);
1416 if (!*vc->uni_pagedict_loc)
1417 con_copy_unimap(vc, svc);
1418
1419 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1420 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1421 cols /= vc->vc_font.width;
1422 rows /= vc->vc_font.height;
1423 ret = vc_resize(vc, cols, rows);
1424
1425 if (con_is_visible(vc) && !ret)
1426 update_screen(vc);
1427 }
1428
ywrap_up(struct vc_data * vc,int count)1429 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1430 {
1431 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1432 struct fbcon_ops *ops = info->fbcon_par;
1433 struct fbcon_display *p = &fb_display[vc->vc_num];
1434
1435 p->yscroll += count;
1436 if (p->yscroll >= p->vrows) /* Deal with wrap */
1437 p->yscroll -= p->vrows;
1438 ops->var.xoffset = 0;
1439 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1440 ops->var.vmode |= FB_VMODE_YWRAP;
1441 ops->update_start(info);
1442 scrollback_max += count;
1443 if (scrollback_max > scrollback_phys_max)
1444 scrollback_max = scrollback_phys_max;
1445 scrollback_current = 0;
1446 }
1447
ywrap_down(struct vc_data * vc,int count)1448 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1449 {
1450 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1451 struct fbcon_ops *ops = info->fbcon_par;
1452 struct fbcon_display *p = &fb_display[vc->vc_num];
1453
1454 p->yscroll -= count;
1455 if (p->yscroll < 0) /* Deal with wrap */
1456 p->yscroll += p->vrows;
1457 ops->var.xoffset = 0;
1458 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1459 ops->var.vmode |= FB_VMODE_YWRAP;
1460 ops->update_start(info);
1461 scrollback_max -= count;
1462 if (scrollback_max < 0)
1463 scrollback_max = 0;
1464 scrollback_current = 0;
1465 }
1466
ypan_up(struct vc_data * vc,int count)1467 static __inline__ void ypan_up(struct vc_data *vc, int count)
1468 {
1469 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1470 struct fbcon_display *p = &fb_display[vc->vc_num];
1471 struct fbcon_ops *ops = info->fbcon_par;
1472
1473 p->yscroll += count;
1474 if (p->yscroll > p->vrows - vc->vc_rows) {
1475 ops->bmove(vc, info, p->vrows - vc->vc_rows,
1476 0, 0, 0, vc->vc_rows, vc->vc_cols);
1477 p->yscroll -= p->vrows - vc->vc_rows;
1478 }
1479
1480 ops->var.xoffset = 0;
1481 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1482 ops->var.vmode &= ~FB_VMODE_YWRAP;
1483 ops->update_start(info);
1484 fbcon_clear_margins(vc, 1);
1485 scrollback_max += count;
1486 if (scrollback_max > scrollback_phys_max)
1487 scrollback_max = scrollback_phys_max;
1488 scrollback_current = 0;
1489 }
1490
ypan_up_redraw(struct vc_data * vc,int t,int count)1491 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1492 {
1493 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1494 struct fbcon_ops *ops = info->fbcon_par;
1495 struct fbcon_display *p = &fb_display[vc->vc_num];
1496
1497 p->yscroll += count;
1498
1499 if (p->yscroll > p->vrows - vc->vc_rows) {
1500 p->yscroll -= p->vrows - vc->vc_rows;
1501 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1502 }
1503
1504 ops->var.xoffset = 0;
1505 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1506 ops->var.vmode &= ~FB_VMODE_YWRAP;
1507 ops->update_start(info);
1508 fbcon_clear_margins(vc, 1);
1509 scrollback_max += count;
1510 if (scrollback_max > scrollback_phys_max)
1511 scrollback_max = scrollback_phys_max;
1512 scrollback_current = 0;
1513 }
1514
ypan_down(struct vc_data * vc,int count)1515 static __inline__ void ypan_down(struct vc_data *vc, int count)
1516 {
1517 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1518 struct fbcon_display *p = &fb_display[vc->vc_num];
1519 struct fbcon_ops *ops = info->fbcon_par;
1520
1521 p->yscroll -= count;
1522 if (p->yscroll < 0) {
1523 ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1524 0, vc->vc_rows, vc->vc_cols);
1525 p->yscroll += p->vrows - vc->vc_rows;
1526 }
1527
1528 ops->var.xoffset = 0;
1529 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1530 ops->var.vmode &= ~FB_VMODE_YWRAP;
1531 ops->update_start(info);
1532 fbcon_clear_margins(vc, 1);
1533 scrollback_max -= count;
1534 if (scrollback_max < 0)
1535 scrollback_max = 0;
1536 scrollback_current = 0;
1537 }
1538
ypan_down_redraw(struct vc_data * vc,int t,int count)1539 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1540 {
1541 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1542 struct fbcon_ops *ops = info->fbcon_par;
1543 struct fbcon_display *p = &fb_display[vc->vc_num];
1544
1545 p->yscroll -= count;
1546
1547 if (p->yscroll < 0) {
1548 p->yscroll += p->vrows - vc->vc_rows;
1549 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1550 }
1551
1552 ops->var.xoffset = 0;
1553 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1554 ops->var.vmode &= ~FB_VMODE_YWRAP;
1555 ops->update_start(info);
1556 fbcon_clear_margins(vc, 1);
1557 scrollback_max -= count;
1558 if (scrollback_max < 0)
1559 scrollback_max = 0;
1560 scrollback_current = 0;
1561 }
1562
fbcon_redraw_move(struct vc_data * vc,struct fbcon_display * p,int line,int count,int dy)1563 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1564 int line, int count, int dy)
1565 {
1566 unsigned short *s = (unsigned short *)
1567 (vc->vc_origin + vc->vc_size_row * line);
1568
1569 while (count--) {
1570 unsigned short *start = s;
1571 unsigned short *le = advance_row(s, 1);
1572 unsigned short c;
1573 int x = 0;
1574 unsigned short attr = 1;
1575
1576 do {
1577 c = scr_readw(s);
1578 if (attr != (c & 0xff00)) {
1579 attr = c & 0xff00;
1580 if (s > start) {
1581 fbcon_putcs(vc, start, s - start,
1582 dy, x);
1583 x += s - start;
1584 start = s;
1585 }
1586 }
1587 console_conditional_schedule();
1588 s++;
1589 } while (s < le);
1590 if (s > start)
1591 fbcon_putcs(vc, start, s - start, dy, x);
1592 console_conditional_schedule();
1593 dy++;
1594 }
1595 }
1596
fbcon_redraw_blit(struct vc_data * vc,struct fb_info * info,struct fbcon_display * p,int line,int count,int ycount)1597 static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1598 struct fbcon_display *p, int line, int count, int ycount)
1599 {
1600 int offset = ycount * vc->vc_cols;
1601 unsigned short *d = (unsigned short *)
1602 (vc->vc_origin + vc->vc_size_row * line);
1603 unsigned short *s = d + offset;
1604 struct fbcon_ops *ops = info->fbcon_par;
1605
1606 while (count--) {
1607 unsigned short *start = s;
1608 unsigned short *le = advance_row(s, 1);
1609 unsigned short c;
1610 int x = 0;
1611
1612 do {
1613 c = scr_readw(s);
1614
1615 if (c == scr_readw(d)) {
1616 if (s > start) {
1617 ops->bmove(vc, info, line + ycount, x,
1618 line, x, 1, s-start);
1619 x += s - start + 1;
1620 start = s + 1;
1621 } else {
1622 x++;
1623 start++;
1624 }
1625 }
1626
1627 scr_writew(c, d);
1628 console_conditional_schedule();
1629 s++;
1630 d++;
1631 } while (s < le);
1632 if (s > start)
1633 ops->bmove(vc, info, line + ycount, x, line, x, 1,
1634 s-start);
1635 console_conditional_schedule();
1636 if (ycount > 0)
1637 line++;
1638 else {
1639 line--;
1640 /* NOTE: We subtract two lines from these pointers */
1641 s -= vc->vc_size_row;
1642 d -= vc->vc_size_row;
1643 }
1644 }
1645 }
1646
fbcon_redraw(struct vc_data * vc,int line,int count,int offset)1647 static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset)
1648 {
1649 unsigned short *d = (unsigned short *)
1650 (vc->vc_origin + vc->vc_size_row * line);
1651 unsigned short *s = d + offset;
1652
1653 while (count--) {
1654 unsigned short *start = s;
1655 unsigned short *le = advance_row(s, 1);
1656 unsigned short c;
1657 int x = 0;
1658 unsigned short attr = 1;
1659
1660 do {
1661 c = scr_readw(s);
1662 if (attr != (c & 0xff00)) {
1663 attr = c & 0xff00;
1664 if (s > start) {
1665 fbcon_putcs(vc, start, s - start,
1666 line, x);
1667 x += s - start;
1668 start = s;
1669 }
1670 }
1671 if (c == scr_readw(d)) {
1672 if (s > start) {
1673 fbcon_putcs(vc, start, s - start,
1674 line, x);
1675 x += s - start + 1;
1676 start = s + 1;
1677 } else {
1678 x++;
1679 start++;
1680 }
1681 }
1682 scr_writew(c, d);
1683 console_conditional_schedule();
1684 s++;
1685 d++;
1686 } while (s < le);
1687 if (s > start)
1688 fbcon_putcs(vc, start, s - start, line, x);
1689 console_conditional_schedule();
1690 if (offset > 0)
1691 line++;
1692 else {
1693 line--;
1694 /* NOTE: We subtract two lines from these pointers */
1695 s -= vc->vc_size_row;
1696 d -= vc->vc_size_row;
1697 }
1698 }
1699 }
1700
fbcon_bmove_rec(struct vc_data * vc,struct fbcon_display * p,int sy,int sx,int dy,int dx,int height,int width,u_int y_break)1701 static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
1702 int dy, int dx, int height, int width, u_int y_break)
1703 {
1704 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1705 struct fbcon_ops *ops = info->fbcon_par;
1706 u_int b;
1707
1708 if (sy < y_break && sy + height > y_break) {
1709 b = y_break - sy;
1710 if (dy < sy) { /* Avoid trashing self */
1711 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1712 y_break);
1713 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1714 height - b, width, y_break);
1715 } else {
1716 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1717 height - b, width, y_break);
1718 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1719 y_break);
1720 }
1721 return;
1722 }
1723
1724 if (dy < y_break && dy + height > y_break) {
1725 b = y_break - dy;
1726 if (dy < sy) { /* Avoid trashing self */
1727 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1728 y_break);
1729 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1730 height - b, width, y_break);
1731 } else {
1732 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1733 height - b, width, y_break);
1734 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1735 y_break);
1736 }
1737 return;
1738 }
1739 ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1740 height, width);
1741 }
1742
fbcon_bmove(struct vc_data * vc,int sy,int sx,int dy,int dx,int height,int width)1743 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1744 int height, int width)
1745 {
1746 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1747 struct fbcon_display *p = &fb_display[vc->vc_num];
1748
1749 if (!fbcon_is_active(vc, info))
1750 return;
1751
1752 if (!width || !height)
1753 return;
1754
1755 /* Split blits that cross physical y_wrap case.
1756 * Pathological case involves 4 blits, better to use recursive
1757 * code rather than unrolled case
1758 *
1759 * Recursive invocations don't need to erase the cursor over and
1760 * over again, so we use fbcon_bmove_rec()
1761 */
1762 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1763 p->vrows - p->yscroll);
1764 }
1765
fbcon_scroll(struct vc_data * vc,unsigned int t,unsigned int b,enum con_scroll dir,unsigned int count)1766 static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
1767 enum con_scroll dir, unsigned int count)
1768 {
1769 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1770 struct fbcon_display *p = &fb_display[vc->vc_num];
1771 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1772
1773 if (!fbcon_is_active(vc, info))
1774 return true;
1775
1776 fbcon_cursor(vc, false);
1777
1778 /*
1779 * ++Geert: Only use ywrap/ypan if the console is in text mode
1780 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1781 * whole screen (prevents flicker).
1782 */
1783
1784 switch (dir) {
1785 case SM_UP:
1786 if (count > vc->vc_rows) /* Maximum realistic size */
1787 count = vc->vc_rows;
1788 switch (fb_scrollmode(p)) {
1789 case SCROLL_MOVE:
1790 fbcon_redraw_blit(vc, info, p, t, b - t - count,
1791 count);
1792 __fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1793 scr_memsetw((unsigned short *) (vc->vc_origin +
1794 vc->vc_size_row *
1795 (b - count)),
1796 vc->vc_video_erase_char,
1797 vc->vc_size_row * count);
1798 return true;
1799
1800 case SCROLL_WRAP_MOVE:
1801 if (b - t - count > 3 * vc->vc_rows >> 2) {
1802 if (t > 0)
1803 fbcon_bmove(vc, 0, 0, count, 0, t,
1804 vc->vc_cols);
1805 ywrap_up(vc, count);
1806 if (vc->vc_rows - b > 0)
1807 fbcon_bmove(vc, b - count, 0, b, 0,
1808 vc->vc_rows - b,
1809 vc->vc_cols);
1810 } else if (info->flags & FBINFO_READS_FAST)
1811 fbcon_bmove(vc, t + count, 0, t, 0,
1812 b - t - count, vc->vc_cols);
1813 else
1814 goto redraw_up;
1815 __fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1816 break;
1817
1818 case SCROLL_PAN_REDRAW:
1819 if ((p->yscroll + count <=
1820 2 * (p->vrows - vc->vc_rows))
1821 && ((!scroll_partial && (b - t == vc->vc_rows))
1822 || (scroll_partial
1823 && (b - t - count >
1824 3 * vc->vc_rows >> 2)))) {
1825 if (t > 0)
1826 fbcon_redraw_move(vc, p, 0, t, count);
1827 ypan_up_redraw(vc, t, count);
1828 if (vc->vc_rows - b > 0)
1829 fbcon_redraw_move(vc, p, b,
1830 vc->vc_rows - b, b);
1831 } else
1832 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1833 __fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1834 break;
1835
1836 case SCROLL_PAN_MOVE:
1837 if ((p->yscroll + count <=
1838 2 * (p->vrows - vc->vc_rows))
1839 && ((!scroll_partial && (b - t == vc->vc_rows))
1840 || (scroll_partial
1841 && (b - t - count >
1842 3 * vc->vc_rows >> 2)))) {
1843 if (t > 0)
1844 fbcon_bmove(vc, 0, 0, count, 0, t,
1845 vc->vc_cols);
1846 ypan_up(vc, count);
1847 if (vc->vc_rows - b > 0)
1848 fbcon_bmove(vc, b - count, 0, b, 0,
1849 vc->vc_rows - b,
1850 vc->vc_cols);
1851 } else if (info->flags & FBINFO_READS_FAST)
1852 fbcon_bmove(vc, t + count, 0, t, 0,
1853 b - t - count, vc->vc_cols);
1854 else
1855 goto redraw_up;
1856 __fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1857 break;
1858
1859 case SCROLL_REDRAW:
1860 redraw_up:
1861 fbcon_redraw(vc, t, b - t - count,
1862 count * vc->vc_cols);
1863 __fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1864 scr_memsetw((unsigned short *) (vc->vc_origin +
1865 vc->vc_size_row *
1866 (b - count)),
1867 vc->vc_video_erase_char,
1868 vc->vc_size_row * count);
1869 return true;
1870 }
1871 break;
1872
1873 case SM_DOWN:
1874 if (count > vc->vc_rows) /* Maximum realistic size */
1875 count = vc->vc_rows;
1876 switch (fb_scrollmode(p)) {
1877 case SCROLL_MOVE:
1878 fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
1879 -count);
1880 __fbcon_clear(vc, t, 0, count, vc->vc_cols);
1881 scr_memsetw((unsigned short *) (vc->vc_origin +
1882 vc->vc_size_row *
1883 t),
1884 vc->vc_video_erase_char,
1885 vc->vc_size_row * count);
1886 return true;
1887
1888 case SCROLL_WRAP_MOVE:
1889 if (b - t - count > 3 * vc->vc_rows >> 2) {
1890 if (vc->vc_rows - b > 0)
1891 fbcon_bmove(vc, b, 0, b - count, 0,
1892 vc->vc_rows - b,
1893 vc->vc_cols);
1894 ywrap_down(vc, count);
1895 if (t > 0)
1896 fbcon_bmove(vc, count, 0, 0, 0, t,
1897 vc->vc_cols);
1898 } else if (info->flags & FBINFO_READS_FAST)
1899 fbcon_bmove(vc, t, 0, t + count, 0,
1900 b - t - count, vc->vc_cols);
1901 else
1902 goto redraw_down;
1903 __fbcon_clear(vc, t, 0, count, vc->vc_cols);
1904 break;
1905
1906 case SCROLL_PAN_MOVE:
1907 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1908 && ((!scroll_partial && (b - t == vc->vc_rows))
1909 || (scroll_partial
1910 && (b - t - count >
1911 3 * vc->vc_rows >> 2)))) {
1912 if (vc->vc_rows - b > 0)
1913 fbcon_bmove(vc, b, 0, b - count, 0,
1914 vc->vc_rows - b,
1915 vc->vc_cols);
1916 ypan_down(vc, count);
1917 if (t > 0)
1918 fbcon_bmove(vc, count, 0, 0, 0, t,
1919 vc->vc_cols);
1920 } else if (info->flags & FBINFO_READS_FAST)
1921 fbcon_bmove(vc, t, 0, t + count, 0,
1922 b - t - count, vc->vc_cols);
1923 else
1924 goto redraw_down;
1925 __fbcon_clear(vc, t, 0, count, vc->vc_cols);
1926 break;
1927
1928 case SCROLL_PAN_REDRAW:
1929 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1930 && ((!scroll_partial && (b - t == vc->vc_rows))
1931 || (scroll_partial
1932 && (b - t - count >
1933 3 * vc->vc_rows >> 2)))) {
1934 if (vc->vc_rows - b > 0)
1935 fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1936 b - count);
1937 ypan_down_redraw(vc, t, count);
1938 if (t > 0)
1939 fbcon_redraw_move(vc, p, count, t, 0);
1940 } else
1941 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1942 __fbcon_clear(vc, t, 0, count, vc->vc_cols);
1943 break;
1944
1945 case SCROLL_REDRAW:
1946 redraw_down:
1947 fbcon_redraw(vc, b - 1, b - t - count,
1948 -count * vc->vc_cols);
1949 __fbcon_clear(vc, t, 0, count, vc->vc_cols);
1950 scr_memsetw((unsigned short *) (vc->vc_origin +
1951 vc->vc_size_row *
1952 t),
1953 vc->vc_video_erase_char,
1954 vc->vc_size_row * count);
1955 return true;
1956 }
1957 }
1958 return false;
1959 }
1960
1961
updatescrollmode_accel(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)1962 static void updatescrollmode_accel(struct fbcon_display *p,
1963 struct fb_info *info,
1964 struct vc_data *vc)
1965 {
1966 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
1967 struct fbcon_ops *ops = info->fbcon_par;
1968 int cap = info->flags;
1969 u16 t = 0;
1970 int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
1971 info->fix.xpanstep);
1972 int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
1973 int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1974 int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1975 info->var.xres_virtual);
1976 int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
1977 divides(ypan, vc->vc_font.height) && vyres > yres;
1978 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
1979 divides(ywrap, vc->vc_font.height) &&
1980 divides(vc->vc_font.height, vyres) &&
1981 divides(vc->vc_font.height, yres);
1982 int reading_fast = cap & FBINFO_READS_FAST;
1983 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
1984 !(cap & FBINFO_HWACCEL_DISABLED);
1985 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
1986 !(cap & FBINFO_HWACCEL_DISABLED);
1987
1988 if (good_wrap || good_pan) {
1989 if (reading_fast || fast_copyarea)
1990 p->scrollmode = good_wrap ?
1991 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1992 else
1993 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1994 SCROLL_PAN_REDRAW;
1995 } else {
1996 if (reading_fast || (fast_copyarea && !fast_imageblit))
1997 p->scrollmode = SCROLL_MOVE;
1998 else
1999 p->scrollmode = SCROLL_REDRAW;
2000 }
2001 #endif
2002 }
2003
updatescrollmode(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)2004 static void updatescrollmode(struct fbcon_display *p,
2005 struct fb_info *info,
2006 struct vc_data *vc)
2007 {
2008 struct fbcon_ops *ops = info->fbcon_par;
2009 int fh = vc->vc_font.height;
2010 int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2011 int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
2012 info->var.xres_virtual);
2013
2014 p->vrows = vyres/fh;
2015 if (yres > (fh * (vc->vc_rows + 1)))
2016 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
2017 if ((yres % fh) && (vyres % fh < yres % fh))
2018 p->vrows--;
2019
2020 /* update scrollmode in case hardware acceleration is used */
2021 updatescrollmode_accel(p, info, vc);
2022 }
2023
2024 #define PITCH(w) (((w) + 7) >> 3)
2025 #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
2026
fbcon_resize(struct vc_data * vc,unsigned int width,unsigned int height,bool from_user)2027 static int fbcon_resize(struct vc_data *vc, unsigned int width,
2028 unsigned int height, bool from_user)
2029 {
2030 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2031 struct fbcon_ops *ops = info->fbcon_par;
2032 struct fbcon_display *p = &fb_display[vc->vc_num];
2033 struct fb_var_screeninfo var = info->var;
2034 int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
2035
2036 if (p->userfont && FNTSIZE(vc->vc_font.data)) {
2037 int size;
2038 int pitch = PITCH(vc->vc_font.width);
2039
2040 /*
2041 * If user font, ensure that a possible change to user font
2042 * height or width will not allow a font data out-of-bounds access.
2043 * NOTE: must use original charcount in calculation as font
2044 * charcount can change and cannot be used to determine the
2045 * font data allocated size.
2046 */
2047 if (pitch <= 0)
2048 return -EINVAL;
2049 size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount);
2050 if (size > FNTSIZE(vc->vc_font.data))
2051 return -EINVAL;
2052 }
2053
2054 virt_w = FBCON_SWAP(ops->rotate, width, height);
2055 virt_h = FBCON_SWAP(ops->rotate, height, width);
2056 virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
2057 vc->vc_font.height);
2058 virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2059 vc->vc_font.width);
2060 var.xres = virt_w * virt_fw;
2061 var.yres = virt_h * virt_fh;
2062 x_diff = info->var.xres - var.xres;
2063 y_diff = info->var.yres - var.yres;
2064 if (x_diff < 0 || x_diff > virt_fw ||
2065 y_diff < 0 || y_diff > virt_fh) {
2066 const struct fb_videomode *mode;
2067
2068 pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
2069 mode = fb_find_best_mode(&var, &info->modelist);
2070 if (mode == NULL)
2071 return -EINVAL;
2072 display_to_var(&var, p);
2073 fb_videomode_to_var(&var, mode);
2074
2075 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
2076 return -EINVAL;
2077
2078 pr_debug("resize now %ix%i\n", var.xres, var.yres);
2079 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
2080 var.activate = FB_ACTIVATE_NOW |
2081 FB_ACTIVATE_FORCE;
2082 fb_set_var(info, &var);
2083 }
2084 var_to_display(p, &info->var, info);
2085 ops->var = info->var;
2086 }
2087 updatescrollmode(p, info, vc);
2088 return 0;
2089 }
2090
fbcon_switch(struct vc_data * vc)2091 static bool fbcon_switch(struct vc_data *vc)
2092 {
2093 struct fb_info *info, *old_info = NULL;
2094 struct fbcon_ops *ops;
2095 struct fbcon_display *p = &fb_display[vc->vc_num];
2096 struct fb_var_screeninfo var;
2097 int i, ret, prev_console;
2098
2099 info = fbcon_info_from_console(vc->vc_num);
2100 ops = info->fbcon_par;
2101
2102 if (logo_shown >= 0) {
2103 struct vc_data *conp2 = vc_cons[logo_shown].d;
2104
2105 if (conp2->vc_top == logo_lines
2106 && conp2->vc_bottom == conp2->vc_rows)
2107 conp2->vc_top = 0;
2108 logo_shown = FBCON_LOGO_CANSHOW;
2109 }
2110
2111 prev_console = ops->currcon;
2112 if (prev_console != -1)
2113 old_info = fbcon_info_from_console(prev_console);
2114 /*
2115 * FIXME: If we have multiple fbdev's loaded, we need to
2116 * update all info->currcon. Perhaps, we can place this
2117 * in a centralized structure, but this might break some
2118 * drivers.
2119 *
2120 * info->currcon = vc->vc_num;
2121 */
2122 fbcon_for_each_registered_fb(i) {
2123 if (fbcon_registered_fb[i]->fbcon_par) {
2124 struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;
2125
2126 o->currcon = vc->vc_num;
2127 }
2128 }
2129 memset(&var, 0, sizeof(struct fb_var_screeninfo));
2130 display_to_var(&var, p);
2131 var.activate = FB_ACTIVATE_NOW;
2132
2133 /*
2134 * make sure we don't unnecessarily trip the memcmp()
2135 * in fb_set_var()
2136 */
2137 info->var.activate = var.activate;
2138 var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
2139 fb_set_var(info, &var);
2140 ops->var = info->var;
2141
2142 if (old_info != NULL && (old_info != info ||
2143 info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
2144 if (info->fbops->fb_set_par) {
2145 ret = info->fbops->fb_set_par(info);
2146
2147 if (ret)
2148 printk(KERN_ERR "fbcon_switch: detected "
2149 "unhandled fb_set_par error, "
2150 "error code %d\n", ret);
2151 }
2152
2153 if (old_info != info)
2154 fbcon_del_cursor_work(old_info);
2155 }
2156
2157 if (!fbcon_is_active(vc, info) ||
2158 ops->blank_state != FB_BLANK_UNBLANK)
2159 fbcon_del_cursor_work(info);
2160 else
2161 fbcon_add_cursor_work(info);
2162
2163 set_blitting_type(vc, info);
2164 ops->cursor_reset = 1;
2165
2166 if (ops->rotate_font && ops->rotate_font(info, vc)) {
2167 ops->rotate = FB_ROTATE_UR;
2168 set_blitting_type(vc, info);
2169 }
2170
2171 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
2172 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2173
2174 if (vc->vc_font.charcount > 256)
2175 vc->vc_complement_mask <<= 1;
2176
2177 updatescrollmode(p, info, vc);
2178
2179 switch (fb_scrollmode(p)) {
2180 case SCROLL_WRAP_MOVE:
2181 scrollback_phys_max = p->vrows - vc->vc_rows;
2182 break;
2183 case SCROLL_PAN_MOVE:
2184 case SCROLL_PAN_REDRAW:
2185 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
2186 if (scrollback_phys_max < 0)
2187 scrollback_phys_max = 0;
2188 break;
2189 default:
2190 scrollback_phys_max = 0;
2191 break;
2192 }
2193
2194 scrollback_max = 0;
2195 scrollback_current = 0;
2196
2197 if (fbcon_is_active(vc, info)) {
2198 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2199 ops->update_start(info);
2200 }
2201
2202 fbcon_set_palette(vc, color_table);
2203 fbcon_clear_margins(vc, 0);
2204
2205 if (logo_shown == FBCON_LOGO_DRAW) {
2206
2207 logo_shown = fg_console;
2208 fb_show_logo(info, ops->rotate);
2209 update_region(vc,
2210 vc->vc_origin + vc->vc_size_row * vc->vc_top,
2211 vc->vc_size_row * (vc->vc_bottom -
2212 vc->vc_top) / 2);
2213 return false;
2214 }
2215 return true;
2216 }
2217
fbcon_generic_blank(struct vc_data * vc,struct fb_info * info,int blank)2218 static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
2219 int blank)
2220 {
2221 if (blank) {
2222 unsigned short charmask = vc->vc_hi_font_mask ?
2223 0x1ff : 0xff;
2224 unsigned short oldc;
2225
2226 oldc = vc->vc_video_erase_char;
2227 vc->vc_video_erase_char &= charmask;
2228 __fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
2229 vc->vc_video_erase_char = oldc;
2230 }
2231 }
2232
fbcon_blank(struct vc_data * vc,enum vesa_blank_mode blank,bool mode_switch)2233 static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
2234 bool mode_switch)
2235 {
2236 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2237 struct fbcon_ops *ops = info->fbcon_par;
2238
2239 if (mode_switch) {
2240 struct fb_var_screeninfo var = info->var;
2241
2242 ops->graphics = 1;
2243
2244 if (!blank) {
2245 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2246 FB_ACTIVATE_KD_TEXT;
2247 fb_set_var(info, &var);
2248 ops->graphics = 0;
2249 ops->var = info->var;
2250 }
2251 }
2252
2253 if (fbcon_is_active(vc, info)) {
2254 if (ops->blank_state != blank) {
2255 ops->blank_state = blank;
2256 fbcon_cursor(vc, !blank);
2257 ops->cursor_flash = (!blank);
2258
2259 if (fb_blank(info, blank))
2260 fbcon_generic_blank(vc, info, blank);
2261 }
2262
2263 if (!blank)
2264 update_screen(vc);
2265 }
2266
2267 if (mode_switch || !fbcon_is_active(vc, info) ||
2268 ops->blank_state != FB_BLANK_UNBLANK)
2269 fbcon_del_cursor_work(info);
2270 else
2271 fbcon_add_cursor_work(info);
2272
2273 return false;
2274 }
2275
fbcon_debug_enter(struct vc_data * vc)2276 static void fbcon_debug_enter(struct vc_data *vc)
2277 {
2278 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2279 struct fbcon_ops *ops = info->fbcon_par;
2280
2281 ops->save_graphics = ops->graphics;
2282 ops->graphics = 0;
2283 if (info->fbops->fb_debug_enter)
2284 info->fbops->fb_debug_enter(info);
2285 fbcon_set_palette(vc, color_table);
2286 }
2287
fbcon_debug_leave(struct vc_data * vc)2288 static void fbcon_debug_leave(struct vc_data *vc)
2289 {
2290 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2291 struct fbcon_ops *ops = info->fbcon_par;
2292
2293 ops->graphics = ops->save_graphics;
2294 if (info->fbops->fb_debug_leave)
2295 info->fbops->fb_debug_leave(info);
2296 }
2297
fbcon_get_font(struct vc_data * vc,struct console_font * font,unsigned int vpitch)2298 static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch)
2299 {
2300 u8 *fontdata = vc->vc_font.data;
2301 u8 *data = font->data;
2302 int i, j;
2303
2304 font->width = vc->vc_font.width;
2305 font->height = vc->vc_font.height;
2306 if (font->height > vpitch)
2307 return -ENOSPC;
2308 font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2309 if (!font->data)
2310 return 0;
2311
2312 if (font->width <= 8) {
2313 j = vc->vc_font.height;
2314 if (font->charcount * j > FNTSIZE(fontdata))
2315 return -EINVAL;
2316
2317 for (i = 0; i < font->charcount; i++) {
2318 memcpy(data, fontdata, j);
2319 memset(data + j, 0, vpitch - j);
2320 data += vpitch;
2321 fontdata += j;
2322 }
2323 } else if (font->width <= 16) {
2324 j = vc->vc_font.height * 2;
2325 if (font->charcount * j > FNTSIZE(fontdata))
2326 return -EINVAL;
2327
2328 for (i = 0; i < font->charcount; i++) {
2329 memcpy(data, fontdata, j);
2330 memset(data + j, 0, 2*vpitch - j);
2331 data += 2*vpitch;
2332 fontdata += j;
2333 }
2334 } else if (font->width <= 24) {
2335 if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
2336 return -EINVAL;
2337
2338 for (i = 0; i < font->charcount; i++) {
2339 for (j = 0; j < vc->vc_font.height; j++) {
2340 *data++ = fontdata[0];
2341 *data++ = fontdata[1];
2342 *data++ = fontdata[2];
2343 fontdata += sizeof(u32);
2344 }
2345 memset(data, 0, 3 * (vpitch - j));
2346 data += 3 * (vpitch - j);
2347 }
2348 } else {
2349 j = vc->vc_font.height * 4;
2350 if (font->charcount * j > FNTSIZE(fontdata))
2351 return -EINVAL;
2352
2353 for (i = 0; i < font->charcount; i++) {
2354 memcpy(data, fontdata, j);
2355 memset(data + j, 0, 4 * vpitch - j);
2356 data += 4 * vpitch;
2357 fontdata += j;
2358 }
2359 }
2360 return 0;
2361 }
2362
2363 /* set/clear vc_hi_font_mask and update vc attrs accordingly */
set_vc_hi_font(struct vc_data * vc,bool set)2364 static void set_vc_hi_font(struct vc_data *vc, bool set)
2365 {
2366 if (!set) {
2367 vc->vc_hi_font_mask = 0;
2368 if (vc->vc_can_do_color) {
2369 vc->vc_complement_mask >>= 1;
2370 vc->vc_s_complement_mask >>= 1;
2371 }
2372
2373 /* ++Edmund: reorder the attribute bits */
2374 if (vc->vc_can_do_color) {
2375 unsigned short *cp =
2376 (unsigned short *) vc->vc_origin;
2377 int count = vc->vc_screenbuf_size / 2;
2378 unsigned short c;
2379 for (; count > 0; count--, cp++) {
2380 c = scr_readw(cp);
2381 scr_writew(((c & 0xfe00) >> 1) |
2382 (c & 0xff), cp);
2383 }
2384 c = vc->vc_video_erase_char;
2385 vc->vc_video_erase_char =
2386 ((c & 0xfe00) >> 1) | (c & 0xff);
2387 vc->vc_attr >>= 1;
2388 }
2389 } else {
2390 vc->vc_hi_font_mask = 0x100;
2391 if (vc->vc_can_do_color) {
2392 vc->vc_complement_mask <<= 1;
2393 vc->vc_s_complement_mask <<= 1;
2394 }
2395
2396 /* ++Edmund: reorder the attribute bits */
2397 {
2398 unsigned short *cp =
2399 (unsigned short *) vc->vc_origin;
2400 int count = vc->vc_screenbuf_size / 2;
2401 unsigned short c;
2402 for (; count > 0; count--, cp++) {
2403 unsigned short newc;
2404 c = scr_readw(cp);
2405 if (vc->vc_can_do_color)
2406 newc =
2407 ((c & 0xff00) << 1) | (c &
2408 0xff);
2409 else
2410 newc = c & ~0x100;
2411 scr_writew(newc, cp);
2412 }
2413 c = vc->vc_video_erase_char;
2414 if (vc->vc_can_do_color) {
2415 vc->vc_video_erase_char =
2416 ((c & 0xff00) << 1) | (c & 0xff);
2417 vc->vc_attr <<= 1;
2418 } else
2419 vc->vc_video_erase_char = c & ~0x100;
2420 }
2421 }
2422 }
2423
fbcon_do_set_font(struct vc_data * vc,int w,int h,int charcount,const u8 * data,int userfont)2424 static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
2425 const u8 * data, int userfont)
2426 {
2427 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2428 struct fbcon_ops *ops = info->fbcon_par;
2429 struct fbcon_display *p = &fb_display[vc->vc_num];
2430 int resize, ret, old_userfont, old_width, old_height, old_charcount;
2431 u8 *old_data = vc->vc_font.data;
2432
2433 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2434 vc->vc_font.data = (void *)(p->fontdata = data);
2435 old_userfont = p->userfont;
2436 if ((p->userfont = userfont))
2437 REFCOUNT(data)++;
2438
2439 old_width = vc->vc_font.width;
2440 old_height = vc->vc_font.height;
2441 old_charcount = vc->vc_font.charcount;
2442
2443 vc->vc_font.width = w;
2444 vc->vc_font.height = h;
2445 vc->vc_font.charcount = charcount;
2446 if (vc->vc_hi_font_mask && charcount == 256)
2447 set_vc_hi_font(vc, false);
2448 else if (!vc->vc_hi_font_mask && charcount == 512)
2449 set_vc_hi_font(vc, true);
2450
2451 if (resize) {
2452 int cols, rows;
2453
2454 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2455 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2456 cols /= w;
2457 rows /= h;
2458 ret = vc_resize(vc, cols, rows);
2459 if (ret)
2460 goto err_out;
2461 } else if (con_is_visible(vc)
2462 && vc->vc_mode == KD_TEXT) {
2463 fbcon_clear_margins(vc, 0);
2464 update_screen(vc);
2465 }
2466
2467 if (old_userfont && (--REFCOUNT(old_data) == 0))
2468 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2469 return 0;
2470
2471 err_out:
2472 p->fontdata = old_data;
2473 vc->vc_font.data = old_data;
2474
2475 if (userfont) {
2476 p->userfont = old_userfont;
2477 if (--REFCOUNT(data) == 0)
2478 kfree(data - FONT_EXTRA_WORDS * sizeof(int));
2479 }
2480
2481 vc->vc_font.width = old_width;
2482 vc->vc_font.height = old_height;
2483 vc->vc_font.charcount = old_charcount;
2484
2485 return ret;
2486 }
2487
2488 /*
2489 * User asked to set font; we are guaranteed that charcount does not exceed 512
2490 * but lets not assume that, since charcount of 512 is small for unicode support.
2491 */
2492
fbcon_set_font(struct vc_data * vc,const struct console_font * font,unsigned int vpitch,unsigned int flags)2493 static int fbcon_set_font(struct vc_data *vc, const struct console_font *font,
2494 unsigned int vpitch, unsigned int flags)
2495 {
2496 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2497 unsigned charcount = font->charcount;
2498 int w = font->width;
2499 int h = font->height;
2500 int size, alloc_size;
2501 int i, csum;
2502 u8 *new_data, *data = font->data;
2503 int pitch = PITCH(font->width);
2504
2505 /* Is there a reason why fbconsole couldn't handle any charcount >256?
2506 * If not this check should be changed to charcount < 256 */
2507 if (charcount != 256 && charcount != 512)
2508 return -EINVAL;
2509
2510 /* font bigger than screen resolution ? */
2511 if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) ||
2512 h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
2513 return -EINVAL;
2514
2515 if (font->width > FB_MAX_BLIT_WIDTH || font->height > FB_MAX_BLIT_HEIGHT)
2516 return -EINVAL;
2517
2518 /* Make sure drawing engine can handle the font */
2519 if (!test_bit(font->width - 1, info->pixmap.blit_x) ||
2520 !test_bit(font->height - 1, info->pixmap.blit_y))
2521 return -EINVAL;
2522
2523 /* Make sure driver can handle the font length */
2524 if (fbcon_invalid_charcount(info, charcount))
2525 return -EINVAL;
2526
2527 /* Check for integer overflow in font size calculation */
2528 if (check_mul_overflow(h, pitch, &size) ||
2529 check_mul_overflow(size, charcount, &size))
2530 return -EINVAL;
2531
2532 /* Check for overflow in allocation size calculation */
2533 if (check_add_overflow(FONT_EXTRA_WORDS * sizeof(int), size, &alloc_size))
2534 return -EINVAL;
2535
2536 new_data = kmalloc(alloc_size, GFP_USER);
2537
2538 if (!new_data)
2539 return -ENOMEM;
2540
2541 memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int));
2542
2543 new_data += FONT_EXTRA_WORDS * sizeof(int);
2544 FNTSIZE(new_data) = size;
2545 REFCOUNT(new_data) = 0; /* usage counter */
2546 for (i=0; i< charcount; i++) {
2547 memcpy(new_data + i*h*pitch, data + i*vpitch*pitch, h*pitch);
2548 }
2549
2550 /* Since linux has a nice crc32 function use it for counting font
2551 * checksums. */
2552 csum = crc32(0, new_data, size);
2553
2554 FNTSUM(new_data) = csum;
2555 /* Check if the same font is on some other console already */
2556 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2557 struct vc_data *tmp = vc_cons[i].d;
2558
2559 if (fb_display[i].userfont &&
2560 fb_display[i].fontdata &&
2561 FNTSUM(fb_display[i].fontdata) == csum &&
2562 FNTSIZE(fb_display[i].fontdata) == size &&
2563 tmp->vc_font.width == w &&
2564 !memcmp(fb_display[i].fontdata, new_data, size)) {
2565 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2566 new_data = (u8 *)fb_display[i].fontdata;
2567 break;
2568 }
2569 }
2570 return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
2571 }
2572
fbcon_set_def_font(struct vc_data * vc,struct console_font * font,const char * name)2573 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font,
2574 const char *name)
2575 {
2576 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2577 const struct font_desc *f;
2578
2579 if (!name)
2580 f = get_default_font(info->var.xres, info->var.yres,
2581 info->pixmap.blit_x, info->pixmap.blit_y);
2582 else if (!(f = find_font(name)))
2583 return -ENOENT;
2584
2585 font->width = f->width;
2586 font->height = f->height;
2587 return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
2588 }
2589
2590 static u16 palette_red[16];
2591 static u16 palette_green[16];
2592 static u16 palette_blue[16];
2593
2594 static struct fb_cmap palette_cmap = {
2595 0, 16, palette_red, palette_green, palette_blue, NULL
2596 };
2597
fbcon_set_palette(struct vc_data * vc,const unsigned char * table)2598 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
2599 {
2600 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2601 int i, j, k, depth;
2602 u8 val;
2603
2604 if (!fbcon_is_active(vc, info))
2605 return;
2606
2607 if (!con_is_visible(vc))
2608 return;
2609
2610 depth = fb_get_color_depth(&info->var, &info->fix);
2611 if (depth > 3) {
2612 for (i = j = 0; i < 16; i++) {
2613 k = table[i];
2614 val = vc->vc_palette[j++];
2615 palette_red[k] = (val << 8) | val;
2616 val = vc->vc_palette[j++];
2617 palette_green[k] = (val << 8) | val;
2618 val = vc->vc_palette[j++];
2619 palette_blue[k] = (val << 8) | val;
2620 }
2621 palette_cmap.len = 16;
2622 palette_cmap.start = 0;
2623 /*
2624 * If framebuffer is capable of less than 16 colors,
2625 * use default palette of fbcon.
2626 */
2627 } else
2628 fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
2629
2630 fb_set_cmap(&palette_cmap, info);
2631 }
2632
2633 /* As we might be inside of softback, we may work with non-contiguous buffer,
2634 that's why we have to use a separate routine. */
fbcon_invert_region(struct vc_data * vc,u16 * p,int cnt)2635 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2636 {
2637 while (cnt--) {
2638 u16 a = scr_readw(p);
2639 if (!vc->vc_can_do_color)
2640 a ^= 0x0800;
2641 else if (vc->vc_hi_font_mask == 0x100)
2642 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2643 (((a) & 0x0e00) << 4);
2644 else
2645 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2646 (((a) & 0x0700) << 4);
2647 scr_writew(a, p++);
2648 }
2649 }
2650
fbcon_suspended(struct fb_info * info)2651 void fbcon_suspended(struct fb_info *info)
2652 {
2653 struct vc_data *vc = NULL;
2654 struct fbcon_ops *ops = info->fbcon_par;
2655
2656 if (!ops || ops->currcon < 0)
2657 return;
2658 vc = vc_cons[ops->currcon].d;
2659
2660 /* Clear cursor, restore saved data */
2661 fbcon_cursor(vc, false);
2662 }
2663
fbcon_resumed(struct fb_info * info)2664 void fbcon_resumed(struct fb_info *info)
2665 {
2666 struct vc_data *vc;
2667 struct fbcon_ops *ops = info->fbcon_par;
2668
2669 if (!ops || ops->currcon < 0)
2670 return;
2671 vc = vc_cons[ops->currcon].d;
2672
2673 update_screen(vc);
2674 }
2675
fbcon_modechanged(struct fb_info * info)2676 static void fbcon_modechanged(struct fb_info *info)
2677 {
2678 struct fbcon_ops *ops = info->fbcon_par;
2679 struct vc_data *vc;
2680 struct fbcon_display *p;
2681 int rows, cols;
2682
2683 if (!ops || ops->currcon < 0)
2684 return;
2685 vc = vc_cons[ops->currcon].d;
2686 if (vc->vc_mode != KD_TEXT ||
2687 fbcon_info_from_console(ops->currcon) != info)
2688 return;
2689
2690 p = &fb_display[vc->vc_num];
2691 set_blitting_type(vc, info);
2692
2693 if (con_is_visible(vc)) {
2694 var_to_display(p, &info->var, info);
2695 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2696 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2697 cols /= vc->vc_font.width;
2698 rows /= vc->vc_font.height;
2699 vc_resize(vc, cols, rows);
2700 updatescrollmode(p, info, vc);
2701 scrollback_max = 0;
2702 scrollback_current = 0;
2703
2704 if (fbcon_is_active(vc, info)) {
2705 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2706 ops->update_start(info);
2707 }
2708
2709 fbcon_set_palette(vc, color_table);
2710 update_screen(vc);
2711 }
2712 }
2713
fbcon_set_all_vcs(struct fb_info * info)2714 static void fbcon_set_all_vcs(struct fb_info *info)
2715 {
2716 struct fbcon_ops *ops = info->fbcon_par;
2717 struct vc_data *vc;
2718 struct fbcon_display *p;
2719 int i, rows, cols, fg = -1;
2720
2721 if (!ops || ops->currcon < 0)
2722 return;
2723
2724 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2725 vc = vc_cons[i].d;
2726 if (!vc || vc->vc_mode != KD_TEXT ||
2727 fbcon_info_from_console(i) != info)
2728 continue;
2729
2730 if (con_is_visible(vc)) {
2731 fg = i;
2732 continue;
2733 }
2734
2735 p = &fb_display[vc->vc_num];
2736 set_blitting_type(vc, info);
2737 var_to_display(p, &info->var, info);
2738 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2739 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2740 cols /= vc->vc_font.width;
2741 rows /= vc->vc_font.height;
2742 vc_resize(vc, cols, rows);
2743 }
2744
2745 if (fg != -1)
2746 fbcon_modechanged(info);
2747 }
2748
2749
fbcon_update_vcs(struct fb_info * info,bool all)2750 void fbcon_update_vcs(struct fb_info *info, bool all)
2751 {
2752 if (all)
2753 fbcon_set_all_vcs(info);
2754 else
2755 fbcon_modechanged(info);
2756 }
2757 EXPORT_SYMBOL(fbcon_update_vcs);
2758
2759 /* let fbcon check if it supports a new screen resolution */
fbcon_modechange_possible(struct fb_info * info,struct fb_var_screeninfo * var)2760 int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
2761 {
2762 struct fbcon_ops *ops = info->fbcon_par;
2763 struct vc_data *vc;
2764 unsigned int i;
2765
2766 WARN_CONSOLE_UNLOCKED();
2767
2768 if (!ops)
2769 return 0;
2770
2771 /* prevent setting a screen size which is smaller than font size */
2772 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2773 vc = vc_cons[i].d;
2774 if (!vc || vc->vc_mode != KD_TEXT ||
2775 fbcon_info_from_console(i) != info)
2776 continue;
2777
2778 if (vc->vc_font.width > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
2779 vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
2780 return -EINVAL;
2781 }
2782
2783 return 0;
2784 }
2785 EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
2786
fbcon_mode_deleted(struct fb_info * info,struct fb_videomode * mode)2787 int fbcon_mode_deleted(struct fb_info *info,
2788 struct fb_videomode *mode)
2789 {
2790 struct fb_info *fb_info;
2791 struct fbcon_display *p;
2792 int i, j, found = 0;
2793
2794 /* before deletion, ensure that mode is not in use */
2795 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2796 j = con2fb_map[i];
2797 if (j == -1)
2798 continue;
2799 fb_info = fbcon_registered_fb[j];
2800 if (fb_info != info)
2801 continue;
2802 p = &fb_display[i];
2803 if (!p || !p->mode)
2804 continue;
2805 if (fb_mode_is_equal(p->mode, mode)) {
2806 found = 1;
2807 break;
2808 }
2809 }
2810 return found;
2811 }
2812
2813 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
fbcon_unbind(void)2814 static void fbcon_unbind(void)
2815 {
2816 int ret;
2817
2818 ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
2819 fbcon_is_default);
2820
2821 if (!ret)
2822 fbcon_has_console_bind = false;
2823 }
2824 #else
fbcon_unbind(void)2825 static inline void fbcon_unbind(void) {}
2826 #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
2827
fbcon_fb_unbind(struct fb_info * info)2828 void fbcon_fb_unbind(struct fb_info *info)
2829 {
2830 int i, new_idx = -1;
2831 int idx = info->node;
2832
2833 console_lock();
2834
2835 if (!fbcon_has_console_bind) {
2836 console_unlock();
2837 return;
2838 }
2839
2840 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2841 if (con2fb_map[i] != idx &&
2842 con2fb_map[i] != -1) {
2843 new_idx = con2fb_map[i];
2844 break;
2845 }
2846 }
2847
2848 if (new_idx != -1) {
2849 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2850 if (con2fb_map[i] == idx)
2851 set_con2fb_map(i, new_idx, 0);
2852 }
2853 } else {
2854 struct fb_info *info = fbcon_registered_fb[idx];
2855
2856 /* This is sort of like set_con2fb_map, except it maps
2857 * the consoles to no device and then releases the
2858 * oldinfo to free memory and cancel the cursor blink
2859 * timer. I can imagine this just becoming part of
2860 * set_con2fb_map where new_idx is -1
2861 */
2862 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2863 if (con2fb_map[i] == idx) {
2864 con2fb_map[i] = -1;
2865 if (!search_fb_in_map(idx)) {
2866 con2fb_release_oldinfo(vc_cons[i].d,
2867 info, NULL);
2868 }
2869 }
2870 }
2871 fbcon_unbind();
2872 }
2873
2874 console_unlock();
2875 }
2876
fbcon_fb_unregistered(struct fb_info * info)2877 void fbcon_fb_unregistered(struct fb_info *info)
2878 {
2879 int i, idx;
2880
2881 console_lock();
2882
2883 fbcon_registered_fb[info->node] = NULL;
2884 fbcon_num_registered_fb--;
2885
2886 if (deferred_takeover) {
2887 console_unlock();
2888 return;
2889 }
2890
2891 idx = info->node;
2892 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2893 if (con2fb_map[i] == idx)
2894 con2fb_map[i] = -1;
2895 }
2896
2897 if (idx == info_idx) {
2898 info_idx = -1;
2899
2900 fbcon_for_each_registered_fb(i) {
2901 info_idx = i;
2902 break;
2903 }
2904 }
2905
2906 if (info_idx != -1) {
2907 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2908 if (con2fb_map[i] == -1)
2909 con2fb_map[i] = info_idx;
2910 }
2911 }
2912
2913 if (primary_device == idx)
2914 primary_device = -1;
2915
2916 if (!fbcon_num_registered_fb)
2917 do_unregister_con_driver(&fb_con);
2918 console_unlock();
2919 }
2920
fbcon_remap_all(struct fb_info * info)2921 void fbcon_remap_all(struct fb_info *info)
2922 {
2923 int i, idx = info->node;
2924
2925 console_lock();
2926 if (deferred_takeover) {
2927 for (i = first_fb_vc; i <= last_fb_vc; i++)
2928 con2fb_map_boot[i] = idx;
2929 fbcon_map_override();
2930 console_unlock();
2931 return;
2932 }
2933
2934 for (i = first_fb_vc; i <= last_fb_vc; i++)
2935 set_con2fb_map(i, idx, 0);
2936
2937 if (con_is_bound(&fb_con)) {
2938 printk(KERN_INFO "fbcon: Remapping primary device, "
2939 "fb%i, to tty %i-%i\n", idx,
2940 first_fb_vc + 1, last_fb_vc + 1);
2941 info_idx = idx;
2942 }
2943 console_unlock();
2944 }
2945
2946 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
fbcon_select_primary(struct fb_info * info)2947 static void fbcon_select_primary(struct fb_info *info)
2948 {
2949 if (!map_override && primary_device == -1 &&
2950 video_is_primary_device(info->device)) {
2951 int i;
2952
2953 printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
2954 info->fix.id, info->node);
2955 primary_device = info->node;
2956
2957 for (i = first_fb_vc; i <= last_fb_vc; i++)
2958 con2fb_map_boot[i] = primary_device;
2959
2960 if (con_is_bound(&fb_con)) {
2961 printk(KERN_INFO "fbcon: Remapping primary device, "
2962 "fb%i, to tty %i-%i\n", info->node,
2963 first_fb_vc + 1, last_fb_vc + 1);
2964 info_idx = primary_device;
2965 }
2966 }
2967
2968 }
2969 #else
fbcon_select_primary(struct fb_info * info)2970 static inline void fbcon_select_primary(struct fb_info *info)
2971 {
2972 return;
2973 }
2974 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
2975
2976 static bool lockless_register_fb;
2977 module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
2978 MODULE_PARM_DESC(lockless_register_fb,
2979 "Lockless framebuffer registration for debugging [default=off]");
2980
2981 /* called with console_lock held */
do_fb_registered(struct fb_info * info)2982 static int do_fb_registered(struct fb_info *info)
2983 {
2984 int ret = 0, i, idx;
2985
2986 WARN_CONSOLE_UNLOCKED();
2987
2988 fbcon_registered_fb[info->node] = info;
2989 fbcon_num_registered_fb++;
2990
2991 idx = info->node;
2992 fbcon_select_primary(info);
2993
2994 if (deferred_takeover) {
2995 pr_info("fbcon: Deferring console take-over\n");
2996 return 0;
2997 }
2998
2999 if (info_idx == -1) {
3000 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3001 if (con2fb_map_boot[i] == idx) {
3002 info_idx = idx;
3003 break;
3004 }
3005 }
3006
3007 if (info_idx != -1)
3008 ret = do_fbcon_takeover(1);
3009 } else {
3010 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3011 if (con2fb_map_boot[i] == idx)
3012 set_con2fb_map(i, idx, 0);
3013 }
3014 }
3015
3016 return ret;
3017 }
3018
fbcon_fb_registered(struct fb_info * info)3019 int fbcon_fb_registered(struct fb_info *info)
3020 {
3021 int ret;
3022
3023 if (!lockless_register_fb)
3024 console_lock();
3025 else
3026 atomic_inc(&ignore_console_lock_warning);
3027
3028 ret = do_fb_registered(info);
3029
3030 if (!lockless_register_fb)
3031 console_unlock();
3032 else
3033 atomic_dec(&ignore_console_lock_warning);
3034
3035 return ret;
3036 }
3037
fbcon_fb_blanked(struct fb_info * info,int blank)3038 void fbcon_fb_blanked(struct fb_info *info, int blank)
3039 {
3040 struct fbcon_ops *ops = info->fbcon_par;
3041 struct vc_data *vc;
3042
3043 if (!ops || ops->currcon < 0)
3044 return;
3045
3046 vc = vc_cons[ops->currcon].d;
3047 if (vc->vc_mode != KD_TEXT ||
3048 fbcon_info_from_console(ops->currcon) != info)
3049 return;
3050
3051 if (con_is_visible(vc)) {
3052 if (blank)
3053 do_blank_screen(0);
3054 else
3055 do_unblank_screen(0);
3056 }
3057 ops->blank_state = blank;
3058 }
3059
fbcon_new_modelist(struct fb_info * info)3060 void fbcon_new_modelist(struct fb_info *info)
3061 {
3062 int i;
3063 struct vc_data *vc;
3064 struct fb_var_screeninfo var;
3065 const struct fb_videomode *mode;
3066
3067 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3068 if (fbcon_info_from_console(i) != info)
3069 continue;
3070 if (!fb_display[i].mode)
3071 continue;
3072 vc = vc_cons[i].d;
3073 display_to_var(&var, &fb_display[i]);
3074 mode = fb_find_nearest_mode(fb_display[i].mode,
3075 &info->modelist);
3076 fb_videomode_to_var(&var, mode);
3077 fbcon_set_disp(info, &var, vc->vc_num);
3078 }
3079 }
3080
fbcon_get_requirement(struct fb_info * info,struct fb_blit_caps * caps)3081 void fbcon_get_requirement(struct fb_info *info,
3082 struct fb_blit_caps *caps)
3083 {
3084 struct vc_data *vc;
3085
3086 if (caps->flags) {
3087 int i, charcnt;
3088
3089 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3090 vc = vc_cons[i].d;
3091 if (vc && vc->vc_mode == KD_TEXT &&
3092 info->node == con2fb_map[i]) {
3093 set_bit(vc->vc_font.width - 1, caps->x);
3094 set_bit(vc->vc_font.height - 1, caps->y);
3095 charcnt = vc->vc_font.charcount;
3096 if (caps->len < charcnt)
3097 caps->len = charcnt;
3098 }
3099 }
3100 } else {
3101 vc = vc_cons[fg_console].d;
3102
3103 if (vc && vc->vc_mode == KD_TEXT &&
3104 info->node == con2fb_map[fg_console]) {
3105 bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH);
3106 set_bit(vc->vc_font.width - 1, caps->x);
3107 bitmap_zero(caps->y, FB_MAX_BLIT_HEIGHT);
3108 set_bit(vc->vc_font.height - 1, caps->y);
3109 caps->len = vc->vc_font.charcount;
3110 }
3111 }
3112 }
3113
fbcon_set_con2fb_map_ioctl(void __user * argp)3114 int fbcon_set_con2fb_map_ioctl(void __user *argp)
3115 {
3116 struct fb_con2fbmap con2fb;
3117 int ret;
3118
3119 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3120 return -EFAULT;
3121 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3122 return -EINVAL;
3123 if (con2fb.framebuffer >= FB_MAX)
3124 return -EINVAL;
3125 if (!fbcon_registered_fb[con2fb.framebuffer])
3126 request_module("fb%d", con2fb.framebuffer);
3127 if (!fbcon_registered_fb[con2fb.framebuffer]) {
3128 return -EINVAL;
3129 }
3130
3131 console_lock();
3132 ret = set_con2fb_map(con2fb.console - 1,
3133 con2fb.framebuffer, 1);
3134 console_unlock();
3135
3136 return ret;
3137 }
3138
fbcon_get_con2fb_map_ioctl(void __user * argp)3139 int fbcon_get_con2fb_map_ioctl(void __user *argp)
3140 {
3141 struct fb_con2fbmap con2fb;
3142
3143 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3144 return -EFAULT;
3145 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3146 return -EINVAL;
3147
3148 console_lock();
3149 con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3150 console_unlock();
3151
3152 return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3153 }
3154
3155 /*
3156 * The console `switch' structure for the frame buffer based console
3157 */
3158
3159 static const struct consw fb_con = {
3160 .owner = THIS_MODULE,
3161 .con_startup = fbcon_startup,
3162 .con_init = fbcon_init,
3163 .con_deinit = fbcon_deinit,
3164 .con_clear = fbcon_clear,
3165 .con_putcs = fbcon_putcs,
3166 .con_cursor = fbcon_cursor,
3167 .con_scroll = fbcon_scroll,
3168 .con_switch = fbcon_switch,
3169 .con_blank = fbcon_blank,
3170 .con_font_set = fbcon_set_font,
3171 .con_font_get = fbcon_get_font,
3172 .con_font_default = fbcon_set_def_font,
3173 .con_set_palette = fbcon_set_palette,
3174 .con_invert_region = fbcon_invert_region,
3175 .con_resize = fbcon_resize,
3176 .con_debug_enter = fbcon_debug_enter,
3177 .con_debug_leave = fbcon_debug_leave,
3178 };
3179
rotate_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)3180 static ssize_t rotate_store(struct device *device,
3181 struct device_attribute *attr, const char *buf,
3182 size_t count)
3183 {
3184 struct fb_info *info;
3185 int rotate, idx;
3186 char **last = NULL;
3187
3188 console_lock();
3189 idx = con2fb_map[fg_console];
3190
3191 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3192 goto err;
3193
3194 info = fbcon_registered_fb[idx];
3195 rotate = simple_strtoul(buf, last, 0);
3196 fbcon_rotate(info, rotate);
3197 err:
3198 console_unlock();
3199 return count;
3200 }
3201
rotate_all_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)3202 static ssize_t rotate_all_store(struct device *device,
3203 struct device_attribute *attr,const char *buf,
3204 size_t count)
3205 {
3206 struct fb_info *info;
3207 int rotate, idx;
3208 char **last = NULL;
3209
3210 console_lock();
3211 idx = con2fb_map[fg_console];
3212
3213 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3214 goto err;
3215
3216 info = fbcon_registered_fb[idx];
3217 rotate = simple_strtoul(buf, last, 0);
3218 fbcon_rotate_all(info, rotate);
3219 err:
3220 console_unlock();
3221 return count;
3222 }
3223
rotate_show(struct device * device,struct device_attribute * attr,char * buf)3224 static ssize_t rotate_show(struct device *device,
3225 struct device_attribute *attr,char *buf)
3226 {
3227 struct fb_info *info;
3228 int rotate = 0, idx;
3229
3230 console_lock();
3231 idx = con2fb_map[fg_console];
3232
3233 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3234 goto err;
3235
3236 info = fbcon_registered_fb[idx];
3237 rotate = fbcon_get_rotate(info);
3238 err:
3239 console_unlock();
3240 return sysfs_emit(buf, "%d\n", rotate);
3241 }
3242
cursor_blink_show(struct device * device,struct device_attribute * attr,char * buf)3243 static ssize_t cursor_blink_show(struct device *device,
3244 struct device_attribute *attr, char *buf)
3245 {
3246 struct fb_info *info;
3247 struct fbcon_ops *ops;
3248 int idx, blink = -1;
3249
3250 console_lock();
3251 idx = con2fb_map[fg_console];
3252
3253 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3254 goto err;
3255
3256 info = fbcon_registered_fb[idx];
3257 ops = info->fbcon_par;
3258
3259 if (!ops)
3260 goto err;
3261
3262 blink = delayed_work_pending(&ops->cursor_work);
3263 err:
3264 console_unlock();
3265 return sysfs_emit(buf, "%d\n", blink);
3266 }
3267
cursor_blink_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)3268 static ssize_t cursor_blink_store(struct device *device,
3269 struct device_attribute *attr,
3270 const char *buf, size_t count)
3271 {
3272 struct fb_info *info;
3273 char **last = NULL;
3274 bool blink;
3275 int idx;
3276
3277 console_lock();
3278 idx = con2fb_map[fg_console];
3279
3280 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3281 goto err;
3282
3283 info = fbcon_registered_fb[idx];
3284
3285 if (!info->fbcon_par)
3286 goto err;
3287
3288 blink = simple_strtoul(buf, last, 0);
3289
3290 if (blink) {
3291 fbcon_cursor_blink = true;
3292 fbcon_add_cursor_work(info);
3293 } else {
3294 fbcon_cursor_blink = false;
3295 fbcon_del_cursor_work(info);
3296 }
3297
3298 err:
3299 console_unlock();
3300 return count;
3301 }
3302
3303 static DEVICE_ATTR_RW(cursor_blink);
3304 static DEVICE_ATTR_RW(rotate);
3305 static DEVICE_ATTR_WO(rotate_all);
3306
3307 static struct attribute *fbcon_device_attrs[] = {
3308 &dev_attr_cursor_blink.attr,
3309 &dev_attr_rotate.attr,
3310 &dev_attr_rotate_all.attr,
3311 NULL
3312 };
3313
3314 ATTRIBUTE_GROUPS(fbcon_device);
3315
3316 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
fbcon_register_existing_fbs(struct work_struct * work)3317 static void fbcon_register_existing_fbs(struct work_struct *work)
3318 {
3319 int i;
3320
3321 console_lock();
3322
3323 deferred_takeover = false;
3324 logo_shown = FBCON_LOGO_DONTSHOW;
3325
3326 fbcon_for_each_registered_fb(i)
3327 do_fb_registered(fbcon_registered_fb[i]);
3328
3329 console_unlock();
3330 }
3331
3332 static struct notifier_block fbcon_output_nb;
3333 static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
3334
fbcon_output_notifier(struct notifier_block * nb,unsigned long action,void * data)3335 static int fbcon_output_notifier(struct notifier_block *nb,
3336 unsigned long action, void *data)
3337 {
3338 WARN_CONSOLE_UNLOCKED();
3339
3340 pr_info("fbcon: Taking over console\n");
3341
3342 dummycon_unregister_output_notifier(&fbcon_output_nb);
3343
3344 /* We may get called in atomic context */
3345 schedule_work(&fbcon_deferred_takeover_work);
3346
3347 return NOTIFY_OK;
3348 }
3349 #endif
3350
fbcon_start(void)3351 static void fbcon_start(void)
3352 {
3353 WARN_CONSOLE_UNLOCKED();
3354
3355 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3356 if (conswitchp != &dummy_con)
3357 deferred_takeover = false;
3358
3359 if (deferred_takeover) {
3360 fbcon_output_nb.notifier_call = fbcon_output_notifier;
3361 dummycon_register_output_notifier(&fbcon_output_nb);
3362 return;
3363 }
3364 #endif
3365 }
3366
fb_console_init(void)3367 void __init fb_console_init(void)
3368 {
3369 int i;
3370
3371 console_lock();
3372 fbcon_device = device_create_with_groups(fb_class, NULL,
3373 MKDEV(0, 0), NULL,
3374 fbcon_device_groups, "fbcon");
3375
3376 if (IS_ERR(fbcon_device)) {
3377 printk(KERN_WARNING "Unable to create device "
3378 "for fbcon; errno = %ld\n",
3379 PTR_ERR(fbcon_device));
3380 fbcon_device = NULL;
3381 }
3382
3383 for (i = 0; i < MAX_NR_CONSOLES; i++)
3384 con2fb_map[i] = -1;
3385
3386 fbcon_start();
3387 console_unlock();
3388 }
3389
3390 #ifdef MODULE
3391
fb_console_exit(void)3392 void __exit fb_console_exit(void)
3393 {
3394 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3395 console_lock();
3396 if (deferred_takeover)
3397 dummycon_unregister_output_notifier(&fbcon_output_nb);
3398 console_unlock();
3399
3400 cancel_work_sync(&fbcon_deferred_takeover_work);
3401 #endif
3402
3403 console_lock();
3404 device_destroy(fb_class, MKDEV(0, 0));
3405
3406 do_unregister_con_driver(&fb_con);
3407 console_unlock();
3408 }
3409 #endif
3410