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