diff options
Diffstat (limited to 'drivers/video/console/fbcon.c')
| -rw-r--r-- | drivers/video/console/fbcon.c | 146 | 
1 files changed, 99 insertions, 47 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 7ccc967831f..57b1d44acbf 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -77,7 +77,6 @@  #include <linux/crc32.h> /* For counting font checksums */  #include <asm/fb.h>  #include <asm/irq.h> -#include <asm/system.h>  #include "fbcon.h" @@ -370,29 +369,34 @@ static void fb_flashcursor(struct work_struct *work)  {  	struct fb_info *info = container_of(work, struct fb_info, queue);  	struct fbcon_ops *ops = info->fbcon_par; -	struct display *p;  	struct vc_data *vc = NULL;  	int c;  	int mode; +	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; -	acquire_console_sem();  	if (ops && ops->currcon != -1)  		vc = vc_cons[ops->currcon].d;  	if (!vc || !CON_IS_VISIBLE(vc) ||   	    registered_fb[con2fb_map[vc->vc_num]] != info ||  	    vc->vc_deccm != 1) { -		release_console_sem(); +		console_unlock();  		return;  	} -	p = &fb_display[vc->vc_num];  	c = scr_readw((u16 *) vc->vc_pos);  	mode = (!ops->cursor_flash || ops->cursor_state.enable) ?  		CM_ERASE : CM_DRAW;  	ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1),  		    get_color(vc, info, c, 0)); -	release_console_sem(); +	console_unlock();  }  static void cursor_timer_handler(unsigned long dev_addr) @@ -400,7 +404,7 @@ static void cursor_timer_handler(unsigned long dev_addr)  	struct fb_info *info = (struct fb_info *) dev_addr;  	struct fbcon_ops *ops = info->fbcon_par; -	schedule_work(&info->queue); +	queue_work(system_power_efficient_wq, &info->queue);  	mod_timer(&ops->cursor_timer, jiffies + HZ/5);  } @@ -445,7 +449,7 @@ static int __init fb_console_setup(char *this_opt)  	while ((options = strsep(&this_opt, ",")) != NULL) {  		if (!strncmp(options, "font:", 5)) -			strcpy(fontname, options + 5); +			strlcpy(fontname, options + 5, sizeof(fontname));  		if (!strncmp(options, "scrollback:", 11)) {  			options += 11; @@ -525,7 +529,7 @@ static int search_for_mapped_con(void)  	return retval;  } -static int fbcon_takeover(int show_logo) +static int do_fbcon_takeover(int show_logo)  {  	int err, i; @@ -538,13 +542,12 @@ static int fbcon_takeover(int show_logo)  	for (i = first_fb_vc; i <= last_fb_vc; i++)  		con2fb_map[i] = info_idx; -	err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, +	err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,  				fbcon_is_default);  	if (err) { -		for (i = first_fb_vc; i <= last_fb_vc; i++) { +		for (i = first_fb_vc; i <= last_fb_vc; i++)  			con2fb_map[i] = -1; -		}  		info_idx = -1;  	} else {  		fbcon_has_console_bind = 1; @@ -745,6 +748,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,  		fbcon_del_cursor_timer(oldinfo);  		kfree(ops->cursor_state.mask);  		kfree(ops->cursor_data); +		kfree(ops->cursor_src);  		kfree(ops->fontbuffer);  		kfree(oldinfo->fbcon_par);  		oldinfo->fbcon_par = NULL; @@ -756,7 +760,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,  		  newinfo in an undefined state. Thus, a call to  		  fb_set_par() may be needed for the newinfo.  		*/ -		if (newinfo->fbops->fb_set_par) { +		if (newinfo && newinfo->fbops->fb_set_par) {  			ret = newinfo->fbops->fb_set_par(newinfo);  			if (ret) @@ -811,6 +815,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,   *   *	Maps a virtual console @unit to a frame buffer device   *	@newidx. + * + *	This should be called with the console lock held.   */  static int set_con2fb_map(int unit, int newidx, int user)  { @@ -823,12 +829,12 @@ static int set_con2fb_map(int unit, int newidx, int user)  	if (oldidx == newidx)  		return 0; -	if (!info || fbcon_has_exited) +	if (!info)  		return -EINVAL; - 	if (!err && !search_for_mapped_con()) { +	if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {  		info_idx = newidx; -		return fbcon_takeover(0); +		return do_fbcon_takeover(0);  	}  	if (oldidx != -1) @@ -836,7 +842,6 @@ static int set_con2fb_map(int unit, int newidx, int user)  	found = search_fb_in_map(newidx); -	acquire_console_sem();  	con2fb_map[unit] = newidx;  	if (!err && !found)   		err = con2fb_acquire_newinfo(vc, info, unit, oldidx); @@ -863,14 +868,13 @@ static int set_con2fb_map(int unit, int newidx, int user)  	if (!search_fb_in_map(info_idx))  		info_idx = newidx; -	release_console_sem();   	return err;  }  /*   *  Low Level Operations   */ -/* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ +/* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */  static int var_to_display(struct display *disp,  			  struct fb_var_screeninfo *var,  			  struct fb_info *info) @@ -986,7 +990,7 @@ static const char *fbcon_startup(void)  	}  	/* Setup default font */ -	if (!p->fontdata) { +	if (!p->fontdata && !vc->vc_font.data) {  		if (!fontname[0] || !(font = find_font(fontname)))  			font = get_default_font(info->var.xres,  						info->var.yres, @@ -996,6 +1000,8 @@ static const char *fbcon_startup(void)  		vc->vc_font.height = font->height;  		vc->vc_font.data = (void *)(p->fontdata = font->data);  		vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */ +	} else { +		p->fontdata = vc->vc_font.data;  	}  	cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); @@ -1155,9 +1161,9 @@ static void fbcon_init(struct vc_data *vc, int init)  	ops->p = &fb_display[fg_console];  } -static void fbcon_free_font(struct display *p) +static void fbcon_free_font(struct display *p, bool freefont)  { -	if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) +	if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))  		kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));  	p->fontdata = NULL;  	p->userfont = 0; @@ -1169,8 +1175,8 @@ static void fbcon_deinit(struct vc_data *vc)  	struct fb_info *info;  	struct fbcon_ops *ops;  	int idx; +	bool free_font = true; -	fbcon_free_font(p);  	idx = con2fb_map[vc->vc_num];  	if (idx == -1) @@ -1181,6 +1187,8 @@ static void fbcon_deinit(struct vc_data *vc)  	if (!info)  		goto finished; +	if (info->flags & FBINFO_MISC_FIRMWARE) +		free_font = false;  	ops = info->fbcon_par;  	if (!ops) @@ -1192,6 +1200,10 @@ static void fbcon_deinit(struct vc_data *vc)  	ops->flags &= ~FBCON_FLAGS_INIT;  finished: +	fbcon_free_font(p, free_font); +	if (free_font) +		vc->vc_font.data = NULL; +  	if (!con_is_bound(&fb_con))  		fbcon_exit(); @@ -1238,8 +1250,16 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,  	if (!height || !width)  		return; -	if (sy < vc->vc_top && vc->vc_top == logo_lines) +	if (sy < vc->vc_top && vc->vc_top == logo_lines) {  		vc->vc_top = 0; +		/* +		 * If the font dimensions are not an integral of the display +		 * dimensions then the ops->clear below won't end up clearing +		 * the margins.  Call clear_margins here in case the logo +		 * bitmap stretched into the margin area. +		 */ +		fbcon_clear_margins(vc, 0); +	}  	/* Split blits that cross physical y_wrap boundary */ @@ -2973,7 +2993,7 @@ static int fbcon_unbind(void)  {  	int ret; -	ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, +	ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,  				fbcon_is_default);  	if (!ret) @@ -2988,6 +3008,7 @@ static inline int fbcon_unbind(void)  }  #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ +/* called with console_lock held */  static int fbcon_fb_unbind(int idx)  {  	int i, new_idx = -1, ret = 0; @@ -3008,12 +3029,36 @@ static int fbcon_fb_unbind(int idx)  			if (con2fb_map[i] == idx)  				set_con2fb_map(i, new_idx, 0);  		} -	} else +	} else { +		struct fb_info *info = registered_fb[idx]; + +		/* This is sort of like set_con2fb_map, except it maps +		 * the consoles to no device and then releases the +		 * oldinfo to free memory and cancel the cursor blink +		 * timer. I can imagine this just becoming part of +		 * set_con2fb_map where new_idx is -1 +		 */ +		for (i = first_fb_vc; i <= last_fb_vc; i++) { +			if (con2fb_map[i] == idx) { +				con2fb_map[i] = -1; +				if (!search_fb_in_map(idx)) { +					ret = con2fb_release_oldinfo(vc_cons[i].d, +								     info, NULL, i, +								     idx, 0); +					if (ret) { +						con2fb_map[i] = idx; +						return ret; +					} +				} +			} +		}  		ret = fbcon_unbind(); +	}  	return ret;  } +/* called with console_lock held */  static int fbcon_fb_unregistered(struct fb_info *info)  {  	int i, idx; @@ -3046,11 +3091,12 @@ static int fbcon_fb_unregistered(struct fb_info *info)  		primary_device = -1;  	if (!num_registered_fb) -		unregister_con_driver(&fb_con); +		do_unregister_con_driver(&fb_con);  	return 0;  } +/* called with console_lock held */  static void fbcon_remap_all(int idx)  {  	int i; @@ -3095,6 +3141,7 @@ static inline void fbcon_select_primary(struct fb_info *info)  }  #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +/* called with console_lock held */  static int fbcon_fb_registered(struct fb_info *info)  {  	int ret = 0, i, idx; @@ -3111,7 +3158,7 @@ static int fbcon_fb_registered(struct fb_info *info)  		}  		if (info_idx != -1) -			ret = fbcon_takeover(1); +			ret = do_fbcon_takeover(1);  	} else {  		for (i = first_fb_vc; i <= last_fb_vc; i++) {  			if (con2fb_map_boot[i] == idx) @@ -3247,6 +3294,7 @@ static int fbcon_event_notify(struct notifier_block *self,  		ret = fbcon_fb_unregistered(info);  		break;  	case FB_EVENT_SET_CONSOLE_MAP: +		/* called with console lock held */  		con2fb = event->data;  		ret = set_con2fb_map(con2fb->console - 1,  				     con2fb->framebuffer, 1); @@ -3321,7 +3369,7 @@ static ssize_t store_rotate(struct device *device,  	if (fbcon_has_exited)  		return count; -	acquire_console_sem(); +	console_lock();  	idx = con2fb_map[fg_console];  	if (idx == -1 || registered_fb[idx] == NULL) @@ -3331,7 +3379,7 @@ static ssize_t store_rotate(struct device *device,  	rotate = simple_strtoul(buf, last, 0);  	fbcon_rotate(info, rotate);  err: -	release_console_sem(); +	console_unlock();  	return count;  } @@ -3346,7 +3394,7 @@ static ssize_t store_rotate_all(struct device *device,  	if (fbcon_has_exited)  		return count; -	acquire_console_sem(); +	console_lock();  	idx = con2fb_map[fg_console];  	if (idx == -1 || registered_fb[idx] == NULL) @@ -3356,7 +3404,7 @@ static ssize_t store_rotate_all(struct device *device,  	rotate = simple_strtoul(buf, last, 0);  	fbcon_rotate_all(info, rotate);  err: -	release_console_sem(); +	console_unlock();  	return count;  } @@ -3369,7 +3417,7 @@ static ssize_t show_rotate(struct device *device,  	if (fbcon_has_exited)  		return 0; -	acquire_console_sem(); +	console_lock();  	idx = con2fb_map[fg_console];  	if (idx == -1 || registered_fb[idx] == NULL) @@ -3378,7 +3426,7 @@ static ssize_t show_rotate(struct device *device,  	info = registered_fb[idx];  	rotate = fbcon_get_rotate(info);  err: -	release_console_sem(); +	console_unlock();  	return snprintf(buf, PAGE_SIZE, "%d\n", rotate);  } @@ -3392,7 +3440,7 @@ static ssize_t show_cursor_blink(struct device *device,  	if (fbcon_has_exited)  		return 0; -	acquire_console_sem(); +	console_lock();  	idx = con2fb_map[fg_console];  	if (idx == -1 || registered_fb[idx] == NULL) @@ -3406,7 +3454,7 @@ static ssize_t show_cursor_blink(struct device *device,  	blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0;  err: -	release_console_sem(); +	console_unlock();  	return snprintf(buf, PAGE_SIZE, "%d\n", blink);  } @@ -3421,7 +3469,7 @@ static ssize_t store_cursor_blink(struct device *device,  	if (fbcon_has_exited)  		return count; -	acquire_console_sem(); +	console_lock();  	idx = con2fb_map[fg_console];  	if (idx == -1 || registered_fb[idx] == NULL) @@ -3443,7 +3491,7 @@ static ssize_t store_cursor_blink(struct device *device,  	}  err: -	release_console_sem(); +	console_unlock();  	return count;  } @@ -3482,7 +3530,7 @@ static void fbcon_start(void)  	if (num_registered_fb) {  		int i; -		acquire_console_sem(); +		console_lock();  		for (i = 0; i < FB_MAX; i++) {  			if (registered_fb[i] != NULL) { @@ -3491,8 +3539,9 @@ static void fbcon_start(void)  			}  		} -		release_console_sem(); -		fbcon_takeover(0); +		do_fbcon_takeover(0); +		console_unlock(); +  	}  } @@ -3522,8 +3571,10 @@ static void fbcon_exit(void)  			"no"));  		for (j = first_fb_vc; j <= last_fb_vc; j++) { -			if (con2fb_map[j] == i) +			if (con2fb_map[j] == i) {  				mapped = 1; +				break; +			}  		}  		if (mapped) { @@ -3536,6 +3587,7 @@ static void fbcon_exit(void)  				fbcon_del_cursor_timer(info);  				kfree(ops->cursor_src); +				kfree(ops->cursor_state.mask);  				kfree(info->fbcon_par);  				info->fbcon_par = NULL;  			} @@ -3552,7 +3604,7 @@ static int __init fb_console_init(void)  {  	int i; -	acquire_console_sem(); +	console_lock();  	fb_register_client(&fbcon_event_notifier);  	fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,  				     "fbcon"); @@ -3568,7 +3620,7 @@ static int __init fb_console_init(void)  	for (i = 0; i < MAX_NR_CONSOLES; i++)  		con2fb_map[i] = -1; -	release_console_sem(); +	console_unlock();  	fbcon_start();  	return 0;  } @@ -3591,13 +3643,13 @@ static void __exit fbcon_deinit_device(void)  static void __exit fb_console_exit(void)  { -	acquire_console_sem(); +	console_lock();  	fb_unregister_client(&fbcon_event_notifier);  	fbcon_deinit_device();  	device_destroy(fb_class, MKDEV(0, 0));  	fbcon_exit(); -	release_console_sem(); -	unregister_con_driver(&fb_con); +	do_unregister_con_driver(&fb_con); +	console_unlock();  }	  module_exit(fb_console_exit);  | 
