3418 lines · 83.1 KB · C
RawDownload
/* *  linux/drivers/video/fbcon.c -- Low level frame buffer based console driver * *	Copyright (C) 1995 Geert Uytterhoeven * * *  This file is based on the original Amiga console driver (amicon.c): * *	Copyright (C) 1993 Hamish Macdonald *			   Greg Harp *	Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] * *	      with work by William Rucklidge (wjr@cs.cornell.edu) *			   Geert Uytterhoeven *			   Jes Sorensen (jds@kom.auc.dk) *			   Martin Apel * *  and on the original Atari console driver (atacon.c): * *	Copyright (C) 1993 Bjoern Brauel *			   Roman Hodek * *	      with work by Guenther Kelleter *			   Martin Schaller *			   Andreas Schwab * *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org) *  Smart redraw scrolling, arbitrary font width support, 512char font support *  and software scrollback added by *                         Jakub Jelinek (jj@ultra.linux.cz) * *  Random hacking by Martin Mares <mj@ucw.cz> * *	2001 - Documented with DocBook *	- Brad Douglas <brad@neruo.com> * *  The low level operations for the various display memory organizations are *  now in separate source files. * *  Currently the following organizations are supported: * *    o afb			Amiga bitplanes *    o cfb{2,4,8,16,24,32}	Packed pixels *    o ilbm			Amiga interleaved bitplanes *    o iplan2p[248]		Atari interleaved bitplanes *    o mfb			Monochrome *    o vga			VGA characters/attributes * *  To do: * *    - Implement 16 plane mode (iplan2p16) * * *  This file is subject to the terms and conditions of the GNU General Public *  License.  See the file COPYING in the main directory of this archive for *  more details. */ #include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/delay.h>	/* MSch: for IRQ probe */#include <linux/console.h>#include <linux/string.h>#include <linux/kd.h>#include <linux/panic.h>#include <linux/printk.h>#include <linux/slab.h>#include <linux/fb.h>#include <linux/fbcon.h>#include <linux/vt_kern.h>#include <linux/selection.h>#include <linux/font.h>#include <linux/smp.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/crc32.h> /* For counting font checksums */#include <linux/uaccess.h>#include <asm/irq.h> #include "fbcon.h"#include "fb_internal.h" /* * FIXME: Locking * * - fbcon state itself is protected by the console_lock, and the code does a *   pretty good job at making sure that lock is held everywhere it's needed. * * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it *   means concurrent access to the same fbdev from both fbcon and userspace *   will blow up. To fix this all fbcon calls from fbmem.c need to be moved out *   of fb_lock/unlock protected sections, since otherwise we'll recurse and *   deadlock eventually. Aside: Due to these deadlock issues the fbdev code in *   fbmem.c cannot use locking asserts, and there's lots of callers which get *   the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too. */ enum {	FBCON_LOGO_CANSHOW	= -1,	/* the logo can be shown */	FBCON_LOGO_DRAW		= -2,	/* draw the logo to a console */	FBCON_LOGO_DONTSHOW	= -3	/* do not show the logo */}; static struct fbcon_display fb_display[MAX_NR_CONSOLES]; static struct fb_info *fbcon_registered_fb[FB_MAX];static int fbcon_num_registered_fb; #define fbcon_for_each_registered_fb(i)		\	for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++)		\		if (!fbcon_registered_fb[i]) {} else static signed char con2fb_map[MAX_NR_CONSOLES];static signed char con2fb_map_boot[MAX_NR_CONSOLES]; static struct fb_info *fbcon_info_from_console(int console){	WARN_CONSOLE_UNLOCKED(); 	return fbcon_registered_fb[con2fb_map[console]];} static int logo_lines;/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO   enums.  */static int logo_shown = FBCON_LOGO_CANSHOW;/* console mappings */static unsigned int first_fb_vc;static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;static int fbcon_is_default = 1;static int primary_device = -1;static int fbcon_has_console_bind; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARYstatic int map_override; static inline void fbcon_map_override(void){	map_override = 1;}#elsestatic inline void fbcon_map_override(void){}#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */ #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVERstatic bool deferred_takeover = true;#else#define deferred_takeover false#endif /* font data */static char fontname[40]; /* current fb_info */static int info_idx = -1; /* console rotation */static int initial_rotation = -1;static int fbcon_has_sysfs;static int margin_color; static const struct consw fb_con; #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) static int fbcon_cursor_noblink; #define divides(a, b)	((!(a) || (b)%(a)) ? 0 : 1) /* *  Interface used by the world */ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table); /* *  Internal routines */static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,			   int unit);static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,			      int line, int count, int dy);static void fbcon_modechanged(struct fb_info *info);static void fbcon_set_all_vcs(struct fb_info *info); static struct device *fbcon_device; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATIONstatic inline void fbcon_set_rotation(struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&	    ops->p->con_rotate < 4)		ops->rotate = ops->p->con_rotate;	else		ops->rotate = 0;} static void fbcon_rotate(struct fb_info *info, u32 rotate){	struct fbcon_ops *ops= info->fbcon_par;	struct fb_info *fb_info; 	if (!ops || ops->currcon == -1)		return; 	fb_info = fbcon_info_from_console(ops->currcon); 	if (info == fb_info) {		struct fbcon_display *p = &fb_display[ops->currcon]; 		if (rotate < 4)			p->con_rotate = rotate;		else			p->con_rotate = 0; 		fbcon_modechanged(info);	}} static void fbcon_rotate_all(struct fb_info *info, u32 rotate){	struct fbcon_ops *ops = info->fbcon_par;	struct vc_data *vc;	struct fbcon_display *p;	int i; 	if (!ops || ops->currcon < 0 || rotate > 3)		return; 	for (i = first_fb_vc; i <= last_fb_vc; i++) {		vc = vc_cons[i].d;		if (!vc || vc->vc_mode != KD_TEXT ||		    fbcon_info_from_console(i) != info)			continue; 		p = &fb_display[vc->vc_num];		p->con_rotate = rotate;	} 	fbcon_set_all_vcs(info);}#elsestatic inline void fbcon_set_rotation(struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	ops->rotate = FB_ROTATE_UR;} static void fbcon_rotate(struct fb_info *info, u32 rotate){	return;} static void fbcon_rotate_all(struct fb_info *info, u32 rotate){	return;}#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */ static int fbcon_get_rotate(struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	return (ops) ? ops->rotate : 0;} static bool fbcon_skip_panic(struct fb_info *info){/* panic_cpu is not exported, and can't be used if built as module. Use * oops_in_progress instead, but non-fatal oops won't be printed. */#if defined(MODULE)	return (info->skip_panic && unlikely(oops_in_progress));#else	return (info->skip_panic && unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID));#endif} static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	return (info->state != FBINFO_STATE_RUNNING ||		vc->vc_mode != KD_TEXT || ops->graphics || fbcon_skip_panic(info));} static int get_color(struct vc_data *vc, struct fb_info *info,	      u16 c, int is_fg){	int depth = fb_get_color_depth(&info->var, &info->fix);	int color = 0; 	if (console_blanked) {		unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; 		c = vc->vc_video_erase_char & charmask;	} 	if (depth != 1)		color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)			: attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c); 	switch (depth) {	case 1:	{		int col = mono_col(info);		/* 0 or 1 */		int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;		int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col; 		if (console_blanked)			fg = bg; 		color = (is_fg) ? fg : bg;		break;	}	case 2:		/*		 * Scale down 16-colors to 4 colors. Default 4-color palette		 * is grayscale. However, simply dividing the values by 4		 * will not work, as colors 1, 2 and 3 will be scaled-down		 * to zero rendering them invisible.  So empirically convert		 * colors to a sane 4-level grayscale.		 */		switch (color) {		case 0:			color = 0; /* black */			break;		case 1 ... 6:			color = 2; /* white */			break;		case 7 ... 8:			color = 1; /* gray */			break;		default:			color = 3; /* intense white */			break;		}		break;	case 3:		/*		 * Last 8 entries of default 16-color palette is a more intense		 * version of the first 8 (i.e., same chrominance, different		 * luminance).		 */		color &= 7;		break;	}  	return color;} static void fb_flashcursor(struct work_struct *work){	struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);	struct fb_info *info;	struct vc_data *vc = NULL;	int c;	bool enable;	int ret; 	/* FIXME: we should sort out the unbind locking instead */	/* instead we just fail to flash the cursor if we can't get	 * the lock instead of blocking fbcon deinit */	ret = console_trylock();	if (ret == 0)		return; 	/* protected by console_lock */	info = ops->info; 	if (ops->currcon != -1)		vc = vc_cons[ops->currcon].d; 	if (!vc || !con_is_visible(vc) ||	    fbcon_info_from_console(vc->vc_num) != info ||	    vc->vc_deccm != 1) {		console_unlock();		return;	} 	c = scr_readw((u16 *) vc->vc_pos);	enable = ops->cursor_flash && !ops->cursor_state.enable;	ops->cursor(vc, info, enable, get_color(vc, info, c, 1),		    get_color(vc, info, c, 0));	console_unlock(); 	queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,			   ops->cur_blink_jiffies);} static void fbcon_add_cursor_work(struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	if (!fbcon_cursor_noblink)		queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,				   ops->cur_blink_jiffies);} static void fbcon_del_cursor_work(struct fb_info *info){	struct fbcon_ops *ops = info->fbcon_par; 	cancel_delayed_work_sync(&ops->cursor_work);} #ifndef MODULEstatic int __init fb_console_setup(char *this_opt){	char *options;	int i, j; 	if (!this_opt || !*this_opt)		return 1; 	while ((options = strsep(&this_opt, ",")) != NULL) {		if (!strncmp(options, "font:", 5)) {			strscpy(fontname, options + 5, sizeof(fontname));			continue;		} 		if (!strncmp(options, "scrollback:", 11)) {			pr_warn("Ignoring scrollback size option\n");			continue;		} 		if (!strncmp(options, "map:", 4)) {			options += 4;			if (*options) {				for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {					if (!options[j])						j = 0;					con2fb_map_boot[i] =						(options[j++]-'0') % FB_MAX;				} 				fbcon_map_override();			}			continue;		} 		if (!strncmp(options, "vc:", 3)) {			options += 3;			if (*options)				first_fb_vc = simple_strtoul(options, &options, 10) - 1;			if (first_fb_vc >= MAX_NR_CONSOLES)				first_fb_vc = 0;			if (*options++ == '-')				last_fb_vc = simple_strtoul(options, &options, 10) - 1;			if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)				last_fb_vc = MAX_NR_CONSOLES - 1;			fbcon_is_default = 0;			continue;		} 		if (!strncmp(options, "rotate:", 7)) {			options += 7;			if (*options)				initial_rotation = simple_strtoul(options, &options, 0);			if (initial_rotation > 3)				initial_rotation = 0;			continue;		} 		if (!strncmp(options, "margin:", 7)) {			options += 7;			if (*options)				margin_color = simple_strtoul(options, &options, 0);			continue;		}#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER		if (!strcmp(options, "nodefer")) {			deferred_takeover = false;			continue;		}#endif #ifdef CONFIG_LOGO		if (!strncmp(options, "logo-pos:", 9)) {			options += 9;			if (!strcmp(options, "center"))				fb_center_logo = true;			continue;		} 		if (!strncmp(options, "logo-count:", 11)) {			options += 11;			if (*options)				fb_logo_count = simple_strtol(options, &options, 0);			continue;

Lines 1–500 of 3418.