Input: wacom - add support for Intuos4 tablets
[safe/jmp/linux-2.6] / drivers / video / console / vgacon.c
index bcf59b2..38e86b8 100644 (file)
  *  more details.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
-#include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
 #include <linux/kd.h>
@@ -49,6 +46,7 @@
 #include <linux/spinlock.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/screen_info.h>
 #include <linux/smp_lock.h>
 #include <video/vga.h>
 #include <asm/io.h>
@@ -56,6 +54,8 @@
 static DEFINE_SPINLOCK(vga_lock);
 static int cursor_size_lastfrom;
 static int cursor_size_lastto;
+static u32 vgacon_xres;
+static u32 vgacon_yres;
 static struct vgastate state;
 
 #define BLANK 0x0020
@@ -69,7 +69,7 @@ static struct vgastate state;
  * appear.
  */
 #undef TRIDENT_GLITCH
-
+#define VGA_FONTWIDTH       8   /* VGA does not support fontwidths != 8 */
 /*
  *  Interface used by the world
  */
@@ -86,32 +86,48 @@ static int vgacon_set_origin(struct vc_data *c);
 static void vgacon_save_screen(struct vc_data *c);
 static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
                         int lines);
-static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
-                           u8 blink, u8 underline, u8 reverse);
 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 */
-static u16             vga_video_port_reg;     /* Video register select port */
-static u16             vga_video_port_val;     /* Video register value port */
-static unsigned int    vga_video_num_columns;  /* Number of text columns */
-static unsigned int    vga_video_num_lines;    /* Number of text lines */
-static int             vga_can_do_color = 0;   /* Do we support colors? */
-static unsigned int    vga_default_font_height;/* Height of default screen font */
-static unsigned char   vga_video_type;         /* Card type */
-static unsigned char   vga_hardscroll_enabled;
-static unsigned char   vga_hardscroll_user_enable = 1;
+static int             vga_init_done           __read_mostly;
+static unsigned long   vga_vram_base           __read_mostly;  /* Base of video memory */
+static unsigned long   vga_vram_end            __read_mostly;  /* End of video memory */
+static unsigned int    vga_vram_size           __read_mostly;  /* Size of video memory */
+static u16             vga_video_port_reg      __read_mostly;  /* Video register select port */
+static u16             vga_video_port_val      __read_mostly;  /* Video register value port */
+static unsigned int    vga_video_num_columns;                  /* Number of text columns */
+static unsigned int    vga_video_num_lines;                    /* Number of text lines */
+static int             vga_can_do_color        __read_mostly;  /* Do we support colors? */
+static unsigned int    vga_default_font_height __read_mostly;  /* Height of default screen font */
+static unsigned char   vga_video_type          __read_mostly;  /* Card type */
+static unsigned char   vga_hardscroll_enabled  __read_mostly;
+static unsigned char   vga_hardscroll_user_enable __read_mostly = 1;
 static unsigned char   vga_font_is_default = 1;
 static int             vga_vesa_blanked;
 static int             vga_palette_blanked;
 static int             vga_is_gfx;
 static int             vga_512_chars;
 static int             vga_video_font_height;
-static int             vga_scan_lines;
-static unsigned int    vga_rolled_over = 0;
+static int             vga_scan_lines          __read_mostly;
+static unsigned int    vga_rolled_over;
+
+int vgacon_text_mode_force = 0;
+
+bool vgacon_text_force(void)
+{
+       return vgacon_text_mode_force ? true : false;
+}
+EXPORT_SYMBOL(vgacon_text_force);
+
+static int __init text_mode(char *str)
+{
+       vgacon_text_mode_force = 1;
+       return 1;
+}
+
+/* force text mode - used by kernel modesetting */
+__setup("nomodeset", text_mode);
 
 static int __init no_scroll(char *str)
 {
@@ -158,13 +174,211 @@ static inline void write_vga(unsigned char reg, unsigned int val)
        spin_unlock_irqrestore(&vga_lock, flags);
 }
 
