File Index
Files and directories
Sign in
FILE

fbcon.c

File preview with syntax highlighting.

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