brintos

brintos / linux-shallow public Read only

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