xref: /linux/drivers/video/fbdev/core/fbcon.c (revision f95c139099f8dad235191caec9e9268ca717beb9)
16104c370SDaniel Vetter /*
26104c370SDaniel Vetter  *  linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
36104c370SDaniel Vetter  *
46104c370SDaniel Vetter  *	Copyright (C) 1995 Geert Uytterhoeven
56104c370SDaniel Vetter  *
66104c370SDaniel Vetter  *
76104c370SDaniel Vetter  *  This file is based on the original Amiga console driver (amicon.c):
86104c370SDaniel Vetter  *
96104c370SDaniel Vetter  *	Copyright (C) 1993 Hamish Macdonald
106104c370SDaniel Vetter  *			   Greg Harp
116104c370SDaniel Vetter  *	Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
126104c370SDaniel Vetter  *
136104c370SDaniel Vetter  *	      with work by William Rucklidge (wjr@cs.cornell.edu)
146104c370SDaniel Vetter  *			   Geert Uytterhoeven
156104c370SDaniel Vetter  *			   Jes Sorensen (jds@kom.auc.dk)
166104c370SDaniel Vetter  *			   Martin Apel
176104c370SDaniel Vetter  *
186104c370SDaniel Vetter  *  and on the original Atari console driver (atacon.c):
196104c370SDaniel Vetter  *
206104c370SDaniel Vetter  *	Copyright (C) 1993 Bjoern Brauel
216104c370SDaniel Vetter  *			   Roman Hodek
226104c370SDaniel Vetter  *
236104c370SDaniel Vetter  *	      with work by Guenther Kelleter
246104c370SDaniel Vetter  *			   Martin Schaller
256104c370SDaniel Vetter  *			   Andreas Schwab
266104c370SDaniel Vetter  *
276104c370SDaniel Vetter  *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
286104c370SDaniel Vetter  *  Smart redraw scrolling, arbitrary font width support, 512char font support
296104c370SDaniel Vetter  *  and software scrollback added by
306104c370SDaniel Vetter  *                         Jakub Jelinek (jj@ultra.linux.cz)
316104c370SDaniel Vetter  *
326104c370SDaniel Vetter  *  Random hacking by Martin Mares <mj@ucw.cz>
336104c370SDaniel Vetter  *
346104c370SDaniel Vetter  *	2001 - Documented with DocBook
356104c370SDaniel Vetter  *	- Brad Douglas <brad@neruo.com>
366104c370SDaniel Vetter  *
376104c370SDaniel Vetter  *  The low level operations for the various display memory organizations are
386104c370SDaniel Vetter  *  now in separate source files.
396104c370SDaniel Vetter  *
406104c370SDaniel Vetter  *  Currently the following organizations are supported:
416104c370SDaniel Vetter  *
426104c370SDaniel Vetter  *    o afb			Amiga bitplanes
436104c370SDaniel Vetter  *    o cfb{2,4,8,16,24,32}	Packed pixels
446104c370SDaniel Vetter  *    o ilbm			Amiga interleaved bitplanes
456104c370SDaniel Vetter  *    o iplan2p[248]		Atari interleaved bitplanes
466104c370SDaniel Vetter  *    o mfb			Monochrome
476104c370SDaniel Vetter  *    o vga			VGA characters/attributes
486104c370SDaniel Vetter  *
496104c370SDaniel Vetter  *  To do:
506104c370SDaniel Vetter  *
516104c370SDaniel Vetter  *    - Implement 16 plane mode (iplan2p16)
526104c370SDaniel Vetter  *
536104c370SDaniel Vetter  *
546104c370SDaniel Vetter  *  This file is subject to the terms and conditions of the GNU General Public
556104c370SDaniel Vetter  *  License.  See the file COPYING in the main directory of this archive for
566104c370SDaniel Vetter  *  more details.
576104c370SDaniel Vetter  */
586104c370SDaniel Vetter 
596104c370SDaniel Vetter #include <linux/module.h>
606104c370SDaniel Vetter #include <linux/types.h>
616104c370SDaniel Vetter #include <linux/fs.h>
626104c370SDaniel Vetter #include <linux/kernel.h>
636104c370SDaniel Vetter #include <linux/delay.h>	/* MSch: for IRQ probe */
646104c370SDaniel Vetter #include <linux/console.h>
656104c370SDaniel Vetter #include <linux/string.h>
666104c370SDaniel Vetter #include <linux/kd.h>
671c1ed27eSJocelyn Falempe #include <linux/panic.h>
681c1ed27eSJocelyn Falempe #include <linux/printk.h>
696104c370SDaniel Vetter #include <linux/slab.h>
706104c370SDaniel Vetter #include <linux/fb.h>
716104c370SDaniel Vetter #include <linux/fbcon.h>
726104c370SDaniel Vetter #include <linux/vt_kern.h>
736104c370SDaniel Vetter #include <linux/selection.h>
746104c370SDaniel Vetter #include <linux/font.h>
756104c370SDaniel Vetter #include <linux/smp.h>
766104c370SDaniel Vetter #include <linux/init.h>
776104c370SDaniel Vetter #include <linux/interrupt.h>
786104c370SDaniel Vetter #include <linux/crc32.h> /* For counting font checksums */
79fe2d70d6SDaniel Vetter #include <linux/uaccess.h>
806104c370SDaniel Vetter #include <asm/irq.h>
816104c370SDaniel Vetter 
826104c370SDaniel Vetter #include "fbcon.h"
83ff8fbcf6SThomas Zimmermann #include "fb_internal.h"
846104c370SDaniel Vetter 
8594193d2aSDaniel Vetter /*
8694193d2aSDaniel Vetter  * FIXME: Locking
8794193d2aSDaniel Vetter  *
8894193d2aSDaniel Vetter  * - fbcon state itself is protected by the console_lock, and the code does a
8994193d2aSDaniel Vetter  *   pretty good job at making sure that lock is held everywhere it's needed.
9094193d2aSDaniel Vetter  *
9194193d2aSDaniel Vetter  * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
9294193d2aSDaniel Vetter  *   means concurrent access to the same fbdev from both fbcon and userspace
9394193d2aSDaniel Vetter  *   will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
9494193d2aSDaniel Vetter  *   of fb_lock/unlock protected sections, since otherwise we'll recurse and
9594193d2aSDaniel Vetter  *   deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
9694193d2aSDaniel Vetter  *   fbmem.c cannot use locking asserts, and there's lots of callers which get
9794193d2aSDaniel Vetter  *   the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
9894193d2aSDaniel Vetter  */
9994193d2aSDaniel Vetter 
1006104c370SDaniel Vetter enum {
1016104c370SDaniel Vetter 	FBCON_LOGO_CANSHOW	= -1,	/* the logo can be shown */
1026104c370SDaniel Vetter 	FBCON_LOGO_DRAW		= -2,	/* draw the logo to a console */
1036104c370SDaniel Vetter 	FBCON_LOGO_DONTSHOW	= -3	/* do not show the logo */
1046104c370SDaniel Vetter };
1056104c370SDaniel Vetter 
10650233393SDaniel Vetter static struct fbcon_display fb_display[MAX_NR_CONSOLES];
1076104c370SDaniel Vetter 
1081e8ea2e9SGUO Zihua static struct fb_info *fbcon_registered_fb[FB_MAX];
1091e8ea2e9SGUO Zihua static int fbcon_num_registered_fb;
110efc3acbcSDaniel Vetter 
111efc3acbcSDaniel Vetter #define fbcon_for_each_registered_fb(i)		\
112efc3acbcSDaniel Vetter 	for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++)		\
113efc3acbcSDaniel Vetter 		if (!fbcon_registered_fb[i]) {} else
114efc3acbcSDaniel Vetter 
1156104c370SDaniel Vetter static signed char con2fb_map[MAX_NR_CONSOLES];
1166104c370SDaniel Vetter static signed char con2fb_map_boot[MAX_NR_CONSOLES];
1176104c370SDaniel Vetter 
fbcon_info_from_console(int console)118409d6c95SDaniel Vetter static struct fb_info *fbcon_info_from_console(int console)
119409d6c95SDaniel Vetter {
120409d6c95SDaniel Vetter 	WARN_CONSOLE_UNLOCKED();
121409d6c95SDaniel Vetter 
122efc3acbcSDaniel Vetter 	return fbcon_registered_fb[con2fb_map[console]];
123409d6c95SDaniel Vetter }
124409d6c95SDaniel Vetter 
1256104c370SDaniel Vetter static int logo_lines;
1266104c370SDaniel Vetter /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
1276104c370SDaniel Vetter    enums.  */
1286104c370SDaniel Vetter static int logo_shown = FBCON_LOGO_CANSHOW;
1296104c370SDaniel Vetter /* console mappings */
130cad564caSHelge Deller static unsigned int first_fb_vc;
131cad564caSHelge Deller static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
1326104c370SDaniel Vetter static int fbcon_is_default = 1;
1336104c370SDaniel Vetter static int primary_device = -1;
1346104c370SDaniel Vetter static int fbcon_has_console_bind;
1356104c370SDaniel Vetter 
1366104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
1376104c370SDaniel Vetter static int map_override;
1386104c370SDaniel Vetter 
fbcon_map_override(void)1396104c370SDaniel Vetter static inline void fbcon_map_override(void)
1406104c370SDaniel Vetter {
1416104c370SDaniel Vetter 	map_override = 1;
1426104c370SDaniel Vetter }
1436104c370SDaniel Vetter #else
fbcon_map_override(void)1446104c370SDaniel Vetter static inline void fbcon_map_override(void)
1456104c370SDaniel Vetter {
1466104c370SDaniel Vetter }
1476104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
1486104c370SDaniel Vetter 
14983d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
15083d83bebSHans de Goede static bool deferred_takeover = true;
15183d83bebSHans de Goede #else
15283d83bebSHans de Goede #define deferred_takeover false
15383d83bebSHans de Goede #endif
15483d83bebSHans de Goede 
1556104c370SDaniel Vetter /* font data */
1566104c370SDaniel Vetter static char fontname[40];
1576104c370SDaniel Vetter 
1586104c370SDaniel Vetter /* current fb_info */
1596104c370SDaniel Vetter static int info_idx = -1;
1606104c370SDaniel Vetter 
1616104c370SDaniel Vetter /* console rotation */
162b0d8e409SHans de Goede static int initial_rotation = -1;
1636104c370SDaniel Vetter static int fbcon_has_sysfs;
16474c1c8b3SDavid Lechner static int margin_color;
1656104c370SDaniel Vetter 
1666104c370SDaniel Vetter static const struct consw fb_con;
1676104c370SDaniel Vetter 
1686104c370SDaniel Vetter #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
1696104c370SDaniel Vetter 
1706104c370SDaniel Vetter static int fbcon_cursor_noblink;
1716104c370SDaniel Vetter 
1726104c370SDaniel Vetter #define divides(a, b)	((!(a) || (b)%(a)) ? 0 : 1)
1736104c370SDaniel Vetter 
1746104c370SDaniel Vetter /*
1756104c370SDaniel Vetter  *  Interface used by the world
1766104c370SDaniel Vetter  */
1776104c370SDaniel Vetter 
1786104c370SDaniel Vetter static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
1796104c370SDaniel Vetter static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
1806104c370SDaniel Vetter 
1816104c370SDaniel Vetter /*
1826104c370SDaniel Vetter  *  Internal routines
1836104c370SDaniel Vetter  */
1846104c370SDaniel Vetter static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1856104c370SDaniel Vetter 			   int unit);
1861148836fSHelge Deller static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1871148836fSHelge Deller 			      int line, int count, int dy);
1886104c370SDaniel Vetter static void fbcon_modechanged(struct fb_info *info);
1896104c370SDaniel Vetter static void fbcon_set_all_vcs(struct fb_info *info);
1909ad7acdaSDaniel Vetter 
1916104c370SDaniel Vetter static struct device *fbcon_device;
1926104c370SDaniel Vetter 
1936104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
fbcon_set_rotation(struct fb_info * info)1946104c370SDaniel Vetter static inline void fbcon_set_rotation(struct fb_info *info)
1956104c370SDaniel Vetter {
1966104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
1976104c370SDaniel Vetter 
1986104c370SDaniel Vetter 	if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
1996104c370SDaniel Vetter 	    ops->p->con_rotate < 4)
2006104c370SDaniel Vetter 		ops->rotate = ops->p->con_rotate;
2016104c370SDaniel Vetter 	else
2026104c370SDaniel Vetter 		ops->rotate = 0;
2036104c370SDaniel Vetter }
2046104c370SDaniel Vetter 
fbcon_rotate(struct fb_info * info,u32 rotate)2056104c370SDaniel Vetter static void fbcon_rotate(struct fb_info *info, u32 rotate)
2066104c370SDaniel Vetter {
2076104c370SDaniel Vetter 	struct fbcon_ops *ops= info->fbcon_par;
2086104c370SDaniel Vetter 	struct fb_info *fb_info;
2096104c370SDaniel Vetter 
2106104c370SDaniel Vetter 	if (!ops || ops->currcon == -1)
2116104c370SDaniel Vetter 		return;
2126104c370SDaniel Vetter 
213409d6c95SDaniel Vetter 	fb_info = fbcon_info_from_console(ops->currcon);
2146104c370SDaniel Vetter 
2156104c370SDaniel Vetter 	if (info == fb_info) {
21650233393SDaniel Vetter 		struct fbcon_display *p = &fb_display[ops->currcon];
2176104c370SDaniel Vetter 
2186104c370SDaniel Vetter 		if (rotate < 4)
2196104c370SDaniel Vetter 			p->con_rotate = rotate;
2206104c370SDaniel Vetter 		else
2216104c370SDaniel Vetter 			p->con_rotate = 0;
2226104c370SDaniel Vetter 
2236104c370SDaniel Vetter 		fbcon_modechanged(info);
2246104c370SDaniel Vetter 	}
2256104c370SDaniel Vetter }
2266104c370SDaniel Vetter 
fbcon_rotate_all(struct fb_info * info,u32 rotate)2276104c370SDaniel Vetter static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
2286104c370SDaniel Vetter {
2296104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
2306104c370SDaniel Vetter 	struct vc_data *vc;
23150233393SDaniel Vetter 	struct fbcon_display *p;
2326104c370SDaniel Vetter 	int i;
2336104c370SDaniel Vetter 
2346104c370SDaniel Vetter 	if (!ops || ops->currcon < 0 || rotate > 3)
2356104c370SDaniel Vetter 		return;
2366104c370SDaniel Vetter 
2376104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
2386104c370SDaniel Vetter 		vc = vc_cons[i].d;
2396104c370SDaniel Vetter 		if (!vc || vc->vc_mode != KD_TEXT ||
240409d6c95SDaniel Vetter 		    fbcon_info_from_console(i) != info)
2416104c370SDaniel Vetter 			continue;
2426104c370SDaniel Vetter 
2436104c370SDaniel Vetter 		p = &fb_display[vc->vc_num];
2446104c370SDaniel Vetter 		p->con_rotate = rotate;
2456104c370SDaniel Vetter 	}
2466104c370SDaniel Vetter 
2476104c370SDaniel Vetter 	fbcon_set_all_vcs(info);
2486104c370SDaniel Vetter }
2496104c370SDaniel Vetter #else
fbcon_set_rotation(struct fb_info * info)2506104c370SDaniel Vetter static inline void fbcon_set_rotation(struct fb_info *info)
2516104c370SDaniel Vetter {
2526104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
2536104c370SDaniel Vetter 
2546104c370SDaniel Vetter 	ops->rotate = FB_ROTATE_UR;
2556104c370SDaniel Vetter }
2566104c370SDaniel Vetter 
fbcon_rotate(struct fb_info * info,u32 rotate)2576104c370SDaniel Vetter static void fbcon_rotate(struct fb_info *info, u32 rotate)
2586104c370SDaniel Vetter {
2596104c370SDaniel Vetter 	return;
2606104c370SDaniel Vetter }
2616104c370SDaniel Vetter 
fbcon_rotate_all(struct fb_info * info,u32 rotate)2626104c370SDaniel Vetter static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
2636104c370SDaniel Vetter {
2646104c370SDaniel Vetter 	return;
2656104c370SDaniel Vetter }
2666104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
2676104c370SDaniel Vetter 
fbcon_get_rotate(struct fb_info * info)2686104c370SDaniel Vetter static int fbcon_get_rotate(struct fb_info *info)
2696104c370SDaniel Vetter {
2706104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
2716104c370SDaniel Vetter 
2726104c370SDaniel Vetter 	return (ops) ? ops->rotate : 0;
2736104c370SDaniel Vetter }
2746104c370SDaniel Vetter 
fbcon_skip_panic(struct fb_info * info)275d20a9f56SJocelyn Falempe static bool fbcon_skip_panic(struct fb_info *info)
276d20a9f56SJocelyn Falempe {
2771c1ed27eSJocelyn Falempe /* panic_cpu is not exported, and can't be used if built as module. Use
2781c1ed27eSJocelyn Falempe  * oops_in_progress instead, but non-fatal oops won't be printed.
2791c1ed27eSJocelyn Falempe  */
2801c1ed27eSJocelyn Falempe #if defined(MODULE)
2811c1ed27eSJocelyn Falempe 	return (info->skip_panic && unlikely(oops_in_progress));
2821c1ed27eSJocelyn Falempe #else
283d20a9f56SJocelyn Falempe 	return (info->skip_panic && unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID));
2841c1ed27eSJocelyn Falempe #endif
285d20a9f56SJocelyn Falempe }
286d20a9f56SJocelyn Falempe 
fbcon_is_inactive(struct vc_data * vc,struct fb_info * info)2876104c370SDaniel Vetter static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
2886104c370SDaniel Vetter {
2896104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
2906104c370SDaniel Vetter 
2916104c370SDaniel Vetter 	return (info->state != FBINFO_STATE_RUNNING ||
292d20a9f56SJocelyn Falempe 		vc->vc_mode != KD_TEXT || ops->graphics || fbcon_skip_panic(info));
2936104c370SDaniel Vetter }
2946104c370SDaniel Vetter 
get_color(struct vc_data * vc,struct fb_info * info,u16 c,int is_fg)2956104c370SDaniel Vetter static int get_color(struct vc_data *vc, struct fb_info *info,
2966104c370SDaniel Vetter 	      u16 c, int is_fg)
2976104c370SDaniel Vetter {
2986104c370SDaniel Vetter 	int depth = fb_get_color_depth(&info->var, &info->fix);
2996104c370SDaniel Vetter 	int color = 0;
3006104c370SDaniel Vetter 
3016104c370SDaniel Vetter 	if (console_blanked) {
3026104c370SDaniel Vetter 		unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
3036104c370SDaniel Vetter 
3046104c370SDaniel Vetter 		c = vc->vc_video_erase_char & charmask;
3056104c370SDaniel Vetter 	}
3066104c370SDaniel Vetter 
3076104c370SDaniel Vetter 	if (depth != 1)
3086104c370SDaniel Vetter 		color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
3096104c370SDaniel Vetter 			: attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
3106104c370SDaniel Vetter 
3116104c370SDaniel Vetter 	switch (depth) {
3126104c370SDaniel Vetter 	case 1:
3136104c370SDaniel Vetter 	{
3146104c370SDaniel Vetter 		int col = mono_col(info);
3156104c370SDaniel Vetter 		/* 0 or 1 */
3166104c370SDaniel Vetter 		int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
3176104c370SDaniel Vetter 		int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
3186104c370SDaniel Vetter 
3196104c370SDaniel Vetter 		if (console_blanked)
3206104c370SDaniel Vetter 			fg = bg;
3216104c370SDaniel Vetter 
3226104c370SDaniel Vetter 		color = (is_fg) ? fg : bg;
3236104c370SDaniel Vetter 		break;
3246104c370SDaniel Vetter 	}
3256104c370SDaniel Vetter 	case 2:
3266104c370SDaniel Vetter 		/*
3276104c370SDaniel Vetter 		 * Scale down 16-colors to 4 colors. Default 4-color palette
3286104c370SDaniel Vetter 		 * is grayscale. However, simply dividing the values by 4
3296104c370SDaniel Vetter 		 * will not work, as colors 1, 2 and 3 will be scaled-down
3306104c370SDaniel Vetter 		 * to zero rendering them invisible.  So empirically convert
3316104c370SDaniel Vetter 		 * colors to a sane 4-level grayscale.
3326104c370SDaniel Vetter 		 */
3336104c370SDaniel Vetter 		switch (color) {
3346104c370SDaniel Vetter 		case 0:
3356104c370SDaniel Vetter 			color = 0; /* black */
3366104c370SDaniel Vetter 			break;
3376104c370SDaniel Vetter 		case 1 ... 6:
3386104c370SDaniel Vetter 			color = 2; /* white */
3396104c370SDaniel Vetter 			break;
3406104c370SDaniel Vetter 		case 7 ... 8:
3416104c370SDaniel Vetter 			color = 1; /* gray */
3426104c370SDaniel Vetter 			break;
3436104c370SDaniel Vetter 		default:
3446104c370SDaniel Vetter 			color = 3; /* intense white */
3456104c370SDaniel Vetter 			break;
3466104c370SDaniel Vetter 		}
3476104c370SDaniel Vetter 		break;
3486104c370SDaniel Vetter 	case 3:
3496104c370SDaniel Vetter 		/*
3506104c370SDaniel Vetter 		 * Last 8 entries of default 16-color palette is a more intense
3516104c370SDaniel Vetter 		 * version of the first 8 (i.e., same chrominance, different
3526104c370SDaniel Vetter 		 * luminance).
3536104c370SDaniel Vetter 		 */
3546104c370SDaniel Vetter 		color &= 7;
3556104c370SDaniel Vetter 		break;
3566104c370SDaniel Vetter 	}
3576104c370SDaniel Vetter 
3586104c370SDaniel Vetter 
3596104c370SDaniel Vetter 	return color;
3606104c370SDaniel Vetter }
3616104c370SDaniel Vetter 
fb_flashcursor(struct work_struct * work)3626104c370SDaniel Vetter static void fb_flashcursor(struct work_struct *work)
3636104c370SDaniel Vetter {
3643b0fb6abSDaniel Vetter 	struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
3653b0fb6abSDaniel Vetter 	struct fb_info *info;
3666104c370SDaniel Vetter 	struct vc_data *vc = NULL;
3676104c370SDaniel Vetter 	int c;
368a292e3fcSJiri Slaby (SUSE) 	bool enable;
3696104c370SDaniel Vetter 	int ret;
3706104c370SDaniel Vetter 
3716104c370SDaniel Vetter 	/* FIXME: we should sort out the unbind locking instead */
3726104c370SDaniel Vetter 	/* instead we just fail to flash the cursor if we can't get
3736104c370SDaniel Vetter 	 * the lock instead of blocking fbcon deinit */
3746104c370SDaniel Vetter 	ret = console_trylock();
3756104c370SDaniel Vetter 	if (ret == 0)
3766104c370SDaniel Vetter 		return;
3776104c370SDaniel Vetter 
3783b0fb6abSDaniel Vetter 	/* protected by console_lock */
3793b0fb6abSDaniel Vetter 	info = ops->info;
3803b0fb6abSDaniel Vetter 
3813b0fb6abSDaniel Vetter 	if (ops->currcon != -1)
3826104c370SDaniel Vetter 		vc = vc_cons[ops->currcon].d;
3836104c370SDaniel Vetter 
3846104c370SDaniel Vetter 	if (!vc || !con_is_visible(vc) ||
385409d6c95SDaniel Vetter 	    fbcon_info_from_console(vc->vc_num) != info ||
3866104c370SDaniel Vetter 	    vc->vc_deccm != 1) {
3876104c370SDaniel Vetter 		console_unlock();
3886104c370SDaniel Vetter 		return;
3896104c370SDaniel Vetter 	}
3906104c370SDaniel Vetter 
3916104c370SDaniel Vetter 	c = scr_readw((u16 *) vc->vc_pos);
392a292e3fcSJiri Slaby (SUSE) 	enable = ops->cursor_flash && !ops->cursor_state.enable;
393a292e3fcSJiri Slaby (SUSE) 	ops->cursor(vc, info, enable, get_color(vc, info, c, 1),
3946104c370SDaniel Vetter 		    get_color(vc, info, c, 0));
3956104c370SDaniel Vetter 	console_unlock();
3963b0fb6abSDaniel Vetter 
3973b0fb6abSDaniel Vetter 	queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
3983b0fb6abSDaniel Vetter 			   ops->cur_blink_jiffies);
3996104c370SDaniel Vetter }
4006104c370SDaniel Vetter 
fbcon_add_cursor_work(struct fb_info * info)4013b0fb6abSDaniel Vetter static void fbcon_add_cursor_work(struct fb_info *info)
4026104c370SDaniel Vetter {
4036104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
4046104c370SDaniel Vetter 
4053b0fb6abSDaniel Vetter 	if (!fbcon_cursor_noblink)
4063b0fb6abSDaniel Vetter 		queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
4073b0fb6abSDaniel Vetter 				   ops->cur_blink_jiffies);
4086104c370SDaniel Vetter }
4096104c370SDaniel Vetter 
fbcon_del_cursor_work(struct fb_info * info)4103b0fb6abSDaniel Vetter static void fbcon_del_cursor_work(struct fb_info *info)
4116104c370SDaniel Vetter {
4126104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
4136104c370SDaniel Vetter 
4143b0fb6abSDaniel Vetter 	cancel_delayed_work_sync(&ops->cursor_work);
4156104c370SDaniel Vetter }
4166104c370SDaniel Vetter 
4176104c370SDaniel Vetter #ifndef MODULE
fb_console_setup(char * this_opt)4186104c370SDaniel Vetter static int __init fb_console_setup(char *this_opt)
4196104c370SDaniel Vetter {
4206104c370SDaniel Vetter 	char *options;
4216104c370SDaniel Vetter 	int i, j;
4226104c370SDaniel Vetter 
4236104c370SDaniel Vetter 	if (!this_opt || !*this_opt)
4246104c370SDaniel Vetter 		return 1;
4256104c370SDaniel Vetter 
4266104c370SDaniel Vetter 	while ((options = strsep(&this_opt, ",")) != NULL) {
4276104c370SDaniel Vetter 		if (!strncmp(options, "font:", 5)) {
4288d026858SWolfram Sang 			strscpy(fontname, options + 5, sizeof(fontname));
4296104c370SDaniel Vetter 			continue;
4306104c370SDaniel Vetter 		}
4316104c370SDaniel Vetter 
4326104c370SDaniel Vetter 		if (!strncmp(options, "scrollback:", 11)) {
43350145474SLinus Torvalds 			pr_warn("Ignoring scrollback size option\n");
4346104c370SDaniel Vetter 			continue;
4356104c370SDaniel Vetter 		}
4366104c370SDaniel Vetter 
4376104c370SDaniel Vetter 		if (!strncmp(options, "map:", 4)) {
4386104c370SDaniel Vetter 			options += 4;
4396104c370SDaniel Vetter 			if (*options) {
4406104c370SDaniel Vetter 				for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
4416104c370SDaniel Vetter 					if (!options[j])
4426104c370SDaniel Vetter 						j = 0;
4436104c370SDaniel Vetter 					con2fb_map_boot[i] =
4446104c370SDaniel Vetter 						(options[j++]-'0') % FB_MAX;
4456104c370SDaniel Vetter 				}
4466104c370SDaniel Vetter 
4476104c370SDaniel Vetter 				fbcon_map_override();
4486104c370SDaniel Vetter 			}
4496104c370SDaniel Vetter 			continue;
4506104c370SDaniel Vetter 		}
4516104c370SDaniel Vetter 
4526104c370SDaniel Vetter 		if (!strncmp(options, "vc:", 3)) {
4536104c370SDaniel Vetter 			options += 3;
4546104c370SDaniel Vetter 			if (*options)
4556104c370SDaniel Vetter 				first_fb_vc = simple_strtoul(options, &options, 10) - 1;
456cad564caSHelge Deller 			if (first_fb_vc >= MAX_NR_CONSOLES)
4576104c370SDaniel Vetter 				first_fb_vc = 0;
4586104c370SDaniel Vetter 			if (*options++ == '-')
4596104c370SDaniel Vetter 				last_fb_vc = simple_strtoul(options, &options, 10) - 1;
460cad564caSHelge Deller 			if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
461cad564caSHelge Deller 				last_fb_vc = MAX_NR_CONSOLES - 1;
4626104c370SDaniel Vetter 			fbcon_is_default = 0;
4636104c370SDaniel Vetter 			continue;
4646104c370SDaniel Vetter 		}
4656104c370SDaniel Vetter 
4666104c370SDaniel Vetter 		if (!strncmp(options, "rotate:", 7)) {
4676104c370SDaniel Vetter 			options += 7;
4686104c370SDaniel Vetter 			if (*options)
4696104c370SDaniel Vetter 				initial_rotation = simple_strtoul(options, &options, 0);
4706104c370SDaniel Vetter 			if (initial_rotation > 3)
4716104c370SDaniel Vetter 				initial_rotation = 0;
4726104c370SDaniel Vetter 			continue;
4736104c370SDaniel Vetter 		}
47474c1c8b3SDavid Lechner 
47574c1c8b3SDavid Lechner 		if (!strncmp(options, "margin:", 7)) {
47674c1c8b3SDavid Lechner 			options += 7;
47774c1c8b3SDavid Lechner 			if (*options)
47874c1c8b3SDavid Lechner 				margin_color = simple_strtoul(options, &options, 0);
47974c1c8b3SDavid Lechner 			continue;
48074c1c8b3SDavid Lechner 		}
48183d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
48283d83bebSHans de Goede 		if (!strcmp(options, "nodefer")) {
48383d83bebSHans de Goede 			deferred_takeover = false;
48483d83bebSHans de Goede 			continue;
48583d83bebSHans de Goede 		}
48683d83bebSHans de Goede #endif
487890d14d2SPeter Rosin 
4888887086eSThomas Zimmermann #ifdef CONFIG_LOGO
489890d14d2SPeter Rosin 		if (!strncmp(options, "logo-pos:", 9)) {
490890d14d2SPeter Rosin 			options += 9;
491890d14d2SPeter Rosin 			if (!strcmp(options, "center"))
492890d14d2SPeter Rosin 				fb_center_logo = true;
493890d14d2SPeter Rosin 			continue;
494890d14d2SPeter Rosin 		}
495691f50abSPeter Rosin 
496691f50abSPeter Rosin 		if (!strncmp(options, "logo-count:", 11)) {
497691f50abSPeter Rosin 			options += 11;
498691f50abSPeter Rosin 			if (*options)
499691f50abSPeter Rosin 				fb_logo_count = simple_strtol(options, &options, 0);
500691f50abSPeter Rosin 			continue;
501691f50abSPeter Rosin 		}
5028887086eSThomas Zimmermann #endif
5036104c370SDaniel Vetter 	}
5046104c370SDaniel Vetter 	return 1;
5056104c370SDaniel Vetter }
5066104c370SDaniel Vetter 
5076104c370SDaniel Vetter __setup("fbcon=", fb_console_setup);
5086104c370SDaniel Vetter #endif
5096104c370SDaniel Vetter 
search_fb_in_map(int idx)5106104c370SDaniel Vetter static int search_fb_in_map(int idx)
5116104c370SDaniel Vetter {
5126104c370SDaniel Vetter 	int i, retval = 0;
5136104c370SDaniel Vetter 
5146104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
515*2555906fSQianqiang Liu 		if (con2fb_map[i] == idx) {
5166104c370SDaniel Vetter 			retval = 1;
517*2555906fSQianqiang Liu 			break;
518*2555906fSQianqiang Liu 		}
5196104c370SDaniel Vetter 	}
5206104c370SDaniel Vetter 	return retval;
5216104c370SDaniel Vetter }
5226104c370SDaniel Vetter 
search_for_mapped_con(void)5236104c370SDaniel Vetter static int search_for_mapped_con(void)
5246104c370SDaniel Vetter {
5256104c370SDaniel Vetter 	int i, retval = 0;
5266104c370SDaniel Vetter 
5276104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
528*2555906fSQianqiang Liu 		if (con2fb_map[i] != -1) {
5296104c370SDaniel Vetter 			retval = 1;
530*2555906fSQianqiang Liu 			break;
531*2555906fSQianqiang Liu 		}
5326104c370SDaniel Vetter 	}
5336104c370SDaniel Vetter 	return retval;
5346104c370SDaniel Vetter }
5356104c370SDaniel Vetter 
do_fbcon_takeover(int show_logo)5366104c370SDaniel Vetter static int do_fbcon_takeover(int show_logo)
5376104c370SDaniel Vetter {
5386104c370SDaniel Vetter 	int err, i;
5396104c370SDaniel Vetter 
540efc3acbcSDaniel Vetter 	if (!fbcon_num_registered_fb)
5416104c370SDaniel Vetter 		return -ENODEV;
5426104c370SDaniel Vetter 
5436104c370SDaniel Vetter 	if (!show_logo)
5446104c370SDaniel Vetter 		logo_shown = FBCON_LOGO_DONTSHOW;
5456104c370SDaniel Vetter 
5466104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++)
5476104c370SDaniel Vetter 		con2fb_map[i] = info_idx;
5486104c370SDaniel Vetter 
5496104c370SDaniel Vetter 	err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
5506104c370SDaniel Vetter 				fbcon_is_default);
5516104c370SDaniel Vetter 
5526104c370SDaniel Vetter 	if (err) {
5536104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++)
5546104c370SDaniel Vetter 			con2fb_map[i] = -1;
5556104c370SDaniel Vetter 		info_idx = -1;
5566104c370SDaniel Vetter 	} else {
5576104c370SDaniel Vetter 		fbcon_has_console_bind = 1;
5586104c370SDaniel Vetter 	}
5596104c370SDaniel Vetter 
5606104c370SDaniel Vetter 	return err;
5616104c370SDaniel Vetter }
5626104c370SDaniel Vetter 
5636104c370SDaniel Vetter #ifdef MODULE
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)5646104c370SDaniel Vetter static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
5656104c370SDaniel Vetter 			       int cols, int rows, int new_cols, int new_rows)
5666104c370SDaniel Vetter {
5676104c370SDaniel Vetter 	logo_shown = FBCON_LOGO_DONTSHOW;
5686104c370SDaniel Vetter }
5696104c370SDaniel Vetter #else
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)5706104c370SDaniel Vetter static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
5716104c370SDaniel Vetter 			       int cols, int rows, int new_cols, int new_rows)
5726104c370SDaniel Vetter {
5736104c370SDaniel Vetter 	/* Need to make room for the logo */
5746104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
5756104c370SDaniel Vetter 	int cnt, erase = vc->vc_video_erase_char, step;
5766104c370SDaniel Vetter 	unsigned short *save = NULL, *r, *q;
5776104c370SDaniel Vetter 	int logo_height;
5786104c370SDaniel Vetter 
579376b3ff5SDaniel Vetter 	if (info->fbops->owner) {
5806104c370SDaniel Vetter 		logo_shown = FBCON_LOGO_DONTSHOW;
5816104c370SDaniel Vetter 		return;
5826104c370SDaniel Vetter 	}
5836104c370SDaniel Vetter 
5846104c370SDaniel Vetter 	/*
5856104c370SDaniel Vetter 	 * remove underline attribute from erase character
5866104c370SDaniel Vetter 	 * if black and white framebuffer.
5876104c370SDaniel Vetter 	 */
5886104c370SDaniel Vetter 	if (fb_get_color_depth(&info->var, &info->fix) == 1)
5896104c370SDaniel Vetter 		erase &= ~0x400;
5906104c370SDaniel Vetter 	logo_height = fb_prepare_logo(info, ops->rotate);
5916104c370SDaniel Vetter 	logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
5926104c370SDaniel Vetter 	q = (unsigned short *) (vc->vc_origin +
5936104c370SDaniel Vetter 				vc->vc_size_row * rows);
5946104c370SDaniel Vetter 	step = logo_lines * cols;
5956104c370SDaniel Vetter 	for (r = q - logo_lines * cols; r < q; r++)
5966104c370SDaniel Vetter 		if (scr_readw(r) != vc->vc_video_erase_char)
5976104c370SDaniel Vetter 			break;
5986104c370SDaniel Vetter 	if (r != q && new_rows >= rows + logo_lines) {
5995c380526SGeert Uytterhoeven 		save = kmalloc(array3_size(logo_lines, new_cols, 2),
6006da2ec56SKees Cook 			       GFP_KERNEL);
6016104c370SDaniel Vetter 		if (save) {
60262c6f4f9SChangcheng Deng 			int i = min(cols, new_cols);
603fcf918b9SGustavo A. R. Silva 			scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
6046104c370SDaniel Vetter 			r = q - step;
6056104c370SDaniel Vetter 			for (cnt = 0; cnt < logo_lines; cnt++, r += i)
6066104c370SDaniel Vetter 				scr_memcpyw(save + cnt * new_cols, r, 2 * i);
6076104c370SDaniel Vetter 			r = q;
6086104c370SDaniel Vetter 		}
6096104c370SDaniel Vetter 	}
6106104c370SDaniel Vetter 	if (r == q) {
6116104c370SDaniel Vetter 		/* We can scroll screen down */
6126104c370SDaniel Vetter 		r = q - step - cols;
6136104c370SDaniel Vetter 		for (cnt = rows - logo_lines; cnt > 0; cnt--) {
6146104c370SDaniel Vetter 			scr_memcpyw(r + step, r, vc->vc_size_row);
6156104c370SDaniel Vetter 			r -= cols;
6166104c370SDaniel Vetter 		}
6176104c370SDaniel Vetter 		if (!save) {
6186104c370SDaniel Vetter 			int lines;
61928bc24fcSJiri Slaby 			if (vc->state.y + logo_lines >= rows)
62028bc24fcSJiri Slaby 				lines = rows - vc->state.y - 1;
6216104c370SDaniel Vetter 			else
6226104c370SDaniel Vetter 				lines = logo_lines;
62328bc24fcSJiri Slaby 			vc->state.y += lines;
6246104c370SDaniel Vetter 			vc->vc_pos += lines * vc->vc_size_row;
6256104c370SDaniel Vetter 		}
6266104c370SDaniel Vetter 	}
6276104c370SDaniel Vetter 	scr_memsetw((unsigned short *) vc->vc_origin,
6286104c370SDaniel Vetter 		    erase,
6296104c370SDaniel Vetter 		    vc->vc_size_row * logo_lines);
6306104c370SDaniel Vetter 
6316104c370SDaniel Vetter 	if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
6326104c370SDaniel Vetter 		fbcon_clear_margins(vc, 0);
6336104c370SDaniel Vetter 		update_screen(vc);
6346104c370SDaniel Vetter 	}
6356104c370SDaniel Vetter 
6366104c370SDaniel Vetter 	if (save) {
6376104c370SDaniel Vetter 		q = (unsigned short *) (vc->vc_origin +
6386104c370SDaniel Vetter 					vc->vc_size_row *
6396104c370SDaniel Vetter 					rows);
640fcf918b9SGustavo A. R. Silva 		scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
64128bc24fcSJiri Slaby 		vc->state.y += logo_lines;
6426104c370SDaniel Vetter 		vc->vc_pos += logo_lines * vc->vc_size_row;
6436104c370SDaniel Vetter 		kfree(save);
6446104c370SDaniel Vetter 	}
6456104c370SDaniel Vetter 
64610993504SPrarit Bhargava 	if (logo_shown == FBCON_LOGO_DONTSHOW)
64710993504SPrarit Bhargava 		return;
64810993504SPrarit Bhargava 
6496104c370SDaniel Vetter 	if (logo_lines > vc->vc_bottom) {
6506104c370SDaniel Vetter 		logo_shown = FBCON_LOGO_CANSHOW;
651018856c3SGeert Uytterhoeven 		pr_info("fbcon: disable boot-logo (boot-logo bigger than screen).\n");
65210993504SPrarit Bhargava 	} else {
6536104c370SDaniel Vetter 		logo_shown = FBCON_LOGO_DRAW;
6546104c370SDaniel Vetter 		vc->vc_top = logo_lines;
6556104c370SDaniel Vetter 	}
6566104c370SDaniel Vetter }
6576104c370SDaniel Vetter #endif /* MODULE */
6586104c370SDaniel Vetter 
6596104c370SDaniel Vetter #ifdef CONFIG_FB_TILEBLITTING
set_blitting_type(struct vc_data * vc,struct fb_info * info)6606104c370SDaniel Vetter static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
6616104c370SDaniel Vetter {
6626104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
6636104c370SDaniel Vetter 
6646104c370SDaniel Vetter 	ops->p = &fb_display[vc->vc_num];
6656104c370SDaniel Vetter 
6666104c370SDaniel Vetter 	if ((info->flags & FBINFO_MISC_TILEBLITTING))
6676104c370SDaniel Vetter 		fbcon_set_tileops(vc, info);
6686104c370SDaniel Vetter 	else {
6696104c370SDaniel Vetter 		fbcon_set_rotation(info);
6706104c370SDaniel Vetter 		fbcon_set_bitops(ops);
6716104c370SDaniel Vetter 	}
6726104c370SDaniel Vetter }
6736104c370SDaniel Vetter 
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)6746104c370SDaniel Vetter static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
6756104c370SDaniel Vetter {
6766104c370SDaniel Vetter 	int err = 0;
6776104c370SDaniel Vetter 
6786104c370SDaniel Vetter 	if (info->flags & FBINFO_MISC_TILEBLITTING &&
6796104c370SDaniel Vetter 	    info->tileops->fb_get_tilemax(info) < charcount)
6806104c370SDaniel Vetter 		err = 1;
6816104c370SDaniel Vetter 
6826104c370SDaniel Vetter 	return err;
6836104c370SDaniel Vetter }
6846104c370SDaniel Vetter #else
set_blitting_type(struct vc_data * vc,struct fb_info * info)6856104c370SDaniel Vetter static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
6866104c370SDaniel Vetter {
6876104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
6886104c370SDaniel Vetter 
6896104c370SDaniel Vetter 	info->flags &= ~FBINFO_MISC_TILEBLITTING;
6906104c370SDaniel Vetter 	ops->p = &fb_display[vc->vc_num];
6916104c370SDaniel Vetter 	fbcon_set_rotation(info);
6926104c370SDaniel Vetter 	fbcon_set_bitops(ops);
6936104c370SDaniel Vetter }
6946104c370SDaniel Vetter 
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)6956104c370SDaniel Vetter static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
6966104c370SDaniel Vetter {
6976104c370SDaniel Vetter 	return 0;
6986104c370SDaniel Vetter }
6996104c370SDaniel Vetter 
7006104c370SDaniel Vetter #endif /* CONFIG_MISC_TILEBLITTING */
7016104c370SDaniel Vetter 
fbcon_release(struct fb_info * info)702d443d938SDaniel Vetter static void fbcon_release(struct fb_info *info)
703d443d938SDaniel Vetter {
70404933a29SDaniel Vetter 	lock_fb_info(info);
705d443d938SDaniel Vetter 	if (info->fbops->fb_release)
706d443d938SDaniel Vetter 		info->fbops->fb_release(info, 0);
70704933a29SDaniel Vetter 	unlock_fb_info(info);
708d443d938SDaniel Vetter 
709d443d938SDaniel Vetter 	module_put(info->fbops->owner);
7103647d6d3SDaniel Vetter 
7113647d6d3SDaniel Vetter 	if (info->fbcon_par) {
7123647d6d3SDaniel Vetter 		struct fbcon_ops *ops = info->fbcon_par;
7133647d6d3SDaniel Vetter 
7143647d6d3SDaniel Vetter 		fbcon_del_cursor_work(info);
7153647d6d3SDaniel Vetter 		kfree(ops->cursor_state.mask);
7163647d6d3SDaniel Vetter 		kfree(ops->cursor_data);
7173647d6d3SDaniel Vetter 		kfree(ops->cursor_src);
7183647d6d3SDaniel Vetter 		kfree(ops->fontbuffer);
7193647d6d3SDaniel Vetter 		kfree(info->fbcon_par);
7203647d6d3SDaniel Vetter 		info->fbcon_par = NULL;
7213647d6d3SDaniel Vetter 	}
722d443d938SDaniel Vetter }
723d443d938SDaniel Vetter 
fbcon_open(struct fb_info * info)724bd6026a8SDaniel Vetter static int fbcon_open(struct fb_info *info)
725bd6026a8SDaniel Vetter {
726d443d938SDaniel Vetter 	struct fbcon_ops *ops;
727d443d938SDaniel Vetter 
728bd6026a8SDaniel Vetter 	if (!try_module_get(info->fbops->owner))
729bd6026a8SDaniel Vetter 		return -ENODEV;
730bd6026a8SDaniel Vetter 
73104933a29SDaniel Vetter 	lock_fb_info(info);
732bd6026a8SDaniel Vetter 	if (info->fbops->fb_open &&
733bd6026a8SDaniel Vetter 	    info->fbops->fb_open(info, 0)) {
73404933a29SDaniel Vetter 		unlock_fb_info(info);
735bd6026a8SDaniel Vetter 		module_put(info->fbops->owner);
736bd6026a8SDaniel Vetter 		return -ENODEV;
737bd6026a8SDaniel Vetter 	}
73804933a29SDaniel Vetter 	unlock_fb_info(info);
739bd6026a8SDaniel Vetter 
740d443d938SDaniel Vetter 	ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
741d443d938SDaniel Vetter 	if (!ops) {
742d443d938SDaniel Vetter 		fbcon_release(info);
743d443d938SDaniel Vetter 		return -ENOMEM;
744d443d938SDaniel Vetter 	}
745d443d938SDaniel Vetter 
746d443d938SDaniel Vetter 	INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor);
747d443d938SDaniel Vetter 	ops->info = info;
748d443d938SDaniel Vetter 	info->fbcon_par = ops;
749d443d938SDaniel Vetter 	ops->cur_blink_jiffies = HZ / 5;
750d443d938SDaniel Vetter 
751bd6026a8SDaniel Vetter 	return 0;
752bd6026a8SDaniel Vetter }
753bd6026a8SDaniel Vetter 
con2fb_acquire_newinfo(struct vc_data * vc,struct fb_info * info,int unit)7546104c370SDaniel Vetter static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
755d443d938SDaniel Vetter 				  int unit)
7566104c370SDaniel Vetter {
757bd6026a8SDaniel Vetter 	int err;
7586104c370SDaniel Vetter 
759bd6026a8SDaniel Vetter 	err = fbcon_open(info);
760bd6026a8SDaniel Vetter 	if (err)
761bd6026a8SDaniel Vetter 		return err;
7626104c370SDaniel Vetter 
7636104c370SDaniel Vetter 	if (vc)
7646104c370SDaniel Vetter 		set_blitting_type(vc, info);
7656104c370SDaniel Vetter 
7666104c370SDaniel Vetter 	return err;
7676104c370SDaniel Vetter }
7686104c370SDaniel Vetter 
con2fb_release_oldinfo(struct vc_data * vc,struct fb_info * oldinfo,struct fb_info * newinfo)769b07db395SDaniel Vetter static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
770b07db395SDaniel Vetter 				   struct fb_info *newinfo)
7716104c370SDaniel Vetter {
772bd6026a8SDaniel Vetter 	int ret;
7736104c370SDaniel Vetter 
774bd6026a8SDaniel Vetter 	fbcon_release(oldinfo);
7756104c370SDaniel Vetter 
7766104c370SDaniel Vetter 	/*
7776104c370SDaniel Vetter 	  If oldinfo and newinfo are driving the same hardware,
7786104c370SDaniel Vetter 	  the fb_release() method of oldinfo may attempt to
7796104c370SDaniel Vetter 	  restore the hardware state.  This will leave the
7806104c370SDaniel Vetter 	  newinfo in an undefined state. Thus, a call to
7816104c370SDaniel Vetter 	  fb_set_par() may be needed for the newinfo.
7826104c370SDaniel Vetter 	*/
7836104c370SDaniel Vetter 	if (newinfo && newinfo->fbops->fb_set_par) {
7846104c370SDaniel Vetter 		ret = newinfo->fbops->fb_set_par(newinfo);
7856104c370SDaniel Vetter 
7866104c370SDaniel Vetter 		if (ret)
7876104c370SDaniel Vetter 			printk(KERN_ERR "con2fb_release_oldinfo: "
7886104c370SDaniel Vetter 				"detected unhandled fb_set_par error, "
7896104c370SDaniel Vetter 				"error code %d\n", ret);
7906104c370SDaniel Vetter 	}
7916104c370SDaniel Vetter }
7926104c370SDaniel Vetter 
con2fb_init_display(struct vc_data * vc,struct fb_info * info,int unit,int show_logo)7936104c370SDaniel Vetter static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
7946104c370SDaniel Vetter 				int unit, int show_logo)
7956104c370SDaniel Vetter {
7966104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
7976104c370SDaniel Vetter 	int ret;
7986104c370SDaniel Vetter 
7996104c370SDaniel Vetter 	ops->currcon = fg_console;
8006104c370SDaniel Vetter 
801cae69e45SDaniel Vetter 	if (info->fbops->fb_set_par && !ops->initialized) {
8026104c370SDaniel Vetter 		ret = info->fbops->fb_set_par(info);
8036104c370SDaniel Vetter 
8046104c370SDaniel Vetter 		if (ret)
8056104c370SDaniel Vetter 			printk(KERN_ERR "con2fb_init_display: detected "
8066104c370SDaniel Vetter 				"unhandled fb_set_par error, "
8076104c370SDaniel Vetter 				"error code %d\n", ret);
8086104c370SDaniel Vetter 	}
8096104c370SDaniel Vetter 
810cae69e45SDaniel Vetter 	ops->initialized = true;
8116104c370SDaniel Vetter 	ops->graphics = 0;
8126104c370SDaniel Vetter 	fbcon_set_disp(info, &info->var, unit);
8136104c370SDaniel Vetter 
8146104c370SDaniel Vetter 	if (show_logo) {
8156104c370SDaniel Vetter 		struct vc_data *fg_vc = vc_cons[fg_console].d;
8166104c370SDaniel Vetter 		struct fb_info *fg_info =
817409d6c95SDaniel Vetter 			fbcon_info_from_console(fg_console);
8186104c370SDaniel Vetter 
8196104c370SDaniel Vetter 		fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
8206104c370SDaniel Vetter 				   fg_vc->vc_rows, fg_vc->vc_cols,
8216104c370SDaniel Vetter 				   fg_vc->vc_rows);
8226104c370SDaniel Vetter 	}
8236104c370SDaniel Vetter 
8246104c370SDaniel Vetter 	update_screen(vc_cons[fg_console].d);
8256104c370SDaniel Vetter }
8266104c370SDaniel Vetter 
8276104c370SDaniel Vetter /**
8286104c370SDaniel Vetter  *	set_con2fb_map - map console to frame buffer device
8296104c370SDaniel Vetter  *	@unit: virtual console number to map
8306104c370SDaniel Vetter  *	@newidx: frame buffer index to map virtual console to
8316104c370SDaniel Vetter  *      @user: user request
8326104c370SDaniel Vetter  *
8336104c370SDaniel Vetter  *	Maps a virtual console @unit to a frame buffer device
8346104c370SDaniel Vetter  *	@newidx.
8356104c370SDaniel Vetter  *
8366104c370SDaniel Vetter  *	This should be called with the console lock held.
8376104c370SDaniel Vetter  */
set_con2fb_map(int unit,int newidx,int user)8386104c370SDaniel Vetter static int set_con2fb_map(int unit, int newidx, int user)
8396104c370SDaniel Vetter {
8406104c370SDaniel Vetter 	struct vc_data *vc = vc_cons[unit].d;
8416104c370SDaniel Vetter 	int oldidx = con2fb_map[unit];
842efc3acbcSDaniel Vetter 	struct fb_info *info = fbcon_registered_fb[newidx];
8436104c370SDaniel Vetter 	struct fb_info *oldinfo = NULL;
844edf79dd2SDaniel Vetter 	int err = 0, show_logo;
8456104c370SDaniel Vetter 
8463bd3a0e3SHans de Goede 	WARN_CONSOLE_UNLOCKED();
8473bd3a0e3SHans de Goede 
8486104c370SDaniel Vetter 	if (oldidx == newidx)
8496104c370SDaniel Vetter 		return 0;
8506104c370SDaniel Vetter 
8516104c370SDaniel Vetter 	if (!info)
8526104c370SDaniel Vetter 		return -EINVAL;
8536104c370SDaniel Vetter 
8546104c370SDaniel Vetter 	if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
8556104c370SDaniel Vetter 		info_idx = newidx;
8566104c370SDaniel Vetter 		return do_fbcon_takeover(0);
8576104c370SDaniel Vetter 	}
8586104c370SDaniel Vetter 
8596104c370SDaniel Vetter 	if (oldidx != -1)
860efc3acbcSDaniel Vetter 		oldinfo = fbcon_registered_fb[oldidx];
8616104c370SDaniel Vetter 
862edf79dd2SDaniel Vetter 	if (!search_fb_in_map(newidx)) {
863d443d938SDaniel Vetter 		err = con2fb_acquire_newinfo(vc, info, unit);
864edf79dd2SDaniel Vetter 		if (err)
865edf79dd2SDaniel Vetter 			return err;
866edf79dd2SDaniel Vetter 
867edf79dd2SDaniel Vetter 		fbcon_add_cursor_work(info);
8685b97eebcSQianqiang Liu 	} else if (vc) {
8695b97eebcSQianqiang Liu 		set_blitting_type(vc, info);
870d443d938SDaniel Vetter 	}
8716104c370SDaniel Vetter 
872fffb0b52SDaniel Vetter 	con2fb_map[unit] = newidx;
873fffb0b52SDaniel Vetter 
8746104c370SDaniel Vetter 	/*
8756104c370SDaniel Vetter 	 * If old fb is not mapped to any of the consoles,
8766104c370SDaniel Vetter 	 * fbcon should release it.
8776104c370SDaniel Vetter 	 */
878edf79dd2SDaniel Vetter 	if (oldinfo && !search_fb_in_map(oldidx))
879b07db395SDaniel Vetter 		con2fb_release_oldinfo(vc, oldinfo, info);
8806104c370SDaniel Vetter 
881b07db395SDaniel Vetter 	show_logo = (fg_console == 0 && !user &&
8826104c370SDaniel Vetter 			 logo_shown != FBCON_LOGO_DONTSHOW);
8836104c370SDaniel Vetter 
8846104c370SDaniel Vetter 	con2fb_map_boot[unit] = newidx;
8856104c370SDaniel Vetter 	con2fb_init_display(vc, info, unit, show_logo);
8866104c370SDaniel Vetter 
8876104c370SDaniel Vetter 	if (!search_fb_in_map(info_idx))
8886104c370SDaniel Vetter 		info_idx = newidx;
8896104c370SDaniel Vetter 
8906104c370SDaniel Vetter 	return err;
8916104c370SDaniel Vetter }
8926104c370SDaniel Vetter 
8936104c370SDaniel Vetter /*
8946104c370SDaniel Vetter  *  Low Level Operations
8956104c370SDaniel Vetter  */
8966104c370SDaniel Vetter /* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
var_to_display(struct fbcon_display * disp,struct fb_var_screeninfo * var,struct fb_info * info)89750233393SDaniel Vetter static int var_to_display(struct fbcon_display *disp,
8986104c370SDaniel Vetter 			  struct fb_var_screeninfo *var,
8996104c370SDaniel Vetter 			  struct fb_info *info)
9006104c370SDaniel Vetter {
9016104c370SDaniel Vetter 	disp->xres_virtual = var->xres_virtual;
9026104c370SDaniel Vetter 	disp->yres_virtual = var->yres_virtual;
9036104c370SDaniel Vetter 	disp->bits_per_pixel = var->bits_per_pixel;
9046104c370SDaniel Vetter 	disp->grayscale = var->grayscale;
9056104c370SDaniel Vetter 	disp->nonstd = var->nonstd;
9066104c370SDaniel Vetter 	disp->accel_flags = var->accel_flags;
9076104c370SDaniel Vetter 	disp->height = var->height;
9086104c370SDaniel Vetter 	disp->width = var->width;
9096104c370SDaniel Vetter 	disp->red = var->red;
9106104c370SDaniel Vetter 	disp->green = var->green;
9116104c370SDaniel Vetter 	disp->blue = var->blue;
9126104c370SDaniel Vetter 	disp->transp = var->transp;
9136104c370SDaniel Vetter 	disp->rotate = var->rotate;
9146104c370SDaniel Vetter 	disp->mode = fb_match_mode(var, &info->modelist);
9156104c370SDaniel Vetter 	if (disp->mode == NULL)
9166104c370SDaniel Vetter 		/* This should not happen */
9176104c370SDaniel Vetter 		return -EINVAL;
9186104c370SDaniel Vetter 	return 0;
9196104c370SDaniel Vetter }
9206104c370SDaniel Vetter 
display_to_var(struct fb_var_screeninfo * var,struct fbcon_display * disp)9216104c370SDaniel Vetter static void display_to_var(struct fb_var_screeninfo *var,
92250233393SDaniel Vetter 			   struct fbcon_display *disp)
9236104c370SDaniel Vetter {
9246104c370SDaniel Vetter 	fb_videomode_to_var(var, disp->mode);
9256104c370SDaniel Vetter 	var->xres_virtual = disp->xres_virtual;
9266104c370SDaniel Vetter 	var->yres_virtual = disp->yres_virtual;
9276104c370SDaniel Vetter 	var->bits_per_pixel = disp->bits_per_pixel;
9286104c370SDaniel Vetter 	var->grayscale = disp->grayscale;
9296104c370SDaniel Vetter 	var->nonstd = disp->nonstd;
9306104c370SDaniel Vetter 	var->accel_flags = disp->accel_flags;
9316104c370SDaniel Vetter 	var->height = disp->height;
9326104c370SDaniel Vetter 	var->width = disp->width;
9336104c370SDaniel Vetter 	var->red = disp->red;
9346104c370SDaniel Vetter 	var->green = disp->green;
9356104c370SDaniel Vetter 	var->blue = disp->blue;
9366104c370SDaniel Vetter 	var->transp = disp->transp;
9376104c370SDaniel Vetter 	var->rotate = disp->rotate;
9386104c370SDaniel Vetter }
9396104c370SDaniel Vetter 
fbcon_startup(void)9406104c370SDaniel Vetter static const char *fbcon_startup(void)
9416104c370SDaniel Vetter {
942b041c605SJiri Slaby (SUSE) 	static const char display_desc[] = "frame buffer device";
94350233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[fg_console];
9446104c370SDaniel Vetter 	struct vc_data *vc = vc_cons[fg_console].d;
9456104c370SDaniel Vetter 	const struct font_desc *font = NULL;
9466104c370SDaniel Vetter 	struct fb_info *info = NULL;
9476104c370SDaniel Vetter 	struct fbcon_ops *ops;
9486104c370SDaniel Vetter 	int rows, cols;
9496104c370SDaniel Vetter 
9506104c370SDaniel Vetter 	/*
9516104c370SDaniel Vetter 	 *  If num_registered_fb is zero, this is a call for the dummy part.
9526104c370SDaniel Vetter 	 *  The frame buffer devices weren't initialized yet.
9536104c370SDaniel Vetter 	 */
954efc3acbcSDaniel Vetter 	if (!fbcon_num_registered_fb || info_idx == -1)
9556104c370SDaniel Vetter 		return display_desc;
9566104c370SDaniel Vetter 	/*
9576104c370SDaniel Vetter 	 * Instead of blindly using registered_fb[0], we use info_idx, set by
9589b0a490eSDaniel Vetter 	 * fbcon_fb_registered();
9596104c370SDaniel Vetter 	 */
960efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[info_idx];
9616104c370SDaniel Vetter 	if (!info)
9626104c370SDaniel Vetter 		return NULL;
9636104c370SDaniel Vetter 
964bd6026a8SDaniel Vetter 	if (fbcon_open(info))
9656104c370SDaniel Vetter 		return NULL;
9666104c370SDaniel Vetter 
967d443d938SDaniel Vetter 	ops = info->fbcon_par;
9686104c370SDaniel Vetter 	ops->currcon = -1;
9696104c370SDaniel Vetter 	ops->graphics = 1;
9706104c370SDaniel Vetter 	ops->cur_rotate = -1;
971c9e6a364SHans de Goede 
9726104c370SDaniel Vetter 	p->con_rotate = initial_rotation;
973c9e6a364SHans de Goede 	if (p->con_rotate == -1)
974c9e6a364SHans de Goede 		p->con_rotate = info->fbcon_rotate_hint;
975c9e6a364SHans de Goede 	if (p->con_rotate == -1)
976f2f4946bSHans de Goede 		p->con_rotate = FB_ROTATE_UR;
977c9e6a364SHans de Goede 
9786104c370SDaniel Vetter 	set_blitting_type(vc, info);
9796104c370SDaniel Vetter 
9806104c370SDaniel Vetter 	/* Setup default font */
98112d5796dSThomas Zimmermann 	if (!p->fontdata) {
9826104c370SDaniel Vetter 		if (!fontname[0] || !(font = find_font(fontname)))
9836104c370SDaniel Vetter 			font = get_default_font(info->var.xres,
9846104c370SDaniel Vetter 						info->var.yres,
9856104c370SDaniel Vetter 						info->pixmap.blit_x,
9866104c370SDaniel Vetter 						info->pixmap.blit_y);
9876104c370SDaniel Vetter 		vc->vc_font.width = font->width;
9886104c370SDaniel Vetter 		vc->vc_font.height = font->height;
9896104c370SDaniel Vetter 		vc->vc_font.data = (void *)(p->fontdata = font->data);
990a1ac250aSPeilin Ye 		vc->vc_font.charcount = font->charcount;
9916104c370SDaniel Vetter 	}
9926104c370SDaniel Vetter 
9936104c370SDaniel Vetter 	cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
9946104c370SDaniel Vetter 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
9956104c370SDaniel Vetter 	cols /= vc->vc_font.width;
9966104c370SDaniel Vetter 	rows /= vc->vc_font.height;
9976104c370SDaniel Vetter 	vc_resize(vc, cols, rows);
9986104c370SDaniel Vetter 
999b1cba76dSSam Ravnborg 	pr_debug("mode:   %s\n", info->fix.id);
1000b1cba76dSSam Ravnborg 	pr_debug("visual: %d\n", info->fix.visual);
1001b1cba76dSSam Ravnborg 	pr_debug("res:    %dx%d-%d\n", info->var.xres,
10026104c370SDaniel Vetter 		 info->var.yres,
10036104c370SDaniel Vetter 		 info->var.bits_per_pixel);
10046104c370SDaniel Vetter 
10053b0fb6abSDaniel Vetter 	fbcon_add_cursor_work(info);
10066104c370SDaniel Vetter 	return display_desc;
10076104c370SDaniel Vetter }
10086104c370SDaniel Vetter 
fbcon_init(struct vc_data * vc,bool init)1009dae3e6b6SJiri Slaby (SUSE) static void fbcon_init(struct vc_data *vc, bool init)
10106104c370SDaniel Vetter {
10111f4ed2fbSDaniel Vetter 	struct fb_info *info;
10126104c370SDaniel Vetter 	struct fbcon_ops *ops;
10136104c370SDaniel Vetter 	struct vc_data **default_mode = vc->vc_display_fg;
10146104c370SDaniel Vetter 	struct vc_data *svc = *default_mode;
101550233393SDaniel Vetter 	struct fbcon_display *t, *p = &fb_display[vc->vc_num];
1016a1ac250aSPeilin Ye 	int logo = 1, new_rows, new_cols, rows, cols;
101750b10528SHelge Deller 	int ret;
10186104c370SDaniel Vetter 
10191f4ed2fbSDaniel Vetter 	if (WARN_ON(info_idx == -1))
10206104c370SDaniel Vetter 	    return;
10216104c370SDaniel Vetter 
10221f4ed2fbSDaniel Vetter 	if (con2fb_map[vc->vc_num] == -1)
10231f4ed2fbSDaniel Vetter 		con2fb_map[vc->vc_num] = info_idx;
10241f4ed2fbSDaniel Vetter 
1025409d6c95SDaniel Vetter 	info = fbcon_info_from_console(vc->vc_num);
10266104c370SDaniel Vetter 
10273c5a1b11SAndreas Schwab 	if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
102810993504SPrarit Bhargava 		logo_shown = FBCON_LOGO_DONTSHOW;
102910993504SPrarit Bhargava 
10306104c370SDaniel Vetter 	if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
10316104c370SDaniel Vetter 	    (info->fix.type == FB_TYPE_TEXT))
10326104c370SDaniel Vetter 		logo = 0;
10336104c370SDaniel Vetter 
10346104c370SDaniel Vetter 	if (var_to_display(p, &info->var, info))
10356104c370SDaniel Vetter 		return;
10366104c370SDaniel Vetter 
10376104c370SDaniel Vetter 	if (!info->fbcon_par)
1038d443d938SDaniel Vetter 		con2fb_acquire_newinfo(vc, info, vc->vc_num);
10396104c370SDaniel Vetter 
10406104c370SDaniel Vetter 	/* If we are not the first console on this
10416104c370SDaniel Vetter 	   fb, copy the font from that console */
10426104c370SDaniel Vetter 	t = &fb_display[fg_console];
10436104c370SDaniel Vetter 	if (!p->fontdata) {
10446104c370SDaniel Vetter 		if (t->fontdata) {
10456104c370SDaniel Vetter 			struct vc_data *fvc = vc_cons[fg_console].d;
10466104c370SDaniel Vetter 
10476104c370SDaniel Vetter 			vc->vc_font.data = (void *)(p->fontdata =
10486104c370SDaniel Vetter 						    fvc->vc_font.data);
10496104c370SDaniel Vetter 			vc->vc_font.width = fvc->vc_font.width;
10506104c370SDaniel Vetter 			vc->vc_font.height = fvc->vc_font.height;
1051a1ac250aSPeilin Ye 			vc->vc_font.charcount = fvc->vc_font.charcount;
10526104c370SDaniel Vetter 			p->userfont = t->userfont;
10536104c370SDaniel Vetter 
10546104c370SDaniel Vetter 			if (p->userfont)
10556104c370SDaniel Vetter 				REFCOUNT(p->fontdata)++;
10566104c370SDaniel Vetter 		} else {
10576104c370SDaniel Vetter 			const struct font_desc *font = NULL;
10586104c370SDaniel Vetter 
10596104c370SDaniel Vetter 			if (!fontname[0] || !(font = find_font(fontname)))
10606104c370SDaniel Vetter 				font = get_default_font(info->var.xres,
10616104c370SDaniel Vetter 							info->var.yres,
10626104c370SDaniel Vetter 							info->pixmap.blit_x,
10636104c370SDaniel Vetter 							info->pixmap.blit_y);
10646104c370SDaniel Vetter 			vc->vc_font.width = font->width;
10656104c370SDaniel Vetter 			vc->vc_font.height = font->height;
10666104c370SDaniel Vetter 			vc->vc_font.data = (void *)(p->fontdata = font->data);
1067a1ac250aSPeilin Ye 			vc->vc_font.charcount = font->charcount;
10686104c370SDaniel Vetter 		}
10696104c370SDaniel Vetter 	}
10706104c370SDaniel Vetter 
10716104c370SDaniel Vetter 	vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
10726104c370SDaniel Vetter 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1073a1ac250aSPeilin Ye 	if (vc->vc_font.charcount == 256) {
10746104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0;
10756104c370SDaniel Vetter 	} else {
10766104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0x100;
10776104c370SDaniel Vetter 		if (vc->vc_can_do_color)
10786104c370SDaniel Vetter 			vc->vc_complement_mask <<= 1;
10796104c370SDaniel Vetter 	}
10806104c370SDaniel Vetter 
10818da443b1SJiri Slaby 	if (!*svc->uni_pagedict_loc)
10826104c370SDaniel Vetter 		con_set_default_unimap(svc);
10838da443b1SJiri Slaby 	if (!*vc->uni_pagedict_loc)
10846104c370SDaniel Vetter 		con_copy_unimap(vc, svc);
10856104c370SDaniel Vetter 
10866104c370SDaniel Vetter 	ops = info->fbcon_par;
10876104c370SDaniel Vetter 	ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1088c9e6a364SHans de Goede 
10896104c370SDaniel Vetter 	p->con_rotate = initial_rotation;
1090c9e6a364SHans de Goede 	if (p->con_rotate == -1)
1091c9e6a364SHans de Goede 		p->con_rotate = info->fbcon_rotate_hint;
1092c9e6a364SHans de Goede 	if (p->con_rotate == -1)
1093f2f4946bSHans de Goede 		p->con_rotate = FB_ROTATE_UR;
1094c9e6a364SHans de Goede 
10956104c370SDaniel Vetter 	set_blitting_type(vc, info);
10966104c370SDaniel Vetter 
10976104c370SDaniel Vetter 	cols = vc->vc_cols;
10986104c370SDaniel Vetter 	rows = vc->vc_rows;
10996104c370SDaniel Vetter 	new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
11006104c370SDaniel Vetter 	new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
11016104c370SDaniel Vetter 	new_cols /= vc->vc_font.width;
11026104c370SDaniel Vetter 	new_rows /= vc->vc_font.height;
11036104c370SDaniel Vetter 
11046104c370SDaniel Vetter 	/*
11056104c370SDaniel Vetter 	 * We must always set the mode. The mode of the previous console
11066104c370SDaniel Vetter 	 * driver could be in the same resolution but we are using different
11076104c370SDaniel Vetter 	 * hardware so we have to initialize the hardware.
11086104c370SDaniel Vetter 	 *
11096104c370SDaniel Vetter 	 * We need to do it in fbcon_init() to prevent screen corruption.
11106104c370SDaniel Vetter 	 */
11116104c370SDaniel Vetter 	if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1112cae69e45SDaniel Vetter 		if (info->fbops->fb_set_par && !ops->initialized) {
11136104c370SDaniel Vetter 			ret = info->fbops->fb_set_par(info);
11146104c370SDaniel Vetter 
11156104c370SDaniel Vetter 			if (ret)
11166104c370SDaniel Vetter 				printk(KERN_ERR "fbcon_init: detected "
11176104c370SDaniel Vetter 					"unhandled fb_set_par error, "
11186104c370SDaniel Vetter 					"error code %d\n", ret);
11196104c370SDaniel Vetter 		}
11206104c370SDaniel Vetter 
1121cae69e45SDaniel Vetter 		ops->initialized = true;
11226104c370SDaniel Vetter 	}
11236104c370SDaniel Vetter 
11246104c370SDaniel Vetter 	ops->graphics = 0;
11256104c370SDaniel Vetter 
1126a3f781a9SHelge Deller #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
112750b10528SHelge Deller 	if ((info->flags & FBINFO_HWACCEL_COPYAREA) &&
112850b10528SHelge Deller 	    !(info->flags & FBINFO_HWACCEL_DISABLED))
112987ab9f6bSHelge Deller 		p->scrollmode = SCROLL_MOVE;
113087ab9f6bSHelge Deller 	else /* default to something safe */
11311148836fSHelge Deller 		p->scrollmode = SCROLL_REDRAW;
1132a3f781a9SHelge Deller #endif
11331148836fSHelge Deller 
11341148836fSHelge Deller 	/*
11356104c370SDaniel Vetter 	 *  ++guenther: console.c:vc_allocate() relies on initializing
11366104c370SDaniel Vetter 	 *  vc_{cols,rows}, but we must not set those if we are only
11376104c370SDaniel Vetter 	 *  resizing the console.
11386104c370SDaniel Vetter 	 */
11396104c370SDaniel Vetter 	if (init) {
11406104c370SDaniel Vetter 		vc->vc_cols = new_cols;
11416104c370SDaniel Vetter 		vc->vc_rows = new_rows;
11426104c370SDaniel Vetter 	} else
11436104c370SDaniel Vetter 		vc_resize(vc, new_cols, new_rows);
11446104c370SDaniel Vetter 
11456104c370SDaniel Vetter 	if (logo)
11466104c370SDaniel Vetter 		fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
11476104c370SDaniel Vetter 
11486104c370SDaniel Vetter 	if (ops->rotate_font && ops->rotate_font(info, vc)) {
11496104c370SDaniel Vetter 		ops->rotate = FB_ROTATE_UR;
11506104c370SDaniel Vetter 		set_blitting_type(vc, info);
11516104c370SDaniel Vetter 	}
11526104c370SDaniel Vetter 
11536104c370SDaniel Vetter 	ops->p = &fb_display[fg_console];
11546104c370SDaniel Vetter }
11556104c370SDaniel Vetter 
fbcon_free_font(struct fbcon_display * p)115612d5796dSThomas Zimmermann static void fbcon_free_font(struct fbcon_display *p)
11576104c370SDaniel Vetter {
115812d5796dSThomas Zimmermann 	if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
11596104c370SDaniel Vetter 		kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
11606104c370SDaniel Vetter 	p->fontdata = NULL;
11616104c370SDaniel Vetter 	p->userfont = 0;
11626104c370SDaniel Vetter }
11636104c370SDaniel Vetter 
11646104c370SDaniel Vetter static void set_vc_hi_font(struct vc_data *vc, bool set);
11656104c370SDaniel Vetter 
fbcon_release_all(void)1166c75300b5SDaniel Vetter static void fbcon_release_all(void)
1167c75300b5SDaniel Vetter {
1168c75300b5SDaniel Vetter 	struct fb_info *info;
1169c75300b5SDaniel Vetter 	int i, j, mapped;
1170c75300b5SDaniel Vetter 
1171efc3acbcSDaniel Vetter 	fbcon_for_each_registered_fb(i) {
1172c75300b5SDaniel Vetter 		mapped = 0;
1173efc3acbcSDaniel Vetter 		info = fbcon_registered_fb[i];
1174c75300b5SDaniel Vetter 
1175c75300b5SDaniel Vetter 		for (j = first_fb_vc; j <= last_fb_vc; j++) {
1176c75300b5SDaniel Vetter 			if (con2fb_map[j] == i) {
1177c75300b5SDaniel Vetter 				mapped = 1;
1178c75300b5SDaniel Vetter 				con2fb_map[j] = -1;
1179c75300b5SDaniel Vetter 			}
1180c75300b5SDaniel Vetter 		}
1181c75300b5SDaniel Vetter 
1182c75300b5SDaniel Vetter 		if (mapped)
1183c75300b5SDaniel Vetter 			fbcon_release(info);
1184c75300b5SDaniel Vetter 	}
1185c75300b5SDaniel Vetter }
1186c75300b5SDaniel Vetter 
fbcon_deinit(struct vc_data * vc)11876104c370SDaniel Vetter static void fbcon_deinit(struct vc_data *vc)
11886104c370SDaniel Vetter {
118950233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
11906104c370SDaniel Vetter 	struct fb_info *info;
11916104c370SDaniel Vetter 	struct fbcon_ops *ops;
11926104c370SDaniel Vetter 	int idx;
11936104c370SDaniel Vetter 
119412d5796dSThomas Zimmermann 	fbcon_free_font(p);
11956104c370SDaniel Vetter 	idx = con2fb_map[vc->vc_num];
11966104c370SDaniel Vetter 
11976104c370SDaniel Vetter 	if (idx == -1)
11986104c370SDaniel Vetter 		goto finished;
11996104c370SDaniel Vetter 
1200efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
12016104c370SDaniel Vetter 
12026104c370SDaniel Vetter 	if (!info)
12036104c370SDaniel Vetter 		goto finished;
12046104c370SDaniel Vetter 
12056104c370SDaniel Vetter 	ops = info->fbcon_par;
12066104c370SDaniel Vetter 
12076104c370SDaniel Vetter 	if (!ops)
12086104c370SDaniel Vetter 		goto finished;
12096104c370SDaniel Vetter 
12106104c370SDaniel Vetter 	if (con_is_visible(vc))
12113b0fb6abSDaniel Vetter 		fbcon_del_cursor_work(info);
12126104c370SDaniel Vetter 
1213cae69e45SDaniel Vetter 	ops->initialized = false;
12146104c370SDaniel Vetter finished:
12156104c370SDaniel Vetter 
121612d5796dSThomas Zimmermann 	fbcon_free_font(p);
12176104c370SDaniel Vetter 	vc->vc_font.data = NULL;
12186104c370SDaniel Vetter 
1219a1ad1cc9SGrzegorz Halat 	if (vc->vc_hi_font_mask && vc->vc_screenbuf)
12206104c370SDaniel Vetter 		set_vc_hi_font(vc, false);
12216104c370SDaniel Vetter 
12226104c370SDaniel Vetter 	if (!con_is_bound(&fb_con))
1223c75300b5SDaniel Vetter 		fbcon_release_all();
12246104c370SDaniel Vetter 
1225b139f8b0SQiujun Huang 	if (vc->vc_num == logo_shown)
1226b139f8b0SQiujun Huang 		logo_shown = FBCON_LOGO_CANSHOW;
1227b139f8b0SQiujun Huang 
12286104c370SDaniel Vetter 	return;
12296104c370SDaniel Vetter }
12306104c370SDaniel Vetter 
12316104c370SDaniel Vetter /* ====================================================================== */
12326104c370SDaniel Vetter 
12336104c370SDaniel Vetter /*  fbcon_XXX routines - interface used by the world
12346104c370SDaniel Vetter  *
12356104c370SDaniel Vetter  *  This system is now divided into two levels because of complications
12366104c370SDaniel Vetter  *  caused by hardware scrolling. Top level functions:
12376104c370SDaniel Vetter  *
12381148836fSHelge Deller  *	fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
12396104c370SDaniel Vetter  *
12406104c370SDaniel Vetter  *  handles y values in range [0, scr_height-1] that correspond to real
12416104c370SDaniel Vetter  *  screen positions. y_wrap shift means that first line of bitmap may be
12426104c370SDaniel Vetter  *  anywhere on this display. These functions convert lineoffsets to
12436104c370SDaniel Vetter  *  bitmap offsets and deal with the wrap-around case by splitting blits.
12446104c370SDaniel Vetter  *
12451148836fSHelge Deller  *	fbcon_bmove_physical_8()    -- These functions fast implementations
12466104c370SDaniel Vetter  *	fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
12476104c370SDaniel Vetter  *	fbcon_putc_physical_8()	    -- (font width != 8) may be added later
12486104c370SDaniel Vetter  *
12496104c370SDaniel Vetter  *  WARNING:
12506104c370SDaniel Vetter  *
12516104c370SDaniel Vetter  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
12526104c370SDaniel Vetter  *  Implies should only really hardware scroll in rows. Only reason for
12536104c370SDaniel Vetter  *  restriction is simplicity & efficiency at the moment.
12546104c370SDaniel Vetter  */
12556104c370SDaniel Vetter 
__fbcon_clear(struct vc_data * vc,unsigned int sy,unsigned int sx,unsigned int height,unsigned int width)1256559f01a0SJiri Slaby (SUSE) static void __fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
1257559f01a0SJiri Slaby (SUSE) 			  unsigned int height, unsigned int width)
12586104c370SDaniel Vetter {
1259409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
12606104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
12616104c370SDaniel Vetter 
126250233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
12636104c370SDaniel Vetter 	u_int y_break;
12646104c370SDaniel Vetter 
12656104c370SDaniel Vetter 	if (fbcon_is_inactive(vc, info))
12666104c370SDaniel Vetter 		return;
12676104c370SDaniel Vetter 
12686104c370SDaniel Vetter 	if (!height || !width)
12696104c370SDaniel Vetter 		return;
12706104c370SDaniel Vetter 
12716104c370SDaniel Vetter 	if (sy < vc->vc_top && vc->vc_top == logo_lines) {
12726104c370SDaniel Vetter 		vc->vc_top = 0;
12736104c370SDaniel Vetter 		/*
12746104c370SDaniel Vetter 		 * If the font dimensions are not an integral of the display
12756104c370SDaniel Vetter 		 * dimensions then the ops->clear below won't end up clearing
12766104c370SDaniel Vetter 		 * the margins.  Call clear_margins here in case the logo
12776104c370SDaniel Vetter 		 * bitmap stretched into the margin area.
12786104c370SDaniel Vetter 		 */
12796104c370SDaniel Vetter 		fbcon_clear_margins(vc, 0);
12806104c370SDaniel Vetter 	}
12816104c370SDaniel Vetter 
12826104c370SDaniel Vetter 	/* Split blits that cross physical y_wrap boundary */
12836104c370SDaniel Vetter 
12846104c370SDaniel Vetter 	y_break = p->vrows - p->yscroll;
12856104c370SDaniel Vetter 	if (sy < y_break && sy + height - 1 >= y_break) {
12866104c370SDaniel Vetter 		u_int b = y_break - sy;
12876104c370SDaniel Vetter 		ops->clear(vc, info, real_y(p, sy), sx, b, width);
12886104c370SDaniel Vetter 		ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
12896104c370SDaniel Vetter 				 width);
12906104c370SDaniel Vetter 	} else
12916104c370SDaniel Vetter 		ops->clear(vc, info, real_y(p, sy), sx, height, width);
12926104c370SDaniel Vetter }
12936104c370SDaniel Vetter 
fbcon_clear(struct vc_data * vc,unsigned int sy,unsigned int sx,unsigned int width)1294559f01a0SJiri Slaby (SUSE) static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
1295559f01a0SJiri Slaby (SUSE) 			unsigned int width)
1296559f01a0SJiri Slaby (SUSE) {
1297559f01a0SJiri Slaby (SUSE) 	__fbcon_clear(vc, sy, sx, 1, width);
1298559f01a0SJiri Slaby (SUSE) }
1299559f01a0SJiri Slaby (SUSE) 
fbcon_putcs(struct vc_data * vc,const u16 * s,unsigned int count,unsigned int ypos,unsigned int xpos)1300bfd7de49SJiri Slaby (SUSE) static void fbcon_putcs(struct vc_data *vc, const u16 *s, unsigned int count,
1301bfd7de49SJiri Slaby (SUSE) 			unsigned int ypos, unsigned int xpos)
13026104c370SDaniel Vetter {
1303409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
130450233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
13056104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
13066104c370SDaniel Vetter 
13076104c370SDaniel Vetter 	if (!fbcon_is_inactive(vc, info))
13086104c370SDaniel Vetter 		ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
13096104c370SDaniel Vetter 			   get_color(vc, info, scr_readw(s), 1),
13106104c370SDaniel Vetter 			   get_color(vc, info, scr_readw(s), 0));
13116104c370SDaniel Vetter }
13126104c370SDaniel Vetter 
fbcon_clear_margins(struct vc_data * vc,int bottom_only)13136104c370SDaniel Vetter static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
13146104c370SDaniel Vetter {
1315409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
13166104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
13176104c370SDaniel Vetter 
13186104c370SDaniel Vetter 	if (!fbcon_is_inactive(vc, info))
131974c1c8b3SDavid Lechner 		ops->clear_margins(vc, info, margin_color, bottom_only);
13206104c370SDaniel Vetter }
13216104c370SDaniel Vetter 
fbcon_cursor(struct vc_data * vc,bool enable)1322a292e3fcSJiri Slaby (SUSE) static void fbcon_cursor(struct vc_data *vc, bool enable)
13236104c370SDaniel Vetter {
1324409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
13256104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
13266104c370SDaniel Vetter  	int c = scr_readw((u16 *) vc->vc_pos);
13276104c370SDaniel Vetter 
13286104c370SDaniel Vetter 	ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
13296104c370SDaniel Vetter 
13306104c370SDaniel Vetter 	if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
13316104c370SDaniel Vetter 		return;
13326104c370SDaniel Vetter 
1333c0e4b3adSJiri Slaby 	if (vc->vc_cursor_type & CUR_SW)
13343b0fb6abSDaniel Vetter 		fbcon_del_cursor_work(info);
13356104c370SDaniel Vetter 	else
13363b0fb6abSDaniel Vetter 		fbcon_add_cursor_work(info);
13376104c370SDaniel Vetter 
1338a292e3fcSJiri Slaby (SUSE) 	ops->cursor_flash = enable;
13396104c370SDaniel Vetter 
134001faae51SDu Cheng 	if (!ops->cursor)
134101faae51SDu Cheng 		return;
134201faae51SDu Cheng 
1343a292e3fcSJiri Slaby (SUSE) 	ops->cursor(vc, info, enable, get_color(vc, info, c, 1),
13446104c370SDaniel Vetter 		    get_color(vc, info, c, 0));
13456104c370SDaniel Vetter }
13466104c370SDaniel Vetter 
13476104c370SDaniel Vetter static int scrollback_phys_max = 0;
13486104c370SDaniel Vetter static int scrollback_max = 0;
13496104c370SDaniel Vetter static int scrollback_current = 0;
13506104c370SDaniel Vetter 
fbcon_set_disp(struct fb_info * info,struct fb_var_screeninfo * var,int unit)13516104c370SDaniel Vetter static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
13526104c370SDaniel Vetter 			   int unit)
13536104c370SDaniel Vetter {
135450233393SDaniel Vetter 	struct fbcon_display *p, *t;
13556104c370SDaniel Vetter 	struct vc_data **default_mode, *vc;
13566104c370SDaniel Vetter 	struct vc_data *svc;
13576104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
1358a1ac250aSPeilin Ye 	int rows, cols;
13596104c370SDaniel Vetter 
13606104c370SDaniel Vetter 	p = &fb_display[unit];
13616104c370SDaniel Vetter 
13626104c370SDaniel Vetter 	if (var_to_display(p, var, info))
13636104c370SDaniel Vetter 		return;
13646104c370SDaniel Vetter 
13656104c370SDaniel Vetter 	vc = vc_cons[unit].d;
13666104c370SDaniel Vetter 
13676104c370SDaniel Vetter 	if (!vc)
13686104c370SDaniel Vetter 		return;
13696104c370SDaniel Vetter 
13706104c370SDaniel Vetter 	default_mode = vc->vc_display_fg;
13716104c370SDaniel Vetter 	svc = *default_mode;
13726104c370SDaniel Vetter 	t = &fb_display[svc->vc_num];
13736104c370SDaniel Vetter 
13746104c370SDaniel Vetter 	if (!vc->vc_font.data) {
13756104c370SDaniel Vetter 		vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
13766104c370SDaniel Vetter 		vc->vc_font.width = (*default_mode)->vc_font.width;
13776104c370SDaniel Vetter 		vc->vc_font.height = (*default_mode)->vc_font.height;
1378a1ac250aSPeilin Ye 		vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
13796104c370SDaniel Vetter 		p->userfont = t->userfont;
13806104c370SDaniel Vetter 		if (p->userfont)
13816104c370SDaniel Vetter 			REFCOUNT(p->fontdata)++;
13826104c370SDaniel Vetter 	}
13836104c370SDaniel Vetter 
13846104c370SDaniel Vetter 	var->activate = FB_ACTIVATE_NOW;
13856104c370SDaniel Vetter 	info->var.activate = var->activate;
13866104c370SDaniel Vetter 	var->yoffset = info->var.yoffset;
13876104c370SDaniel Vetter 	var->xoffset = info->var.xoffset;
13886104c370SDaniel Vetter 	fb_set_var(info, var);
13896104c370SDaniel Vetter 	ops->var = info->var;
13906104c370SDaniel Vetter 	vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
13916104c370SDaniel Vetter 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1392a1ac250aSPeilin Ye 	if (vc->vc_font.charcount == 256) {
13936104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0;
13946104c370SDaniel Vetter 	} else {
13956104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0x100;
13966104c370SDaniel Vetter 		if (vc->vc_can_do_color)
13976104c370SDaniel Vetter 			vc->vc_complement_mask <<= 1;
13986104c370SDaniel Vetter 	}
13996104c370SDaniel Vetter 
14008da443b1SJiri Slaby 	if (!*svc->uni_pagedict_loc)
14016104c370SDaniel Vetter 		con_set_default_unimap(svc);
14028da443b1SJiri Slaby 	if (!*vc->uni_pagedict_loc)
14036104c370SDaniel Vetter 		con_copy_unimap(vc, svc);
14046104c370SDaniel Vetter 
14056104c370SDaniel Vetter 	cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
14066104c370SDaniel Vetter 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
14076104c370SDaniel Vetter 	cols /= vc->vc_font.width;
14086104c370SDaniel Vetter 	rows /= vc->vc_font.height;
14096104c370SDaniel Vetter 	vc_resize(vc, cols, rows);
14106104c370SDaniel Vetter 
14116104c370SDaniel Vetter 	if (con_is_visible(vc)) {
14126104c370SDaniel Vetter 		update_screen(vc);
14136104c370SDaniel Vetter 	}
14146104c370SDaniel Vetter }
14156104c370SDaniel Vetter 
ywrap_up(struct vc_data * vc,int count)14161148836fSHelge Deller static __inline__ void ywrap_up(struct vc_data *vc, int count)
14171148836fSHelge Deller {
1418409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14191148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
14201148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
14211148836fSHelge Deller 
14221148836fSHelge Deller 	p->yscroll += count;
14231148836fSHelge Deller 	if (p->yscroll >= p->vrows)	/* Deal with wrap */
14241148836fSHelge Deller 		p->yscroll -= p->vrows;
14251148836fSHelge Deller 	ops->var.xoffset = 0;
14261148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
14271148836fSHelge Deller 	ops->var.vmode |= FB_VMODE_YWRAP;
14281148836fSHelge Deller 	ops->update_start(info);
14291148836fSHelge Deller 	scrollback_max += count;
14301148836fSHelge Deller 	if (scrollback_max > scrollback_phys_max)
14311148836fSHelge Deller 		scrollback_max = scrollback_phys_max;
14321148836fSHelge Deller 	scrollback_current = 0;
14331148836fSHelge Deller }
14341148836fSHelge Deller 
ywrap_down(struct vc_data * vc,int count)14351148836fSHelge Deller static __inline__ void ywrap_down(struct vc_data *vc, int count)
14361148836fSHelge Deller {
1437409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14381148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
14391148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
14401148836fSHelge Deller 
14411148836fSHelge Deller 	p->yscroll -= count;
14421148836fSHelge Deller 	if (p->yscroll < 0)	/* Deal with wrap */
14431148836fSHelge Deller 		p->yscroll += p->vrows;
14441148836fSHelge Deller 	ops->var.xoffset = 0;
14451148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
14461148836fSHelge Deller 	ops->var.vmode |= FB_VMODE_YWRAP;
14471148836fSHelge Deller 	ops->update_start(info);
14481148836fSHelge Deller 	scrollback_max -= count;
14491148836fSHelge Deller 	if (scrollback_max < 0)
14501148836fSHelge Deller 		scrollback_max = 0;
14511148836fSHelge Deller 	scrollback_current = 0;
14521148836fSHelge Deller }
14531148836fSHelge Deller 
ypan_up(struct vc_data * vc,int count)14541148836fSHelge Deller static __inline__ void ypan_up(struct vc_data *vc, int count)
14551148836fSHelge Deller {
1456409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14571148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
14581148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
14591148836fSHelge Deller 
14601148836fSHelge Deller 	p->yscroll += count;
14611148836fSHelge Deller 	if (p->yscroll > p->vrows - vc->vc_rows) {
14621148836fSHelge Deller 		ops->bmove(vc, info, p->vrows - vc->vc_rows,
14631148836fSHelge Deller 			    0, 0, 0, vc->vc_rows, vc->vc_cols);
14641148836fSHelge Deller 		p->yscroll -= p->vrows - vc->vc_rows;
14651148836fSHelge Deller 	}
14661148836fSHelge Deller 
14671148836fSHelge Deller 	ops->var.xoffset = 0;
14681148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
14691148836fSHelge Deller 	ops->var.vmode &= ~FB_VMODE_YWRAP;
14701148836fSHelge Deller 	ops->update_start(info);
14711148836fSHelge Deller 	fbcon_clear_margins(vc, 1);
14721148836fSHelge Deller 	scrollback_max += count;
14731148836fSHelge Deller 	if (scrollback_max > scrollback_phys_max)
14741148836fSHelge Deller 		scrollback_max = scrollback_phys_max;
14751148836fSHelge Deller 	scrollback_current = 0;
14761148836fSHelge Deller }
14771148836fSHelge Deller 
ypan_up_redraw(struct vc_data * vc,int t,int count)14781148836fSHelge Deller static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
14791148836fSHelge Deller {
1480409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14811148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
14821148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
14831148836fSHelge Deller 
14841148836fSHelge Deller 	p->yscroll += count;
14851148836fSHelge Deller 
14861148836fSHelge Deller 	if (p->yscroll > p->vrows - vc->vc_rows) {
14871148836fSHelge Deller 		p->yscroll -= p->vrows - vc->vc_rows;
14881148836fSHelge Deller 		fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
14891148836fSHelge Deller 	}
14901148836fSHelge Deller 
14911148836fSHelge Deller 	ops->var.xoffset = 0;
14921148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
14931148836fSHelge Deller 	ops->var.vmode &= ~FB_VMODE_YWRAP;
14941148836fSHelge Deller 	ops->update_start(info);
14951148836fSHelge Deller 	fbcon_clear_margins(vc, 1);
14961148836fSHelge Deller 	scrollback_max += count;
14971148836fSHelge Deller 	if (scrollback_max > scrollback_phys_max)
14981148836fSHelge Deller 		scrollback_max = scrollback_phys_max;
14991148836fSHelge Deller 	scrollback_current = 0;
15001148836fSHelge Deller }
15011148836fSHelge Deller 
ypan_down(struct vc_data * vc,int count)15021148836fSHelge Deller static __inline__ void ypan_down(struct vc_data *vc, int count)
15031148836fSHelge Deller {
1504409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
15051148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
15061148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
15071148836fSHelge Deller 
15081148836fSHelge Deller 	p->yscroll -= count;
15091148836fSHelge Deller 	if (p->yscroll < 0) {
15101148836fSHelge Deller 		ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
15111148836fSHelge Deller 			    0, vc->vc_rows, vc->vc_cols);
15121148836fSHelge Deller 		p->yscroll += p->vrows - vc->vc_rows;
15131148836fSHelge Deller 	}
15141148836fSHelge Deller 
15151148836fSHelge Deller 	ops->var.xoffset = 0;
15161148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
15171148836fSHelge Deller 	ops->var.vmode &= ~FB_VMODE_YWRAP;
15181148836fSHelge Deller 	ops->update_start(info);
15191148836fSHelge Deller 	fbcon_clear_margins(vc, 1);
15201148836fSHelge Deller 	scrollback_max -= count;
15211148836fSHelge Deller 	if (scrollback_max < 0)
15221148836fSHelge Deller 		scrollback_max = 0;
15231148836fSHelge Deller 	scrollback_current = 0;
15241148836fSHelge Deller }
15251148836fSHelge Deller 
ypan_down_redraw(struct vc_data * vc,int t,int count)15261148836fSHelge Deller static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
15271148836fSHelge Deller {
1528409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
15291148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
15301148836fSHelge Deller 	struct fbcon_display *p = &fb_display[vc->vc_num];
15311148836fSHelge Deller 
15321148836fSHelge Deller 	p->yscroll -= count;
15331148836fSHelge Deller 
15341148836fSHelge Deller 	if (p->yscroll < 0) {
15351148836fSHelge Deller 		p->yscroll += p->vrows - vc->vc_rows;
15361148836fSHelge Deller 		fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
15371148836fSHelge Deller 	}
15381148836fSHelge Deller 
15391148836fSHelge Deller 	ops->var.xoffset = 0;
15401148836fSHelge Deller 	ops->var.yoffset = p->yscroll * vc->vc_font.height;
15411148836fSHelge Deller 	ops->var.vmode &= ~FB_VMODE_YWRAP;
15421148836fSHelge Deller 	ops->update_start(info);
15431148836fSHelge Deller 	fbcon_clear_margins(vc, 1);
15441148836fSHelge Deller 	scrollback_max -= count;
15451148836fSHelge Deller 	if (scrollback_max < 0)
15461148836fSHelge Deller 		scrollback_max = 0;
15471148836fSHelge Deller 	scrollback_current = 0;
15481148836fSHelge Deller }
15491148836fSHelge Deller 
fbcon_redraw_move(struct vc_data * vc,struct fbcon_display * p,int line,int count,int dy)15501148836fSHelge Deller static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
15511148836fSHelge Deller 			      int line, int count, int dy)
15521148836fSHelge Deller {
15531148836fSHelge Deller 	unsigned short *s = (unsigned short *)
15541148836fSHelge Deller 		(vc->vc_origin + vc->vc_size_row * line);
15551148836fSHelge Deller 
15561148836fSHelge Deller 	while (count--) {
15571148836fSHelge Deller 		unsigned short *start = s;
15581148836fSHelge Deller 		unsigned short *le = advance_row(s, 1);
15591148836fSHelge Deller 		unsigned short c;
15601148836fSHelge Deller 		int x = 0;
15611148836fSHelge Deller 		unsigned short attr = 1;
15621148836fSHelge Deller 
15631148836fSHelge Deller 		do {
15641148836fSHelge Deller 			c = scr_readw(s);
15651148836fSHelge Deller 			if (attr != (c & 0xff00)) {
15661148836fSHelge Deller 				attr = c & 0xff00;
15671148836fSHelge Deller 				if (s > start) {
15681148836fSHelge Deller 					fbcon_putcs(vc, start, s - start,
15691148836fSHelge Deller 						    dy, x);
15701148836fSHelge Deller 					x += s - start;
15711148836fSHelge Deller 					start = s;
15721148836fSHelge Deller 				}
15731148836fSHelge Deller 			}
15741148836fSHelge Deller 			console_conditional_schedule();
15751148836fSHelge Deller 			s++;
15761148836fSHelge Deller 		} while (s < le);
15771148836fSHelge Deller 		if (s > start)
15781148836fSHelge Deller 			fbcon_putcs(vc, start, s - start, dy, x);
15791148836fSHelge Deller 		console_conditional_schedule();
15801148836fSHelge Deller 		dy++;
15811148836fSHelge Deller 	}
15821148836fSHelge Deller }
15831148836fSHelge Deller 
fbcon_redraw_blit(struct vc_data * vc,struct fb_info * info,struct fbcon_display * p,int line,int count,int ycount)15841148836fSHelge Deller static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
15851148836fSHelge Deller 			struct fbcon_display *p, int line, int count, int ycount)
15861148836fSHelge Deller {
15871148836fSHelge Deller 	int offset = ycount * vc->vc_cols;
15881148836fSHelge Deller 	unsigned short *d = (unsigned short *)
15891148836fSHelge Deller 	    (vc->vc_origin + vc->vc_size_row * line);
15901148836fSHelge Deller 	unsigned short *s = d + offset;
15911148836fSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
15921148836fSHelge Deller 
15931148836fSHelge Deller 	while (count--) {
15941148836fSHelge Deller 		unsigned short *start = s;
15951148836fSHelge Deller 		unsigned short *le = advance_row(s, 1);
15961148836fSHelge Deller 		unsigned short c;
15971148836fSHelge Deller 		int x = 0;
15981148836fSHelge Deller 
15991148836fSHelge Deller 		do {
16001148836fSHelge Deller 			c = scr_readw(s);
16011148836fSHelge Deller 
16021148836fSHelge Deller 			if (c == scr_readw(d)) {
16031148836fSHelge Deller 				if (s > start) {
16041148836fSHelge Deller 					ops->bmove(vc, info, line + ycount, x,
16051148836fSHelge Deller 						   line, x, 1, s-start);
16061148836fSHelge Deller 					x += s - start + 1;
16071148836fSHelge Deller 					start = s + 1;
16081148836fSHelge Deller 				} else {
16091148836fSHelge Deller 					x++;
16101148836fSHelge Deller 					start++;
16111148836fSHelge Deller 				}
16121148836fSHelge Deller 			}
16131148836fSHelge Deller 
16141148836fSHelge Deller 			scr_writew(c, d);
16151148836fSHelge Deller 			console_conditional_schedule();
16161148836fSHelge Deller 			s++;
16171148836fSHelge Deller 			d++;
16181148836fSHelge Deller 		} while (s < le);
16191148836fSHelge Deller 		if (s > start)
16201148836fSHelge Deller 			ops->bmove(vc, info, line + ycount, x, line, x, 1,
16211148836fSHelge Deller 				   s-start);
16221148836fSHelge Deller 		console_conditional_schedule();
16231148836fSHelge Deller 		if (ycount > 0)
16241148836fSHelge Deller 			line++;
16251148836fSHelge Deller 		else {
16261148836fSHelge Deller 			line--;
16271148836fSHelge Deller 			/* NOTE: We subtract two lines from these pointers */
16281148836fSHelge Deller 			s -= vc->vc_size_row;
16291148836fSHelge Deller 			d -= vc->vc_size_row;
16301148836fSHelge Deller 		}
16311148836fSHelge Deller 	}
16321148836fSHelge Deller }
16331148836fSHelge Deller 
fbcon_redraw(struct vc_data * vc,int line,int count,int offset)1634933ab3a8SJiri Slaby (SUSE) static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset)
16356104c370SDaniel Vetter {
16366104c370SDaniel Vetter 	unsigned short *d = (unsigned short *)
16376104c370SDaniel Vetter 	    (vc->vc_origin + vc->vc_size_row * line);
16386104c370SDaniel Vetter 	unsigned short *s = d + offset;
16396104c370SDaniel Vetter 
16406104c370SDaniel Vetter 	while (count--) {
16416104c370SDaniel Vetter 		unsigned short *start = s;
16426104c370SDaniel Vetter 		unsigned short *le = advance_row(s, 1);
16436104c370SDaniel Vetter 		unsigned short c;
16446104c370SDaniel Vetter 		int x = 0;
16456104c370SDaniel Vetter 		unsigned short attr = 1;
16466104c370SDaniel Vetter 
16476104c370SDaniel Vetter 		do {
16486104c370SDaniel Vetter 			c = scr_readw(s);
16496104c370SDaniel Vetter 			if (attr != (c & 0xff00)) {
16506104c370SDaniel Vetter 				attr = c & 0xff00;
16516104c370SDaniel Vetter 				if (s > start) {
16526104c370SDaniel Vetter 					fbcon_putcs(vc, start, s - start,
16536104c370SDaniel Vetter 						    line, x);
16546104c370SDaniel Vetter 					x += s - start;
16556104c370SDaniel Vetter 					start = s;
16566104c370SDaniel Vetter 				}
16576104c370SDaniel Vetter 			}
16586104c370SDaniel Vetter 			if (c == scr_readw(d)) {
16596104c370SDaniel Vetter 				if (s > start) {
16606104c370SDaniel Vetter 					fbcon_putcs(vc, start, s - start,
16616104c370SDaniel Vetter 						     line, x);
16626104c370SDaniel Vetter 					x += s - start + 1;
16636104c370SDaniel Vetter 					start = s + 1;
16646104c370SDaniel Vetter 				} else {
16656104c370SDaniel Vetter 					x++;
16666104c370SDaniel Vetter 					start++;
16676104c370SDaniel Vetter 				}
16686104c370SDaniel Vetter 			}
16696104c370SDaniel Vetter 			scr_writew(c, d);
16706104c370SDaniel Vetter 			console_conditional_schedule();
16716104c370SDaniel Vetter 			s++;
16726104c370SDaniel Vetter 			d++;
16736104c370SDaniel Vetter 		} while (s < le);
16746104c370SDaniel Vetter 		if (s > start)
16756104c370SDaniel Vetter 			fbcon_putcs(vc, start, s - start, line, x);
16766104c370SDaniel Vetter 		console_conditional_schedule();
16776104c370SDaniel Vetter 		if (offset > 0)
16786104c370SDaniel Vetter 			line++;
16796104c370SDaniel Vetter 		else {
16806104c370SDaniel Vetter 			line--;
16816104c370SDaniel Vetter 			/* NOTE: We subtract two lines from these pointers */
16826104c370SDaniel Vetter 			s -= vc->vc_size_row;
16836104c370SDaniel Vetter 			d -= vc->vc_size_row;
16846104c370SDaniel Vetter 		}
16856104c370SDaniel Vetter 	}
16866104c370SDaniel Vetter }
16876104c370SDaniel Vetter 
fbcon_bmove_rec(struct vc_data * vc,struct fbcon_display * p,int sy,int sx,int dy,int dx,int height,int width,u_int y_break)168868933313SDaniel Vetter static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
168968933313SDaniel Vetter 			    int dy, int dx, int height, int width, u_int y_break)
169068933313SDaniel Vetter {
1691409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
169268933313SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
169368933313SDaniel Vetter 	u_int b;
169468933313SDaniel Vetter 
169568933313SDaniel Vetter 	if (sy < y_break && sy + height > y_break) {
169668933313SDaniel Vetter 		b = y_break - sy;
169768933313SDaniel Vetter 		if (dy < sy) {	/* Avoid trashing self */
169868933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
169968933313SDaniel Vetter 					y_break);
170068933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
170168933313SDaniel Vetter 					height - b, width, y_break);
170268933313SDaniel Vetter 		} else {
170368933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
170468933313SDaniel Vetter 					height - b, width, y_break);
170568933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
170668933313SDaniel Vetter 					y_break);
170768933313SDaniel Vetter 		}
170868933313SDaniel Vetter 		return;
170968933313SDaniel Vetter 	}
171068933313SDaniel Vetter 
171168933313SDaniel Vetter 	if (dy < y_break && dy + height > y_break) {
171268933313SDaniel Vetter 		b = y_break - dy;
171368933313SDaniel Vetter 		if (dy < sy) {	/* Avoid trashing self */
171468933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
171568933313SDaniel Vetter 					y_break);
171668933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
171768933313SDaniel Vetter 					height - b, width, y_break);
171868933313SDaniel Vetter 		} else {
171968933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
172068933313SDaniel Vetter 					height - b, width, y_break);
172168933313SDaniel Vetter 			fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
172268933313SDaniel Vetter 					y_break);
172368933313SDaniel Vetter 		}
172468933313SDaniel Vetter 		return;
172568933313SDaniel Vetter 	}
172668933313SDaniel Vetter 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
172768933313SDaniel Vetter 		   height, width);
172868933313SDaniel Vetter }
172968933313SDaniel Vetter 
fbcon_bmove(struct vc_data * vc,int sy,int sx,int dy,int dx,int height,int width)173068933313SDaniel Vetter static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
173168933313SDaniel Vetter 			int height, int width)
173268933313SDaniel Vetter {
1733409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
173468933313SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
173568933313SDaniel Vetter 
173668933313SDaniel Vetter 	if (fbcon_is_inactive(vc, info))
173768933313SDaniel Vetter 		return;
173868933313SDaniel Vetter 
173968933313SDaniel Vetter 	if (!width || !height)
174068933313SDaniel Vetter 		return;
174168933313SDaniel Vetter 
174268933313SDaniel Vetter 	/*  Split blits that cross physical y_wrap case.
174368933313SDaniel Vetter 	 *  Pathological case involves 4 blits, better to use recursive
174468933313SDaniel Vetter 	 *  code rather than unrolled case
174568933313SDaniel Vetter 	 *
174668933313SDaniel Vetter 	 *  Recursive invocations don't need to erase the cursor over and
174768933313SDaniel Vetter 	 *  over again, so we use fbcon_bmove_rec()
174868933313SDaniel Vetter 	 */
174968933313SDaniel Vetter 	fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
175068933313SDaniel Vetter 			p->vrows - p->yscroll);
175168933313SDaniel Vetter }
175268933313SDaniel Vetter 
fbcon_scroll(struct vc_data * vc,unsigned int t,unsigned int b,enum con_scroll dir,unsigned int count)17536104c370SDaniel Vetter static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
17546104c370SDaniel Vetter 		enum con_scroll dir, unsigned int count)
17556104c370SDaniel Vetter {
1756409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
175750233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
17581148836fSHelge Deller 	int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
17596104c370SDaniel Vetter 
17606104c370SDaniel Vetter 	if (fbcon_is_inactive(vc, info))
17616104c370SDaniel Vetter 		return true;
17626104c370SDaniel Vetter 
1763a292e3fcSJiri Slaby (SUSE) 	fbcon_cursor(vc, false);
17646104c370SDaniel Vetter 
17656104c370SDaniel Vetter 	/*
17666104c370SDaniel Vetter 	 * ++Geert: Only use ywrap/ypan if the console is in text mode
17676104c370SDaniel Vetter 	 * ++Andrew: Only use ypan on hardware text mode when scrolling the
17686104c370SDaniel Vetter 	 *           whole screen (prevents flicker).
17696104c370SDaniel Vetter 	 */
17706104c370SDaniel Vetter 
17716104c370SDaniel Vetter 	switch (dir) {
17726104c370SDaniel Vetter 	case SM_UP:
17736104c370SDaniel Vetter 		if (count > vc->vc_rows)	/* Maximum realistic size */
17746104c370SDaniel Vetter 			count = vc->vc_rows;
1775a3f781a9SHelge Deller 		switch (fb_scrollmode(p)) {
17761148836fSHelge Deller 		case SCROLL_MOVE:
17771148836fSHelge Deller 			fbcon_redraw_blit(vc, info, p, t, b - t - count,
17781148836fSHelge Deller 				     count);
1779559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
17801148836fSHelge Deller 			scr_memsetw((unsigned short *) (vc->vc_origin +
17811148836fSHelge Deller 							vc->vc_size_row *
17821148836fSHelge Deller 							(b - count)),
17831148836fSHelge Deller 				    vc->vc_video_erase_char,
17841148836fSHelge Deller 				    vc->vc_size_row * count);
17851148836fSHelge Deller 			return true;
17861148836fSHelge Deller 
17871148836fSHelge Deller 		case SCROLL_WRAP_MOVE:
17881148836fSHelge Deller 			if (b - t - count > 3 * vc->vc_rows >> 2) {
17891148836fSHelge Deller 				if (t > 0)
17901148836fSHelge Deller 					fbcon_bmove(vc, 0, 0, count, 0, t,
17911148836fSHelge Deller 						    vc->vc_cols);
17921148836fSHelge Deller 				ywrap_up(vc, count);
17931148836fSHelge Deller 				if (vc->vc_rows - b > 0)
17941148836fSHelge Deller 					fbcon_bmove(vc, b - count, 0, b, 0,
17951148836fSHelge Deller 						    vc->vc_rows - b,
17961148836fSHelge Deller 						    vc->vc_cols);
17971148836fSHelge Deller 			} else if (info->flags & FBINFO_READS_FAST)
17981148836fSHelge Deller 				fbcon_bmove(vc, t + count, 0, t, 0,
17991148836fSHelge Deller 					    b - t - count, vc->vc_cols);
18001148836fSHelge Deller 			else
18011148836fSHelge Deller 				goto redraw_up;
1802559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18031148836fSHelge Deller 			break;
18041148836fSHelge Deller 
18051148836fSHelge Deller 		case SCROLL_PAN_REDRAW:
18061148836fSHelge Deller 			if ((p->yscroll + count <=
18071148836fSHelge Deller 			     2 * (p->vrows - vc->vc_rows))
18081148836fSHelge Deller 			    && ((!scroll_partial && (b - t == vc->vc_rows))
18091148836fSHelge Deller 				|| (scroll_partial
18101148836fSHelge Deller 				    && (b - t - count >
18111148836fSHelge Deller 					3 * vc->vc_rows >> 2)))) {
18121148836fSHelge Deller 				if (t > 0)
18131148836fSHelge Deller 					fbcon_redraw_move(vc, p, 0, t, count);
18141148836fSHelge Deller 				ypan_up_redraw(vc, t, count);
18151148836fSHelge Deller 				if (vc->vc_rows - b > 0)
18161148836fSHelge Deller 					fbcon_redraw_move(vc, p, b,
18171148836fSHelge Deller 							  vc->vc_rows - b, b);
18181148836fSHelge Deller 			} else
18191148836fSHelge Deller 				fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1820559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18211148836fSHelge Deller 			break;
18221148836fSHelge Deller 
18231148836fSHelge Deller 		case SCROLL_PAN_MOVE:
18241148836fSHelge Deller 			if ((p->yscroll + count <=
18251148836fSHelge Deller 			     2 * (p->vrows - vc->vc_rows))
18261148836fSHelge Deller 			    && ((!scroll_partial && (b - t == vc->vc_rows))
18271148836fSHelge Deller 				|| (scroll_partial
18281148836fSHelge Deller 				    && (b - t - count >
18291148836fSHelge Deller 					3 * vc->vc_rows >> 2)))) {
18301148836fSHelge Deller 				if (t > 0)
18311148836fSHelge Deller 					fbcon_bmove(vc, 0, 0, count, 0, t,
18321148836fSHelge Deller 						    vc->vc_cols);
18331148836fSHelge Deller 				ypan_up(vc, count);
18341148836fSHelge Deller 				if (vc->vc_rows - b > 0)
18351148836fSHelge Deller 					fbcon_bmove(vc, b - count, 0, b, 0,
18361148836fSHelge Deller 						    vc->vc_rows - b,
18371148836fSHelge Deller 						    vc->vc_cols);
18381148836fSHelge Deller 			} else if (info->flags & FBINFO_READS_FAST)
18391148836fSHelge Deller 				fbcon_bmove(vc, t + count, 0, t, 0,
18401148836fSHelge Deller 					    b - t - count, vc->vc_cols);
18411148836fSHelge Deller 			else
18421148836fSHelge Deller 				goto redraw_up;
1843559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18441148836fSHelge Deller 			break;
18451148836fSHelge Deller 
18461148836fSHelge Deller 		case SCROLL_REDRAW:
18471148836fSHelge Deller 		      redraw_up:
1848933ab3a8SJiri Slaby (SUSE) 			fbcon_redraw(vc, t, b - t - count,
18496104c370SDaniel Vetter 				     count * vc->vc_cols);
1850559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18516104c370SDaniel Vetter 			scr_memsetw((unsigned short *) (vc->vc_origin +
18526104c370SDaniel Vetter 							vc->vc_size_row *
18536104c370SDaniel Vetter 							(b - count)),
18546104c370SDaniel Vetter 				    vc->vc_video_erase_char,
18556104c370SDaniel Vetter 				    vc->vc_size_row * count);
18566104c370SDaniel Vetter 			return true;
18571148836fSHelge Deller 		}
18581148836fSHelge Deller 		break;
18596104c370SDaniel Vetter 
18606104c370SDaniel Vetter 	case SM_DOWN:
18616104c370SDaniel Vetter 		if (count > vc->vc_rows)	/* Maximum realistic size */
18626104c370SDaniel Vetter 			count = vc->vc_rows;
1863a3f781a9SHelge Deller 		switch (fb_scrollmode(p)) {
18641148836fSHelge Deller 		case SCROLL_MOVE:
18651148836fSHelge Deller 			fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
18661148836fSHelge Deller 				     -count);
1867559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, t, 0, count, vc->vc_cols);
18681148836fSHelge Deller 			scr_memsetw((unsigned short *) (vc->vc_origin +
18691148836fSHelge Deller 							vc->vc_size_row *
18701148836fSHelge Deller 							t),
18711148836fSHelge Deller 				    vc->vc_video_erase_char,
18721148836fSHelge Deller 				    vc->vc_size_row * count);
18731148836fSHelge Deller 			return true;
18741148836fSHelge Deller 
18751148836fSHelge Deller 		case SCROLL_WRAP_MOVE:
18761148836fSHelge Deller 			if (b - t - count > 3 * vc->vc_rows >> 2) {
18771148836fSHelge Deller 				if (vc->vc_rows - b > 0)
18781148836fSHelge Deller 					fbcon_bmove(vc, b, 0, b - count, 0,
18791148836fSHelge Deller 						    vc->vc_rows - b,
18801148836fSHelge Deller 						    vc->vc_cols);
18811148836fSHelge Deller 				ywrap_down(vc, count);
18821148836fSHelge Deller 				if (t > 0)
18831148836fSHelge Deller 					fbcon_bmove(vc, count, 0, 0, 0, t,
18841148836fSHelge Deller 						    vc->vc_cols);
18851148836fSHelge Deller 			} else if (info->flags & FBINFO_READS_FAST)
18861148836fSHelge Deller 				fbcon_bmove(vc, t, 0, t + count, 0,
18871148836fSHelge Deller 					    b - t - count, vc->vc_cols);
18881148836fSHelge Deller 			else
18891148836fSHelge Deller 				goto redraw_down;
1890559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, t, 0, count, vc->vc_cols);
18911148836fSHelge Deller 			break;
18921148836fSHelge Deller 
18931148836fSHelge Deller 		case SCROLL_PAN_MOVE:
18941148836fSHelge Deller 			if ((count - p->yscroll <= p->vrows - vc->vc_rows)
18951148836fSHelge Deller 			    && ((!scroll_partial && (b - t == vc->vc_rows))
18961148836fSHelge Deller 				|| (scroll_partial
18971148836fSHelge Deller 				    && (b - t - count >
18981148836fSHelge Deller 					3 * vc->vc_rows >> 2)))) {
18991148836fSHelge Deller 				if (vc->vc_rows - b > 0)
19001148836fSHelge Deller 					fbcon_bmove(vc, b, 0, b - count, 0,
19011148836fSHelge Deller 						    vc->vc_rows - b,
19021148836fSHelge Deller 						    vc->vc_cols);
19031148836fSHelge Deller 				ypan_down(vc, count);
19041148836fSHelge Deller 				if (t > 0)
19051148836fSHelge Deller 					fbcon_bmove(vc, count, 0, 0, 0, t,
19061148836fSHelge Deller 						    vc->vc_cols);
19071148836fSHelge Deller 			} else if (info->flags & FBINFO_READS_FAST)
19081148836fSHelge Deller 				fbcon_bmove(vc, t, 0, t + count, 0,
19091148836fSHelge Deller 					    b - t - count, vc->vc_cols);
19101148836fSHelge Deller 			else
19111148836fSHelge Deller 				goto redraw_down;
1912559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, t, 0, count, vc->vc_cols);
19131148836fSHelge Deller 			break;
19141148836fSHelge Deller 
19151148836fSHelge Deller 		case SCROLL_PAN_REDRAW:
19161148836fSHelge Deller 			if ((count - p->yscroll <= p->vrows - vc->vc_rows)
19171148836fSHelge Deller 			    && ((!scroll_partial && (b - t == vc->vc_rows))
19181148836fSHelge Deller 				|| (scroll_partial
19191148836fSHelge Deller 				    && (b - t - count >
19201148836fSHelge Deller 					3 * vc->vc_rows >> 2)))) {
19211148836fSHelge Deller 				if (vc->vc_rows - b > 0)
19221148836fSHelge Deller 					fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
19231148836fSHelge Deller 							  b - count);
19241148836fSHelge Deller 				ypan_down_redraw(vc, t, count);
19251148836fSHelge Deller 				if (t > 0)
19261148836fSHelge Deller 					fbcon_redraw_move(vc, p, count, t, 0);
19271148836fSHelge Deller 			} else
19281148836fSHelge Deller 				fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1929559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, t, 0, count, vc->vc_cols);
19301148836fSHelge Deller 			break;
19311148836fSHelge Deller 
19321148836fSHelge Deller 		case SCROLL_REDRAW:
19331148836fSHelge Deller 		      redraw_down:
1934933ab3a8SJiri Slaby (SUSE) 			fbcon_redraw(vc, b - 1, b - t - count,
19356104c370SDaniel Vetter 				     -count * vc->vc_cols);
1936559f01a0SJiri Slaby (SUSE) 			__fbcon_clear(vc, t, 0, count, vc->vc_cols);
19376104c370SDaniel Vetter 			scr_memsetw((unsigned short *) (vc->vc_origin +
19386104c370SDaniel Vetter 							vc->vc_size_row *
19396104c370SDaniel Vetter 							t),
19406104c370SDaniel Vetter 				    vc->vc_video_erase_char,
19416104c370SDaniel Vetter 				    vc->vc_size_row * count);
19426104c370SDaniel Vetter 			return true;
19436104c370SDaniel Vetter 		}
19441148836fSHelge Deller 	}
19456104c370SDaniel Vetter 	return false;
19466104c370SDaniel Vetter }
19476104c370SDaniel Vetter 
19481148836fSHelge Deller 
updatescrollmode_accel(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)1949a3f781a9SHelge Deller static void updatescrollmode_accel(struct fbcon_display *p,
19506104c370SDaniel Vetter 					struct fb_info *info,
19516104c370SDaniel Vetter 					struct vc_data *vc)
19526104c370SDaniel Vetter {
1953a3f781a9SHelge Deller #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
19546104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
195587ab9f6bSHelge Deller 	int cap = info->flags;
195687ab9f6bSHelge Deller 	u16 t = 0;
195787ab9f6bSHelge Deller 	int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
195887ab9f6bSHelge Deller 				  info->fix.xpanstep);
195987ab9f6bSHelge Deller 	int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
19606104c370SDaniel Vetter 	int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
19616104c370SDaniel Vetter 	int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
19626104c370SDaniel Vetter 				   info->var.xres_virtual);
196387ab9f6bSHelge Deller 	int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
196487ab9f6bSHelge Deller 		divides(ypan, vc->vc_font.height) && vyres > yres;
196587ab9f6bSHelge Deller 	int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
196687ab9f6bSHelge Deller 		divides(ywrap, vc->vc_font.height) &&
196787ab9f6bSHelge Deller 		divides(vc->vc_font.height, vyres) &&
196887ab9f6bSHelge Deller 		divides(vc->vc_font.height, yres);
196987ab9f6bSHelge Deller 	int reading_fast = cap & FBINFO_READS_FAST;
197087ab9f6bSHelge Deller 	int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
197187ab9f6bSHelge Deller 		!(cap & FBINFO_HWACCEL_DISABLED);
197287ab9f6bSHelge Deller 	int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
197387ab9f6bSHelge Deller 		!(cap & FBINFO_HWACCEL_DISABLED);
19746104c370SDaniel Vetter 
197587ab9f6bSHelge Deller 	if (good_wrap || good_pan) {
197687ab9f6bSHelge Deller 		if (reading_fast || fast_copyarea)
197787ab9f6bSHelge Deller 			p->scrollmode = good_wrap ?
197887ab9f6bSHelge Deller 				SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
197987ab9f6bSHelge Deller 		else
198087ab9f6bSHelge Deller 			p->scrollmode = good_wrap ? SCROLL_REDRAW :
198187ab9f6bSHelge Deller 				SCROLL_PAN_REDRAW;
198287ab9f6bSHelge Deller 	} else {
198387ab9f6bSHelge Deller 		if (reading_fast || (fast_copyarea && !fast_imageblit))
198487ab9f6bSHelge Deller 			p->scrollmode = SCROLL_MOVE;
198587ab9f6bSHelge Deller 		else
198687ab9f6bSHelge Deller 			p->scrollmode = SCROLL_REDRAW;
198787ab9f6bSHelge Deller 	}
1988a3f781a9SHelge Deller #endif
1989a3f781a9SHelge Deller }
1990a3f781a9SHelge Deller 
updatescrollmode(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)1991a3f781a9SHelge Deller static void updatescrollmode(struct fbcon_display *p,
1992a3f781a9SHelge Deller 					struct fb_info *info,
1993a3f781a9SHelge Deller 					struct vc_data *vc)
1994a3f781a9SHelge Deller {
1995a3f781a9SHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
1996a3f781a9SHelge Deller 	int fh = vc->vc_font.height;
1997a3f781a9SHelge Deller 	int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1998a3f781a9SHelge Deller 	int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1999a3f781a9SHelge Deller 				   info->var.xres_virtual);
2000a3f781a9SHelge Deller 
2001a3f781a9SHelge Deller 	p->vrows = vyres/fh;
2002a3f781a9SHelge Deller 	if (yres > (fh * (vc->vc_rows + 1)))
2003a3f781a9SHelge Deller 		p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
2004a3f781a9SHelge Deller 	if ((yres % fh) && (vyres % fh < yres % fh))
2005a3f781a9SHelge Deller 		p->vrows--;
2006a3f781a9SHelge Deller 
2007a3f781a9SHelge Deller 	/* update scrollmode in case hardware acceleration is used */
2008a3f781a9SHelge Deller 	updatescrollmode_accel(p, info, vc);
20096104c370SDaniel Vetter }
20106104c370SDaniel Vetter 
201139b3cffbSGeorge Kennedy #define PITCH(w) (((w) + 7) >> 3)
201239b3cffbSGeorge Kennedy #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
201339b3cffbSGeorge Kennedy 
fbcon_resize(struct vc_data * vc,unsigned int width,unsigned int height,bool from_user)20146104c370SDaniel Vetter static int fbcon_resize(struct vc_data *vc, unsigned int width,
2015beccdcfaSJiri Slaby (SUSE) 			unsigned int height, bool from_user)
20166104c370SDaniel Vetter {
2017409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
20186104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
201950233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
20206104c370SDaniel Vetter 	struct fb_var_screeninfo var = info->var;
20216104c370SDaniel Vetter 	int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
20226104c370SDaniel Vetter 
2023ec0972adSTetsuo Handa 	if (p->userfont && FNTSIZE(vc->vc_font.data)) {
202439b3cffbSGeorge Kennedy 		int size;
202539b3cffbSGeorge Kennedy 		int pitch = PITCH(vc->vc_font.width);
202639b3cffbSGeorge Kennedy 
202739b3cffbSGeorge Kennedy 		/*
202839b3cffbSGeorge Kennedy 		 * If user font, ensure that a possible change to user font
202939b3cffbSGeorge Kennedy 		 * height or width will not allow a font data out-of-bounds access.
203039b3cffbSGeorge Kennedy 		 * NOTE: must use original charcount in calculation as font
203139b3cffbSGeorge Kennedy 		 * charcount can change and cannot be used to determine the
203239b3cffbSGeorge Kennedy 		 * font data allocated size.
203339b3cffbSGeorge Kennedy 		 */
203439b3cffbSGeorge Kennedy 		if (pitch <= 0)
203539b3cffbSGeorge Kennedy 			return -EINVAL;
2036a1ac250aSPeilin Ye 		size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount);
203739b3cffbSGeorge Kennedy 		if (size > FNTSIZE(vc->vc_font.data))
203839b3cffbSGeorge Kennedy 			return -EINVAL;
203939b3cffbSGeorge Kennedy 	}
204039b3cffbSGeorge Kennedy 
20416104c370SDaniel Vetter 	virt_w = FBCON_SWAP(ops->rotate, width, height);
20426104c370SDaniel Vetter 	virt_h = FBCON_SWAP(ops->rotate, height, width);
20436104c370SDaniel Vetter 	virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
20446104c370SDaniel Vetter 				 vc->vc_font.height);
20456104c370SDaniel Vetter 	virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
20466104c370SDaniel Vetter 				 vc->vc_font.width);
20476104c370SDaniel Vetter 	var.xres = virt_w * virt_fw;
20486104c370SDaniel Vetter 	var.yres = virt_h * virt_fh;
20496104c370SDaniel Vetter 	x_diff = info->var.xres - var.xres;
20506104c370SDaniel Vetter 	y_diff = info->var.yres - var.yres;
20516104c370SDaniel Vetter 	if (x_diff < 0 || x_diff > virt_fw ||
20526104c370SDaniel Vetter 	    y_diff < 0 || y_diff > virt_fh) {
20536104c370SDaniel Vetter 		const struct fb_videomode *mode;
20546104c370SDaniel Vetter 
2055b1cba76dSSam Ravnborg 		pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
20566104c370SDaniel Vetter 		mode = fb_find_best_mode(&var, &info->modelist);
20576104c370SDaniel Vetter 		if (mode == NULL)
20586104c370SDaniel Vetter 			return -EINVAL;
20596104c370SDaniel Vetter 		display_to_var(&var, p);
20606104c370SDaniel Vetter 		fb_videomode_to_var(&var, mode);
20616104c370SDaniel Vetter 
20626104c370SDaniel Vetter 		if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
20636104c370SDaniel Vetter 			return -EINVAL;
20646104c370SDaniel Vetter 
2065b1cba76dSSam Ravnborg 		pr_debug("resize now %ix%i\n", var.xres, var.yres);
2066ffb324e6STetsuo Handa 		if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
20676104c370SDaniel Vetter 			var.activate = FB_ACTIVATE_NOW |
20686104c370SDaniel Vetter 				FB_ACTIVATE_FORCE;
20696104c370SDaniel Vetter 			fb_set_var(info, &var);
20706104c370SDaniel Vetter 		}
20716104c370SDaniel Vetter 		var_to_display(p, &info->var, info);
20726104c370SDaniel Vetter 		ops->var = info->var;
20736104c370SDaniel Vetter 	}
20746104c370SDaniel Vetter 	updatescrollmode(p, info, vc);
20756104c370SDaniel Vetter 	return 0;
20766104c370SDaniel Vetter }
20776104c370SDaniel Vetter 
fbcon_switch(struct vc_data * vc)20788d5cc8eeSJiri Slaby (SUSE) static bool fbcon_switch(struct vc_data *vc)
20796104c370SDaniel Vetter {
20806104c370SDaniel Vetter 	struct fb_info *info, *old_info = NULL;
20816104c370SDaniel Vetter 	struct fbcon_ops *ops;
208250233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
20836104c370SDaniel Vetter 	struct fb_var_screeninfo var;
2084a1ac250aSPeilin Ye 	int i, ret, prev_console;
20856104c370SDaniel Vetter 
2086409d6c95SDaniel Vetter 	info = fbcon_info_from_console(vc->vc_num);
20876104c370SDaniel Vetter 	ops = info->fbcon_par;
20886104c370SDaniel Vetter 
20896104c370SDaniel Vetter 	if (logo_shown >= 0) {
20906104c370SDaniel Vetter 		struct vc_data *conp2 = vc_cons[logo_shown].d;
20916104c370SDaniel Vetter 
20926104c370SDaniel Vetter 		if (conp2->vc_top == logo_lines
20936104c370SDaniel Vetter 		    && conp2->vc_bottom == conp2->vc_rows)
20946104c370SDaniel Vetter 			conp2->vc_top = 0;
20956104c370SDaniel Vetter 		logo_shown = FBCON_LOGO_CANSHOW;
20966104c370SDaniel Vetter 	}
20976104c370SDaniel Vetter 
20986104c370SDaniel Vetter 	prev_console = ops->currcon;
20996104c370SDaniel Vetter 	if (prev_console != -1)
2100409d6c95SDaniel Vetter 		old_info = fbcon_info_from_console(prev_console);
21016104c370SDaniel Vetter 	/*
21026104c370SDaniel Vetter 	 * FIXME: If we have multiple fbdev's loaded, we need to
21036104c370SDaniel Vetter 	 * update all info->currcon.  Perhaps, we can place this
21046104c370SDaniel Vetter 	 * in a centralized structure, but this might break some
21056104c370SDaniel Vetter 	 * drivers.
21066104c370SDaniel Vetter 	 *
21076104c370SDaniel Vetter 	 * info->currcon = vc->vc_num;
21086104c370SDaniel Vetter 	 */
2109efc3acbcSDaniel Vetter 	fbcon_for_each_registered_fb(i) {
2110efc3acbcSDaniel Vetter 		if (fbcon_registered_fb[i]->fbcon_par) {
2111efc3acbcSDaniel Vetter 			struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;
21126104c370SDaniel Vetter 
21136104c370SDaniel Vetter 			o->currcon = vc->vc_num;
21146104c370SDaniel Vetter 		}
21156104c370SDaniel Vetter 	}
21166104c370SDaniel Vetter 	memset(&var, 0, sizeof(struct fb_var_screeninfo));
21176104c370SDaniel Vetter 	display_to_var(&var, p);
21186104c370SDaniel Vetter 	var.activate = FB_ACTIVATE_NOW;
21196104c370SDaniel Vetter 
21206104c370SDaniel Vetter 	/*
21216104c370SDaniel Vetter 	 * make sure we don't unnecessarily trip the memcmp()
21226104c370SDaniel Vetter 	 * in fb_set_var()
21236104c370SDaniel Vetter 	 */
21246104c370SDaniel Vetter 	info->var.activate = var.activate;
21256104c370SDaniel Vetter 	var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
21266104c370SDaniel Vetter 	fb_set_var(info, &var);
21276104c370SDaniel Vetter 	ops->var = info->var;
21286104c370SDaniel Vetter 
21296104c370SDaniel Vetter 	if (old_info != NULL && (old_info != info ||
21306104c370SDaniel Vetter 				 info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
21316104c370SDaniel Vetter 		if (info->fbops->fb_set_par) {
21326104c370SDaniel Vetter 			ret = info->fbops->fb_set_par(info);
21336104c370SDaniel Vetter 
21346104c370SDaniel Vetter 			if (ret)
21356104c370SDaniel Vetter 				printk(KERN_ERR "fbcon_switch: detected "
21366104c370SDaniel Vetter 					"unhandled fb_set_par error, "
21376104c370SDaniel Vetter 					"error code %d\n", ret);
21386104c370SDaniel Vetter 		}
21396104c370SDaniel Vetter 
21406104c370SDaniel Vetter 		if (old_info != info)
21413b0fb6abSDaniel Vetter 			fbcon_del_cursor_work(old_info);
21426104c370SDaniel Vetter 	}
21436104c370SDaniel Vetter 
21446104c370SDaniel Vetter 	if (fbcon_is_inactive(vc, info) ||
21456104c370SDaniel Vetter 	    ops->blank_state != FB_BLANK_UNBLANK)
21463b0fb6abSDaniel Vetter 		fbcon_del_cursor_work(info);
21476104c370SDaniel Vetter 	else
21483b0fb6abSDaniel Vetter 		fbcon_add_cursor_work(info);
21496104c370SDaniel Vetter 
21506104c370SDaniel Vetter 	set_blitting_type(vc, info);
21516104c370SDaniel Vetter 	ops->cursor_reset = 1;
21526104c370SDaniel Vetter 
21536104c370SDaniel Vetter 	if (ops->rotate_font && ops->rotate_font(info, vc)) {
21546104c370SDaniel Vetter 		ops->rotate = FB_ROTATE_UR;
21556104c370SDaniel Vetter 		set_blitting_type(vc, info);
21566104c370SDaniel Vetter 	}
21576104c370SDaniel Vetter 
21586104c370SDaniel Vetter 	vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
21596104c370SDaniel Vetter 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
21606104c370SDaniel Vetter 
2161a1ac250aSPeilin Ye 	if (vc->vc_font.charcount > 256)
21626104c370SDaniel Vetter 		vc->vc_complement_mask <<= 1;
21636104c370SDaniel Vetter 
21646104c370SDaniel Vetter 	updatescrollmode(p, info, vc);
21656104c370SDaniel Vetter 
2166a3f781a9SHelge Deller 	switch (fb_scrollmode(p)) {
21671148836fSHelge Deller 	case SCROLL_WRAP_MOVE:
21681148836fSHelge Deller 		scrollback_phys_max = p->vrows - vc->vc_rows;
21691148836fSHelge Deller 		break;
21701148836fSHelge Deller 	case SCROLL_PAN_MOVE:
21711148836fSHelge Deller 	case SCROLL_PAN_REDRAW:
21721148836fSHelge Deller 		scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
21731148836fSHelge Deller 		if (scrollback_phys_max < 0)
21746104c370SDaniel Vetter 			scrollback_phys_max = 0;
21751148836fSHelge Deller 		break;
21761148836fSHelge Deller 	default:
21771148836fSHelge Deller 		scrollback_phys_max = 0;
21781148836fSHelge Deller 		break;
21791148836fSHelge Deller 	}
21801148836fSHelge Deller 
21816104c370SDaniel Vetter 	scrollback_max = 0;
21826104c370SDaniel Vetter 	scrollback_current = 0;
21836104c370SDaniel Vetter 
21846104c370SDaniel Vetter 	if (!fbcon_is_inactive(vc, info)) {
21856104c370SDaniel Vetter 	    ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
21866104c370SDaniel Vetter 	    ops->update_start(info);
21876104c370SDaniel Vetter 	}
21886104c370SDaniel Vetter 
21896104c370SDaniel Vetter 	fbcon_set_palette(vc, color_table);
21906104c370SDaniel Vetter 	fbcon_clear_margins(vc, 0);
21916104c370SDaniel Vetter 
21926104c370SDaniel Vetter 	if (logo_shown == FBCON_LOGO_DRAW) {
21936104c370SDaniel Vetter 
21946104c370SDaniel Vetter 		logo_shown = fg_console;
21956104c370SDaniel Vetter 		fb_show_logo(info, ops->rotate);
21966104c370SDaniel Vetter 		update_region(vc,
21976104c370SDaniel Vetter 			      vc->vc_origin + vc->vc_size_row * vc->vc_top,
21986104c370SDaniel Vetter 			      vc->vc_size_row * (vc->vc_bottom -
21996104c370SDaniel Vetter 						 vc->vc_top) / 2);
22008d5cc8eeSJiri Slaby (SUSE) 		return false;
22016104c370SDaniel Vetter 	}
22028d5cc8eeSJiri Slaby (SUSE) 	return true;
22036104c370SDaniel Vetter }
22046104c370SDaniel Vetter 
fbcon_generic_blank(struct vc_data * vc,struct fb_info * info,int blank)22056104c370SDaniel Vetter static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
22066104c370SDaniel Vetter 				int blank)
22076104c370SDaniel Vetter {
22086104c370SDaniel Vetter 	if (blank) {
22096104c370SDaniel Vetter 		unsigned short charmask = vc->vc_hi_font_mask ?
22106104c370SDaniel Vetter 			0x1ff : 0xff;
22116104c370SDaniel Vetter 		unsigned short oldc;
22126104c370SDaniel Vetter 
22136104c370SDaniel Vetter 		oldc = vc->vc_video_erase_char;
22146104c370SDaniel Vetter 		vc->vc_video_erase_char &= charmask;
2215559f01a0SJiri Slaby (SUSE) 		__fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
22166104c370SDaniel Vetter 		vc->vc_video_erase_char = oldc;
22176104c370SDaniel Vetter 	}
22186104c370SDaniel Vetter }
22196104c370SDaniel Vetter 
fbcon_blank(struct vc_data * vc,enum vesa_blank_mode blank,bool mode_switch)222077e11093SJiri Slaby (SUSE) static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
222177e11093SJiri Slaby (SUSE) 			bool mode_switch)
22226104c370SDaniel Vetter {
2223409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22246104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
22256104c370SDaniel Vetter 
22266104c370SDaniel Vetter 	if (mode_switch) {
22276104c370SDaniel Vetter 		struct fb_var_screeninfo var = info->var;
22286104c370SDaniel Vetter 
22296104c370SDaniel Vetter 		ops->graphics = 1;
22306104c370SDaniel Vetter 
22316104c370SDaniel Vetter 		if (!blank) {
2232dc5bdb68SDaniel Vetter 			var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2233dc5bdb68SDaniel Vetter 				FB_ACTIVATE_KD_TEXT;
22346104c370SDaniel Vetter 			fb_set_var(info, &var);
22356104c370SDaniel Vetter 			ops->graphics = 0;
22366104c370SDaniel Vetter 			ops->var = info->var;
22376104c370SDaniel Vetter 		}
22386104c370SDaniel Vetter 	}
22396104c370SDaniel Vetter 
22406104c370SDaniel Vetter  	if (!fbcon_is_inactive(vc, info)) {
22416104c370SDaniel Vetter 		if (ops->blank_state != blank) {
22426104c370SDaniel Vetter 			ops->blank_state = blank;
2243a292e3fcSJiri Slaby (SUSE) 			fbcon_cursor(vc, !blank);
22446104c370SDaniel Vetter 			ops->cursor_flash = (!blank);
22456104c370SDaniel Vetter 
22466104c370SDaniel Vetter 			if (fb_blank(info, blank))
22476104c370SDaniel Vetter 				fbcon_generic_blank(vc, info, blank);
22486104c370SDaniel Vetter 		}
22496104c370SDaniel Vetter 
22506104c370SDaniel Vetter 		if (!blank)
22516104c370SDaniel Vetter 			update_screen(vc);
22526104c370SDaniel Vetter 	}
22536104c370SDaniel Vetter 
22546104c370SDaniel Vetter 	if (mode_switch || fbcon_is_inactive(vc, info) ||
22556104c370SDaniel Vetter 	    ops->blank_state != FB_BLANK_UNBLANK)
22563b0fb6abSDaniel Vetter 		fbcon_del_cursor_work(info);
22576104c370SDaniel Vetter 	else
22583b0fb6abSDaniel Vetter 		fbcon_add_cursor_work(info);
22596104c370SDaniel Vetter 
226077e11093SJiri Slaby (SUSE) 	return false;
22616104c370SDaniel Vetter }
22626104c370SDaniel Vetter 
fbcon_debug_enter(struct vc_data * vc)22637995c30dSJiri Slaby (SUSE) static void fbcon_debug_enter(struct vc_data *vc)
22646104c370SDaniel Vetter {
2265409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22666104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
22676104c370SDaniel Vetter 
22686104c370SDaniel Vetter 	ops->save_graphics = ops->graphics;
22696104c370SDaniel Vetter 	ops->graphics = 0;
22706104c370SDaniel Vetter 	if (info->fbops->fb_debug_enter)
22716104c370SDaniel Vetter 		info->fbops->fb_debug_enter(info);
22726104c370SDaniel Vetter 	fbcon_set_palette(vc, color_table);
22736104c370SDaniel Vetter }
22746104c370SDaniel Vetter 
fbcon_debug_leave(struct vc_data * vc)22757995c30dSJiri Slaby (SUSE) static void fbcon_debug_leave(struct vc_data *vc)
22766104c370SDaniel Vetter {
2277409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22786104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
22796104c370SDaniel Vetter 
22806104c370SDaniel Vetter 	ops->graphics = ops->save_graphics;
22816104c370SDaniel Vetter 	if (info->fbops->fb_debug_leave)
22826104c370SDaniel Vetter 		info->fbops->fb_debug_leave(info);
22836104c370SDaniel Vetter }
22846104c370SDaniel Vetter 
fbcon_get_font(struct vc_data * vc,struct console_font * font,unsigned int vpitch)2285ffc1e089SSamuel Thibault static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch)
22866104c370SDaniel Vetter {
22876104c370SDaniel Vetter 	u8 *fontdata = vc->vc_font.data;
22886104c370SDaniel Vetter 	u8 *data = font->data;
22896104c370SDaniel Vetter 	int i, j;
22906104c370SDaniel Vetter 
22916104c370SDaniel Vetter 	font->width = vc->vc_font.width;
22926104c370SDaniel Vetter 	font->height = vc->vc_font.height;
229305e2600cSSamuel Thibault 	if (font->height > vpitch)
229405e2600cSSamuel Thibault 		return -ENOSPC;
22956104c370SDaniel Vetter 	font->charcount = vc->vc_hi_font_mask ? 512 : 256;
22966104c370SDaniel Vetter 	if (!font->data)
22976104c370SDaniel Vetter 		return 0;
22986104c370SDaniel Vetter 
22996104c370SDaniel Vetter 	if (font->width <= 8) {
23006104c370SDaniel Vetter 		j = vc->vc_font.height;
23015af08640SPeilin Ye 		if (font->charcount * j > FNTSIZE(fontdata))
23025af08640SPeilin Ye 			return -EINVAL;
23035af08640SPeilin Ye 
23046104c370SDaniel Vetter 		for (i = 0; i < font->charcount; i++) {
23056104c370SDaniel Vetter 			memcpy(data, fontdata, j);
2306ffc1e089SSamuel Thibault 			memset(data + j, 0, vpitch - j);
2307ffc1e089SSamuel Thibault 			data += vpitch;
23086104c370SDaniel Vetter 			fontdata += j;
23096104c370SDaniel Vetter 		}
23106104c370SDaniel Vetter 	} else if (font->width <= 16) {
23116104c370SDaniel Vetter 		j = vc->vc_font.height * 2;
23125af08640SPeilin Ye 		if (font->charcount * j > FNTSIZE(fontdata))
23135af08640SPeilin Ye 			return -EINVAL;
23145af08640SPeilin Ye 
23156104c370SDaniel Vetter 		for (i = 0; i < font->charcount; i++) {
23166104c370SDaniel Vetter 			memcpy(data, fontdata, j);
2317ffc1e089SSamuel Thibault 			memset(data + j, 0, 2*vpitch - j);
2318ffc1e089SSamuel Thibault 			data += 2*vpitch;
23196104c370SDaniel Vetter 			fontdata += j;
23206104c370SDaniel Vetter 		}
23216104c370SDaniel Vetter 	} else if (font->width <= 24) {
23225af08640SPeilin Ye 		if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
23235af08640SPeilin Ye 			return -EINVAL;
23245af08640SPeilin Ye 
23256104c370SDaniel Vetter 		for (i = 0; i < font->charcount; i++) {
23266104c370SDaniel Vetter 			for (j = 0; j < vc->vc_font.height; j++) {
23276104c370SDaniel Vetter 				*data++ = fontdata[0];
23286104c370SDaniel Vetter 				*data++ = fontdata[1];
23296104c370SDaniel Vetter 				*data++ = fontdata[2];
23306104c370SDaniel Vetter 				fontdata += sizeof(u32);
23316104c370SDaniel Vetter 			}
2332ffc1e089SSamuel Thibault 			memset(data, 0, 3 * (vpitch - j));
2333ffc1e089SSamuel Thibault 			data += 3 * (vpitch - j);
23346104c370SDaniel Vetter 		}
23356104c370SDaniel Vetter 	} else {
23366104c370SDaniel Vetter 		j = vc->vc_font.height * 4;
23375af08640SPeilin Ye 		if (font->charcount * j > FNTSIZE(fontdata))
23385af08640SPeilin Ye 			return -EINVAL;
23395af08640SPeilin Ye 
23406104c370SDaniel Vetter 		for (i = 0; i < font->charcount; i++) {
23416104c370SDaniel Vetter 			memcpy(data, fontdata, j);
2342ffc1e089SSamuel Thibault 			memset(data + j, 0, 4 * vpitch - j);
2343ffc1e089SSamuel Thibault 			data += 4 * vpitch;
23446104c370SDaniel Vetter 			fontdata += j;
23456104c370SDaniel Vetter 		}
23466104c370SDaniel Vetter 	}
23476104c370SDaniel Vetter 	return 0;
23486104c370SDaniel Vetter }
23496104c370SDaniel Vetter 
23506104c370SDaniel Vetter /* set/clear vc_hi_font_mask and update vc attrs accordingly */
set_vc_hi_font(struct vc_data * vc,bool set)23516104c370SDaniel Vetter static void set_vc_hi_font(struct vc_data *vc, bool set)
23526104c370SDaniel Vetter {
23536104c370SDaniel Vetter 	if (!set) {
23546104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0;
23556104c370SDaniel Vetter 		if (vc->vc_can_do_color) {
23566104c370SDaniel Vetter 			vc->vc_complement_mask >>= 1;
23576104c370SDaniel Vetter 			vc->vc_s_complement_mask >>= 1;
23586104c370SDaniel Vetter 		}
23596104c370SDaniel Vetter 
23606104c370SDaniel Vetter 		/* ++Edmund: reorder the attribute bits */
23616104c370SDaniel Vetter 		if (vc->vc_can_do_color) {
23626104c370SDaniel Vetter 			unsigned short *cp =
23636104c370SDaniel Vetter 			    (unsigned short *) vc->vc_origin;
23646104c370SDaniel Vetter 			int count = vc->vc_screenbuf_size / 2;
23656104c370SDaniel Vetter 			unsigned short c;
23666104c370SDaniel Vetter 			for (; count > 0; count--, cp++) {
23676104c370SDaniel Vetter 				c = scr_readw(cp);
23686104c370SDaniel Vetter 				scr_writew(((c & 0xfe00) >> 1) |
23696104c370SDaniel Vetter 					   (c & 0xff), cp);
23706104c370SDaniel Vetter 			}
23716104c370SDaniel Vetter 			c = vc->vc_video_erase_char;
23726104c370SDaniel Vetter 			vc->vc_video_erase_char =
23736104c370SDaniel Vetter 			    ((c & 0xfe00) >> 1) | (c & 0xff);
23746104c370SDaniel Vetter 			vc->vc_attr >>= 1;
23756104c370SDaniel Vetter 		}
23766104c370SDaniel Vetter 	} else {
23776104c370SDaniel Vetter 		vc->vc_hi_font_mask = 0x100;
23786104c370SDaniel Vetter 		if (vc->vc_can_do_color) {
23796104c370SDaniel Vetter 			vc->vc_complement_mask <<= 1;
23806104c370SDaniel Vetter 			vc->vc_s_complement_mask <<= 1;
23816104c370SDaniel Vetter 		}
23826104c370SDaniel Vetter 
23836104c370SDaniel Vetter 		/* ++Edmund: reorder the attribute bits */
23846104c370SDaniel Vetter 		{
23856104c370SDaniel Vetter 			unsigned short *cp =
23866104c370SDaniel Vetter 			    (unsigned short *) vc->vc_origin;
23876104c370SDaniel Vetter 			int count = vc->vc_screenbuf_size / 2;
23886104c370SDaniel Vetter 			unsigned short c;
23896104c370SDaniel Vetter 			for (; count > 0; count--, cp++) {
23906104c370SDaniel Vetter 				unsigned short newc;
23916104c370SDaniel Vetter 				c = scr_readw(cp);
23926104c370SDaniel Vetter 				if (vc->vc_can_do_color)
23936104c370SDaniel Vetter 					newc =
23946104c370SDaniel Vetter 					    ((c & 0xff00) << 1) | (c &
23956104c370SDaniel Vetter 								   0xff);
23966104c370SDaniel Vetter 				else
23976104c370SDaniel Vetter 					newc = c & ~0x100;
23986104c370SDaniel Vetter 				scr_writew(newc, cp);
23996104c370SDaniel Vetter 			}
24006104c370SDaniel Vetter 			c = vc->vc_video_erase_char;
24016104c370SDaniel Vetter 			if (vc->vc_can_do_color) {
24026104c370SDaniel Vetter 				vc->vc_video_erase_char =
24036104c370SDaniel Vetter 				    ((c & 0xff00) << 1) | (c & 0xff);
24046104c370SDaniel Vetter 				vc->vc_attr <<= 1;
24056104c370SDaniel Vetter 			} else
24066104c370SDaniel Vetter 				vc->vc_video_erase_char = c & ~0x100;
24076104c370SDaniel Vetter 		}
24086104c370SDaniel Vetter 	}
24096104c370SDaniel Vetter }
24106104c370SDaniel Vetter 
fbcon_do_set_font(struct vc_data * vc,int w,int h,int charcount,const u8 * data,int userfont)2411a1ac250aSPeilin Ye static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
24126104c370SDaniel Vetter 			     const u8 * data, int userfont)
24136104c370SDaniel Vetter {
2414409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
24156104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
241650233393SDaniel Vetter 	struct fbcon_display *p = &fb_display[vc->vc_num];
2417a5a92303SShigeru Yoshida 	int resize, ret, old_userfont, old_width, old_height, old_charcount;
241800d6a284SJiri Slaby (SUSE) 	u8 *old_data = vc->vc_font.data;
24196104c370SDaniel Vetter 
24206104c370SDaniel Vetter 	resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
24216104c370SDaniel Vetter 	vc->vc_font.data = (void *)(p->fontdata = data);
2422a5a92303SShigeru Yoshida 	old_userfont = p->userfont;
24236104c370SDaniel Vetter 	if ((p->userfont = userfont))
24246104c370SDaniel Vetter 		REFCOUNT(data)++;
2425a5a92303SShigeru Yoshida 
2426a5a92303SShigeru Yoshida 	old_width = vc->vc_font.width;
2427a5a92303SShigeru Yoshida 	old_height = vc->vc_font.height;
2428a5a92303SShigeru Yoshida 	old_charcount = vc->vc_font.charcount;
2429a5a92303SShigeru Yoshida 
24306104c370SDaniel Vetter 	vc->vc_font.width = w;
24316104c370SDaniel Vetter 	vc->vc_font.height = h;
2432a1ac250aSPeilin Ye 	vc->vc_font.charcount = charcount;
2433a1ac250aSPeilin Ye 	if (vc->vc_hi_font_mask && charcount == 256)
24346104c370SDaniel Vetter 		set_vc_hi_font(vc, false);
2435a1ac250aSPeilin Ye 	else if (!vc->vc_hi_font_mask && charcount == 512)
24366104c370SDaniel Vetter 		set_vc_hi_font(vc, true);
24376104c370SDaniel Vetter 
24386104c370SDaniel Vetter 	if (resize) {
24396104c370SDaniel Vetter 		int cols, rows;
24406104c370SDaniel Vetter 
24416104c370SDaniel Vetter 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
24426104c370SDaniel Vetter 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
24436104c370SDaniel Vetter 		cols /= w;
24446104c370SDaniel Vetter 		rows /= h;
2445a5a92303SShigeru Yoshida 		ret = vc_resize(vc, cols, rows);
2446a5a92303SShigeru Yoshida 		if (ret)
2447a5a92303SShigeru Yoshida 			goto err_out;
24486104c370SDaniel Vetter 	} else if (con_is_visible(vc)
24496104c370SDaniel Vetter 		   && vc->vc_mode == KD_TEXT) {
24506104c370SDaniel Vetter 		fbcon_clear_margins(vc, 0);
24516104c370SDaniel Vetter 		update_screen(vc);
24526104c370SDaniel Vetter 	}
24536104c370SDaniel Vetter 
245400d6a284SJiri Slaby (SUSE) 	if (old_userfont && (--REFCOUNT(old_data) == 0))
24556104c370SDaniel Vetter 		kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
24566104c370SDaniel Vetter 	return 0;
2457a5a92303SShigeru Yoshida 
2458a5a92303SShigeru Yoshida err_out:
2459a5a92303SShigeru Yoshida 	p->fontdata = old_data;
246000d6a284SJiri Slaby (SUSE) 	vc->vc_font.data = old_data;
2461a5a92303SShigeru Yoshida 
2462a5a92303SShigeru Yoshida 	if (userfont) {
2463a5a92303SShigeru Yoshida 		p->userfont = old_userfont;
24643c3bfb85STetsuo Handa 		if (--REFCOUNT(data) == 0)
24653c3bfb85STetsuo Handa 			kfree(data - FONT_EXTRA_WORDS * sizeof(int));
2466a5a92303SShigeru Yoshida 	}
2467a5a92303SShigeru Yoshida 
2468a5a92303SShigeru Yoshida 	vc->vc_font.width = old_width;
2469a5a92303SShigeru Yoshida 	vc->vc_font.height = old_height;
2470a5a92303SShigeru Yoshida 	vc->vc_font.charcount = old_charcount;
2471a5a92303SShigeru Yoshida 
2472a5a92303SShigeru Yoshida 	return ret;
24736104c370SDaniel Vetter }
24746104c370SDaniel Vetter 
24756104c370SDaniel Vetter /*
2476ffc1e089SSamuel Thibault  *  User asked to set font; we are guaranteed that charcount does not exceed 512
2477ffc1e089SSamuel Thibault  *  but lets not assume that, since charcount of 512 is small for unicode support.
24786104c370SDaniel Vetter  */
24796104c370SDaniel Vetter 
fbcon_set_font(struct vc_data * vc,const struct console_font * font,unsigned int vpitch,unsigned int flags)2480fd0f631fSJiri Slaby (SUSE) static int fbcon_set_font(struct vc_data *vc, const struct console_font *font,
2481ffc1e089SSamuel Thibault 			  unsigned int vpitch, unsigned int flags)
24826104c370SDaniel Vetter {
2483409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
24846104c370SDaniel Vetter 	unsigned charcount = font->charcount;
24856104c370SDaniel Vetter 	int w = font->width;
24866104c370SDaniel Vetter 	int h = font->height;
24876104c370SDaniel Vetter 	int size;
24886104c370SDaniel Vetter 	int i, csum;
24896104c370SDaniel Vetter 	u8 *new_data, *data = font->data;
249039b3cffbSGeorge Kennedy 	int pitch = PITCH(font->width);
24916104c370SDaniel Vetter 
24926104c370SDaniel Vetter 	/* Is there a reason why fbconsole couldn't handle any charcount >256?
24936104c370SDaniel Vetter 	 * If not this check should be changed to charcount < 256 */
24946104c370SDaniel Vetter 	if (charcount != 256 && charcount != 512)
24956104c370SDaniel Vetter 		return -EINVAL;
24966104c370SDaniel Vetter 
249765a01e60SHelge Deller 	/* font bigger than screen resolution ? */
249865a01e60SHelge Deller 	if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) ||
249965a01e60SHelge Deller 	    h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
250065a01e60SHelge Deller 		return -EINVAL;
250165a01e60SHelge Deller 
250215260979SSamuel Thibault 	if (font->width > FB_MAX_BLIT_WIDTH || font->height > FB_MAX_BLIT_HEIGHT)
25032b09d5d3SSamuel Thibault 		return -EINVAL;
25042b09d5d3SSamuel Thibault 
25056104c370SDaniel Vetter 	/* Make sure drawing engine can handle the font */
250615260979SSamuel Thibault 	if (!test_bit(font->width - 1, info->pixmap.blit_x) ||
250715260979SSamuel Thibault 	    !test_bit(font->height - 1, info->pixmap.blit_y))
25086104c370SDaniel Vetter 		return -EINVAL;
25096104c370SDaniel Vetter 
25106104c370SDaniel Vetter 	/* Make sure driver can handle the font length */
25116104c370SDaniel Vetter 	if (fbcon_invalid_charcount(info, charcount))
25126104c370SDaniel Vetter 		return -EINVAL;
25136104c370SDaniel Vetter 
251439b3cffbSGeorge Kennedy 	size = CALC_FONTSZ(h, pitch, charcount);
25156104c370SDaniel Vetter 
25166104c370SDaniel Vetter 	new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
25176104c370SDaniel Vetter 
25186104c370SDaniel Vetter 	if (!new_data)
25196104c370SDaniel Vetter 		return -ENOMEM;
25206104c370SDaniel Vetter 
2521a1ac250aSPeilin Ye 	memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int));
2522a1ac250aSPeilin Ye 
25236104c370SDaniel Vetter 	new_data += FONT_EXTRA_WORDS * sizeof(int);
25246104c370SDaniel Vetter 	FNTSIZE(new_data) = size;
25256104c370SDaniel Vetter 	REFCOUNT(new_data) = 0;	/* usage counter */
25266104c370SDaniel Vetter 	for (i=0; i< charcount; i++) {
2527ffc1e089SSamuel Thibault 		memcpy(new_data + i*h*pitch, data +  i*vpitch*pitch, h*pitch);
25286104c370SDaniel Vetter 	}
25296104c370SDaniel Vetter 
25306104c370SDaniel Vetter 	/* Since linux has a nice crc32 function use it for counting font
25316104c370SDaniel Vetter 	 * checksums. */
25326104c370SDaniel Vetter 	csum = crc32(0, new_data, size);
25336104c370SDaniel Vetter 
25346104c370SDaniel Vetter 	FNTSUM(new_data) = csum;
25356104c370SDaniel Vetter 	/* Check if the same font is on some other console already */
25366104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
25376104c370SDaniel Vetter 		struct vc_data *tmp = vc_cons[i].d;
25386104c370SDaniel Vetter 
25396104c370SDaniel Vetter 		if (fb_display[i].userfont &&
25406104c370SDaniel Vetter 		    fb_display[i].fontdata &&
25416104c370SDaniel Vetter 		    FNTSUM(fb_display[i].fontdata) == csum &&
25426104c370SDaniel Vetter 		    FNTSIZE(fb_display[i].fontdata) == size &&
25436104c370SDaniel Vetter 		    tmp->vc_font.width == w &&
25446104c370SDaniel Vetter 		    !memcmp(fb_display[i].fontdata, new_data, size)) {
25456104c370SDaniel Vetter 			kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
25466104c370SDaniel Vetter 			new_data = (u8 *)fb_display[i].fontdata;
25476104c370SDaniel Vetter 			break;
25486104c370SDaniel Vetter 		}
25496104c370SDaniel Vetter 	}
2550a1ac250aSPeilin Ye 	return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
25516104c370SDaniel Vetter }
25526104c370SDaniel Vetter 
fbcon_set_def_font(struct vc_data * vc,struct console_font * font,const char * name)25534f596170SJiri Slaby (SUSE) static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font,
25544f596170SJiri Slaby (SUSE) 			      const char *name)
25556104c370SDaniel Vetter {
2556409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
25576104c370SDaniel Vetter 	const struct font_desc *f;
25586104c370SDaniel Vetter 
25596104c370SDaniel Vetter 	if (!name)
25606104c370SDaniel Vetter 		f = get_default_font(info->var.xres, info->var.yres,
25616104c370SDaniel Vetter 				     info->pixmap.blit_x, info->pixmap.blit_y);
25626104c370SDaniel Vetter 	else if (!(f = find_font(name)))
25636104c370SDaniel Vetter 		return -ENOENT;
25646104c370SDaniel Vetter 
25656104c370SDaniel Vetter 	font->width = f->width;
25666104c370SDaniel Vetter 	font->height = f->height;
2567a1ac250aSPeilin Ye 	return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
25686104c370SDaniel Vetter }
25696104c370SDaniel Vetter 
25706104c370SDaniel Vetter static u16 palette_red[16];
25716104c370SDaniel Vetter static u16 palette_green[16];
25726104c370SDaniel Vetter static u16 palette_blue[16];
25736104c370SDaniel Vetter 
25746104c370SDaniel Vetter static struct fb_cmap palette_cmap = {
25756104c370SDaniel Vetter 	0, 16, palette_red, palette_green, palette_blue, NULL
25766104c370SDaniel Vetter };
25776104c370SDaniel Vetter 
fbcon_set_palette(struct vc_data * vc,const unsigned char * table)25786104c370SDaniel Vetter static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
25796104c370SDaniel Vetter {
2580409d6c95SDaniel Vetter 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
25816104c370SDaniel Vetter 	int i, j, k, depth;
25826104c370SDaniel Vetter 	u8 val;
25836104c370SDaniel Vetter 
25846104c370SDaniel Vetter 	if (fbcon_is_inactive(vc, info))
25856104c370SDaniel Vetter 		return;
25866104c370SDaniel Vetter 
25876104c370SDaniel Vetter 	if (!con_is_visible(vc))
25886104c370SDaniel Vetter 		return;
25896104c370SDaniel Vetter 
25906104c370SDaniel Vetter 	depth = fb_get_color_depth(&info->var, &info->fix);
25916104c370SDaniel Vetter 	if (depth > 3) {
25926104c370SDaniel Vetter 		for (i = j = 0; i < 16; i++) {
25936104c370SDaniel Vetter 			k = table[i];
25946104c370SDaniel Vetter 			val = vc->vc_palette[j++];
25956104c370SDaniel Vetter 			palette_red[k] = (val << 8) | val;
25966104c370SDaniel Vetter 			val = vc->vc_palette[j++];
25976104c370SDaniel Vetter 			palette_green[k] = (val << 8) | val;
25986104c370SDaniel Vetter 			val = vc->vc_palette[j++];
25996104c370SDaniel Vetter 			palette_blue[k] = (val << 8) | val;
26006104c370SDaniel Vetter 		}
26016104c370SDaniel Vetter 		palette_cmap.len = 16;
26026104c370SDaniel Vetter 		palette_cmap.start = 0;
26036104c370SDaniel Vetter 	/*
26046104c370SDaniel Vetter 	 * If framebuffer is capable of less than 16 colors,
26056104c370SDaniel Vetter 	 * use default palette of fbcon.
26066104c370SDaniel Vetter 	 */
26076104c370SDaniel Vetter 	} else
26086104c370SDaniel Vetter 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
26096104c370SDaniel Vetter 
26106104c370SDaniel Vetter 	fb_set_cmap(&palette_cmap, info);
26116104c370SDaniel Vetter }
26126104c370SDaniel Vetter 
26136104c370SDaniel Vetter /* As we might be inside of softback, we may work with non-contiguous buffer,
26146104c370SDaniel Vetter    that's why we have to use a separate routine. */
fbcon_invert_region(struct vc_data * vc,u16 * p,int cnt)26156104c370SDaniel Vetter static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
26166104c370SDaniel Vetter {
26176104c370SDaniel Vetter 	while (cnt--) {
26186104c370SDaniel Vetter 		u16 a = scr_readw(p);
26196104c370SDaniel Vetter 		if (!vc->vc_can_do_color)
26206104c370SDaniel Vetter 			a ^= 0x0800;
26216104c370SDaniel Vetter 		else if (vc->vc_hi_font_mask == 0x100)
26226104c370SDaniel Vetter 			a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
26236104c370SDaniel Vetter 			    (((a) & 0x0e00) << 4);
26246104c370SDaniel Vetter 		else
26256104c370SDaniel Vetter 			a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
26266104c370SDaniel Vetter 			    (((a) & 0x0700) << 4);
26276104c370SDaniel Vetter 		scr_writew(a, p++);
26286104c370SDaniel Vetter 	}
26296104c370SDaniel Vetter }
26306104c370SDaniel Vetter 
fbcon_suspended(struct fb_info * info)263150c50563SDaniel Vetter void fbcon_suspended(struct fb_info *info)
26326104c370SDaniel Vetter {
26336104c370SDaniel Vetter 	struct vc_data *vc = NULL;
26346104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
26356104c370SDaniel Vetter 
26366104c370SDaniel Vetter 	if (!ops || ops->currcon < 0)
26376104c370SDaniel Vetter 		return;
26386104c370SDaniel Vetter 	vc = vc_cons[ops->currcon].d;
26396104c370SDaniel Vetter 
26406104c370SDaniel Vetter 	/* Clear cursor, restore saved data */
2641a292e3fcSJiri Slaby (SUSE) 	fbcon_cursor(vc, false);
26426104c370SDaniel Vetter }
26436104c370SDaniel Vetter 
fbcon_resumed(struct fb_info * info)264450c50563SDaniel Vetter void fbcon_resumed(struct fb_info *info)
26456104c370SDaniel Vetter {
26466104c370SDaniel Vetter 	struct vc_data *vc;
26476104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
26486104c370SDaniel Vetter 
26496104c370SDaniel Vetter 	if (!ops || ops->currcon < 0)
26506104c370SDaniel Vetter 		return;
26516104c370SDaniel Vetter 	vc = vc_cons[ops->currcon].d;
26526104c370SDaniel Vetter 
26536104c370SDaniel Vetter 	update_screen(vc);
26546104c370SDaniel Vetter }
26556104c370SDaniel Vetter 
fbcon_modechanged(struct fb_info * info)26566104c370SDaniel Vetter static void fbcon_modechanged(struct fb_info *info)
26576104c370SDaniel Vetter {
26586104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
26596104c370SDaniel Vetter 	struct vc_data *vc;
266050233393SDaniel Vetter 	struct fbcon_display *p;
26616104c370SDaniel Vetter 	int rows, cols;
26626104c370SDaniel Vetter 
26636104c370SDaniel Vetter 	if (!ops || ops->currcon < 0)
26646104c370SDaniel Vetter 		return;
26656104c370SDaniel Vetter 	vc = vc_cons[ops->currcon].d;
26666104c370SDaniel Vetter 	if (vc->vc_mode != KD_TEXT ||
2667409d6c95SDaniel Vetter 	    fbcon_info_from_console(ops->currcon) != info)
26686104c370SDaniel Vetter 		return;
26696104c370SDaniel Vetter 
26706104c370SDaniel Vetter 	p = &fb_display[vc->vc_num];
26716104c370SDaniel Vetter 	set_blitting_type(vc, info);
26726104c370SDaniel Vetter 
26736104c370SDaniel Vetter 	if (con_is_visible(vc)) {
26746104c370SDaniel Vetter 		var_to_display(p, &info->var, info);
26756104c370SDaniel Vetter 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
26766104c370SDaniel Vetter 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
26776104c370SDaniel Vetter 		cols /= vc->vc_font.width;
26786104c370SDaniel Vetter 		rows /= vc->vc_font.height;
26796104c370SDaniel Vetter 		vc_resize(vc, cols, rows);
26806104c370SDaniel Vetter 		updatescrollmode(p, info, vc);
26816104c370SDaniel Vetter 		scrollback_max = 0;
26826104c370SDaniel Vetter 		scrollback_current = 0;
26836104c370SDaniel Vetter 
26846104c370SDaniel Vetter 		if (!fbcon_is_inactive(vc, info)) {
26856104c370SDaniel Vetter 		    ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
26866104c370SDaniel Vetter 		    ops->update_start(info);
26876104c370SDaniel Vetter 		}
26886104c370SDaniel Vetter 
26896104c370SDaniel Vetter 		fbcon_set_palette(vc, color_table);
26906104c370SDaniel Vetter 		update_screen(vc);
26916104c370SDaniel Vetter 	}
26926104c370SDaniel Vetter }
26936104c370SDaniel Vetter 
fbcon_set_all_vcs(struct fb_info * info)26946104c370SDaniel Vetter static void fbcon_set_all_vcs(struct fb_info *info)
26956104c370SDaniel Vetter {
26966104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
26976104c370SDaniel Vetter 	struct vc_data *vc;
269850233393SDaniel Vetter 	struct fbcon_display *p;
26996104c370SDaniel Vetter 	int i, rows, cols, fg = -1;
27006104c370SDaniel Vetter 
27016104c370SDaniel Vetter 	if (!ops || ops->currcon < 0)
27026104c370SDaniel Vetter 		return;
27036104c370SDaniel Vetter 
27046104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
27056104c370SDaniel Vetter 		vc = vc_cons[i].d;
27066104c370SDaniel Vetter 		if (!vc || vc->vc_mode != KD_TEXT ||
2707409d6c95SDaniel Vetter 		    fbcon_info_from_console(i) != info)
27086104c370SDaniel Vetter 			continue;
27096104c370SDaniel Vetter 
27106104c370SDaniel Vetter 		if (con_is_visible(vc)) {
27116104c370SDaniel Vetter 			fg = i;
27126104c370SDaniel Vetter 			continue;
27136104c370SDaniel Vetter 		}
27146104c370SDaniel Vetter 
27156104c370SDaniel Vetter 		p = &fb_display[vc->vc_num];
27166104c370SDaniel Vetter 		set_blitting_type(vc, info);
27176104c370SDaniel Vetter 		var_to_display(p, &info->var, info);
27186104c370SDaniel Vetter 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
27196104c370SDaniel Vetter 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
27206104c370SDaniel Vetter 		cols /= vc->vc_font.width;
27216104c370SDaniel Vetter 		rows /= vc->vc_font.height;
27226104c370SDaniel Vetter 		vc_resize(vc, cols, rows);
27236104c370SDaniel Vetter 	}
27246104c370SDaniel Vetter 
27256104c370SDaniel Vetter 	if (fg != -1)
27266104c370SDaniel Vetter 		fbcon_modechanged(info);
27276104c370SDaniel Vetter }
27286104c370SDaniel Vetter 
27299e146700SDaniel Vetter 
fbcon_update_vcs(struct fb_info * info,bool all)27309e146700SDaniel Vetter void fbcon_update_vcs(struct fb_info *info, bool all)
27319e146700SDaniel Vetter {
27329e146700SDaniel Vetter 	if (all)
27339e146700SDaniel Vetter 		fbcon_set_all_vcs(info);
27349e146700SDaniel Vetter 	else
27359e146700SDaniel Vetter 		fbcon_modechanged(info);
27369e146700SDaniel Vetter }
273724430914SDaniel Vetter EXPORT_SYMBOL(fbcon_update_vcs);
27389e146700SDaniel Vetter 
2739e64242caSHelge Deller /* let fbcon check if it supports a new screen resolution */
fbcon_modechange_possible(struct fb_info * info,struct fb_var_screeninfo * var)2740e64242caSHelge Deller int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
2741e64242caSHelge Deller {
2742e64242caSHelge Deller 	struct fbcon_ops *ops = info->fbcon_par;
2743e64242caSHelge Deller 	struct vc_data *vc;
2744e64242caSHelge Deller 	unsigned int i;
2745e64242caSHelge Deller 
2746e64242caSHelge Deller 	WARN_CONSOLE_UNLOCKED();
2747e64242caSHelge Deller 
2748e64242caSHelge Deller 	if (!ops)
2749e64242caSHelge Deller 		return 0;
2750e64242caSHelge Deller 
2751e64242caSHelge Deller 	/* prevent setting a screen size which is smaller than font size */
2752e64242caSHelge Deller 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
2753e64242caSHelge Deller 		vc = vc_cons[i].d;
2754e64242caSHelge Deller 		if (!vc || vc->vc_mode != KD_TEXT ||
275553a6e66bSHelge Deller 			   fbcon_info_from_console(i) != info)
2756e64242caSHelge Deller 			continue;
2757e64242caSHelge Deller 
2758e64242caSHelge Deller 		if (vc->vc_font.width  > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
2759e64242caSHelge Deller 		    vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
2760e64242caSHelge Deller 			return -EINVAL;
2761e64242caSHelge Deller 	}
2762e64242caSHelge Deller 
2763e64242caSHelge Deller 	return 0;
2764e64242caSHelge Deller }
2765e64242caSHelge Deller EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
2766e64242caSHelge Deller 
fbcon_mode_deleted(struct fb_info * info,struct fb_videomode * mode)276713ff178cSDaniel Vetter int fbcon_mode_deleted(struct fb_info *info,
27686104c370SDaniel Vetter 		       struct fb_videomode *mode)
27696104c370SDaniel Vetter {
27706104c370SDaniel Vetter 	struct fb_info *fb_info;
277150233393SDaniel Vetter 	struct fbcon_display *p;
27726104c370SDaniel Vetter 	int i, j, found = 0;
27736104c370SDaniel Vetter 
27746104c370SDaniel Vetter 	/* before deletion, ensure that mode is not in use */
27756104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
27766104c370SDaniel Vetter 		j = con2fb_map[i];
27776104c370SDaniel Vetter 		if (j == -1)
27786104c370SDaniel Vetter 			continue;
2779efc3acbcSDaniel Vetter 		fb_info = fbcon_registered_fb[j];
27806104c370SDaniel Vetter 		if (fb_info != info)
27816104c370SDaniel Vetter 			continue;
27826104c370SDaniel Vetter 		p = &fb_display[i];
27836104c370SDaniel Vetter 		if (!p || !p->mode)
27846104c370SDaniel Vetter 			continue;
27856104c370SDaniel Vetter 		if (fb_mode_is_equal(p->mode, mode)) {
27866104c370SDaniel Vetter 			found = 1;
27876104c370SDaniel Vetter 			break;
27886104c370SDaniel Vetter 		}
27896104c370SDaniel Vetter 	}
27906104c370SDaniel Vetter 	return found;
27916104c370SDaniel Vetter }
27926104c370SDaniel Vetter 
27936104c370SDaniel Vetter #ifdef CONFIG_VT_HW_CONSOLE_BINDING
fbcon_unbind(void)27940e0f3250SDaniel Vetter static void fbcon_unbind(void)
27956104c370SDaniel Vetter {
27966104c370SDaniel Vetter 	int ret;
27976104c370SDaniel Vetter 
27986104c370SDaniel Vetter 	ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
27996104c370SDaniel Vetter 				fbcon_is_default);
28006104c370SDaniel Vetter 
28016104c370SDaniel Vetter 	if (!ret)
28026104c370SDaniel Vetter 		fbcon_has_console_bind = 0;
28036104c370SDaniel Vetter }
28046104c370SDaniel Vetter #else
fbcon_unbind(void)28050e0f3250SDaniel Vetter static inline void fbcon_unbind(void) {}
28066104c370SDaniel Vetter #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
28076104c370SDaniel Vetter 
fbcon_fb_unbind(struct fb_info * info)28080e0f3250SDaniel Vetter void fbcon_fb_unbind(struct fb_info *info)
28096104c370SDaniel Vetter {
2810b07db395SDaniel Vetter 	int i, new_idx = -1;
28110e0f3250SDaniel Vetter 	int idx = info->node;
28126104c370SDaniel Vetter 
28136e7da3afSDaniel Vetter 	console_lock();
28143bd3a0e3SHans de Goede 
28156e7da3afSDaniel Vetter 	if (!fbcon_has_console_bind) {
28166e7da3afSDaniel Vetter 		console_unlock();
28170e0f3250SDaniel Vetter 		return;
28186e7da3afSDaniel Vetter 	}
28196104c370SDaniel Vetter 
28206104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
28216104c370SDaniel Vetter 		if (con2fb_map[i] != idx &&
28226104c370SDaniel Vetter 		    con2fb_map[i] != -1) {
28232122b405SNoralf Trønnes 			new_idx = con2fb_map[i];
28246104c370SDaniel Vetter 			break;
28256104c370SDaniel Vetter 		}
28266104c370SDaniel Vetter 	}
28276104c370SDaniel Vetter 
28286104c370SDaniel Vetter 	if (new_idx != -1) {
28296104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
28306104c370SDaniel Vetter 			if (con2fb_map[i] == idx)
28316104c370SDaniel Vetter 				set_con2fb_map(i, new_idx, 0);
28326104c370SDaniel Vetter 		}
28336104c370SDaniel Vetter 	} else {
2834efc3acbcSDaniel Vetter 		struct fb_info *info = fbcon_registered_fb[idx];
28356104c370SDaniel Vetter 
28366104c370SDaniel Vetter 		/* This is sort of like set_con2fb_map, except it maps
28376104c370SDaniel Vetter 		 * the consoles to no device and then releases the
28386104c370SDaniel Vetter 		 * oldinfo to free memory and cancel the cursor blink
28396104c370SDaniel Vetter 		 * timer. I can imagine this just becoming part of
28406104c370SDaniel Vetter 		 * set_con2fb_map where new_idx is -1
28416104c370SDaniel Vetter 		 */
28426104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
28436104c370SDaniel Vetter 			if (con2fb_map[i] == idx) {
28446104c370SDaniel Vetter 				con2fb_map[i] = -1;
28456104c370SDaniel Vetter 				if (!search_fb_in_map(idx)) {
2846b07db395SDaniel Vetter 					con2fb_release_oldinfo(vc_cons[i].d,
2847b07db395SDaniel Vetter 							       info, NULL);
28486104c370SDaniel Vetter 				}
28496104c370SDaniel Vetter 			}
28506104c370SDaniel Vetter 		}
28510e0f3250SDaniel Vetter 		fbcon_unbind();
28526104c370SDaniel Vetter 	}
28536e7da3afSDaniel Vetter 
28546e7da3afSDaniel Vetter 	console_unlock();
28556104c370SDaniel Vetter }
28566104c370SDaniel Vetter 
fbcon_fb_unregistered(struct fb_info * info)285797b67986SDaniel Vetter void fbcon_fb_unregistered(struct fb_info *info)
28586104c370SDaniel Vetter {
28596104c370SDaniel Vetter 	int i, idx;
28606104c370SDaniel Vetter 
28616e7da3afSDaniel Vetter 	console_lock();
28623bd3a0e3SHans de Goede 
2863efc3acbcSDaniel Vetter 	fbcon_registered_fb[info->node] = NULL;
2864efc3acbcSDaniel Vetter 	fbcon_num_registered_fb--;
2865efc3acbcSDaniel Vetter 
28666e7da3afSDaniel Vetter 	if (deferred_takeover) {
28676e7da3afSDaniel Vetter 		console_unlock();
286897b67986SDaniel Vetter 		return;
28696e7da3afSDaniel Vetter 	}
287083d83bebSHans de Goede 
28716104c370SDaniel Vetter 	idx = info->node;
28726104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
28736104c370SDaniel Vetter 		if (con2fb_map[i] == idx)
28746104c370SDaniel Vetter 			con2fb_map[i] = -1;
28756104c370SDaniel Vetter 	}
28766104c370SDaniel Vetter 
28776104c370SDaniel Vetter 	if (idx == info_idx) {
28786104c370SDaniel Vetter 		info_idx = -1;
28796104c370SDaniel Vetter 
2880efc3acbcSDaniel Vetter 		fbcon_for_each_registered_fb(i) {
28816104c370SDaniel Vetter 			info_idx = i;
28826104c370SDaniel Vetter 			break;
28836104c370SDaniel Vetter 		}
28846104c370SDaniel Vetter 	}
28856104c370SDaniel Vetter 
28866104c370SDaniel Vetter 	if (info_idx != -1) {
28876104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
28886104c370SDaniel Vetter 			if (con2fb_map[i] == -1)
28896104c370SDaniel Vetter 				con2fb_map[i] = info_idx;
28906104c370SDaniel Vetter 		}
28916104c370SDaniel Vetter 	}
28926104c370SDaniel Vetter 
28936104c370SDaniel Vetter 	if (primary_device == idx)
28946104c370SDaniel Vetter 		primary_device = -1;
28956104c370SDaniel Vetter 
2896efc3acbcSDaniel Vetter 	if (!fbcon_num_registered_fb)
28976104c370SDaniel Vetter 		do_unregister_con_driver(&fb_con);
28986e7da3afSDaniel Vetter 	console_unlock();
28996104c370SDaniel Vetter }
29006104c370SDaniel Vetter 
fbcon_remap_all(struct fb_info * info)29011cd51b5dSDaniel Vetter void fbcon_remap_all(struct fb_info *info)
29026104c370SDaniel Vetter {
29031cd51b5dSDaniel Vetter 	int i, idx = info->node;
29043bd3a0e3SHans de Goede 
29051cd51b5dSDaniel Vetter 	console_lock();
290683d83bebSHans de Goede 	if (deferred_takeover) {
290783d83bebSHans de Goede 		for (i = first_fb_vc; i <= last_fb_vc; i++)
290883d83bebSHans de Goede 			con2fb_map_boot[i] = idx;
290983d83bebSHans de Goede 		fbcon_map_override();
29101cd51b5dSDaniel Vetter 		console_unlock();
291183d83bebSHans de Goede 		return;
291283d83bebSHans de Goede 	}
291383d83bebSHans de Goede 
29146104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++)
29156104c370SDaniel Vetter 		set_con2fb_map(i, idx, 0);
29166104c370SDaniel Vetter 
29176104c370SDaniel Vetter 	if (con_is_bound(&fb_con)) {
29186104c370SDaniel Vetter 		printk(KERN_INFO "fbcon: Remapping primary device, "
29196104c370SDaniel Vetter 		       "fb%i, to tty %i-%i\n", idx,
29206104c370SDaniel Vetter 		       first_fb_vc + 1, last_fb_vc + 1);
29216104c370SDaniel Vetter 		info_idx = idx;
29226104c370SDaniel Vetter 	}
29231cd51b5dSDaniel Vetter 	console_unlock();
29246104c370SDaniel Vetter }
29256104c370SDaniel Vetter 
29266104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
fbcon_select_primary(struct fb_info * info)29276104c370SDaniel Vetter static void fbcon_select_primary(struct fb_info *info)
29286104c370SDaniel Vetter {
29296104c370SDaniel Vetter 	if (!map_override && primary_device == -1 &&
2930f178e96dSThomas Zimmermann 	    video_is_primary_device(info->device)) {
29316104c370SDaniel Vetter 		int i;
29326104c370SDaniel Vetter 
29336104c370SDaniel Vetter 		printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
29346104c370SDaniel Vetter 		       info->fix.id, info->node);
29356104c370SDaniel Vetter 		primary_device = info->node;
29366104c370SDaniel Vetter 
29376104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++)
29386104c370SDaniel Vetter 			con2fb_map_boot[i] = primary_device;
29396104c370SDaniel Vetter 
29406104c370SDaniel Vetter 		if (con_is_bound(&fb_con)) {
29416104c370SDaniel Vetter 			printk(KERN_INFO "fbcon: Remapping primary device, "
29426104c370SDaniel Vetter 			       "fb%i, to tty %i-%i\n", info->node,
29436104c370SDaniel Vetter 			       first_fb_vc + 1, last_fb_vc + 1);
29446104c370SDaniel Vetter 			info_idx = primary_device;
29456104c370SDaniel Vetter 		}
29466104c370SDaniel Vetter 	}
29476104c370SDaniel Vetter 
29486104c370SDaniel Vetter }
29496104c370SDaniel Vetter #else
fbcon_select_primary(struct fb_info * info)29506104c370SDaniel Vetter static inline void fbcon_select_primary(struct fb_info *info)
29516104c370SDaniel Vetter {
29526104c370SDaniel Vetter 	return;
29536104c370SDaniel Vetter }
29546104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
29556104c370SDaniel Vetter 
29566e7da3afSDaniel Vetter static bool lockless_register_fb;
29576e7da3afSDaniel Vetter module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
29586e7da3afSDaniel Vetter MODULE_PARM_DESC(lockless_register_fb,
29596e7da3afSDaniel Vetter 	"Lockless framebuffer registration for debugging [default=off]");
29606e7da3afSDaniel Vetter 
29616104c370SDaniel Vetter /* called with console_lock held */
do_fb_registered(struct fb_info * info)29629d797991SDaniel Vetter static int do_fb_registered(struct fb_info *info)
29636104c370SDaniel Vetter {
29646104c370SDaniel Vetter 	int ret = 0, i, idx;
29656104c370SDaniel Vetter 
29669d797991SDaniel Vetter 	WARN_CONSOLE_UNLOCKED();
29673bd3a0e3SHans de Goede 
2968efc3acbcSDaniel Vetter 	fbcon_registered_fb[info->node] = info;
2969efc3acbcSDaniel Vetter 	fbcon_num_registered_fb++;
2970efc3acbcSDaniel Vetter 
29716104c370SDaniel Vetter 	idx = info->node;
29726104c370SDaniel Vetter 	fbcon_select_primary(info);
29736104c370SDaniel Vetter 
297483d83bebSHans de Goede 	if (deferred_takeover) {
297583d83bebSHans de Goede 		pr_info("fbcon: Deferring console take-over\n");
29769d797991SDaniel Vetter 		return 0;
297783d83bebSHans de Goede 	}
297883d83bebSHans de Goede 
29796104c370SDaniel Vetter 	if (info_idx == -1) {
29806104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
29816104c370SDaniel Vetter 			if (con2fb_map_boot[i] == idx) {
29826104c370SDaniel Vetter 				info_idx = idx;
29836104c370SDaniel Vetter 				break;
29846104c370SDaniel Vetter 			}
29856104c370SDaniel Vetter 		}
29866104c370SDaniel Vetter 
29876104c370SDaniel Vetter 		if (info_idx != -1)
29886104c370SDaniel Vetter 			ret = do_fbcon_takeover(1);
29896104c370SDaniel Vetter 	} else {
29906104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
29916104c370SDaniel Vetter 			if (con2fb_map_boot[i] == idx)
29926104c370SDaniel Vetter 				set_con2fb_map(i, idx, 0);
29936104c370SDaniel Vetter 		}
29946104c370SDaniel Vetter 	}
29956104c370SDaniel Vetter 
29969d797991SDaniel Vetter 	return ret;
29979d797991SDaniel Vetter }
29989d797991SDaniel Vetter 
fbcon_fb_registered(struct fb_info * info)29999d797991SDaniel Vetter int fbcon_fb_registered(struct fb_info *info)
30009d797991SDaniel Vetter {
30019d797991SDaniel Vetter 	int ret;
30029d797991SDaniel Vetter 
30039d797991SDaniel Vetter 	if (!lockless_register_fb)
30049d797991SDaniel Vetter 		console_lock();
30059d797991SDaniel Vetter 	else
30069d797991SDaniel Vetter 		atomic_inc(&ignore_console_lock_warning);
30079d797991SDaniel Vetter 
30089d797991SDaniel Vetter 	ret = do_fb_registered(info);
30099d797991SDaniel Vetter 
30106e7da3afSDaniel Vetter 	if (!lockless_register_fb)
30116e7da3afSDaniel Vetter 		console_unlock();
30126e7da3afSDaniel Vetter 	else
30136e7da3afSDaniel Vetter 		atomic_dec(&ignore_console_lock_warning);
30146e7da3afSDaniel Vetter 
30156104c370SDaniel Vetter 	return ret;
30166104c370SDaniel Vetter }
30176104c370SDaniel Vetter 
fbcon_fb_blanked(struct fb_info * info,int blank)30187a625549SDaniel Vetter void fbcon_fb_blanked(struct fb_info *info, int blank)
30196104c370SDaniel Vetter {
30206104c370SDaniel Vetter 	struct fbcon_ops *ops = info->fbcon_par;
30216104c370SDaniel Vetter 	struct vc_data *vc;
30226104c370SDaniel Vetter 
30236104c370SDaniel Vetter 	if (!ops || ops->currcon < 0)
30246104c370SDaniel Vetter 		return;
30256104c370SDaniel Vetter 
30266104c370SDaniel Vetter 	vc = vc_cons[ops->currcon].d;
30276104c370SDaniel Vetter 	if (vc->vc_mode != KD_TEXT ||
3028409d6c95SDaniel Vetter 			fbcon_info_from_console(ops->currcon) != info)
30296104c370SDaniel Vetter 		return;
30306104c370SDaniel Vetter 
30316104c370SDaniel Vetter 	if (con_is_visible(vc)) {
30326104c370SDaniel Vetter 		if (blank)
30336104c370SDaniel Vetter 			do_blank_screen(0);
30346104c370SDaniel Vetter 		else
30356104c370SDaniel Vetter 			do_unblank_screen(0);
30366104c370SDaniel Vetter 	}
30376104c370SDaniel Vetter 	ops->blank_state = blank;
30386104c370SDaniel Vetter }
30396104c370SDaniel Vetter 
fbcon_new_modelist(struct fb_info * info)304013ff178cSDaniel Vetter void fbcon_new_modelist(struct fb_info *info)
30416104c370SDaniel Vetter {
30426104c370SDaniel Vetter 	int i;
30436104c370SDaniel Vetter 	struct vc_data *vc;
30446104c370SDaniel Vetter 	struct fb_var_screeninfo var;
30456104c370SDaniel Vetter 	const struct fb_videomode *mode;
30466104c370SDaniel Vetter 
30476104c370SDaniel Vetter 	for (i = first_fb_vc; i <= last_fb_vc; i++) {
3048409d6c95SDaniel Vetter 		if (fbcon_info_from_console(i) != info)
30496104c370SDaniel Vetter 			continue;
30506104c370SDaniel Vetter 		if (!fb_display[i].mode)
30516104c370SDaniel Vetter 			continue;
30526104c370SDaniel Vetter 		vc = vc_cons[i].d;
30536104c370SDaniel Vetter 		display_to_var(&var, &fb_display[i]);
30546104c370SDaniel Vetter 		mode = fb_find_nearest_mode(fb_display[i].mode,
30556104c370SDaniel Vetter 					    &info->modelist);
30566104c370SDaniel Vetter 		fb_videomode_to_var(&var, mode);
30576104c370SDaniel Vetter 		fbcon_set_disp(info, &var, vc->vc_num);
30586104c370SDaniel Vetter 	}
30596104c370SDaniel Vetter }
30606104c370SDaniel Vetter 
fbcon_get_requirement(struct fb_info * info,struct fb_blit_caps * caps)30610526c223SDaniel Vetter void fbcon_get_requirement(struct fb_info *info,
30626104c370SDaniel Vetter 			   struct fb_blit_caps *caps)
30636104c370SDaniel Vetter {
30646104c370SDaniel Vetter 	struct vc_data *vc;
30656104c370SDaniel Vetter 
30666104c370SDaniel Vetter 	if (caps->flags) {
30676104c370SDaniel Vetter 		int i, charcnt;
30686104c370SDaniel Vetter 
30696104c370SDaniel Vetter 		for (i = first_fb_vc; i <= last_fb_vc; i++) {
30706104c370SDaniel Vetter 			vc = vc_cons[i].d;
30716104c370SDaniel Vetter 			if (vc && vc->vc_mode == KD_TEXT &&
30726104c370SDaniel Vetter 			    info->node == con2fb_map[i]) {
307315260979SSamuel Thibault 				set_bit(vc->vc_font.width - 1, caps->x);
307415260979SSamuel Thibault 				set_bit(vc->vc_font.height - 1, caps->y);
3075a1ac250aSPeilin Ye 				charcnt = vc->vc_font.charcount;
30766104c370SDaniel Vetter 				if (caps->len < charcnt)
30776104c370SDaniel Vetter 					caps->len = charcnt;
30786104c370SDaniel Vetter 			}
30796104c370SDaniel Vetter 		}
30806104c370SDaniel Vetter 	} else {
30816104c370SDaniel Vetter 		vc = vc_cons[fg_console].d;
30826104c370SDaniel Vetter 
30836104c370SDaniel Vetter 		if (vc && vc->vc_mode == KD_TEXT &&
30846104c370SDaniel Vetter 		    info->node == con2fb_map[fg_console]) {
308515260979SSamuel Thibault 			bitmap_zero(caps->x, FB_MAX_BLIT_WIDTH);
308615260979SSamuel Thibault 			set_bit(vc->vc_font.width - 1, caps->x);
308715260979SSamuel Thibault 			bitmap_zero(caps->y, FB_MAX_BLIT_HEIGHT);
308815260979SSamuel Thibault 			set_bit(vc->vc_font.height - 1, caps->y);
3089a1ac250aSPeilin Ye 			caps->len = vc->vc_font.charcount;
30906104c370SDaniel Vetter 		}
30916104c370SDaniel Vetter 	}
30926104c370SDaniel Vetter }
30936104c370SDaniel Vetter 
fbcon_set_con2fb_map_ioctl(void __user * argp)3094fe2d70d6SDaniel Vetter int fbcon_set_con2fb_map_ioctl(void __user *argp)
30956104c370SDaniel Vetter {
3096fe2d70d6SDaniel Vetter 	struct fb_con2fbmap con2fb;
3097fe2d70d6SDaniel Vetter 	int ret;
30986104c370SDaniel Vetter 
3099fe2d70d6SDaniel Vetter 	if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3100fe2d70d6SDaniel Vetter 		return -EFAULT;
3101fe2d70d6SDaniel Vetter 	if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3102fe2d70d6SDaniel Vetter 		return -EINVAL;
3103fe2d70d6SDaniel Vetter 	if (con2fb.framebuffer >= FB_MAX)
3104fe2d70d6SDaniel Vetter 		return -EINVAL;
3105efc3acbcSDaniel Vetter 	if (!fbcon_registered_fb[con2fb.framebuffer])
3106fe2d70d6SDaniel Vetter 		request_module("fb%d", con2fb.framebuffer);
3107efc3acbcSDaniel Vetter 	if (!fbcon_registered_fb[con2fb.framebuffer]) {
3108fe2d70d6SDaniel Vetter 		return -EINVAL;
31096104c370SDaniel Vetter 	}
3110fe2d70d6SDaniel Vetter 
3111fe2d70d6SDaniel Vetter 	console_lock();
3112fe2d70d6SDaniel Vetter 	ret = set_con2fb_map(con2fb.console - 1,
3113fe2d70d6SDaniel Vetter 			     con2fb.framebuffer, 1);
3114fe2d70d6SDaniel Vetter 	console_unlock();
3115fe2d70d6SDaniel Vetter 
31166104c370SDaniel Vetter 	return ret;
31176104c370SDaniel Vetter }
31186104c370SDaniel Vetter 
fbcon_get_con2fb_map_ioctl(void __user * argp)3119fe2d70d6SDaniel Vetter int fbcon_get_con2fb_map_ioctl(void __user *argp)
3120fe2d70d6SDaniel Vetter {
3121fe2d70d6SDaniel Vetter 	struct fb_con2fbmap con2fb;
3122fe2d70d6SDaniel Vetter 
3123fe2d70d6SDaniel Vetter 	if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3124fe2d70d6SDaniel Vetter 		return -EFAULT;
3125fe2d70d6SDaniel Vetter 	if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3126fe2d70d6SDaniel Vetter 		return -EINVAL;
3127fe2d70d6SDaniel Vetter 
3128fe2d70d6SDaniel Vetter 	console_lock();
3129fe2d70d6SDaniel Vetter 	con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3130fe2d70d6SDaniel Vetter 	console_unlock();
3131fe2d70d6SDaniel Vetter 
3132fe2d70d6SDaniel Vetter 	return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3133fe2d70d6SDaniel Vetter }
3134fe2d70d6SDaniel Vetter 
31356104c370SDaniel Vetter /*
31366104c370SDaniel Vetter  *  The console `switch' structure for the frame buffer based console
31376104c370SDaniel Vetter  */
31386104c370SDaniel Vetter 
31396104c370SDaniel Vetter static const struct consw fb_con = {
31406104c370SDaniel Vetter 	.owner			= THIS_MODULE,
31416104c370SDaniel Vetter 	.con_startup 		= fbcon_startup,
31426104c370SDaniel Vetter 	.con_init 		= fbcon_init,
31436104c370SDaniel Vetter 	.con_deinit 		= fbcon_deinit,
31446104c370SDaniel Vetter 	.con_clear 		= fbcon_clear,
31456104c370SDaniel Vetter 	.con_putcs 		= fbcon_putcs,
31466104c370SDaniel Vetter 	.con_cursor 		= fbcon_cursor,
31476104c370SDaniel Vetter 	.con_scroll 		= fbcon_scroll,
31486104c370SDaniel Vetter 	.con_switch 		= fbcon_switch,
31496104c370SDaniel Vetter 	.con_blank 		= fbcon_blank,
31506104c370SDaniel Vetter 	.con_font_set 		= fbcon_set_font,
31516104c370SDaniel Vetter 	.con_font_get 		= fbcon_get_font,
31526104c370SDaniel Vetter 	.con_font_default	= fbcon_set_def_font,
31536104c370SDaniel Vetter 	.con_set_palette 	= fbcon_set_palette,
31546104c370SDaniel Vetter 	.con_invert_region 	= fbcon_invert_region,
31556104c370SDaniel Vetter 	.con_resize             = fbcon_resize,
31566104c370SDaniel Vetter 	.con_debug_enter	= fbcon_debug_enter,
31576104c370SDaniel Vetter 	.con_debug_leave	= fbcon_debug_leave,
31586104c370SDaniel Vetter };
31596104c370SDaniel Vetter 
store_rotate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)31606104c370SDaniel Vetter static ssize_t store_rotate(struct device *device,
31616104c370SDaniel Vetter 			    struct device_attribute *attr, const char *buf,
31626104c370SDaniel Vetter 			    size_t count)
31636104c370SDaniel Vetter {
31646104c370SDaniel Vetter 	struct fb_info *info;
31656104c370SDaniel Vetter 	int rotate, idx;
31666104c370SDaniel Vetter 	char **last = NULL;
31676104c370SDaniel Vetter 
31686104c370SDaniel Vetter 	console_lock();
31696104c370SDaniel Vetter 	idx = con2fb_map[fg_console];
31706104c370SDaniel Vetter 
3171efc3acbcSDaniel Vetter 	if (idx == -1 || fbcon_registered_fb[idx] == NULL)
31726104c370SDaniel Vetter 		goto err;
31736104c370SDaniel Vetter 
3174efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
31756104c370SDaniel Vetter 	rotate = simple_strtoul(buf, last, 0);
31766104c370SDaniel Vetter 	fbcon_rotate(info, rotate);
31776104c370SDaniel Vetter err:
31786104c370SDaniel Vetter 	console_unlock();
31796104c370SDaniel Vetter 	return count;
31806104c370SDaniel Vetter }
31816104c370SDaniel Vetter 
store_rotate_all(struct device * device,struct device_attribute * attr,const char * buf,size_t count)31826104c370SDaniel Vetter static ssize_t store_rotate_all(struct device *device,
31836104c370SDaniel Vetter 				struct device_attribute *attr,const char *buf,
31846104c370SDaniel Vetter 				size_t count)
31856104c370SDaniel Vetter {
31866104c370SDaniel Vetter 	struct fb_info *info;
31876104c370SDaniel Vetter 	int rotate, idx;
31886104c370SDaniel Vetter 	char **last = NULL;
31896104c370SDaniel Vetter 
31906104c370SDaniel Vetter 	console_lock();
31916104c370SDaniel Vetter 	idx = con2fb_map[fg_console];
31926104c370SDaniel Vetter 
3193efc3acbcSDaniel Vetter 	if (idx == -1 || fbcon_registered_fb[idx] == NULL)
31946104c370SDaniel Vetter 		goto err;
31956104c370SDaniel Vetter 
3196efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
31976104c370SDaniel Vetter 	rotate = simple_strtoul(buf, last, 0);
31986104c370SDaniel Vetter 	fbcon_rotate_all(info, rotate);
31996104c370SDaniel Vetter err:
32006104c370SDaniel Vetter 	console_unlock();
32016104c370SDaniel Vetter 	return count;
32026104c370SDaniel Vetter }
32036104c370SDaniel Vetter 
show_rotate(struct device * device,struct device_attribute * attr,char * buf)32046104c370SDaniel Vetter static ssize_t show_rotate(struct device *device,
32056104c370SDaniel Vetter 			   struct device_attribute *attr,char *buf)
32066104c370SDaniel Vetter {
32076104c370SDaniel Vetter 	struct fb_info *info;
32086104c370SDaniel Vetter 	int rotate = 0, idx;
32096104c370SDaniel Vetter 
32106104c370SDaniel Vetter 	console_lock();
32116104c370SDaniel Vetter 	idx = con2fb_map[fg_console];
32126104c370SDaniel Vetter 
3213efc3acbcSDaniel Vetter 	if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32146104c370SDaniel Vetter 		goto err;
32156104c370SDaniel Vetter 
3216efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
32176104c370SDaniel Vetter 	rotate = fbcon_get_rotate(info);
32186104c370SDaniel Vetter err:
32196104c370SDaniel Vetter 	console_unlock();
322016a54d4eSYang Guang 	return sysfs_emit(buf, "%d\n", rotate);
32216104c370SDaniel Vetter }
32226104c370SDaniel Vetter 
show_cursor_blink(struct device * device,struct device_attribute * attr,char * buf)32236104c370SDaniel Vetter static ssize_t show_cursor_blink(struct device *device,
32246104c370SDaniel Vetter 				 struct device_attribute *attr, char *buf)
32256104c370SDaniel Vetter {
32266104c370SDaniel Vetter 	struct fb_info *info;
32276104c370SDaniel Vetter 	struct fbcon_ops *ops;
32286104c370SDaniel Vetter 	int idx, blink = -1;
32296104c370SDaniel Vetter 
32306104c370SDaniel Vetter 	console_lock();
32316104c370SDaniel Vetter 	idx = con2fb_map[fg_console];
32326104c370SDaniel Vetter 
3233efc3acbcSDaniel Vetter 	if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32346104c370SDaniel Vetter 		goto err;
32356104c370SDaniel Vetter 
3236efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
32376104c370SDaniel Vetter 	ops = info->fbcon_par;
32386104c370SDaniel Vetter 
32396104c370SDaniel Vetter 	if (!ops)
32406104c370SDaniel Vetter 		goto err;
32416104c370SDaniel Vetter 
32423b0fb6abSDaniel Vetter 	blink = delayed_work_pending(&ops->cursor_work);
32436104c370SDaniel Vetter err:
32446104c370SDaniel Vetter 	console_unlock();
324516a54d4eSYang Guang 	return sysfs_emit(buf, "%d\n", blink);
32466104c370SDaniel Vetter }
32476104c370SDaniel Vetter 
store_cursor_blink(struct device * device,struct device_attribute * attr,const char * buf,size_t count)32486104c370SDaniel Vetter static ssize_t store_cursor_blink(struct device *device,
32496104c370SDaniel Vetter 				  struct device_attribute *attr,
32506104c370SDaniel Vetter 				  const char *buf, size_t count)
32516104c370SDaniel Vetter {
32526104c370SDaniel Vetter 	struct fb_info *info;
32536104c370SDaniel Vetter 	int blink, idx;
32546104c370SDaniel Vetter 	char **last = NULL;
32556104c370SDaniel Vetter 
32566104c370SDaniel Vetter 	console_lock();
32576104c370SDaniel Vetter 	idx = con2fb_map[fg_console];
32586104c370SDaniel Vetter 
3259efc3acbcSDaniel Vetter 	if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32606104c370SDaniel Vetter 		goto err;
32616104c370SDaniel Vetter 
3262efc3acbcSDaniel Vetter 	info = fbcon_registered_fb[idx];
32636104c370SDaniel Vetter 
32646104c370SDaniel Vetter 	if (!info->fbcon_par)
32656104c370SDaniel Vetter 		goto err;
32666104c370SDaniel Vetter 
32676104c370SDaniel Vetter 	blink = simple_strtoul(buf, last, 0);
32686104c370SDaniel Vetter 
32696104c370SDaniel Vetter 	if (blink) {
32706104c370SDaniel Vetter 		fbcon_cursor_noblink = 0;
32713b0fb6abSDaniel Vetter 		fbcon_add_cursor_work(info);
32726104c370SDaniel Vetter 	} else {
32736104c370SDaniel Vetter 		fbcon_cursor_noblink = 1;
32743b0fb6abSDaniel Vetter 		fbcon_del_cursor_work(info);
32756104c370SDaniel Vetter 	}
32766104c370SDaniel Vetter 
32776104c370SDaniel Vetter err:
32786104c370SDaniel Vetter 	console_unlock();
32796104c370SDaniel Vetter 	return count;
32806104c370SDaniel Vetter }
32816104c370SDaniel Vetter 
32826104c370SDaniel Vetter static struct device_attribute device_attrs[] = {
32836104c370SDaniel Vetter 	__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
32846104c370SDaniel Vetter 	__ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
32856104c370SDaniel Vetter 	__ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
32866104c370SDaniel Vetter 	       store_cursor_blink),
32876104c370SDaniel Vetter };
32886104c370SDaniel Vetter 
fbcon_init_device(void)32896104c370SDaniel Vetter static int fbcon_init_device(void)
32906104c370SDaniel Vetter {
32916104c370SDaniel Vetter 	int i, error = 0;
32926104c370SDaniel Vetter 
32936104c370SDaniel Vetter 	fbcon_has_sysfs = 1;
32946104c370SDaniel Vetter 
32956104c370SDaniel Vetter 	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
32966104c370SDaniel Vetter 		error = device_create_file(fbcon_device, &device_attrs[i]);
32976104c370SDaniel Vetter 
32986104c370SDaniel Vetter 		if (error)
32996104c370SDaniel Vetter 			break;
33006104c370SDaniel Vetter 	}
33016104c370SDaniel Vetter 
33026104c370SDaniel Vetter 	if (error) {
33036104c370SDaniel Vetter 		while (--i >= 0)
33046104c370SDaniel Vetter 			device_remove_file(fbcon_device, &device_attrs[i]);
33056104c370SDaniel Vetter 
33066104c370SDaniel Vetter 		fbcon_has_sysfs = 0;
33076104c370SDaniel Vetter 	}
33086104c370SDaniel Vetter 
33096104c370SDaniel Vetter 	return 0;
33106104c370SDaniel Vetter }
33116104c370SDaniel Vetter 
331283d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
fbcon_register_existing_fbs(struct work_struct * work)3313df37e225SHans de Goede static void fbcon_register_existing_fbs(struct work_struct *work)
3314df37e225SHans de Goede {
3315df37e225SHans de Goede 	int i;
3316df37e225SHans de Goede 
3317df37e225SHans de Goede 	console_lock();
3318df37e225SHans de Goede 
331943553559SDaniel Vetter 	deferred_takeover = false;
332043553559SDaniel Vetter 	logo_shown = FBCON_LOGO_DONTSHOW;
332143553559SDaniel Vetter 
3322efc3acbcSDaniel Vetter 	fbcon_for_each_registered_fb(i)
33239d797991SDaniel Vetter 		do_fb_registered(fbcon_registered_fb[i]);
3324df37e225SHans de Goede 
3325df37e225SHans de Goede 	console_unlock();
3326df37e225SHans de Goede }
3327df37e225SHans de Goede 
332883d83bebSHans de Goede static struct notifier_block fbcon_output_nb;
3329df37e225SHans de Goede static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
333083d83bebSHans de Goede 
fbcon_output_notifier(struct notifier_block * nb,unsigned long action,void * data)333183d83bebSHans de Goede static int fbcon_output_notifier(struct notifier_block *nb,
333283d83bebSHans de Goede 				 unsigned long action, void *data)
333383d83bebSHans de Goede {
333483d83bebSHans de Goede 	WARN_CONSOLE_UNLOCKED();
333583d83bebSHans de Goede 
333683d83bebSHans de Goede 	pr_info("fbcon: Taking over console\n");
333783d83bebSHans de Goede 
333883d83bebSHans de Goede 	dummycon_unregister_output_notifier(&fbcon_output_nb);
333983d83bebSHans de Goede 
3340df37e225SHans de Goede 	/* We may get called in atomic context */
3341df37e225SHans de Goede 	schedule_work(&fbcon_deferred_takeover_work);
334283d83bebSHans de Goede 
334383d83bebSHans de Goede 	return NOTIFY_OK;
334483d83bebSHans de Goede }
334583d83bebSHans de Goede #endif
334683d83bebSHans de Goede 
fbcon_start(void)33476104c370SDaniel Vetter static void fbcon_start(void)
33486104c370SDaniel Vetter {
3349bedb38fcSHans de Goede 	WARN_CONSOLE_UNLOCKED();
3350bedb38fcSHans de Goede 
3351bedb38fcSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3352bedb38fcSHans de Goede 	if (conswitchp != &dummy_con)
3353bedb38fcSHans de Goede 		deferred_takeover = false;
3354bedb38fcSHans de Goede 
335583d83bebSHans de Goede 	if (deferred_takeover) {
3356bedb38fcSHans de Goede 		fbcon_output_nb.notifier_call = fbcon_output_notifier;
3357bedb38fcSHans de Goede 		dummycon_register_output_notifier(&fbcon_output_nb);
335883d83bebSHans de Goede 		return;
335983d83bebSHans de Goede 	}
3360bedb38fcSHans de Goede #endif
33616104c370SDaniel Vetter }
33626104c370SDaniel Vetter 
fb_console_init(void)33636104c370SDaniel Vetter void __init fb_console_init(void)
33646104c370SDaniel Vetter {
33656104c370SDaniel Vetter 	int i;
33666104c370SDaniel Vetter 
33676104c370SDaniel Vetter 	console_lock();
33686104c370SDaniel Vetter 	fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
33696104c370SDaniel Vetter 				     "fbcon");
33706104c370SDaniel Vetter 
33716104c370SDaniel Vetter 	if (IS_ERR(fbcon_device)) {
33726104c370SDaniel Vetter 		printk(KERN_WARNING "Unable to create device "
33736104c370SDaniel Vetter 		       "for fbcon; errno = %ld\n",
33746104c370SDaniel Vetter 		       PTR_ERR(fbcon_device));
33756104c370SDaniel Vetter 		fbcon_device = NULL;
33766104c370SDaniel Vetter 	} else
33776104c370SDaniel Vetter 		fbcon_init_device();
33786104c370SDaniel Vetter 
33796104c370SDaniel Vetter 	for (i = 0; i < MAX_NR_CONSOLES; i++)
33806104c370SDaniel Vetter 		con2fb_map[i] = -1;
33816104c370SDaniel Vetter 
33826104c370SDaniel Vetter 	fbcon_start();
3383bedb38fcSHans de Goede 	console_unlock();
33846104c370SDaniel Vetter }
33856104c370SDaniel Vetter 
33866104c370SDaniel Vetter #ifdef MODULE
33876104c370SDaniel Vetter 
fbcon_deinit_device(void)33886104c370SDaniel Vetter static void __exit fbcon_deinit_device(void)
33896104c370SDaniel Vetter {
33906104c370SDaniel Vetter 	int i;
33916104c370SDaniel Vetter 
33926104c370SDaniel Vetter 	if (fbcon_has_sysfs) {
33936104c370SDaniel Vetter 		for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
33946104c370SDaniel Vetter 			device_remove_file(fbcon_device, &device_attrs[i]);
33956104c370SDaniel Vetter 
33966104c370SDaniel Vetter 		fbcon_has_sysfs = 0;
33976104c370SDaniel Vetter 	}
33986104c370SDaniel Vetter }
33996104c370SDaniel Vetter 
fb_console_exit(void)34006104c370SDaniel Vetter void __exit fb_console_exit(void)
34016104c370SDaniel Vetter {
3402c75300b5SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3403c75300b5SDaniel Vetter 	console_lock();
3404c75300b5SDaniel Vetter 	if (deferred_takeover)
3405c75300b5SDaniel Vetter 		dummycon_unregister_output_notifier(&fbcon_output_nb);
3406c75300b5SDaniel Vetter 	console_unlock();
3407c75300b5SDaniel Vetter 
3408c75300b5SDaniel Vetter 	cancel_work_sync(&fbcon_deferred_takeover_work);
3409c75300b5SDaniel Vetter #endif
3410c75300b5SDaniel Vetter 
34116104c370SDaniel Vetter 	console_lock();
34126104c370SDaniel Vetter 	fbcon_deinit_device();
34136104c370SDaniel Vetter 	device_destroy(fb_class, MKDEV(0, 0));
3414c75300b5SDaniel Vetter 
34156104c370SDaniel Vetter 	do_unregister_con_driver(&fb_con);
34166104c370SDaniel Vetter 	console_unlock();
34176104c370SDaniel Vetter }
34186104c370SDaniel Vetter #endif
3419