diff options
-rw-r--r-- | drivers/video/console/Kconfig | 24 | ||||
-rw-r--r-- | drivers/video/console/vgacon.c | 244 |
2 files changed, 225 insertions, 43 deletions
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 6ee449858a5c..4444bef68fba 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -26,6 +26,30 @@ config VGA_CONSOLE # fi # fi +config VGACON_SOFT_SCROLLBACK + bool "Enable Scrollback Buffer in System RAM" + depends on VGA_CONSOLE + default n + help + The scrollback buffer of the standard VGA console is located in + the VGA RAM. The size of this RAM is fixed and is quite small. + If you require a larger scrollback buffer, this can be placed in + System RAM which is dynamically allocated during intialization. + Placing the scrollback buffer in System RAM will slightly slow + down the console. + + If you want this feature, say 'Y' here and enter the amount of + RAM to allocate for this buffer. If unsure, say 'N'. + +config VGACON_SOFT_SCROLLBACK_SIZE + int "Scrollback Buffer Size (in KB)" + depends on VGACON_SOFT_SCROLLBACK + default "64" + help + Enter the amount of System RAM to allocate for the scrollback + buffer. Each 64KB will give you approximately 16 80x25 + screenfuls of scrollback buffer + config VIDEO_SELECT bool "Video mode selection support" depends on X86 && VGA_CONSOLE diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 61894d5bb219..d5a04b68c4d4 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -93,7 +93,6 @@ static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity, static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); static unsigned long vgacon_uni_pagedir[2]; - /* Description of the hardware situation */ static unsigned long vga_vram_base; /* Base of video memory */ static unsigned long vga_vram_end; /* End of video memory */ @@ -161,6 +160,201 @@ static inline void write_vga(unsigned char reg, unsigned int val) spin_unlock_irqrestore(&vga_lock, flags); } +static inline void vga_set_mem_top(struct vc_data *c) +{ + write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); +} + +#ifdef CONFIG_VGACON_SOFT_SCROLLBACK +#include <linux/bootmem.h> +/* software scrollback */ +static void *vgacon_scrollback; +static int vgacon_scrollback_tail; +static int vgacon_scrollback_size; +static int vgacon_scrollback_rows; +static int vgacon_scrollback_cnt; +static int vgacon_scrollback_cur; +static int vgacon_scrollback_save; +static int vgacon_scrollback_restore; + +static void vgacon_scrollback_init(int pitch) +{ + int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch; + + if (vgacon_scrollback) { + vgacon_scrollback_cnt = 0; + vgacon_scrollback_tail = 0; + vgacon_scrollback_cur = 0; + vgacon_scrollback_rows = rows - 1; + vgacon_scrollback_size = rows * pitch; + } +} + +static void __init vgacon_scrollback_startup(void) +{ + vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE + * 1024); + vgacon_scrollback_init(vga_video_num_columns * 2); +} + +static void vgacon_scrollback_update(struct vc_data *c, int t, int count) +{ + void *p; + + if (!vgacon_scrollback_size || c->vc_num != fg_console) + return; + + p = (void *) (c->vc_origin + t * c->vc_size_row); + + while (count--) { + scr_memcpyw(vgacon_scrollback + vgacon_scrollback_tail, + p, c->vc_size_row); + vgacon_scrollback_cnt++; + p += c->vc_size_row; + vgacon_scrollback_tail += c->vc_size_row; + + if (vgacon_scrollback_tail >= vgacon_scrollback_size) + vgacon_scrollback_tail = 0; + + if (vgacon_scrollback_cnt > vgacon_scrollback_rows) + vgacon_scrollback_cnt = vgacon_scrollback_rows; + + vgacon_scrollback_cur = vgacon_scrollback_cnt; + } +} + +static void vgacon_restore_screen(struct vc_data *c) +{ + vgacon_scrollback_save = 0; + + if (!vga_is_gfx && !vgacon_scrollback_restore) { + scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, + c->vc_screenbuf_size > vga_vram_size ? + vga_vram_size : c->vc_screenbuf_size); + vgacon_scrollback_restore = 1; + vgacon_scrollback_cur = vgacon_scrollback_cnt; + } +} + +static int vgacon_scrolldelta(struct vc_data *c, int lines) +{ + int start, end, count, soff, diff; + void *d, *s; + + if (!lines) { + c->vc_visible_origin = c->vc_origin; + vga_set_mem_top(c); + return 1; + } + + if (!vgacon_scrollback) + return 1; + + if (!vgacon_scrollback_save) { + vgacon_cursor(c, CM_ERASE); + vgacon_save_screen(c); + vgacon_scrollback_save = 1; + } + + vgacon_scrollback_restore = 0; + start = vgacon_scrollback_cur + lines; + end = start + abs(lines); + + if (start < 0) + start = 0; + + if (start > vgacon_scrollback_cnt) + start = vgacon_scrollback_cnt; + + if (end < 0) + end = 0; + + if (end > vgacon_scrollback_cnt) + end = vgacon_scrollback_cnt; + + vgacon_scrollback_cur = start; + count = end - start; + soff = vgacon_scrollback_tail - ((vgacon_scrollback_cnt - end) * + c->vc_size_row); + soff -= count * c->vc_size_row; + + if (soff < 0) + soff += vgacon_scrollback_size; + + count = vgacon_scrollback_cnt - start; + + if (count > c->vc_rows) + count = c->vc_rows; + + diff = c->vc_rows - count; + + d = (void *) c->vc_origin; + s = (void *) c->vc_screenbuf; + + while (count--) { + scr_memcpyw(d, vgacon_scrollback + soff, c->vc_size_row); + d += c->vc_size_row; + soff += c->vc_size_row; + + if (soff >= vgacon_scrollback_size) + soff = 0; + } + + if (diff == c->vc_rows) { + vgacon_cursor(c, CM_MOVE); + } else { + while (diff--) { + scr_memcpyw(d, s, c->vc_size_row); + d += c->vc_size_row; + s += c->vc_size_row; + } + } + + return 1; +} +#else +#define vgacon_scrollback_startup(...) do { } while (0) +#define vgacon_scrollback_init(...) do { } while (0) +#define vgacon_scrollback_update(...) do { } while (0) + +static void vgacon_restore_screen(struct vc_data *c) +{ + if (c->vc_origin != c->vc_visible_origin) + vgacon_scrolldelta(c, 0); +} + +static int vgacon_scrolldelta(struct vc_data *c, int lines) +{ + if (!lines) /* Turn scrollback off */ + c->vc_visible_origin = c->vc_origin; + else { + int margin = c->vc_size_row * 4; + int ul, we, p, st; + + if (vga_rolled_over > + (c->vc_scr_end - vga_vram_base) + margin) { + ul = c->vc_scr_end - vga_vram_base; + we = vga_rolled_over + c->vc_size_row; + } else { + ul = 0; + we = vga_vram_size; + } + p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + + lines * c->vc_size_row; + st = (c->vc_origin - vga_vram_base - ul + we) % we; + if (st < 2 * margin) + margin = 0; + if (p < margin) + p = 0; + if (p > st - margin) + p = st; + c->vc_visible_origin = vga_vram_base + (p + ul) % we; + } + vga_set_mem_top(c); + return 1; +} +#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ + static const char __init *vgacon_startup(void) { const char *display_desc = NULL; @@ -330,7 +524,7 @@ static const char __init *vgacon_startup(void) vgacon_xres = ORIG_VIDEO_COLS * VGA_FONTWIDTH; vgacon_yres = vga_scan_lines; - + vgacon_scrollback_startup(); return display_desc; } @@ -357,11 +551,6 @@ static void vgacon_init(struct vc_data *c, int init) con_set_default_unimap(c); } -static inline void vga_set_mem_top(struct vc_data *c) -{ - write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); -} - static void vgacon_deinit(struct vc_data *c) { /* When closing the last console, reset video origin */ @@ -455,8 +644,8 @@ static void vgacon_set_cursor_size(int xpos, int from, int to) static void vgacon_cursor(struct vc_data *c, int mode) { - if (c->vc_origin != c->vc_visible_origin) - vgacon_scrolldelta(c, 0); + vgacon_restore_screen(c); + switch (mode) { case CM_ERASE: write_vga(14, (c->vc_pos - vga_vram_base) / 2); @@ -606,6 +795,7 @@ static int vgacon_switch(struct vc_data *c) vgacon_doresize(c, c->vc_cols, c->vc_rows); } + vgacon_scrollback_init(c->vc_size_row); return 0; /* Redrawing not needed */ } @@ -1073,37 +1263,6 @@ static int vgacon_resize(struct vc_data *c, unsigned int width, return 0; } -static int vgacon_scrolldelta(struct vc_data *c, int lines) -{ - if (!lines) /* Turn scrollback off */ - c->vc_visible_origin = c->vc_origin; - else { - int margin = c->vc_size_row * 4; - int ul, we, p, st; - - if (vga_rolled_over > - (c->vc_scr_end - vga_vram_base) + margin) { - ul = c->vc_scr_end - vga_vram_base; - we = vga_rolled_over + c->vc_size_row; - } else { - ul = 0; - we = vga_vram_size; - } - p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + - lines * c->vc_size_row; - st = (c->vc_origin - vga_vram_base - ul + we) % we; - if (st < 2 * margin) - margin = 0; - if (p < margin) - p = 0; - if (p > st - margin) - p = st; - c->vc_visible_origin = vga_vram_base + (p + ul) % we; - } - vga_set_mem_top(c); - return 1; -} - static int vgacon_set_origin(struct vc_data *c) { if (vga_is_gfx || /* We don't play origin tricks in graphic modes */ @@ -1146,15 +1305,14 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, if (t || b != c->vc_rows || vga_is_gfx) return 0; - if (c->vc_origin != c->vc_visible_origin) - vgacon_scrolldelta(c, 0); - if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) return 0; + vgacon_restore_screen(c); oldo = c->vc_origin; delta = lines * c->vc_size_row; if (dir == SM_UP) { + vgacon_scrollback_update(c, t, lines); if (c->vc_scr_end + delta >= vga_vram_end) { scr_memcpyw((u16 *) vga_vram_base, (u16 *) (oldo + delta), |