aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/console/fbcon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/console/fbcon.c')
-rw-r--r--drivers/video/console/fbcon.c427
1 files changed, 232 insertions, 195 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 022282494d3..57b1d44acbf 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -77,22 +77,11 @@
#include <linux/crc32.h> /* For counting font checksums */
#include <asm/fb.h>
#include <asm/irq.h>
-#include <asm/system.h>
-#ifdef CONFIG_ATARI
-#include <asm/atariints.h>
-#endif
-#ifdef CONFIG_MAC
-#include <asm/macints.h>
-#endif
-#if defined(__mc68000__)
-#include <asm/machdep.h>
-#include <asm/setup.h>
-#endif
#include "fbcon.h"
#ifdef FBCONDEBUG
-# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
#else
# define DPRINTK(fmt, args...)
#endif
@@ -107,9 +96,7 @@ static struct display fb_display[MAX_NR_CONSOLES];
static signed char con2fb_map[MAX_NR_CONSOLES];
static signed char con2fb_map_boot[MAX_NR_CONSOLES];
-#ifndef MODULE
-static int logo_height;
-#endif
+
static int logo_lines;
/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
enums. */
@@ -126,6 +113,7 @@ static int last_fb_vc = MAX_NR_CONSOLES - 1;
static int fbcon_is_default = 1;
static int fbcon_has_exited;
static int primary_device = -1;
+static int fbcon_has_console_bind;
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
static int map_override;
@@ -160,11 +148,6 @@ static int fbcon_set_origin(struct vc_data *);
#define CURSOR_DRAW_DELAY (1)
-/* # VBL ints between cursor state changes */
-#define ATARI_CURSOR_BLINK_RATE (42)
-#define MAC_CURSOR_BLINK_RATE (32)
-#define DEFAULT_CURSOR_BLINK_RATE (20)
-
static int vbl_cursor_cnt;
static int fbcon_cursor_noblink;
@@ -212,19 +195,6 @@ static void fbcon_start(void);
static void fbcon_exit(void);
static struct device *fbcon_device;
-#ifdef CONFIG_MAC
-/*
- * On the Macintoy, there may or may not be a working VBL int. We need to probe
- */
-static int vbl_detected;
-
-static irqreturn_t fb_vbl_detect(int irq, void *dummy)
-{
- vbl_detected++;
- return IRQ_HANDLED;
-}
-#endif
-
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
static inline void fbcon_set_rotation(struct fb_info *info)
{
@@ -312,10 +282,11 @@ 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);
+ vc->vc_mode != KD_TEXT || ops->graphics) &&
+ !vt_force_oops_output(vc);
}
-static inline int get_color(struct vc_data *vc, struct fb_info *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);
@@ -398,51 +369,42 @@ 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();
}
-#if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
-static int cursor_blink_rate;
-static irqreturn_t fb_vbl_handler(int irq, void *dev_id)
-{
- struct fb_info *info = dev_id;
-
- if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
- schedule_work(&info->queue);
- vbl_cursor_cnt = cursor_blink_rate;
- }
- return IRQ_HANDLED;
-}
-#endif
-
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);
}
@@ -487,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;
@@ -567,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;
@@ -580,14 +542,15 @@ 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;
}
return err;
@@ -607,6 +570,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
struct fbcon_ops *ops = info->fbcon_par;
int cnt, erase = vc->vc_video_erase_char, step;
unsigned short *save = NULL, *r, *q;
+ int logo_height;
if (info->flags & FBINFO_MODULE) {
logo_shown = FBCON_LOGO_DONTSHOW;
@@ -620,8 +584,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
if (fb_get_color_depth(&info->var, &info->fix) == 1)
erase &= ~0x400;
logo_height = fb_prepare_logo(info, ops->rotate);
- logo_lines = (logo_height + vc->vc_font.height - 1) /
- vc->vc_font.height;
+ logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
q = (unsigned short *) (vc->vc_origin +
vc->vc_size_row * rows);
step = logo_lines * cols;
@@ -769,7 +732,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
int oldidx, int found)
{
struct fbcon_ops *ops = oldinfo->fbcon_par;
- int err = 0;
+ int err = 0, ret;
if (oldinfo->fbops->fb_release &&
oldinfo->fbops->fb_release(oldinfo, 0)) {
@@ -785,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;
@@ -796,8 +760,14 @@ 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)
- newinfo->fbops->fb_set_par(newinfo);
+ if (newinfo && newinfo->fbops->fb_set_par) {
+ ret = newinfo->fbops->fb_set_par(newinfo);
+
+ if (ret)
+ printk(KERN_ERR "con2fb_release_oldinfo: "
+ "detected unhandled fb_set_par error, "
+ "error code %d\n", ret);
+ }
}
return err;
@@ -807,11 +777,18 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
int unit, int show_logo)
{
struct fbcon_ops *ops = info->fbcon_par;
+ int ret;
ops->currcon = fg_console;
- if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT))
- info->fbops->fb_set_par(info);
+ if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret)
+ printk(KERN_ERR "con2fb_init_display: detected "
+ "unhandled fb_set_par error, "
+ "error code %d\n", ret);
+ }
ops->flags |= FBCON_FLAGS_INIT;
ops->graphics = 0;
@@ -838,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)
{
@@ -850,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)
@@ -863,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);
@@ -890,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)
@@ -951,9 +928,7 @@ static const char *fbcon_startup(void)
struct fb_info *info = NULL;
struct fbcon_ops *ops;
int rows, cols;
- int irqres;
- irqres = 1;
/*
* If num_registered_fb is zero, this is a call for the dummy part.
* The frame buffer devices weren't initialized yet.
@@ -1015,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,
@@ -1025,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);
@@ -1039,60 +1016,6 @@ static const char *fbcon_startup(void)
info->var.yres,
info->var.bits_per_pixel);
-#ifdef CONFIG_ATARI
- if (MACH_IS_ATARI) {
- cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
- irqres =
- request_irq(IRQ_AUTO_4, fb_vbl_handler,
- IRQ_TYPE_PRIO, "framebuffer vbl",
- info);
- }
-#endif /* CONFIG_ATARI */
-
-#ifdef CONFIG_MAC
- /*
- * On a Macintoy, the VBL interrupt may or may not be active.
- * As interrupt based cursor is more reliable and race free, we
- * probe for VBL interrupts.
- */
- if (MACH_IS_MAC) {
- int ct = 0;
- /*
- * Probe for VBL: set temp. handler ...
- */
- irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
- "framebuffer vbl", info);
- vbl_detected = 0;
-
- /*
- * ... and spin for 20 ms ...
- */
- while (!vbl_detected && ++ct < 1000)
- udelay(20);
-
- if (ct == 1000)
- printk
- ("fbcon_startup: No VBL detected, using timer based cursor.\n");
-
- free_irq(IRQ_MAC_VBL, fb_vbl_detect);
-
- if (vbl_detected) {
- /*
- * interrupt based cursor ok
- */
- cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
- irqres =
- request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
- "framebuffer vbl", info);
- } else {
- /*
- * VBL not detected: fall through, use timer based cursor
- */
- irqres = 1;
- }
- }
-#endif /* CONFIG_MAC */
-
fbcon_add_cursor_timer(info);
fbcon_has_exited = 0;
return display_desc;
@@ -1106,7 +1029,7 @@ static void fbcon_init(struct vc_data *vc, int init)
struct vc_data *svc = *default_mode;
struct display *t, *p = &fb_display[vc->vc_num];
int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
- int cap;
+ int cap, ret;
if (info_idx == -1 || info == NULL)
return;
@@ -1157,6 +1080,7 @@ static void fbcon_init(struct vc_data *vc, int init)
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
+ vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT);
vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
if (charcnt == 256) {
@@ -1182,7 +1106,6 @@ static void fbcon_init(struct vc_data *vc, int init)
new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
new_cols /= vc->vc_font.width;
new_rows /= vc->vc_font.height;
- vc_resize(vc, new_cols, new_rows);
/*
* We must always set the mode. The mode of the previous console
@@ -1193,8 +1116,15 @@ static void fbcon_init(struct vc_data *vc, int init)
*/
if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) {
if (info->fbops->fb_set_par &&
- !(ops->flags & FBCON_FLAGS_INIT))
- info->fbops->fb_set_par(info);
+ !(ops->flags & FBCON_FLAGS_INIT)) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret)
+ printk(KERN_ERR "fbcon_init: detected "
+ "unhandled fb_set_par error, "
+ "error code %d\n", ret);
+ }
+
ops->flags |= FBCON_FLAGS_INIT;
}
@@ -1211,10 +1141,11 @@ static void fbcon_init(struct vc_data *vc, int init)
* vc_{cols,rows}, but we must not set those if we are only
* resizing the console.
*/
- if (!init) {
+ if (init) {
vc->vc_cols = new_cols;
vc->vc_rows = new_rows;
- }
+ } else
+ vc_resize(vc, new_cols, new_rows);
if (logo)
fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
@@ -1230,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;
@@ -1244,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)
@@ -1256,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)
@@ -1267,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();
@@ -1313,6 +1250,17 @@ 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) {
+ 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 */
y_break = p->vrows - p->yscroll;
@@ -2117,7 +2065,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
height, width);
}
-static __inline__ void updatescrollmode(struct display *p,
+static void updatescrollmode(struct display *p,
struct fb_info *info,
struct vc_data *vc)
{
@@ -2216,7 +2164,7 @@ static int fbcon_switch(struct vc_data *vc)
struct fbcon_ops *ops;
struct display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
- int i, prev_console, charcnt = 256;
+ int i, ret, prev_console, charcnt = 256;
info = registered_fb[con2fb_map[vc->vc_num]];
ops = info->fbcon_par;
@@ -2265,16 +2213,20 @@ static int fbcon_switch(struct vc_data *vc)
* in fb_set_var()
*/
info->var.activate = var.activate;
- var.yoffset = info->var.yoffset;
- var.xoffset = info->var.xoffset;
- var.vmode = info->var.vmode;
+ var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
fb_set_var(info, &var);
ops->var = info->var;
if (old_info != NULL && (old_info != info ||
info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
- if (info->fbops->fb_set_par)
- info->fbops->fb_set_par(info);
+ if (info->fbops->fb_set_par) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret)
+ printk(KERN_ERR "fbcon_switch: detected "
+ "unhandled fb_set_par error, "
+ "error code %d\n", ret);
+ }
if (old_info != info)
fbcon_del_cursor_timer(old_info);
@@ -2362,9 +2314,12 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
}
+ if (!lock_fb_info(info))
+ return;
event.info = info;
event.data = &blank;
fb_notifier_call_chain(FB_EVENT_CONBLANK, &event);
+ unlock_fb_info(info);
}
static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
@@ -2378,14 +2333,11 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
ops->graphics = 1;
if (!blank) {
- if (info->fbops->fb_save_state)
- info->fbops->fb_save_state(info);
var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
fb_set_var(info, &var);
ops->graphics = 0;
ops->var = info->var;
- } else if (info->fbops->fb_restore_state)
- info->fbops->fb_restore_state(info);
+ }
}
if (!fbcon_is_inactive(vc, info)) {
@@ -2394,8 +2346,9 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
ops->cursor_flash = (!blank);
- if (fb_blank(info, blank))
- fbcon_generic_blank(vc, info, blank);
+ if (!(info->flags & FBINFO_MISC_USEREVENT))
+ if (fb_blank(info, blank))
+ fbcon_generic_blank(vc, info, blank);
}
if (!blank)
@@ -2411,6 +2364,30 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
return 0;
}
+static int fbcon_debug_enter(struct vc_data *vc)
+{
+ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+ struct fbcon_ops *ops = info->fbcon_par;
+
+ ops->save_graphics = ops->graphics;
+ ops->graphics = 0;
+ if (info->fbops->fb_debug_enter)
+ info->fbops->fb_debug_enter(info);
+ fbcon_set_palette(vc, color_table);
+ return 0;
+}
+
+static int fbcon_debug_leave(struct vc_data *vc)
+{
+ struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+ struct fbcon_ops *ops = info->fbcon_par;
+
+ ops->graphics = ops->save_graphics;
+ if (info->fbops->fb_debug_leave)
+ info->fbops->fb_debug_leave(info);
+ return 0;
+}
+
static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
{
u8 *fontdata = vc->vc_font.data;
@@ -2974,8 +2951,8 @@ static void fbcon_set_all_vcs(struct fb_info *info)
p = &fb_display[vc->vc_num];
set_blitting_type(vc, info);
var_to_display(p, &info->var, info);
- cols = FBCON_SWAP(p->rotate, info->var.xres, info->var.yres);
- rows = FBCON_SWAP(p->rotate, info->var.yres, info->var.xres);
+ cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+ rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
cols /= vc->vc_font.width;
rows /= vc->vc_font.height;
vc_resize(vc, cols, rows);
@@ -3016,8 +2993,12 @@ 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)
+ fbcon_has_console_bind = 0;
+
return ret;
}
#else
@@ -3027,10 +3008,14 @@ 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;
+ if (!fbcon_has_console_bind)
+ return 0;
+
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] != idx &&
con2fb_map[i] != -1) {
@@ -3044,16 +3029,41 @@ 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 = info->node;
+ int i, idx;
+ idx = info->node;
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] == idx)
con2fb_map[i] = -1;
@@ -3077,16 +3087,30 @@ static int fbcon_fb_unregistered(struct fb_info *info)
}
}
- if (!num_registered_fb)
- unregister_con_driver(&fb_con);
-
-
if (primary_device == idx)
primary_device = -1;
+ if (!num_registered_fb)
+ do_unregister_con_driver(&fb_con);
+
return 0;
}
+/* called with console_lock held */
+static void fbcon_remap_all(int idx)
+{
+ int i;
+ for (i = first_fb_vc; i <= last_fb_vc; i++)
+ set_con2fb_map(i, idx, 0);
+
+ if (con_is_bound(&fb_con)) {
+ printk(KERN_INFO "fbcon: Remapping primary device, "
+ "fb%i, to tty %i-%i\n", idx,
+ first_fb_vc + 1, last_fb_vc + 1);
+ info_idx = idx;
+ }
+}
+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
static void fbcon_select_primary(struct fb_info *info)
{
@@ -3117,10 +3141,12 @@ 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 = info->node;
+ int ret = 0, i, idx;
+ idx = info->node;
fbcon_select_primary(info);
if (info_idx == -1) {
@@ -3132,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)
@@ -3222,7 +3248,7 @@ static void fbcon_get_requirement(struct fb_info *info,
}
}
-static int fbcon_event_notify(struct notifier_block *self,
+static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = data;
@@ -3230,7 +3256,7 @@ static int fbcon_event_notify(struct notifier_block *self,
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
struct fb_blit_caps *caps;
- int ret = 0;
+ int idx, ret = 0;
/*
* ignore all events except driver registration and deregistration
@@ -3258,7 +3284,8 @@ static int fbcon_event_notify(struct notifier_block *self,
ret = fbcon_mode_deleted(info, mode);
break;
case FB_EVENT_FB_UNBIND:
- ret = fbcon_fb_unbind(info->node);
+ idx = info->node;
+ ret = fbcon_fb_unbind(idx);
break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info);
@@ -3267,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);
@@ -3285,8 +3313,11 @@ static int fbcon_event_notify(struct notifier_block *self,
caps = event->data;
fbcon_get_requirement(info, caps);
break;
+ case FB_EVENT_REMAP_ALL_CONSOLE:
+ idx = info->node;
+ fbcon_remap_all(idx);
+ break;
}
-
done:
return ret;
}
@@ -3319,6 +3350,8 @@ static const struct consw fb_con = {
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,
.con_resize = fbcon_resize,
+ .con_debug_enter = fbcon_debug_enter,
+ .con_debug_leave = fbcon_debug_leave,
};
static struct notifier_block fbcon_event_notifier = {
@@ -3336,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)
@@ -3346,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;
}
@@ -3361,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)
@@ -3371,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;
}
@@ -3384,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)
@@ -3393,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);
}
@@ -3407,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)
@@ -3421,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);
}
@@ -3436,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)
@@ -3458,7 +3491,7 @@ static ssize_t store_cursor_blink(struct device *device,
}
err:
- release_console_sem();
+ console_unlock();
return count;
}
@@ -3497,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) {
@@ -3506,8 +3539,9 @@ static void fbcon_start(void)
}
}
- release_console_sem();
- fbcon_takeover(0);
+ do_fbcon_takeover(0);
+ console_unlock();
+
}
}
@@ -3519,27 +3553,28 @@ static void fbcon_exit(void)
if (fbcon_has_exited)
return;
-#ifdef CONFIG_ATARI
- free_irq(IRQ_AUTO_4, fb_vbl_handler);
-#endif
-#ifdef CONFIG_MAC
- if (MACH_IS_MAC && vbl_detected)
- free_irq(IRQ_MAC_VBL, fb_vbl_handler);
-#endif
-
kfree((void *)softback_buf);
softback_buf = 0UL;
for (i = 0; i < FB_MAX; i++) {
+ int pending = 0;
+
mapped = 0;
info = registered_fb[i];
if (info == NULL)
continue;
+ if (info->queue.func)
+ pending = cancel_work_sync(&info->queue);
+ DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" :
+ "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) {
@@ -3552,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;
}
@@ -3568,9 +3604,10 @@ 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), "fbcon");
+ fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
+ "fbcon");
if (IS_ERR(fbcon_device)) {
printk(KERN_WARNING "Unable to create device "
@@ -3583,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;
}
@@ -3606,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);