diff options
Diffstat (limited to 'drivers/video/console/fbcon.c')
| -rw-r--r-- | drivers/video/console/fbcon.c | 453 |
1 files changed, 237 insertions, 216 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 3ccfa76d9b2..57b1d44acbf 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -77,17 +77,6 @@ #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" @@ -124,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; @@ -158,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; @@ -210,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) { @@ -310,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); @@ -396,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); } @@ -485,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; @@ -565,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; @@ -578,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; @@ -767,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)) { @@ -783,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; @@ -794,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; @@ -805,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; @@ -836,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) { @@ -848,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) @@ -861,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); @@ -888,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) @@ -949,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. @@ -1013,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, @@ -1023,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); @@ -1037,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; @@ -1104,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; @@ -1155,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) { @@ -1180,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 @@ -1191,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; } @@ -1209,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); @@ -1228,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; @@ -1242,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) @@ -1254,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) @@ -1265,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(); @@ -1311,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; @@ -1852,8 +1802,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - unsigned short saved_ec; - int ret; if (fbcon_is_inactive(vc, info)) return -EINVAL; @@ -1866,11 +1814,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, * whole screen (prevents flicker). */ - saved_ec = vc->vc_video_erase_char; - vc->vc_video_erase_char = vc->vc_scrl_erase_char; - - ret = 0; - switch (dir) { case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ @@ -1887,9 +1830,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -1959,10 +1902,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } break; @@ -1979,9 +1921,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -2049,15 +1991,12 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } - break; } - vc->vc_video_erase_char = saved_ec; - return ret; + return 0; } @@ -2126,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) { @@ -2225,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; @@ -2280,8 +2219,14 @@ static int fbcon_switch(struct vc_data *vc) 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); @@ -2369,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 = ␣ 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) @@ -2385,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)) { @@ -2401,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) @@ -2418,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; @@ -2515,9 +2485,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, c = vc->vc_video_erase_char; vc->vc_video_erase_char = ((c & 0xfe00) >> 1) | (c & 0xff); - c = vc->vc_def_color; - vc->vc_scrl_erase_char = - ((c & 0xFE00) >> 1) | (c & 0xFF); vc->vc_attr >>= 1; } } else if (!vc->vc_hi_font_mask && cnt == 512) { @@ -2548,14 +2515,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, if (vc->vc_can_do_color) { vc->vc_video_erase_char = ((c & 0xff00) << 1) | (c & 0xff); - c = vc->vc_def_color; - vc->vc_scrl_erase_char = - ((c & 0xFF00) << 1) | (c & 0xFF); vc->vc_attr <<= 1; - } else { + } else vc->vc_video_erase_char = c & ~0x100; - vc->vc_scrl_erase_char = c & ~0x100; - } } } @@ -2989,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); @@ -3031,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 @@ -3042,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) { @@ -3059,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; @@ -3092,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) { @@ -3132,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) { @@ -3147,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) @@ -3237,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; @@ -3245,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 @@ -3273,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); @@ -3282,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); @@ -3300,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; } @@ -3334,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 = { @@ -3351,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) @@ -3361,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; } @@ -3376,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) @@ -3386,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; } @@ -3399,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) @@ -3408,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); } @@ -3422,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) @@ -3436,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); } @@ -3451,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) @@ -3473,7 +3491,7 @@ static ssize_t store_cursor_blink(struct device *device, } err: - release_console_sem(); + console_unlock(); return count; } @@ -3512,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) { @@ -3521,8 +3539,9 @@ static void fbcon_start(void) } } - release_console_sem(); - fbcon_takeover(0); + do_fbcon_takeover(0); + console_unlock(); + } } @@ -3534,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) { @@ -3567,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; } @@ -3583,10 +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_drvdata(fb_class, NULL, MKDEV(0, 0), - NULL, "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 " @@ -3599,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; } @@ -3622,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); |
