cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fbcon.c (84887B)


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