git.y1.nz

fbui

Framebuffer-based graphical environment
download: https://git.y1.nz/archives/fbui.tar.gz
README | Files | Log | Refs

drivers/video/console/fbcon.c

      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/config.h>
     62 #include <linux/module.h>
     63 #include <linux/types.h>
     64 #include <linux/sched.h>
     65 #include <linux/fs.h>
     66 #include <linux/kernel.h>
     67 #include <linux/delay.h>	/* MSch: for IRQ probe */
     68 #include <linux/tty.h>
     69 #include <linux/console.h>
     70 #include <linux/string.h>
     71 #include <linux/kd.h>
     72 #include <linux/slab.h>
     73 #include <linux/fb.h>
     74 #include <linux/vt_kern.h>
     75 #include <linux/selection.h>
     76 #include <linux/font.h>
     77 #include <linux/smp.h>
     78 #include <linux/init.h>
     79 #include <linux/interrupt.h>
     80 
     81 #include <asm/irq.h>
     82 #include <asm/system.h>
     83 #include <asm/uaccess.h>
     84 #ifdef CONFIG_ATARI
     85 #include <asm/atariints.h>
     86 #endif
     87 #ifdef CONFIG_MAC
     88 #include <asm/macints.h>
     89 #endif
     90 #if defined(__mc68000__) || defined(CONFIG_APUS)
     91 #include <asm/machdep.h>
     92 #include <asm/setup.h>
     93 #endif
     94 
     95 #include "fbcon.h"
     96 
     97 #ifdef FBCONDEBUG
     98 #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
     99 #else
    100 #  define DPRINTK(fmt, args...)
    101 #endif
    102 
    103 struct display fb_display[MAX_NR_CONSOLES];
    104 signed char con2fb_map[MAX_NR_CONSOLES];
    105 static int logo_height;
    106 static int logo_lines;
    107 static int logo_shown = -1;
    108 /* Software scrollback */
    109 int fbcon_softback_size = 32768;
    110 static unsigned long softback_buf, softback_curr;
    111 static unsigned long softback_in;
    112 static unsigned long softback_top, softback_end;
    113 static int softback_lines;
    114 /* console mappings */
    115 static int first_fb_vc;
    116 static int last_fb_vc = MAX_NR_CONSOLES - 1;
    117 static int fbcon_is_default = 1; 
    118 /* font data */
    119 static char fontname[40];
    120 
    121 /* current fb_info */
    122 static int info_idx =  -1;
    123 
    124 #define REFCOUNT(fd)	(((int *)(fd))[-1])
    125 #define FNTSIZE(fd)	(((int *)(fd))[-2])
    126 #define FNTCHARCNT(fd)	(((int *)(fd))[-3])
    127 #define FNTSUM(fd)	(((int *)(fd))[-4])
    128 #define FONT_EXTRA_WORDS 4
    129 
    130 #define CM_SOFTBACK	(8)
    131 
    132 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
    133 
    134 static void fbcon_free_font(struct display *);
    135 static int fbcon_set_origin(struct vc_data *);
    136 
    137 #define CURSOR_DRAW_DELAY		(1)
    138 
    139 /* # VBL ints between cursor state changes */
    140 #define ARM_CURSOR_BLINK_RATE		(10)
    141 #define ATARI_CURSOR_BLINK_RATE		(42)
    142 #define MAC_CURSOR_BLINK_RATE		(32)
    143 #define DEFAULT_CURSOR_BLINK_RATE	(20)
    144 
    145 static int vbl_cursor_cnt;
    146 
    147 #define divides(a, b)	((!(a) || (b)%(a)) ? 0 : 1)
    148 
    149 /*
    150  *  Interface used by the world
    151  */
    152 
    153 static const char *fbcon_startup(void);
    154 static void fbcon_init(struct vc_data *vc, int init);
    155 static void fbcon_deinit(struct vc_data *vc);
    156 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
    157 			int width);
    158 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
    159 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
    160 			int count, int ypos, int xpos);
    161 static void fbcon_cursor(struct vc_data *vc, int mode);
    162 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
    163 			int count);
    164 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
    165 			int height, int width);
    166 static int fbcon_switch(struct vc_data *vc);
    167 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
    168 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
    169 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
    170 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
    171 			 int bottom_only);
    172 
    173 
    174 /*
    175  *  Internal routines
    176  */
    177 static __inline__ int real_y(struct display *p, int ypos);
    178 static __inline__ void ywrap_up(struct vc_data *vc, int count);
    179 static __inline__ void ywrap_down(struct vc_data *vc, int count);
    180 static __inline__ void ypan_up(struct vc_data *vc, int count);
    181 static __inline__ void ypan_down(struct vc_data *vc, int count);
    182 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
    183 			    int dy, int dx, int height, int width, u_int y_break);
    184 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
    185 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
    186 			      int line, int count, int dy);
    187 
    188 #ifdef CONFIG_MAC
    189 /*
    190  * On the Macintoy, there may or may not be a working VBL int. We need to probe
    191  */
    192 static int vbl_detected;
    193 
    194 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
    195 {
    196 	vbl_detected++;
    197 	return IRQ_HANDLED;
    198 }
    199 #endif
    200 
    201 static void fb_flashcursor(void *private)
    202 {
    203 	struct fb_info *info = (struct fb_info *) private;
    204 	struct vc_data *vc = NULL;
    205 
    206 	if (info->currcon != -1)
    207 		vc = vc_cons[info->currcon].d;
    208 
    209 	if (info->state != FBINFO_STATE_RUNNING ||
    210 	    info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
    211 	    || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
    212 		return;
    213 	acquire_console_sem();
    214 	info->cursor.enable ^= 1;
    215 	info->fbops->fb_cursor(info, &info->cursor);
    216 	release_console_sem();
    217 }
    218 
    219 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
    220 static int cursor_blink_rate;
    221 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
    222 {
    223 	struct fb_info *info = dev_id;
    224 
    225 	if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
    226 		schedule_work(&info->queue);	
    227 		vbl_cursor_cnt = cursor_blink_rate; 
    228 	}
    229 	return IRQ_HANDLED;
    230 }
    231 #endif
    232 	
    233 static void cursor_timer_handler(unsigned long dev_addr)
    234 {
    235 	struct fb_info *info = (struct fb_info *) dev_addr;
    236 	
    237 	schedule_work(&info->queue);
    238 	mod_timer(&info->cursor_timer, jiffies + HZ/5);
    239 }
    240 
    241 int __init fb_console_setup(char *this_opt)
    242 {
    243 	char *options;
    244 	int i, j;
    245 
    246 	if (!this_opt || !*this_opt)
    247 		return 0;
    248 
    249 	while ((options = strsep(&this_opt, ",")) != NULL) {
    250 		if (!strncmp(options, "font:", 5))
    251 			strcpy(fontname, options + 5);
    252 		
    253 		if (!strncmp(options, "scrollback:", 11)) {
    254 			options += 11;
    255 			if (*options) {
    256 				fbcon_softback_size = simple_strtoul(options, &options, 0);
    257 				if (*options == 'k' || *options == 'K') {
    258 					fbcon_softback_size *= 1024;
    259 					options++;
    260 				}
    261 				if (*options != ',')
    262 					return 0;
    263 				options++;
    264 			} else
    265 				return 0;
    266 		}
    267 		
    268 		if (!strncmp(options, "map:", 4)) {
    269 			options += 4;
    270 			if (*options)
    271 				for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
    272 					if (!options[j])
    273 						j = 0;
    274 					con2fb_map[i] = (options[j++]-'0') % FB_MAX;
    275 				}
    276 			return 0;
    277 		}
    278 
    279 		if (!strncmp(options, "vc:", 3)) {
    280 			options += 3;
    281 			if (*options)
    282 				first_fb_vc = simple_strtoul(options, &options, 10) - 1;
    283 			if (first_fb_vc < 0)
    284 				first_fb_vc = 0;
    285 			if (*options++ == '-')
    286 				last_fb_vc = simple_strtoul(options, &options, 10) - 1;
    287 			fbcon_is_default = 0; 
    288 		}	
    289 	}
    290 	return 0;
    291 }
    292 
    293 __setup("fbcon=", fb_console_setup);
    294 
    295 static int search_fb_in_map(int idx)
    296 {
    297 	int i;
    298 
    299 	for (i = 0; i < MAX_NR_CONSOLES; i++) {
    300 		if (con2fb_map[i] == idx)
    301 			return 1;
    302 	}
    303 	return 0;
    304 }
    305 
    306 static int search_for_mapped_con(void)
    307 {
    308 	int i;
    309 
    310 	for (i = 0; i < MAX_NR_CONSOLES; i++) {
    311 		if (con2fb_map[i] != -1)
    312 			return 1;
    313 	}
    314 	return 0;
    315 }
    316 
    317 static int fbcon_takeover(void)
    318 {
    319 	int err, i;
    320 
    321 	if (!num_registered_fb)
    322 		return -ENODEV;
    323 
    324 	for (i = first_fb_vc; i <= last_fb_vc; i++)
    325 		con2fb_map[i] = info_idx;
    326 
    327 	err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
    328 				fbcon_is_default);
    329 	if (err) {
    330 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
    331 			con2fb_map[i] = -1;
    332 		}
    333 		info_idx = -1;
    334 	}
    335 
    336 	return err;
    337 }
    338 
    339 /**
    340  *	set_con2fb_map - map console to frame buffer device
    341  *	@unit: virtual console number to map
    342  *	@newidx: frame buffer index to map virtual console to
    343  *
    344  *	Maps a virtual console @unit to a frame buffer device
    345  *	@newidx.
    346  */
    347 static int set_con2fb_map(int unit, int newidx)
    348 {
    349 	struct vc_data *vc = vc_cons[unit].d;
    350 	int oldidx = con2fb_map[unit];
    351 	struct fb_info *info = registered_fb[newidx];
    352 	struct fb_info *oldinfo = NULL;
    353 	int found;
    354 
    355 	if (oldidx == newidx)
    356 		return 0;
    357 
    358 	if (!vc)
    359 	    return -ENODEV;
    360 
    361 	if (!search_for_mapped_con()) {
    362 		info_idx = newidx;
    363 		return fbcon_takeover();
    364 	}
    365 
    366 	if (oldidx != -1)
    367 		oldinfo = registered_fb[oldidx];
    368 
    369 	found = search_fb_in_map(newidx);
    370 
    371 	acquire_console_sem();
    372 	con2fb_map[unit] = newidx;
    373 	if (!found) {
    374 		if (!try_module_get(info->fbops->owner)) {
    375 			con2fb_map[unit] = oldidx;
    376 			release_console_sem();
    377 			return -ENODEV;
    378 		}
    379 		if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
    380 			module_put(info->fbops->owner);
    381 			con2fb_map[unit] = oldidx;
    382 			release_console_sem();
    383 			return -ENODEV;
    384 		}
    385 	}
    386 
    387 	/*
    388 	 * If old fb is not mapped to any of the consoles,
    389 	 * fbcon should release it.
    390 	 */
    391 	if (oldinfo && !search_fb_in_map(oldidx)) {
    392 		if (oldinfo->fbops->fb_release &&
    393 		    oldinfo->fbops->fb_release(oldinfo, 0)) {
    394 			con2fb_map[unit] = oldidx;
    395 			if (!found && info->fbops->fb_release)
    396 				info->fbops->fb_release(info, 0);
    397 			if (!found)
    398 				module_put(info->fbops->owner);
    399 			release_console_sem();
    400 			return -ENODEV;
    401 		}
    402 		if (oldinfo->queue.func == fb_flashcursor)
    403 			del_timer_sync(&oldinfo->cursor_timer);
    404 		module_put(oldinfo->fbops->owner);
    405 	}
    406 	info->currcon = -1;
    407 	if (!found) {
    408 		if (!info->queue.func || info->queue.func == fb_flashcursor) {
    409 			if (!info->queue.func)
    410 				INIT_WORK(&info->queue, fb_flashcursor, info);
    411 
    412 			init_timer(&info->cursor_timer);
    413 			info->cursor_timer.function = cursor_timer_handler;
    414 			info->cursor_timer.expires = jiffies + HZ / 5;
    415 			info->cursor_timer.data = (unsigned long ) info;
    416 			add_timer(&info->cursor_timer);
    417 		}
    418 	}
    419 	if (info->fbops->fb_set_par)
    420 		info->fbops->fb_set_par(info);
    421 	fbcon_set_disp(info, vc);
    422 	release_console_sem();
    423 	return 0;
    424 }
    425 
    426 /*
    427  * Accelerated handlers.
    428  */
    429 static inline int get_color(struct vc_data *vc, struct fb_info *info,
    430 			    u16 c, int is_fg)
    431 {
    432 	int depth = fb_get_color_depth(info);
    433 	int color = 0;
    434 
    435 	if (depth != 1)
    436 		color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
    437 			: attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
    438 
    439 	switch (depth) {
    440 	case 1:
    441 	{
    442 		/* 0 or 1 */
    443 		int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0;
    444 		int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1;
    445 
    446 		color = (is_fg) ? fg : bg;
    447 		break;
    448 	}
    449 	case 2:
    450 		/*
    451 		 * Scale down 16-colors to 4 colors. Default 4-color palette
    452 		 * is grayscale.
    453 		 */
    454 		color /= 4;
    455 		break;
    456 	case 3:
    457 		/*
    458 		 * Last 8 entries of default 16-color palette is a more intense
    459 		 * version of the first 8 (i.e., same chrominance, different
    460 		 * luminance).
    461 		 */
    462 		color &= 7;
    463 		break;
    464 	}
    465 
    466 	return color;
    467 }
    468 
    469 #define FBCON_ATTRIBUTE_UNDERLINE 1
    470 #define FBCON_ATTRIBUTE_REVERSE   2
    471 #define FBCON_ATTRIBUTE_BOLD      4
    472 
    473 static inline int get_attribute(struct fb_info *info, u16 c)
    474 {
    475 	int attribute = 0;
    476 
    477 	if (fb_get_color_depth(info) == 1) {
    478 		if (attr_underline(c))
    479 			attribute |= FBCON_ATTRIBUTE_UNDERLINE;
    480 		if (attr_reverse(c))
    481 			attribute |= FBCON_ATTRIBUTE_REVERSE;
    482 		if (attr_bold(c))
    483 			attribute |= FBCON_ATTRIBUTE_BOLD;
    484 	}
    485 
    486 	return attribute;
    487 }
    488 
    489 static inline void update_attr(u8 *dst, u8 *src, int attribute,
    490 			       struct vc_data *vc)
    491 {
    492 	int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
    493 	int width = (vc->vc_font.width + 7) >> 3;
    494 	unsigned int cellsize = vc->vc_font.height * width;
    495 	u8 c;
    496 
    497 	offset = cellsize - (offset * width);
    498 	for (i = 0; i < cellsize; i++) {
    499 		c = src[i];
    500 		if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
    501 			c = 0xff;
    502 		if (attribute & FBCON_ATTRIBUTE_BOLD)
    503 			c |= c >> 1;
    504 		if (attribute & FBCON_ATTRIBUTE_REVERSE)
    505 			c = ~c;
    506 		dst[i] = c;
    507 	}
    508 }
    509 
    510 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy, 
    511 		int sx, int dy, int dx, int height, int width)
    512 {
    513 	struct fb_copyarea area;
    514 
    515 	area.sx = sx * vc->vc_font.width;
    516 	area.sy = sy * vc->vc_font.height;
    517 	area.dx = dx * vc->vc_font.width;
    518 	area.dy = dy * vc->vc_font.height;
    519 	area.height = height * vc->vc_font.height;
    520 	area.width = width * vc->vc_font.width;
    521 
    522 	info->fbops->fb_copyarea(info, &area);
    523 }
    524 
    525 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
    526 			int sx, int height, int width)
    527 {
    528 	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
    529 	struct fb_fillrect region;
    530 
    531 	region.color = attr_bgcol_ec(bgshift, vc);
    532 	region.dx = sx * vc->vc_font.width;
    533 	region.dy = sy * vc->vc_font.height;
    534 	region.width = width * vc->vc_font.width;
    535 	region.height = height * vc->vc_font.height;
    536 	region.rop = ROP_COPY;
    537 
    538 	info->fbops->fb_fillrect(info, &region);
    539 }	
    540 
    541 void accel_putcs(struct vc_data *vc, struct fb_info *info,
    542 		 const unsigned short *s, int count, int yy, int xx)
    543 {
    544 	void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
    545 			       u8 *dst, u32 d_pitch, u8 *src, u32 idx,
    546 			       u32 height, u32 shift_high, u32 shift_low,
    547 			       u32 mod);
    548 	void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
    549 			     u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
    550 			     u32 height);
    551 	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
    552 	unsigned int width = (vc->vc_font.width + 7) >> 3;
    553 	unsigned int cellsize = vc->vc_font.height * width;
    554 	unsigned int maxcnt = info->pixmap.size/cellsize;
    555 	unsigned int scan_align = info->pixmap.scan_align - 1;
    556 	unsigned int buf_align = info->pixmap.buf_align - 1;
    557 	unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
    558 	unsigned int shift_high = 8, pitch, cnt, size, k;
    559 	unsigned int idx = vc->vc_font.width >> 3;
    560 	unsigned int attribute = get_attribute(info, scr_readw(s));
    561 	struct fb_image image;
    562 	u8 *src, *dst, *buf = NULL;
    563 
    564 	if (attribute) {
    565 		buf = kmalloc(cellsize, GFP_KERNEL);
    566 		if (!buf)
    567 			return;
    568 	}
    569 
    570 	image.fg_color = get_color(vc, info, scr_readw(s), 1);
    571 	image.bg_color = get_color(vc, info, scr_readw(s), 0);
    572 
    573 	image.dx = xx * vc->vc_font.width;
    574 	image.dy = yy * vc->vc_font.height;
    575 	image.height = vc->vc_font.height;
    576 	image.depth = 1;
    577 
    578 	if (info->pixmap.outbuf && info->pixmap.inbuf) {
    579 		move_aligned = fb_iomove_buf_aligned;
    580 		move_unaligned = fb_iomove_buf_unaligned;
    581 	} else {
    582 		move_aligned = fb_sysmove_buf_aligned;
    583 		move_unaligned = fb_sysmove_buf_unaligned;
    584 	}
    585 	while (count) {
    586 		if (count > maxcnt)
    587 			cnt = k = maxcnt;
    588 		else
    589 			cnt = k = count;
    590 
    591 		image.width = vc->vc_font.width * cnt;
    592 		pitch = ((image.width + 7) >> 3) + scan_align;
    593 		pitch &= ~scan_align;
    594 		size = pitch * image.height + buf_align;
    595 		size &= ~buf_align;
    596 		dst = fb_get_buffer_offset(info, &info->pixmap, size);
    597 		image.data = dst;
    598 		if (mod) {
    599 			while (k--) {
    600 				src = vc->vc_font.data + (scr_readw(s++)&
    601 							  charmask)*cellsize;
    602 
    603 				if (attribute) {
    604 					update_attr(buf, src, attribute, vc);
    605 					src = buf;
    606 				}
    607 
    608 				move_unaligned(info, &info->pixmap, dst, pitch,
    609 					       src, idx, image.height,
    610 					       shift_high, shift_low, mod);
    611 				shift_low += mod;
    612 				dst += (shift_low >= 8) ? width : width - 1;
    613 				shift_low &= 7;
    614 				shift_high = 8 - shift_low;
    615 			}
    616 		} else {
    617 			while (k--) {
    618 				src = vc->vc_font.data + (scr_readw(s++)&
    619 							  charmask)*cellsize;
    620 
    621 				if (attribute) {
    622 					update_attr(buf, src, attribute, vc);
    623 					src = buf;
    624 				}
    625 
    626 				move_aligned(info, &info->pixmap, dst, pitch,
    627 					     src, idx, image.height);
    628 				dst += width;
    629 			}
    630 		}
    631 		info->fbops->fb_imageblit(info, &image);
    632 		image.dx += cnt * vc->vc_font.width;
    633 		count -= cnt;
    634 	}
    635 
    636 	if (buf)
    637 		kfree(buf);
    638 }
    639 
    640 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
    641 				int bottom_only)
    642 {
    643 	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
    644 	unsigned int cw = vc->vc_font.width;
    645 	unsigned int ch = vc->vc_font.height;
    646 	unsigned int rw = info->var.xres - (vc->vc_cols*cw);
    647 	unsigned int bh = info->var.yres - (vc->vc_rows*ch);
    648 	unsigned int rs = info->var.xres - rw;
    649 	unsigned int bs = info->var.yres - bh;
    650 	struct fb_fillrect region;
    651 
    652 	region.color = attr_bgcol_ec(bgshift, vc);
    653 	region.rop = ROP_COPY;
    654 
    655 	if (rw && !bottom_only) {
    656 		region.dx = info->var.xoffset + rs;
    657 		region.dy = 0;
    658 		region.width = rw;
    659 		region.height = info->var.yres_virtual;
    660 		info->fbops->fb_fillrect(info, &region);
    661 	}
    662 
    663 	if (bh) {
    664 		region.dx = info->var.xoffset;
    665 		region.dy = info->var.yoffset + bs;
    666 		region.width = rs;
    667 		region.height = bh;
    668 		info->fbops->fb_fillrect(info, &region);
    669 	}	
    670 }	
    671 
    672 /*
    673  *  Low Level Operations
    674  */
    675 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
    676 static int var_to_display(struct display *disp,
    677 			  struct fb_var_screeninfo *var,
    678 			  struct fb_info *info)
    679 {
    680 	disp->xres_virtual = var->xres_virtual;
    681 	disp->yres_virtual = var->yres_virtual;
    682 	disp->bits_per_pixel = var->bits_per_pixel;
    683 	disp->grayscale = var->grayscale;
    684 	disp->nonstd = var->nonstd;
    685 	disp->accel_flags = var->accel_flags;
    686 	disp->height = var->height;
    687 	disp->width = var->width;
    688 	disp->red = var->red;
    689 	disp->green = var->green;
    690 	disp->blue = var->blue;
    691 	disp->transp = var->transp;
    692 	disp->mode = fb_match_mode(var, &info->modelist);
    693 	if (disp->mode == NULL)
    694 		/* This should not happen */
    695 		return -EINVAL;
    696 	return 0;
    697 }
    698 
    699 static void display_to_var(struct fb_var_screeninfo *var,
    700 			   struct display *disp)
    701 {
    702 	fb_videomode_to_var(var, disp->mode);
    703 	var->xres_virtual = disp->xres_virtual;
    704 	var->yres_virtual = disp->yres_virtual;
    705 	var->bits_per_pixel = disp->bits_per_pixel;
    706 	var->grayscale = disp->grayscale;
    707 	var->nonstd = disp->nonstd;
    708 	var->accel_flags = disp->accel_flags;
    709 	var->height = disp->height;
    710 	var->width = disp->width;
    711 	var->red = disp->red;
    712 	var->green = disp->green;
    713 	var->blue = disp->blue;
    714 	var->transp = disp->transp;
    715 }
    716 
    717 static const char *fbcon_startup(void)
    718 {
    719 	const char *display_desc = "frame buffer device";
    720 	struct display *p = &fb_display[fg_console];
    721 	struct vc_data *vc = vc_cons[fg_console].d;
    722 	struct font_desc *font = NULL;
    723 	struct module *owner;
    724 	struct fb_info *info = NULL;
    725 	int rows, cols;
    726 	int irqres;
    727 
    728 	irqres = 1;
    729 	/*
    730 	 *  If num_registered_fb is zero, this is a call for the dummy part.
    731 	 *  The frame buffer devices weren't initialized yet.
    732 	 */
    733 	if (!num_registered_fb || info_idx == -1)
    734 		return display_desc;
    735 	/*
    736 	 * Instead of blindly using registered_fb[0], we use info_idx, set by
    737 	 * fb_console_init();
    738 	 */
    739 	info = registered_fb[info_idx];
    740 	if (!info)
    741 		return NULL;
    742 	info->currcon = -1;
    743 	
    744 	owner = info->fbops->owner;
    745 	if (!try_module_get(owner))
    746 		return NULL;
    747 	if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
    748 		module_put(owner);
    749 		return NULL;
    750 	}
    751 	if (info->fix.type != FB_TYPE_TEXT) {
    752 		if (fbcon_softback_size) {
    753 			if (!softback_buf) {
    754 				softback_buf =
    755 				    (unsigned long)
    756 				    kmalloc(fbcon_softback_size,
    757 					    GFP_KERNEL);
    758 				if (!softback_buf) {
    759 					fbcon_softback_size = 0;
    760 					softback_top = 0;
    761 				}
    762 			}
    763 		} else {
    764 			if (softback_buf) {
    765 				kfree((void *) softback_buf);
    766 				softback_buf = 0;
    767 				softback_top = 0;
    768 			}
    769 		}
    770 		if (softback_buf)
    771 			softback_in = softback_top = softback_curr =
    772 			    softback_buf;
    773 		softback_lines = 0;
    774 	}
    775 
    776 	/* Setup default font */
    777 	if (!p->fontdata) {
    778 		if (!fontname[0] || !(font = find_font(fontname)))
    779 			font = get_default_font(info->var.xres,
    780 						   info->var.yres);
    781 		vc->vc_font.width = font->width;
    782 		vc->vc_font.height = font->height;
    783 		vc->vc_font.data = p->fontdata = font->data;
    784 		vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */
    785 	}
    786 
    787 	cols = info->var.xres / vc->vc_font.width;
    788 	rows = info->var.yres / vc->vc_font.height;
    789 	vc_resize(vc->vc_num, cols, rows);
    790 
    791 	DPRINTK("mode:   %s\n", info->fix.id);
    792 	DPRINTK("visual: %d\n", info->fix.visual);
    793 	DPRINTK("res:    %dx%d-%d\n", info->var.xres,
    794 		info->var.yres,
    795 		info->var.bits_per_pixel);
    796 	con_set_default_unimap(vc->vc_num);
    797 
    798 #ifdef CONFIG_ATARI
    799 	if (MACH_IS_ATARI) {
    800 		cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
    801 		irqres =
    802 		    request_irq(IRQ_AUTO_4, fb_vbl_handler,
    803 				IRQ_TYPE_PRIO, "framebuffer vbl",
    804 				info);
    805 	}
    806 #endif				/* CONFIG_ATARI */
    807 
    808 #ifdef CONFIG_MAC
    809 	/*
    810 	 * On a Macintoy, the VBL interrupt may or may not be active. 
    811 	 * As interrupt based cursor is more reliable and race free, we 
    812 	 * probe for VBL interrupts.
    813 	 */
    814 	if (MACH_IS_MAC) {
    815 		int ct = 0;
    816 		/*
    817 		 * Probe for VBL: set temp. handler ...
    818 		 */
    819 		irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
    820 				     "framebuffer vbl", info);
    821 		vbl_detected = 0;
    822 
    823 		/*
    824 		 * ... and spin for 20 ms ...
    825 		 */
    826 		while (!vbl_detected && ++ct < 1000)
    827 			udelay(20);
    828 
    829 		if (ct == 1000)
    830 			printk
    831 			    ("fbcon_startup: No VBL detected, using timer based cursor.\n");
    832 
    833 		free_irq(IRQ_MAC_VBL, fb_vbl_detect);
    834 
    835 		if (vbl_detected) {
    836 			/*
    837 			 * interrupt based cursor ok
    838 			 */
    839 			cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
    840 			irqres =
    841 			    request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
    842 					"framebuffer vbl", info);
    843 		} else {
    844 			/*
    845 			 * VBL not detected: fall through, use timer based cursor
    846 			 */
    847 			irqres = 1;
    848 		}
    849 	}
    850 #endif				/* CONFIG_MAC */
    851 
    852 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
    853 	cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
    854 	irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
    855 			     "framebuffer vbl", info);
    856 #endif
    857 	/* Initialize the work queue. If the driver provides its
    858 	 * own work queue this means it will use something besides 
    859 	 * default timer to flash the cursor. */
    860 	if (!info->queue.func) {
    861 		INIT_WORK(&info->queue, fb_flashcursor, info);
    862 
    863 		init_timer(&info->cursor_timer);
    864 		info->cursor_timer.function = cursor_timer_handler;
    865 		info->cursor_timer.expires = jiffies + HZ / 5;
    866 		info->cursor_timer.data = (unsigned long ) info;
    867 		add_timer(&info->cursor_timer);
    868 	}
    869 	return display_desc;
    870 }
    871 
    872 static void fbcon_init(struct vc_data *vc, int init)
    873 {
    874 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
    875 	struct vc_data **default_mode = vc->vc_display_fg;
    876 	struct display *t, *p = &fb_display[vc->vc_num];
    877 	int display_fg = (*default_mode)->vc_num;
    878 	int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
    879 	unsigned short *save = NULL, *r, *q;
    880 	int cap = info->flags;
    881 
    882 	if (info_idx == -1 || info == NULL)
    883 	    return;
    884 	if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
    885 	    (info->fix.type == FB_TYPE_TEXT))
    886 		logo = 0;
    887 
    888 	info->var.xoffset = info->var.yoffset = p->yscroll = 0;	/* reset wrap/pan */
    889 
    890 	if (var_to_display(p, &info->var, info))
    891 		return;
    892 
    893 	/* If we are not the first console on this
    894 	   fb, copy the font from that console */
    895 	t = &fb_display[display_fg];
    896 	if (!vc->vc_font.data) {
    897 		vc->vc_font.data = p->fontdata = t->fontdata;
    898 		vc->vc_font.width = (*default_mode)->vc_font.width;
    899 		vc->vc_font.height = (*default_mode)->vc_font.height;
    900 		p->userfont = t->userfont;
    901 		if (p->userfont)
    902 			REFCOUNT(p->fontdata)++;
    903 		con_copy_unimap(vc->vc_num, display_fg);
    904 	}
    905 	if (p->userfont)
    906 		charcnt = FNTCHARCNT(p->fontdata);
    907 	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
    908 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
    909 	if (charcnt == 256) {
    910 		vc->vc_hi_font_mask = 0;
    911 	} else {
    912 		vc->vc_hi_font_mask = 0x100;
    913 		if (vc->vc_can_do_color)
    914 			vc->vc_complement_mask <<= 1;
    915 	}
    916 	cols = vc->vc_cols;
    917 	rows = vc->vc_rows;
    918 	new_cols = info->var.xres / vc->vc_font.width;
    919 	new_rows = info->var.yres / vc->vc_font.height;
    920 	vc_resize(vc->vc_num, new_cols, new_rows);
    921 	/*
    922 	 * We must always set the mode. The mode of the previous console
    923 	 * driver could be in the same resolution but we are using different
    924 	 * hardware so we have to initialize the hardware.
    925 	 *
    926 	 * We need to do it in fbcon_init() to prevent screen corruption.
    927 	 */
    928 	if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
    929 		info->fbops->fb_set_par(info);
    930 
    931 
    932 	if ((cap & FBINFO_HWACCEL_COPYAREA) &&
    933 	    !(cap & FBINFO_HWACCEL_DISABLED))
    934 		p->scrollmode = SCROLL_MOVE;
    935 	else /* default to something safe */
    936 		p->scrollmode = SCROLL_REDRAW;
    937 
    938 	/*
    939 	 *  ++guenther: console.c:vc_allocate() relies on initializing
    940 	 *  vc_{cols,rows}, but we must not set those if we are only
    941 	 *  resizing the console.
    942 	 */
    943 	if (!init) {
    944 		vc->vc_cols = new_cols;
    945 		vc->vc_rows = new_rows;
    946 	}
    947 
    948 	if (logo) {
    949 		/* Need to make room for the logo */
    950 		int cnt, erase = vc->vc_video_erase_char;
    951 		int step;
    952 
    953 		/*
    954 		 * remove underline attribute from erase character
    955 		 * if black and white framebuffer.
    956 		 */
    957 		if (fb_get_color_depth(info) == 1)
    958 			erase &= ~0x400;
    959 		logo_height = fb_prepare_logo(info);
    960 		logo_lines = (logo_height + vc->vc_font.height - 1) /
    961 			     vc->vc_font.height;
    962 		q = (unsigned short *) (vc->vc_origin +
    963 					vc->vc_size_row * rows);
    964 		step = logo_lines * cols;
    965 		for (r = q - logo_lines * cols; r < q; r++)
    966 			if (scr_readw(r) != vc->vc_video_erase_char)
    967 				break;
    968 		if (r != q && new_rows >= rows + logo_lines) {
    969 			save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
    970 			if (save) {
    971 				int i = cols < new_cols ? cols : new_cols;
    972 				scr_memsetw(save, erase, logo_lines * new_cols * 2);
    973 				r = q - step;
    974 				for (cnt = 0; cnt < logo_lines; cnt++, r += i)
    975 					scr_memcpyw(save + cnt * new_cols, r, 2 * i);
    976 				r = q;
    977 			}
    978 		}
    979 		if (r == q) {
    980 			/* We can scroll screen down */
    981 			r = q - step - cols;
    982 			for (cnt = rows - logo_lines; cnt > 0; cnt--) {
    983 				scr_memcpyw(r + step, r, vc->vc_size_row);
    984 				r -= cols;
    985 			}
    986 			if (!save) {
    987 				vc->vc_y += logo_lines;
    988 				vc->vc_pos += logo_lines * vc->vc_size_row;
    989 			}
    990 		}
    991 		scr_memsetw((unsigned short *) vc->vc_origin,
    992 			    erase,
    993 			    vc->vc_size_row * logo_lines);
    994 
    995 		if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
    996 			accel_clear_margins(vc, info, 0);
    997 			update_screen(vc->vc_num);
    998 		}
    999 		if (save) {
   1000 			q = (unsigned short *) (vc->vc_origin +
   1001 						vc->vc_size_row *
   1002 						rows);
   1003 			scr_memcpyw(q, save, logo_lines * new_cols * 2);
   1004 			vc->vc_y += logo_lines;
   1005 			vc->vc_pos += logo_lines * vc->vc_size_row;
   1006 			kfree(save);
   1007 		}
   1008 		if (logo_lines > vc->vc_bottom) {
   1009 			logo_shown = -1;
   1010 			printk(KERN_INFO
   1011 			       "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
   1012 		} else {
   1013 			logo_shown = -2;
   1014 			vc->vc_top = logo_lines;
   1015 		}
   1016 	}
   1017 
   1018 	if (vc->vc_num == display_fg && softback_buf) {
   1019 		int l = fbcon_softback_size / vc->vc_size_row;
   1020 		if (l > 5)
   1021 			softback_end = softback_buf + l * vc->vc_size_row;
   1022 		else {
   1023 			/* Smaller scrollback makes no sense, and 0 would screw
   1024 			   the operation totally */
   1025 			softback_top = 0;
   1026 		}
   1027 	}
   1028 }
   1029 
   1030 static void fbcon_deinit(struct vc_data *vc)
   1031 {
   1032 	struct display *p = &fb_display[vc->vc_num];
   1033 
   1034 	if (info_idx != -1)
   1035 	    return;
   1036 	fbcon_free_font(p);
   1037 }
   1038 
   1039 /* ====================================================================== */
   1040 
   1041 /*  fbcon_XXX routines - interface used by the world
   1042  *
   1043  *  This system is now divided into two levels because of complications
   1044  *  caused by hardware scrolling. Top level functions:
   1045  *
   1046  *	fbcon_bmove(), fbcon_clear(), fbcon_putc()
   1047  *
   1048  *  handles y values in range [0, scr_height-1] that correspond to real
   1049  *  screen positions. y_wrap shift means that first line of bitmap may be
   1050  *  anywhere on this display. These functions convert lineoffsets to
   1051  *  bitmap offsets and deal with the wrap-around case by splitting blits.
   1052  *
   1053  *	fbcon_bmove_physical_8()    -- These functions fast implementations
   1054  *	fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
   1055  *	fbcon_putc_physical_8()	    -- (font width != 8) may be added later
   1056  *
   1057  *  WARNING:
   1058  *
   1059  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
   1060  *  Implies should only really hardware scroll in rows. Only reason for
   1061  *  restriction is simplicity & efficiency at the moment.
   1062  */
   1063 
   1064 static __inline__ int real_y(struct display *p, int ypos)
   1065 {
   1066 	int rows = p->vrows;
   1067 
   1068 	ypos += p->yscroll;
   1069 	return ypos < rows ? ypos : ypos - rows;
   1070 }
   1071 
   1072 
   1073 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
   1074 			int width)
   1075 {
   1076 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1077 	
   1078 	struct display *p = &fb_display[vc->vc_num];
   1079 	u_int y_break;
   1080 
   1081 	if (!info->fbops->fb_blank && console_blanked)
   1082 		return;
   1083 	if (info->state != FBINFO_STATE_RUNNING)
   1084 		return;
   1085 
   1086 	if (!height || !width)
   1087 		return;
   1088 
   1089 	/* Split blits that cross physical y_wrap boundary */
   1090 
   1091 	y_break = p->vrows - p->yscroll;
   1092 	if (sy < y_break && sy + height - 1 >= y_break) {
   1093 		u_int b = y_break - sy;
   1094 		accel_clear(vc, info, real_y(p, sy), sx, b, width);
   1095 		accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
   1096 				 width);
   1097 	} else
   1098 		accel_clear(vc, info, real_y(p, sy), sx, height, width);
   1099 }
   1100 
   1101 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
   1102 			int count, int ypos, int xpos)
   1103 {
   1104 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1105 	struct display *p = &fb_display[vc->vc_num];
   1106 
   1107 	if (!info->fbops->fb_blank && console_blanked)
   1108 		return;
   1109 	if (info->state != FBINFO_STATE_RUNNING)
   1110 		return;
   1111 
   1112 	if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
   1113 		return;
   1114 
   1115 	accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
   1116 }
   1117 
   1118 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
   1119 {
   1120 	fbcon_putcs(vc, (const unsigned short *) &c, 1, ypos, xpos);
   1121 }
   1122 
   1123 static void fbcon_cursor(struct vc_data *vc, int mode)
   1124 {
   1125 	struct fb_cursor cursor;
   1126 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1127 	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
   1128 	struct display *p = &fb_display[vc->vc_num];
   1129 	int w = (vc->vc_font.width + 7) >> 3, c;
   1130 	int y = real_y(p, vc->vc_y), fg, bg;
   1131 	int attribute;
   1132 	u8 *src;
   1133 
   1134 	if (mode & CM_SOFTBACK) {
   1135 		mode &= ~CM_SOFTBACK;
   1136 		if (softback_lines) {
   1137 			if (y + softback_lines >= vc->vc_rows)
   1138 				mode = CM_ERASE;
   1139 			else
   1140 				y += softback_lines;
   1141 		}
   1142 	} else if (softback_lines)
   1143 		fbcon_set_origin(vc);
   1144 
   1145  	c = scr_readw((u16 *) vc->vc_pos);
   1146 	attribute = get_attribute(info, c);
   1147 	src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
   1148 	if (attribute) {
   1149 		u8 *dst;
   1150 
   1151 		dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
   1152 		if (!dst)
   1153 			return;
   1154 		if (info->cursor.data)
   1155 			kfree(info->cursor.data);
   1156 		info->cursor.data = dst;
   1157 		update_attr(dst, src, attribute, vc);
   1158 		src = dst;
   1159 	}
   1160 
   1161 	cursor.image.data = src;
   1162 	cursor.set = FB_CUR_SETCUR;
   1163 	cursor.image.depth = 1;
   1164 	
   1165 	switch (mode) {
   1166 	case CM_ERASE:
   1167 		if (info->cursor.rop == ROP_XOR) {
   1168 			info->cursor.enable = 0;
   1169 			info->cursor.rop = ROP_COPY;
   1170 			info->fbops->fb_cursor(info, &cursor);
   1171 		}	
   1172 		break;
   1173 	case CM_MOVE:
   1174 	case CM_DRAW:
   1175 		info->cursor.enable = 1;
   1176 		fg = get_color(vc, info, c, 1);
   1177 		bg = get_color(vc, info, c, 0);
   1178 
   1179 		if (info->cursor.image.fg_color != fg ||
   1180 		    info->cursor.image.bg_color != bg) {
   1181 			cursor.image.fg_color = fg;
   1182 			cursor.image.bg_color = bg;
   1183 			cursor.set |= FB_CUR_SETCMAP;
   1184 		}
   1185 		
   1186 		if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
   1187 		    (info->cursor.image.dy != (vc->vc_font.height * y))) {
   1188 			cursor.image.dx = vc->vc_font.width * vc->vc_x;
   1189 			cursor.image.dy = vc->vc_font.height * y;
   1190 			cursor.set |= FB_CUR_SETPOS;
   1191 		}
   1192 
   1193 		if (info->cursor.image.height != vc->vc_font.height ||
   1194 		    info->cursor.image.width != vc->vc_font.width) {
   1195 			cursor.image.height = vc->vc_font.height;
   1196 			cursor.image.width = vc->vc_font.width;
   1197 			cursor.set |= FB_CUR_SETSIZE;
   1198 		}
   1199 
   1200 		if (info->cursor.hot.x || info->cursor.hot.y) {
   1201 			cursor.hot.x = cursor.hot.y = 0;
   1202 			cursor.set |= FB_CUR_SETHOT;
   1203 		}
   1204 
   1205 		if ((cursor.set & FB_CUR_SETSIZE) ||
   1206 		    ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
   1207 		    || info->cursor.mask == NULL) {
   1208 			char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
   1209 			int cur_height, size, i = 0;
   1210 			u8 msk = 0xff;
   1211 
   1212 			if (!mask)
   1213 				return;
   1214 		
   1215 			if (info->cursor.mask)
   1216 				kfree(info->cursor.mask);
   1217 			info->cursor.mask = mask;
   1218 			p->cursor_shape = vc->vc_cursor_type & 0x0f;
   1219 			cursor.set |= FB_CUR_SETSHAPE;
   1220 
   1221 			switch (vc->vc_cursor_type & 0x0f) {
   1222 			case CUR_NONE:
   1223 				cur_height = 0;
   1224 				break;
   1225 			case CUR_UNDERLINE:
   1226 				cur_height = (vc->vc_font.height < 10) ? 1 : 2;
   1227 				break;
   1228 			case CUR_LOWER_THIRD:
   1229 				cur_height = vc->vc_font.height/3;
   1230 				break;
   1231 			case CUR_LOWER_HALF:
   1232 				cur_height = vc->vc_font.height >> 1;
   1233 				break;
   1234 			case CUR_TWO_THIRDS:
   1235 				cur_height = (vc->vc_font.height << 1)/3;
   1236 				break;
   1237 			case CUR_BLOCK:
   1238 			default:
   1239 				cur_height = vc->vc_font.height;
   1240 				break;
   1241 			}
   1242 			size = (vc->vc_font.height - cur_height) * w;
   1243 			while (size--)
   1244 				mask[i++] = ~msk;
   1245 			size = cur_height * w;
   1246 			while (size--)
   1247 				mask[i++] = msk;
   1248 		}
   1249         	info->cursor.rop = ROP_XOR;
   1250 		info->fbops->fb_cursor(info, &cursor);
   1251 		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
   1252 		break;
   1253 	}
   1254 }
   1255 
   1256 static int scrollback_phys_max = 0;
   1257 static int scrollback_max = 0;
   1258 static int scrollback_current = 0;
   1259 
   1260 int update_var(int con, struct fb_info *info)
   1261 {
   1262 	if (con == info->currcon) 
   1263 		return fb_pan_display(info, &info->var);
   1264 	return 0;
   1265 }
   1266 
   1267 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
   1268 {
   1269 	struct display *p = &fb_display[vc->vc_num], *t;
   1270 	struct vc_data **default_mode = vc->vc_display_fg;
   1271 	int display_fg = (*default_mode)->vc_num;
   1272 	int rows, cols, charcnt = 256;
   1273 
   1274 	info->var.xoffset = info->var.yoffset = p->yscroll = 0;
   1275 	if (var_to_display(p, &info->var, info))
   1276 		return;
   1277 	t = &fb_display[display_fg];
   1278 	if (!vc->vc_font.data) {
   1279 		vc->vc_font.data = p->fontdata = t->fontdata;
   1280 		vc->vc_font.width = (*default_mode)->vc_font.width;
   1281 		vc->vc_font.height = (*default_mode)->vc_font.height;
   1282 		p->userfont = t->userfont;
   1283 		if (p->userfont)
   1284 			REFCOUNT(p->fontdata)++;
   1285 		con_copy_unimap(vc->vc_num, display_fg);
   1286 	}
   1287 	if (p->userfont)
   1288 		charcnt = FNTCHARCNT(p->fontdata);
   1289 
   1290 	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
   1291 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
   1292 	if (charcnt == 256) {
   1293 		vc->vc_hi_font_mask = 0;
   1294 	} else {
   1295 		vc->vc_hi_font_mask = 0x100;
   1296 		if (vc->vc_can_do_color)
   1297 			vc->vc_complement_mask <<= 1;
   1298 	}
   1299 	cols = info->var.xres / vc->vc_font.width;
   1300 	rows = info->var.yres / vc->vc_font.height;
   1301 	vc_resize(vc->vc_num, cols, rows);
   1302 	if (CON_IS_VISIBLE(vc)) {
   1303 		update_screen(vc->vc_num);
   1304 		if (softback_buf) {
   1305 			int l = fbcon_softback_size / vc->vc_size_row;
   1306 
   1307 			if (l > 5)
   1308 				softback_end = softback_buf + l *
   1309 					vc->vc_size_row;
   1310 			else {
   1311 				/* Smaller scrollback makes no sense, and 0
   1312 				   would screw the operation totally */
   1313 				softback_top = 0;
   1314 			}
   1315 		}
   1316 	}
   1317 	switch_screen(fg_console);
   1318 }
   1319 
   1320 static __inline__ void ywrap_up(struct vc_data *vc, int count)
   1321 {
   1322 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1323 	struct display *p = &fb_display[vc->vc_num];
   1324 	
   1325 	p->yscroll += count;
   1326 	if (p->yscroll >= p->vrows)	/* Deal with wrap */
   1327 		p->yscroll -= p->vrows;
   1328 	info->var.xoffset = 0;
   1329 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1330 	info->var.vmode |= FB_VMODE_YWRAP;
   1331 	update_var(vc->vc_num, info);
   1332 	scrollback_max += count;
   1333 	if (scrollback_max > scrollback_phys_max)
   1334 		scrollback_max = scrollback_phys_max;
   1335 	scrollback_current = 0;
   1336 }
   1337 
   1338 static __inline__ void ywrap_down(struct vc_data *vc, int count)
   1339 {
   1340 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1341 	struct display *p = &fb_display[vc->vc_num];
   1342 	
   1343 	p->yscroll -= count;
   1344 	if (p->yscroll < 0)	/* Deal with wrap */
   1345 		p->yscroll += p->vrows;
   1346 	info->var.xoffset = 0;
   1347 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1348 	info->var.vmode |= FB_VMODE_YWRAP;
   1349 	update_var(vc->vc_num, info);
   1350 	scrollback_max -= count;
   1351 	if (scrollback_max < 0)
   1352 		scrollback_max = 0;
   1353 	scrollback_current = 0;
   1354 }
   1355 
   1356 static __inline__ void ypan_up(struct vc_data *vc, int count)
   1357 {
   1358 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1359 	struct display *p = &fb_display[vc->vc_num];
   1360 	
   1361 	p->yscroll += count;
   1362 	if (p->yscroll > p->vrows - vc->vc_rows) {
   1363 		accel_bmove(vc, info, p->vrows - vc->vc_rows, 
   1364 			 	0, 0, 0, vc->vc_rows, vc->vc_cols);
   1365 		p->yscroll -= p->vrows - vc->vc_rows;
   1366 	}
   1367 	info->var.xoffset = 0;
   1368 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1369 	info->var.vmode &= ~FB_VMODE_YWRAP;
   1370 	update_var(vc->vc_num, info);
   1371 	accel_clear_margins(vc, info, 1);
   1372 	scrollback_max += count;
   1373 	if (scrollback_max > scrollback_phys_max)
   1374 		scrollback_max = scrollback_phys_max;
   1375 	scrollback_current = 0;
   1376 }
   1377 
   1378 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
   1379 {
   1380 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1381 	struct display *p = &fb_display[vc->vc_num];
   1382 	int redraw = 0;
   1383 
   1384 	p->yscroll += count;
   1385 	if (p->yscroll > p->vrows - vc->vc_rows) {
   1386 		p->yscroll -= p->vrows - vc->vc_rows;
   1387 		redraw = 1;
   1388 	}
   1389 
   1390 	info->var.xoffset = 0;
   1391 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1392 	info->var.vmode &= ~FB_VMODE_YWRAP;
   1393 	if (redraw)
   1394 		fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
   1395 	update_var(vc->vc_num, info);
   1396 	accel_clear_margins(vc, info, 1);
   1397 	scrollback_max += count;
   1398 	if (scrollback_max > scrollback_phys_max)
   1399 		scrollback_max = scrollback_phys_max;
   1400 	scrollback_current = 0;
   1401 }
   1402 
   1403 static __inline__ void ypan_down(struct vc_data *vc, int count)
   1404 {
   1405 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1406 	struct display *p = &fb_display[vc->vc_num];
   1407 	
   1408 	p->yscroll -= count;
   1409 	if (p->yscroll < 0) {
   1410 		accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
   1411 			 	0, vc->vc_rows, vc->vc_cols);
   1412 		p->yscroll += p->vrows - vc->vc_rows;
   1413 	}
   1414 	info->var.xoffset = 0;
   1415 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1416 	info->var.vmode &= ~FB_VMODE_YWRAP;
   1417 	update_var(vc->vc_num, info);
   1418 	accel_clear_margins(vc, info, 1);
   1419 	scrollback_max -= count;
   1420 	if (scrollback_max < 0)
   1421 		scrollback_max = 0;
   1422 	scrollback_current = 0;
   1423 }
   1424 
   1425 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
   1426 {
   1427 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1428 	struct display *p = &fb_display[vc->vc_num];
   1429 	int redraw = 0;
   1430 
   1431 	p->yscroll -= count;
   1432 	if (p->yscroll < 0) {
   1433 		p->yscroll += p->vrows - vc->vc_rows;
   1434 		redraw = 1;
   1435 	}
   1436 	info->var.xoffset = 0;
   1437 	info->var.yoffset = p->yscroll * vc->vc_font.height;
   1438 	info->var.vmode &= ~FB_VMODE_YWRAP;
   1439 	if (redraw)
   1440 		fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
   1441 	update_var(vc->vc_num, info);
   1442 	accel_clear_margins(vc, info, 1);
   1443 	scrollback_max -= count;
   1444 	if (scrollback_max < 0)
   1445 		scrollback_max = 0;
   1446 	scrollback_current = 0;
   1447 }
   1448 
   1449 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
   1450 				  long delta)
   1451 {
   1452 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1453 	int count = vc->vc_rows;
   1454 	unsigned short *d, *s;
   1455 	unsigned long n;
   1456 	int line = 0;
   1457 
   1458 	d = (u16 *) softback_curr;
   1459 	if (d == (u16 *) softback_in)
   1460 		d = (u16 *) vc->vc_origin;
   1461 	n = softback_curr + delta * vc->vc_size_row;
   1462 	softback_lines -= delta;
   1463 	if (delta < 0) {
   1464 		if (softback_curr < softback_top && n < softback_buf) {
   1465 			n += softback_end - softback_buf;
   1466 			if (n < softback_top) {
   1467 				softback_lines -=
   1468 				    (softback_top - n) / vc->vc_size_row;
   1469 				n = softback_top;
   1470 			}
   1471 		} else if (softback_curr >= softback_top
   1472 			   && n < softback_top) {
   1473 			softback_lines -=
   1474 			    (softback_top - n) / vc->vc_size_row;
   1475 			n = softback_top;
   1476 		}
   1477 	} else {
   1478 		if (softback_curr > softback_in && n >= softback_end) {
   1479 			n += softback_buf - softback_end;
   1480 			if (n > softback_in) {
   1481 				n = softback_in;
   1482 				softback_lines = 0;
   1483 			}
   1484 		} else if (softback_curr <= softback_in && n > softback_in) {
   1485 			n = softback_in;
   1486 			softback_lines = 0;
   1487 		}
   1488 	}
   1489 	if (n == softback_curr)
   1490 		return;
   1491 	softback_curr = n;
   1492 	s = (u16 *) softback_curr;
   1493 	if (s == (u16 *) softback_in)
   1494 		s = (u16 *) vc->vc_origin;
   1495 	while (count--) {
   1496 		unsigned short *start;
   1497 		unsigned short *le;
   1498 		unsigned short c;
   1499 		int x = 0;
   1500 		unsigned short attr = 1;
   1501 
   1502 		start = s;
   1503 		le = advance_row(s, 1);
   1504 		do {
   1505 			c = scr_readw(s);
   1506 			if (attr != (c & 0xff00)) {
   1507 				attr = c & 0xff00;
   1508 				if (s > start) {
   1509 					accel_putcs(vc, info, start, s - start,
   1510 						    real_y(p, line), x);
   1511 					x += s - start;
   1512 					start = s;
   1513 				}
   1514 			}
   1515 			if (c == scr_readw(d)) {
   1516 				if (s > start) {
   1517 					accel_putcs(vc, info, start, s - start,
   1518 						    real_y(p, line), x);
   1519 					x += s - start + 1;
   1520 					start = s + 1;
   1521 				} else {
   1522 					x++;
   1523 					start++;
   1524 				}
   1525 			}
   1526 			s++;
   1527 			d++;
   1528 		} while (s < le);
   1529 		if (s > start)
   1530 			accel_putcs(vc, info, start, s - start,
   1531 				    real_y(p, line), x);
   1532 		line++;
   1533 		if (d == (u16 *) softback_end)
   1534 			d = (u16 *) softback_buf;
   1535 		if (d == (u16 *) softback_in)
   1536 			d = (u16 *) vc->vc_origin;
   1537 		if (s == (u16 *) softback_end)
   1538 			s = (u16 *) softback_buf;
   1539 		if (s == (u16 *) softback_in)
   1540 			s = (u16 *) vc->vc_origin;
   1541 	}
   1542 }
   1543 
   1544 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
   1545 			      int line, int count, int dy)
   1546 {
   1547 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1548 	unsigned short *s = (unsigned short *)
   1549 		(vc->vc_origin + vc->vc_size_row * line);
   1550 
   1551 	while (count--) {
   1552 		unsigned short *start = s;
   1553 		unsigned short *le = advance_row(s, 1);
   1554 		unsigned short c;
   1555 		int x = 0;
   1556 		unsigned short attr = 1;
   1557 
   1558 		do {
   1559 			c = scr_readw(s);
   1560 			if (attr != (c & 0xff00)) {
   1561 				attr = c & 0xff00;
   1562 				if (s > start) {
   1563 					accel_putcs(vc, info, start, s - start,
   1564 						    real_y(p, dy), x);
   1565 					x += s - start;
   1566 					start = s;
   1567 				}
   1568 			}
   1569 			console_conditional_schedule();
   1570 			s++;
   1571 		} while (s < le);
   1572 		if (s > start)
   1573 			accel_putcs(vc, info, start, s - start,
   1574 				    real_y(p, dy), x);
   1575 		console_conditional_schedule();
   1576 		dy++;
   1577 	}
   1578 }
   1579 
   1580 static void fbcon_redraw(struct vc_data *vc, struct display *p,
   1581 			 int line, int count, int offset)
   1582 {
   1583 	unsigned short *d = (unsigned short *)
   1584 	    (vc->vc_origin + vc->vc_size_row * line);
   1585 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1586 	unsigned short *s = d + offset;
   1587 
   1588 	while (count--) {
   1589 		unsigned short *start = s;
   1590 		unsigned short *le = advance_row(s, 1);
   1591 		unsigned short c;
   1592 		int x = 0;
   1593 		unsigned short attr = 1;
   1594 
   1595 		do {
   1596 			c = scr_readw(s);
   1597 			if (attr != (c & 0xff00)) {
   1598 				attr = c & 0xff00;
   1599 				if (s > start) {
   1600 					accel_putcs(vc, info, start, s - start,
   1601 						    real_y(p, line), x);
   1602 					x += s - start;
   1603 					start = s;
   1604 				}
   1605 			}
   1606 			if (c == scr_readw(d)) {
   1607 				if (s > start) {
   1608 					accel_putcs(vc, info, start, s - start,
   1609 						    real_y(p, line), x);
   1610 					x += s - start + 1;
   1611 					start = s + 1;
   1612 				} else {
   1613 					x++;
   1614 					start++;
   1615 				}
   1616 			}
   1617 			scr_writew(c, d);
   1618 			console_conditional_schedule();
   1619 			s++;
   1620 			d++;
   1621 		} while (s < le);
   1622 		if (s > start)
   1623 			accel_putcs(vc, info, start, s - start,
   1624 				    real_y(p, line), x);
   1625 		console_conditional_schedule();
   1626 		if (offset > 0)
   1627 			line++;
   1628 		else {
   1629 			line--;
   1630 			/* NOTE: We subtract two lines from these pointers */
   1631 			s -= vc->vc_size_row;
   1632 			d -= vc->vc_size_row;
   1633 		}
   1634 	}
   1635 }
   1636 
   1637 static inline void fbcon_softback_note(struct vc_data *vc, int t,
   1638 				       int count)
   1639 {
   1640 	unsigned short *p;
   1641 
   1642 	if (vc->vc_num != fg_console)
   1643 		return;
   1644 	p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
   1645 
   1646 	while (count) {
   1647 		scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
   1648 		count--;
   1649 		p = advance_row(p, 1);
   1650 		softback_in += vc->vc_size_row;
   1651 		if (softback_in == softback_end)
   1652 			softback_in = softback_buf;
   1653 		if (softback_in == softback_top) {
   1654 			softback_top += vc->vc_size_row;
   1655 			if (softback_top == softback_end)
   1656 				softback_top = softback_buf;
   1657 		}
   1658 	}
   1659 	softback_curr = softback_in;
   1660 }
   1661 
   1662 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
   1663 			int count)
   1664 {
   1665 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1666 	struct display *p = &fb_display[vc->vc_num];
   1667 	int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
   1668 
   1669 	if (!info->fbops->fb_blank && console_blanked)
   1670 		return 0;
   1671 
   1672 	if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
   1673 		return 0;
   1674 
   1675 	fbcon_cursor(vc, CM_ERASE);
   1676 
   1677 	/*
   1678 	 * ++Geert: Only use ywrap/ypan if the console is in text mode
   1679 	 * ++Andrew: Only use ypan on hardware text mode when scrolling the
   1680 	 *           whole screen (prevents flicker).
   1681 	 */
   1682 
   1683 	switch (dir) {
   1684 	case SM_UP:
   1685 		if (count > vc->vc_rows)	/* Maximum realistic size */
   1686 			count = vc->vc_rows;
   1687 		if (softback_top)
   1688 			fbcon_softback_note(vc, t, count);
   1689 		if (logo_shown >= 0)
   1690 			goto redraw_up;
   1691 		switch (p->scrollmode) {
   1692 		case SCROLL_MOVE:
   1693 			accel_bmove(vc, info, t + count, 0, t, 0,
   1694 					 b - t - count, vc->vc_cols);
   1695 			accel_clear(vc, info, b - count, 0, count,
   1696 					 vc->vc_cols);
   1697 			break;
   1698 
   1699 		case SCROLL_WRAP_MOVE:
   1700 			if (b - t - count > 3 * vc->vc_rows >> 2) {
   1701 				if (t > 0)
   1702 					fbcon_bmove(vc, 0, 0, count, 0, t,
   1703 						    vc->vc_cols);
   1704 				ywrap_up(vc, count);
   1705 				if (vc->vc_rows - b > 0)
   1706 					fbcon_bmove(vc, b - count, 0, b, 0,
   1707 						    vc->vc_rows - b,
   1708 						    vc->vc_cols);
   1709 			} else if (info->flags & FBINFO_READS_FAST)
   1710 				fbcon_bmove(vc, t + count, 0, t, 0,
   1711 					    b - t - count, vc->vc_cols);
   1712 			else
   1713 				goto redraw_up;
   1714 			fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
   1715 			break;
   1716 
   1717 		case SCROLL_PAN_REDRAW:
   1718 			if ((p->yscroll + count <=
   1719 			     2 * (p->vrows - vc->vc_rows))
   1720 			    && ((!scroll_partial && (b - t == vc->vc_rows))
   1721 				|| (scroll_partial
   1722 				    && (b - t - count >
   1723 					3 * vc->vc_rows >> 2)))) {
   1724 				if (t > 0)
   1725 					fbcon_redraw_move(vc, p, 0, t, count);
   1726 				ypan_up_redraw(vc, t, count);
   1727 				if (vc->vc_rows - b > 0)
   1728 					fbcon_redraw_move(vc, p, b - count,
   1729 							  vc->vc_rows - b, b);
   1730 			} else
   1731 				fbcon_redraw_move(vc, p, t + count, b - t - count, t);
   1732 			fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
   1733 			break;
   1734 
   1735 		case SCROLL_PAN_MOVE:
   1736 			if ((p->yscroll + count <=
   1737 			     2 * (p->vrows - vc->vc_rows))
   1738 			    && ((!scroll_partial && (b - t == vc->vc_rows))
   1739 				|| (scroll_partial
   1740 				    && (b - t - count >
   1741 					3 * vc->vc_rows >> 2)))) {
   1742 				if (t > 0)
   1743 					fbcon_bmove(vc, 0, 0, count, 0, t,
   1744 						    vc->vc_cols);
   1745 				ypan_up(vc, count);
   1746 				if (vc->vc_rows - b > 0)
   1747 					fbcon_bmove(vc, b - count, 0, b, 0,
   1748 						    vc->vc_rows - b,
   1749 						    vc->vc_cols);
   1750 			} else if (info->flags & FBINFO_READS_FAST)
   1751 				fbcon_bmove(vc, t + count, 0, t, 0,
   1752 					    b - t - count, vc->vc_cols);
   1753 			else
   1754 				goto redraw_up;
   1755 			fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
   1756 			break;
   1757 
   1758 		case SCROLL_REDRAW:
   1759 		      redraw_up:
   1760 			fbcon_redraw(vc, p, t, b - t - count,
   1761 				     count * vc->vc_cols);
   1762 			accel_clear(vc, info, real_y(p, b - count), 0,
   1763 					 count, vc->vc_cols);
   1764 			scr_memsetw((unsigned short *) (vc->vc_origin +
   1765 							vc->vc_size_row *
   1766 							(b - count)),
   1767 				    vc->vc_video_erase_char,
   1768 				    vc->vc_size_row * count);
   1769 			return 1;
   1770 		}
   1771 		break;
   1772 
   1773 	case SM_DOWN:
   1774 		if (count > vc->vc_rows)	/* Maximum realistic size */
   1775 			count = vc->vc_rows;
   1776 		switch (p->scrollmode) {
   1777 		case SCROLL_MOVE:
   1778 			accel_bmove(vc, info, t, 0, t + count, 0,
   1779 					 b - t - count, vc->vc_cols);
   1780 			accel_clear(vc, info, t, 0, count, vc->vc_cols);
   1781 			break;
   1782 
   1783 		case SCROLL_WRAP_MOVE:
   1784 			if (b - t - count > 3 * vc->vc_rows >> 2) {
   1785 				if (vc->vc_rows - b > 0)
   1786 					fbcon_bmove(vc, b, 0, b - count, 0,
   1787 						    vc->vc_rows - b,
   1788 						    vc->vc_cols);
   1789 				ywrap_down(vc, count);
   1790 				if (t > 0)
   1791 					fbcon_bmove(vc, count, 0, 0, 0, t,
   1792 						    vc->vc_cols);
   1793 			} else if (info->flags & FBINFO_READS_FAST)
   1794 				fbcon_bmove(vc, t, 0, t + count, 0,
   1795 					    b - t - count, vc->vc_cols);
   1796 			else
   1797 				goto redraw_down;
   1798 			fbcon_clear(vc, t, 0, count, vc->vc_cols);
   1799 			break;
   1800 
   1801 		case SCROLL_PAN_MOVE:
   1802 			if ((count - p->yscroll <= p->vrows - vc->vc_rows)
   1803 			    && ((!scroll_partial && (b - t == vc->vc_rows))
   1804 				|| (scroll_partial
   1805 				    && (b - t - count >
   1806 					3 * vc->vc_rows >> 2)))) {
   1807 				if (vc->vc_rows - b > 0)
   1808 					fbcon_bmove(vc, b, 0, b - count, 0,
   1809 						    vc->vc_rows - b,
   1810 						    vc->vc_cols);
   1811 				ypan_down(vc, count);
   1812 				if (t > 0)
   1813 					fbcon_bmove(vc, count, 0, 0, 0, t,
   1814 						    vc->vc_cols);
   1815 			} else if (info->flags & FBINFO_READS_FAST)
   1816 				fbcon_bmove(vc, t, 0, t + count, 0,
   1817 					    b - t - count, vc->vc_cols);
   1818 			else
   1819 				goto redraw_down;
   1820 			fbcon_clear(vc, t, 0, count, vc->vc_cols);
   1821 			break;
   1822 
   1823 		case SCROLL_PAN_REDRAW:
   1824 			if ((count - p->yscroll <= p->vrows - vc->vc_rows)
   1825 			    && ((!scroll_partial && (b - t == vc->vc_rows))
   1826 				|| (scroll_partial
   1827 				    && (b - t - count >
   1828 					3 * vc->vc_rows >> 2)))) {
   1829 				if (vc->vc_rows - b > 0)
   1830 					fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
   1831 							  b - count);
   1832 				ypan_down_redraw(vc, t, count);
   1833 				if (t > 0)
   1834 					fbcon_redraw_move(vc, p, count, t, 0);
   1835 			} else
   1836 				fbcon_redraw_move(vc, p, t, b - t - count, t + count);
   1837 			fbcon_clear(vc, t, 0, count, vc->vc_cols);
   1838 			break;
   1839 
   1840 		case SCROLL_REDRAW:
   1841 		      redraw_down:
   1842 			fbcon_redraw(vc, p, b - 1, b - t - count,
   1843 				     -count * vc->vc_cols);
   1844 			accel_clear(vc, info, real_y(p, t), 0, count,
   1845 					 vc->vc_cols);
   1846 			scr_memsetw((unsigned short *) (vc->vc_origin +
   1847 							vc->vc_size_row *
   1848 							t),
   1849 				    vc->vc_video_erase_char,
   1850 				    vc->vc_size_row * count);
   1851 			return 1;
   1852 		}
   1853 	}
   1854 	return 0;
   1855 }
   1856 
   1857 
   1858 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
   1859 			int height, int width)
   1860 {
   1861 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1862 	struct display *p = &fb_display[vc->vc_num];
   1863 	
   1864 	if (!info->fbops->fb_blank && console_blanked)
   1865 		return;
   1866 
   1867 	if (!width || !height)
   1868 		return;
   1869 
   1870 	/*  Split blits that cross physical y_wrap case.
   1871 	 *  Pathological case involves 4 blits, better to use recursive
   1872 	 *  code rather than unrolled case
   1873 	 *
   1874 	 *  Recursive invocations don't need to erase the cursor over and
   1875 	 *  over again, so we use fbcon_bmove_rec()
   1876 	 */
   1877 	fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
   1878 			p->vrows - p->yscroll);
   1879 }
   1880 
   1881 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, 
   1882 			    int dy, int dx, int height, int width, u_int y_break)
   1883 {
   1884 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1885 	u_int b;
   1886 
   1887 	if (sy < y_break && sy + height > y_break) {
   1888 		b = y_break - sy;
   1889 		if (dy < sy) {	/* Avoid trashing self */
   1890 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
   1891 					y_break);
   1892 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
   1893 					height - b, width, y_break);
   1894 		} else {
   1895 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
   1896 					height - b, width, y_break);
   1897 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
   1898 					y_break);
   1899 		}
   1900 		return;
   1901 	}
   1902 
   1903 	if (dy < y_break && dy + height > y_break) {
   1904 		b = y_break - dy;
   1905 		if (dy < sy) {	/* Avoid trashing self */
   1906 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
   1907 					y_break);
   1908 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
   1909 					height - b, width, y_break);
   1910 		} else {
   1911 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
   1912 					height - b, width, y_break);
   1913 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
   1914 					y_break);
   1915 		}
   1916 		return;
   1917 	}
   1918 	accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
   1919 			height, width);
   1920 }
   1921 
   1922 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
   1923 					struct vc_data *vc)
   1924 {
   1925 	int fh = vc->vc_font.height;
   1926 	int cap = info->flags;
   1927 	int good_pan = (cap & FBINFO_HWACCEL_YPAN)
   1928 		 && divides(info->fix.ypanstep, vc->vc_font.height)
   1929 		 && info->var.yres_virtual > info->var.yres;
   1930 	int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
   1931 		 && divides(info->fix.ywrapstep, vc->vc_font.height)
   1932 		 && divides(vc->vc_font.height, info->var.yres_virtual);
   1933 	int reading_fast = cap & FBINFO_READS_FAST;
   1934 	int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
   1935 	int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED);
   1936 
   1937 	p->vrows = info->var.yres_virtual/fh;
   1938 	if (info->var.yres > (fh * (vc->vc_rows + 1)))
   1939 		p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh;
   1940 	if ((info->var.yres % fh) && (info->var.yres_virtual % fh <
   1941 				      info->var.yres % fh))
   1942 		p->vrows--;
   1943 
   1944 	if (good_wrap || good_pan) {
   1945 		if (reading_fast || fast_copyarea)
   1946 			p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
   1947 		else
   1948 			p->scrollmode = good_wrap ? SCROLL_REDRAW :
   1949 				SCROLL_PAN_REDRAW;
   1950 	} else {
   1951 		if (reading_fast || (fast_copyarea && !fast_imageblit))
   1952 			p->scrollmode = SCROLL_MOVE;
   1953 		else
   1954 			p->scrollmode = SCROLL_REDRAW;
   1955 	}
   1956 }
   1957 
   1958 static int fbcon_resize(struct vc_data *vc, unsigned int width, 
   1959 			unsigned int height)
   1960 {
   1961 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   1962 	struct display *p = &fb_display[vc->vc_num];
   1963 	struct fb_var_screeninfo var = info->var;
   1964 	int x_diff, y_diff;
   1965 	int fw = vc->vc_font.width;
   1966 	int fh = vc->vc_font.height;
   1967 
   1968 	var.xres = width * fw;
   1969 	var.yres = height * fh;
   1970 	x_diff = info->var.xres - var.xres;
   1971 	y_diff = info->var.yres - var.yres;
   1972 	if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
   1973 		struct fb_videomode *mode;
   1974 
   1975 		DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
   1976 		mode = fb_find_best_mode(&var, &info->modelist);
   1977 		if (mode == NULL)
   1978 			return -EINVAL;
   1979 		fb_videomode_to_var(&var, mode);
   1980 		if (width > var.xres/fw || height > var.yres/fh)
   1981 			return -EINVAL;
   1982 		/*
   1983 		 * The following can probably have any value... Do we need to
   1984 		 * set all of them?
   1985 		 */
   1986 		var.bits_per_pixel = p->bits_per_pixel;
   1987 		var.xres_virtual = p->xres_virtual;
   1988 		var.yres_virtual = p->yres_virtual;
   1989 		var.accel_flags = p->accel_flags;
   1990 		var.width = p->width;
   1991 		var.height = p->height;
   1992 		var.red = p->red;
   1993 		var.green = p->green;
   1994 		var.blue = p->blue;
   1995 		var.transp = p->transp;
   1996 		var.nonstd = p->nonstd;
   1997 
   1998 		DPRINTK("resize now %ix%i\n", var.xres, var.yres);
   1999 		if (CON_IS_VISIBLE(vc)) {
   2000 			var.activate = FB_ACTIVATE_NOW |
   2001 				FB_ACTIVATE_FORCE;
   2002 			fb_set_var(info, &var);
   2003 			info->flags &= ~FBINFO_MISC_MODESWITCH;
   2004 		}
   2005 		var_to_display(p, &info->var, info);
   2006 	}
   2007 	updatescrollmode(p, info, vc);
   2008 	return 0;
   2009 }
   2010 
   2011 static int fbcon_switch(struct vc_data *vc)
   2012 {
   2013 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   2014 	struct display *p = &fb_display[vc->vc_num];
   2015 	struct fb_var_screeninfo var;
   2016 	int i;
   2017 
   2018 	if (softback_top) {
   2019 		int l = fbcon_softback_size / vc->vc_size_row;
   2020 		if (softback_lines)
   2021 			fbcon_set_origin(vc);
   2022 		softback_top = softback_curr = softback_in = softback_buf;
   2023 		softback_lines = 0;
   2024 
   2025 		if (l > 5)
   2026 			softback_end = softback_buf + l * vc->vc_size_row;
   2027 		else {
   2028 			/* Smaller scrollback makes no sense, and 0 would screw
   2029 			   the operation totally */
   2030 			softback_top = 0;
   2031 		}
   2032 	}
   2033 	if (logo_shown >= 0) {
   2034 		struct vc_data *conp2 = vc_cons[logo_shown].d;
   2035 
   2036 		if (conp2->vc_top == logo_lines
   2037 		    && conp2->vc_bottom == conp2->vc_rows)
   2038 			conp2->vc_top = 0;
   2039 		logo_shown = -1;
   2040 	}
   2041 
   2042 #ifdef CONFIG_FB_UI
   2043         fbui_switch (info, vc->vc_num);
   2044 #endif
   2045 
   2046 	/*
   2047 	 * FIXME: If we have multiple fbdev's loaded, we need to
   2048 	 * update all info->currcon.  Perhaps, we can place this
   2049 	 * in a centralized structure, but this might break some
   2050 	 * drivers.
   2051 	 *
   2052 	 * info->currcon = vc->vc_num;
   2053 	 */
   2054 	for (i = 0; i < FB_MAX; i++) {
   2055 		if (registered_fb[i] != NULL)
   2056 			registered_fb[i]->currcon = vc->vc_num;
   2057 	}
   2058 
   2059 	memset(&var, 0, sizeof(struct fb_var_screeninfo));
   2060 	fb_videomode_to_var(&var, p->mode);
   2061 	display_to_var(&var, p);
   2062 	var.activate = FB_ACTIVATE_NOW;
   2063 
   2064 	/*
   2065 	 * make sure we don't unnecessarily trip the memcmp()
   2066 	 * in fb_set_var()
   2067 	 */
   2068 	info->var.activate = var.activate;
   2069 	info->var.yoffset = info->var.xoffset = p->yscroll = 0;
   2070 	fb_set_var(info, &var);
   2071 
   2072 	if (info->flags & FBINFO_MISC_MODESWITCH) {
   2073 		if (info->fbops->fb_set_par)
   2074 			info->fbops->fb_set_par(info);
   2075 		info->flags &= ~FBINFO_MISC_MODESWITCH;
   2076 	}
   2077 
   2078 	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
   2079 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
   2080 	updatescrollmode(p, info, vc);
   2081 
   2082 	switch (p->scrollmode) {
   2083 	case SCROLL_WRAP_MOVE:
   2084 		scrollback_phys_max = p->vrows - vc->vc_rows;
   2085 		break;
   2086 	case SCROLL_PAN_MOVE:
   2087 	case SCROLL_PAN_REDRAW:
   2088 		scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
   2089 		if (scrollback_phys_max < 0)
   2090 			scrollback_phys_max = 0;
   2091 		break;
   2092 	default:
   2093 		scrollback_phys_max = 0;
   2094 		break;
   2095 	}
   2096 	scrollback_max = 0;
   2097 	scrollback_current = 0;
   2098 
   2099 	update_var(vc->vc_num, info);
   2100 	fbcon_set_palette(vc, color_table); 	
   2101 
   2102 	if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
   2103 		accel_clear_margins(vc, info, 0);
   2104 	if (logo_shown == -2) {
   2105 		logo_shown = fg_console;
   2106 		/* This is protected above by initmem_freed */
   2107 		fb_show_logo(info);
   2108 		update_region(fg_console,
   2109 			      vc->vc_origin + vc->vc_size_row * vc->vc_top,
   2110 			      vc->vc_size_row * (vc->vc_bottom -
   2111 						 vc->vc_top) / 2);
   2112 		return 0;
   2113 	}
   2114 	return 1;
   2115 }
   2116 
   2117 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
   2118 {
   2119 	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
   2120 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   2121 	struct display *p = &fb_display[vc->vc_num];
   2122 
   2123 	if (mode_switch) {
   2124 		struct fb_var_screeninfo var = info->var;
   2125 
   2126 /*
   2127  * HACK ALERT: Some hardware will require reinitializion at this stage,
   2128  *             others will require it to be done as late as possible.
   2129  *             For now, we differentiate this with the
   2130  *             FBINFO_MISC_MODESWITCHLATE bitflag.  Worst case will be
   2131  *             hardware that requires it here and another one later.
   2132  *             A definitive solution may require fixing X or the VT
   2133  *             system.
   2134  */
   2135 		if (info->flags & FBINFO_MISC_MODESWITCHLATE)
   2136 			info->flags |= FBINFO_MISC_MODESWITCH;
   2137 
   2138 		if (blank) {
   2139 			fbcon_cursor(vc, CM_ERASE);
   2140 			return 0;
   2141 		}
   2142 
   2143 		if (!(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
   2144 			var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
   2145 			fb_set_var(info, &var);
   2146 		}
   2147 	}
   2148 
   2149 	fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
   2150 
   2151 	if (!info->fbops->fb_blank) {
   2152 		if (blank) {
   2153 			unsigned short oldc;
   2154 			u_int height;
   2155 			u_int y_break;
   2156 
   2157 			oldc = vc->vc_video_erase_char;
   2158 			vc->vc_video_erase_char &= charmask;
   2159 			height = vc->vc_rows;
   2160 			y_break = p->vrows - p->yscroll;
   2161 			if (height > y_break) {
   2162 				accel_clear(vc, info, real_y(p, 0),
   2163 					    0, y_break, vc->vc_cols);
   2164 				accel_clear(vc, info, real_y(p, y_break),
   2165 					    0, height - y_break, 
   2166 					    vc->vc_cols);
   2167 			} else
   2168 				accel_clear(vc, info, real_y(p, 0),
   2169 					    0, height, vc->vc_cols);
   2170 			vc->vc_video_erase_char = oldc;
   2171 		} else
   2172 			update_screen(vc->vc_num);
   2173 		return 0;
   2174 	} else
   2175 		return fb_blank(info, blank);
   2176 }
   2177 
   2178 static void fbcon_free_font(struct display *p)
   2179 {
   2180 	if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
   2181 		kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
   2182 	p->fontdata = NULL;
   2183 	p->userfont = 0;
   2184 }
   2185 
   2186 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
   2187 {
   2188 	u8 *fontdata = vc->vc_font.data;
   2189 	u8 *data = font->data;
   2190 	int i, j;
   2191 
   2192 	font->width = vc->vc_font.width;
   2193 	font->height = vc->vc_font.height;
   2194 	font->charcount = vc->vc_hi_font_mask ? 512 : 256;
   2195 	if (!font->data)
   2196 		return 0;
   2197 
   2198 	if (font->width <= 8) {
   2199 		j = vc->vc_font.height;
   2200 		for (i = 0; i < font->charcount; i++) {
   2201 			memcpy(data, fontdata, j);
   2202 			memset(data + j, 0, 32 - j);
   2203 			data += 32;
   2204 			fontdata += j;
   2205 		}
   2206 	} else if (font->width <= 16) {
   2207 		j = vc->vc_font.height * 2;
   2208 		for (i = 0; i < font->charcount; i++) {
   2209 			memcpy(data, fontdata, j);
   2210 			memset(data + j, 0, 64 - j);
   2211 			data += 64;
   2212 			fontdata += j;
   2213 		}
   2214 	} else if (font->width <= 24) {
   2215 		for (i = 0; i < font->charcount; i++) {
   2216 			for (j = 0; j < vc->vc_font.height; j++) {
   2217 				*data++ = fontdata[0];
   2218 				*data++ = fontdata[1];
   2219 				*data++ = fontdata[2];
   2220 				fontdata += sizeof(u32);
   2221 			}
   2222 			memset(data, 0, 3 * (32 - j));
   2223 			data += 3 * (32 - j);
   2224 		}
   2225 	} else {
   2226 		j = vc->vc_font.height * 4;
   2227 		for (i = 0; i < font->charcount; i++) {
   2228 			memcpy(data, fontdata, j);
   2229 			memset(data + j, 0, 128 - j);
   2230 			data += 128;
   2231 			fontdata += j;
   2232 		}
   2233 	}
   2234 	return 0;
   2235 }
   2236 
   2237 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
   2238 			     u8 * data, int userfont)
   2239 {
   2240 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   2241 	struct display *p = &fb_display[vc->vc_num];
   2242 	int resize;
   2243 	int cnt;
   2244 	char *old_data = NULL;
   2245 
   2246 	if (CON_IS_VISIBLE(vc) && softback_lines)
   2247 		fbcon_set_origin(vc);
   2248 
   2249 	resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
   2250 	if (p->userfont)
   2251 		old_data = vc->vc_font.data;
   2252 	if (userfont)
   2253 		cnt = FNTCHARCNT(data);
   2254 	else
   2255 		cnt = 256;
   2256 	vc->vc_font.data = p->fontdata = data;
   2257 	if ((p->userfont = userfont))
   2258 		REFCOUNT(data)++;
   2259 	vc->vc_font.width = w;
   2260 	vc->vc_font.height = h;
   2261 	if (vc->vc_hi_font_mask && cnt == 256) {
   2262 		vc->vc_hi_font_mask = 0;
   2263 		if (vc->vc_can_do_color) {
   2264 			vc->vc_complement_mask >>= 1;
   2265 			vc->vc_s_complement_mask >>= 1;
   2266 		}
   2267 			
   2268 		/* ++Edmund: reorder the attribute bits */
   2269 		if (vc->vc_can_do_color) {
   2270 			unsigned short *cp =
   2271 			    (unsigned short *) vc->vc_origin;
   2272 			int count = vc->vc_screenbuf_size / 2;
   2273 			unsigned short c;
   2274 			for (; count > 0; count--, cp++) {
   2275 				c = scr_readw(cp);
   2276 				scr_writew(((c & 0xfe00) >> 1) |
   2277 					   (c & 0xff), cp);
   2278 			}
   2279 			c = vc->vc_video_erase_char;
   2280 			vc->vc_video_erase_char =
   2281 			    ((c & 0xfe00) >> 1) | (c & 0xff);
   2282 			vc->vc_attr >>= 1;
   2283 		}
   2284 	} else if (!vc->vc_hi_font_mask && cnt == 512) {
   2285 		vc->vc_hi_font_mask = 0x100;
   2286 		if (vc->vc_can_do_color) {
   2287 			vc->vc_complement_mask <<= 1;
   2288 			vc->vc_s_complement_mask <<= 1;
   2289 		}
   2290 			
   2291 		/* ++Edmund: reorder the attribute bits */
   2292 		{
   2293 			unsigned short *cp =
   2294 			    (unsigned short *) vc->vc_origin;
   2295 			int count = vc->vc_screenbuf_size / 2;
   2296 			unsigned short c;
   2297 			for (; count > 0; count--, cp++) {
   2298 				unsigned short newc;
   2299 				c = scr_readw(cp);
   2300 				if (vc->vc_can_do_color)
   2301 					newc =
   2302 					    ((c & 0xff00) << 1) | (c &
   2303 								   0xff);
   2304 				else
   2305 					newc = c & ~0x100;
   2306 				scr_writew(newc, cp);
   2307 			}
   2308 			c = vc->vc_video_erase_char;
   2309 			if (vc->vc_can_do_color) {
   2310 				vc->vc_video_erase_char =
   2311 				    ((c & 0xff00) << 1) | (c & 0xff);
   2312 				vc->vc_attr <<= 1;
   2313 			} else
   2314 				vc->vc_video_erase_char = c & ~0x100;
   2315 		}
   2316 
   2317 	}
   2318 
   2319 	if (resize) {
   2320 		/* reset wrap/pan */
   2321 		info->var.xoffset = info->var.yoffset = p->yscroll = 0;
   2322 		vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
   2323 		if (CON_IS_VISIBLE(vc) && softback_buf) {
   2324 			int l = fbcon_softback_size / vc->vc_size_row;
   2325 			if (l > 5)
   2326 				softback_end =
   2327 				    softback_buf + l * vc->vc_size_row;
   2328 			else {
   2329 				/* Smaller scrollback makes no sense, and 0 would screw
   2330 				   the operation totally */
   2331 				softback_top = 0;
   2332 			}
   2333 		}
   2334 	} else if (CON_IS_VISIBLE(vc)
   2335 		   && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
   2336 		accel_clear_margins(vc, info, 0);
   2337 		update_screen(vc->vc_num);
   2338 	}
   2339 
   2340 	if (old_data && (--REFCOUNT(old_data) == 0))
   2341 		kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
   2342 	return 0;
   2343 }
   2344 
   2345 static int fbcon_copy_font(struct vc_data *vc, int con)
   2346 {
   2347 	struct display *od = &fb_display[con];
   2348 	struct console_font *f = &vc->vc_font;
   2349 
   2350 	if (od->fontdata == f->data)
   2351 		return 0;	/* already the same font... */
   2352 	return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
   2353 }
   2354 
   2355 /*
   2356  *  User asked to set font; we are guaranteed that
   2357  *	a) width and height are in range 1..32
   2358  *	b) charcount does not exceed 512
   2359  */
   2360 
   2361 static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
   2362 {
   2363 	unsigned charcount = font->charcount;
   2364 	int w = font->width;
   2365 	int h = font->height;
   2366 	int size = h;
   2367 	int i, k;
   2368 	u8 *new_data, *data = font->data, *p;
   2369 
   2370 	if (charcount != 256 && charcount != 512)
   2371 		return -EINVAL;
   2372 
   2373 	if (w > 8) {
   2374 		if (w <= 16)
   2375 			size *= 2;
   2376 		else
   2377 			size *= 4;
   2378 	}
   2379 	size *= charcount;
   2380 
   2381 	new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
   2382 
   2383 	if (!new_data)
   2384 		return -ENOMEM;
   2385 
   2386 	new_data += FONT_EXTRA_WORDS * sizeof(int);
   2387 	FNTSIZE(new_data) = size;
   2388 	FNTCHARCNT(new_data) = charcount;
   2389 	REFCOUNT(new_data) = 0;	/* usage counter */
   2390 	p = new_data;
   2391 	if (w <= 8) {
   2392 		for (i = 0; i < charcount; i++) {
   2393 			memcpy(p, data, h);
   2394 			data += 32;
   2395 			p += h;
   2396 		}
   2397 	} else if (w <= 16) {
   2398 		h *= 2;
   2399 		for (i = 0; i < charcount; i++) {
   2400 			memcpy(p, data, h);
   2401 			data += 64;
   2402 			p += h;
   2403 		}
   2404 	} else if (w <= 24) {
   2405 		for (i = 0; i < charcount; i++) {
   2406 			int j;
   2407 			for (j = 0; j < h; j++) {
   2408 				memcpy(p, data, 3);
   2409 				p[3] = 0;
   2410 				data += 3;
   2411 				p += sizeof(u32);
   2412 			}
   2413 			data += 3 * (32 - h);
   2414 		}
   2415 	} else {
   2416 		h *= 4;
   2417 		for (i = 0; i < charcount; i++) {
   2418 			memcpy(p, data, h);
   2419 			data += 128;
   2420 			p += h;
   2421 		}
   2422 	}
   2423 	/* we can do it in u32 chunks because of charcount is 256 or 512, so
   2424 	   font length must be multiple of 256, at least. And 256 is multiple
   2425 	   of 4 */
   2426 	k = 0;
   2427 	while (p > new_data) {
   2428 		p = (u8 *)((u32 *)p - 1);
   2429 		k += *(u32 *) p;
   2430 	}
   2431 	FNTSUM(new_data) = k;
   2432 	/* Check if the same font is on some other console already */
   2433 	for (i = 0; i < MAX_NR_CONSOLES; i++) {
   2434 		struct vc_data *tmp = vc_cons[i].d;
   2435 		
   2436 		if (fb_display[i].userfont &&
   2437 		    fb_display[i].fontdata &&
   2438 		    FNTSUM(fb_display[i].fontdata) == k &&
   2439 		    FNTSIZE(fb_display[i].fontdata) == size &&
   2440 		    tmp->vc_font.width == w &&
   2441 		    !memcmp(fb_display[i].fontdata, new_data, size)) {
   2442 			kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
   2443 			new_data = fb_display[i].fontdata;
   2444 			break;
   2445 		}
   2446 	}
   2447 	return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
   2448 }
   2449 
   2450 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
   2451 {
   2452 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   2453 	struct font_desc *f;
   2454 
   2455 	if (!name)
   2456 		f = get_default_font(info->var.xres, info->var.yres);
   2457 	else if (!(f = find_font(name)))
   2458 		return -ENOENT;
   2459 
   2460 	font->width = f->width;
   2461 	font->height = f->height;
   2462 	return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
   2463 }
   2464 
   2465 static u16 palette_red[16];
   2466 static u16 palette_green[16];
   2467 static u16 palette_blue[16];
   2468 
   2469 static struct fb_cmap palette_cmap = {
   2470 	0, 16, palette_red, palette_green, palette_blue, NULL
   2471 };
   2472 
   2473 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
   2474 {
   2475 	struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
   2476 	int i, j, k, depth;
   2477 	u8 val;
   2478 
   2479 	if (!info->fbops->fb_blank && console_blanked)
   2480 		return -EINVAL;
   2481 	depth = fb_get_color_depth(info);
   2482 	if (depth > 3) {
   2483 		for (i = j = 0; i < 16; i++) {
   2484 			k = table[i];
   2485 			val = vc->vc_palette[j++];
   2486 			palette_red[k] = (val << 8) | val;
   2487 			val = vc->vc_palette[j++];
   2488 			palette_green[k] = (val << 8) | val;
   2489 			val = vc->vc_palette[j++];
   2490 			palette_blue[k] = (val << 8) | val;
   2491 		}
   2492 		palette_cmap.len = 16;
   2493 		palette_cmap.start = 0;
   2494 	/*
   2495 	 * If framebuffer is capable of less than 16 colors,
   2496 	 * use default palette of fbcon.
   2497 	 */
   2498 	} else
   2499 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
   2500 
   2501 	return fb_set_cmap(&palette_cmap, info);
   2502 }
   2503 
   2504 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
   2505 {
   2506 	unsigned long p;
   2507 	int line;
   2508 	
   2509 	if (vc->vc_num != fg_console || !softback_lines)
   2510 		return (u16 *) (vc->vc_origin + offset);
   2511 	line = offset / vc->vc_size_row;
   2512 	if (line >= softback_lines)
   2513 		return (u16 *) (vc->vc_origin + offset -
   2514 				softback_lines * vc->vc_size_row);
   2515 	p = softback_curr + offset;
   2516 	if (p >= softback_end)
   2517 		p += softback_buf - softback_end;
   2518 	return (u16 *) p;
   2519 }
   2520 
   2521 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
   2522 				 int *px, int *py)
   2523 {
   2524 	unsigned long ret;
   2525 	int x, y;
   2526 
   2527 	if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
   2528 		unsigned long offset = (pos - vc->vc_origin) / 2;
   2529 
   2530 		x = offset % vc->vc_cols;
   2531 		y = offset / vc->vc_cols;
   2532 		if (vc->vc_num == fg_console)
   2533 			y += softback_lines;
   2534 		ret = pos + (vc->vc_cols - x) * 2;
   2535 	} else if (vc->vc_num == fg_console && softback_lines) {
   2536 		unsigned long offset = pos - softback_curr;
   2537 
   2538 		if (pos < softback_curr)
   2539 			offset += softback_end - softback_buf;
   2540 		offset /= 2;
   2541 		x = offset % vc->vc_cols;
   2542 		y = offset / vc->vc_cols;
   2543 		ret = pos + (vc->vc_cols - x) * 2;
   2544 		if (ret == softback_end)
   2545 			ret = softback_buf;
   2546 		if (ret == softback_in)
   2547 			ret = vc->vc_origin;
   2548 	} else {
   2549 		/* Should not happen */
   2550 		x = y = 0;
   2551 		ret = vc->vc_origin;
   2552 	}
   2553 	if (px)
   2554 		*px = x;
   2555 	if (py)
   2556 		*py = y;
   2557 	return ret;
   2558 }
   2559 
   2560 /* As we might be inside of softback, we may work with non-contiguous buffer,
   2561    that's why we have to use a separate routine. */
   2562 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
   2563 {
   2564 	while (cnt--) {
   2565 		u16 a = scr_readw(p);
   2566 		if (!vc->vc_can_do_color)
   2567 			a ^= 0x0800;
   2568 		else if (vc->vc_hi_font_mask == 0x100)
   2569 			a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
   2570 			    (((a) & 0x0e00) << 4);
   2571 		else
   2572 			a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
   2573 			    (((a) & 0x0700) << 4);
   2574 		scr_writew(a, p++);
   2575 		if (p == (u16 *) softback_end)
   2576 			p = (u16 *) softback_buf;
   2577 		if (p == (u16 *) softback_in)
   2578 			p = (u16 *) vc->vc_origin;
   2579 	}
   2580 }
   2581 
   2582 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
   2583 {
   2584 	struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
   2585 	struct display *p = &fb_display[fg_console];
   2586 	int offset, limit, scrollback_old;
   2587 
   2588 	if (softback_top) {
   2589 		if (vc->vc_num != fg_console)
   2590 			return 0;
   2591 		if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
   2592 			return 0;
   2593 		if (logo_shown >= 0) {
   2594 			struct vc_data *conp2 = vc_cons[logo_shown].d;
   2595 
   2596 			if (conp2->vc_top == logo_lines
   2597 			    && conp2->vc_bottom == conp2->vc_rows)
   2598 				conp2->vc_top = 0;
   2599 			if (logo_shown == vc->vc_num) {
   2600 				unsigned long p, q;
   2601 				int i;
   2602 
   2603 				p = softback_in;
   2604 				q = vc->vc_origin +
   2605 				    logo_lines * vc->vc_size_row;
   2606 				for (i = 0; i < logo_lines; i++) {
   2607 					if (p == softback_top)
   2608 						break;
   2609 					if (p == softback_buf)
   2610 						p = softback_end;
   2611 					p -= vc->vc_size_row;
   2612 					q -= vc->vc_size_row;
   2613 					scr_memcpyw((u16 *) q, (u16 *) p,
   2614 						    vc->vc_size_row);
   2615 				}
   2616 				softback_in = p;
   2617 				update_region(vc->vc_num, vc->vc_origin,
   2618 					      logo_lines * vc->vc_cols);
   2619 			}
   2620 			logo_shown = -1;
   2621 		}
   2622 		fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
   2623 		fbcon_redraw_softback(vc, p, lines);
   2624 		fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
   2625 		return 0;
   2626 	}
   2627 
   2628 	if (!scrollback_phys_max)
   2629 		return -ENOSYS;
   2630 
   2631 	scrollback_old = scrollback_current;
   2632 	scrollback_current -= lines;
   2633 	if (scrollback_current < 0)
   2634 		scrollback_current = 0;
   2635 	else if (scrollback_current > scrollback_max)
   2636 		scrollback_current = scrollback_max;
   2637 	if (scrollback_current == scrollback_old)
   2638 		return 0;
   2639 
   2640 	if (!info->fbops->fb_blank &&
   2641 	    (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
   2642 	     || !lines))
   2643 		return 0;
   2644 	fbcon_cursor(vc, CM_ERASE);
   2645 
   2646 	offset = p->yscroll - scrollback_current;
   2647 	limit = p->vrows;
   2648 	switch (p->scrollmode) {
   2649 	case SCROLL_WRAP_MOVE:
   2650 		info->var.vmode |= FB_VMODE_YWRAP;
   2651 		break;
   2652 	case SCROLL_PAN_MOVE:
   2653 	case SCROLL_PAN_REDRAW:
   2654 		limit -= vc->vc_rows;
   2655 		info->var.vmode &= ~FB_VMODE_YWRAP;
   2656 		break;
   2657 	}
   2658 	if (offset < 0)
   2659 		offset += limit;
   2660 	else if (offset >= limit)
   2661 		offset -= limit;
   2662 	info->var.xoffset = 0;
   2663 	info->var.yoffset = offset * vc->vc_font.height;
   2664 	update_var(vc->vc_num, info);
   2665 	if (!scrollback_current)
   2666 		fbcon_cursor(vc, CM_DRAW);
   2667 	return 0;
   2668 }
   2669 
   2670 static int fbcon_set_origin(struct vc_data *vc)
   2671 {
   2672 	if (softback_lines && !console_blanked)
   2673 		fbcon_scrolldelta(vc, softback_lines);
   2674 	return 0;
   2675 }
   2676 
   2677 static void fbcon_suspended(struct fb_info *info)
   2678 {
   2679 	/* Clear cursor, restore saved data */
   2680 	info->cursor.enable = 0;
   2681 	info->fbops->fb_cursor(info, &info->cursor);
   2682 }
   2683 
   2684 static void fbcon_resumed(struct fb_info *info)
   2685 {
   2686 	struct vc_data *vc;
   2687 
   2688 	if (info->currcon < 0)
   2689 		return;
   2690 	vc = vc_cons[info->currcon].d;
   2691 
   2692 	update_screen(vc->vc_num);
   2693 }
   2694 
   2695 static void fbcon_modechanged(struct fb_info *info)
   2696 {
   2697 	struct vc_data *vc = vc_cons[info->currcon].d;
   2698 	struct display *p;
   2699 	int rows, cols;
   2700 
   2701 	if (info->currcon < 0 || vt_cons[info->currcon]->vc_mode !=
   2702 	    KD_TEXT)
   2703 		return;
   2704 	p = &fb_display[vc->vc_num];
   2705 
   2706 	info->var.xoffset = info->var.yoffset = p->yscroll = 0;
   2707 
   2708 	if (CON_IS_VISIBLE(vc)) {
   2709 		var_to_display(p, &info->var, info);
   2710 		cols = info->var.xres / vc->vc_font.width;
   2711 		rows = info->var.yres / vc->vc_font.height;
   2712 		vc_resize(vc->vc_num, cols, rows);
   2713 		updatescrollmode(p, info, vc);
   2714 		scrollback_max = 0;
   2715 		scrollback_current = 0;
   2716 		update_var(vc->vc_num, info);
   2717 		fbcon_set_palette(vc, color_table);
   2718 		update_screen(vc->vc_num);
   2719 		if (softback_buf) {
   2720 			int l = fbcon_softback_size / vc->vc_size_row;
   2721 			if (l > 5)
   2722 				softback_end = softback_buf + l * vc->vc_size_row;
   2723 			else {
   2724 				/* Smaller scrollback makes no sense, and 0
   2725 				   would screw the operation totally */
   2726 				softback_top = 0;
   2727 			}
   2728 		}
   2729 	}
   2730 }
   2731 
   2732 static int fbcon_mode_deleted(struct fb_info *info,
   2733 			      struct fb_videomode *mode)
   2734 {
   2735 	struct fb_info *fb_info;
   2736 	struct display *p;
   2737 	int i, j, found = 0;
   2738 
   2739 	/* before deletion, ensure that mode is not in use */
   2740 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
   2741 		j = (int) con2fb_map[i];
   2742 		if (j == -1)
   2743 			continue;
   2744 		fb_info = registered_fb[j];
   2745 		if (fb_info != info)
   2746 			continue;
   2747 		p = &fb_display[i];
   2748 		if (!p || !p->mode)
   2749 			continue;
   2750 		if (fb_mode_is_equal(p->mode, mode)) {
   2751 			found = 1;
   2752 			break;
   2753 		}
   2754 	}
   2755 	return found;
   2756 }
   2757 
   2758 static int fbcon_fb_registered(int idx)
   2759 {
   2760 	int ret = 0;
   2761 
   2762 	if (info_idx == -1) {
   2763 		info_idx = idx;
   2764 		ret = fbcon_takeover();
   2765 	}
   2766 
   2767 	return ret;
   2768 }
   2769 
   2770 static int fbcon_event_notify(struct notifier_block *self, 
   2771 			      unsigned long action, void *data)
   2772 {
   2773 	struct fb_event *event = (struct fb_event *) data;
   2774 	struct fb_info *info = event->info;
   2775 	struct fb_videomode *mode;
   2776 	struct fb_con2fbmap *con2fb;
   2777 	int ret = 0;
   2778 
   2779 	switch(action) {
   2780 	case FB_EVENT_SUSPEND:
   2781 		fbcon_suspended(info);
   2782 		break;
   2783 	case FB_EVENT_RESUME:
   2784 		fbcon_resumed(info);
   2785 		break;
   2786 	case FB_EVENT_MODE_CHANGE:
   2787 		fbcon_modechanged(info);
   2788 		break;
   2789 	case FB_EVENT_MODE_DELETE:
   2790 		mode = (struct fb_videomode *) event->data;
   2791 		ret = fbcon_mode_deleted(info, mode);
   2792 		break;
   2793 	case FB_EVENT_FB_REGISTERED:
   2794 		ret = fbcon_fb_registered(info->node);
   2795 		break;
   2796 	case FB_EVENT_SET_CONSOLE_MAP:
   2797 		con2fb = (struct fb_con2fbmap *) event->data;
   2798 		ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer);
   2799 		break;
   2800 	case FB_EVENT_GET_CONSOLE_MAP:
   2801 		con2fb = (struct fb_con2fbmap *) event->data;
   2802 		con2fb->framebuffer = con2fb_map[con2fb->console - 1];
   2803 		break;
   2804 	}
   2805 
   2806 	return ret;
   2807 }
   2808 
   2809 /*
   2810  *  The console `switch' structure for the frame buffer based console
   2811  */
   2812 
   2813 const struct consw fb_con = {
   2814 	.owner			= THIS_MODULE,
   2815 	.con_startup 		= fbcon_startup,
   2816 	.con_init 		= fbcon_init,
   2817 	.con_deinit 		= fbcon_deinit,
   2818 	.con_clear 		= fbcon_clear,
   2819 	.con_putc 		= fbcon_putc,
   2820 	.con_putcs 		= fbcon_putcs,
   2821 	.con_cursor 		= fbcon_cursor,
   2822 	.con_scroll 		= fbcon_scroll,
   2823 	.con_bmove 		= fbcon_bmove,
   2824 	.con_switch 		= fbcon_switch,
   2825 	.con_blank 		= fbcon_blank,
   2826 	.con_font_set 		= fbcon_set_font,
   2827 	.con_font_get 		= fbcon_get_font,
   2828 	.con_font_default	= fbcon_set_def_font,
   2829 	.con_font_copy 		= fbcon_copy_font,
   2830 	.con_set_palette 	= fbcon_set_palette,
   2831 	.con_scrolldelta 	= fbcon_scrolldelta,
   2832 	.con_set_origin 	= fbcon_set_origin,
   2833 	.con_invert_region 	= fbcon_invert_region,
   2834 	.con_screen_pos 	= fbcon_screen_pos,
   2835 	.con_getxy 		= fbcon_getxy,
   2836 	.con_resize             = fbcon_resize,
   2837 };
   2838 
   2839 static struct notifier_block fbcon_event_notifier = {
   2840 	.notifier_call	= fbcon_event_notify,
   2841 };
   2842 
   2843 int __init fb_console_init(void)
   2844 {
   2845 	int i;
   2846 
   2847 	acquire_console_sem();
   2848 	fb_register_client(&fbcon_event_notifier);
   2849 	release_console_sem();
   2850 
   2851 	for (i = 0; i < MAX_NR_CONSOLES; i++)
   2852 		con2fb_map[i] = -1;
   2853 
   2854 	if (num_registered_fb) {
   2855 		for (i = 0; i < FB_MAX; i++) {
   2856 			if (registered_fb[i] != NULL) {
   2857 				info_idx = i;
   2858 				break;
   2859 			}
   2860 		}
   2861 		fbcon_takeover();
   2862 	}
   2863 
   2864 	return 0;
   2865 }
   2866 
   2867 module_init(fb_console_init);
   2868 
   2869 #ifdef MODULE
   2870 
   2871 void __exit fb_console_exit(void)
   2872 {
   2873 	acquire_console_sem();
   2874 	fb_unregister_client(&fbcon_event_notifier);
   2875 	release_console_sem();
   2876 	give_up_console(&fb_con);
   2877 }	
   2878 
   2879 module_exit(fb_console_exit);
   2880 
   2881 #endif
   2882 
   2883 /*
   2884  *  Visible symbols for modules
   2885  */
   2886 
   2887 EXPORT_SYMBOL(fb_display);
   2888 EXPORT_SYMBOL(fb_con);
   2889 
   2890 MODULE_LICENSE("GPL");

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.