-static const char __init *vgacon_startup(void)
+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;
+       }
+}
+
+/*
+ * Called only duing init so call of alloc_bootmen is ok.
+ * Marked __init_refok to silence modpost.
+ */
+static void __init_refok 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;
+
+       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;
+
+       if (count) {
+               int copysize;
+
+               int diff = c->vc_rows - count;
+               void *d = (void *) c->vc_origin;
+               void *s = (void *) c->vc_screenbuf;
+
+               count *= c->vc_size_row;
+               /* how much memory to end of buffer left? */
+               copysize = min(count, vgacon_scrollback_size - soff);
+               scr_memcpyw(d, vgacon_scrollback + soff, copysize);
+               d += copysize;
+               count -= copysize;
+
+               if (count) {
+                       scr_memcpyw(d, vgacon_scrollback, count);
+                       d += count;
+               }
+
+               if (diff)
+                       scr_memcpyw(d, s, diff * c->vc_size_row);
+       } else
+               vgacon_cursor(c, CM_MOVE);
+
+       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 *vgacon_startup(void)
 {
        const char *display_desc = NULL;
        u16 saved1, saved2;
        volatile u16 *p;
 
-       if (ORIG_VIDEO_ISVGA == VIDEO_TYPE_VLFB) {
+       if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB) {
              no_vga:
 #ifdef CONFIG_DUMMY_CONSOLE
                conswitchp = &dummy_con;
@@ -174,37 +388,44 @@ static const char __init *vgacon_startup(void)
 #endif
        }
 
+       /* boot_params.screen_info initialized? */
+       if ((screen_info.orig_video_mode  == 0) &&
+           (screen_info.orig_video_lines == 0) &&
+           (screen_info.orig_video_cols  == 0))
+               goto no_vga;
+
        /* VGA16 modes are not handled by VGACON */
-       if ((ORIG_VIDEO_MODE == 0x0D) ||        /* 320x200/4 */
-           (ORIG_VIDEO_MODE == 0x0E) ||        /* 640x200/4 */
-           (ORIG_VIDEO_MODE == 0x10) ||        /* 640x350/4 */
-           (ORIG_VIDEO_MODE == 0x12) ||        /* 640x480/4 */
-           (ORIG_VIDEO_MODE == 0x6A))  /* 800x600/4, 0x6A is very common */
+       if ((screen_info.orig_video_mode == 0x0D) ||    /* 320x200/4 */
+           (screen_info.orig_video_mode == 0x0E) ||    /* 640x200/4 */
+           (screen_info.orig_video_mode == 0x10) ||    /* 640x350/4 */
+           (screen_info.orig_video_mode == 0x12) ||    /* 640x480/4 */
+           (screen_info.orig_video_mode == 0x6A))      /* 800x600/4 (VESA) */
                goto no_vga;
 
-       vga_video_num_lines = ORIG_VIDEO_LINES;
-       vga_video_num_columns = ORIG_VIDEO_COLS;
+       vga_video_num_lines = screen_info.orig_video_lines;
+       vga_video_num_columns = screen_info.orig_video_cols;
        state.vgabase = NULL;
 
-       if (ORIG_VIDEO_MODE == 7) {     /* Is this a monochrome display? */
+       if (screen_info.orig_video_mode == 7) {
+               /* Monochrome display */
                vga_vram_base = 0xb0000;
                vga_video_port_reg = VGA_CRT_IM;
                vga_video_port_val = VGA_CRT_DM;
-               if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {
+               if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
                        static struct resource ega_console_resource =
-                           { "ega", 0x3B0, 0x3BF };
+                           { .name = "ega", .start = 0x3B0, .end = 0x3BF };
                        vga_video_type = VIDEO_TYPE_EGAM;
-                       vga_vram_end = 0xb8000;
+                       vga_vram_size = 0x8000;
                        display_desc = "EGA+";
                        request_resource(&ioport_resource,
                                         &ega_console_resource);
                } else {
                        static struct resource mda1_console_resource =
-                           { "mda", 0x3B0, 0x3BB };
+                           { .name = "mda", .start = 0x3B0, .end = 0x3BB };
                        static struct resource mda2_console_resource =
-                           { "mda", 0x3BF, 0x3BF };
+                           { .name = "mda", .start = 0x3BF, .end = 0x3BF };
                        vga_video_type = VIDEO_TYPE_MDA;
-                       vga_vram_end = 0xb2000;
+                       vga_vram_size = 0x2000;
                        display_desc = "*MDA";
                        request_resource(&ioport_resource,
                                         &mda1_console_resource);
@@ -218,21 +439,21 @@ static const char __init *vgacon_startup(void)
                vga_vram_base = 0xb8000;
                vga_video_port_reg = VGA_CRT_IC;
                vga_video_port_val = VGA_CRT_DC;
-               if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {
+               if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
                        int i;
 
-                       vga_vram_end = 0xc0000;
+                       vga_vram_size = 0x8000;
 
-                       if (!ORIG_VIDEO_ISVGA) {
+                       if (!screen_info.orig_video_isVGA) {
                                static struct resource ega_console_resource
-                                   = { "ega", 0x3C0, 0x3DF };
+                                   = { .name = "ega", .start = 0x3C0, .end = 0x3DF };
                                vga_video_type = VIDEO_TYPE_EGAC;
                                display_desc = "EGA";
                                request_resource(&ioport_resource,
                                                 &ega_console_resource);
                        } else {
                                static struct resource vga_console_resource
-                                   = { "vga+", 0x3C0, 0x3DF };
+                                   = { .name = "vga+", .start = 0x3C0, .end = 0x3DF };
                                vga_video_type = VIDEO_TYPE_VGAC;
                                display_desc = "VGA+";
                                request_resource(&ioport_resource,
@@ -246,7 +467,7 @@ static const char __init *vgacon_startup(void)
                                 * and COE=1 isn't necessarily a good idea)
                                 */
                                vga_vram_base = 0xa0000;
-                               vga_vram_end = 0xb0000;
+                               vga_vram_size = 0x10000;
                                outb_p(6, VGA_GFX_I);
                                outb_p(6, VGA_GFX_D);
 #endif
@@ -276,9 +497,9 @@ static const char __init *vgacon_startup(void)
                        }
                } else {
                        static struct resource cga_console_resource =
-                           { "cga", 0x3D4, 0x3D5 };
+                           { .name = "cga", .start = 0x3D4, .end = 0x3D5 };
                        vga_video_type = VIDEO_TYPE_CGA;
-                       vga_vram_end = 0xba000;
+                       vga_vram_size = 0x2000;
                        display_desc = "*CGA";
                        request_resource(&ioport_resource,
                                         &cga_console_resource);
@@ -286,8 +507,8 @@ static const char __init *vgacon_startup(void)
                }
        }
 
-       vga_vram_base = VGA_MAP_MEM(vga_vram_base);
-       vga_vram_end = VGA_MAP_MEM(vga_vram_end);
+       vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size);
+       vga_vram_end = vga_vram_base + vga_vram_size;
 
        /*
         *      Find out if there is a graphics card present.
@@ -317,12 +538,21 @@ static const char __init *vgacon_startup(void)
            || vga_video_type == VIDEO_TYPE_VGAC
            || vga_video_type == VIDEO_TYPE_EGAM) {
                vga_hardscroll_enabled = vga_hardscroll_user_enable;
-               vga_default_font_height = ORIG_VIDEO_POINTS;
-               vga_video_font_height = ORIG_VIDEO_POINTS;
+               vga_default_font_height = screen_info.orig_video_points;
+               vga_video_font_height = screen_info.orig_video_points;
                /* This may be suboptimal but is a safe bet - go with it */
                vga_scan_lines =
                    vga_video_font_height * vga_video_num_lines;
        }
+
+       vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH;
+       vgacon_yres = vga_scan_lines;
+
+       if (!vga_init_done) {
+               vgacon_scrollback_startup();
+               vga_init_done = 1;
+       }
+
        return display_desc;
 }
 
@@ -330,10 +560,20 @@ static void vgacon_init(struct vc_data *c, int init)
 {
        unsigned long p;
 
-       /* We cannot be loaded as a module, therefore init is always 1 */
+       /*
+        * We cannot be loaded as a module, therefore init is always 1,
+        * but vgacon_init can be called more than once, and init will
+        * not be 1.
+        */
        c->vc_can_do_color = vga_can_do_color;
-       c->vc_cols = vga_video_num_columns;
-       c->vc_rows = vga_video_num_lines;
+
+       /* set dimensions manually if init != 0 since vc_resize() will fail */
+       if (init) {
+               c->vc_cols = vga_video_num_columns;
+               c->vc_rows = vga_video_num_lines;
+       } else
+               vc_resize(c, vga_video_num_columns, vga_video_num_lines);
+
        c->vc_scan_lines = vga_scan_lines;
        c->vc_font.height = vga_video_font_height;
        c->vc_complement_mask = 0x7700;
@@ -349,11 +589,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 */
@@ -367,12 +602,14 @@ static void vgacon_deinit(struct vc_data *c)
 }
 
 static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
-                           u8 blink, u8 underline, u8 reverse)
+                           u8 blink, u8 underline, u8 reverse, u8 italic)
 {
        u8 attr = color;
 
        if (vga_can_do_color) {
-               if (underline)
+               if (italic)
+                       attr = (attr & 0xF0) | c->vc_itcolor;
+               else if (underline)
                        attr = (attr & 0xf0) | c->vc_ulcolor;
                else if (intensity == 0)
                        attr = (attr & 0xf0) | c->vc_halfcolor;
@@ -386,7 +623,9 @@ static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
        if (intensity == 2)
                attr ^= 0x08;
        if (!vga_can_do_color) {
-               if (underline)
+               if (italic)
+                       attr = (attr & 0xF8) | 0x02;
+               else if (underline)
                        attr = (attr & 0xf8) | 0x01;
                else if (intensity == 0)
                        attr = (attr & 0xf0) | 0x08;
@@ -425,28 +664,40 @@ static void vgacon_set_cursor_size(int xpos, int from, int to)
        cursor_size_lastto = to;
 
        spin_lock_irqsave(&vga_lock, flags);
-       outb_p(0x0a, vga_video_port_reg);       /* Cursor start */
-       curs = inb_p(vga_video_port_val);
-       outb_p(0x0b, vga_video_port_reg);       /* Cursor end */
-       cure = inb_p(vga_video_port_val);
+       if (vga_video_type >= VIDEO_TYPE_VGAC) {
+               outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
+               curs = inb_p(vga_video_port_val);
+               outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
+               cure = inb_p(vga_video_port_val);
+       } else {
+               curs = 0;
+               cure = 0;
+       }
 
        curs = (curs & 0xc0) | from;
        cure = (cure & 0xe0) | to;
 
-       outb_p(0x0a, vga_video_port_reg);       /* Cursor start */
+       outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
        outb_p(curs, vga_video_port_val);
-       outb_p(0x0b, vga_video_port_reg);       /* Cursor end */
+       outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
        outb_p(cure, vga_video_port_val);
        spin_unlock_irqrestore(&vga_lock, flags);
 }
 
 static void vgacon_cursor(struct vc_data *c, int mode)
 {
-       if (c->vc_origin != c->vc_visible_origin)
-               vgacon_scrolldelta(c, 0);
+       if (c->vc_mode != KD_TEXT)
+               return;
+
+       vgacon_restore_screen(c);
+
        switch (mode) {
        case CM_ERASE:
-               write_vga(14, (vga_vram_end - vga_vram_base - 1) / 2);
+               write_vga(14, (c->vc_pos - vga_vram_base) / 2);
+               if (vga_video_type >= VIDEO_TYPE_VGAC)
+                       vgacon_set_cursor_size(c->vc_x, 31, 30);
+               else
+                       vgacon_set_cursor_size(c->vc_x, 31, 31);
                break;
 
        case CM_MOVE:
@@ -484,7 +735,10 @@ static void vgacon_cursor(struct vc_data *c, int mode)
                                                10 ? 1 : 2));
                        break;
                case CUR_NONE:
-                       vgacon_set_cursor_size(c->vc_x, 31, 30);
+                       if (vga_video_type >= VIDEO_TYPE_VGAC)
+                               vgacon_set_cursor_size(c->vc_x, 31, 30);
+                       else
+                               vgacon_set_cursor_size(c->vc_x, 31, 31);
                        break;
                default:
                        vgacon_set_cursor_size(c->vc_x, 1,
@@ -495,8 +749,74 @@ static void vgacon_cursor(struct vc_data *c, int mode)
        }
 }
 
+static int vgacon_doresize(struct vc_data *c,
+               unsigned int width, unsigned int height)
+{
+       unsigned long flags;
+       unsigned int scanlines = height * c->vc_font.height;
+       u8 scanlines_lo = 0, r7 = 0, vsync_end = 0, mode, max_scan;
+
+       spin_lock_irqsave(&vga_lock, flags);
+
+       vgacon_xres = width * VGA_FONTWIDTH;
+       vgacon_yres = height * c->vc_font.height;
+       if (vga_video_type >= VIDEO_TYPE_VGAC) {
+               outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg);
+               max_scan = inb_p(vga_video_port_val);
+
+               if (max_scan & 0x80)
+                       scanlines <<= 1;
+
+               outb_p(VGA_CRTC_MODE, vga_video_port_reg);
+               mode = inb_p(vga_video_port_val);
+
+               if (mode & 0x04)
+                       scanlines >>= 1;
+
+               scanlines -= 1;
+               scanlines_lo = scanlines & 0xff;
+
+               outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
+               r7 = inb_p(vga_video_port_val) & ~0x42;
+
+               if (scanlines & 0x100)
+                       r7 |= 0x02;
+               if (scanlines & 0x200)
+                       r7 |= 0x40;
+
+               /* deprotect registers */
+               outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
+               vsync_end = inb_p(vga_video_port_val);
+               outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
+               outb_p(vsync_end & ~0x80, vga_video_port_val);
+       }
+
+       outb_p(VGA_CRTC_H_DISP, vga_video_port_reg);
+       outb_p(width - 1, vga_video_port_val);
+       outb_p(VGA_CRTC_OFFSET, vga_video_port_reg);
+       outb_p(width >> 1, vga_video_port_val);
+
+       if (vga_video_type >= VIDEO_TYPE_VGAC) {
+               outb_p(VGA_CRTC_V_DISP_END, vga_video_port_reg);
+               outb_p(scanlines_lo, vga_video_port_val);
+               outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
+               outb_p(r7,vga_video_port_val);
+
+               /* reprotect registers */
+               outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
+               outb_p(vsync_end, vga_video_port_val);
+       }
+
+       spin_unlock_irqrestore(&vga_lock, flags);
+       return 0;
+}
+
 static int vgacon_switch(struct vc_data *c)
 {
+       int x = c->vc_cols * VGA_FONTWIDTH;
+       int y = c->vc_rows * c->vc_font.height;
+       int rows = screen_info.orig_video_lines * vga_default_font_height/
+               c->vc_font.height;
        /*
         * We need to save screen size here as it's the only way
         * we can spot the screen has been resized and we need to
@@ -504,9 +824,23 @@ static int vgacon_switch(struct vc_data *c)
         */
        vga_video_num_columns = c->vc_cols;
        vga_video_num_lines = c->vc_rows;
-       if (!vga_is_gfx)
+
+       /* We can only copy out the size of the video buffer here,
+        * otherwise we get into VGA BIOS */
+
+       if (!vga_is_gfx) {
                scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
-                           c->vc_screenbuf_size);
+                           c->vc_screenbuf_size > vga_vram_size ?
+                               vga_vram_size : c->vc_screenbuf_size);
+
+               if ((vgacon_xres != x || vgacon_yres != y) &&
+                   (!(vga_video_num_columns % 2) &&
+                    vga_video_num_columns <= screen_info.orig_video_cols &&
+                    vga_video_num_lines <= rows))
+                       vgacon_doresize(c, c->vc_cols, c->vc_rows);
+       }
+
+       vgacon_scrollback_init(c->vc_size_row);
        return 0;               /* Redrawing not needed */
 }
 
