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