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