@@ -514,6 +848,7 @@ static void vga_set_palette(struct vc_data *vc, unsigned char *table)
 {
        int i, j;
 
+       vga_w(state.vgabase, VGA_PEL_MSK, 0xff);
        for (i = j = 0; i < 16; i++) {
                vga_w(state.vgabase, VGA_PEL_IW, table[i]);
                vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
@@ -656,6 +991,7 @@ static void vga_pal_blank(struct vgastate *state)
 {
        int i;
 
+       vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
        for (i = 0; i < 16; i++) {
                vga_w(state->vgabase, VGA_PEL_IW, i);
                vga_w(state->vgabase, VGA_PEL_D, 0);
@@ -729,14 +1065,14 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
        char *charmap;
        
        if (vga_video_type != VIDEO_TYPE_EGAM) {
-               charmap = (char *) VGA_MAP_MEM(colourmap);
+               charmap = (char *) VGA_MAP_MEM(colourmap, 0);
                beg = 0x0e;
 #ifdef VGA_CAN_DO_64KB
                if (vga_video_type == VIDEO_TYPE_VGAC)
                        beg = 0x06;
 #endif
        } else {
-               charmap = (char *) VGA_MAP_MEM(blackwmap);
+               charmap = (char *) VGA_MAP_MEM(blackwmap, 0);
                beg = 0x0a;
        }
 
@@ -833,8 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 
        /* if 512 char mode is already enabled don't re-enable it. */
        if ((set) && (ch512 != vga_512_chars)) {
-               int i;  
-               
                /* attribute controller */
                for (i = 0; i < MAX_NR_CONSOLES; i++) {
                        struct vc_data *c = vc_cons[i].d;
@@ -898,6 +1232,7 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
        outb_p(0x12, vga_video_port_reg);       /* Vertical display limit */
        outb_p(vde, vga_video_port_val);
        spin_unlock_irq(&vga_lock);
+       vga_video_font_height = fontheight;
 
        for (i = 0; i < MAX_NR_CONSOLES; i++) {
                struct vc_data *c = vc_cons[i].d;
@@ -924,7 +1259,8 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigne
        if (vga_video_type < VIDEO_TYPE_EGAM)
                return -EINVAL;
 
-       if (font->width != 8 || (charcount != 256 && charcount != 512))
+       if (font->width != VGA_FONTWIDTH ||
+           (charcount != 256 && charcount != 512))
                return -EINVAL;
 
        rc = vgacon_do_font_op(&state, font->data, 1, charcount == 512);
@@ -941,12 +1277,12 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font)
        if (vga_video_type < VIDEO_TYPE_EGAM)
                return -EINVAL;
 
-       font->width = 8;
+       font->width = VGA_FONTWIDTH;
        font->height = c->vc_font.height;
        font->charcount = vga_512_chars ? 512 : 256;
        if (!font->data)
                return 0;
-       return vgacon_do_font_op(&state, font->data, 0, 0);
+       return vgacon_do_font_op(&state, font->data, 0, vga_512_chars);
 }
 
 #else
@@ -956,36 +1292,19 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font)
 
 #endif
 
-static int vgacon_scrolldelta(struct vc_data *c, int lines)
+static int vgacon_resize(struct vc_data *c, unsigned int width,
+                        unsigned int height, unsigned int user)
 {
-       if (!lines)             /* Turn scrollback off */
-               c->vc_visible_origin = c->vc_origin;
-       else {
-               int vram_size = vga_vram_end - vga_vram_base;
-               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 = 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;
+       if (width % 2 || width > screen_info.orig_video_cols ||
+           height > (screen_info.orig_video_lines * vga_default_font_height)/
+           c->vc_font.height)
+               /* let svgatextmode tinker with video timings and
+                  return success */
+               return (user) ? 0 : -EINVAL;
+
+       if (CON_IS_VISIBLE(c) && !vga_is_gfx) /* who knows */
+               vgacon_doresize(c, width, height);
+       return 0;
 }
 
 static int vgacon_set_origin(struct vc_data *c)
@@ -1009,12 +1328,16 @@ static void vgacon_save_screen(struct vc_data *c)
                 * console initialization routines.
                 */
                vga_bootup_console = 1;
-               c->vc_x = ORIG_X;
-               c->vc_y = ORIG_Y;
+               c->vc_x = screen_info.orig_x;
+               c->vc_y = screen_info.orig_y;
        }
+
+       /* We can't copy in more than the size of the video buffer,
+        * or we'll be copying in VGA BIOS */
+
        if (!vga_is_gfx)
                scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin,
-                           c->vc_screenbuf_size);
+                           c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size);
 }
 
 static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
@@ -1023,18 +1346,17 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
        unsigned long oldo;
        unsigned int delta;
 
-       if (t || b != c->vc_rows || vga_is_gfx)
+       if (t || b != c->vc_rows || vga_is_gfx || c->vc_mode != KD_TEXT)
                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),
@@ -1094,6 +1416,7 @@ const struct consw vga_con = {
        .con_blank = vgacon_blank,
        .con_font_set = vgacon_font_set,
        .con_font_get = vgacon_font_get,
+       .con_resize = vgacon_resize,
        .con_set_palette = vgacon_set_palette,
        .con_scrolldelta = vgacon_scrolldelta,
        .con_set_origin = vgacon_set_origin,