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>
676104c370SDaniel Vetter #include <linux/slab.h>
686104c370SDaniel Vetter #include <linux/fb.h>
696104c370SDaniel Vetter #include <linux/fbcon.h>
706104c370SDaniel Vetter #include <linux/vt_kern.h>
716104c370SDaniel Vetter #include <linux/selection.h>
726104c370SDaniel Vetter #include <linux/font.h>
736104c370SDaniel Vetter #include <linux/smp.h>
746104c370SDaniel Vetter #include <linux/init.h>
756104c370SDaniel Vetter #include <linux/interrupt.h>
766104c370SDaniel Vetter #include <linux/crc32.h> /* For counting font checksums */
77fe2d70d6SDaniel Vetter #include <linux/uaccess.h>
786104c370SDaniel Vetter #include <asm/irq.h>
796104c370SDaniel Vetter
806104c370SDaniel Vetter #include "fbcon.h"
81ff8fbcf6SThomas Zimmermann #include "fb_internal.h"
826104c370SDaniel Vetter
8394193d2aSDaniel Vetter /*
8494193d2aSDaniel Vetter * FIXME: Locking
8594193d2aSDaniel Vetter *
8694193d2aSDaniel Vetter * - fbcon state itself is protected by the console_lock, and the code does a
8794193d2aSDaniel Vetter * pretty good job at making sure that lock is held everywhere it's needed.
8894193d2aSDaniel Vetter *
8994193d2aSDaniel Vetter * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
9094193d2aSDaniel Vetter * means concurrent access to the same fbdev from both fbcon and userspace
9194193d2aSDaniel Vetter * will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
9294193d2aSDaniel Vetter * of fb_lock/unlock protected sections, since otherwise we'll recurse and
9394193d2aSDaniel Vetter * deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
9494193d2aSDaniel Vetter * fbmem.c cannot use locking asserts, and there's lots of callers which get
9594193d2aSDaniel Vetter * the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
9694193d2aSDaniel Vetter */
9794193d2aSDaniel Vetter
986104c370SDaniel Vetter enum {
996104c370SDaniel Vetter FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */
1006104c370SDaniel Vetter FBCON_LOGO_DRAW = -2, /* draw the logo to a console */
1016104c370SDaniel Vetter FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
1026104c370SDaniel Vetter };
1036104c370SDaniel Vetter
10450233393SDaniel Vetter static struct fbcon_display fb_display[MAX_NR_CONSOLES];
1056104c370SDaniel Vetter
1061e8ea2e9SGUO Zihua static struct fb_info *fbcon_registered_fb[FB_MAX];
1071e8ea2e9SGUO Zihua static int fbcon_num_registered_fb;
108efc3acbcSDaniel Vetter
109efc3acbcSDaniel Vetter #define fbcon_for_each_registered_fb(i) \
110efc3acbcSDaniel Vetter for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \
111efc3acbcSDaniel Vetter if (!fbcon_registered_fb[i]) {} else
112efc3acbcSDaniel Vetter
1136104c370SDaniel Vetter static signed char con2fb_map[MAX_NR_CONSOLES];
1146104c370SDaniel Vetter static signed char con2fb_map_boot[MAX_NR_CONSOLES];
1156104c370SDaniel Vetter
fbcon_info_from_console(int console)116409d6c95SDaniel Vetter static struct fb_info *fbcon_info_from_console(int console)
117409d6c95SDaniel Vetter {
118409d6c95SDaniel Vetter WARN_CONSOLE_UNLOCKED();
119409d6c95SDaniel Vetter
120efc3acbcSDaniel Vetter return fbcon_registered_fb[con2fb_map[console]];
121409d6c95SDaniel Vetter }
122409d6c95SDaniel Vetter
1236104c370SDaniel Vetter static int logo_lines;
1246104c370SDaniel Vetter /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
1256104c370SDaniel Vetter enums. */
1266104c370SDaniel Vetter static int logo_shown = FBCON_LOGO_CANSHOW;
1276104c370SDaniel Vetter /* console mappings */
128cad564caSHelge Deller static unsigned int first_fb_vc;
129cad564caSHelge Deller static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
1306104c370SDaniel Vetter static int fbcon_is_default = 1;
1316104c370SDaniel Vetter static int primary_device = -1;
1326104c370SDaniel Vetter static int fbcon_has_console_bind;
1336104c370SDaniel Vetter
1346104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
1356104c370SDaniel Vetter static int map_override;
1366104c370SDaniel Vetter
fbcon_map_override(void)1376104c370SDaniel Vetter static inline void fbcon_map_override(void)
1386104c370SDaniel Vetter {
1396104c370SDaniel Vetter map_override = 1;
1406104c370SDaniel Vetter }
1416104c370SDaniel Vetter #else
fbcon_map_override(void)1426104c370SDaniel Vetter static inline void fbcon_map_override(void)
1436104c370SDaniel Vetter {
1446104c370SDaniel Vetter }
1456104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
1466104c370SDaniel Vetter
14783d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
14883d83bebSHans de Goede static bool deferred_takeover = true;
14983d83bebSHans de Goede #else
15083d83bebSHans de Goede #define deferred_takeover false
15183d83bebSHans de Goede #endif
15283d83bebSHans de Goede
1536104c370SDaniel Vetter /* font data */
1546104c370SDaniel Vetter static char fontname[40];
1556104c370SDaniel Vetter
1566104c370SDaniel Vetter /* current fb_info */
1576104c370SDaniel Vetter static int info_idx = -1;
1586104c370SDaniel Vetter
1596104c370SDaniel Vetter /* console rotation */
160b0d8e409SHans de Goede static int initial_rotation = -1;
1616104c370SDaniel Vetter static int fbcon_has_sysfs;
16274c1c8b3SDavid Lechner static int margin_color;
1636104c370SDaniel Vetter
1646104c370SDaniel Vetter static const struct consw fb_con;
1656104c370SDaniel Vetter
1666104c370SDaniel Vetter #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
1676104c370SDaniel Vetter
1686104c370SDaniel Vetter static int fbcon_cursor_noblink;
1696104c370SDaniel Vetter
1706104c370SDaniel Vetter #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
1716104c370SDaniel Vetter
1726104c370SDaniel Vetter /*
1736104c370SDaniel Vetter * Interface used by the world
1746104c370SDaniel Vetter */
1756104c370SDaniel Vetter
1766104c370SDaniel Vetter static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
1776104c370SDaniel Vetter static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
1786104c370SDaniel Vetter
1796104c370SDaniel Vetter /*
1806104c370SDaniel Vetter * Internal routines
1816104c370SDaniel Vetter */
1826104c370SDaniel Vetter static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1836104c370SDaniel Vetter int unit);
1841148836fSHelge Deller static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1851148836fSHelge Deller int line, int count, int dy);
1866104c370SDaniel Vetter static void fbcon_modechanged(struct fb_info *info);
1876104c370SDaniel Vetter static void fbcon_set_all_vcs(struct fb_info *info);
1889ad7acdaSDaniel Vetter
1896104c370SDaniel Vetter static struct device *fbcon_device;
1906104c370SDaniel Vetter
1916104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
fbcon_set_rotation(struct fb_info * info)1926104c370SDaniel Vetter static inline void fbcon_set_rotation(struct fb_info *info)
1936104c370SDaniel Vetter {
1946104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
1956104c370SDaniel Vetter
1966104c370SDaniel Vetter if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
1976104c370SDaniel Vetter ops->p->con_rotate < 4)
1986104c370SDaniel Vetter ops->rotate = ops->p->con_rotate;
1996104c370SDaniel Vetter else
2006104c370SDaniel Vetter ops->rotate = 0;
2016104c370SDaniel Vetter }
2026104c370SDaniel Vetter
fbcon_rotate(struct fb_info * info,u32 rotate)2036104c370SDaniel Vetter static void fbcon_rotate(struct fb_info *info, u32 rotate)
2046104c370SDaniel Vetter {
2056104c370SDaniel Vetter struct fbcon_ops *ops= info->fbcon_par;
2066104c370SDaniel Vetter struct fb_info *fb_info;
2076104c370SDaniel Vetter
2086104c370SDaniel Vetter if (!ops || ops->currcon == -1)
2096104c370SDaniel Vetter return;
2106104c370SDaniel Vetter
211409d6c95SDaniel Vetter fb_info = fbcon_info_from_console(ops->currcon);
2126104c370SDaniel Vetter
2136104c370SDaniel Vetter if (info == fb_info) {
21450233393SDaniel Vetter struct fbcon_display *p = &fb_display[ops->currcon];
2156104c370SDaniel Vetter
2166104c370SDaniel Vetter if (rotate < 4)
2176104c370SDaniel Vetter p->con_rotate = rotate;
2186104c370SDaniel Vetter else
2196104c370SDaniel Vetter p->con_rotate = 0;
2206104c370SDaniel Vetter
2216104c370SDaniel Vetter fbcon_modechanged(info);
2226104c370SDaniel Vetter }
2236104c370SDaniel Vetter }
2246104c370SDaniel Vetter
fbcon_rotate_all(struct fb_info * info,u32 rotate)2256104c370SDaniel Vetter static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
2266104c370SDaniel Vetter {
2276104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
2286104c370SDaniel Vetter struct vc_data *vc;
22950233393SDaniel Vetter struct fbcon_display *p;
2306104c370SDaniel Vetter int i;
2316104c370SDaniel Vetter
2326104c370SDaniel Vetter if (!ops || ops->currcon < 0 || rotate > 3)
2336104c370SDaniel Vetter return;
2346104c370SDaniel Vetter
2356104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
2366104c370SDaniel Vetter vc = vc_cons[i].d;
2376104c370SDaniel Vetter if (!vc || vc->vc_mode != KD_TEXT ||
238409d6c95SDaniel Vetter fbcon_info_from_console(i) != info)
2396104c370SDaniel Vetter continue;
2406104c370SDaniel Vetter
2416104c370SDaniel Vetter p = &fb_display[vc->vc_num];
2426104c370SDaniel Vetter p->con_rotate = rotate;
2436104c370SDaniel Vetter }
2446104c370SDaniel Vetter
2456104c370SDaniel Vetter fbcon_set_all_vcs(info);
2466104c370SDaniel Vetter }
2476104c370SDaniel Vetter #else
fbcon_set_rotation(struct fb_info * info)2486104c370SDaniel Vetter static inline void fbcon_set_rotation(struct fb_info *info)
2496104c370SDaniel Vetter {
2506104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
2516104c370SDaniel Vetter
2526104c370SDaniel Vetter ops->rotate = FB_ROTATE_UR;
2536104c370SDaniel Vetter }
2546104c370SDaniel Vetter
fbcon_rotate(struct fb_info * info,u32 rotate)2556104c370SDaniel Vetter static void fbcon_rotate(struct fb_info *info, u32 rotate)
2566104c370SDaniel Vetter {
2576104c370SDaniel Vetter return;
2586104c370SDaniel Vetter }
2596104c370SDaniel Vetter
fbcon_rotate_all(struct fb_info * info,u32 rotate)2606104c370SDaniel Vetter static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
2616104c370SDaniel Vetter {
2626104c370SDaniel Vetter return;
2636104c370SDaniel Vetter }
2646104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
2656104c370SDaniel Vetter
fbcon_get_rotate(struct fb_info * info)2666104c370SDaniel Vetter static int fbcon_get_rotate(struct fb_info *info)
2676104c370SDaniel Vetter {
2686104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
2696104c370SDaniel Vetter
2706104c370SDaniel Vetter return (ops) ? ops->rotate : 0;
2716104c370SDaniel Vetter }
2726104c370SDaniel Vetter
fbcon_is_inactive(struct vc_data * vc,struct fb_info * info)2736104c370SDaniel Vetter static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
2746104c370SDaniel Vetter {
2756104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
2766104c370SDaniel Vetter
2776104c370SDaniel Vetter return (info->state != FBINFO_STATE_RUNNING ||
2788d7fc299SDaniel Vetter vc->vc_mode != KD_TEXT || ops->graphics);
2796104c370SDaniel Vetter }
2806104c370SDaniel Vetter
get_color(struct vc_data * vc,struct fb_info * info,u16 c,int is_fg)2816104c370SDaniel Vetter static int get_color(struct vc_data *vc, struct fb_info *info,
2826104c370SDaniel Vetter u16 c, int is_fg)
2836104c370SDaniel Vetter {
2846104c370SDaniel Vetter int depth = fb_get_color_depth(&info->var, &info->fix);
2856104c370SDaniel Vetter int color = 0;
2866104c370SDaniel Vetter
2876104c370SDaniel Vetter if (console_blanked) {
2886104c370SDaniel Vetter unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
2896104c370SDaniel Vetter
2906104c370SDaniel Vetter c = vc->vc_video_erase_char & charmask;
2916104c370SDaniel Vetter }
2926104c370SDaniel Vetter
2936104c370SDaniel Vetter if (depth != 1)
2946104c370SDaniel Vetter color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
2956104c370SDaniel Vetter : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
2966104c370SDaniel Vetter
2976104c370SDaniel Vetter switch (depth) {
2986104c370SDaniel Vetter case 1:
2996104c370SDaniel Vetter {
3006104c370SDaniel Vetter int col = mono_col(info);
3016104c370SDaniel Vetter /* 0 or 1 */
3026104c370SDaniel Vetter int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
3036104c370SDaniel Vetter int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
3046104c370SDaniel Vetter
3056104c370SDaniel Vetter if (console_blanked)
3066104c370SDaniel Vetter fg = bg;
3076104c370SDaniel Vetter
3086104c370SDaniel Vetter color = (is_fg) ? fg : bg;
3096104c370SDaniel Vetter break;
3106104c370SDaniel Vetter }
3116104c370SDaniel Vetter case 2:
3126104c370SDaniel Vetter /*
3136104c370SDaniel Vetter * Scale down 16-colors to 4 colors. Default 4-color palette
3146104c370SDaniel Vetter * is grayscale. However, simply dividing the values by 4
3156104c370SDaniel Vetter * will not work, as colors 1, 2 and 3 will be scaled-down
3166104c370SDaniel Vetter * to zero rendering them invisible. So empirically convert
3176104c370SDaniel Vetter * colors to a sane 4-level grayscale.
3186104c370SDaniel Vetter */
3196104c370SDaniel Vetter switch (color) {
3206104c370SDaniel Vetter case 0:
3216104c370SDaniel Vetter color = 0; /* black */
3226104c370SDaniel Vetter break;
3236104c370SDaniel Vetter case 1 ... 6:
3246104c370SDaniel Vetter color = 2; /* white */
3256104c370SDaniel Vetter break;
3266104c370SDaniel Vetter case 7 ... 8:
3276104c370SDaniel Vetter color = 1; /* gray */
3286104c370SDaniel Vetter break;
3296104c370SDaniel Vetter default:
3306104c370SDaniel Vetter color = 3; /* intense white */
3316104c370SDaniel Vetter break;
3326104c370SDaniel Vetter }
3336104c370SDaniel Vetter break;
3346104c370SDaniel Vetter case 3:
3356104c370SDaniel Vetter /*
3366104c370SDaniel Vetter * Last 8 entries of default 16-color palette is a more intense
3376104c370SDaniel Vetter * version of the first 8 (i.e., same chrominance, different
3386104c370SDaniel Vetter * luminance).
3396104c370SDaniel Vetter */
3406104c370SDaniel Vetter color &= 7;
3416104c370SDaniel Vetter break;
3426104c370SDaniel Vetter }
3436104c370SDaniel Vetter
3446104c370SDaniel Vetter
3456104c370SDaniel Vetter return color;
3466104c370SDaniel Vetter }
3476104c370SDaniel Vetter
fb_flashcursor(struct work_struct * work)3486104c370SDaniel Vetter static void fb_flashcursor(struct work_struct *work)
3496104c370SDaniel Vetter {
3503b0fb6abSDaniel Vetter struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
3513b0fb6abSDaniel Vetter struct fb_info *info;
3526104c370SDaniel Vetter struct vc_data *vc = NULL;
3536104c370SDaniel Vetter int c;
3546104c370SDaniel Vetter int mode;
3556104c370SDaniel Vetter int ret;
3566104c370SDaniel Vetter
3576104c370SDaniel Vetter /* FIXME: we should sort out the unbind locking instead */
3586104c370SDaniel Vetter /* instead we just fail to flash the cursor if we can't get
3596104c370SDaniel Vetter * the lock instead of blocking fbcon deinit */
3606104c370SDaniel Vetter ret = console_trylock();
3616104c370SDaniel Vetter if (ret == 0)
3626104c370SDaniel Vetter return;
3636104c370SDaniel Vetter
3643b0fb6abSDaniel Vetter /* protected by console_lock */
3653b0fb6abSDaniel Vetter info = ops->info;
3663b0fb6abSDaniel Vetter
3673b0fb6abSDaniel Vetter if (ops->currcon != -1)
3686104c370SDaniel Vetter vc = vc_cons[ops->currcon].d;
3696104c370SDaniel Vetter
3706104c370SDaniel Vetter if (!vc || !con_is_visible(vc) ||
371409d6c95SDaniel Vetter fbcon_info_from_console(vc->vc_num) != info ||
3726104c370SDaniel Vetter vc->vc_deccm != 1) {
3736104c370SDaniel Vetter console_unlock();
3746104c370SDaniel Vetter return;
3756104c370SDaniel Vetter }
3766104c370SDaniel Vetter
3776104c370SDaniel Vetter c = scr_readw((u16 *) vc->vc_pos);
3786104c370SDaniel Vetter mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
3796104c370SDaniel Vetter CM_ERASE : CM_DRAW;
38006a0df4dSLinus Torvalds ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
3816104c370SDaniel Vetter get_color(vc, info, c, 0));
3826104c370SDaniel Vetter console_unlock();
3833b0fb6abSDaniel Vetter
3843b0fb6abSDaniel Vetter queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
3853b0fb6abSDaniel Vetter ops->cur_blink_jiffies);
3866104c370SDaniel Vetter }
3876104c370SDaniel Vetter
fbcon_add_cursor_work(struct fb_info * info)3883b0fb6abSDaniel Vetter static void fbcon_add_cursor_work(struct fb_info *info)
3896104c370SDaniel Vetter {
3906104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
3916104c370SDaniel Vetter
3923b0fb6abSDaniel Vetter if (!fbcon_cursor_noblink)
3933b0fb6abSDaniel Vetter queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
3943b0fb6abSDaniel Vetter ops->cur_blink_jiffies);
3956104c370SDaniel Vetter }
3966104c370SDaniel Vetter
fbcon_del_cursor_work(struct fb_info * info)3973b0fb6abSDaniel Vetter static void fbcon_del_cursor_work(struct fb_info *info)
3986104c370SDaniel Vetter {
3996104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
4006104c370SDaniel Vetter
4013b0fb6abSDaniel Vetter cancel_delayed_work_sync(&ops->cursor_work);
4026104c370SDaniel Vetter }
4036104c370SDaniel Vetter
4046104c370SDaniel Vetter #ifndef MODULE
fb_console_setup(char * this_opt)4056104c370SDaniel Vetter static int __init fb_console_setup(char *this_opt)
4066104c370SDaniel Vetter {
4076104c370SDaniel Vetter char *options;
4086104c370SDaniel Vetter int i, j;
4096104c370SDaniel Vetter
4106104c370SDaniel Vetter if (!this_opt || !*this_opt)
4116104c370SDaniel Vetter return 1;
4126104c370SDaniel Vetter
4136104c370SDaniel Vetter while ((options = strsep(&this_opt, ",")) != NULL) {
4146104c370SDaniel Vetter if (!strncmp(options, "font:", 5)) {
4158d026858SWolfram Sang strscpy(fontname, options + 5, sizeof(fontname));
4166104c370SDaniel Vetter continue;
4176104c370SDaniel Vetter }
4186104c370SDaniel Vetter
4196104c370SDaniel Vetter if (!strncmp(options, "scrollback:", 11)) {
42050145474SLinus Torvalds pr_warn("Ignoring scrollback size option\n");
4216104c370SDaniel Vetter continue;
4226104c370SDaniel Vetter }
4236104c370SDaniel Vetter
4246104c370SDaniel Vetter if (!strncmp(options, "map:", 4)) {
4256104c370SDaniel Vetter options += 4;
4266104c370SDaniel Vetter if (*options) {
4276104c370SDaniel Vetter for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
4286104c370SDaniel Vetter if (!options[j])
4296104c370SDaniel Vetter j = 0;
4306104c370SDaniel Vetter con2fb_map_boot[i] =
4316104c370SDaniel Vetter (options[j++]-'0') % FB_MAX;
4326104c370SDaniel Vetter }
4336104c370SDaniel Vetter
4346104c370SDaniel Vetter fbcon_map_override();
4356104c370SDaniel Vetter }
4366104c370SDaniel Vetter continue;
4376104c370SDaniel Vetter }
4386104c370SDaniel Vetter
4396104c370SDaniel Vetter if (!strncmp(options, "vc:", 3)) {
4406104c370SDaniel Vetter options += 3;
4416104c370SDaniel Vetter if (*options)
4426104c370SDaniel Vetter first_fb_vc = simple_strtoul(options, &options, 10) - 1;
443cad564caSHelge Deller if (first_fb_vc >= MAX_NR_CONSOLES)
4446104c370SDaniel Vetter first_fb_vc = 0;
4456104c370SDaniel Vetter if (*options++ == '-')
4466104c370SDaniel Vetter last_fb_vc = simple_strtoul(options, &options, 10) - 1;
447cad564caSHelge Deller if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
448cad564caSHelge Deller last_fb_vc = MAX_NR_CONSOLES - 1;
4496104c370SDaniel Vetter fbcon_is_default = 0;
4506104c370SDaniel Vetter continue;
4516104c370SDaniel Vetter }
4526104c370SDaniel Vetter
4536104c370SDaniel Vetter if (!strncmp(options, "rotate:", 7)) {
4546104c370SDaniel Vetter options += 7;
4556104c370SDaniel Vetter if (*options)
4566104c370SDaniel Vetter initial_rotation = simple_strtoul(options, &options, 0);
4576104c370SDaniel Vetter if (initial_rotation > 3)
4586104c370SDaniel Vetter initial_rotation = 0;
4596104c370SDaniel Vetter continue;
4606104c370SDaniel Vetter }
46174c1c8b3SDavid Lechner
46274c1c8b3SDavid Lechner if (!strncmp(options, "margin:", 7)) {
46374c1c8b3SDavid Lechner options += 7;
46474c1c8b3SDavid Lechner if (*options)
46574c1c8b3SDavid Lechner margin_color = simple_strtoul(options, &options, 0);
46674c1c8b3SDavid Lechner continue;
46774c1c8b3SDavid Lechner }
46883d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
46983d83bebSHans de Goede if (!strcmp(options, "nodefer")) {
47083d83bebSHans de Goede deferred_takeover = false;
47183d83bebSHans de Goede continue;
47283d83bebSHans de Goede }
47383d83bebSHans de Goede #endif
474890d14d2SPeter Rosin
475890d14d2SPeter Rosin if (!strncmp(options, "logo-pos:", 9)) {
476890d14d2SPeter Rosin options += 9;
477890d14d2SPeter Rosin if (!strcmp(options, "center"))
478890d14d2SPeter Rosin fb_center_logo = true;
479890d14d2SPeter Rosin continue;
480890d14d2SPeter Rosin }
481691f50abSPeter Rosin
482691f50abSPeter Rosin if (!strncmp(options, "logo-count:", 11)) {
483691f50abSPeter Rosin options += 11;
484691f50abSPeter Rosin if (*options)
485691f50abSPeter Rosin fb_logo_count = simple_strtol(options, &options, 0);
486691f50abSPeter Rosin continue;
487691f50abSPeter Rosin }
4886104c370SDaniel Vetter }
4896104c370SDaniel Vetter return 1;
4906104c370SDaniel Vetter }
4916104c370SDaniel Vetter
4926104c370SDaniel Vetter __setup("fbcon=", fb_console_setup);
4936104c370SDaniel Vetter #endif
4946104c370SDaniel Vetter
search_fb_in_map(int idx)4956104c370SDaniel Vetter static int search_fb_in_map(int idx)
4966104c370SDaniel Vetter {
4976104c370SDaniel Vetter int i, retval = 0;
4986104c370SDaniel Vetter
4996104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
5006104c370SDaniel Vetter if (con2fb_map[i] == idx)
5016104c370SDaniel Vetter retval = 1;
5026104c370SDaniel Vetter }
5036104c370SDaniel Vetter return retval;
5046104c370SDaniel Vetter }
5056104c370SDaniel Vetter
search_for_mapped_con(void)5066104c370SDaniel Vetter static int search_for_mapped_con(void)
5076104c370SDaniel Vetter {
5086104c370SDaniel Vetter int i, retval = 0;
5096104c370SDaniel Vetter
5106104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
5116104c370SDaniel Vetter if (con2fb_map[i] != -1)
5126104c370SDaniel Vetter retval = 1;
5136104c370SDaniel Vetter }
5146104c370SDaniel Vetter return retval;
5156104c370SDaniel Vetter }
5166104c370SDaniel Vetter
do_fbcon_takeover(int show_logo)5176104c370SDaniel Vetter static int do_fbcon_takeover(int show_logo)
5186104c370SDaniel Vetter {
5196104c370SDaniel Vetter int err, i;
5206104c370SDaniel Vetter
521efc3acbcSDaniel Vetter if (!fbcon_num_registered_fb)
5226104c370SDaniel Vetter return -ENODEV;
5236104c370SDaniel Vetter
5246104c370SDaniel Vetter if (!show_logo)
5256104c370SDaniel Vetter logo_shown = FBCON_LOGO_DONTSHOW;
5266104c370SDaniel Vetter
5276104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++)
5286104c370SDaniel Vetter con2fb_map[i] = info_idx;
5296104c370SDaniel Vetter
5306104c370SDaniel Vetter err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
5316104c370SDaniel Vetter fbcon_is_default);
5326104c370SDaniel Vetter
5336104c370SDaniel Vetter if (err) {
5346104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++)
5356104c370SDaniel Vetter con2fb_map[i] = -1;
5366104c370SDaniel Vetter info_idx = -1;
5376104c370SDaniel Vetter } else {
5386104c370SDaniel Vetter fbcon_has_console_bind = 1;
5396104c370SDaniel Vetter }
5406104c370SDaniel Vetter
5416104c370SDaniel Vetter return err;
5426104c370SDaniel Vetter }
5436104c370SDaniel Vetter
5446104c370SDaniel Vetter #ifdef MODULE
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)5456104c370SDaniel Vetter static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
5466104c370SDaniel Vetter int cols, int rows, int new_cols, int new_rows)
5476104c370SDaniel Vetter {
5486104c370SDaniel Vetter logo_shown = FBCON_LOGO_DONTSHOW;
5496104c370SDaniel Vetter }
5506104c370SDaniel Vetter #else
fbcon_prepare_logo(struct vc_data * vc,struct fb_info * info,int cols,int rows,int new_cols,int new_rows)5516104c370SDaniel Vetter static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
5526104c370SDaniel Vetter int cols, int rows, int new_cols, int new_rows)
5536104c370SDaniel Vetter {
5546104c370SDaniel Vetter /* Need to make room for the logo */
5556104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
5566104c370SDaniel Vetter int cnt, erase = vc->vc_video_erase_char, step;
5576104c370SDaniel Vetter unsigned short *save = NULL, *r, *q;
5586104c370SDaniel Vetter int logo_height;
5596104c370SDaniel Vetter
560376b3ff5SDaniel Vetter if (info->fbops->owner) {
5616104c370SDaniel Vetter logo_shown = FBCON_LOGO_DONTSHOW;
5626104c370SDaniel Vetter return;
5636104c370SDaniel Vetter }
5646104c370SDaniel Vetter
5656104c370SDaniel Vetter /*
5666104c370SDaniel Vetter * remove underline attribute from erase character
5676104c370SDaniel Vetter * if black and white framebuffer.
5686104c370SDaniel Vetter */
5696104c370SDaniel Vetter if (fb_get_color_depth(&info->var, &info->fix) == 1)
5706104c370SDaniel Vetter erase &= ~0x400;
5716104c370SDaniel Vetter logo_height = fb_prepare_logo(info, ops->rotate);
5726104c370SDaniel Vetter logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
5736104c370SDaniel Vetter q = (unsigned short *) (vc->vc_origin +
5746104c370SDaniel Vetter vc->vc_size_row * rows);
5756104c370SDaniel Vetter step = logo_lines * cols;
5766104c370SDaniel Vetter for (r = q - logo_lines * cols; r < q; r++)
5776104c370SDaniel Vetter if (scr_readw(r) != vc->vc_video_erase_char)
5786104c370SDaniel Vetter break;
5796104c370SDaniel Vetter if (r != q && new_rows >= rows + logo_lines) {
5805c380526SGeert Uytterhoeven save = kmalloc(array3_size(logo_lines, new_cols, 2),
5816da2ec56SKees Cook GFP_KERNEL);
5826104c370SDaniel Vetter if (save) {
58362c6f4f9SChangcheng Deng int i = min(cols, new_cols);
584fcf918b9SGustavo A. R. Silva scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
5856104c370SDaniel Vetter r = q - step;
5866104c370SDaniel Vetter for (cnt = 0; cnt < logo_lines; cnt++, r += i)
5876104c370SDaniel Vetter scr_memcpyw(save + cnt * new_cols, r, 2 * i);
5886104c370SDaniel Vetter r = q;
5896104c370SDaniel Vetter }
5906104c370SDaniel Vetter }
5916104c370SDaniel Vetter if (r == q) {
5926104c370SDaniel Vetter /* We can scroll screen down */
5936104c370SDaniel Vetter r = q - step - cols;
5946104c370SDaniel Vetter for (cnt = rows - logo_lines; cnt > 0; cnt--) {
5956104c370SDaniel Vetter scr_memcpyw(r + step, r, vc->vc_size_row);
5966104c370SDaniel Vetter r -= cols;
5976104c370SDaniel Vetter }
5986104c370SDaniel Vetter if (!save) {
5996104c370SDaniel Vetter int lines;
60028bc24fcSJiri Slaby if (vc->state.y + logo_lines >= rows)
60128bc24fcSJiri Slaby lines = rows - vc->state.y - 1;
6026104c370SDaniel Vetter else
6036104c370SDaniel Vetter lines = logo_lines;
60428bc24fcSJiri Slaby vc->state.y += lines;
6056104c370SDaniel Vetter vc->vc_pos += lines * vc->vc_size_row;
6066104c370SDaniel Vetter }
6076104c370SDaniel Vetter }
6086104c370SDaniel Vetter scr_memsetw((unsigned short *) vc->vc_origin,
6096104c370SDaniel Vetter erase,
6106104c370SDaniel Vetter vc->vc_size_row * logo_lines);
6116104c370SDaniel Vetter
6126104c370SDaniel Vetter if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
6136104c370SDaniel Vetter fbcon_clear_margins(vc, 0);
6146104c370SDaniel Vetter update_screen(vc);
6156104c370SDaniel Vetter }
6166104c370SDaniel Vetter
6176104c370SDaniel Vetter if (save) {
6186104c370SDaniel Vetter q = (unsigned short *) (vc->vc_origin +
6196104c370SDaniel Vetter vc->vc_size_row *
6206104c370SDaniel Vetter rows);
621fcf918b9SGustavo A. R. Silva scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
62228bc24fcSJiri Slaby vc->state.y += logo_lines;
6236104c370SDaniel Vetter vc->vc_pos += logo_lines * vc->vc_size_row;
6246104c370SDaniel Vetter kfree(save);
6256104c370SDaniel Vetter }
6266104c370SDaniel Vetter
62710993504SPrarit Bhargava if (logo_shown == FBCON_LOGO_DONTSHOW)
62810993504SPrarit Bhargava return;
62910993504SPrarit Bhargava
6306104c370SDaniel Vetter if (logo_lines > vc->vc_bottom) {
6316104c370SDaniel Vetter logo_shown = FBCON_LOGO_CANSHOW;
6326104c370SDaniel Vetter printk(KERN_INFO
6336104c370SDaniel Vetter "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
63410993504SPrarit Bhargava } else {
6356104c370SDaniel Vetter logo_shown = FBCON_LOGO_DRAW;
6366104c370SDaniel Vetter vc->vc_top = logo_lines;
6376104c370SDaniel Vetter }
6386104c370SDaniel Vetter }
6396104c370SDaniel Vetter #endif /* MODULE */
6406104c370SDaniel Vetter
6416104c370SDaniel Vetter #ifdef CONFIG_FB_TILEBLITTING
set_blitting_type(struct vc_data * vc,struct fb_info * info)6426104c370SDaniel Vetter static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
6436104c370SDaniel Vetter {
6446104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
6456104c370SDaniel Vetter
6466104c370SDaniel Vetter ops->p = &fb_display[vc->vc_num];
6476104c370SDaniel Vetter
6486104c370SDaniel Vetter if ((info->flags & FBINFO_MISC_TILEBLITTING))
6496104c370SDaniel Vetter fbcon_set_tileops(vc, info);
6506104c370SDaniel Vetter else {
6516104c370SDaniel Vetter fbcon_set_rotation(info);
6526104c370SDaniel Vetter fbcon_set_bitops(ops);
6536104c370SDaniel Vetter }
6546104c370SDaniel Vetter }
6556104c370SDaniel Vetter
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)6566104c370SDaniel Vetter static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
6576104c370SDaniel Vetter {
6586104c370SDaniel Vetter int err = 0;
6596104c370SDaniel Vetter
6606104c370SDaniel Vetter if (info->flags & FBINFO_MISC_TILEBLITTING &&
6616104c370SDaniel Vetter info->tileops->fb_get_tilemax(info) < charcount)
6626104c370SDaniel Vetter err = 1;
6636104c370SDaniel Vetter
6646104c370SDaniel Vetter return err;
6656104c370SDaniel Vetter }
6666104c370SDaniel Vetter #else
set_blitting_type(struct vc_data * vc,struct fb_info * info)6676104c370SDaniel Vetter static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
6686104c370SDaniel Vetter {
6696104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
6706104c370SDaniel Vetter
6716104c370SDaniel Vetter info->flags &= ~FBINFO_MISC_TILEBLITTING;
6726104c370SDaniel Vetter ops->p = &fb_display[vc->vc_num];
6736104c370SDaniel Vetter fbcon_set_rotation(info);
6746104c370SDaniel Vetter fbcon_set_bitops(ops);
6756104c370SDaniel Vetter }
6766104c370SDaniel Vetter
fbcon_invalid_charcount(struct fb_info * info,unsigned charcount)6776104c370SDaniel Vetter static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
6786104c370SDaniel Vetter {
6796104c370SDaniel Vetter return 0;
6806104c370SDaniel Vetter }
6816104c370SDaniel Vetter
6826104c370SDaniel Vetter #endif /* CONFIG_MISC_TILEBLITTING */
6836104c370SDaniel Vetter
fbcon_release(struct fb_info * info)684d443d938SDaniel Vetter static void fbcon_release(struct fb_info *info)
685d443d938SDaniel Vetter {
68604933a29SDaniel Vetter lock_fb_info(info);
687d443d938SDaniel Vetter if (info->fbops->fb_release)
688d443d938SDaniel Vetter info->fbops->fb_release(info, 0);
68904933a29SDaniel Vetter unlock_fb_info(info);
690d443d938SDaniel Vetter
691d443d938SDaniel Vetter module_put(info->fbops->owner);
6923647d6d3SDaniel Vetter
6933647d6d3SDaniel Vetter if (info->fbcon_par) {
6943647d6d3SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
6953647d6d3SDaniel Vetter
6963647d6d3SDaniel Vetter fbcon_del_cursor_work(info);
6973647d6d3SDaniel Vetter kfree(ops->cursor_state.mask);
6983647d6d3SDaniel Vetter kfree(ops->cursor_data);
6993647d6d3SDaniel Vetter kfree(ops->cursor_src);
7003647d6d3SDaniel Vetter kfree(ops->fontbuffer);
7013647d6d3SDaniel Vetter kfree(info->fbcon_par);
7023647d6d3SDaniel Vetter info->fbcon_par = NULL;
7033647d6d3SDaniel Vetter }
704d443d938SDaniel Vetter }
705d443d938SDaniel Vetter
fbcon_open(struct fb_info * info)706bd6026a8SDaniel Vetter static int fbcon_open(struct fb_info *info)
707bd6026a8SDaniel Vetter {
708d443d938SDaniel Vetter struct fbcon_ops *ops;
709d443d938SDaniel Vetter
710bd6026a8SDaniel Vetter if (!try_module_get(info->fbops->owner))
711bd6026a8SDaniel Vetter return -ENODEV;
712bd6026a8SDaniel Vetter
71304933a29SDaniel Vetter lock_fb_info(info);
714bd6026a8SDaniel Vetter if (info->fbops->fb_open &&
715bd6026a8SDaniel Vetter info->fbops->fb_open(info, 0)) {
71604933a29SDaniel Vetter unlock_fb_info(info);
717bd6026a8SDaniel Vetter module_put(info->fbops->owner);
718bd6026a8SDaniel Vetter return -ENODEV;
719bd6026a8SDaniel Vetter }
72004933a29SDaniel Vetter unlock_fb_info(info);
721bd6026a8SDaniel Vetter
722d443d938SDaniel Vetter ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
723d443d938SDaniel Vetter if (!ops) {
724d443d938SDaniel Vetter fbcon_release(info);
725d443d938SDaniel Vetter return -ENOMEM;
726d443d938SDaniel Vetter }
727d443d938SDaniel Vetter
728d443d938SDaniel Vetter INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor);
729d443d938SDaniel Vetter ops->info = info;
730d443d938SDaniel Vetter info->fbcon_par = ops;
731d443d938SDaniel Vetter ops->cur_blink_jiffies = HZ / 5;
732d443d938SDaniel Vetter
733bd6026a8SDaniel Vetter return 0;
734bd6026a8SDaniel Vetter }
735bd6026a8SDaniel Vetter
con2fb_acquire_newinfo(struct vc_data * vc,struct fb_info * info,int unit)7366104c370SDaniel Vetter static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
737d443d938SDaniel Vetter int unit)
7386104c370SDaniel Vetter {
739bd6026a8SDaniel Vetter int err;
7406104c370SDaniel Vetter
741bd6026a8SDaniel Vetter err = fbcon_open(info);
742bd6026a8SDaniel Vetter if (err)
743bd6026a8SDaniel Vetter return err;
7446104c370SDaniel Vetter
7456104c370SDaniel Vetter if (vc)
7466104c370SDaniel Vetter set_blitting_type(vc, info);
7476104c370SDaniel Vetter
7486104c370SDaniel Vetter return err;
7496104c370SDaniel Vetter }
7506104c370SDaniel Vetter
con2fb_release_oldinfo(struct vc_data * vc,struct fb_info * oldinfo,struct fb_info * newinfo)751b07db395SDaniel Vetter static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
752b07db395SDaniel Vetter struct fb_info *newinfo)
7536104c370SDaniel Vetter {
754bd6026a8SDaniel Vetter int ret;
7556104c370SDaniel Vetter
756bd6026a8SDaniel Vetter fbcon_release(oldinfo);
7576104c370SDaniel Vetter
7586104c370SDaniel Vetter /*
7596104c370SDaniel Vetter If oldinfo and newinfo are driving the same hardware,
7606104c370SDaniel Vetter the fb_release() method of oldinfo may attempt to
7616104c370SDaniel Vetter restore the hardware state. This will leave the
7626104c370SDaniel Vetter newinfo in an undefined state. Thus, a call to
7636104c370SDaniel Vetter fb_set_par() may be needed for the newinfo.
7646104c370SDaniel Vetter */
7656104c370SDaniel Vetter if (newinfo && newinfo->fbops->fb_set_par) {
7666104c370SDaniel Vetter ret = newinfo->fbops->fb_set_par(newinfo);
7676104c370SDaniel Vetter
7686104c370SDaniel Vetter if (ret)
7696104c370SDaniel Vetter printk(KERN_ERR "con2fb_release_oldinfo: "
7706104c370SDaniel Vetter "detected unhandled fb_set_par error, "
7716104c370SDaniel Vetter "error code %d\n", ret);
7726104c370SDaniel Vetter }
7736104c370SDaniel Vetter }
7746104c370SDaniel Vetter
con2fb_init_display(struct vc_data * vc,struct fb_info * info,int unit,int show_logo)7756104c370SDaniel Vetter static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
7766104c370SDaniel Vetter int unit, int show_logo)
7776104c370SDaniel Vetter {
7786104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
7796104c370SDaniel Vetter int ret;
7806104c370SDaniel Vetter
7816104c370SDaniel Vetter ops->currcon = fg_console;
7826104c370SDaniel Vetter
783cae69e45SDaniel Vetter if (info->fbops->fb_set_par && !ops->initialized) {
7846104c370SDaniel Vetter ret = info->fbops->fb_set_par(info);
7856104c370SDaniel Vetter
7866104c370SDaniel Vetter if (ret)
7876104c370SDaniel Vetter printk(KERN_ERR "con2fb_init_display: detected "
7886104c370SDaniel Vetter "unhandled fb_set_par error, "
7896104c370SDaniel Vetter "error code %d\n", ret);
7906104c370SDaniel Vetter }
7916104c370SDaniel Vetter
792cae69e45SDaniel Vetter ops->initialized = true;
7936104c370SDaniel Vetter ops->graphics = 0;
7946104c370SDaniel Vetter fbcon_set_disp(info, &info->var, unit);
7956104c370SDaniel Vetter
7966104c370SDaniel Vetter if (show_logo) {
7976104c370SDaniel Vetter struct vc_data *fg_vc = vc_cons[fg_console].d;
7986104c370SDaniel Vetter struct fb_info *fg_info =
799409d6c95SDaniel Vetter fbcon_info_from_console(fg_console);
8006104c370SDaniel Vetter
8016104c370SDaniel Vetter fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
8026104c370SDaniel Vetter fg_vc->vc_rows, fg_vc->vc_cols,
8036104c370SDaniel Vetter fg_vc->vc_rows);
8046104c370SDaniel Vetter }
8056104c370SDaniel Vetter
8066104c370SDaniel Vetter update_screen(vc_cons[fg_console].d);
8076104c370SDaniel Vetter }
8086104c370SDaniel Vetter
8096104c370SDaniel Vetter /**
8106104c370SDaniel Vetter * set_con2fb_map - map console to frame buffer device
8116104c370SDaniel Vetter * @unit: virtual console number to map
8126104c370SDaniel Vetter * @newidx: frame buffer index to map virtual console to
8136104c370SDaniel Vetter * @user: user request
8146104c370SDaniel Vetter *
8156104c370SDaniel Vetter * Maps a virtual console @unit to a frame buffer device
8166104c370SDaniel Vetter * @newidx.
8176104c370SDaniel Vetter *
8186104c370SDaniel Vetter * This should be called with the console lock held.
8196104c370SDaniel Vetter */
set_con2fb_map(int unit,int newidx,int user)8206104c370SDaniel Vetter static int set_con2fb_map(int unit, int newidx, int user)
8216104c370SDaniel Vetter {
8226104c370SDaniel Vetter struct vc_data *vc = vc_cons[unit].d;
8236104c370SDaniel Vetter int oldidx = con2fb_map[unit];
824efc3acbcSDaniel Vetter struct fb_info *info = fbcon_registered_fb[newidx];
8256104c370SDaniel Vetter struct fb_info *oldinfo = NULL;
826edf79dd2SDaniel Vetter int err = 0, show_logo;
8276104c370SDaniel Vetter
8283bd3a0e3SHans de Goede WARN_CONSOLE_UNLOCKED();
8293bd3a0e3SHans de Goede
8306104c370SDaniel Vetter if (oldidx == newidx)
8316104c370SDaniel Vetter return 0;
8326104c370SDaniel Vetter
8336104c370SDaniel Vetter if (!info)
8346104c370SDaniel Vetter return -EINVAL;
8356104c370SDaniel Vetter
8366104c370SDaniel Vetter if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
8376104c370SDaniel Vetter info_idx = newidx;
8386104c370SDaniel Vetter return do_fbcon_takeover(0);
8396104c370SDaniel Vetter }
8406104c370SDaniel Vetter
8416104c370SDaniel Vetter if (oldidx != -1)
842efc3acbcSDaniel Vetter oldinfo = fbcon_registered_fb[oldidx];
8436104c370SDaniel Vetter
844edf79dd2SDaniel Vetter if (!search_fb_in_map(newidx)) {
845d443d938SDaniel Vetter err = con2fb_acquire_newinfo(vc, info, unit);
846edf79dd2SDaniel Vetter if (err)
847edf79dd2SDaniel Vetter return err;
848edf79dd2SDaniel Vetter
849edf79dd2SDaniel Vetter fbcon_add_cursor_work(info);
850d443d938SDaniel Vetter }
8516104c370SDaniel Vetter
852fffb0b52SDaniel Vetter con2fb_map[unit] = newidx;
853fffb0b52SDaniel Vetter
8546104c370SDaniel Vetter /*
8556104c370SDaniel Vetter * If old fb is not mapped to any of the consoles,
8566104c370SDaniel Vetter * fbcon should release it.
8576104c370SDaniel Vetter */
858edf79dd2SDaniel Vetter if (oldinfo && !search_fb_in_map(oldidx))
859b07db395SDaniel Vetter con2fb_release_oldinfo(vc, oldinfo, info);
8606104c370SDaniel Vetter
861b07db395SDaniel Vetter show_logo = (fg_console == 0 && !user &&
8626104c370SDaniel Vetter logo_shown != FBCON_LOGO_DONTSHOW);
8636104c370SDaniel Vetter
8646104c370SDaniel Vetter con2fb_map_boot[unit] = newidx;
8656104c370SDaniel Vetter con2fb_init_display(vc, info, unit, show_logo);
8666104c370SDaniel Vetter
8676104c370SDaniel Vetter if (!search_fb_in_map(info_idx))
8686104c370SDaniel Vetter info_idx = newidx;
8696104c370SDaniel Vetter
8706104c370SDaniel Vetter return err;
8716104c370SDaniel Vetter }
8726104c370SDaniel Vetter
8736104c370SDaniel Vetter /*
8746104c370SDaniel Vetter * Low Level Operations
8756104c370SDaniel Vetter */
8766104c370SDaniel 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)87750233393SDaniel Vetter static int var_to_display(struct fbcon_display *disp,
8786104c370SDaniel Vetter struct fb_var_screeninfo *var,
8796104c370SDaniel Vetter struct fb_info *info)
8806104c370SDaniel Vetter {
8816104c370SDaniel Vetter disp->xres_virtual = var->xres_virtual;
8826104c370SDaniel Vetter disp->yres_virtual = var->yres_virtual;
8836104c370SDaniel Vetter disp->bits_per_pixel = var->bits_per_pixel;
8846104c370SDaniel Vetter disp->grayscale = var->grayscale;
8856104c370SDaniel Vetter disp->nonstd = var->nonstd;
8866104c370SDaniel Vetter disp->accel_flags = var->accel_flags;
8876104c370SDaniel Vetter disp->height = var->height;
8886104c370SDaniel Vetter disp->width = var->width;
8896104c370SDaniel Vetter disp->red = var->red;
8906104c370SDaniel Vetter disp->green = var->green;
8916104c370SDaniel Vetter disp->blue = var->blue;
8926104c370SDaniel Vetter disp->transp = var->transp;
8936104c370SDaniel Vetter disp->rotate = var->rotate;
8946104c370SDaniel Vetter disp->mode = fb_match_mode(var, &info->modelist);
8956104c370SDaniel Vetter if (disp->mode == NULL)
8966104c370SDaniel Vetter /* This should not happen */
8976104c370SDaniel Vetter return -EINVAL;
8986104c370SDaniel Vetter return 0;
8996104c370SDaniel Vetter }
9006104c370SDaniel Vetter
display_to_var(struct fb_var_screeninfo * var,struct fbcon_display * disp)9016104c370SDaniel Vetter static void display_to_var(struct fb_var_screeninfo *var,
90250233393SDaniel Vetter struct fbcon_display *disp)
9036104c370SDaniel Vetter {
9046104c370SDaniel Vetter fb_videomode_to_var(var, disp->mode);
9056104c370SDaniel Vetter var->xres_virtual = disp->xres_virtual;
9066104c370SDaniel Vetter var->yres_virtual = disp->yres_virtual;
9076104c370SDaniel Vetter var->bits_per_pixel = disp->bits_per_pixel;
9086104c370SDaniel Vetter var->grayscale = disp->grayscale;
9096104c370SDaniel Vetter var->nonstd = disp->nonstd;
9106104c370SDaniel Vetter var->accel_flags = disp->accel_flags;
9116104c370SDaniel Vetter var->height = disp->height;
9126104c370SDaniel Vetter var->width = disp->width;
9136104c370SDaniel Vetter var->red = disp->red;
9146104c370SDaniel Vetter var->green = disp->green;
9156104c370SDaniel Vetter var->blue = disp->blue;
9166104c370SDaniel Vetter var->transp = disp->transp;
9176104c370SDaniel Vetter var->rotate = disp->rotate;
9186104c370SDaniel Vetter }
9196104c370SDaniel Vetter
fbcon_startup(void)9206104c370SDaniel Vetter static const char *fbcon_startup(void)
9216104c370SDaniel Vetter {
9226104c370SDaniel Vetter const char *display_desc = "frame buffer device";
92350233393SDaniel Vetter struct fbcon_display *p = &fb_display[fg_console];
9246104c370SDaniel Vetter struct vc_data *vc = vc_cons[fg_console].d;
9256104c370SDaniel Vetter const struct font_desc *font = NULL;
9266104c370SDaniel Vetter struct fb_info *info = NULL;
9276104c370SDaniel Vetter struct fbcon_ops *ops;
9286104c370SDaniel Vetter int rows, cols;
9296104c370SDaniel Vetter
9306104c370SDaniel Vetter /*
9316104c370SDaniel Vetter * If num_registered_fb is zero, this is a call for the dummy part.
9326104c370SDaniel Vetter * The frame buffer devices weren't initialized yet.
9336104c370SDaniel Vetter */
934efc3acbcSDaniel Vetter if (!fbcon_num_registered_fb || info_idx == -1)
9356104c370SDaniel Vetter return display_desc;
9366104c370SDaniel Vetter /*
9376104c370SDaniel Vetter * Instead of blindly using registered_fb[0], we use info_idx, set by
9389b0a490eSDaniel Vetter * fbcon_fb_registered();
9396104c370SDaniel Vetter */
940efc3acbcSDaniel Vetter info = fbcon_registered_fb[info_idx];
9416104c370SDaniel Vetter if (!info)
9426104c370SDaniel Vetter return NULL;
9436104c370SDaniel Vetter
944bd6026a8SDaniel Vetter if (fbcon_open(info))
9456104c370SDaniel Vetter return NULL;
9466104c370SDaniel Vetter
947d443d938SDaniel Vetter ops = info->fbcon_par;
9486104c370SDaniel Vetter ops->currcon = -1;
9496104c370SDaniel Vetter ops->graphics = 1;
9506104c370SDaniel Vetter ops->cur_rotate = -1;
951c9e6a364SHans de Goede
9526104c370SDaniel Vetter p->con_rotate = initial_rotation;
953c9e6a364SHans de Goede if (p->con_rotate == -1)
954c9e6a364SHans de Goede p->con_rotate = info->fbcon_rotate_hint;
955c9e6a364SHans de Goede if (p->con_rotate == -1)
956f2f4946bSHans de Goede p->con_rotate = FB_ROTATE_UR;
957c9e6a364SHans de Goede
9586104c370SDaniel Vetter set_blitting_type(vc, info);
9596104c370SDaniel Vetter
9606104c370SDaniel Vetter /* Setup default font */
96112d5796dSThomas Zimmermann if (!p->fontdata) {
9626104c370SDaniel Vetter if (!fontname[0] || !(font = find_font(fontname)))
9636104c370SDaniel Vetter font = get_default_font(info->var.xres,
9646104c370SDaniel Vetter info->var.yres,
9656104c370SDaniel Vetter info->pixmap.blit_x,
9666104c370SDaniel Vetter info->pixmap.blit_y);
9676104c370SDaniel Vetter vc->vc_font.width = font->width;
9686104c370SDaniel Vetter vc->vc_font.height = font->height;
9696104c370SDaniel Vetter vc->vc_font.data = (void *)(p->fontdata = font->data);
970a1ac250aSPeilin Ye vc->vc_font.charcount = font->charcount;
9716104c370SDaniel Vetter }
9726104c370SDaniel Vetter
9736104c370SDaniel Vetter cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
9746104c370SDaniel Vetter rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
9756104c370SDaniel Vetter cols /= vc->vc_font.width;
9766104c370SDaniel Vetter rows /= vc->vc_font.height;
9776104c370SDaniel Vetter vc_resize(vc, cols, rows);
9786104c370SDaniel Vetter
979b1cba76dSSam Ravnborg pr_debug("mode: %s\n", info->fix.id);
980b1cba76dSSam Ravnborg pr_debug("visual: %d\n", info->fix.visual);
981b1cba76dSSam Ravnborg pr_debug("res: %dx%d-%d\n", info->var.xres,
9826104c370SDaniel Vetter info->var.yres,
9836104c370SDaniel Vetter info->var.bits_per_pixel);
9846104c370SDaniel Vetter
9853b0fb6abSDaniel Vetter fbcon_add_cursor_work(info);
9866104c370SDaniel Vetter return display_desc;
9876104c370SDaniel Vetter }
9886104c370SDaniel Vetter
fbcon_init(struct vc_data * vc,int init)9896104c370SDaniel Vetter static void fbcon_init(struct vc_data *vc, int init)
9906104c370SDaniel Vetter {
9911f4ed2fbSDaniel Vetter struct fb_info *info;
9926104c370SDaniel Vetter struct fbcon_ops *ops;
9936104c370SDaniel Vetter struct vc_data **default_mode = vc->vc_display_fg;
9946104c370SDaniel Vetter struct vc_data *svc = *default_mode;
99550233393SDaniel Vetter struct fbcon_display *t, *p = &fb_display[vc->vc_num];
996a1ac250aSPeilin Ye int logo = 1, new_rows, new_cols, rows, cols;
99750b10528SHelge Deller int ret;
9986104c370SDaniel Vetter
9991f4ed2fbSDaniel Vetter if (WARN_ON(info_idx == -1))
10006104c370SDaniel Vetter return;
10016104c370SDaniel Vetter
10021f4ed2fbSDaniel Vetter if (con2fb_map[vc->vc_num] == -1)
10031f4ed2fbSDaniel Vetter con2fb_map[vc->vc_num] = info_idx;
10041f4ed2fbSDaniel Vetter
1005409d6c95SDaniel Vetter info = fbcon_info_from_console(vc->vc_num);
10066104c370SDaniel Vetter
10073c5a1b11SAndreas Schwab if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
100810993504SPrarit Bhargava logo_shown = FBCON_LOGO_DONTSHOW;
100910993504SPrarit Bhargava
10106104c370SDaniel Vetter if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
10116104c370SDaniel Vetter (info->fix.type == FB_TYPE_TEXT))
10126104c370SDaniel Vetter logo = 0;
10136104c370SDaniel Vetter
10146104c370SDaniel Vetter if (var_to_display(p, &info->var, info))
10156104c370SDaniel Vetter return;
10166104c370SDaniel Vetter
10176104c370SDaniel Vetter if (!info->fbcon_par)
1018d443d938SDaniel Vetter con2fb_acquire_newinfo(vc, info, vc->vc_num);
10196104c370SDaniel Vetter
10206104c370SDaniel Vetter /* If we are not the first console on this
10216104c370SDaniel Vetter fb, copy the font from that console */
10226104c370SDaniel Vetter t = &fb_display[fg_console];
10236104c370SDaniel Vetter if (!p->fontdata) {
10246104c370SDaniel Vetter if (t->fontdata) {
10256104c370SDaniel Vetter struct vc_data *fvc = vc_cons[fg_console].d;
10266104c370SDaniel Vetter
10276104c370SDaniel Vetter vc->vc_font.data = (void *)(p->fontdata =
10286104c370SDaniel Vetter fvc->vc_font.data);
10296104c370SDaniel Vetter vc->vc_font.width = fvc->vc_font.width;
10306104c370SDaniel Vetter vc->vc_font.height = fvc->vc_font.height;
1031a1ac250aSPeilin Ye vc->vc_font.charcount = fvc->vc_font.charcount;
10326104c370SDaniel Vetter p->userfont = t->userfont;
10336104c370SDaniel Vetter
10346104c370SDaniel Vetter if (p->userfont)
10356104c370SDaniel Vetter REFCOUNT(p->fontdata)++;
10366104c370SDaniel Vetter } else {
10376104c370SDaniel Vetter const struct font_desc *font = NULL;
10386104c370SDaniel Vetter
10396104c370SDaniel Vetter if (!fontname[0] || !(font = find_font(fontname)))
10406104c370SDaniel Vetter font = get_default_font(info->var.xres,
10416104c370SDaniel Vetter info->var.yres,
10426104c370SDaniel Vetter info->pixmap.blit_x,
10436104c370SDaniel Vetter info->pixmap.blit_y);
10446104c370SDaniel Vetter vc->vc_font.width = font->width;
10456104c370SDaniel Vetter vc->vc_font.height = font->height;
10466104c370SDaniel Vetter vc->vc_font.data = (void *)(p->fontdata = font->data);
1047a1ac250aSPeilin Ye vc->vc_font.charcount = font->charcount;
10486104c370SDaniel Vetter }
10496104c370SDaniel Vetter }
10506104c370SDaniel Vetter
10516104c370SDaniel Vetter vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
10526104c370SDaniel Vetter vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1053a1ac250aSPeilin Ye if (vc->vc_font.charcount == 256) {
10546104c370SDaniel Vetter vc->vc_hi_font_mask = 0;
10556104c370SDaniel Vetter } else {
10566104c370SDaniel Vetter vc->vc_hi_font_mask = 0x100;
10576104c370SDaniel Vetter if (vc->vc_can_do_color)
10586104c370SDaniel Vetter vc->vc_complement_mask <<= 1;
10596104c370SDaniel Vetter }
10606104c370SDaniel Vetter
10618da443b1SJiri Slaby if (!*svc->uni_pagedict_loc)
10626104c370SDaniel Vetter con_set_default_unimap(svc);
10638da443b1SJiri Slaby if (!*vc->uni_pagedict_loc)
10646104c370SDaniel Vetter con_copy_unimap(vc, svc);
10656104c370SDaniel Vetter
10666104c370SDaniel Vetter ops = info->fbcon_par;
10676104c370SDaniel Vetter ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1068c9e6a364SHans de Goede
10696104c370SDaniel Vetter p->con_rotate = initial_rotation;
1070c9e6a364SHans de Goede if (p->con_rotate == -1)
1071c9e6a364SHans de Goede p->con_rotate = info->fbcon_rotate_hint;
1072c9e6a364SHans de Goede if (p->con_rotate == -1)
1073f2f4946bSHans de Goede p->con_rotate = FB_ROTATE_UR;
1074c9e6a364SHans de Goede
10756104c370SDaniel Vetter set_blitting_type(vc, info);
10766104c370SDaniel Vetter
10776104c370SDaniel Vetter cols = vc->vc_cols;
10786104c370SDaniel Vetter rows = vc->vc_rows;
10796104c370SDaniel Vetter new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
10806104c370SDaniel Vetter new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
10816104c370SDaniel Vetter new_cols /= vc->vc_font.width;
10826104c370SDaniel Vetter new_rows /= vc->vc_font.height;
10836104c370SDaniel Vetter
10846104c370SDaniel Vetter /*
10856104c370SDaniel Vetter * We must always set the mode. The mode of the previous console
10866104c370SDaniel Vetter * driver could be in the same resolution but we are using different
10876104c370SDaniel Vetter * hardware so we have to initialize the hardware.
10886104c370SDaniel Vetter *
10896104c370SDaniel Vetter * We need to do it in fbcon_init() to prevent screen corruption.
10906104c370SDaniel Vetter */
10916104c370SDaniel Vetter if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1092cae69e45SDaniel Vetter if (info->fbops->fb_set_par && !ops->initialized) {
10936104c370SDaniel Vetter ret = info->fbops->fb_set_par(info);
10946104c370SDaniel Vetter
10956104c370SDaniel Vetter if (ret)
10966104c370SDaniel Vetter printk(KERN_ERR "fbcon_init: detected "
10976104c370SDaniel Vetter "unhandled fb_set_par error, "
10986104c370SDaniel Vetter "error code %d\n", ret);
10996104c370SDaniel Vetter }
11006104c370SDaniel Vetter
1101cae69e45SDaniel Vetter ops->initialized = true;
11026104c370SDaniel Vetter }
11036104c370SDaniel Vetter
11046104c370SDaniel Vetter ops->graphics = 0;
11056104c370SDaniel Vetter
1106a3f781a9SHelge Deller #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
110750b10528SHelge Deller if ((info->flags & FBINFO_HWACCEL_COPYAREA) &&
110850b10528SHelge Deller !(info->flags & FBINFO_HWACCEL_DISABLED))
110987ab9f6bSHelge Deller p->scrollmode = SCROLL_MOVE;
111087ab9f6bSHelge Deller else /* default to something safe */
11111148836fSHelge Deller p->scrollmode = SCROLL_REDRAW;
1112a3f781a9SHelge Deller #endif
11131148836fSHelge Deller
11141148836fSHelge Deller /*
11156104c370SDaniel Vetter * ++guenther: console.c:vc_allocate() relies on initializing
11166104c370SDaniel Vetter * vc_{cols,rows}, but we must not set those if we are only
11176104c370SDaniel Vetter * resizing the console.
11186104c370SDaniel Vetter */
11196104c370SDaniel Vetter if (init) {
11206104c370SDaniel Vetter vc->vc_cols = new_cols;
11216104c370SDaniel Vetter vc->vc_rows = new_rows;
11226104c370SDaniel Vetter } else
11236104c370SDaniel Vetter vc_resize(vc, new_cols, new_rows);
11246104c370SDaniel Vetter
11256104c370SDaniel Vetter if (logo)
11266104c370SDaniel Vetter fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
11276104c370SDaniel Vetter
11286104c370SDaniel Vetter if (ops->rotate_font && ops->rotate_font(info, vc)) {
11296104c370SDaniel Vetter ops->rotate = FB_ROTATE_UR;
11306104c370SDaniel Vetter set_blitting_type(vc, info);
11316104c370SDaniel Vetter }
11326104c370SDaniel Vetter
11336104c370SDaniel Vetter ops->p = &fb_display[fg_console];
11346104c370SDaniel Vetter }
11356104c370SDaniel Vetter
fbcon_free_font(struct fbcon_display * p)113612d5796dSThomas Zimmermann static void fbcon_free_font(struct fbcon_display *p)
11376104c370SDaniel Vetter {
113812d5796dSThomas Zimmermann if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
11396104c370SDaniel Vetter kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
11406104c370SDaniel Vetter p->fontdata = NULL;
11416104c370SDaniel Vetter p->userfont = 0;
11426104c370SDaniel Vetter }
11436104c370SDaniel Vetter
11446104c370SDaniel Vetter static void set_vc_hi_font(struct vc_data *vc, bool set);
11456104c370SDaniel Vetter
fbcon_release_all(void)1146c75300b5SDaniel Vetter static void fbcon_release_all(void)
1147c75300b5SDaniel Vetter {
1148c75300b5SDaniel Vetter struct fb_info *info;
1149c75300b5SDaniel Vetter int i, j, mapped;
1150c75300b5SDaniel Vetter
1151efc3acbcSDaniel Vetter fbcon_for_each_registered_fb(i) {
1152c75300b5SDaniel Vetter mapped = 0;
1153efc3acbcSDaniel Vetter info = fbcon_registered_fb[i];
1154c75300b5SDaniel Vetter
1155c75300b5SDaniel Vetter for (j = first_fb_vc; j <= last_fb_vc; j++) {
1156c75300b5SDaniel Vetter if (con2fb_map[j] == i) {
1157c75300b5SDaniel Vetter mapped = 1;
1158c75300b5SDaniel Vetter con2fb_map[j] = -1;
1159c75300b5SDaniel Vetter }
1160c75300b5SDaniel Vetter }
1161c75300b5SDaniel Vetter
1162c75300b5SDaniel Vetter if (mapped)
1163c75300b5SDaniel Vetter fbcon_release(info);
1164c75300b5SDaniel Vetter }
1165c75300b5SDaniel Vetter }
1166c75300b5SDaniel Vetter
fbcon_deinit(struct vc_data * vc)11676104c370SDaniel Vetter static void fbcon_deinit(struct vc_data *vc)
11686104c370SDaniel Vetter {
116950233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
11706104c370SDaniel Vetter struct fb_info *info;
11716104c370SDaniel Vetter struct fbcon_ops *ops;
11726104c370SDaniel Vetter int idx;
11736104c370SDaniel Vetter
117412d5796dSThomas Zimmermann fbcon_free_font(p);
11756104c370SDaniel Vetter idx = con2fb_map[vc->vc_num];
11766104c370SDaniel Vetter
11776104c370SDaniel Vetter if (idx == -1)
11786104c370SDaniel Vetter goto finished;
11796104c370SDaniel Vetter
1180efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
11816104c370SDaniel Vetter
11826104c370SDaniel Vetter if (!info)
11836104c370SDaniel Vetter goto finished;
11846104c370SDaniel Vetter
11856104c370SDaniel Vetter ops = info->fbcon_par;
11866104c370SDaniel Vetter
11876104c370SDaniel Vetter if (!ops)
11886104c370SDaniel Vetter goto finished;
11896104c370SDaniel Vetter
11906104c370SDaniel Vetter if (con_is_visible(vc))
11913b0fb6abSDaniel Vetter fbcon_del_cursor_work(info);
11926104c370SDaniel Vetter
1193cae69e45SDaniel Vetter ops->initialized = false;
11946104c370SDaniel Vetter finished:
11956104c370SDaniel Vetter
119612d5796dSThomas Zimmermann fbcon_free_font(p);
11976104c370SDaniel Vetter vc->vc_font.data = NULL;
11986104c370SDaniel Vetter
1199a1ad1cc9SGrzegorz Halat if (vc->vc_hi_font_mask && vc->vc_screenbuf)
12006104c370SDaniel Vetter set_vc_hi_font(vc, false);
12016104c370SDaniel Vetter
12026104c370SDaniel Vetter if (!con_is_bound(&fb_con))
1203c75300b5SDaniel Vetter fbcon_release_all();
12046104c370SDaniel Vetter
1205b139f8b0SQiujun Huang if (vc->vc_num == logo_shown)
1206b139f8b0SQiujun Huang logo_shown = FBCON_LOGO_CANSHOW;
1207b139f8b0SQiujun Huang
12086104c370SDaniel Vetter return;
12096104c370SDaniel Vetter }
12106104c370SDaniel Vetter
12116104c370SDaniel Vetter /* ====================================================================== */
12126104c370SDaniel Vetter
12136104c370SDaniel Vetter /* fbcon_XXX routines - interface used by the world
12146104c370SDaniel Vetter *
12156104c370SDaniel Vetter * This system is now divided into two levels because of complications
12166104c370SDaniel Vetter * caused by hardware scrolling. Top level functions:
12176104c370SDaniel Vetter *
12181148836fSHelge Deller * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
12196104c370SDaniel Vetter *
12206104c370SDaniel Vetter * handles y values in range [0, scr_height-1] that correspond to real
12216104c370SDaniel Vetter * screen positions. y_wrap shift means that first line of bitmap may be
12226104c370SDaniel Vetter * anywhere on this display. These functions convert lineoffsets to
12236104c370SDaniel Vetter * bitmap offsets and deal with the wrap-around case by splitting blits.
12246104c370SDaniel Vetter *
12251148836fSHelge Deller * fbcon_bmove_physical_8() -- These functions fast implementations
12266104c370SDaniel Vetter * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
12276104c370SDaniel Vetter * fbcon_putc_physical_8() -- (font width != 8) may be added later
12286104c370SDaniel Vetter *
12296104c370SDaniel Vetter * WARNING:
12306104c370SDaniel Vetter *
12316104c370SDaniel Vetter * At the moment fbcon_putc() cannot blit across vertical wrap boundary
12326104c370SDaniel Vetter * Implies should only really hardware scroll in rows. Only reason for
12336104c370SDaniel Vetter * restriction is simplicity & efficiency at the moment.
12346104c370SDaniel Vetter */
12356104c370SDaniel Vetter
fbcon_clear(struct vc_data * vc,int sy,int sx,int height,int width)12366104c370SDaniel Vetter static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
12376104c370SDaniel Vetter int width)
12386104c370SDaniel Vetter {
1239409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
12406104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
12416104c370SDaniel Vetter
124250233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
12436104c370SDaniel Vetter u_int y_break;
12446104c370SDaniel Vetter
12456104c370SDaniel Vetter if (fbcon_is_inactive(vc, info))
12466104c370SDaniel Vetter return;
12476104c370SDaniel Vetter
12486104c370SDaniel Vetter if (!height || !width)
12496104c370SDaniel Vetter return;
12506104c370SDaniel Vetter
12516104c370SDaniel Vetter if (sy < vc->vc_top && vc->vc_top == logo_lines) {
12526104c370SDaniel Vetter vc->vc_top = 0;
12536104c370SDaniel Vetter /*
12546104c370SDaniel Vetter * If the font dimensions are not an integral of the display
12556104c370SDaniel Vetter * dimensions then the ops->clear below won't end up clearing
12566104c370SDaniel Vetter * the margins. Call clear_margins here in case the logo
12576104c370SDaniel Vetter * bitmap stretched into the margin area.
12586104c370SDaniel Vetter */
12596104c370SDaniel Vetter fbcon_clear_margins(vc, 0);
12606104c370SDaniel Vetter }
12616104c370SDaniel Vetter
12626104c370SDaniel Vetter /* Split blits that cross physical y_wrap boundary */
12636104c370SDaniel Vetter
12646104c370SDaniel Vetter y_break = p->vrows - p->yscroll;
12656104c370SDaniel Vetter if (sy < y_break && sy + height - 1 >= y_break) {
12666104c370SDaniel Vetter u_int b = y_break - sy;
12676104c370SDaniel Vetter ops->clear(vc, info, real_y(p, sy), sx, b, width);
12686104c370SDaniel Vetter ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
12696104c370SDaniel Vetter width);
12706104c370SDaniel Vetter } else
12716104c370SDaniel Vetter ops->clear(vc, info, real_y(p, sy), sx, height, width);
12726104c370SDaniel Vetter }
12736104c370SDaniel Vetter
fbcon_putcs(struct vc_data * vc,const unsigned short * s,int count,int ypos,int xpos)12746104c370SDaniel Vetter static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
12756104c370SDaniel Vetter int count, int ypos, int xpos)
12766104c370SDaniel Vetter {
1277409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
127850233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
12796104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
12806104c370SDaniel Vetter
12816104c370SDaniel Vetter if (!fbcon_is_inactive(vc, info))
12826104c370SDaniel Vetter ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
12836104c370SDaniel Vetter get_color(vc, info, scr_readw(s), 1),
12846104c370SDaniel Vetter get_color(vc, info, scr_readw(s), 0));
12856104c370SDaniel Vetter }
12866104c370SDaniel Vetter
fbcon_putc(struct vc_data * vc,int c,int ypos,int xpos)12876104c370SDaniel Vetter static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
12886104c370SDaniel Vetter {
12896104c370SDaniel Vetter unsigned short chr;
12906104c370SDaniel Vetter
12916104c370SDaniel Vetter scr_writew(c, &chr);
12926104c370SDaniel Vetter fbcon_putcs(vc, &chr, 1, ypos, xpos);
12936104c370SDaniel Vetter }
12946104c370SDaniel Vetter
fbcon_clear_margins(struct vc_data * vc,int bottom_only)12956104c370SDaniel Vetter static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
12966104c370SDaniel Vetter {
1297409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
12986104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
12996104c370SDaniel Vetter
13006104c370SDaniel Vetter if (!fbcon_is_inactive(vc, info))
130174c1c8b3SDavid Lechner ops->clear_margins(vc, info, margin_color, bottom_only);
13026104c370SDaniel Vetter }
13036104c370SDaniel Vetter
fbcon_cursor(struct vc_data * vc,int mode)13046104c370SDaniel Vetter static void fbcon_cursor(struct vc_data *vc, int mode)
13056104c370SDaniel Vetter {
1306409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
13076104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
13086104c370SDaniel Vetter int c = scr_readw((u16 *) vc->vc_pos);
13096104c370SDaniel Vetter
13106104c370SDaniel Vetter ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
13116104c370SDaniel Vetter
13126104c370SDaniel Vetter if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
13136104c370SDaniel Vetter return;
13146104c370SDaniel Vetter
1315c0e4b3adSJiri Slaby if (vc->vc_cursor_type & CUR_SW)
13163b0fb6abSDaniel Vetter fbcon_del_cursor_work(info);
13176104c370SDaniel Vetter else
13183b0fb6abSDaniel Vetter fbcon_add_cursor_work(info);
13196104c370SDaniel Vetter
13206104c370SDaniel Vetter ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
13216104c370SDaniel Vetter
132201faae51SDu Cheng if (!ops->cursor)
132301faae51SDu Cheng return;
132401faae51SDu Cheng
132506a0df4dSLinus Torvalds ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
13266104c370SDaniel Vetter get_color(vc, info, c, 0));
13276104c370SDaniel Vetter }
13286104c370SDaniel Vetter
13296104c370SDaniel Vetter static int scrollback_phys_max = 0;
13306104c370SDaniel Vetter static int scrollback_max = 0;
13316104c370SDaniel Vetter static int scrollback_current = 0;
13326104c370SDaniel Vetter
fbcon_set_disp(struct fb_info * info,struct fb_var_screeninfo * var,int unit)13336104c370SDaniel Vetter static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
13346104c370SDaniel Vetter int unit)
13356104c370SDaniel Vetter {
133650233393SDaniel Vetter struct fbcon_display *p, *t;
13376104c370SDaniel Vetter struct vc_data **default_mode, *vc;
13386104c370SDaniel Vetter struct vc_data *svc;
13396104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
1340a1ac250aSPeilin Ye int rows, cols;
13416104c370SDaniel Vetter
13426104c370SDaniel Vetter p = &fb_display[unit];
13436104c370SDaniel Vetter
13446104c370SDaniel Vetter if (var_to_display(p, var, info))
13456104c370SDaniel Vetter return;
13466104c370SDaniel Vetter
13476104c370SDaniel Vetter vc = vc_cons[unit].d;
13486104c370SDaniel Vetter
13496104c370SDaniel Vetter if (!vc)
13506104c370SDaniel Vetter return;
13516104c370SDaniel Vetter
13526104c370SDaniel Vetter default_mode = vc->vc_display_fg;
13536104c370SDaniel Vetter svc = *default_mode;
13546104c370SDaniel Vetter t = &fb_display[svc->vc_num];
13556104c370SDaniel Vetter
13566104c370SDaniel Vetter if (!vc->vc_font.data) {
13576104c370SDaniel Vetter vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
13586104c370SDaniel Vetter vc->vc_font.width = (*default_mode)->vc_font.width;
13596104c370SDaniel Vetter vc->vc_font.height = (*default_mode)->vc_font.height;
1360a1ac250aSPeilin Ye vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
13616104c370SDaniel Vetter p->userfont = t->userfont;
13626104c370SDaniel Vetter if (p->userfont)
13636104c370SDaniel Vetter REFCOUNT(p->fontdata)++;
13646104c370SDaniel Vetter }
13656104c370SDaniel Vetter
13666104c370SDaniel Vetter var->activate = FB_ACTIVATE_NOW;
13676104c370SDaniel Vetter info->var.activate = var->activate;
13686104c370SDaniel Vetter var->yoffset = info->var.yoffset;
13696104c370SDaniel Vetter var->xoffset = info->var.xoffset;
13706104c370SDaniel Vetter fb_set_var(info, var);
13716104c370SDaniel Vetter ops->var = info->var;
13726104c370SDaniel Vetter vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
13736104c370SDaniel Vetter vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1374a1ac250aSPeilin Ye if (vc->vc_font.charcount == 256) {
13756104c370SDaniel Vetter vc->vc_hi_font_mask = 0;
13766104c370SDaniel Vetter } else {
13776104c370SDaniel Vetter vc->vc_hi_font_mask = 0x100;
13786104c370SDaniel Vetter if (vc->vc_can_do_color)
13796104c370SDaniel Vetter vc->vc_complement_mask <<= 1;
13806104c370SDaniel Vetter }
13816104c370SDaniel Vetter
13828da443b1SJiri Slaby if (!*svc->uni_pagedict_loc)
13836104c370SDaniel Vetter con_set_default_unimap(svc);
13848da443b1SJiri Slaby if (!*vc->uni_pagedict_loc)
13856104c370SDaniel Vetter con_copy_unimap(vc, svc);
13866104c370SDaniel Vetter
13876104c370SDaniel Vetter cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
13886104c370SDaniel Vetter rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
13896104c370SDaniel Vetter cols /= vc->vc_font.width;
13906104c370SDaniel Vetter rows /= vc->vc_font.height;
13916104c370SDaniel Vetter vc_resize(vc, cols, rows);
13926104c370SDaniel Vetter
13936104c370SDaniel Vetter if (con_is_visible(vc)) {
13946104c370SDaniel Vetter update_screen(vc);
13956104c370SDaniel Vetter }
13966104c370SDaniel Vetter }
13976104c370SDaniel Vetter
ywrap_up(struct vc_data * vc,int count)13981148836fSHelge Deller static __inline__ void ywrap_up(struct vc_data *vc, int count)
13991148836fSHelge Deller {
1400409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14011148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
14021148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
14031148836fSHelge Deller
14041148836fSHelge Deller p->yscroll += count;
14051148836fSHelge Deller if (p->yscroll >= p->vrows) /* Deal with wrap */
14061148836fSHelge Deller p->yscroll -= p->vrows;
14071148836fSHelge Deller ops->var.xoffset = 0;
14081148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
14091148836fSHelge Deller ops->var.vmode |= FB_VMODE_YWRAP;
14101148836fSHelge Deller ops->update_start(info);
14111148836fSHelge Deller scrollback_max += count;
14121148836fSHelge Deller if (scrollback_max > scrollback_phys_max)
14131148836fSHelge Deller scrollback_max = scrollback_phys_max;
14141148836fSHelge Deller scrollback_current = 0;
14151148836fSHelge Deller }
14161148836fSHelge Deller
ywrap_down(struct vc_data * vc,int count)14171148836fSHelge Deller static __inline__ void ywrap_down(struct vc_data *vc, int count)
14181148836fSHelge Deller {
1419409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14201148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
14211148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
14221148836fSHelge Deller
14231148836fSHelge Deller p->yscroll -= count;
14241148836fSHelge Deller if (p->yscroll < 0) /* Deal with wrap */
14251148836fSHelge Deller p->yscroll += p->vrows;
14261148836fSHelge Deller ops->var.xoffset = 0;
14271148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
14281148836fSHelge Deller ops->var.vmode |= FB_VMODE_YWRAP;
14291148836fSHelge Deller ops->update_start(info);
14301148836fSHelge Deller scrollback_max -= count;
14311148836fSHelge Deller if (scrollback_max < 0)
14321148836fSHelge Deller scrollback_max = 0;
14331148836fSHelge Deller scrollback_current = 0;
14341148836fSHelge Deller }
14351148836fSHelge Deller
ypan_up(struct vc_data * vc,int count)14361148836fSHelge Deller static __inline__ void ypan_up(struct vc_data *vc, int count)
14371148836fSHelge Deller {
1438409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14391148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
14401148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
14411148836fSHelge Deller
14421148836fSHelge Deller p->yscroll += count;
14431148836fSHelge Deller if (p->yscroll > p->vrows - vc->vc_rows) {
14441148836fSHelge Deller ops->bmove(vc, info, p->vrows - vc->vc_rows,
14451148836fSHelge Deller 0, 0, 0, vc->vc_rows, vc->vc_cols);
14461148836fSHelge Deller p->yscroll -= p->vrows - vc->vc_rows;
14471148836fSHelge Deller }
14481148836fSHelge Deller
14491148836fSHelge Deller ops->var.xoffset = 0;
14501148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
14511148836fSHelge Deller ops->var.vmode &= ~FB_VMODE_YWRAP;
14521148836fSHelge Deller ops->update_start(info);
14531148836fSHelge Deller fbcon_clear_margins(vc, 1);
14541148836fSHelge Deller scrollback_max += count;
14551148836fSHelge Deller if (scrollback_max > scrollback_phys_max)
14561148836fSHelge Deller scrollback_max = scrollback_phys_max;
14571148836fSHelge Deller scrollback_current = 0;
14581148836fSHelge Deller }
14591148836fSHelge Deller
ypan_up_redraw(struct vc_data * vc,int t,int count)14601148836fSHelge Deller static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
14611148836fSHelge Deller {
1462409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14631148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
14641148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
14651148836fSHelge Deller
14661148836fSHelge Deller p->yscroll += count;
14671148836fSHelge Deller
14681148836fSHelge Deller if (p->yscroll > p->vrows - vc->vc_rows) {
14691148836fSHelge Deller p->yscroll -= p->vrows - vc->vc_rows;
14701148836fSHelge Deller fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
14711148836fSHelge Deller }
14721148836fSHelge Deller
14731148836fSHelge Deller ops->var.xoffset = 0;
14741148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
14751148836fSHelge Deller ops->var.vmode &= ~FB_VMODE_YWRAP;
14761148836fSHelge Deller ops->update_start(info);
14771148836fSHelge Deller fbcon_clear_margins(vc, 1);
14781148836fSHelge Deller scrollback_max += count;
14791148836fSHelge Deller if (scrollback_max > scrollback_phys_max)
14801148836fSHelge Deller scrollback_max = scrollback_phys_max;
14811148836fSHelge Deller scrollback_current = 0;
14821148836fSHelge Deller }
14831148836fSHelge Deller
ypan_down(struct vc_data * vc,int count)14841148836fSHelge Deller static __inline__ void ypan_down(struct vc_data *vc, int count)
14851148836fSHelge Deller {
1486409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
14871148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
14881148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
14891148836fSHelge Deller
14901148836fSHelge Deller p->yscroll -= count;
14911148836fSHelge Deller if (p->yscroll < 0) {
14921148836fSHelge Deller ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
14931148836fSHelge Deller 0, vc->vc_rows, vc->vc_cols);
14941148836fSHelge Deller p->yscroll += p->vrows - vc->vc_rows;
14951148836fSHelge Deller }
14961148836fSHelge Deller
14971148836fSHelge Deller ops->var.xoffset = 0;
14981148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
14991148836fSHelge Deller ops->var.vmode &= ~FB_VMODE_YWRAP;
15001148836fSHelge Deller ops->update_start(info);
15011148836fSHelge Deller fbcon_clear_margins(vc, 1);
15021148836fSHelge Deller scrollback_max -= count;
15031148836fSHelge Deller if (scrollback_max < 0)
15041148836fSHelge Deller scrollback_max = 0;
15051148836fSHelge Deller scrollback_current = 0;
15061148836fSHelge Deller }
15071148836fSHelge Deller
ypan_down_redraw(struct vc_data * vc,int t,int count)15081148836fSHelge Deller static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
15091148836fSHelge Deller {
1510409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
15111148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
15121148836fSHelge Deller struct fbcon_display *p = &fb_display[vc->vc_num];
15131148836fSHelge Deller
15141148836fSHelge Deller p->yscroll -= count;
15151148836fSHelge Deller
15161148836fSHelge Deller if (p->yscroll < 0) {
15171148836fSHelge Deller p->yscroll += p->vrows - vc->vc_rows;
15181148836fSHelge Deller fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
15191148836fSHelge Deller }
15201148836fSHelge Deller
15211148836fSHelge Deller ops->var.xoffset = 0;
15221148836fSHelge Deller ops->var.yoffset = p->yscroll * vc->vc_font.height;
15231148836fSHelge Deller ops->var.vmode &= ~FB_VMODE_YWRAP;
15241148836fSHelge Deller ops->update_start(info);
15251148836fSHelge Deller fbcon_clear_margins(vc, 1);
15261148836fSHelge Deller scrollback_max -= count;
15271148836fSHelge Deller if (scrollback_max < 0)
15281148836fSHelge Deller scrollback_max = 0;
15291148836fSHelge Deller scrollback_current = 0;
15301148836fSHelge Deller }
15311148836fSHelge Deller
fbcon_redraw_move(struct vc_data * vc,struct fbcon_display * p,int line,int count,int dy)15321148836fSHelge Deller static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
15331148836fSHelge Deller int line, int count, int dy)
15341148836fSHelge Deller {
15351148836fSHelge Deller unsigned short *s = (unsigned short *)
15361148836fSHelge Deller (vc->vc_origin + vc->vc_size_row * line);
15371148836fSHelge Deller
15381148836fSHelge Deller while (count--) {
15391148836fSHelge Deller unsigned short *start = s;
15401148836fSHelge Deller unsigned short *le = advance_row(s, 1);
15411148836fSHelge Deller unsigned short c;
15421148836fSHelge Deller int x = 0;
15431148836fSHelge Deller unsigned short attr = 1;
15441148836fSHelge Deller
15451148836fSHelge Deller do {
15461148836fSHelge Deller c = scr_readw(s);
15471148836fSHelge Deller if (attr != (c & 0xff00)) {
15481148836fSHelge Deller attr = c & 0xff00;
15491148836fSHelge Deller if (s > start) {
15501148836fSHelge Deller fbcon_putcs(vc, start, s - start,
15511148836fSHelge Deller dy, x);
15521148836fSHelge Deller x += s - start;
15531148836fSHelge Deller start = s;
15541148836fSHelge Deller }
15551148836fSHelge Deller }
15561148836fSHelge Deller console_conditional_schedule();
15571148836fSHelge Deller s++;
15581148836fSHelge Deller } while (s < le);
15591148836fSHelge Deller if (s > start)
15601148836fSHelge Deller fbcon_putcs(vc, start, s - start, dy, x);
15611148836fSHelge Deller console_conditional_schedule();
15621148836fSHelge Deller dy++;
15631148836fSHelge Deller }
15641148836fSHelge Deller }
15651148836fSHelge Deller
fbcon_redraw_blit(struct vc_data * vc,struct fb_info * info,struct fbcon_display * p,int line,int count,int ycount)15661148836fSHelge Deller static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
15671148836fSHelge Deller struct fbcon_display *p, int line, int count, int ycount)
15681148836fSHelge Deller {
15691148836fSHelge Deller int offset = ycount * vc->vc_cols;
15701148836fSHelge Deller unsigned short *d = (unsigned short *)
15711148836fSHelge Deller (vc->vc_origin + vc->vc_size_row * line);
15721148836fSHelge Deller unsigned short *s = d + offset;
15731148836fSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
15741148836fSHelge Deller
15751148836fSHelge Deller while (count--) {
15761148836fSHelge Deller unsigned short *start = s;
15771148836fSHelge Deller unsigned short *le = advance_row(s, 1);
15781148836fSHelge Deller unsigned short c;
15791148836fSHelge Deller int x = 0;
15801148836fSHelge Deller
15811148836fSHelge Deller do {
15821148836fSHelge Deller c = scr_readw(s);
15831148836fSHelge Deller
15841148836fSHelge Deller if (c == scr_readw(d)) {
15851148836fSHelge Deller if (s > start) {
15861148836fSHelge Deller ops->bmove(vc, info, line + ycount, x,
15871148836fSHelge Deller line, x, 1, s-start);
15881148836fSHelge Deller x += s - start + 1;
15891148836fSHelge Deller start = s + 1;
15901148836fSHelge Deller } else {
15911148836fSHelge Deller x++;
15921148836fSHelge Deller start++;
15931148836fSHelge Deller }
15941148836fSHelge Deller }
15951148836fSHelge Deller
15961148836fSHelge Deller scr_writew(c, d);
15971148836fSHelge Deller console_conditional_schedule();
15981148836fSHelge Deller s++;
15991148836fSHelge Deller d++;
16001148836fSHelge Deller } while (s < le);
16011148836fSHelge Deller if (s > start)
16021148836fSHelge Deller ops->bmove(vc, info, line + ycount, x, line, x, 1,
16031148836fSHelge Deller s-start);
16041148836fSHelge Deller console_conditional_schedule();
16051148836fSHelge Deller if (ycount > 0)
16061148836fSHelge Deller line++;
16071148836fSHelge Deller else {
16081148836fSHelge Deller line--;
16091148836fSHelge Deller /* NOTE: We subtract two lines from these pointers */
16101148836fSHelge Deller s -= vc->vc_size_row;
16111148836fSHelge Deller d -= vc->vc_size_row;
16121148836fSHelge Deller }
16131148836fSHelge Deller }
16141148836fSHelge Deller }
16151148836fSHelge Deller
fbcon_redraw(struct vc_data * vc,int line,int count,int offset)1616933ab3a8SJiri Slaby (SUSE) static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset)
16176104c370SDaniel Vetter {
16186104c370SDaniel Vetter unsigned short *d = (unsigned short *)
16196104c370SDaniel Vetter (vc->vc_origin + vc->vc_size_row * line);
16206104c370SDaniel Vetter unsigned short *s = d + offset;
16216104c370SDaniel Vetter
16226104c370SDaniel Vetter while (count--) {
16236104c370SDaniel Vetter unsigned short *start = s;
16246104c370SDaniel Vetter unsigned short *le = advance_row(s, 1);
16256104c370SDaniel Vetter unsigned short c;
16266104c370SDaniel Vetter int x = 0;
16276104c370SDaniel Vetter unsigned short attr = 1;
16286104c370SDaniel Vetter
16296104c370SDaniel Vetter do {
16306104c370SDaniel Vetter c = scr_readw(s);
16316104c370SDaniel Vetter if (attr != (c & 0xff00)) {
16326104c370SDaniel Vetter attr = c & 0xff00;
16336104c370SDaniel Vetter if (s > start) {
16346104c370SDaniel Vetter fbcon_putcs(vc, start, s - start,
16356104c370SDaniel Vetter line, x);
16366104c370SDaniel Vetter x += s - start;
16376104c370SDaniel Vetter start = s;
16386104c370SDaniel Vetter }
16396104c370SDaniel Vetter }
16406104c370SDaniel Vetter if (c == scr_readw(d)) {
16416104c370SDaniel Vetter if (s > start) {
16426104c370SDaniel Vetter fbcon_putcs(vc, start, s - start,
16436104c370SDaniel Vetter line, x);
16446104c370SDaniel Vetter x += s - start + 1;
16456104c370SDaniel Vetter start = s + 1;
16466104c370SDaniel Vetter } else {
16476104c370SDaniel Vetter x++;
16486104c370SDaniel Vetter start++;
16496104c370SDaniel Vetter }
16506104c370SDaniel Vetter }
16516104c370SDaniel Vetter scr_writew(c, d);
16526104c370SDaniel Vetter console_conditional_schedule();
16536104c370SDaniel Vetter s++;
16546104c370SDaniel Vetter d++;
16556104c370SDaniel Vetter } while (s < le);
16566104c370SDaniel Vetter if (s > start)
16576104c370SDaniel Vetter fbcon_putcs(vc, start, s - start, line, x);
16586104c370SDaniel Vetter console_conditional_schedule();
16596104c370SDaniel Vetter if (offset > 0)
16606104c370SDaniel Vetter line++;
16616104c370SDaniel Vetter else {
16626104c370SDaniel Vetter line--;
16636104c370SDaniel Vetter /* NOTE: We subtract two lines from these pointers */
16646104c370SDaniel Vetter s -= vc->vc_size_row;
16656104c370SDaniel Vetter d -= vc->vc_size_row;
16666104c370SDaniel Vetter }
16676104c370SDaniel Vetter }
16686104c370SDaniel Vetter }
16696104c370SDaniel 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)167068933313SDaniel Vetter static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
167168933313SDaniel Vetter int dy, int dx, int height, int width, u_int y_break)
167268933313SDaniel Vetter {
1673409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
167468933313SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
167568933313SDaniel Vetter u_int b;
167668933313SDaniel Vetter
167768933313SDaniel Vetter if (sy < y_break && sy + height > y_break) {
167868933313SDaniel Vetter b = y_break - sy;
167968933313SDaniel Vetter if (dy < sy) { /* Avoid trashing self */
168068933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
168168933313SDaniel Vetter y_break);
168268933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
168368933313SDaniel Vetter height - b, width, y_break);
168468933313SDaniel Vetter } else {
168568933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
168668933313SDaniel Vetter height - b, width, y_break);
168768933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
168868933313SDaniel Vetter y_break);
168968933313SDaniel Vetter }
169068933313SDaniel Vetter return;
169168933313SDaniel Vetter }
169268933313SDaniel Vetter
169368933313SDaniel Vetter if (dy < y_break && dy + height > y_break) {
169468933313SDaniel Vetter b = y_break - dy;
169568933313SDaniel Vetter if (dy < sy) { /* Avoid trashing self */
169668933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
169768933313SDaniel Vetter y_break);
169868933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
169968933313SDaniel Vetter height - b, width, y_break);
170068933313SDaniel Vetter } else {
170168933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
170268933313SDaniel Vetter height - b, width, y_break);
170368933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
170468933313SDaniel Vetter y_break);
170568933313SDaniel Vetter }
170668933313SDaniel Vetter return;
170768933313SDaniel Vetter }
170868933313SDaniel Vetter ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
170968933313SDaniel Vetter height, width);
171068933313SDaniel Vetter }
171168933313SDaniel Vetter
fbcon_bmove(struct vc_data * vc,int sy,int sx,int dy,int dx,int height,int width)171268933313SDaniel Vetter static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
171368933313SDaniel Vetter int height, int width)
171468933313SDaniel Vetter {
1715409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
171668933313SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
171768933313SDaniel Vetter
171868933313SDaniel Vetter if (fbcon_is_inactive(vc, info))
171968933313SDaniel Vetter return;
172068933313SDaniel Vetter
172168933313SDaniel Vetter if (!width || !height)
172268933313SDaniel Vetter return;
172368933313SDaniel Vetter
172468933313SDaniel Vetter /* Split blits that cross physical y_wrap case.
172568933313SDaniel Vetter * Pathological case involves 4 blits, better to use recursive
172668933313SDaniel Vetter * code rather than unrolled case
172768933313SDaniel Vetter *
172868933313SDaniel Vetter * Recursive invocations don't need to erase the cursor over and
172968933313SDaniel Vetter * over again, so we use fbcon_bmove_rec()
173068933313SDaniel Vetter */
173168933313SDaniel Vetter fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
173268933313SDaniel Vetter p->vrows - p->yscroll);
173368933313SDaniel Vetter }
173468933313SDaniel Vetter
fbcon_scroll(struct vc_data * vc,unsigned int t,unsigned int b,enum con_scroll dir,unsigned int count)17356104c370SDaniel Vetter static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
17366104c370SDaniel Vetter enum con_scroll dir, unsigned int count)
17376104c370SDaniel Vetter {
1738409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
173950233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
17401148836fSHelge Deller int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
17416104c370SDaniel Vetter
17426104c370SDaniel Vetter if (fbcon_is_inactive(vc, info))
17436104c370SDaniel Vetter return true;
17446104c370SDaniel Vetter
17456104c370SDaniel Vetter fbcon_cursor(vc, CM_ERASE);
17466104c370SDaniel Vetter
17476104c370SDaniel Vetter /*
17486104c370SDaniel Vetter * ++Geert: Only use ywrap/ypan if the console is in text mode
17496104c370SDaniel Vetter * ++Andrew: Only use ypan on hardware text mode when scrolling the
17506104c370SDaniel Vetter * whole screen (prevents flicker).
17516104c370SDaniel Vetter */
17526104c370SDaniel Vetter
17536104c370SDaniel Vetter switch (dir) {
17546104c370SDaniel Vetter case SM_UP:
17556104c370SDaniel Vetter if (count > vc->vc_rows) /* Maximum realistic size */
17566104c370SDaniel Vetter count = vc->vc_rows;
1757a3f781a9SHelge Deller switch (fb_scrollmode(p)) {
17581148836fSHelge Deller case SCROLL_MOVE:
17591148836fSHelge Deller fbcon_redraw_blit(vc, info, p, t, b - t - count,
17601148836fSHelge Deller count);
17611148836fSHelge Deller fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
17621148836fSHelge Deller scr_memsetw((unsigned short *) (vc->vc_origin +
17631148836fSHelge Deller vc->vc_size_row *
17641148836fSHelge Deller (b - count)),
17651148836fSHelge Deller vc->vc_video_erase_char,
17661148836fSHelge Deller vc->vc_size_row * count);
17671148836fSHelge Deller return true;
17681148836fSHelge Deller
17691148836fSHelge Deller case SCROLL_WRAP_MOVE:
17701148836fSHelge Deller if (b - t - count > 3 * vc->vc_rows >> 2) {
17711148836fSHelge Deller if (t > 0)
17721148836fSHelge Deller fbcon_bmove(vc, 0, 0, count, 0, t,
17731148836fSHelge Deller vc->vc_cols);
17741148836fSHelge Deller ywrap_up(vc, count);
17751148836fSHelge Deller if (vc->vc_rows - b > 0)
17761148836fSHelge Deller fbcon_bmove(vc, b - count, 0, b, 0,
17771148836fSHelge Deller vc->vc_rows - b,
17781148836fSHelge Deller vc->vc_cols);
17791148836fSHelge Deller } else if (info->flags & FBINFO_READS_FAST)
17801148836fSHelge Deller fbcon_bmove(vc, t + count, 0, t, 0,
17811148836fSHelge Deller b - t - count, vc->vc_cols);
17821148836fSHelge Deller else
17831148836fSHelge Deller goto redraw_up;
17841148836fSHelge Deller fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
17851148836fSHelge Deller break;
17861148836fSHelge Deller
17871148836fSHelge Deller case SCROLL_PAN_REDRAW:
17881148836fSHelge Deller if ((p->yscroll + count <=
17891148836fSHelge Deller 2 * (p->vrows - vc->vc_rows))
17901148836fSHelge Deller && ((!scroll_partial && (b - t == vc->vc_rows))
17911148836fSHelge Deller || (scroll_partial
17921148836fSHelge Deller && (b - t - count >
17931148836fSHelge Deller 3 * vc->vc_rows >> 2)))) {
17941148836fSHelge Deller if (t > 0)
17951148836fSHelge Deller fbcon_redraw_move(vc, p, 0, t, count);
17961148836fSHelge Deller ypan_up_redraw(vc, t, count);
17971148836fSHelge Deller if (vc->vc_rows - b > 0)
17981148836fSHelge Deller fbcon_redraw_move(vc, p, b,
17991148836fSHelge Deller vc->vc_rows - b, b);
18001148836fSHelge Deller } else
18011148836fSHelge Deller fbcon_redraw_move(vc, p, t + count, b - t - count, t);
18021148836fSHelge Deller fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18031148836fSHelge Deller break;
18041148836fSHelge Deller
18051148836fSHelge Deller case SCROLL_PAN_MOVE:
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_bmove(vc, 0, 0, count, 0, t,
18141148836fSHelge Deller vc->vc_cols);
18151148836fSHelge Deller ypan_up(vc, count);
18161148836fSHelge Deller if (vc->vc_rows - b > 0)
18171148836fSHelge Deller fbcon_bmove(vc, b - count, 0, b, 0,
18181148836fSHelge Deller vc->vc_rows - b,
18191148836fSHelge Deller vc->vc_cols);
18201148836fSHelge Deller } else if (info->flags & FBINFO_READS_FAST)
18211148836fSHelge Deller fbcon_bmove(vc, t + count, 0, t, 0,
18221148836fSHelge Deller b - t - count, vc->vc_cols);
18231148836fSHelge Deller else
18241148836fSHelge Deller goto redraw_up;
18251148836fSHelge Deller fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18261148836fSHelge Deller break;
18271148836fSHelge Deller
18281148836fSHelge Deller case SCROLL_REDRAW:
18291148836fSHelge Deller redraw_up:
1830933ab3a8SJiri Slaby (SUSE) fbcon_redraw(vc, t, b - t - count,
18316104c370SDaniel Vetter count * vc->vc_cols);
18326104c370SDaniel Vetter fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
18336104c370SDaniel Vetter scr_memsetw((unsigned short *) (vc->vc_origin +
18346104c370SDaniel Vetter vc->vc_size_row *
18356104c370SDaniel Vetter (b - count)),
18366104c370SDaniel Vetter vc->vc_video_erase_char,
18376104c370SDaniel Vetter vc->vc_size_row * count);
18386104c370SDaniel Vetter return true;
18391148836fSHelge Deller }
18401148836fSHelge Deller break;
18416104c370SDaniel Vetter
18426104c370SDaniel Vetter case SM_DOWN:
18436104c370SDaniel Vetter if (count > vc->vc_rows) /* Maximum realistic size */
18446104c370SDaniel Vetter count = vc->vc_rows;
1845a3f781a9SHelge Deller switch (fb_scrollmode(p)) {
18461148836fSHelge Deller case SCROLL_MOVE:
18471148836fSHelge Deller fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
18481148836fSHelge Deller -count);
18491148836fSHelge Deller fbcon_clear(vc, t, 0, count, vc->vc_cols);
18501148836fSHelge Deller scr_memsetw((unsigned short *) (vc->vc_origin +
18511148836fSHelge Deller vc->vc_size_row *
18521148836fSHelge Deller t),
18531148836fSHelge Deller vc->vc_video_erase_char,
18541148836fSHelge Deller vc->vc_size_row * count);
18551148836fSHelge Deller return true;
18561148836fSHelge Deller
18571148836fSHelge Deller case SCROLL_WRAP_MOVE:
18581148836fSHelge Deller if (b - t - count > 3 * vc->vc_rows >> 2) {
18591148836fSHelge Deller if (vc->vc_rows - b > 0)
18601148836fSHelge Deller fbcon_bmove(vc, b, 0, b - count, 0,
18611148836fSHelge Deller vc->vc_rows - b,
18621148836fSHelge Deller vc->vc_cols);
18631148836fSHelge Deller ywrap_down(vc, count);
18641148836fSHelge Deller if (t > 0)
18651148836fSHelge Deller fbcon_bmove(vc, count, 0, 0, 0, t,
18661148836fSHelge Deller vc->vc_cols);
18671148836fSHelge Deller } else if (info->flags & FBINFO_READS_FAST)
18681148836fSHelge Deller fbcon_bmove(vc, t, 0, t + count, 0,
18691148836fSHelge Deller b - t - count, vc->vc_cols);
18701148836fSHelge Deller else
18711148836fSHelge Deller goto redraw_down;
18721148836fSHelge Deller fbcon_clear(vc, t, 0, count, vc->vc_cols);
18731148836fSHelge Deller break;
18741148836fSHelge Deller
18751148836fSHelge Deller case SCROLL_PAN_MOVE:
18761148836fSHelge Deller if ((count - p->yscroll <= p->vrows - vc->vc_rows)
18771148836fSHelge Deller && ((!scroll_partial && (b - t == vc->vc_rows))
18781148836fSHelge Deller || (scroll_partial
18791148836fSHelge Deller && (b - t - count >
18801148836fSHelge Deller 3 * vc->vc_rows >> 2)))) {
18811148836fSHelge Deller if (vc->vc_rows - b > 0)
18821148836fSHelge Deller fbcon_bmove(vc, b, 0, b - count, 0,
18831148836fSHelge Deller vc->vc_rows - b,
18841148836fSHelge Deller vc->vc_cols);
18851148836fSHelge Deller ypan_down(vc, count);
18861148836fSHelge Deller if (t > 0)
18871148836fSHelge Deller fbcon_bmove(vc, count, 0, 0, 0, t,
18881148836fSHelge Deller vc->vc_cols);
18891148836fSHelge Deller } else if (info->flags & FBINFO_READS_FAST)
18901148836fSHelge Deller fbcon_bmove(vc, t, 0, t + count, 0,
18911148836fSHelge Deller b - t - count, vc->vc_cols);
18921148836fSHelge Deller else
18931148836fSHelge Deller goto redraw_down;
18941148836fSHelge Deller fbcon_clear(vc, t, 0, count, vc->vc_cols);
18951148836fSHelge Deller break;
18961148836fSHelge Deller
18971148836fSHelge Deller case SCROLL_PAN_REDRAW:
18981148836fSHelge Deller if ((count - p->yscroll <= p->vrows - vc->vc_rows)
18991148836fSHelge Deller && ((!scroll_partial && (b - t == vc->vc_rows))
19001148836fSHelge Deller || (scroll_partial
19011148836fSHelge Deller && (b - t - count >
19021148836fSHelge Deller 3 * vc->vc_rows >> 2)))) {
19031148836fSHelge Deller if (vc->vc_rows - b > 0)
19041148836fSHelge Deller fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
19051148836fSHelge Deller b - count);
19061148836fSHelge Deller ypan_down_redraw(vc, t, count);
19071148836fSHelge Deller if (t > 0)
19081148836fSHelge Deller fbcon_redraw_move(vc, p, count, t, 0);
19091148836fSHelge Deller } else
19101148836fSHelge Deller fbcon_redraw_move(vc, p, t, b - t - count, t + count);
19111148836fSHelge Deller fbcon_clear(vc, t, 0, count, vc->vc_cols);
19121148836fSHelge Deller break;
19131148836fSHelge Deller
19141148836fSHelge Deller case SCROLL_REDRAW:
19151148836fSHelge Deller redraw_down:
1916933ab3a8SJiri Slaby (SUSE) fbcon_redraw(vc, b - 1, b - t - count,
19176104c370SDaniel Vetter -count * vc->vc_cols);
19186104c370SDaniel Vetter fbcon_clear(vc, t, 0, count, vc->vc_cols);
19196104c370SDaniel Vetter scr_memsetw((unsigned short *) (vc->vc_origin +
19206104c370SDaniel Vetter vc->vc_size_row *
19216104c370SDaniel Vetter t),
19226104c370SDaniel Vetter vc->vc_video_erase_char,
19236104c370SDaniel Vetter vc->vc_size_row * count);
19246104c370SDaniel Vetter return true;
19256104c370SDaniel Vetter }
19261148836fSHelge Deller }
19276104c370SDaniel Vetter return false;
19286104c370SDaniel Vetter }
19296104c370SDaniel Vetter
19301148836fSHelge Deller
updatescrollmode_accel(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)1931a3f781a9SHelge Deller static void updatescrollmode_accel(struct fbcon_display *p,
19326104c370SDaniel Vetter struct fb_info *info,
19336104c370SDaniel Vetter struct vc_data *vc)
19346104c370SDaniel Vetter {
1935a3f781a9SHelge Deller #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
19366104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
193787ab9f6bSHelge Deller int cap = info->flags;
193887ab9f6bSHelge Deller u16 t = 0;
193987ab9f6bSHelge Deller int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
194087ab9f6bSHelge Deller info->fix.xpanstep);
194187ab9f6bSHelge Deller int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
19426104c370SDaniel Vetter int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
19436104c370SDaniel Vetter int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
19446104c370SDaniel Vetter info->var.xres_virtual);
194587ab9f6bSHelge Deller int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
194687ab9f6bSHelge Deller divides(ypan, vc->vc_font.height) && vyres > yres;
194787ab9f6bSHelge Deller int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
194887ab9f6bSHelge Deller divides(ywrap, vc->vc_font.height) &&
194987ab9f6bSHelge Deller divides(vc->vc_font.height, vyres) &&
195087ab9f6bSHelge Deller divides(vc->vc_font.height, yres);
195187ab9f6bSHelge Deller int reading_fast = cap & FBINFO_READS_FAST;
195287ab9f6bSHelge Deller int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
195387ab9f6bSHelge Deller !(cap & FBINFO_HWACCEL_DISABLED);
195487ab9f6bSHelge Deller int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
195587ab9f6bSHelge Deller !(cap & FBINFO_HWACCEL_DISABLED);
19566104c370SDaniel Vetter
195787ab9f6bSHelge Deller if (good_wrap || good_pan) {
195887ab9f6bSHelge Deller if (reading_fast || fast_copyarea)
195987ab9f6bSHelge Deller p->scrollmode = good_wrap ?
196087ab9f6bSHelge Deller SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
196187ab9f6bSHelge Deller else
196287ab9f6bSHelge Deller p->scrollmode = good_wrap ? SCROLL_REDRAW :
196387ab9f6bSHelge Deller SCROLL_PAN_REDRAW;
196487ab9f6bSHelge Deller } else {
196587ab9f6bSHelge Deller if (reading_fast || (fast_copyarea && !fast_imageblit))
196687ab9f6bSHelge Deller p->scrollmode = SCROLL_MOVE;
196787ab9f6bSHelge Deller else
196887ab9f6bSHelge Deller p->scrollmode = SCROLL_REDRAW;
196987ab9f6bSHelge Deller }
1970a3f781a9SHelge Deller #endif
1971a3f781a9SHelge Deller }
1972a3f781a9SHelge Deller
updatescrollmode(struct fbcon_display * p,struct fb_info * info,struct vc_data * vc)1973a3f781a9SHelge Deller static void updatescrollmode(struct fbcon_display *p,
1974a3f781a9SHelge Deller struct fb_info *info,
1975a3f781a9SHelge Deller struct vc_data *vc)
1976a3f781a9SHelge Deller {
1977a3f781a9SHelge Deller struct fbcon_ops *ops = info->fbcon_par;
1978a3f781a9SHelge Deller int fh = vc->vc_font.height;
1979a3f781a9SHelge Deller int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1980a3f781a9SHelge Deller int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1981a3f781a9SHelge Deller info->var.xres_virtual);
1982a3f781a9SHelge Deller
1983a3f781a9SHelge Deller p->vrows = vyres/fh;
1984a3f781a9SHelge Deller if (yres > (fh * (vc->vc_rows + 1)))
1985a3f781a9SHelge Deller p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1986a3f781a9SHelge Deller if ((yres % fh) && (vyres % fh < yres % fh))
1987a3f781a9SHelge Deller p->vrows--;
1988a3f781a9SHelge Deller
1989a3f781a9SHelge Deller /* update scrollmode in case hardware acceleration is used */
1990a3f781a9SHelge Deller updatescrollmode_accel(p, info, vc);
19916104c370SDaniel Vetter }
19926104c370SDaniel Vetter
199339b3cffbSGeorge Kennedy #define PITCH(w) (((w) + 7) >> 3)
199439b3cffbSGeorge Kennedy #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
199539b3cffbSGeorge Kennedy
fbcon_resize(struct vc_data * vc,unsigned int width,unsigned int height,unsigned int user)19966104c370SDaniel Vetter static int fbcon_resize(struct vc_data *vc, unsigned int width,
19976104c370SDaniel Vetter unsigned int height, unsigned int user)
19986104c370SDaniel Vetter {
1999409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
20006104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
200150233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
20026104c370SDaniel Vetter struct fb_var_screeninfo var = info->var;
20036104c370SDaniel Vetter int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
20046104c370SDaniel Vetter
2005ec0972adSTetsuo Handa if (p->userfont && FNTSIZE(vc->vc_font.data)) {
200639b3cffbSGeorge Kennedy int size;
200739b3cffbSGeorge Kennedy int pitch = PITCH(vc->vc_font.width);
200839b3cffbSGeorge Kennedy
200939b3cffbSGeorge Kennedy /*
201039b3cffbSGeorge Kennedy * If user font, ensure that a possible change to user font
201139b3cffbSGeorge Kennedy * height or width will not allow a font data out-of-bounds access.
201239b3cffbSGeorge Kennedy * NOTE: must use original charcount in calculation as font
201339b3cffbSGeorge Kennedy * charcount can change and cannot be used to determine the
201439b3cffbSGeorge Kennedy * font data allocated size.
201539b3cffbSGeorge Kennedy */
201639b3cffbSGeorge Kennedy if (pitch <= 0)
201739b3cffbSGeorge Kennedy return -EINVAL;
2018a1ac250aSPeilin Ye size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount);
201939b3cffbSGeorge Kennedy if (size > FNTSIZE(vc->vc_font.data))
202039b3cffbSGeorge Kennedy return -EINVAL;
202139b3cffbSGeorge Kennedy }
202239b3cffbSGeorge Kennedy
20236104c370SDaniel Vetter virt_w = FBCON_SWAP(ops->rotate, width, height);
20246104c370SDaniel Vetter virt_h = FBCON_SWAP(ops->rotate, height, width);
20256104c370SDaniel Vetter virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
20266104c370SDaniel Vetter vc->vc_font.height);
20276104c370SDaniel Vetter virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
20286104c370SDaniel Vetter vc->vc_font.width);
20296104c370SDaniel Vetter var.xres = virt_w * virt_fw;
20306104c370SDaniel Vetter var.yres = virt_h * virt_fh;
20316104c370SDaniel Vetter x_diff = info->var.xres - var.xres;
20326104c370SDaniel Vetter y_diff = info->var.yres - var.yres;
20336104c370SDaniel Vetter if (x_diff < 0 || x_diff > virt_fw ||
20346104c370SDaniel Vetter y_diff < 0 || y_diff > virt_fh) {
20356104c370SDaniel Vetter const struct fb_videomode *mode;
20366104c370SDaniel Vetter
2037b1cba76dSSam Ravnborg pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
20386104c370SDaniel Vetter mode = fb_find_best_mode(&var, &info->modelist);
20396104c370SDaniel Vetter if (mode == NULL)
20406104c370SDaniel Vetter return -EINVAL;
20416104c370SDaniel Vetter display_to_var(&var, p);
20426104c370SDaniel Vetter fb_videomode_to_var(&var, mode);
20436104c370SDaniel Vetter
20446104c370SDaniel Vetter if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
20456104c370SDaniel Vetter return -EINVAL;
20466104c370SDaniel Vetter
2047b1cba76dSSam Ravnborg pr_debug("resize now %ix%i\n", var.xres, var.yres);
2048ffb324e6STetsuo Handa if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
20496104c370SDaniel Vetter var.activate = FB_ACTIVATE_NOW |
20506104c370SDaniel Vetter FB_ACTIVATE_FORCE;
20516104c370SDaniel Vetter fb_set_var(info, &var);
20526104c370SDaniel Vetter }
20536104c370SDaniel Vetter var_to_display(p, &info->var, info);
20546104c370SDaniel Vetter ops->var = info->var;
20556104c370SDaniel Vetter }
20566104c370SDaniel Vetter updatescrollmode(p, info, vc);
20576104c370SDaniel Vetter return 0;
20586104c370SDaniel Vetter }
20596104c370SDaniel Vetter
fbcon_switch(struct vc_data * vc)20606104c370SDaniel Vetter static int fbcon_switch(struct vc_data *vc)
20616104c370SDaniel Vetter {
20626104c370SDaniel Vetter struct fb_info *info, *old_info = NULL;
20636104c370SDaniel Vetter struct fbcon_ops *ops;
206450233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
20656104c370SDaniel Vetter struct fb_var_screeninfo var;
2066a1ac250aSPeilin Ye int i, ret, prev_console;
20676104c370SDaniel Vetter
2068409d6c95SDaniel Vetter info = fbcon_info_from_console(vc->vc_num);
20696104c370SDaniel Vetter ops = info->fbcon_par;
20706104c370SDaniel Vetter
20716104c370SDaniel Vetter if (logo_shown >= 0) {
20726104c370SDaniel Vetter struct vc_data *conp2 = vc_cons[logo_shown].d;
20736104c370SDaniel Vetter
20746104c370SDaniel Vetter if (conp2->vc_top == logo_lines
20756104c370SDaniel Vetter && conp2->vc_bottom == conp2->vc_rows)
20766104c370SDaniel Vetter conp2->vc_top = 0;
20776104c370SDaniel Vetter logo_shown = FBCON_LOGO_CANSHOW;
20786104c370SDaniel Vetter }
20796104c370SDaniel Vetter
20806104c370SDaniel Vetter prev_console = ops->currcon;
20816104c370SDaniel Vetter if (prev_console != -1)
2082409d6c95SDaniel Vetter old_info = fbcon_info_from_console(prev_console);
20836104c370SDaniel Vetter /*
20846104c370SDaniel Vetter * FIXME: If we have multiple fbdev's loaded, we need to
20856104c370SDaniel Vetter * update all info->currcon. Perhaps, we can place this
20866104c370SDaniel Vetter * in a centralized structure, but this might break some
20876104c370SDaniel Vetter * drivers.
20886104c370SDaniel Vetter *
20896104c370SDaniel Vetter * info->currcon = vc->vc_num;
20906104c370SDaniel Vetter */
2091efc3acbcSDaniel Vetter fbcon_for_each_registered_fb(i) {
2092efc3acbcSDaniel Vetter if (fbcon_registered_fb[i]->fbcon_par) {
2093efc3acbcSDaniel Vetter struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;
20946104c370SDaniel Vetter
20956104c370SDaniel Vetter o->currcon = vc->vc_num;
20966104c370SDaniel Vetter }
20976104c370SDaniel Vetter }
20986104c370SDaniel Vetter memset(&var, 0, sizeof(struct fb_var_screeninfo));
20996104c370SDaniel Vetter display_to_var(&var, p);
21006104c370SDaniel Vetter var.activate = FB_ACTIVATE_NOW;
21016104c370SDaniel Vetter
21026104c370SDaniel Vetter /*
21036104c370SDaniel Vetter * make sure we don't unnecessarily trip the memcmp()
21046104c370SDaniel Vetter * in fb_set_var()
21056104c370SDaniel Vetter */
21066104c370SDaniel Vetter info->var.activate = var.activate;
21076104c370SDaniel Vetter var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
21086104c370SDaniel Vetter fb_set_var(info, &var);
21096104c370SDaniel Vetter ops->var = info->var;
21106104c370SDaniel Vetter
21116104c370SDaniel Vetter if (old_info != NULL && (old_info != info ||
21126104c370SDaniel Vetter info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
21136104c370SDaniel Vetter if (info->fbops->fb_set_par) {
21146104c370SDaniel Vetter ret = info->fbops->fb_set_par(info);
21156104c370SDaniel Vetter
21166104c370SDaniel Vetter if (ret)
21176104c370SDaniel Vetter printk(KERN_ERR "fbcon_switch: detected "
21186104c370SDaniel Vetter "unhandled fb_set_par error, "
21196104c370SDaniel Vetter "error code %d\n", ret);
21206104c370SDaniel Vetter }
21216104c370SDaniel Vetter
21226104c370SDaniel Vetter if (old_info != info)
21233b0fb6abSDaniel Vetter fbcon_del_cursor_work(old_info);
21246104c370SDaniel Vetter }
21256104c370SDaniel Vetter
21266104c370SDaniel Vetter if (fbcon_is_inactive(vc, info) ||
21276104c370SDaniel Vetter ops->blank_state != FB_BLANK_UNBLANK)
21283b0fb6abSDaniel Vetter fbcon_del_cursor_work(info);
21296104c370SDaniel Vetter else
21303b0fb6abSDaniel Vetter fbcon_add_cursor_work(info);
21316104c370SDaniel Vetter
21326104c370SDaniel Vetter set_blitting_type(vc, info);
21336104c370SDaniel Vetter ops->cursor_reset = 1;
21346104c370SDaniel Vetter
21356104c370SDaniel Vetter if (ops->rotate_font && ops->rotate_font(info, vc)) {
21366104c370SDaniel Vetter ops->rotate = FB_ROTATE_UR;
21376104c370SDaniel Vetter set_blitting_type(vc, info);
21386104c370SDaniel Vetter }
21396104c370SDaniel Vetter
21406104c370SDaniel Vetter vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
21416104c370SDaniel Vetter vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
21426104c370SDaniel Vetter
2143a1ac250aSPeilin Ye if (vc->vc_font.charcount > 256)
21446104c370SDaniel Vetter vc->vc_complement_mask <<= 1;
21456104c370SDaniel Vetter
21466104c370SDaniel Vetter updatescrollmode(p, info, vc);
21476104c370SDaniel Vetter
2148a3f781a9SHelge Deller switch (fb_scrollmode(p)) {
21491148836fSHelge Deller case SCROLL_WRAP_MOVE:
21501148836fSHelge Deller scrollback_phys_max = p->vrows - vc->vc_rows;
21511148836fSHelge Deller break;
21521148836fSHelge Deller case SCROLL_PAN_MOVE:
21531148836fSHelge Deller case SCROLL_PAN_REDRAW:
21541148836fSHelge Deller scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
21551148836fSHelge Deller if (scrollback_phys_max < 0)
21566104c370SDaniel Vetter scrollback_phys_max = 0;
21571148836fSHelge Deller break;
21581148836fSHelge Deller default:
21591148836fSHelge Deller scrollback_phys_max = 0;
21601148836fSHelge Deller break;
21611148836fSHelge Deller }
21621148836fSHelge Deller
21636104c370SDaniel Vetter scrollback_max = 0;
21646104c370SDaniel Vetter scrollback_current = 0;
21656104c370SDaniel Vetter
21666104c370SDaniel Vetter if (!fbcon_is_inactive(vc, info)) {
21676104c370SDaniel Vetter ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
21686104c370SDaniel Vetter ops->update_start(info);
21696104c370SDaniel Vetter }
21706104c370SDaniel Vetter
21716104c370SDaniel Vetter fbcon_set_palette(vc, color_table);
21726104c370SDaniel Vetter fbcon_clear_margins(vc, 0);
21736104c370SDaniel Vetter
21746104c370SDaniel Vetter if (logo_shown == FBCON_LOGO_DRAW) {
21756104c370SDaniel Vetter
21766104c370SDaniel Vetter logo_shown = fg_console;
21776104c370SDaniel Vetter fb_show_logo(info, ops->rotate);
21786104c370SDaniel Vetter update_region(vc,
21796104c370SDaniel Vetter vc->vc_origin + vc->vc_size_row * vc->vc_top,
21806104c370SDaniel Vetter vc->vc_size_row * (vc->vc_bottom -
21816104c370SDaniel Vetter vc->vc_top) / 2);
21826104c370SDaniel Vetter return 0;
21836104c370SDaniel Vetter }
21846104c370SDaniel Vetter return 1;
21856104c370SDaniel Vetter }
21866104c370SDaniel Vetter
fbcon_generic_blank(struct vc_data * vc,struct fb_info * info,int blank)21876104c370SDaniel Vetter static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
21886104c370SDaniel Vetter int blank)
21896104c370SDaniel Vetter {
21906104c370SDaniel Vetter if (blank) {
21916104c370SDaniel Vetter unsigned short charmask = vc->vc_hi_font_mask ?
21926104c370SDaniel Vetter 0x1ff : 0xff;
21936104c370SDaniel Vetter unsigned short oldc;
21946104c370SDaniel Vetter
21956104c370SDaniel Vetter oldc = vc->vc_video_erase_char;
21966104c370SDaniel Vetter vc->vc_video_erase_char &= charmask;
21976104c370SDaniel Vetter fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
21986104c370SDaniel Vetter vc->vc_video_erase_char = oldc;
21996104c370SDaniel Vetter }
22006104c370SDaniel Vetter }
22016104c370SDaniel Vetter
fbcon_blank(struct vc_data * vc,int blank,int mode_switch)22026104c370SDaniel Vetter static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
22036104c370SDaniel Vetter {
2204409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22056104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
22066104c370SDaniel Vetter
22076104c370SDaniel Vetter if (mode_switch) {
22086104c370SDaniel Vetter struct fb_var_screeninfo var = info->var;
22096104c370SDaniel Vetter
22106104c370SDaniel Vetter ops->graphics = 1;
22116104c370SDaniel Vetter
22126104c370SDaniel Vetter if (!blank) {
2213dc5bdb68SDaniel Vetter var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2214dc5bdb68SDaniel Vetter FB_ACTIVATE_KD_TEXT;
22156104c370SDaniel Vetter fb_set_var(info, &var);
22166104c370SDaniel Vetter ops->graphics = 0;
22176104c370SDaniel Vetter ops->var = info->var;
22186104c370SDaniel Vetter }
22196104c370SDaniel Vetter }
22206104c370SDaniel Vetter
22216104c370SDaniel Vetter if (!fbcon_is_inactive(vc, info)) {
22226104c370SDaniel Vetter if (ops->blank_state != blank) {
22236104c370SDaniel Vetter ops->blank_state = blank;
22246104c370SDaniel Vetter fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
22256104c370SDaniel Vetter ops->cursor_flash = (!blank);
22266104c370SDaniel Vetter
22276104c370SDaniel Vetter if (fb_blank(info, blank))
22286104c370SDaniel Vetter fbcon_generic_blank(vc, info, blank);
22296104c370SDaniel Vetter }
22306104c370SDaniel Vetter
22316104c370SDaniel Vetter if (!blank)
22326104c370SDaniel Vetter update_screen(vc);
22336104c370SDaniel Vetter }
22346104c370SDaniel Vetter
22356104c370SDaniel Vetter if (mode_switch || fbcon_is_inactive(vc, info) ||
22366104c370SDaniel Vetter ops->blank_state != FB_BLANK_UNBLANK)
22373b0fb6abSDaniel Vetter fbcon_del_cursor_work(info);
22386104c370SDaniel Vetter else
22393b0fb6abSDaniel Vetter fbcon_add_cursor_work(info);
22406104c370SDaniel Vetter
22416104c370SDaniel Vetter return 0;
22426104c370SDaniel Vetter }
22436104c370SDaniel Vetter
fbcon_debug_enter(struct vc_data * vc)22446104c370SDaniel Vetter static int fbcon_debug_enter(struct vc_data *vc)
22456104c370SDaniel Vetter {
2246409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22476104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
22486104c370SDaniel Vetter
22496104c370SDaniel Vetter ops->save_graphics = ops->graphics;
22506104c370SDaniel Vetter ops->graphics = 0;
22516104c370SDaniel Vetter if (info->fbops->fb_debug_enter)
22526104c370SDaniel Vetter info->fbops->fb_debug_enter(info);
22536104c370SDaniel Vetter fbcon_set_palette(vc, color_table);
22546104c370SDaniel Vetter return 0;
22556104c370SDaniel Vetter }
22566104c370SDaniel Vetter
fbcon_debug_leave(struct vc_data * vc)22576104c370SDaniel Vetter static int fbcon_debug_leave(struct vc_data *vc)
22586104c370SDaniel Vetter {
2259409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
22606104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
22616104c370SDaniel Vetter
22626104c370SDaniel Vetter ops->graphics = ops->save_graphics;
22636104c370SDaniel Vetter if (info->fbops->fb_debug_leave)
22646104c370SDaniel Vetter info->fbops->fb_debug_leave(info);
22656104c370SDaniel Vetter return 0;
22666104c370SDaniel Vetter }
22676104c370SDaniel Vetter
fbcon_get_font(struct vc_data * vc,struct console_font * font,unsigned int vpitch)2268ffc1e089SSamuel Thibault static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch)
22696104c370SDaniel Vetter {
22706104c370SDaniel Vetter u8 *fontdata = vc->vc_font.data;
22716104c370SDaniel Vetter u8 *data = font->data;
22726104c370SDaniel Vetter int i, j;
22736104c370SDaniel Vetter
22746104c370SDaniel Vetter font->width = vc->vc_font.width;
22756104c370SDaniel Vetter font->height = vc->vc_font.height;
227605e2600cSSamuel Thibault if (font->height > vpitch)
227705e2600cSSamuel Thibault return -ENOSPC;
22786104c370SDaniel Vetter font->charcount = vc->vc_hi_font_mask ? 512 : 256;
22796104c370SDaniel Vetter if (!font->data)
22806104c370SDaniel Vetter return 0;
22816104c370SDaniel Vetter
22826104c370SDaniel Vetter if (font->width <= 8) {
22836104c370SDaniel Vetter j = vc->vc_font.height;
22845af08640SPeilin Ye if (font->charcount * j > FNTSIZE(fontdata))
22855af08640SPeilin Ye return -EINVAL;
22865af08640SPeilin Ye
22876104c370SDaniel Vetter for (i = 0; i < font->charcount; i++) {
22886104c370SDaniel Vetter memcpy(data, fontdata, j);
2289ffc1e089SSamuel Thibault memset(data + j, 0, vpitch - j);
2290ffc1e089SSamuel Thibault data += vpitch;
22916104c370SDaniel Vetter fontdata += j;
22926104c370SDaniel Vetter }
22936104c370SDaniel Vetter } else if (font->width <= 16) {
22946104c370SDaniel Vetter j = vc->vc_font.height * 2;
22955af08640SPeilin Ye if (font->charcount * j > FNTSIZE(fontdata))
22965af08640SPeilin Ye return -EINVAL;
22975af08640SPeilin Ye
22986104c370SDaniel Vetter for (i = 0; i < font->charcount; i++) {
22996104c370SDaniel Vetter memcpy(data, fontdata, j);
2300ffc1e089SSamuel Thibault memset(data + j, 0, 2*vpitch - j);
2301ffc1e089SSamuel Thibault data += 2*vpitch;
23026104c370SDaniel Vetter fontdata += j;
23036104c370SDaniel Vetter }
23046104c370SDaniel Vetter } else if (font->width <= 24) {
23055af08640SPeilin Ye if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
23065af08640SPeilin Ye return -EINVAL;
23075af08640SPeilin Ye
23086104c370SDaniel Vetter for (i = 0; i < font->charcount; i++) {
23096104c370SDaniel Vetter for (j = 0; j < vc->vc_font.height; j++) {
23106104c370SDaniel Vetter *data++ = fontdata[0];
23116104c370SDaniel Vetter *data++ = fontdata[1];
23126104c370SDaniel Vetter *data++ = fontdata[2];
23136104c370SDaniel Vetter fontdata += sizeof(u32);
23146104c370SDaniel Vetter }
2315ffc1e089SSamuel Thibault memset(data, 0, 3 * (vpitch - j));
2316ffc1e089SSamuel Thibault data += 3 * (vpitch - j);
23176104c370SDaniel Vetter }
23186104c370SDaniel Vetter } else {
23196104c370SDaniel Vetter j = vc->vc_font.height * 4;
23205af08640SPeilin Ye if (font->charcount * j > FNTSIZE(fontdata))
23215af08640SPeilin Ye return -EINVAL;
23225af08640SPeilin Ye
23236104c370SDaniel Vetter for (i = 0; i < font->charcount; i++) {
23246104c370SDaniel Vetter memcpy(data, fontdata, j);
2325ffc1e089SSamuel Thibault memset(data + j, 0, 4 * vpitch - j);
2326ffc1e089SSamuel Thibault data += 4 * vpitch;
23276104c370SDaniel Vetter fontdata += j;
23286104c370SDaniel Vetter }
23296104c370SDaniel Vetter }
23306104c370SDaniel Vetter return 0;
23316104c370SDaniel Vetter }
23326104c370SDaniel Vetter
23336104c370SDaniel Vetter /* set/clear vc_hi_font_mask and update vc attrs accordingly */
set_vc_hi_font(struct vc_data * vc,bool set)23346104c370SDaniel Vetter static void set_vc_hi_font(struct vc_data *vc, bool set)
23356104c370SDaniel Vetter {
23366104c370SDaniel Vetter if (!set) {
23376104c370SDaniel Vetter vc->vc_hi_font_mask = 0;
23386104c370SDaniel Vetter if (vc->vc_can_do_color) {
23396104c370SDaniel Vetter vc->vc_complement_mask >>= 1;
23406104c370SDaniel Vetter vc->vc_s_complement_mask >>= 1;
23416104c370SDaniel Vetter }
23426104c370SDaniel Vetter
23436104c370SDaniel Vetter /* ++Edmund: reorder the attribute bits */
23446104c370SDaniel Vetter if (vc->vc_can_do_color) {
23456104c370SDaniel Vetter unsigned short *cp =
23466104c370SDaniel Vetter (unsigned short *) vc->vc_origin;
23476104c370SDaniel Vetter int count = vc->vc_screenbuf_size / 2;
23486104c370SDaniel Vetter unsigned short c;
23496104c370SDaniel Vetter for (; count > 0; count--, cp++) {
23506104c370SDaniel Vetter c = scr_readw(cp);
23516104c370SDaniel Vetter scr_writew(((c & 0xfe00) >> 1) |
23526104c370SDaniel Vetter (c & 0xff), cp);
23536104c370SDaniel Vetter }
23546104c370SDaniel Vetter c = vc->vc_video_erase_char;
23556104c370SDaniel Vetter vc->vc_video_erase_char =
23566104c370SDaniel Vetter ((c & 0xfe00) >> 1) | (c & 0xff);
23576104c370SDaniel Vetter vc->vc_attr >>= 1;
23586104c370SDaniel Vetter }
23596104c370SDaniel Vetter } else {
23606104c370SDaniel Vetter vc->vc_hi_font_mask = 0x100;
23616104c370SDaniel Vetter if (vc->vc_can_do_color) {
23626104c370SDaniel Vetter vc->vc_complement_mask <<= 1;
23636104c370SDaniel Vetter vc->vc_s_complement_mask <<= 1;
23646104c370SDaniel Vetter }
23656104c370SDaniel Vetter
23666104c370SDaniel Vetter /* ++Edmund: reorder the attribute bits */
23676104c370SDaniel Vetter {
23686104c370SDaniel Vetter unsigned short *cp =
23696104c370SDaniel Vetter (unsigned short *) vc->vc_origin;
23706104c370SDaniel Vetter int count = vc->vc_screenbuf_size / 2;
23716104c370SDaniel Vetter unsigned short c;
23726104c370SDaniel Vetter for (; count > 0; count--, cp++) {
23736104c370SDaniel Vetter unsigned short newc;
23746104c370SDaniel Vetter c = scr_readw(cp);
23756104c370SDaniel Vetter if (vc->vc_can_do_color)
23766104c370SDaniel Vetter newc =
23776104c370SDaniel Vetter ((c & 0xff00) << 1) | (c &
23786104c370SDaniel Vetter 0xff);
23796104c370SDaniel Vetter else
23806104c370SDaniel Vetter newc = c & ~0x100;
23816104c370SDaniel Vetter scr_writew(newc, cp);
23826104c370SDaniel Vetter }
23836104c370SDaniel Vetter c = vc->vc_video_erase_char;
23846104c370SDaniel Vetter if (vc->vc_can_do_color) {
23856104c370SDaniel Vetter vc->vc_video_erase_char =
23866104c370SDaniel Vetter ((c & 0xff00) << 1) | (c & 0xff);
23876104c370SDaniel Vetter vc->vc_attr <<= 1;
23886104c370SDaniel Vetter } else
23896104c370SDaniel Vetter vc->vc_video_erase_char = c & ~0x100;
23906104c370SDaniel Vetter }
23916104c370SDaniel Vetter }
23926104c370SDaniel Vetter }
23936104c370SDaniel Vetter
fbcon_do_set_font(struct vc_data * vc,int w,int h,int charcount,const u8 * data,int userfont)2394a1ac250aSPeilin Ye static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
23956104c370SDaniel Vetter const u8 * data, int userfont)
23966104c370SDaniel Vetter {
2397409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
23986104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
239950233393SDaniel Vetter struct fbcon_display *p = &fb_display[vc->vc_num];
2400a5a92303SShigeru Yoshida int resize, ret, old_userfont, old_width, old_height, old_charcount;
2401*73a6bd68SJiri Slaby (SUSE) u8 *old_data = vc->vc_font.data;
24026104c370SDaniel Vetter
24036104c370SDaniel Vetter resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
24046104c370SDaniel Vetter vc->vc_font.data = (void *)(p->fontdata = data);
2405a5a92303SShigeru Yoshida old_userfont = p->userfont;
24066104c370SDaniel Vetter if ((p->userfont = userfont))
24076104c370SDaniel Vetter REFCOUNT(data)++;
2408a5a92303SShigeru Yoshida
2409a5a92303SShigeru Yoshida old_width = vc->vc_font.width;
2410a5a92303SShigeru Yoshida old_height = vc->vc_font.height;
2411a5a92303SShigeru Yoshida old_charcount = vc->vc_font.charcount;
2412a5a92303SShigeru Yoshida
24136104c370SDaniel Vetter vc->vc_font.width = w;
24146104c370SDaniel Vetter vc->vc_font.height = h;
2415a1ac250aSPeilin Ye vc->vc_font.charcount = charcount;
2416a1ac250aSPeilin Ye if (vc->vc_hi_font_mask && charcount == 256)
24176104c370SDaniel Vetter set_vc_hi_font(vc, false);
2418a1ac250aSPeilin Ye else if (!vc->vc_hi_font_mask && charcount == 512)
24196104c370SDaniel Vetter set_vc_hi_font(vc, true);
24206104c370SDaniel Vetter
24216104c370SDaniel Vetter if (resize) {
24226104c370SDaniel Vetter int cols, rows;
24236104c370SDaniel Vetter
24246104c370SDaniel Vetter cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
24256104c370SDaniel Vetter rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
24266104c370SDaniel Vetter cols /= w;
24276104c370SDaniel Vetter rows /= h;
2428a5a92303SShigeru Yoshida ret = vc_resize(vc, cols, rows);
2429a5a92303SShigeru Yoshida if (ret)
2430a5a92303SShigeru Yoshida goto err_out;
24316104c370SDaniel Vetter } else if (con_is_visible(vc)
24326104c370SDaniel Vetter && vc->vc_mode == KD_TEXT) {
24336104c370SDaniel Vetter fbcon_clear_margins(vc, 0);
24346104c370SDaniel Vetter update_screen(vc);
24356104c370SDaniel Vetter }
24366104c370SDaniel Vetter
2437*73a6bd68SJiri Slaby (SUSE) if (old_userfont && (--REFCOUNT(old_data) == 0))
24386104c370SDaniel Vetter kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
24396104c370SDaniel Vetter return 0;
2440a5a92303SShigeru Yoshida
2441a5a92303SShigeru Yoshida err_out:
2442a5a92303SShigeru Yoshida p->fontdata = old_data;
2443*73a6bd68SJiri Slaby (SUSE) vc->vc_font.data = old_data;
2444a5a92303SShigeru Yoshida
2445a5a92303SShigeru Yoshida if (userfont) {
2446a5a92303SShigeru Yoshida p->userfont = old_userfont;
24473c3bfb85STetsuo Handa if (--REFCOUNT(data) == 0)
24483c3bfb85STetsuo Handa kfree(data - FONT_EXTRA_WORDS * sizeof(int));
2449a5a92303SShigeru Yoshida }
2450a5a92303SShigeru Yoshida
2451a5a92303SShigeru Yoshida vc->vc_font.width = old_width;
2452a5a92303SShigeru Yoshida vc->vc_font.height = old_height;
2453a5a92303SShigeru Yoshida vc->vc_font.charcount = old_charcount;
2454a5a92303SShigeru Yoshida
2455a5a92303SShigeru Yoshida return ret;
24566104c370SDaniel Vetter }
24576104c370SDaniel Vetter
24586104c370SDaniel Vetter /*
2459ffc1e089SSamuel Thibault * User asked to set font; we are guaranteed that charcount does not exceed 512
2460ffc1e089SSamuel Thibault * but lets not assume that, since charcount of 512 is small for unicode support.
24616104c370SDaniel Vetter */
24626104c370SDaniel Vetter
fbcon_set_font(struct vc_data * vc,struct console_font * font,unsigned int vpitch,unsigned int flags)2463c396a5bfSKees Cook static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
2464ffc1e089SSamuel Thibault unsigned int vpitch, unsigned int flags)
24656104c370SDaniel Vetter {
2466409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
24676104c370SDaniel Vetter unsigned charcount = font->charcount;
24686104c370SDaniel Vetter int w = font->width;
24696104c370SDaniel Vetter int h = font->height;
24706104c370SDaniel Vetter int size;
24716104c370SDaniel Vetter int i, csum;
24726104c370SDaniel Vetter u8 *new_data, *data = font->data;
247339b3cffbSGeorge Kennedy int pitch = PITCH(font->width);
24746104c370SDaniel Vetter
24756104c370SDaniel Vetter /* Is there a reason why fbconsole couldn't handle any charcount >256?
24766104c370SDaniel Vetter * If not this check should be changed to charcount < 256 */
24776104c370SDaniel Vetter if (charcount != 256 && charcount != 512)
24786104c370SDaniel Vetter return -EINVAL;
24796104c370SDaniel Vetter
248065a01e60SHelge Deller /* font bigger than screen resolution ? */
248165a01e60SHelge Deller if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) ||
248265a01e60SHelge Deller h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
248365a01e60SHelge Deller return -EINVAL;
248465a01e60SHelge Deller
24852b09d5d3SSamuel Thibault if (font->width > 32 || font->height > 32)
24862b09d5d3SSamuel Thibault return -EINVAL;
24872b09d5d3SSamuel Thibault
24886104c370SDaniel Vetter /* Make sure drawing engine can handle the font */
24892b09d5d3SSamuel Thibault if (!(info->pixmap.blit_x & BIT(font->width - 1)) ||
24902b09d5d3SSamuel Thibault !(info->pixmap.blit_y & BIT(font->height - 1)))
24916104c370SDaniel Vetter return -EINVAL;
24926104c370SDaniel Vetter
24936104c370SDaniel Vetter /* Make sure driver can handle the font length */
24946104c370SDaniel Vetter if (fbcon_invalid_charcount(info, charcount))
24956104c370SDaniel Vetter return -EINVAL;
24966104c370SDaniel Vetter
249739b3cffbSGeorge Kennedy size = CALC_FONTSZ(h, pitch, charcount);
24986104c370SDaniel Vetter
24996104c370SDaniel Vetter new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
25006104c370SDaniel Vetter
25016104c370SDaniel Vetter if (!new_data)
25026104c370SDaniel Vetter return -ENOMEM;
25036104c370SDaniel Vetter
2504a1ac250aSPeilin Ye memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int));
2505a1ac250aSPeilin Ye
25066104c370SDaniel Vetter new_data += FONT_EXTRA_WORDS * sizeof(int);
25076104c370SDaniel Vetter FNTSIZE(new_data) = size;
25086104c370SDaniel Vetter REFCOUNT(new_data) = 0; /* usage counter */
25096104c370SDaniel Vetter for (i=0; i< charcount; i++) {
2510ffc1e089SSamuel Thibault memcpy(new_data + i*h*pitch, data + i*vpitch*pitch, h*pitch);
25116104c370SDaniel Vetter }
25126104c370SDaniel Vetter
25136104c370SDaniel Vetter /* Since linux has a nice crc32 function use it for counting font
25146104c370SDaniel Vetter * checksums. */
25156104c370SDaniel Vetter csum = crc32(0, new_data, size);
25166104c370SDaniel Vetter
25176104c370SDaniel Vetter FNTSUM(new_data) = csum;
25186104c370SDaniel Vetter /* Check if the same font is on some other console already */
25196104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
25206104c370SDaniel Vetter struct vc_data *tmp = vc_cons[i].d;
25216104c370SDaniel Vetter
25226104c370SDaniel Vetter if (fb_display[i].userfont &&
25236104c370SDaniel Vetter fb_display[i].fontdata &&
25246104c370SDaniel Vetter FNTSUM(fb_display[i].fontdata) == csum &&
25256104c370SDaniel Vetter FNTSIZE(fb_display[i].fontdata) == size &&
25266104c370SDaniel Vetter tmp->vc_font.width == w &&
25276104c370SDaniel Vetter !memcmp(fb_display[i].fontdata, new_data, size)) {
25286104c370SDaniel Vetter kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
25296104c370SDaniel Vetter new_data = (u8 *)fb_display[i].fontdata;
25306104c370SDaniel Vetter break;
25316104c370SDaniel Vetter }
25326104c370SDaniel Vetter }
2533a1ac250aSPeilin Ye return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
25346104c370SDaniel Vetter }
25356104c370SDaniel Vetter
fbcon_set_def_font(struct vc_data * vc,struct console_font * font,char * name)25366104c370SDaniel Vetter static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
25376104c370SDaniel Vetter {
2538409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
25396104c370SDaniel Vetter const struct font_desc *f;
25406104c370SDaniel Vetter
25416104c370SDaniel Vetter if (!name)
25426104c370SDaniel Vetter f = get_default_font(info->var.xres, info->var.yres,
25436104c370SDaniel Vetter info->pixmap.blit_x, info->pixmap.blit_y);
25446104c370SDaniel Vetter else if (!(f = find_font(name)))
25456104c370SDaniel Vetter return -ENOENT;
25466104c370SDaniel Vetter
25476104c370SDaniel Vetter font->width = f->width;
25486104c370SDaniel Vetter font->height = f->height;
2549a1ac250aSPeilin Ye return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
25506104c370SDaniel Vetter }
25516104c370SDaniel Vetter
25526104c370SDaniel Vetter static u16 palette_red[16];
25536104c370SDaniel Vetter static u16 palette_green[16];
25546104c370SDaniel Vetter static u16 palette_blue[16];
25556104c370SDaniel Vetter
25566104c370SDaniel Vetter static struct fb_cmap palette_cmap = {
25576104c370SDaniel Vetter 0, 16, palette_red, palette_green, palette_blue, NULL
25586104c370SDaniel Vetter };
25596104c370SDaniel Vetter
fbcon_set_palette(struct vc_data * vc,const unsigned char * table)25606104c370SDaniel Vetter static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
25616104c370SDaniel Vetter {
2562409d6c95SDaniel Vetter struct fb_info *info = fbcon_info_from_console(vc->vc_num);
25636104c370SDaniel Vetter int i, j, k, depth;
25646104c370SDaniel Vetter u8 val;
25656104c370SDaniel Vetter
25666104c370SDaniel Vetter if (fbcon_is_inactive(vc, info))
25676104c370SDaniel Vetter return;
25686104c370SDaniel Vetter
25696104c370SDaniel Vetter if (!con_is_visible(vc))
25706104c370SDaniel Vetter return;
25716104c370SDaniel Vetter
25726104c370SDaniel Vetter depth = fb_get_color_depth(&info->var, &info->fix);
25736104c370SDaniel Vetter if (depth > 3) {
25746104c370SDaniel Vetter for (i = j = 0; i < 16; i++) {
25756104c370SDaniel Vetter k = table[i];
25766104c370SDaniel Vetter val = vc->vc_palette[j++];
25776104c370SDaniel Vetter palette_red[k] = (val << 8) | val;
25786104c370SDaniel Vetter val = vc->vc_palette[j++];
25796104c370SDaniel Vetter palette_green[k] = (val << 8) | val;
25806104c370SDaniel Vetter val = vc->vc_palette[j++];
25816104c370SDaniel Vetter palette_blue[k] = (val << 8) | val;
25826104c370SDaniel Vetter }
25836104c370SDaniel Vetter palette_cmap.len = 16;
25846104c370SDaniel Vetter palette_cmap.start = 0;
25856104c370SDaniel Vetter /*
25866104c370SDaniel Vetter * If framebuffer is capable of less than 16 colors,
25876104c370SDaniel Vetter * use default palette of fbcon.
25886104c370SDaniel Vetter */
25896104c370SDaniel Vetter } else
25906104c370SDaniel Vetter fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
25916104c370SDaniel Vetter
25926104c370SDaniel Vetter fb_set_cmap(&palette_cmap, info);
25936104c370SDaniel Vetter }
25946104c370SDaniel Vetter
fbcon_screen_pos(const struct vc_data * vc,int offset)2595d73568c4SJiri Slaby static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
25966104c370SDaniel Vetter {
25976104c370SDaniel Vetter return (u16 *) (vc->vc_origin + offset);
25986104c370SDaniel Vetter }
25996104c370SDaniel Vetter
fbcon_getxy(struct vc_data * vc,unsigned long pos,int * px,int * py)26006104c370SDaniel Vetter static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
26016104c370SDaniel Vetter int *px, int *py)
26026104c370SDaniel Vetter {
26036104c370SDaniel Vetter unsigned long ret;
26046104c370SDaniel Vetter int x, y;
26056104c370SDaniel Vetter
26066104c370SDaniel Vetter if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
26076104c370SDaniel Vetter unsigned long offset = (pos - vc->vc_origin) / 2;
26086104c370SDaniel Vetter
26096104c370SDaniel Vetter x = offset % vc->vc_cols;
26106104c370SDaniel Vetter y = offset / vc->vc_cols;
26116104c370SDaniel Vetter ret = pos + (vc->vc_cols - x) * 2;
26126104c370SDaniel Vetter } else {
26136104c370SDaniel Vetter /* Should not happen */
26146104c370SDaniel Vetter x = y = 0;
26156104c370SDaniel Vetter ret = vc->vc_origin;
26166104c370SDaniel Vetter }
26176104c370SDaniel Vetter if (px)
26186104c370SDaniel Vetter *px = x;
26196104c370SDaniel Vetter if (py)
26206104c370SDaniel Vetter *py = y;
26216104c370SDaniel Vetter return ret;
26226104c370SDaniel Vetter }
26236104c370SDaniel Vetter
26246104c370SDaniel Vetter /* As we might be inside of softback, we may work with non-contiguous buffer,
26256104c370SDaniel Vetter that's why we have to use a separate routine. */
fbcon_invert_region(struct vc_data * vc,u16 * p,int cnt)26266104c370SDaniel Vetter static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
26276104c370SDaniel Vetter {
26286104c370SDaniel Vetter while (cnt--) {
26296104c370SDaniel Vetter u16 a = scr_readw(p);
26306104c370SDaniel Vetter if (!vc->vc_can_do_color)
26316104c370SDaniel Vetter a ^= 0x0800;
26326104c370SDaniel Vetter else if (vc->vc_hi_font_mask == 0x100)
26336104c370SDaniel Vetter a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
26346104c370SDaniel Vetter (((a) & 0x0e00) << 4);
26356104c370SDaniel Vetter else
26366104c370SDaniel Vetter a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
26376104c370SDaniel Vetter (((a) & 0x0700) << 4);
26386104c370SDaniel Vetter scr_writew(a, p++);
26396104c370SDaniel Vetter }
26406104c370SDaniel Vetter }
26416104c370SDaniel Vetter
fbcon_suspended(struct fb_info * info)264250c50563SDaniel Vetter void fbcon_suspended(struct fb_info *info)
26436104c370SDaniel Vetter {
26446104c370SDaniel Vetter struct vc_data *vc = NULL;
26456104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
26466104c370SDaniel Vetter
26476104c370SDaniel Vetter if (!ops || ops->currcon < 0)
26486104c370SDaniel Vetter return;
26496104c370SDaniel Vetter vc = vc_cons[ops->currcon].d;
26506104c370SDaniel Vetter
26516104c370SDaniel Vetter /* Clear cursor, restore saved data */
26526104c370SDaniel Vetter fbcon_cursor(vc, CM_ERASE);
26536104c370SDaniel Vetter }
26546104c370SDaniel Vetter
fbcon_resumed(struct fb_info * info)265550c50563SDaniel Vetter void fbcon_resumed(struct fb_info *info)
26566104c370SDaniel Vetter {
26576104c370SDaniel Vetter struct vc_data *vc;
26586104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
26596104c370SDaniel Vetter
26606104c370SDaniel Vetter if (!ops || ops->currcon < 0)
26616104c370SDaniel Vetter return;
26626104c370SDaniel Vetter vc = vc_cons[ops->currcon].d;
26636104c370SDaniel Vetter
26646104c370SDaniel Vetter update_screen(vc);
26656104c370SDaniel Vetter }
26666104c370SDaniel Vetter
fbcon_modechanged(struct fb_info * info)26676104c370SDaniel Vetter static void fbcon_modechanged(struct fb_info *info)
26686104c370SDaniel Vetter {
26696104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
26706104c370SDaniel Vetter struct vc_data *vc;
267150233393SDaniel Vetter struct fbcon_display *p;
26726104c370SDaniel Vetter int rows, cols;
26736104c370SDaniel Vetter
26746104c370SDaniel Vetter if (!ops || ops->currcon < 0)
26756104c370SDaniel Vetter return;
26766104c370SDaniel Vetter vc = vc_cons[ops->currcon].d;
26776104c370SDaniel Vetter if (vc->vc_mode != KD_TEXT ||
2678409d6c95SDaniel Vetter fbcon_info_from_console(ops->currcon) != info)
26796104c370SDaniel Vetter return;
26806104c370SDaniel Vetter
26816104c370SDaniel Vetter p = &fb_display[vc->vc_num];
26826104c370SDaniel Vetter set_blitting_type(vc, info);
26836104c370SDaniel Vetter
26846104c370SDaniel Vetter if (con_is_visible(vc)) {
26856104c370SDaniel Vetter var_to_display(p, &info->var, info);
26866104c370SDaniel Vetter cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
26876104c370SDaniel Vetter rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
26886104c370SDaniel Vetter cols /= vc->vc_font.width;
26896104c370SDaniel Vetter rows /= vc->vc_font.height;
26906104c370SDaniel Vetter vc_resize(vc, cols, rows);
26916104c370SDaniel Vetter updatescrollmode(p, info, vc);
26926104c370SDaniel Vetter scrollback_max = 0;
26936104c370SDaniel Vetter scrollback_current = 0;
26946104c370SDaniel Vetter
26956104c370SDaniel Vetter if (!fbcon_is_inactive(vc, info)) {
26966104c370SDaniel Vetter ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
26976104c370SDaniel Vetter ops->update_start(info);
26986104c370SDaniel Vetter }
26996104c370SDaniel Vetter
27006104c370SDaniel Vetter fbcon_set_palette(vc, color_table);
27016104c370SDaniel Vetter update_screen(vc);
27026104c370SDaniel Vetter }
27036104c370SDaniel Vetter }
27046104c370SDaniel Vetter
fbcon_set_all_vcs(struct fb_info * info)27056104c370SDaniel Vetter static void fbcon_set_all_vcs(struct fb_info *info)
27066104c370SDaniel Vetter {
27076104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
27086104c370SDaniel Vetter struct vc_data *vc;
270950233393SDaniel Vetter struct fbcon_display *p;
27106104c370SDaniel Vetter int i, rows, cols, fg = -1;
27116104c370SDaniel Vetter
27126104c370SDaniel Vetter if (!ops || ops->currcon < 0)
27136104c370SDaniel Vetter return;
27146104c370SDaniel Vetter
27156104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
27166104c370SDaniel Vetter vc = vc_cons[i].d;
27176104c370SDaniel Vetter if (!vc || vc->vc_mode != KD_TEXT ||
2718409d6c95SDaniel Vetter fbcon_info_from_console(i) != info)
27196104c370SDaniel Vetter continue;
27206104c370SDaniel Vetter
27216104c370SDaniel Vetter if (con_is_visible(vc)) {
27226104c370SDaniel Vetter fg = i;
27236104c370SDaniel Vetter continue;
27246104c370SDaniel Vetter }
27256104c370SDaniel Vetter
27266104c370SDaniel Vetter p = &fb_display[vc->vc_num];
27276104c370SDaniel Vetter set_blitting_type(vc, info);
27286104c370SDaniel Vetter var_to_display(p, &info->var, info);
27296104c370SDaniel Vetter cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
27306104c370SDaniel Vetter rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
27316104c370SDaniel Vetter cols /= vc->vc_font.width;
27326104c370SDaniel Vetter rows /= vc->vc_font.height;
27336104c370SDaniel Vetter vc_resize(vc, cols, rows);
27346104c370SDaniel Vetter }
27356104c370SDaniel Vetter
27366104c370SDaniel Vetter if (fg != -1)
27376104c370SDaniel Vetter fbcon_modechanged(info);
27386104c370SDaniel Vetter }
27396104c370SDaniel Vetter
27409e146700SDaniel Vetter
fbcon_update_vcs(struct fb_info * info,bool all)27419e146700SDaniel Vetter void fbcon_update_vcs(struct fb_info *info, bool all)
27429e146700SDaniel Vetter {
27439e146700SDaniel Vetter if (all)
27449e146700SDaniel Vetter fbcon_set_all_vcs(info);
27459e146700SDaniel Vetter else
27469e146700SDaniel Vetter fbcon_modechanged(info);
27479e146700SDaniel Vetter }
274824430914SDaniel Vetter EXPORT_SYMBOL(fbcon_update_vcs);
27499e146700SDaniel Vetter
2750e64242caSHelge Deller /* let fbcon check if it supports a new screen resolution */
fbcon_modechange_possible(struct fb_info * info,struct fb_var_screeninfo * var)2751e64242caSHelge Deller int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
2752e64242caSHelge Deller {
2753e64242caSHelge Deller struct fbcon_ops *ops = info->fbcon_par;
2754e64242caSHelge Deller struct vc_data *vc;
2755e64242caSHelge Deller unsigned int i;
2756e64242caSHelge Deller
2757e64242caSHelge Deller WARN_CONSOLE_UNLOCKED();
2758e64242caSHelge Deller
2759e64242caSHelge Deller if (!ops)
2760e64242caSHelge Deller return 0;
2761e64242caSHelge Deller
2762e64242caSHelge Deller /* prevent setting a screen size which is smaller than font size */
2763e64242caSHelge Deller for (i = first_fb_vc; i <= last_fb_vc; i++) {
2764e64242caSHelge Deller vc = vc_cons[i].d;
2765e64242caSHelge Deller if (!vc || vc->vc_mode != KD_TEXT ||
276653a6e66bSHelge Deller fbcon_info_from_console(i) != info)
2767e64242caSHelge Deller continue;
2768e64242caSHelge Deller
2769e64242caSHelge Deller if (vc->vc_font.width > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
2770e64242caSHelge Deller vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
2771e64242caSHelge Deller return -EINVAL;
2772e64242caSHelge Deller }
2773e64242caSHelge Deller
2774e64242caSHelge Deller return 0;
2775e64242caSHelge Deller }
2776e64242caSHelge Deller EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
2777e64242caSHelge Deller
fbcon_mode_deleted(struct fb_info * info,struct fb_videomode * mode)277813ff178cSDaniel Vetter int fbcon_mode_deleted(struct fb_info *info,
27796104c370SDaniel Vetter struct fb_videomode *mode)
27806104c370SDaniel Vetter {
27816104c370SDaniel Vetter struct fb_info *fb_info;
278250233393SDaniel Vetter struct fbcon_display *p;
27836104c370SDaniel Vetter int i, j, found = 0;
27846104c370SDaniel Vetter
27856104c370SDaniel Vetter /* before deletion, ensure that mode is not in use */
27866104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
27876104c370SDaniel Vetter j = con2fb_map[i];
27886104c370SDaniel Vetter if (j == -1)
27896104c370SDaniel Vetter continue;
2790efc3acbcSDaniel Vetter fb_info = fbcon_registered_fb[j];
27916104c370SDaniel Vetter if (fb_info != info)
27926104c370SDaniel Vetter continue;
27936104c370SDaniel Vetter p = &fb_display[i];
27946104c370SDaniel Vetter if (!p || !p->mode)
27956104c370SDaniel Vetter continue;
27966104c370SDaniel Vetter if (fb_mode_is_equal(p->mode, mode)) {
27976104c370SDaniel Vetter found = 1;
27986104c370SDaniel Vetter break;
27996104c370SDaniel Vetter }
28006104c370SDaniel Vetter }
28016104c370SDaniel Vetter return found;
28026104c370SDaniel Vetter }
28036104c370SDaniel Vetter
28046104c370SDaniel Vetter #ifdef CONFIG_VT_HW_CONSOLE_BINDING
fbcon_unbind(void)28050e0f3250SDaniel Vetter static void fbcon_unbind(void)
28066104c370SDaniel Vetter {
28076104c370SDaniel Vetter int ret;
28086104c370SDaniel Vetter
28096104c370SDaniel Vetter ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
28106104c370SDaniel Vetter fbcon_is_default);
28116104c370SDaniel Vetter
28126104c370SDaniel Vetter if (!ret)
28136104c370SDaniel Vetter fbcon_has_console_bind = 0;
28146104c370SDaniel Vetter }
28156104c370SDaniel Vetter #else
fbcon_unbind(void)28160e0f3250SDaniel Vetter static inline void fbcon_unbind(void) {}
28176104c370SDaniel Vetter #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
28186104c370SDaniel Vetter
fbcon_fb_unbind(struct fb_info * info)28190e0f3250SDaniel Vetter void fbcon_fb_unbind(struct fb_info *info)
28206104c370SDaniel Vetter {
2821b07db395SDaniel Vetter int i, new_idx = -1;
28220e0f3250SDaniel Vetter int idx = info->node;
28236104c370SDaniel Vetter
28246e7da3afSDaniel Vetter console_lock();
28253bd3a0e3SHans de Goede
28266e7da3afSDaniel Vetter if (!fbcon_has_console_bind) {
28276e7da3afSDaniel Vetter console_unlock();
28280e0f3250SDaniel Vetter return;
28296e7da3afSDaniel Vetter }
28306104c370SDaniel Vetter
28316104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
28326104c370SDaniel Vetter if (con2fb_map[i] != idx &&
28336104c370SDaniel Vetter con2fb_map[i] != -1) {
28342122b405SNoralf Trønnes new_idx = con2fb_map[i];
28356104c370SDaniel Vetter break;
28366104c370SDaniel Vetter }
28376104c370SDaniel Vetter }
28386104c370SDaniel Vetter
28396104c370SDaniel Vetter if (new_idx != -1) {
28406104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
28416104c370SDaniel Vetter if (con2fb_map[i] == idx)
28426104c370SDaniel Vetter set_con2fb_map(i, new_idx, 0);
28436104c370SDaniel Vetter }
28446104c370SDaniel Vetter } else {
2845efc3acbcSDaniel Vetter struct fb_info *info = fbcon_registered_fb[idx];
28466104c370SDaniel Vetter
28476104c370SDaniel Vetter /* This is sort of like set_con2fb_map, except it maps
28486104c370SDaniel Vetter * the consoles to no device and then releases the
28496104c370SDaniel Vetter * oldinfo to free memory and cancel the cursor blink
28506104c370SDaniel Vetter * timer. I can imagine this just becoming part of
28516104c370SDaniel Vetter * set_con2fb_map where new_idx is -1
28526104c370SDaniel Vetter */
28536104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
28546104c370SDaniel Vetter if (con2fb_map[i] == idx) {
28556104c370SDaniel Vetter con2fb_map[i] = -1;
28566104c370SDaniel Vetter if (!search_fb_in_map(idx)) {
2857b07db395SDaniel Vetter con2fb_release_oldinfo(vc_cons[i].d,
2858b07db395SDaniel Vetter info, NULL);
28596104c370SDaniel Vetter }
28606104c370SDaniel Vetter }
28616104c370SDaniel Vetter }
28620e0f3250SDaniel Vetter fbcon_unbind();
28636104c370SDaniel Vetter }
28646e7da3afSDaniel Vetter
28656e7da3afSDaniel Vetter console_unlock();
28666104c370SDaniel Vetter }
28676104c370SDaniel Vetter
fbcon_fb_unregistered(struct fb_info * info)286897b67986SDaniel Vetter void fbcon_fb_unregistered(struct fb_info *info)
28696104c370SDaniel Vetter {
28706104c370SDaniel Vetter int i, idx;
28716104c370SDaniel Vetter
28726e7da3afSDaniel Vetter console_lock();
28733bd3a0e3SHans de Goede
2874efc3acbcSDaniel Vetter fbcon_registered_fb[info->node] = NULL;
2875efc3acbcSDaniel Vetter fbcon_num_registered_fb--;
2876efc3acbcSDaniel Vetter
28776e7da3afSDaniel Vetter if (deferred_takeover) {
28786e7da3afSDaniel Vetter console_unlock();
287997b67986SDaniel Vetter return;
28806e7da3afSDaniel Vetter }
288183d83bebSHans de Goede
28826104c370SDaniel Vetter idx = info->node;
28836104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
28846104c370SDaniel Vetter if (con2fb_map[i] == idx)
28856104c370SDaniel Vetter con2fb_map[i] = -1;
28866104c370SDaniel Vetter }
28876104c370SDaniel Vetter
28886104c370SDaniel Vetter if (idx == info_idx) {
28896104c370SDaniel Vetter info_idx = -1;
28906104c370SDaniel Vetter
2891efc3acbcSDaniel Vetter fbcon_for_each_registered_fb(i) {
28926104c370SDaniel Vetter info_idx = i;
28936104c370SDaniel Vetter break;
28946104c370SDaniel Vetter }
28956104c370SDaniel Vetter }
28966104c370SDaniel Vetter
28976104c370SDaniel Vetter if (info_idx != -1) {
28986104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
28996104c370SDaniel Vetter if (con2fb_map[i] == -1)
29006104c370SDaniel Vetter con2fb_map[i] = info_idx;
29016104c370SDaniel Vetter }
29026104c370SDaniel Vetter }
29036104c370SDaniel Vetter
29046104c370SDaniel Vetter if (primary_device == idx)
29056104c370SDaniel Vetter primary_device = -1;
29066104c370SDaniel Vetter
2907efc3acbcSDaniel Vetter if (!fbcon_num_registered_fb)
29086104c370SDaniel Vetter do_unregister_con_driver(&fb_con);
29096e7da3afSDaniel Vetter console_unlock();
29106104c370SDaniel Vetter }
29116104c370SDaniel Vetter
fbcon_remap_all(struct fb_info * info)29121cd51b5dSDaniel Vetter void fbcon_remap_all(struct fb_info *info)
29136104c370SDaniel Vetter {
29141cd51b5dSDaniel Vetter int i, idx = info->node;
29153bd3a0e3SHans de Goede
29161cd51b5dSDaniel Vetter console_lock();
291783d83bebSHans de Goede if (deferred_takeover) {
291883d83bebSHans de Goede for (i = first_fb_vc; i <= last_fb_vc; i++)
291983d83bebSHans de Goede con2fb_map_boot[i] = idx;
292083d83bebSHans de Goede fbcon_map_override();
29211cd51b5dSDaniel Vetter console_unlock();
292283d83bebSHans de Goede return;
292383d83bebSHans de Goede }
292483d83bebSHans de Goede
29256104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++)
29266104c370SDaniel Vetter set_con2fb_map(i, idx, 0);
29276104c370SDaniel Vetter
29286104c370SDaniel Vetter if (con_is_bound(&fb_con)) {
29296104c370SDaniel Vetter printk(KERN_INFO "fbcon: Remapping primary device, "
29306104c370SDaniel Vetter "fb%i, to tty %i-%i\n", idx,
29316104c370SDaniel Vetter first_fb_vc + 1, last_fb_vc + 1);
29326104c370SDaniel Vetter info_idx = idx;
29336104c370SDaniel Vetter }
29341cd51b5dSDaniel Vetter console_unlock();
29356104c370SDaniel Vetter }
29366104c370SDaniel Vetter
29376104c370SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
fbcon_select_primary(struct fb_info * info)29386104c370SDaniel Vetter static void fbcon_select_primary(struct fb_info *info)
29396104c370SDaniel Vetter {
29406104c370SDaniel Vetter if (!map_override && primary_device == -1 &&
29416104c370SDaniel Vetter fb_is_primary_device(info)) {
29426104c370SDaniel Vetter int i;
29436104c370SDaniel Vetter
29446104c370SDaniel Vetter printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
29456104c370SDaniel Vetter info->fix.id, info->node);
29466104c370SDaniel Vetter primary_device = info->node;
29476104c370SDaniel Vetter
29486104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++)
29496104c370SDaniel Vetter con2fb_map_boot[i] = primary_device;
29506104c370SDaniel Vetter
29516104c370SDaniel Vetter if (con_is_bound(&fb_con)) {
29526104c370SDaniel Vetter printk(KERN_INFO "fbcon: Remapping primary device, "
29536104c370SDaniel Vetter "fb%i, to tty %i-%i\n", info->node,
29546104c370SDaniel Vetter first_fb_vc + 1, last_fb_vc + 1);
29556104c370SDaniel Vetter info_idx = primary_device;
29566104c370SDaniel Vetter }
29576104c370SDaniel Vetter }
29586104c370SDaniel Vetter
29596104c370SDaniel Vetter }
29606104c370SDaniel Vetter #else
fbcon_select_primary(struct fb_info * info)29616104c370SDaniel Vetter static inline void fbcon_select_primary(struct fb_info *info)
29626104c370SDaniel Vetter {
29636104c370SDaniel Vetter return;
29646104c370SDaniel Vetter }
29656104c370SDaniel Vetter #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
29666104c370SDaniel Vetter
29676e7da3afSDaniel Vetter static bool lockless_register_fb;
29686e7da3afSDaniel Vetter module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
29696e7da3afSDaniel Vetter MODULE_PARM_DESC(lockless_register_fb,
29706e7da3afSDaniel Vetter "Lockless framebuffer registration for debugging [default=off]");
29716e7da3afSDaniel Vetter
29726104c370SDaniel Vetter /* called with console_lock held */
do_fb_registered(struct fb_info * info)29739d797991SDaniel Vetter static int do_fb_registered(struct fb_info *info)
29746104c370SDaniel Vetter {
29756104c370SDaniel Vetter int ret = 0, i, idx;
29766104c370SDaniel Vetter
29779d797991SDaniel Vetter WARN_CONSOLE_UNLOCKED();
29783bd3a0e3SHans de Goede
2979efc3acbcSDaniel Vetter fbcon_registered_fb[info->node] = info;
2980efc3acbcSDaniel Vetter fbcon_num_registered_fb++;
2981efc3acbcSDaniel Vetter
29826104c370SDaniel Vetter idx = info->node;
29836104c370SDaniel Vetter fbcon_select_primary(info);
29846104c370SDaniel Vetter
298583d83bebSHans de Goede if (deferred_takeover) {
298683d83bebSHans de Goede pr_info("fbcon: Deferring console take-over\n");
29879d797991SDaniel Vetter return 0;
298883d83bebSHans de Goede }
298983d83bebSHans de Goede
29906104c370SDaniel Vetter if (info_idx == -1) {
29916104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
29926104c370SDaniel Vetter if (con2fb_map_boot[i] == idx) {
29936104c370SDaniel Vetter info_idx = idx;
29946104c370SDaniel Vetter break;
29956104c370SDaniel Vetter }
29966104c370SDaniel Vetter }
29976104c370SDaniel Vetter
29986104c370SDaniel Vetter if (info_idx != -1)
29996104c370SDaniel Vetter ret = do_fbcon_takeover(1);
30006104c370SDaniel Vetter } else {
30016104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
30026104c370SDaniel Vetter if (con2fb_map_boot[i] == idx)
30036104c370SDaniel Vetter set_con2fb_map(i, idx, 0);
30046104c370SDaniel Vetter }
30056104c370SDaniel Vetter }
30066104c370SDaniel Vetter
30079d797991SDaniel Vetter return ret;
30089d797991SDaniel Vetter }
30099d797991SDaniel Vetter
fbcon_fb_registered(struct fb_info * info)30109d797991SDaniel Vetter int fbcon_fb_registered(struct fb_info *info)
30119d797991SDaniel Vetter {
30129d797991SDaniel Vetter int ret;
30139d797991SDaniel Vetter
30149d797991SDaniel Vetter if (!lockless_register_fb)
30159d797991SDaniel Vetter console_lock();
30169d797991SDaniel Vetter else
30179d797991SDaniel Vetter atomic_inc(&ignore_console_lock_warning);
30189d797991SDaniel Vetter
30199d797991SDaniel Vetter ret = do_fb_registered(info);
30209d797991SDaniel Vetter
30216e7da3afSDaniel Vetter if (!lockless_register_fb)
30226e7da3afSDaniel Vetter console_unlock();
30236e7da3afSDaniel Vetter else
30246e7da3afSDaniel Vetter atomic_dec(&ignore_console_lock_warning);
30256e7da3afSDaniel Vetter
30266104c370SDaniel Vetter return ret;
30276104c370SDaniel Vetter }
30286104c370SDaniel Vetter
fbcon_fb_blanked(struct fb_info * info,int blank)30297a625549SDaniel Vetter void fbcon_fb_blanked(struct fb_info *info, int blank)
30306104c370SDaniel Vetter {
30316104c370SDaniel Vetter struct fbcon_ops *ops = info->fbcon_par;
30326104c370SDaniel Vetter struct vc_data *vc;
30336104c370SDaniel Vetter
30346104c370SDaniel Vetter if (!ops || ops->currcon < 0)
30356104c370SDaniel Vetter return;
30366104c370SDaniel Vetter
30376104c370SDaniel Vetter vc = vc_cons[ops->currcon].d;
30386104c370SDaniel Vetter if (vc->vc_mode != KD_TEXT ||
3039409d6c95SDaniel Vetter fbcon_info_from_console(ops->currcon) != info)
30406104c370SDaniel Vetter return;
30416104c370SDaniel Vetter
30426104c370SDaniel Vetter if (con_is_visible(vc)) {
30436104c370SDaniel Vetter if (blank)
30446104c370SDaniel Vetter do_blank_screen(0);
30456104c370SDaniel Vetter else
30466104c370SDaniel Vetter do_unblank_screen(0);
30476104c370SDaniel Vetter }
30486104c370SDaniel Vetter ops->blank_state = blank;
30496104c370SDaniel Vetter }
30506104c370SDaniel Vetter
fbcon_new_modelist(struct fb_info * info)305113ff178cSDaniel Vetter void fbcon_new_modelist(struct fb_info *info)
30526104c370SDaniel Vetter {
30536104c370SDaniel Vetter int i;
30546104c370SDaniel Vetter struct vc_data *vc;
30556104c370SDaniel Vetter struct fb_var_screeninfo var;
30566104c370SDaniel Vetter const struct fb_videomode *mode;
30576104c370SDaniel Vetter
30586104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
3059409d6c95SDaniel Vetter if (fbcon_info_from_console(i) != info)
30606104c370SDaniel Vetter continue;
30616104c370SDaniel Vetter if (!fb_display[i].mode)
30626104c370SDaniel Vetter continue;
30636104c370SDaniel Vetter vc = vc_cons[i].d;
30646104c370SDaniel Vetter display_to_var(&var, &fb_display[i]);
30656104c370SDaniel Vetter mode = fb_find_nearest_mode(fb_display[i].mode,
30666104c370SDaniel Vetter &info->modelist);
30676104c370SDaniel Vetter fb_videomode_to_var(&var, mode);
30686104c370SDaniel Vetter fbcon_set_disp(info, &var, vc->vc_num);
30696104c370SDaniel Vetter }
30706104c370SDaniel Vetter }
30716104c370SDaniel Vetter
fbcon_get_requirement(struct fb_info * info,struct fb_blit_caps * caps)30720526c223SDaniel Vetter void fbcon_get_requirement(struct fb_info *info,
30736104c370SDaniel Vetter struct fb_blit_caps *caps)
30746104c370SDaniel Vetter {
30756104c370SDaniel Vetter struct vc_data *vc;
30766104c370SDaniel Vetter
30776104c370SDaniel Vetter if (caps->flags) {
30786104c370SDaniel Vetter int i, charcnt;
30796104c370SDaniel Vetter
30806104c370SDaniel Vetter for (i = first_fb_vc; i <= last_fb_vc; i++) {
30816104c370SDaniel Vetter vc = vc_cons[i].d;
30826104c370SDaniel Vetter if (vc && vc->vc_mode == KD_TEXT &&
30836104c370SDaniel Vetter info->node == con2fb_map[i]) {
30846104c370SDaniel Vetter caps->x |= 1 << (vc->vc_font.width - 1);
30856104c370SDaniel Vetter caps->y |= 1 << (vc->vc_font.height - 1);
3086a1ac250aSPeilin Ye charcnt = vc->vc_font.charcount;
30876104c370SDaniel Vetter if (caps->len < charcnt)
30886104c370SDaniel Vetter caps->len = charcnt;
30896104c370SDaniel Vetter }
30906104c370SDaniel Vetter }
30916104c370SDaniel Vetter } else {
30926104c370SDaniel Vetter vc = vc_cons[fg_console].d;
30936104c370SDaniel Vetter
30946104c370SDaniel Vetter if (vc && vc->vc_mode == KD_TEXT &&
30956104c370SDaniel Vetter info->node == con2fb_map[fg_console]) {
30966104c370SDaniel Vetter caps->x = 1 << (vc->vc_font.width - 1);
30976104c370SDaniel Vetter caps->y = 1 << (vc->vc_font.height - 1);
3098a1ac250aSPeilin Ye caps->len = vc->vc_font.charcount;
30996104c370SDaniel Vetter }
31006104c370SDaniel Vetter }
31016104c370SDaniel Vetter }
31026104c370SDaniel Vetter
fbcon_set_con2fb_map_ioctl(void __user * argp)3103fe2d70d6SDaniel Vetter int fbcon_set_con2fb_map_ioctl(void __user *argp)
31046104c370SDaniel Vetter {
3105fe2d70d6SDaniel Vetter struct fb_con2fbmap con2fb;
3106fe2d70d6SDaniel Vetter int ret;
31076104c370SDaniel Vetter
3108fe2d70d6SDaniel Vetter if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3109fe2d70d6SDaniel Vetter return -EFAULT;
3110fe2d70d6SDaniel Vetter if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3111fe2d70d6SDaniel Vetter return -EINVAL;
3112fe2d70d6SDaniel Vetter if (con2fb.framebuffer >= FB_MAX)
3113fe2d70d6SDaniel Vetter return -EINVAL;
3114efc3acbcSDaniel Vetter if (!fbcon_registered_fb[con2fb.framebuffer])
3115fe2d70d6SDaniel Vetter request_module("fb%d", con2fb.framebuffer);
3116efc3acbcSDaniel Vetter if (!fbcon_registered_fb[con2fb.framebuffer]) {
3117fe2d70d6SDaniel Vetter return -EINVAL;
31186104c370SDaniel Vetter }
3119fe2d70d6SDaniel Vetter
3120fe2d70d6SDaniel Vetter console_lock();
3121fe2d70d6SDaniel Vetter ret = set_con2fb_map(con2fb.console - 1,
3122fe2d70d6SDaniel Vetter con2fb.framebuffer, 1);
3123fe2d70d6SDaniel Vetter console_unlock();
3124fe2d70d6SDaniel Vetter
31256104c370SDaniel Vetter return ret;
31266104c370SDaniel Vetter }
31276104c370SDaniel Vetter
fbcon_get_con2fb_map_ioctl(void __user * argp)3128fe2d70d6SDaniel Vetter int fbcon_get_con2fb_map_ioctl(void __user *argp)
3129fe2d70d6SDaniel Vetter {
3130fe2d70d6SDaniel Vetter struct fb_con2fbmap con2fb;
3131fe2d70d6SDaniel Vetter
3132fe2d70d6SDaniel Vetter if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3133fe2d70d6SDaniel Vetter return -EFAULT;
3134fe2d70d6SDaniel Vetter if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3135fe2d70d6SDaniel Vetter return -EINVAL;
3136fe2d70d6SDaniel Vetter
3137fe2d70d6SDaniel Vetter console_lock();
3138fe2d70d6SDaniel Vetter con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3139fe2d70d6SDaniel Vetter console_unlock();
3140fe2d70d6SDaniel Vetter
3141fe2d70d6SDaniel Vetter return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3142fe2d70d6SDaniel Vetter }
3143fe2d70d6SDaniel Vetter
31446104c370SDaniel Vetter /*
31456104c370SDaniel Vetter * The console `switch' structure for the frame buffer based console
31466104c370SDaniel Vetter */
31476104c370SDaniel Vetter
31486104c370SDaniel Vetter static const struct consw fb_con = {
31496104c370SDaniel Vetter .owner = THIS_MODULE,
31506104c370SDaniel Vetter .con_startup = fbcon_startup,
31516104c370SDaniel Vetter .con_init = fbcon_init,
31526104c370SDaniel Vetter .con_deinit = fbcon_deinit,
31536104c370SDaniel Vetter .con_clear = fbcon_clear,
31546104c370SDaniel Vetter .con_putc = fbcon_putc,
31556104c370SDaniel Vetter .con_putcs = fbcon_putcs,
31566104c370SDaniel Vetter .con_cursor = fbcon_cursor,
31576104c370SDaniel Vetter .con_scroll = fbcon_scroll,
31586104c370SDaniel Vetter .con_switch = fbcon_switch,
31596104c370SDaniel Vetter .con_blank = fbcon_blank,
31606104c370SDaniel Vetter .con_font_set = fbcon_set_font,
31616104c370SDaniel Vetter .con_font_get = fbcon_get_font,
31626104c370SDaniel Vetter .con_font_default = fbcon_set_def_font,
31636104c370SDaniel Vetter .con_set_palette = fbcon_set_palette,
31646104c370SDaniel Vetter .con_invert_region = fbcon_invert_region,
31656104c370SDaniel Vetter .con_screen_pos = fbcon_screen_pos,
31666104c370SDaniel Vetter .con_getxy = fbcon_getxy,
31676104c370SDaniel Vetter .con_resize = fbcon_resize,
31686104c370SDaniel Vetter .con_debug_enter = fbcon_debug_enter,
31696104c370SDaniel Vetter .con_debug_leave = fbcon_debug_leave,
31706104c370SDaniel Vetter };
31716104c370SDaniel Vetter
store_rotate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)31726104c370SDaniel Vetter static ssize_t store_rotate(struct device *device,
31736104c370SDaniel Vetter struct device_attribute *attr, const char *buf,
31746104c370SDaniel Vetter size_t count)
31756104c370SDaniel Vetter {
31766104c370SDaniel Vetter struct fb_info *info;
31776104c370SDaniel Vetter int rotate, idx;
31786104c370SDaniel Vetter char **last = NULL;
31796104c370SDaniel Vetter
31806104c370SDaniel Vetter console_lock();
31816104c370SDaniel Vetter idx = con2fb_map[fg_console];
31826104c370SDaniel Vetter
3183efc3acbcSDaniel Vetter if (idx == -1 || fbcon_registered_fb[idx] == NULL)
31846104c370SDaniel Vetter goto err;
31856104c370SDaniel Vetter
3186efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
31876104c370SDaniel Vetter rotate = simple_strtoul(buf, last, 0);
31886104c370SDaniel Vetter fbcon_rotate(info, rotate);
31896104c370SDaniel Vetter err:
31906104c370SDaniel Vetter console_unlock();
31916104c370SDaniel Vetter return count;
31926104c370SDaniel Vetter }
31936104c370SDaniel Vetter
store_rotate_all(struct device * device,struct device_attribute * attr,const char * buf,size_t count)31946104c370SDaniel Vetter static ssize_t store_rotate_all(struct device *device,
31956104c370SDaniel Vetter struct device_attribute *attr,const char *buf,
31966104c370SDaniel Vetter size_t count)
31976104c370SDaniel Vetter {
31986104c370SDaniel Vetter struct fb_info *info;
31996104c370SDaniel Vetter int rotate, idx;
32006104c370SDaniel Vetter char **last = NULL;
32016104c370SDaniel Vetter
32026104c370SDaniel Vetter console_lock();
32036104c370SDaniel Vetter idx = con2fb_map[fg_console];
32046104c370SDaniel Vetter
3205efc3acbcSDaniel Vetter if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32066104c370SDaniel Vetter goto err;
32076104c370SDaniel Vetter
3208efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
32096104c370SDaniel Vetter rotate = simple_strtoul(buf, last, 0);
32106104c370SDaniel Vetter fbcon_rotate_all(info, rotate);
32116104c370SDaniel Vetter err:
32126104c370SDaniel Vetter console_unlock();
32136104c370SDaniel Vetter return count;
32146104c370SDaniel Vetter }
32156104c370SDaniel Vetter
show_rotate(struct device * device,struct device_attribute * attr,char * buf)32166104c370SDaniel Vetter static ssize_t show_rotate(struct device *device,
32176104c370SDaniel Vetter struct device_attribute *attr,char *buf)
32186104c370SDaniel Vetter {
32196104c370SDaniel Vetter struct fb_info *info;
32206104c370SDaniel Vetter int rotate = 0, idx;
32216104c370SDaniel Vetter
32226104c370SDaniel Vetter console_lock();
32236104c370SDaniel Vetter idx = con2fb_map[fg_console];
32246104c370SDaniel Vetter
3225efc3acbcSDaniel Vetter if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32266104c370SDaniel Vetter goto err;
32276104c370SDaniel Vetter
3228efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
32296104c370SDaniel Vetter rotate = fbcon_get_rotate(info);
32306104c370SDaniel Vetter err:
32316104c370SDaniel Vetter console_unlock();
323216a54d4eSYang Guang return sysfs_emit(buf, "%d\n", rotate);
32336104c370SDaniel Vetter }
32346104c370SDaniel Vetter
show_cursor_blink(struct device * device,struct device_attribute * attr,char * buf)32356104c370SDaniel Vetter static ssize_t show_cursor_blink(struct device *device,
32366104c370SDaniel Vetter struct device_attribute *attr, char *buf)
32376104c370SDaniel Vetter {
32386104c370SDaniel Vetter struct fb_info *info;
32396104c370SDaniel Vetter struct fbcon_ops *ops;
32406104c370SDaniel Vetter int idx, blink = -1;
32416104c370SDaniel Vetter
32426104c370SDaniel Vetter console_lock();
32436104c370SDaniel Vetter idx = con2fb_map[fg_console];
32446104c370SDaniel Vetter
3245efc3acbcSDaniel Vetter if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32466104c370SDaniel Vetter goto err;
32476104c370SDaniel Vetter
3248efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
32496104c370SDaniel Vetter ops = info->fbcon_par;
32506104c370SDaniel Vetter
32516104c370SDaniel Vetter if (!ops)
32526104c370SDaniel Vetter goto err;
32536104c370SDaniel Vetter
32543b0fb6abSDaniel Vetter blink = delayed_work_pending(&ops->cursor_work);
32556104c370SDaniel Vetter err:
32566104c370SDaniel Vetter console_unlock();
325716a54d4eSYang Guang return sysfs_emit(buf, "%d\n", blink);
32586104c370SDaniel Vetter }
32596104c370SDaniel Vetter
store_cursor_blink(struct device * device,struct device_attribute * attr,const char * buf,size_t count)32606104c370SDaniel Vetter static ssize_t store_cursor_blink(struct device *device,
32616104c370SDaniel Vetter struct device_attribute *attr,
32626104c370SDaniel Vetter const char *buf, size_t count)
32636104c370SDaniel Vetter {
32646104c370SDaniel Vetter struct fb_info *info;
32656104c370SDaniel Vetter int blink, idx;
32666104c370SDaniel Vetter char **last = NULL;
32676104c370SDaniel Vetter
32686104c370SDaniel Vetter console_lock();
32696104c370SDaniel Vetter idx = con2fb_map[fg_console];
32706104c370SDaniel Vetter
3271efc3acbcSDaniel Vetter if (idx == -1 || fbcon_registered_fb[idx] == NULL)
32726104c370SDaniel Vetter goto err;
32736104c370SDaniel Vetter
3274efc3acbcSDaniel Vetter info = fbcon_registered_fb[idx];
32756104c370SDaniel Vetter
32766104c370SDaniel Vetter if (!info->fbcon_par)
32776104c370SDaniel Vetter goto err;
32786104c370SDaniel Vetter
32796104c370SDaniel Vetter blink = simple_strtoul(buf, last, 0);
32806104c370SDaniel Vetter
32816104c370SDaniel Vetter if (blink) {
32826104c370SDaniel Vetter fbcon_cursor_noblink = 0;
32833b0fb6abSDaniel Vetter fbcon_add_cursor_work(info);
32846104c370SDaniel Vetter } else {
32856104c370SDaniel Vetter fbcon_cursor_noblink = 1;
32863b0fb6abSDaniel Vetter fbcon_del_cursor_work(info);
32876104c370SDaniel Vetter }
32886104c370SDaniel Vetter
32896104c370SDaniel Vetter err:
32906104c370SDaniel Vetter console_unlock();
32916104c370SDaniel Vetter return count;
32926104c370SDaniel Vetter }
32936104c370SDaniel Vetter
32946104c370SDaniel Vetter static struct device_attribute device_attrs[] = {
32956104c370SDaniel Vetter __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
32966104c370SDaniel Vetter __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
32976104c370SDaniel Vetter __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
32986104c370SDaniel Vetter store_cursor_blink),
32996104c370SDaniel Vetter };
33006104c370SDaniel Vetter
fbcon_init_device(void)33016104c370SDaniel Vetter static int fbcon_init_device(void)
33026104c370SDaniel Vetter {
33036104c370SDaniel Vetter int i, error = 0;
33046104c370SDaniel Vetter
33056104c370SDaniel Vetter fbcon_has_sysfs = 1;
33066104c370SDaniel Vetter
33076104c370SDaniel Vetter for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
33086104c370SDaniel Vetter error = device_create_file(fbcon_device, &device_attrs[i]);
33096104c370SDaniel Vetter
33106104c370SDaniel Vetter if (error)
33116104c370SDaniel Vetter break;
33126104c370SDaniel Vetter }
33136104c370SDaniel Vetter
33146104c370SDaniel Vetter if (error) {
33156104c370SDaniel Vetter while (--i >= 0)
33166104c370SDaniel Vetter device_remove_file(fbcon_device, &device_attrs[i]);
33176104c370SDaniel Vetter
33186104c370SDaniel Vetter fbcon_has_sysfs = 0;
33196104c370SDaniel Vetter }
33206104c370SDaniel Vetter
33216104c370SDaniel Vetter return 0;
33226104c370SDaniel Vetter }
33236104c370SDaniel Vetter
332483d83bebSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
fbcon_register_existing_fbs(struct work_struct * work)3325df37e225SHans de Goede static void fbcon_register_existing_fbs(struct work_struct *work)
3326df37e225SHans de Goede {
3327df37e225SHans de Goede int i;
3328df37e225SHans de Goede
3329df37e225SHans de Goede console_lock();
3330df37e225SHans de Goede
333143553559SDaniel Vetter deferred_takeover = false;
333243553559SDaniel Vetter logo_shown = FBCON_LOGO_DONTSHOW;
333343553559SDaniel Vetter
3334efc3acbcSDaniel Vetter fbcon_for_each_registered_fb(i)
33359d797991SDaniel Vetter do_fb_registered(fbcon_registered_fb[i]);
3336df37e225SHans de Goede
3337df37e225SHans de Goede console_unlock();
3338df37e225SHans de Goede }
3339df37e225SHans de Goede
334083d83bebSHans de Goede static struct notifier_block fbcon_output_nb;
3341df37e225SHans de Goede static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
334283d83bebSHans de Goede
fbcon_output_notifier(struct notifier_block * nb,unsigned long action,void * data)334383d83bebSHans de Goede static int fbcon_output_notifier(struct notifier_block *nb,
334483d83bebSHans de Goede unsigned long action, void *data)
334583d83bebSHans de Goede {
334683d83bebSHans de Goede WARN_CONSOLE_UNLOCKED();
334783d83bebSHans de Goede
334883d83bebSHans de Goede pr_info("fbcon: Taking over console\n");
334983d83bebSHans de Goede
335083d83bebSHans de Goede dummycon_unregister_output_notifier(&fbcon_output_nb);
335183d83bebSHans de Goede
3352df37e225SHans de Goede /* We may get called in atomic context */
3353df37e225SHans de Goede schedule_work(&fbcon_deferred_takeover_work);
335483d83bebSHans de Goede
335583d83bebSHans de Goede return NOTIFY_OK;
335683d83bebSHans de Goede }
335783d83bebSHans de Goede #endif
335883d83bebSHans de Goede
fbcon_start(void)33596104c370SDaniel Vetter static void fbcon_start(void)
33606104c370SDaniel Vetter {
3361bedb38fcSHans de Goede WARN_CONSOLE_UNLOCKED();
3362bedb38fcSHans de Goede
3363bedb38fcSHans de Goede #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3364bedb38fcSHans de Goede if (conswitchp != &dummy_con)
3365bedb38fcSHans de Goede deferred_takeover = false;
3366bedb38fcSHans de Goede
336783d83bebSHans de Goede if (deferred_takeover) {
3368bedb38fcSHans de Goede fbcon_output_nb.notifier_call = fbcon_output_notifier;
3369bedb38fcSHans de Goede dummycon_register_output_notifier(&fbcon_output_nb);
337083d83bebSHans de Goede return;
337183d83bebSHans de Goede }
3372bedb38fcSHans de Goede #endif
33736104c370SDaniel Vetter }
33746104c370SDaniel Vetter
fb_console_init(void)33756104c370SDaniel Vetter void __init fb_console_init(void)
33766104c370SDaniel Vetter {
33776104c370SDaniel Vetter int i;
33786104c370SDaniel Vetter
33796104c370SDaniel Vetter console_lock();
33806104c370SDaniel Vetter fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
33816104c370SDaniel Vetter "fbcon");
33826104c370SDaniel Vetter
33836104c370SDaniel Vetter if (IS_ERR(fbcon_device)) {
33846104c370SDaniel Vetter printk(KERN_WARNING "Unable to create device "
33856104c370SDaniel Vetter "for fbcon; errno = %ld\n",
33866104c370SDaniel Vetter PTR_ERR(fbcon_device));
33876104c370SDaniel Vetter fbcon_device = NULL;
33886104c370SDaniel Vetter } else
33896104c370SDaniel Vetter fbcon_init_device();
33906104c370SDaniel Vetter
33916104c370SDaniel Vetter for (i = 0; i < MAX_NR_CONSOLES; i++)
33926104c370SDaniel Vetter con2fb_map[i] = -1;
33936104c370SDaniel Vetter
33946104c370SDaniel Vetter fbcon_start();
3395bedb38fcSHans de Goede console_unlock();
33966104c370SDaniel Vetter }
33976104c370SDaniel Vetter
33986104c370SDaniel Vetter #ifdef MODULE
33996104c370SDaniel Vetter
fbcon_deinit_device(void)34006104c370SDaniel Vetter static void __exit fbcon_deinit_device(void)
34016104c370SDaniel Vetter {
34026104c370SDaniel Vetter int i;
34036104c370SDaniel Vetter
34046104c370SDaniel Vetter if (fbcon_has_sysfs) {
34056104c370SDaniel Vetter for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
34066104c370SDaniel Vetter device_remove_file(fbcon_device, &device_attrs[i]);
34076104c370SDaniel Vetter
34086104c370SDaniel Vetter fbcon_has_sysfs = 0;
34096104c370SDaniel Vetter }
34106104c370SDaniel Vetter }
34116104c370SDaniel Vetter
fb_console_exit(void)34126104c370SDaniel Vetter void __exit fb_console_exit(void)
34136104c370SDaniel Vetter {
3414c75300b5SDaniel Vetter #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3415c75300b5SDaniel Vetter console_lock();
3416c75300b5SDaniel Vetter if (deferred_takeover)
3417c75300b5SDaniel Vetter dummycon_unregister_output_notifier(&fbcon_output_nb);
3418c75300b5SDaniel Vetter console_unlock();
3419c75300b5SDaniel Vetter
3420c75300b5SDaniel Vetter cancel_work_sync(&fbcon_deferred_takeover_work);
3421c75300b5SDaniel Vetter #endif
3422c75300b5SDaniel Vetter
34236104c370SDaniel Vetter console_lock();
34246104c370SDaniel Vetter fbcon_deinit_device();
34256104c370SDaniel Vetter device_destroy(fb_class, MKDEV(0, 0));
3426c75300b5SDaniel Vetter
34276104c370SDaniel Vetter do_unregister_con_driver(&fb_con);
34286104c370SDaniel Vetter console_unlock();
34296104c370SDaniel Vetter }
34306104c370SDaniel Vetter #endif
3431