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