X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fvideo%2Fconsole%2Ffbcon.c;h=661685cc823dffea40138cfbfce028be0ab71a05;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=d0f9920c269db35ab9a7b1256dde21265a8975c2;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index d0f9920c2..661685cc8 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -77,7 +77,7 @@ #include #include #include - +#include /* For counting font checksums */ #include #include #include @@ -100,11 +100,20 @@ # define DPRINTK(fmt, args...) #endif +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 */ +}; + struct display fb_display[MAX_NR_CONSOLES]; -char con2fb_map[MAX_NR_CONSOLES]; +signed char con2fb_map[MAX_NR_CONSOLES]; +signed char con2fb_map_boot[MAX_NR_CONSOLES]; static int logo_height; static int logo_lines; -static int logo_shown = -1; +/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO + enums. */ +static int logo_shown = FBCON_LOGO_CANSHOW; /* Software scrollback */ int fbcon_softback_size = 32768; static unsigned long softback_buf, softback_curr; @@ -118,11 +127,8 @@ static int fbcon_is_default = 1; /* font data */ static char fontname[40]; -#define REFCOUNT(fd) (((int *)(fd))[-1]) -#define FNTSIZE(fd) (((int *)(fd))[-2]) -#define FNTCHARCNT(fd) (((int *)(fd))[-3]) -#define FNTSUM(fd) (((int *)(fd))[-4]) -#define FONT_EXTRA_WORDS 4 +/* current fb_info */ +static int info_idx = -1; #define CM_SOFTBACK (8) @@ -155,6 +161,7 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos); static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); +static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); static void fbcon_cursor(struct vc_data *vc, int mode); static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, int count); @@ -162,11 +169,9 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, int height, int width); static int fbcon_switch(struct vc_data *vc); static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); -static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op); static int fbcon_set_palette(struct vc_data *vc, unsigned char *table); static int fbcon_scrolldelta(struct vc_data *vc, int lines); - /* * Internal routines */ @@ -177,6 +182,10 @@ static __inline__ void ypan_up(struct vc_data *vc, int count); static __inline__ void ypan_down(struct vc_data *vc, int count); static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break); +static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc); +static void fbcon_preset_disp(struct fb_info *info, int unit); +static void fbcon_redraw_move(struct vc_data *vc, struct display *p, + int line, int count, int dy); #ifdef CONFIG_MAC /* @@ -191,16 +200,85 @@ static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp) } #endif -static void fb_flashcursor(void *private) +static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) +{ + return (info->state != FBINFO_STATE_RUNNING || + vt_cons[vc->vc_num]->vc_mode != KD_TEXT); +} + +static inline int get_color(struct vc_data *vc, struct fb_info *info, + u16 c, int is_fg) { - struct fb_info *info = (struct fb_info *) private; + int depth = fb_get_color_depth(info); + 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: + { + /* 0 or 1 */ + int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0; + int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1; + + 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. + */ + color /= 4; + 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; +} - if (!info || info->state != FBINFO_STATE_RUNNING || - info->cursor.rop == ROP_COPY) +static void fb_flashcursor(void *private) +{ + struct fb_info *info = private; + struct fbcon_ops *ops = info->fbcon_par; + struct display *p; + struct vc_data *vc = NULL; + int c; + int mode; + + if (ops->currcon != -1) + vc = vc_cons[ops->currcon].d; + + if (!vc || !CON_IS_VISIBLE(vc) || + fbcon_is_inactive(vc, info) || + registered_fb[con2fb_map[vc->vc_num]] != info) return; acquire_console_sem(); - info->cursor.enable ^= 1; - info->fbops->fb_cursor(info, &info->cursor); + 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, p, mode, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); release_console_sem(); } @@ -218,17 +296,13 @@ static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp) } #endif -static void cursor_timer_handler(unsigned long dev_addr); - -static struct timer_list cursor_timer = - TIMER_INITIALIZER(cursor_timer_handler, 0, 0); - static void cursor_timer_handler(unsigned long dev_addr) { struct fb_info *info = (struct fb_info *) dev_addr; - - schedule_work(&info->queue); - mod_timer(&cursor_timer, jiffies + HZ/5); + struct fbcon_ops *ops = info->fbcon_par; + + schedule_work(&info->queue); + mod_timer(&ops->cursor_timer, jiffies + HZ/5); } int __init fb_console_setup(char *this_opt) @@ -264,7 +338,8 @@ int __init fb_console_setup(char *this_opt) for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { if (!options[j]) j = 0; - con2fb_map[i] = (options[j++]-'0') % FB_MAX; + con2fb_map_boot[i] = + (options[j++]-'0') % FB_MAX; } return 0; } @@ -285,157 +360,364 @@ int __init fb_console_setup(char *this_opt) __setup("fbcon=", fb_console_setup); +static int search_fb_in_map(int idx) +{ + int i, retval = 0; + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map[i] == idx) + retval = 1; + } + return retval; +} + +static int search_for_mapped_con(void) +{ + int i, retval = 0; + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map[i] != -1) + retval = 1; + } + return retval; +} + +static int fbcon_takeover(int show_logo) +{ + int err, i; + + if (!num_registered_fb) + return -ENODEV; + + if (!show_logo) + logo_shown = FBCON_LOGO_DONTSHOW; + + 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, + fbcon_is_default); + if (err) { + for (i = first_fb_vc; i <= last_fb_vc; i++) { + con2fb_map[i] = -1; + } + info_idx = -1; + } + + return err; +} + +static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, + int cols, int rows, int new_cols, int new_rows) +{ + /* Need to make room for the logo */ + int cnt, erase = vc->vc_video_erase_char, step; + unsigned short *save = NULL, *r, *q; + + /* + * remove underline attribute from erase character + * if black and white framebuffer. + */ + if (fb_get_color_depth(info) == 1) + erase &= ~0x400; + logo_height = fb_prepare_logo(info); + logo_lines = (logo_height + vc->vc_font.height - 1) / + vc->vc_font.height; + q = (unsigned short *) (vc->vc_origin + + vc->vc_size_row * rows); + step = logo_lines * cols; + for (r = q - logo_lines * cols; r < q; r++) + if (scr_readw(r) != vc->vc_video_erase_char) + break; + if (r != q && new_rows >= rows + logo_lines) { + save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL); + if (save) { + int i = cols < new_cols ? cols : new_cols; + scr_memsetw(save, erase, logo_lines * new_cols * 2); + r = q - step; + for (cnt = 0; cnt < logo_lines; cnt++, r += i) + scr_memcpyw(save + cnt * new_cols, r, 2 * i); + r = q; + } + } + if (r == q) { + /* We can scroll screen down */ + r = q - step - cols; + for (cnt = rows - logo_lines; cnt > 0; cnt--) { + scr_memcpyw(r + step, r, vc->vc_size_row); + r -= cols; + } + if (!save) { + vc->vc_y += logo_lines; + vc->vc_pos += logo_lines * vc->vc_size_row; + } + } + scr_memsetw((unsigned short *) vc->vc_origin, + erase, + vc->vc_size_row * logo_lines); + + if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { + fbcon_clear_margins(vc, 0); + update_screen(vc->vc_num); + } + + if (save) { + q = (unsigned short *) (vc->vc_origin + + vc->vc_size_row * + rows); + scr_memcpyw(q, save, logo_lines * new_cols * 2); + vc->vc_y += logo_lines; + vc->vc_pos += logo_lines * vc->vc_size_row; + kfree(save); + } + + if (logo_lines > vc->vc_bottom) { + logo_shown = FBCON_LOGO_CANSHOW; + printk(KERN_INFO + "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); + } else if (logo_shown != FBCON_LOGO_DONTSHOW) { + logo_shown = FBCON_LOGO_DRAW; + vc->vc_top = logo_lines; + } +} + +#ifdef CONFIG_FB_TILEBLITTING +static void set_blitting_type(struct vc_data *vc, struct fb_info *info, + struct display *p) +{ + struct fbcon_ops *ops = info->fbcon_par; + + if ((info->flags & FBINFO_MISC_TILEBLITTING)) + fbcon_set_tileops(vc, info, p, ops); + else + fbcon_set_bitops(ops); +} +#else +static void set_blitting_type(struct vc_data *vc, struct fb_info *info, + struct display *p) +{ + struct fbcon_ops *ops = info->fbcon_par; + + info->flags &= ~FBINFO_MISC_TILEBLITTING; + fbcon_set_bitops(ops); +} +#endif /* CONFIG_MISC_TILEBLITTING */ + + +static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, + int unit, int oldidx) +{ + struct fbcon_ops *ops = NULL; + int err = 0; + + if (!try_module_get(info->fbops->owner)) + err = -ENODEV; + + if (!err && info->fbops->fb_open && + info->fbops->fb_open(info, 0)) + err = -ENODEV; + + if (!err) { + ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); + if (!ops) + err = -ENOMEM; + } + + if (!err) { + memset(ops, 0, sizeof(struct fbcon_ops)); + info->fbcon_par = ops; + set_blitting_type(vc, info, NULL); + } + + if (err) { + con2fb_map[unit] = oldidx; + module_put(info->fbops->owner); + } + + return err; +} + +static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, + struct fb_info *newinfo, int unit, + int oldidx, int found) +{ + struct fbcon_ops *ops = oldinfo->fbcon_par; + int err = 0; + + if (oldinfo->fbops->fb_release && + oldinfo->fbops->fb_release(oldinfo, 0)) { + con2fb_map[unit] = oldidx; + if (!found && newinfo->fbops->fb_release) + newinfo->fbops->fb_release(newinfo, 0); + if (!found) + module_put(newinfo->fbops->owner); + err = -ENODEV; + } + + if (!err) { + if (oldinfo->queue.func == fb_flashcursor) + del_timer_sync(&ops->cursor_timer); + + kfree(ops->cursor_state.mask); + kfree(ops->cursor_data); + kfree(oldinfo->fbcon_par); + oldinfo->fbcon_par = NULL; + module_put(oldinfo->fbops->owner); + } + + return err; +} + +static void con2fb_init_newinfo(struct fb_info *info) +{ + if (!info->queue.func || info->queue.func == fb_flashcursor) { + struct fbcon_ops *ops = info->fbcon_par; + + if (!info->queue.func) + INIT_WORK(&info->queue, fb_flashcursor, info); + + init_timer(&ops->cursor_timer); + ops->cursor_timer.function = cursor_timer_handler; + ops->cursor_timer.expires = jiffies + HZ / 5; + ops->cursor_timer.data = (unsigned long ) info; + add_timer(&ops->cursor_timer); + } +} + +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; + + ops->currcon = fg_console; + + if (info->fbops->fb_set_par) + info->fbops->fb_set_par(info); + + if (vc) + fbcon_set_disp(info, vc); + else + fbcon_preset_disp(info, unit); + + if (show_logo) { + struct vc_data *fg_vc = vc_cons[fg_console].d; + struct fb_info *fg_info = + registered_fb[con2fb_map[fg_console]]; + + fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols, + fg_vc->vc_rows, fg_vc->vc_cols, + fg_vc->vc_rows); + } + + switch_screen(fg_console); +} + /** * set_con2fb_map - map console to frame buffer device * @unit: virtual console number to map * @newidx: frame buffer index to map virtual console to + * @user: user request * * Maps a virtual console @unit to a frame buffer device * @newidx. */ -int set_con2fb_map(int unit, int newidx) +static int set_con2fb_map(int unit, int newidx, int user) { struct vc_data *vc = vc_cons[unit].d; + int oldidx = con2fb_map[unit]; + struct fb_info *info = registered_fb[newidx]; + struct fb_info *oldinfo = NULL; + int found, err = 0; - if (!vc) - return -ENODEV; - con2fb_map[unit] = newidx; - fbcon_is_default = (vc->vc_sw == &fb_con) ? 1 : 0; - return take_over_console(&fb_con, unit, unit, fbcon_is_default); -} + if (oldidx == newidx) + return 0; -/* - * Accelerated handlers. - */ -void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int dy, int dx, int height, int width) -{ - struct fb_copyarea area; + if (!info) + err = -EINVAL; - area.sx = sx * vc->vc_font.width; - area.sy = sy * vc->vc_font.height; - area.dx = dx * vc->vc_font.width; - area.dy = dy * vc->vc_font.height; - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; + if (!err && !search_for_mapped_con()) { + info_idx = newidx; + return fbcon_takeover(0); + } - info->fbops->fb_copyarea(info, &area); -} + if (oldidx != -1) + oldinfo = registered_fb[oldidx]; -void accel_clear(struct vc_data *vc, struct fb_info *info, int sy, - int sx, int height, int width) -{ - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - struct fb_fillrect region; + found = search_fb_in_map(newidx); - region.color = attr_bgcol_ec(bgshift, vc); - region.dx = sx * vc->vc_font.width; - region.dy = sy * vc->vc_font.height; - region.width = width * vc->vc_font.width; - region.height = height * vc->vc_font.height; - region.rop = ROP_COPY; + acquire_console_sem(); + con2fb_map[unit] = newidx; + if (!err && !found) + err = con2fb_acquire_newinfo(vc, info, unit, oldidx); - info->fbops->fb_fillrect(info, ®ion); -} -void accel_putcs(struct vc_data *vc, struct fb_info *info, - const unsigned short *s, int count, int yy, int xx) -{ - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - unsigned int width = (vc->vc_font.width + 7) >> 3; - unsigned int cellsize = vc->vc_font.height * width; - unsigned int maxcnt = info->pixmap.size/cellsize; - unsigned int scan_align = info->pixmap.scan_align - 1; - unsigned int buf_align = info->pixmap.buf_align - 1; - unsigned int shift_low = 0, mod = vc->vc_font.width % 8; - unsigned int shift_high = 8, pitch, cnt, size, k; - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; - unsigned int idx = vc->vc_font.width >> 3; - struct fb_image image; - u16 c = scr_readw(s); - u8 *src, *dst, *dst0; - - image.fg_color = attr_fgcol(fgshift, c); - image.bg_color = attr_bgcol(bgshift, c); - image.dx = xx * vc->vc_font.width; - image.dy = yy * vc->vc_font.height; - image.height = vc->vc_font.height; - image.depth = 1; + /* + * If old fb is not mapped to any of the consoles, + * fbcon should release it. + */ + if (!err && oldinfo && !search_fb_in_map(oldidx)) + err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, + found); + + if (!err) { + int show_logo = (fg_console == 0 && !user && + logo_shown != FBCON_LOGO_DONTSHOW); + + if (!found) + con2fb_init_newinfo(info); + con2fb_map_boot[unit] = newidx; + con2fb_init_display(vc, info, unit, show_logo); + } - while (count) { - if (count > maxcnt) - cnt = k = maxcnt; - else - cnt = k = count; - - image.width = vc->vc_font.width * cnt; - pitch = ((image.width + 7) >> 3) + scan_align; - pitch &= ~scan_align; - size = pitch * image.height + buf_align; - size &= ~buf_align; - dst0 = fb_get_buffer_offset(info, &info->pixmap, size); - image.data = dst0; - while (k--) { - src = vc->vc_font.data + (scr_readw(s++) & charmask)*cellsize; - dst = dst0; - - if (mod) { - fb_move_buf_unaligned(info, &info->pixmap, dst, pitch, - src, idx, image.height, shift_high, - shift_low, mod); - shift_low += mod; - dst0 += (shift_low >= 8) ? width : width - 1; - shift_low &= 7; - shift_high = 8 - shift_low; - } else { - fb_move_buf_aligned(info, &info->pixmap, dst, pitch, - src, idx, image.height); - dst0 += width; - } - } - info->fbops->fb_imageblit(info, &image); - image.dx += cnt * vc->vc_font.width; - count -= cnt; - } -} - -void accel_clear_margins(struct vc_data *vc, struct fb_info *info, - int bottom_only) -{ - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - unsigned int cw = vc->vc_font.width; - unsigned int ch = vc->vc_font.height; - unsigned int rw = info->var.xres - (vc->vc_cols*cw); - unsigned int bh = info->var.yres - (vc->vc_rows*ch); - unsigned int rs = info->var.xres - rw; - unsigned int bs = info->var.yres - bh; - struct fb_fillrect region; - - region.color = attr_bgcol_ec(bgshift, vc); - region.rop = ROP_COPY; - - if (rw && !bottom_only) { - region.dx = info->var.xoffset + rs; - region.dy = 0; - region.width = rw; - region.height = info->var.yres_virtual; - info->fbops->fb_fillrect(info, ®ion); - } - - if (bh) { - region.dx = info->var.xoffset; - region.dy = info->var.yoffset + bs; - region.width = rs; - region.height = bh; - info->fbops->fb_fillrect(info, ®ion); - } -} + release_console_sem(); + return err; +} /* * Low Level Operations */ /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ +static int var_to_display(struct display *disp, + struct fb_var_screeninfo *var, + struct fb_info *info) +{ + disp->xres_virtual = var->xres_virtual; + disp->yres_virtual = var->yres_virtual; + disp->bits_per_pixel = var->bits_per_pixel; + disp->grayscale = var->grayscale; + disp->nonstd = var->nonstd; + disp->accel_flags = var->accel_flags; + disp->height = var->height; + disp->width = var->width; + disp->red = var->red; + disp->green = var->green; + disp->blue = var->blue; + disp->transp = var->transp; + disp->mode = fb_match_mode(var, &info->modelist); + if (disp->mode == NULL) + /* This should not happen */ + return -EINVAL; + return 0; +} + +static void display_to_var(struct fb_var_screeninfo *var, + struct display *disp) +{ + fb_videomode_to_var(var, disp->mode); + var->xres_virtual = disp->xres_virtual; + var->yres_virtual = disp->yres_virtual; + var->bits_per_pixel = disp->bits_per_pixel; + var->grayscale = disp->grayscale; + var->nonstd = disp->nonstd; + var->accel_flags = disp->accel_flags; + var->height = disp->height; + var->width = disp->width; + var->red = disp->red; + var->green = disp->green; + var->blue = disp->blue; + var->transp = disp->transp; +} + static const char *fbcon_startup(void) { const char *display_desc = "frame buffer device"; @@ -443,9 +725,9 @@ static const char *fbcon_startup(void) struct vc_data *vc = vc_cons[fg_console].d; struct font_desc *font = NULL; struct module *owner; - struct fb_info *info; - static int done = 0; - int cols, rows; + struct fb_info *info = NULL; + struct fbcon_ops *ops; + int rows, cols; int irqres; irqres = 1; @@ -453,20 +735,35 @@ static const char *fbcon_startup(void) * If num_registered_fb is zero, this is a call for the dummy part. * The frame buffer devices weren't initialized yet. */ - if (!num_registered_fb || done) + if (!num_registered_fb || info_idx == -1) return display_desc; - done = 1; - - info = registered_fb[0]; - if (!info) return NULL; - info->currcon = -1; + /* + * Instead of blindly using registered_fb[0], we use info_idx, set by + * fb_console_init(); + */ + info = registered_fb[info_idx]; + if (!info) + return NULL; owner = info->fbops->owner; if (!try_module_get(owner)) return NULL; - if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) + if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { module_put(owner); - + return NULL; + } + + ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL); + if (!ops) { + module_put(owner); + return NULL; + } + + memset(ops, 0, sizeof(struct fbcon_ops)); + ops->currcon = -1; + info->fbcon_par = ops; + set_blitting_type(vc, info, NULL); + if (info->fix.type != FB_TYPE_TEXT) { if (fbcon_softback_size) { if (!softback_buf) { @@ -496,22 +793,15 @@ static const char *fbcon_startup(void) if (!p->fontdata) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, - info->var.yres); + info->var.yres); vc->vc_font.width = font->width; vc->vc_font.height = font->height; vc->vc_font.data = p->fontdata = font->data; vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ } - /* - * We must always set the mode. The mode of the previous console - * driver could be in the same resolution but we are using different - * hardware so we have to initialize the hardware. - */ - if (info->fbops->fb_set_par) - info->fbops->fb_set_par(info); - cols = info->var.xres/vc->vc_font.width; - rows = info->var.yres/vc->vc_font.height; + cols = info->var.xres / vc->vc_font.width; + rows = info->var.yres / vc->vc_font.height; vc_resize(vc->vc_num, cols, rows); DPRINTK("mode: %s\n", info->fix.id); @@ -519,7 +809,6 @@ static const char *fbcon_startup(void) DPRINTK("res: %dx%d-%d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); - con_set_default_unimap(vc->vc_num); #ifdef CONFIG_ATARI if (MACH_IS_ATARI) { @@ -585,43 +874,51 @@ static const char *fbcon_startup(void) * default timer to flash the cursor. */ if (!info->queue.func) { INIT_WORK(&info->queue, fb_flashcursor, info); - - cursor_timer.expires = jiffies + HZ / 5; - cursor_timer.data = (unsigned long ) info; - add_timer(&cursor_timer); + + init_timer(&ops->cursor_timer); + ops->cursor_timer.function = cursor_timer_handler; + ops->cursor_timer.expires = jiffies + HZ / 5; + ops->cursor_timer.data = (unsigned long ) info; + add_timer(&ops->cursor_timer); } return display_desc; } static void fbcon_init(struct vc_data *vc, int init) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct vc_data **default_mode = vc->vc_display_fg; + struct vc_data *svc = *default_mode; struct display *t, *p = &fb_display[vc->vc_num]; int display_fg = (*default_mode)->vc_num; - int logo = 1, rows, cols, charcnt = 256; - unsigned short *save = NULL, *r, *q; + int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256; + int cap = info->flags; - if (vc->vc_num != display_fg || (info->flags & FBINFO_FLAG_MODULE) || + if (info_idx == -1 || info == NULL) + return; + if (vc->vc_num != display_fg || logo_shown == FBCON_LOGO_DONTSHOW || (info->fix.type == FB_TYPE_TEXT)) logo = 0; info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ + if (var_to_display(p, &info->var, info)) + return; + /* If we are not the first console on this fb, copy the font from that console */ t = &fb_display[display_fg]; - vc->vc_font.width = (*default_mode)->vc_font.width; - vc->vc_font.height = (*default_mode)->vc_font.height; - vc->vc_font.data = p->fontdata = t->fontdata; - p->userfont = t->userfont; - if (p->userfont) { - REFCOUNT(p->fontdata)++; - charcnt = FNTCHARCNT(p->fontdata); + if (!vc->vc_font.data) { + vc->vc_font.data = p->fontdata = t->fontdata; + vc->vc_font.width = (*default_mode)->vc_font.width; + vc->vc_font.height = (*default_mode)->vc_font.height; + p->userfont = t->userfont; + if (p->userfont) + REFCOUNT(p->fontdata)++; } - con_copy_unimap(vc->vc_num, display_fg); - - vc->vc_can_do_color = info->var.bits_per_pixel != 1; + if (p->userfont) + charcnt = FNTCHARCNT(p->fontdata); + vc->vc_can_do_color = (fb_get_color_depth(info) != 1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; if (charcnt == 256) { vc->vc_hi_font_mask = 0; @@ -631,88 +928,45 @@ static void fbcon_init(struct vc_data *vc, int init) vc->vc_complement_mask <<= 1; } - cols = info->var.xres / vc->vc_font.width; - rows = info->var.yres / vc->vc_font.height; - vc_resize(vc->vc_num, cols, rows); + if (!*svc->vc_uni_pagedir_loc) + con_set_default_unimap(display_fg); + if (!*vc->vc_uni_pagedir_loc) + con_copy_unimap(vc->vc_num, display_fg); - if (info->var.accel_flags) - p->scrollmode = SCROLL_YNOMOVE; - else - p->scrollmode = SCROLL_YREDRAW; + cols = vc->vc_cols; + rows = vc->vc_rows; + new_cols = info->var.xres / vc->vc_font.width; + new_rows = info->var.yres / vc->vc_font.height; + vc_resize(vc->vc_num, new_cols, new_rows); + /* + * We must always set the mode. The mode of the previous console + * driver could be in the same resolution but we are using different + * hardware so we have to initialize the hardware. + * + * We need to do it in fbcon_init() to prevent screen corruption. + */ + if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par) + info->fbops->fb_set_par(info); + + + if ((cap & FBINFO_HWACCEL_COPYAREA) && + !(cap & FBINFO_HWACCEL_DISABLED)) + p->scrollmode = SCROLL_MOVE; + else /* default to something safe */ + p->scrollmode = SCROLL_REDRAW; /* * ++guenther: console.c:vc_allocate() relies on initializing * vc_{cols,rows}, but we must not set those if we are only * resizing the console. */ - if (init) { - vc->vc_cols = cols; - vc->vc_rows = rows; + if (!init) { + vc->vc_cols = new_cols; + vc->vc_rows = new_rows; } - if (logo) { - /* Need to make room for the logo */ - int cnt; - int step; - - logo_height = fb_prepare_logo(info); - logo_lines = (logo_height + vc->vc_font.height - 1) / - vc->vc_font.height; - q = (unsigned short *) (vc->vc_origin + - vc->vc_size_row * rows); - step = logo_lines * cols; - for (r = q - logo_lines * cols; r < q; r++) - if (scr_readw(r) != vc->vc_video_erase_char) - break; - if (r != q && rows >= rows + logo_lines) { - save = kmalloc(logo_lines * cols * 2, GFP_KERNEL); - if (save) { - scr_memsetw(save, vc->vc_video_erase_char, - logo_lines * cols * 2); - r = q - step; - for (cnt = 0; cnt < logo_lines; cnt++, r += cols) - scr_memcpyw(save + cnt * cols, r, 2 * cols); - r = q; - } - } - if (r == q) { - /* We can scroll screen down */ - r = q - step - cols; - for (cnt = rows - logo_lines; cnt > 0; cnt--) { - scr_memcpyw(r + step, r, vc->vc_size_row); - r -= cols; - } - if (!save) { - vc->vc_y += logo_lines; - vc->vc_pos += logo_lines * vc->vc_size_row; - } - } - scr_memsetw((unsigned short *) vc->vc_origin, - vc->vc_video_erase_char, - vc->vc_size_row * logo_lines); - - if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { - accel_clear_margins(vc, info, 0); - update_screen(vc->vc_num); - } - if (save) { - q = (unsigned short *) (vc->vc_origin + - vc->vc_size_row * - rows); - scr_memcpyw(q, save, logo_lines * cols * 2); - vc->vc_y += logo_lines; - vc->vc_pos += logo_lines * vc->vc_size_row; - kfree(save); - } - if (logo_lines > vc->vc_bottom) { - logo_shown = -1; - printk(KERN_INFO - "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); - } else { - logo_shown = -2; - vc->vc_top = logo_lines; - } - } + if (logo) + fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); if (vc->vc_num == display_fg && softback_buf) { int l = fbcon_softback_size / vc->vc_size_row; @@ -730,6 +984,8 @@ static void fbcon_deinit(struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num]; + if (info_idx != -1) + return; fbcon_free_font(p); } @@ -740,7 +996,7 @@ static void fbcon_deinit(struct vc_data *vc) * This system is now divided into two levels because of complications * caused by hardware scrolling. Top level functions: * - * fbcon_bmove(), fbcon_clear(), fbcon_putc() + * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() * * handles y values in range [0, scr_height-1] that correspond to real * screen positions. y_wrap shift means that first line of bitmap may be @@ -770,14 +1026,13 @@ static __inline__ int real_y(struct display *p, int ypos) static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, int width) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + struct display *p = &fb_display[vc->vc_num]; u_int y_break; - if (!info->fbops->fb_blank && console_blanked) - return; - if (info->state != FBINFO_STATE_RUNNING) + if (fbcon_is_inactive(vc, info)) return; if (!height || !width) @@ -788,188 +1043,71 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, y_break = p->vrows - p->yscroll; if (sy < y_break && sy + height - 1 >= y_break) { u_int b = y_break - sy; - accel_clear(vc, info, real_y(p, sy), sx, b, width); - accel_clear(vc, info, real_y(p, sy + b), sx, height - b, + ops->clear(vc, info, real_y(p, sy), sx, b, width); + ops->clear(vc, info, real_y(p, sy + b), sx, height - b, width); } else - accel_clear(vc, info, real_y(p, sy), sx, height, width); + ops->clear(vc, info, real_y(p, sy), sx, height, width); } -static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) +static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, + int count, int ypos, int xpos) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - unsigned int scan_align = info->pixmap.scan_align - 1; - unsigned int buf_align = info->pixmap.buf_align - 1; - unsigned int width = (vc->vc_font.width + 7) >> 3; - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - unsigned int size, pitch; - struct fb_image image; - u8 *src, *dst; - - if (!info->fbops->fb_blank && console_blanked) - return; - if (info->state != FBINFO_STATE_RUNNING) - return; - - if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT) - return; - - image.dx = xpos * vc->vc_font.width; - image.dy = real_y(p, ypos) * vc->vc_font.height; - image.width = vc->vc_font.width; - image.height = vc->vc_font.height; - image.fg_color = attr_fgcol(fgshift, c); - image.bg_color = attr_bgcol(bgshift, c); - image.depth = 1; - - src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width; + struct fbcon_ops *ops = info->fbcon_par; - pitch = width + scan_align; - pitch &= ~scan_align; - size = pitch * vc->vc_font.height; - size += buf_align; - size &= ~buf_align; - - dst = fb_get_buffer_offset(info, &info->pixmap, size); - image.data = dst; - - fb_move_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height); - - info->fbops->fb_imageblit(info, &image); + if (!fbcon_is_inactive(vc, info)) + ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, + get_color(vc, info, scr_readw(s), 1), + get_color(vc, info, scr_readw(s), 0)); } -static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, - int count, int ypos, int xpos) +static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - struct display *p = &fb_display[vc->vc_num]; + unsigned short chr; - if (!info->fbops->fb_blank && console_blanked) - return; - if (info->state != FBINFO_STATE_RUNNING) - return; + scr_writew(c, &chr); + fbcon_putcs(vc, &chr, 1, ypos, xpos); +} - if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT) - return; +static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; - accel_putcs(vc, info, s, count, real_y(p, ypos), xpos); + if (!fbcon_is_inactive(vc, info)) + ops->clear_margins(vc, info, bottom_only); } static void fbcon_cursor(struct vc_data *vc, int mode) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; - int fgshift = (vc->vc_hi_font_mask) ? 9 : 8; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; - int w = (vc->vc_font.width + 7) >> 3, c; int y = real_y(p, vc->vc_y); - struct fb_cursor cursor; - + int c = scr_readw((u16 *) vc->vc_pos); + + if (fbcon_is_inactive(vc, info)) + return; + + ops->cursor_flash = 1; if (mode & CM_SOFTBACK) { mode &= ~CM_SOFTBACK; if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) + if (y + softback_lines >= vc->vc_rows) { mode = CM_ERASE; + ops->cursor_flash = 0; + } else y += softback_lines; } } else if (softback_lines) fbcon_set_origin(vc); - c = scr_readw((u16 *) vc->vc_pos); - - cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); - cursor.set = FB_CUR_SETCUR; - cursor.image.depth = 1; - - switch (mode) { - case CM_ERASE: - if (info->cursor.rop == ROP_XOR) { - info->cursor.enable = 0; - info->cursor.rop = ROP_COPY; - info->fbops->fb_cursor(info, &cursor); - } - break; - case CM_MOVE: - case CM_DRAW: - info->cursor.enable = 1; - - if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) || - info->cursor.image.bg_color != attr_bgcol(bgshift, c)) { - cursor.image.fg_color = attr_fgcol(fgshift, c); - cursor.image.bg_color = attr_bgcol(bgshift, c); - cursor.set |= FB_CUR_SETCMAP; - } - - if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) || - (info->cursor.image.dy != (vc->vc_font.height * y))) { - cursor.image.dx = vc->vc_font.width * vc->vc_x; - cursor.image.dy = vc->vc_font.height * y; - cursor.set |= FB_CUR_SETPOS; - } - - if (info->cursor.image.height != vc->vc_font.height || - info->cursor.image.width != vc->vc_font.width) { - cursor.image.height = vc->vc_font.height; - cursor.image.width = vc->vc_font.width; - cursor.set |= FB_CUR_SETSIZE; - } - - if (info->cursor.hot.x || info->cursor.hot.y) { - cursor.hot.x = cursor.hot.y = 0; - cursor.set |= FB_CUR_SETHOT; - } - - if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)) { - char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC); - int cur_height, size, i = 0; - - if (!mask) return; - - if (info->cursor.mask) - kfree(info->cursor.mask); - info->cursor.mask = mask; - - p->cursor_shape = vc->vc_cursor_type & 0x0f; - cursor.set |= FB_CUR_SETSHAPE; - - switch (vc->vc_cursor_type & 0x0f) { - case CUR_NONE: - cur_height = 0; - break; - case CUR_UNDERLINE: - cur_height = (vc->vc_font.height < 10) ? 1 : 2; - break; - case CUR_LOWER_THIRD: - cur_height = vc->vc_font.height/3; - break; - case CUR_LOWER_HALF: - cur_height = vc->vc_font.height >> 1; - break; - case CUR_TWO_THIRDS: - cur_height = (vc->vc_font.height << 1)/3; - break; - case CUR_BLOCK: - default: - cur_height = vc->vc_font.height; - break; - } - size = (vc->vc_font.height - cur_height) * w; - while (size--) - mask[i++] = 0; - size = cur_height * w; - while (size--) - mask[i++] = 0xff; - } - info->cursor.rop = ROP_XOR; - info->fbops->fb_cursor(info, &cursor); - vbl_cursor_cnt = CURSOR_DRAW_DELAY; - break; - } + ops->cursor(vc, info, p, mode, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + vbl_cursor_cnt = CURSOR_DRAW_DELAY; } static int scrollback_phys_max = 0; @@ -978,14 +1116,90 @@ static int scrollback_current = 0; int update_var(int con, struct fb_info *info) { - if (con == info->currcon) + if (con == ((struct fbcon_ops *)info->fbcon_par)->currcon) return fb_pan_display(info, &info->var); return 0; } +/* + * If no vc is existent yet, just set struct display + */ +static void fbcon_preset_disp(struct fb_info *info, int unit) +{ + struct display *p = &fb_display[unit]; + struct display *t = &fb_display[fg_console]; + + info->var.xoffset = info->var.yoffset = p->yscroll = 0; + if (var_to_display(p, &info->var, info)) + return; + + p->fontdata = t->fontdata; + p->userfont = t->userfont; + if (p->userfont) + REFCOUNT(p->fontdata)++; +} + +static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc) +{ + struct display *p = &fb_display[vc->vc_num], *t; + struct vc_data **default_mode = vc->vc_display_fg; + struct vc_data *svc = *default_mode; + int display_fg = (*default_mode)->vc_num; + int rows, cols, charcnt = 256; + + info->var.xoffset = info->var.yoffset = p->yscroll = 0; + if (var_to_display(p, &info->var, info)) + return; + t = &fb_display[display_fg]; + if (!vc->vc_font.data) { + vc->vc_font.data = p->fontdata = t->fontdata; + vc->vc_font.width = (*default_mode)->vc_font.width; + vc->vc_font.height = (*default_mode)->vc_font.height; + p->userfont = t->userfont; + if (p->userfont) + REFCOUNT(p->fontdata)++; + } + if (p->userfont) + charcnt = FNTCHARCNT(p->fontdata); + + vc->vc_can_do_color = (fb_get_color_depth(info) != 1); + vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; + if (charcnt == 256) { + vc->vc_hi_font_mask = 0; + } else { + vc->vc_hi_font_mask = 0x100; + if (vc->vc_can_do_color) + vc->vc_complement_mask <<= 1; + } + + if (!*svc->vc_uni_pagedir_loc) + con_set_default_unimap(display_fg); + if (!*vc->vc_uni_pagedir_loc) + con_copy_unimap(vc->vc_num, display_fg); + + cols = info->var.xres / vc->vc_font.width; + rows = info->var.yres / vc->vc_font.height; + vc_resize(vc->vc_num, cols, rows); + if (CON_IS_VISIBLE(vc)) { + update_screen(vc->vc_num); + if (softback_buf) { + int l = fbcon_softback_size / vc->vc_size_row; + + if (l > 5) + softback_end = softback_buf + l * + vc->vc_size_row; + else { + /* Smaller scrollback makes no sense, and 0 + would screw the operation totally */ + softback_top = 0; + } + } + } +} + static __inline__ void ywrap_up(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; p->yscroll += count; @@ -1003,7 +1217,7 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count) static __inline__ void ywrap_down(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; p->yscroll -= count; @@ -1021,42 +1235,93 @@ static __inline__ void ywrap_down(struct vc_data *vc, int count) static __inline__ void ypan_up(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = info->fbcon_par; + + p->yscroll += count; + if (p->yscroll > p->vrows - vc->vc_rows) { + ops->bmove(vc, info, p->vrows - vc->vc_rows, + 0, 0, 0, vc->vc_rows, vc->vc_cols); + p->yscroll -= p->vrows - vc->vc_rows; + } + info->var.xoffset = 0; + info->var.yoffset = p->yscroll * vc->vc_font.height; + info->var.vmode &= ~FB_VMODE_YWRAP; + update_var(vc->vc_num, info); + fbcon_clear_margins(vc, 1); + scrollback_max += count; + if (scrollback_max > scrollback_phys_max) + scrollback_max = scrollback_phys_max; + scrollback_current = 0; +} + +static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct display *p = &fb_display[vc->vc_num]; + int redraw = 0; + + p->yscroll += count; + if (p->yscroll > p->vrows - vc->vc_rows) { + p->yscroll -= p->vrows - vc->vc_rows; + redraw = 1; + } + + info->var.xoffset = 0; + info->var.yoffset = p->yscroll * vc->vc_font.height; + info->var.vmode &= ~FB_VMODE_YWRAP; + if (redraw) + fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); + update_var(vc->vc_num, info); + fbcon_clear_margins(vc, 1); + scrollback_max += count; + if (scrollback_max > scrollback_phys_max) + scrollback_max = scrollback_phys_max; + scrollback_current = 0; +} + +static __inline__ void ypan_down(struct vc_data *vc, int count) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = info->fbcon_par; - p->yscroll += count; - if (p->yscroll > p->vrows - vc->vc_rows) { - accel_bmove(vc, info, p->vrows - vc->vc_rows, - 0, 0, 0, vc->vc_rows, vc->vc_cols); - p->yscroll -= p->vrows - vc->vc_rows; + p->yscroll -= count; + if (p->yscroll < 0) { + ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, + 0, vc->vc_rows, vc->vc_cols); + p->yscroll += p->vrows - vc->vc_rows; } info->var.xoffset = 0; info->var.yoffset = p->yscroll * vc->vc_font.height; info->var.vmode &= ~FB_VMODE_YWRAP; update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); - scrollback_max += count; - if (scrollback_max > scrollback_phys_max) - scrollback_max = scrollback_phys_max; + fbcon_clear_margins(vc, 1); + scrollback_max -= count; + if (scrollback_max < 0) + scrollback_max = 0; scrollback_current = 0; } -static __inline__ void ypan_down(struct vc_data *vc, int count) +static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - + int redraw = 0; + p->yscroll -= count; if (p->yscroll < 0) { - accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, - 0, vc->vc_rows, vc->vc_cols); p->yscroll += p->vrows - vc->vc_rows; + redraw = 1; } info->var.xoffset = 0; info->var.yoffset = p->yscroll * vc->vc_font.height; info->var.vmode &= ~FB_VMODE_YWRAP; + if (redraw) + fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); update_var(vc->vc_num, info); - accel_clear_margins(vc, info, 1); + fbcon_clear_margins(vc, 1); scrollback_max -= count; if (scrollback_max < 0) scrollback_max = 0; @@ -1066,7 +1331,6 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, long delta) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; int count = vc->vc_rows; unsigned short *d, *s; unsigned long n; @@ -1123,16 +1387,16 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, if (attr != (c & 0xff00)) { attr = c & 0xff00; if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start; start = s; } } if (c == scr_readw(d)) { if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start + 1; start = s + 1; } else { @@ -1144,8 +1408,7 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, d++; } while (s < le); if (s > start) - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, line, x); line++; if (d == (u16 *) softback_end) d = (u16 *) softback_buf; @@ -1158,12 +1421,45 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, } } +static void fbcon_redraw_move(struct vc_data *vc, struct display *p, + int line, int count, int dy) +{ + unsigned short *s = (unsigned short *) + (vc->vc_origin + vc->vc_size_row * line); + + while (count--) { + unsigned short *start = s; + unsigned short *le = advance_row(s, 1); + unsigned short c; + int x = 0; + unsigned short attr = 1; + + do { + c = scr_readw(s); + if (attr != (c & 0xff00)) { + attr = c & 0xff00; + if (s > start) { + fbcon_putcs(vc, start, s - start, + dy, x); + x += s - start; + start = s; + } + } + console_conditional_schedule(); + s++; + } while (s < le); + if (s > start) + fbcon_putcs(vc, start, s - start, dy, x); + console_conditional_schedule(); + dy++; + } +} + static void fbcon_redraw(struct vc_data *vc, struct display *p, int line, int count, int offset) { unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * line); - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; unsigned short *s = d + offset; while (count--) { @@ -1178,16 +1474,16 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p, if (attr != (c & 0xff00)) { attr = c & 0xff00; if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start; start = s; } } if (c == scr_readw(d)) { if (s > start) { - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, + line, x); x += s - start + 1; start = s + 1; } else { @@ -1201,8 +1497,7 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p, d++; } while (s < le); if (s > start) - accel_putcs(vc, info, start, s - start, - real_y(p, line), x); + fbcon_putcs(vc, start, s - start, line, x); console_conditional_schedule(); if (offset > 0) line++; @@ -1243,15 +1538,13 @@ static inline void fbcon_softback_note(struct vc_data *vc, int t, static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, int count) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL); - - if (!info->fbops->fb_blank && console_blanked) - return 0; + struct fbcon_ops *ops = info->fbcon_par; + int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT) - return 0; + if (fbcon_is_inactive(vc, info)) + return -EINVAL; fbcon_cursor(vc, CM_ERASE); @@ -1269,15 +1562,15 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_softback_note(vc, t, count); if (logo_shown >= 0) goto redraw_up; - switch (p->scrollmode & __SCROLL_YMASK) { - case __SCROLL_YMOVE: - accel_bmove(vc, info, t + count, 0, t, 0, - b - t - count, vc->vc_cols); - accel_clear(vc, info, b - count, 0, count, - vc->vc_cols); + switch (p->scrollmode) { + case SCROLL_MOVE: + ops->bmove(vc, info, t + count, 0, t, 0, + b - t - count, vc->vc_cols); + ops->clear(vc, info, b - count, 0, count, + vc->vc_cols); break; - case __SCROLL_YWRAP: + case SCROLL_WRAP_MOVE: if (b - t - count > 3 * vc->vc_rows >> 2) { if (t > 0) fbcon_bmove(vc, 0, 0, count, 0, t, @@ -1287,15 +1580,33 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_bmove(vc, b - count, 0, b, 0, vc->vc_rows - b, vc->vc_cols); - } else if (p->scrollmode & __SCROLL_YPANREDRAW) - goto redraw_up; - else + } else if (info->flags & FBINFO_READS_FAST) fbcon_bmove(vc, t + count, 0, t, 0, b - t - count, vc->vc_cols); + else + goto redraw_up; + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + break; + + case SCROLL_PAN_REDRAW: + if ((p->yscroll + count <= + 2 * (p->vrows - vc->vc_rows)) + && ((!scroll_partial && (b - t == vc->vc_rows)) + || (scroll_partial + && (b - t - count > + 3 * vc->vc_rows >> 2)))) { + if (t > 0) + fbcon_redraw_move(vc, p, 0, t, count); + ypan_up_redraw(vc, t, count); + if (vc->vc_rows - b > 0) + fbcon_redraw_move(vc, p, b - count, + vc->vc_rows - b, b); + } else + fbcon_redraw_move(vc, p, t + count, b - t - count, t); fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; - case __SCROLL_YPAN: + case SCROLL_PAN_MOVE: if ((p->yscroll + count <= 2 * (p->vrows - vc->vc_rows)) && ((!scroll_partial && (b - t == vc->vc_rows)) @@ -1310,20 +1621,19 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_bmove(vc, b - count, 0, b, 0, vc->vc_rows - b, vc->vc_cols); - } else if (p->scrollmode & __SCROLL_YPANREDRAW) - goto redraw_up; - else + } else if (info->flags & FBINFO_READS_FAST) fbcon_bmove(vc, t + count, 0, t, 0, b - t - count, vc->vc_cols); + else + goto redraw_up; fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; - case __SCROLL_YREDRAW: + case SCROLL_REDRAW: redraw_up: fbcon_redraw(vc, p, t, b - t - count, count * vc->vc_cols); - accel_clear(vc, info, real_y(p, b - count), 0, - count, vc->vc_cols); + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), @@ -1336,14 +1646,14 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, case SM_DOWN: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; - switch (p->scrollmode & __SCROLL_YMASK) { - case __SCROLL_YMOVE: - accel_bmove(vc, info, t, 0, t + count, 0, - b - t - count, vc->vc_cols); - accel_clear(vc, info, t, 0, count, vc->vc_cols); + switch (p->scrollmode) { + case SCROLL_MOVE: + ops->bmove(vc, info, t, 0, t + count, 0, + b - t - count, vc->vc_cols); + ops->clear(vc, info, t, 0, count, vc->vc_cols); break; - case __SCROLL_YWRAP: + case SCROLL_WRAP_MOVE: if (b - t - count > 3 * vc->vc_rows >> 2) { if (vc->vc_rows - b > 0) fbcon_bmove(vc, b, 0, b - count, 0, @@ -1353,15 +1663,15 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, if (t > 0) fbcon_bmove(vc, count, 0, 0, 0, t, vc->vc_cols); - } else if (p->scrollmode & __SCROLL_YPANREDRAW) - goto redraw_down; - else + } else if (info->flags & FBINFO_READS_FAST) fbcon_bmove(vc, t, 0, t + count, 0, b - t - count, vc->vc_cols); + else + goto redraw_down; fbcon_clear(vc, t, 0, count, vc->vc_cols); break; - case __SCROLL_YPAN: + case SCROLL_PAN_MOVE: if ((count - p->yscroll <= p->vrows - vc->vc_rows) && ((!scroll_partial && (b - t == vc->vc_rows)) || (scroll_partial @@ -1375,20 +1685,36 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, if (t > 0) fbcon_bmove(vc, count, 0, 0, 0, t, vc->vc_cols); - } else if (p->scrollmode & __SCROLL_YPANREDRAW) - goto redraw_down; - else + } else if (info->flags & FBINFO_READS_FAST) fbcon_bmove(vc, t, 0, t + count, 0, b - t - count, vc->vc_cols); + else + goto redraw_down; + fbcon_clear(vc, t, 0, count, vc->vc_cols); + break; + + case SCROLL_PAN_REDRAW: + if ((count - p->yscroll <= p->vrows - vc->vc_rows) + && ((!scroll_partial && (b - t == vc->vc_rows)) + || (scroll_partial + && (b - t - count > + 3 * vc->vc_rows >> 2)))) { + if (vc->vc_rows - b > 0) + fbcon_redraw_move(vc, p, b, vc->vc_rows - b, + b - count); + ypan_down_redraw(vc, t, count); + if (t > 0) + fbcon_redraw_move(vc, p, count, t, 0); + } else + fbcon_redraw_move(vc, p, t, b - t - count, t + count); fbcon_clear(vc, t, 0, count, vc->vc_cols); break; - case __SCROLL_YREDRAW: + case SCROLL_REDRAW: redraw_down: fbcon_redraw(vc, p, b - 1, b - t - count, -count * vc->vc_cols); - accel_clear(vc, info, real_y(p, t), 0, count, - vc->vc_cols); + fbcon_clear(vc, t, 0, count, vc->vc_cols); scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), @@ -1404,10 +1730,10 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, int height, int width) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; - if (!info->fbops->fb_blank && console_blanked) + if (fbcon_is_inactive(vc, info)) return; if (!width || !height) @@ -1427,7 +1753,8 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; u_int b; if (sy < y_break && sy + height > y_break) { @@ -1461,36 +1788,53 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s } return; } - accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); } -static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, struct vc_data *vc) +static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, + struct vc_data *vc) { - int m; + int fh = vc->vc_font.height; + int cap = info->flags; + int good_pan = (cap & FBINFO_HWACCEL_YPAN) + && divides(info->fix.ypanstep, vc->vc_font.height) + && info->var.yres_virtual > info->var.yres; + int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) + && divides(info->fix.ywrapstep, vc->vc_font.height) + && divides(vc->vc_font.height, info->var.yres_virtual); + int reading_fast = cap & FBINFO_READS_FAST; + int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED); + int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED); + + p->vrows = info->var.yres_virtual/fh; + if (info->var.yres > (fh * (vc->vc_rows + 1))) + p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh; + if ((info->var.yres % fh) && (info->var.yres_virtual % fh < + info->var.yres % fh)) + p->vrows--; - if (p->scrollmode & __SCROLL_YFIXED) - return; - if (divides(info->fix.ywrapstep, vc->vc_font.height) && - divides(vc->vc_font.height, info->var.yres_virtual)) - m = __SCROLL_YWRAP; - else if (divides(info->fix.ypanstep, vc->vc_font.height) && - info->var.yres_virtual >= info->var.yres + vc->vc_font.height) - m = __SCROLL_YPAN; - else if (p->scrollmode & __SCROLL_YNOMOVE) - m = __SCROLL_YREDRAW; - else - m = __SCROLL_YMOVE; - p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m; + if (good_wrap || good_pan) { + if (reading_fast || fast_copyarea) + p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE; + else + p->scrollmode = good_wrap ? SCROLL_REDRAW : + SCROLL_PAN_REDRAW; + } else { + if (reading_fast || (fast_copyarea && !fast_imageblit)) + p->scrollmode = SCROLL_MOVE; + else + p->scrollmode = SCROLL_REDRAW; + } } static int fbcon_resize(struct vc_data *vc, unsigned int width, unsigned int height) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var = info->var; - int err; int x_diff, y_diff; + int x_diff, y_diff; int fw = vc->vc_font.width; int fh = vc->vc_font.height; @@ -1499,36 +1843,52 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, x_diff = info->var.xres - var.xres; y_diff = info->var.yres - var.yres; if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) { - char mode[40]; + struct fb_videomode *mode; DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); - if (!info->fbops->fb_set_par) + mode = fb_find_best_mode(&var, &info->modelist); + if (mode == NULL) return -EINVAL; - - sprintf(mode, "%dx%d", var.xres, var.yres); - err = fb_find_mode(&var, info, mode, NULL, 0, NULL, - info->var.bits_per_pixel); - if (!err || width > var.xres/fw || height > var.yres/fh) + fb_videomode_to_var(&var, mode); + if (width > var.xres/fw || height > var.yres/fh) return -EINVAL; + /* + * The following can probably have any value... Do we need to + * set all of them? + */ + var.bits_per_pixel = p->bits_per_pixel; + var.xres_virtual = p->xres_virtual; + var.yres_virtual = p->yres_virtual; + var.accel_flags = p->accel_flags; + var.width = p->width; + var.height = p->height; + var.red = p->red; + var.green = p->green; + var.blue = p->blue; + var.transp = p->transp; + var.nonstd = p->nonstd; + DPRINTK("resize now %ix%i\n", var.xres, var.yres); if (CON_IS_VISIBLE(vc)) { - var.activate = FB_ACTIVATE_NOW; + var.activate = FB_ACTIVATE_NOW | + FB_ACTIVATE_FORCE; fb_set_var(info, &var); + info->flags &= ~FBINFO_MISC_MODESWITCH; } + var_to_display(p, &info->var, info); } - p->vrows = var.yres_virtual/fh; - if (var.yres > (fh * (height + 1))) - p->vrows -= (var.yres - (fh * height)) / fh; - if ((var.yres % fh) && (var.yres_virtual % fh < var.yres % fh)) - p->vrows--; updatescrollmode(p, info, vc); return 0; } static int fbcon_switch(struct vc_data *vc) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info; struct display *p = &fb_display[vc->vc_num]; + struct fb_var_screeninfo var; + int i, prev_console, do_set_par = 0; + + info = registered_fb[con2fb_map[vc->vc_num]]; if (softback_top) { int l = fbcon_softback_size / vc->vc_size_row; @@ -1545,22 +1905,68 @@ static int fbcon_switch(struct vc_data *vc) softback_top = 0; } } + if (logo_shown >= 0) { struct vc_data *conp2 = vc_cons[logo_shown].d; if (conp2->vc_top == logo_lines && conp2->vc_bottom == conp2->vc_rows) conp2->vc_top = 0; - logo_shown = -1; + logo_shown = FBCON_LOGO_CANSHOW; + } + + prev_console = ((struct fbcon_ops *)info->fbcon_par)->currcon; + + /* + * FIXME: If we have multiple fbdev's loaded, we need to + * update all info->currcon. Perhaps, we can place this + * in a centralized structure, but this might break some + * drivers. + * + * info->currcon = vc->vc_num; + */ + for (i = 0; i < FB_MAX; i++) { + if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { + struct fbcon_ops *ops = registered_fb[i]->fbcon_par; + + ops->currcon = vc->vc_num; + } + } + memset(&var, 0, sizeof(struct fb_var_screeninfo)); + display_to_var(&var, p); + var.activate = FB_ACTIVATE_NOW; + + /* + * make sure we don't unnecessarily trip the memcmp() + * in fb_set_var() + */ + info->var.activate = var.activate; + info->var.yoffset = info->var.xoffset = p->yscroll = 0; + fb_set_var(info, &var); + + if (prev_console != -1 && + registered_fb[con2fb_map[prev_console]] != info) + do_set_par = 1; + + if (do_set_par || info->flags & FBINFO_MISC_MODESWITCH) { + if (info->fbops->fb_set_par) + info->fbops->fb_set_par(info); + info->flags &= ~FBINFO_MISC_MODESWITCH; } - if (info) - info->var.yoffset = p->yscroll = 0; - fbcon_resize(vc, vc->vc_cols, vc->vc_rows); - switch (p->scrollmode & __SCROLL_YMASK) { - case __SCROLL_YWRAP: + + set_blitting_type(vc, info, p); + ((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1; + + vc->vc_can_do_color = (fb_get_color_depth(info) != 1); + vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; + updatescrollmode(p, info, vc); + + switch (p->scrollmode) { + case SCROLL_WRAP_MOVE: scrollback_phys_max = p->vrows - vc->vc_rows; break; - case __SCROLL_YPAN: + case SCROLL_PAN_MOVE: + case SCROLL_PAN_REDRAW: scrollback_phys_max = p->vrows - 2 * vc->vc_rows; if (scrollback_phys_max < 0) scrollback_phys_max = 0; @@ -1572,14 +1978,12 @@ static int fbcon_switch(struct vc_data *vc) scrollback_max = 0; scrollback_current = 0; - info->currcon = vc->vc_num; - update_var(vc->vc_num, info); fbcon_set_palette(vc, color_table); + fbcon_clear_margins(vc, 0); + + if (logo_shown == FBCON_LOGO_DRAW) { - if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT) - accel_clear_margins(vc, info, 0); - if (logo_shown == -2) { logo_shown = fg_console; /* This is protected above by initmem_freed */ fb_show_logo(info); @@ -1592,51 +1996,62 @@ static int fbcon_switch(struct vc_data *vc) return 1; } +static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, + int blank) +{ + if (blank) { + unsigned short charmask = vc->vc_hi_font_mask ? + 0x1ff : 0xff; + unsigned short oldc; + + oldc = vc->vc_video_erase_char; + vc->vc_video_erase_char &= charmask; + fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); + vc->vc_video_erase_char = oldc; + } +} + static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) { - unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - struct display *p = &fb_display[vc->vc_num]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + int active = !fbcon_is_inactive(vc, info); if (mode_switch) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; struct fb_var_screeninfo var = info->var; +/* + * HACK ALERT: Some hardware will require reinitializion at this stage, + * others will require it to be done as late as possible. + * For now, we differentiate this with the + * FBINFO_MISC_MODESWITCHLATE bitflag. Worst case will be + * hardware that requires it here and another one later. + * A definitive solution may require fixing X or the VT + * system. + */ + if (info->flags & FBINFO_MISC_MODESWITCHLATE) + info->flags |= FBINFO_MISC_MODESWITCH; - if (blank) { - fbcon_cursor(vc, CM_ERASE); - return 0; + if (!blank && !(info->flags & FBINFO_MISC_MODESWITCHLATE)) { + var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + fb_set_var(info, &var); } - var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; - fb_set_var(info, &var); - } - - fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); - - if (!info->fbops->fb_blank) { - if (blank) { - unsigned short oldc; - u_int height; - u_int y_break; - - oldc = vc->vc_video_erase_char; - vc->vc_video_erase_char &= charmask; - height = vc->vc_rows; - y_break = p->vrows - p->yscroll; - if (height > y_break) { - accel_clear(vc, info, real_y(p, 0), - 0, y_break, vc->vc_cols); - accel_clear(vc, info, real_y(p, y_break), - 0, height - y_break, - vc->vc_cols); - } else - accel_clear(vc, info, real_y(p, 0), - 0, height, vc->vc_cols); - vc->vc_video_erase_char = oldc; - } else - update_screen(vc->vc_num); - return 0; - } else - return fb_blank(info, blank); + } + + if (active) { + if (ops->blank_state != blank) { + ops->blank_state = blank; + fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); + ops->cursor_flash = (!blank); + + if (fb_blank(info, blank)) + fbcon_generic_blank(vc, info, blank); + } + + if (!blank) + update_screen(vc->vc_num); + } + + return 0; } static void fbcon_free_font(struct display *p) @@ -1647,36 +2062,36 @@ static void fbcon_free_font(struct display *p) p->userfont = 0; } -static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op) +static int fbcon_get_font(struct vc_data *vc, struct console_font *font) { u8 *fontdata = vc->vc_font.data; - u8 *data = op->data; + u8 *data = font->data; int i, j; - op->width = vc->vc_font.width; - op->height = vc->vc_font.height; - op->charcount = vc->vc_hi_font_mask ? 512 : 256; - if (!op->data) + font->width = vc->vc_font.width; + font->height = vc->vc_font.height; + font->charcount = vc->vc_hi_font_mask ? 512 : 256; + if (!font->data) return 0; - if (op->width <= 8) { + if (font->width <= 8) { j = vc->vc_font.height; - for (i = 0; i < op->charcount; i++) { + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 32 - j); data += 32; fontdata += j; } - } else if (op->width <= 16) { + } else if (font->width <= 16) { j = vc->vc_font.height * 2; - for (i = 0; i < op->charcount; i++) { + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 64 - j); data += 64; fontdata += j; } - } else if (op->width <= 24) { - for (i = 0; i < op->charcount; i++) { + } else if (font->width <= 24) { + for (i = 0; i < font->charcount; i++) { for (j = 0; j < vc->vc_font.height; j++) { *data++ = fontdata[0]; *data++ = fontdata[1]; @@ -1688,7 +2103,7 @@ static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op) } } else { j = vc->vc_font.height * 4; - for (i = 0; i < op->charcount; i++) { + for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); memset(data + j, 0, 128 - j); data += 128; @@ -1698,23 +2113,15 @@ static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op) return 0; } -static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op, +static int fbcon_do_set_font(struct vc_data *vc, int w, int h, u8 * data, int userfont) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; int resize; - int w = op->width; - int h = op->height; int cnt; char *old_data = NULL; - if (!w > 32) { - if (userfont && op->op != KD_FONT_OP_COPY) - kfree(data - FONT_EXTRA_WORDS * sizeof(int)); - return -ENXIO; - } - if (CON_IS_VISIBLE(vc) && softback_lines) fbcon_set_origin(vc); @@ -1805,7 +2212,7 @@ static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op, } } else if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) { - accel_clear_margins(vc, info, 0); + fbcon_clear_margins(vc, 0); update_screen(vc->vc_num); } @@ -1814,100 +2221,70 @@ static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op, return 0; } -static inline int fbcon_copy_font(struct vc_data *vc, struct console_font_op *op) +static int fbcon_copy_font(struct vc_data *vc, int con) { - struct display *od; - int h = op->height; + struct display *od = &fb_display[con]; + struct console_font *f = &vc->vc_font; - if (h < 0 || !vc_cons_allocated(h)) - return -ENOTTY; - if (h == vc->vc_num) - return 0; /* nothing to do */ - od = &fb_display[h]; - if (od->fontdata == vc->vc_font.data) + if (od->fontdata == f->data) return 0; /* already the same font... */ - op->width = vc->vc_font.width; - op->height = vc->vc_font.height; - return fbcon_do_set_font(vc, op, od->fontdata, od->userfont); + return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont); } -static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op) -{ - int w = op->width; - int h = op->height; - int size = h; - int i, k; - u8 *new_data, *data = op->data, *p; +/* + * User asked to set font; we are guaranteed that + * a) width and height are in range 1..32 + * b) charcount does not exceed 512 + * but lets not assume that, since someone might someday want to use larger + * fonts. And charcount of 512 is small for unicode support. + * + * However, user space gives the font in 32 rows , regardless of + * actual font height. So a new API is needed if support for larger fonts + * is ever implemented. + */ - if ((w <= 0) || (w > 32) - || (op->charcount != 256 && op->charcount != 512)) +static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) +{ + unsigned charcount = font->charcount; + int w = font->width; + int h = font->height; + int size; + int i, csum; + u8 *new_data, *data = font->data; + int pitch = (font->width+7) >> 3; + + /* Is there a reason why fbconsole couldn't handle any charcount >256? + * If not this check should be changed to charcount < 256 */ + if (charcount != 256 && charcount != 512) return -EINVAL; - if (w > 8) { - if (w <= 16) - size *= 2; - else - size *= 4; - } - size *= op->charcount; + size = h * pitch * charcount; + + new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER); - if (! - (new_data = - kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER))) + if (!new_data) return -ENOMEM; + new_data += FONT_EXTRA_WORDS * sizeof(int); FNTSIZE(new_data) = size; - FNTCHARCNT(new_data) = op->charcount; + FNTCHARCNT(new_data) = charcount; REFCOUNT(new_data) = 0; /* usage counter */ - p = new_data; - if (w <= 8) { - for (i = 0; i < op->charcount; i++) { - memcpy(p, data, h); - data += 32; - p += h; - } - } else if (w <= 16) { - h *= 2; - for (i = 0; i < op->charcount; i++) { - memcpy(p, data, h); - data += 64; - p += h; - } - } else if (w <= 24) { - for (i = 0; i < op->charcount; i++) { - int j; - for (j = 0; j < h; j++) { - memcpy(p, data, 3); - p[3] = 0; - data += 3; - p += sizeof(u32); - } - data += 3 * (32 - h); - } - } else { - h *= 4; - for (i = 0; i < op->charcount; i++) { - memcpy(p, data, h); - data += 128; - p += h; - } + for (i=0; i< charcount; i++) { + memcpy(new_data + i*h*pitch, data + i*32*pitch, h*pitch); } - /* we can do it in u32 chunks because of charcount is 256 or 512, so - font length must be multiple of 256, at least. And 256 is multiple - of 4 */ - k = 0; - while (p > new_data) { - p = (u8 *)((u32 *)p - 1); - k += *(u32 *) p; - } - FNTSUM(new_data) = k; + + /* Since linux has a nice crc32 function use it for counting font + * checksums. */ + csum = crc32(0, new_data, size); + + FNTSUM(new_data) = csum; /* Check if the same font is on some other console already */ for (i = 0; i < MAX_NR_CONSOLES; i++) { struct vc_data *tmp = vc_cons[i].d; if (fb_display[i].userfont && fb_display[i].fontdata && - FNTSUM(fb_display[i].fontdata) == k && + FNTSUM(fb_display[i].fontdata) == csum && FNTSIZE(fb_display[i].fontdata) == size && tmp->vc_font.width == w && !memcmp(fb_display[i].fontdata, new_data, size)) { @@ -1916,43 +2293,22 @@ static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op) break; } } - return fbcon_do_set_font(vc, op, new_data, 1); + return fbcon_do_set_font(vc, font->width, font->height, new_data, 1); } -static inline int fbcon_set_def_font(struct vc_data *vc, struct console_font_op *op) +static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - char name[MAX_FONT_NAME]; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct font_desc *f; - if (!op->data) + if (!name) f = get_default_font(info->var.xres, info->var.yres); - else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0) - return -EFAULT; - else { - name[MAX_FONT_NAME - 1] = 0; - if (!(f = find_font(name))) - return -ENOENT; - } - op->width = f->width; - op->height = f->height; - return fbcon_do_set_font(vc, op, f->data, 0); -} - -static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op) -{ - switch (op->op) { - case KD_FONT_OP_SET: - return fbcon_set_font(vc, op); - case KD_FONT_OP_GET: - return fbcon_get_font(vc, op); - case KD_FONT_OP_SET_DEFAULT: - return fbcon_set_def_font(vc, op); - case KD_FONT_OP_COPY: - return fbcon_copy_font(vc, op); - default: - return -ENOSYS; - } + else if (!(f = find_font(name))) + return -ENOENT; + + font->width = f->width; + font->height = f->height; + return fbcon_do_set_font(vc, f->width, f->height, f->data, 0); } static u16 palette_red[16]; @@ -1965,28 +2321,34 @@ static struct fb_cmap palette_cmap = { static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) { - struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; - int i, j, k; + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + int i, j, k, depth; u8 val; - if (!vc->vc_can_do_color - || (!info->fbops->fb_blank && console_blanked)) + if (fbcon_is_inactive(vc, info)) return -EINVAL; - for (i = j = 0; i < 16; i++) { - k = table[i]; - val = vc->vc_palette[j++]; - palette_red[k] = (val << 8) | val; - val = vc->vc_palette[j++]; - palette_green[k] = (val << 8) | val; - val = vc->vc_palette[j++]; - palette_blue[k] = (val << 8) | val; - } - if (info->var.bits_per_pixel <= 4) - palette_cmap.len = 1 << info->var.bits_per_pixel; - else + + depth = fb_get_color_depth(info); + if (depth > 3) { + for (i = j = 0; i < 16; i++) { + k = table[i]; + val = vc->vc_palette[j++]; + palette_red[k] = (val << 8) | val; + val = vc->vc_palette[j++]; + palette_green[k] = (val << 8) | val; + val = vc->vc_palette[j++]; + palette_blue[k] = (val << 8) | val; + } palette_cmap.len = 16; - palette_cmap.start = 0; - return fb_set_cmap(&palette_cmap, 1, info); + palette_cmap.start = 0; + /* + * If framebuffer is capable of less than 16 colors, + * use default palette of fbcon. + */ + } else + fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); + + return fb_set_cmap(&palette_cmap, info); } static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) @@ -2069,7 +2431,7 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) static int fbcon_scrolldelta(struct vc_data *vc, int lines) { - struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]]; + struct fb_info *info = registered_fb[con2fb_map[fg_console]]; struct display *p = &fb_display[fg_console]; int offset, limit, scrollback_old; @@ -2105,7 +2467,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) update_region(vc->vc_num, vc->vc_origin, logo_lines * vc->vc_cols); } - logo_shown = -1; + logo_shown = FBCON_LOGO_CANSHOW; } fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); fbcon_redraw_softback(vc, p, lines); @@ -2125,19 +2487,19 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) if (scrollback_current == scrollback_old) return 0; - if (!info->fbops->fb_blank && - (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT - || !lines)) + if (fbcon_is_inactive(vc, info)) return 0; + fbcon_cursor(vc, CM_ERASE); offset = p->yscroll - scrollback_current; limit = p->vrows; - switch (p->scrollmode && __SCROLL_YMASK) { - case __SCROLL_YWRAP: + switch (p->scrollmode) { + case SCROLL_WRAP_MOVE: info->var.vmode |= FB_VMODE_YWRAP; break; - case __SCROLL_YPAN: + case SCROLL_PAN_MOVE: + case SCROLL_PAN_REDRAW: limit -= vc->vc_rows; info->var.vmode &= ~FB_VMODE_YWRAP; break; @@ -2156,32 +2518,159 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) static int fbcon_set_origin(struct vc_data *vc) { - if (softback_lines && !console_blanked) + if (softback_lines) fbcon_scrolldelta(vc, softback_lines); return 0; } static void fbcon_suspended(struct fb_info *info) { + struct vc_data *vc = NULL; + struct fbcon_ops *ops = info->fbcon_par; + + if (!ops || ops->currcon < 0) + return; + vc = vc_cons[ops->currcon].d; + /* Clear cursor, restore saved data */ - info->cursor.enable = 0; - info->fbops->fb_cursor(info, &info->cursor); + fbcon_cursor(vc, CM_ERASE); } static void fbcon_resumed(struct fb_info *info) { struct vc_data *vc; + struct fbcon_ops *ops = info->fbcon_par; - if (info->currcon < 0) + if (!ops || ops->currcon < 0) return; - vc = vc_cons[info->currcon].d; + vc = vc_cons[ops->currcon].d; update_screen(vc->vc_num); } + +static void fbcon_modechanged(struct fb_info *info) +{ + struct fbcon_ops *ops = info->fbcon_par; + struct vc_data *vc; + struct display *p; + int rows, cols; + + if (!ops || ops->currcon < 0 || vt_cons[ops->currcon]->vc_mode != + KD_TEXT || registered_fb[con2fb_map[ops->currcon]] != info) + return; + + vc = vc_cons[ops->currcon].d; + + p = &fb_display[vc->vc_num]; + + info->var.xoffset = info->var.yoffset = p->yscroll = 0; + + if (CON_IS_VISIBLE(vc)) { + var_to_display(p, &info->var, info); + cols = info->var.xres / vc->vc_font.width; + rows = info->var.yres / vc->vc_font.height; + vc_resize(vc->vc_num, cols, rows); + updatescrollmode(p, info, vc); + scrollback_max = 0; + scrollback_current = 0; + update_var(vc->vc_num, info); + fbcon_set_palette(vc, color_table); + update_screen(vc->vc_num); + if (softback_buf) { + int l = fbcon_softback_size / vc->vc_size_row; + if (l > 5) + softback_end = softback_buf + l * vc->vc_size_row; + else { + /* Smaller scrollback makes no sense, and 0 + would screw the operation totally */ + softback_top = 0; + } + } + } +} + +static int fbcon_mode_deleted(struct fb_info *info, + struct fb_videomode *mode) +{ + struct fb_info *fb_info; + struct display *p; + int i, j, found = 0; + + /* before deletion, ensure that mode is not in use */ + for (i = first_fb_vc; i <= last_fb_vc; i++) { + j = con2fb_map[i]; + if (j == -1) + continue; + fb_info = registered_fb[j]; + if (fb_info != info) + continue; + p = &fb_display[i]; + if (!p || !p->mode) + continue; + if (fb_mode_is_equal(p->mode, mode)) { + found = 1; + break; + } + } + return found; +} + +static int fbcon_fb_registered(int idx) +{ + int ret = 0, i; + + if (info_idx == -1) { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map_boot[i] == idx) { + info_idx = idx; + break; + } + } + if (info_idx != -1) + ret = fbcon_takeover(1); + } else { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (con2fb_map_boot[i] == idx) + set_con2fb_map(i, idx, 0); + } + } + + return ret; +} + +static void fbcon_fb_blanked(struct fb_info *info, int blank) +{ + struct fbcon_ops *ops = info->fbcon_par; + int valid = 1; + + if (!ops || ops->currcon < 0 || + vt_cons[ops->currcon]->vc_mode != KD_TEXT || + registered_fb[con2fb_map[ops->currcon]] != info) + valid = 0; + + if (valid) { + struct vc_data *vc; + + vc = vc_cons[ops->currcon].d; + + if (CON_IS_VISIBLE(vc)) { + if (blank) + do_blank_screen(0); + else + do_unblank_screen(0); + } + ops->blank_state = blank; + } +} + static int fbcon_event_notify(struct notifier_block *self, unsigned long action, void *data) { - struct fb_info *info = (struct fb_info *) data; + struct fb_event *event = data; + struct fb_info *info = event->info; + struct fb_videomode *mode; + struct fb_con2fbmap *con2fb; + int ret = 0; switch(action) { case FB_EVENT_SUSPEND: @@ -2190,8 +2679,31 @@ static int fbcon_event_notify(struct notifier_block *self, case FB_EVENT_RESUME: fbcon_resumed(info); break; + case FB_EVENT_MODE_CHANGE: + fbcon_modechanged(info); + break; + case FB_EVENT_MODE_DELETE: + mode = event->data; + ret = fbcon_mode_deleted(info, mode); + break; + case FB_EVENT_FB_REGISTERED: + ret = fbcon_fb_registered(info->node); + break; + case FB_EVENT_SET_CONSOLE_MAP: + con2fb = event->data; + ret = set_con2fb_map(con2fb->console - 1, + con2fb->framebuffer, 1); + break; + case FB_EVENT_GET_CONSOLE_MAP: + con2fb = event->data; + con2fb->framebuffer = con2fb_map[con2fb->console - 1]; + break; + case FB_EVENT_BLANK: + fbcon_fb_blanked(info, *(int *)event->data); + break; } - return 0; + + return ret; } /* @@ -2211,7 +2723,10 @@ const struct consw fb_con = { .con_bmove = fbcon_bmove, .con_switch = fbcon_switch, .con_blank = fbcon_blank, - .con_font_op = fbcon_font_op, + .con_font_set = fbcon_set_font, + .con_font_get = fbcon_get_font, + .con_font_default = fbcon_set_def_font, + .con_font_copy = fbcon_copy_font, .con_set_palette = fbcon_set_palette, .con_scrolldelta = fbcon_scrolldelta, .con_set_origin = fbcon_set_origin, @@ -2221,47 +2736,46 @@ const struct consw fb_con = { .con_resize = fbcon_resize, }; -static struct notifier_block fbcon_event_notifer = { +static struct notifier_block fbcon_event_notifier = { .notifier_call = fbcon_event_notify, }; -static int fbcon_event_notifier_registered; - int __init fb_console_init(void) { - int err; - - if (!num_registered_fb) - return -ENODEV; - - err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, - fbcon_is_default); - if (err) - return err; + int i; acquire_console_sem(); - if (!fbcon_event_notifier_registered) { - fb_register_client(&fbcon_event_notifer); - fbcon_event_notifier_registered = 1; - } + fb_register_client(&fbcon_event_notifier); release_console_sem(); + + for (i = 0; i < MAX_NR_CONSOLES; i++) + con2fb_map[i] = -1; + + if (num_registered_fb) { + for (i = 0; i < FB_MAX; i++) { + if (registered_fb[i] != NULL) { + info_idx = i; + break; + } + } + fbcon_takeover(0); + } + return 0; } +module_init(fb_console_init); + #ifdef MODULE void __exit fb_console_exit(void) { acquire_console_sem(); - if (fbcon_event_notifier_registered) { - fb_unregister_client(&fbcon_event_notifer); - fbcon_event_notifier_registered = 0; - } + fb_unregister_client(&fbcon_event_notifier); release_console_sem(); give_up_console(&fb_con); } -module_init(fb_console_init); module_exit(fb_console_exit); #endif @@ -2270,7 +2784,6 @@ module_exit(fb_console_exit); * Visible symbols for modules */ -EXPORT_SYMBOL(fb_display); EXPORT_SYMBOL(fb_con); MODULE_LICENSE("GPL");