Subject: sgiarcs_con generic SGI console driver

From: Adrian Schroeter <[email-masked]>
Date: Tue, 14 May 2002 11:26:05 +0200 (CEST)

Hi,

Michael Schroeder has written a console driver that only uses
functions from the ARCS prom and therefore should work on all
SGIs. The disadvantage is that it is not very fast and it needs
the RAM area reserved for the PROM. But you'll get a nice console
on IMPACT machines and other.

enjoy,
adrian

**********************************************************************
Adrian Schroeter
SuSE AG, Deutschherrnstr. 15-19, 90429 Nuernberg, Germany
email: [email-masked]   (296 mails already received today.)

--- ./arch/mips/arc/arc_con.c.orig	2002-05-11 17:17:06.000000000 +0200
+++ ./arch/mips/arc/arc_con.c	2002-05-11 17:17:08.000000000 +0200
@@ -35,8 +35,11 @@
 	return prom_getchar();
 }
 
+extern int prom_memory_freed;
+
 static int __init prom_console_setup(struct console *co, char *options)
 {
+	prom_memory_freed = -1;		/* needed for prom functions */
 	return 0;
 }
 
--- ./arch/mips/config.in.orig	2002-05-11 19:09:59.000000000 +0200
+++ ./arch/mips/config.in	2002-05-11 19:38:57.000000000 +0200
@@ -590,7 +590,8 @@
    comment 'SGI Character devices'
    if [ "$CONFIG_VT" = "y" ]; then
       tristate 'SGI Newport Console support' CONFIG_SGI_NEWPORT_CONSOLE
-      if [ "$CONFIG_SGI_NEWPORT_CONSOLE" = "y" ]; then
+      tristate 'SGI ARCS Console support' CONFIG_SGI_ARCS_CONSOLE
+      if [ "$CONFIG_SGI_NEWPORT_CONSOLE" = "y" -o "$CONFIG_SGI_ARCS_CONSOLE" = "y" ]; then
 	 define_bool CONFIG_FONT_8x16 y
       fi
       define_bool CONFIG_DUMMY_CONSOLE y
--- ./arch/mips/mm/init.c.orig	2002-05-11 17:15:22.000000000 +0200
+++ ./arch/mips/mm/init.c	2002-05-11 17:15:47.000000000 +0200
@@ -360,11 +360,16 @@
 extern char __init_begin, __init_end;
 extern void prom_free_prom_memory(void);
 
+int	prom_memory_freed;
+
 void free_initmem(void)
 {
 	unsigned long addr;
 
-	prom_free_prom_memory ();
+	if (prom_memory_freed == 0) {
+		prom_free_prom_memory ();
+		prom_memory_freed = 1;
+	}
     
 	addr = (unsigned long) &__init_begin;
 	while (addr < (unsigned long) &__init_end) {
--- ./arch/mips/sgi-ip22/ip22-mc.c.orig	2002-05-13 21:59:37.000000000 +0200
+++ ./arch/mips/sgi-ip22/ip22-mc.c	2002-05-13 22:57:30.000000000 +0200
@@ -162,3 +162,8 @@
 	}
 	mcmisc_regs->gioparm = tmpreg; /* poof */
 }
+
+void
+sgimc_exp064() {
+	mcmisc_regs->gioparm |= SGIMC_GIOPARM_EXP064;
+}
--- ./arch/mips/sgi-ip22/ip22-setup.c.orig	2002-05-11 16:59:43.000000000 +0200
+++ ./arch/mips/sgi-ip22/ip22-setup.c	2002-05-13 23:26:39.000000000 +0200
@@ -124,6 +124,26 @@
 {
 }
 
+struct sgi_promgfxinfo sgi_promgfxinfo;
+struct sgi_promgfx *sgi_promgfx;
+
+static void sgi_getgfxinfo(void)
+{
+	struct sgi_pvector *pvector = (PROMBLOCK)->pvector;
+	sgi_promgfx = ARC_CALL0_VEC(pvector, get_promgfx);
+	if (sgi_promgfx && sgi_promgfx->gfxaddr) {
+		/* we copy some fields into sgi_promgfxinfo so that
+		 * we have the info even if the prom data is freed.
+		 */
+		sgi_promgfxinfo.addr = sgi_promgfx->gfxaddr;
+		sgi_promgfxinfo.width = sgi_promgfx->width;
+		sgi_promgfxinfo.height = sgi_promgfx->height;
+	}
+	/* impact graphics seems to need EXP064 */
+	if (sgi_promgfxinfo.addr == 0xbf400000)
+		sgimc_exp064();
+}
+
 void __init ip22_setup(void)
 {
 #ifdef CONFIG_SERIAL_CONSOLE
@@ -142,6 +162,7 @@
 	/* Init INDY memory controller. */
 	sgimc_init();
 
+	sgi_getgfxinfo();
 	/* Now enable boardcaches, if any. */
 	indy_sc_init();
 
@@ -186,10 +207,11 @@
  
 	sgi_volume_set(simple_strtoul(ArcGetEnvironmentVariable("volume"), NULL, 10));
 
+	sgi_getgfxinfo();
 #ifdef CONFIG_VT
+	conswitchp = 0;
 #ifdef CONFIG_SGI_NEWPORT_CONSOLE
-	if( mips_machtype == MACH_SGI_INDY ) {
-		conswitchp = &newport_con;
+	if (sgi_promgfxinfo.addr == 0xbf0f0000 || sgi_promgfxinfo.addr == 0xbf4f0000) {
 
 		screen_info = (struct screen_info) {
 			0, 0,		/* orig-x, orig-y */
@@ -202,12 +224,28 @@
 			0,		/* orig_video_isVGA */
 			16		/* orig_video_points */
 		};
-	} else {
-		conswitchp = &dummy_con;
+		conswitchp = &newport_con;
+	}
+#endif
+#ifdef CONFIG_SGI_ARCS_CONSOLE
+	if (!conswitchp && sgi_promgfxinfo.addr) {
+		extern const struct consw sgiarcs_con;
+		screen_info = (struct screen_info) {
+			0, 0,		/* orig-x, orig-y */
+			0,		/* unused */
+			0,		/* orig_video_page */
+			0,		/* orig_video_mode */
+			160,		/* orig_video_cols */
+			0, 0, 0,	/* unused, ega_bx, unused */
+			64,		/* orig_video_lines */
+			0,		/* orig_video_isVGA */
+			16		/* orig_video_points */
+		};
+		conswitchp = &sgiarcs_con;
 	}
-#else
-	conswitchp = &dummy_con;
 #endif
+	if (conswitchp == 0)
+		conswitchp == &dummy_con;
 #endif
 
 	rtc_ops = &indy_rtc_ops;
--- ./drivers/video/Makefile.orig	2002-05-11 18:57:13.000000000 +0200
+++ ./drivers/video/Makefile	2002-05-11 18:57:44.000000000 +0200
@@ -21,6 +21,7 @@
 
 obj-$(CONFIG_DUMMY_CONSOLE)       += dummycon.o
 obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o
+obj-$(CONFIG_SGI_ARCS_CONSOLE)    += sgiarcs_con.o
 obj-$(CONFIG_PROM_CONSOLE)        += promcon.o promcon_tbl.o
 obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticon-bmode.o sticore.o
 obj-$(CONFIG_VGA_CONSOLE)         += vgacon.o
--- ./drivers/video/sgiarcs_con.c.orig	2002-05-11 17:19:51.000000000 +0200
+++ ./drivers/video/sgiarcs_con.c	2002-05-13 23:28:52.000000000 +0200
@@ -0,0 +1,627 @@
+/*
+ * sgiarcs_con.c: generic console for SGI ARCS systems
+ * 
+ * (C) 2002 Michael Schroeder ([email-masked])
+ * 
+ * Advantages:
+ *  - should work on every SGI with ARCS prom
+ * Disadvantages:
+ *  - is slow
+ *  - needs prom memory block
+ *
+ * Some code stolen from fbcon.c and newport_con.c
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/kd.h>
+#include <linux/selection.h>
+#include <linux/console.h>
+#include <linux/console_struct.h>
+#include <linux/vt_kern.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/sgiarcs.h>
+#include <asm/bcache.h>
+
+#ifndef CONFIG_SGI_NEWPORT_CONSOLE
+# define INCLUDE_LINUX_LOGO_DATA
+#endif
+#include <asm/linux_logo.h>
+
+#include <video/font.h>
+
+#define LOGO_W		80
+#define LOGO_H		80
+
+extern struct fbcon_font_desc font_vga_8x16;
+
+#define FONT_DATA ((unsigned char *)font_vga_8x16.data)
+
+/* borrowed from fbcon.c */
+#define REFCOUNT(fd)	(((int *)(fd))[-1])
+#define FNTSIZE(fd)	(((int *)(fd))[-2])
+#define FNTCHARCNT(fd)	(((int *)(fd))[-3])
+#define FONT_EXTRA_WORDS 3
+
+static unsigned char *font_data[MAX_NR_CONSOLES];
+
+static int logo_active;
+
+extern struct sgi_promgfx *sgi_promgfx;
+extern int prom_memory_freed;
+
+extern void sgi_hpc64_restore(void), sgi_hpc64_off(void);
+
+#define SGIGFXVECTOR ((struct sgi_gfxvector *)sgi_promgfx->gfxvector)
+#define SGIARCS_CALL0(func)		ARC_CALL1_VEC(SGIGFXVECTOR, func, sgi_promgfx->gfxaddr)
+#define SGIARCS_CALL1(func,a1)		ARC_CALL2_VEC(SGIGFXVECTOR, func, sgi_promgfx->gfxaddr, a1)
+#define SGIARCS_CALL2(func,a1,a2)	ARC_CALL3_VEC(SGIGFXVECTOR, func, sgi_promgfx->gfxaddr, a1, a2)
+#define SGIARCS_CALL3(func,a1,a2,a3)	ARC_CALL4_VEC(SGIGFXVECTOR, func, sgi_promgfx->gfxaddr, a1, a2, a3)
+#define SGIARCS_CALL4(func,a1,a2,a3,a4)	ARC_CALL5_VEC(SGIGFXVECTOR, func, sgi_promgfx->gfxaddr, a1, a2, a3, a4)
+
+#define SGIARCS_START(flags)		do {save_and_cli(flags);bc_disable();} while (0)
+#define SGIARCS_END(flags)		do {bc_enable();restore_flags(flags);} while (0)
+
+#define CURSOR_DRAW_DELAY               (1)
+#define DEFAULT_CURSOR_BLINK_RATE       (20)
+
+static int cursor_drawn;
+static int vbl_cursor_cnt;
+static int cursor_on;
+static int cursor_blink_rate;
+static int cursor_x;
+static int cursor_y;
+
+static inline void cursor_undrawn(void)
+{
+    vbl_cursor_cnt = 0;
+    cursor_drawn = 0;
+}
+
+static void cursor_timer_handler(unsigned long dev_addr);
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
+
+static struct timer_list cursor_timer = {
+    function: cursor_timer_handler
+};
+
+static void cursor_timer_handler(unsigned long dev_addr)
+{
+      fbcon_vbl_handler(0, NULL, NULL);
+      cursor_timer.expires = jiffies+HZ/50;
+      add_timer(&cursor_timer);
+}
+
+static inline void sgiarcs_init_cmap(void)
+{
+	unsigned short i;
+	unsigned long flags;
+
+	SGIARCS_START(flags);
+	for (i = 0; i < 16; i++) {
+		SGIARCS_CALL4(SetCmap, color_table[i], default_red[i], default_grn[i], default_blu[i]);
+	}
+	SGIARCS_END(flags);
+}
+
+static inline void sgiarcs_show_logo(void)
+{
+	unsigned long i, x, y;
+	unsigned long flags;
+
+	SGIARCS_START(flags);
+	for (i = 0; i < LINUX_LOGO_COLORS; i++)
+		 SGIARCS_CALL4(SetCmap, i + 0x20, linux_logo_red[i], linux_logo_green[i], linux_logo_blue[i]);
+	for (y = 0; y < LOGO_H; y++)
+		for (x = 0; x < LOGO_W; x++) {
+			SGIARCS_CALL1(SetColor, linux_logo[y * LOGO_W + x]);
+			SGIARCS_CALL2(DrawPixel, sgi_promgfx->width - LOGO_W + x, sgi_promgfx->height - 1 - y);
+		}
+	SGIARCS_END(flags);
+}
+
+static inline void sgiarcs_clear_screen(int xstart, int ystart, int xend,
+					int yend, int ci)
+{
+	unsigned long flags;
+	if (logo_active)
+		return;
+	SGIARCS_START(flags);
+	SGIARCS_CALL1(SetColor, ci);
+	SGIARCS_CALL4(DrawBlock, xstart, sgi_promgfx->height - 1 - ystart, xend, sgi_promgfx->height - 1 - yend);
+	SGIARCS_END(flags);
+}
+
+#ifdef MODULE
+static const char *sgiarcs_startup(void)
+#else
+static const char *__init sgiarcs_startup(void)
+#endif
+{
+	int i;
+	unsigned long flags;
+
+	if (sgi_promgfx == 0 || prom_memory_freed > 0)
+		return NULL;
+	prom_memory_freed = -1;
+	for (i = 0; i < MAX_NR_CONSOLES; i++)
+		font_data[i] = FONT_DATA;
+	sgiarcs_init_cmap();
+	sgiarcs_clear_screen(0, 0, sgi_promgfx->width - 1, sgi_promgfx->height - 1, 0);
+	SGIARCS_START(flags);
+	/* move mouse offscreen */
+	SGIARCS_CALL2(SetCursor, sgi_promgfx->width + 10, sgi_promgfx->height + 10);
+	SGIARCS_END(flags);
+	
+	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+	cursor_timer.expires = jiffies+HZ/50;
+	add_timer(&cursor_timer);
+
+	printk("SGIARCS colole driver, %dx%d at 0x%x\n", sgi_promgfx->width, sgi_promgfx->height, sgi_promgfx->gfxaddr);
+	return "SGI_ARCS";
+}
+
+static void sgiarcs_init(struct vc_data *vc, int init)
+{
+	vc->vc_cols = sgi_promgfx->width / 8;
+	vc->vc_rows = sgi_promgfx->height / 16;
+	vc->vc_can_do_color = 1;
+}
+
+static void sgiarcs_clear(struct vc_data *vc, int sy, int sx, int height,
+			  int width)
+{
+	int redraw_cursor = 0;
+	if (logo_active)
+		return;
+	if ((sy <= cursor_y) && (cursor_y < sy+height) &&
+	    (sx <= cursor_x) && (cursor_x < sx+width)) {
+		cursor_undrawn();
+		redraw_cursor = 1;
+	}
+	sgiarcs_clear_screen(sx << 3, sy << 4, ((sx + width) << 3) - 1, ((sy + height) << 4) - 1, (vc->vc_color & 0xf0) >> 4);
+	if (redraw_cursor)
+		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+}
+
+static void sgiarcs_putc_raw(struct vc_data *vc, int charattr, int ypos,
+			 int xpos)
+{
+	struct sgi_gfxbitmap bm;
+	unsigned char *p;
+	USHORT m[16];
+	int i;
+	unsigned long flags;
+
+	p = &font_data[vc->vc_num][(charattr & 0xff) << 4];
+	charattr = (charattr >> 8) & 0xff;
+	xpos <<= 3;
+	ypos <<= 4;
+
+	if (!logo_active)
+		sgiarcs_clear_screen(xpos, ypos, xpos + 7, ypos + 15, (charattr & 0xf0) >> 4);
+	for (i = 0; i < 16; i++)
+		m[15 - i] = p[i] << 8;
+	bm.bitmap = m;
+	bm.width = 8;
+	bm.height = 16;
+	bm.offx = bm.offy = bm.movex = bm.movey = 0;
+	bm.shortsperline = 1;
+	SGIARCS_START(flags);
+	SGIARCS_CALL1(SetColor, charattr & 0x0f);
+	SGIARCS_CALL2(SetPos, xpos, sgi_promgfx->height - 16 - ypos);
+	SGIARCS_CALL1(DrawBitmap, &bm);
+	SGIARCS_END(flags);
+}
+
+static void sgiarcs_putc(struct vc_data *vc, int charattr, int ypos,
+			 int xpos)
+{
+	int redraw_cursor = 0;
+	if ((cursor_x == xpos) && (cursor_y == ypos)) {
+		cursor_undrawn();
+		redraw_cursor = 1;
+	}
+	sgiarcs_putc_raw(vc, charattr, ypos, xpos);
+	if (redraw_cursor)
+		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+}
+
+static void sgiarcs_putcs(struct vc_data *vc, const unsigned short *s,
+			  int count, int ypos, int xpos)
+{
+	int redraw_cursor = 0;
+	int charattr;
+	struct sgi_gfxbitmap bm;
+	unsigned char *p;
+	int line, bite, i, j;
+	USHORT m[16 * 16];	/* 32 chars */
+	unsigned long flags;
+
+	if ((cursor_y == ypos) && (xpos <= cursor_x) &&
+	    (cursor_x < (xpos + count))) {
+		cursor_undrawn();
+		redraw_cursor = 1;
+	}
+
+	xpos <<= 3;
+	ypos <<= 4;
+	charattr = (scr_readw(s) >> 8) & 0xff;
+	if (!logo_active)
+		sgiarcs_clear_screen(xpos, ypos, xpos + count * 8 - 1, ypos + 15, (charattr & 0xf0) >> 4);
+	while (count > 0) {
+		bite = count > 32 ? 32 : count;
+		line = (bite + 1) & ~1;
+		for (i = 0; i < bite; i++) {
+			p = &font_data[vc->vc_num][(scr_readw(s++) & 0xff) << 4];
+			for (j = 15 * line; j >= 0; j -= line)
+				*((unsigned char *)m + j + i) = *p++;
+		}
+		bm.bitmap = m;
+		bm.width = bite * 8;
+		bm.height = 16;
+		bm.offx = bm.offy = bm.movex = bm.movey = 0;
+		bm.shortsperline = line / 2;
+		SGIARCS_START(flags);
+		SGIARCS_CALL1(SetColor, charattr & 0x0f);
+		SGIARCS_CALL2(SetPos, xpos, sgi_promgfx->height - 16 - ypos);
+		SGIARCS_CALL1(DrawBitmap, &bm);
+		SGIARCS_END(flags);
+		xpos += bite * 8;
+		count -= bite;
+	}
+	if (redraw_cursor)
+	    vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+}
+
+static void sgiarcs_cursor_revc(struct vc_data *vc, int x, int y)
+{
+	unsigned short c = *(unsigned short *)(vc->vc_origin + vc->vc_size_row * y + x * 2);
+	if (!cursor_drawn)
+		c ^= 0xff00;
+	sgiarcs_putc_raw(vc, c, y, x);
+}
+
+static void sgiarcs_cursor(struct vc_data *vc, int mode)
+{
+	if (cursor_x == vc->vc_x && cursor_y == vc->vc_y &&
+	    (mode == CM_ERASE) == !cursor_on)
+		return;
+	cursor_on = 0;
+	if (cursor_drawn)
+	    sgiarcs_cursor_revc(vc, cursor_x, cursor_y);
+	cursor_x = vc->vc_x;
+	cursor_y = vc->vc_y;
+	switch (mode) {
+	case CM_ERASE:
+		cursor_drawn = 0;
+		break;
+	case CM_MOVE:
+	case CM_DRAW:
+		if (cursor_drawn)
+		    sgiarcs_cursor_revc(vc, cursor_x, cursor_y);
+		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+		cursor_on = 1;
+		break;
+	}
+}
+
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
+{
+    if (!cursor_on)
+	return;
+    if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
+	sgiarcs_cursor_revc(vc_cons[fg_console].d, cursor_x, cursor_y);
+	cursor_drawn ^= 1;
+	vbl_cursor_cnt = cursor_blink_rate;
+    }
+}
+
+
+static int sgiarcs_switch(struct vc_data *vc)
+{
+	static int logo_drawn = 0;
+
+	if (!logo_drawn) {
+		sgiarcs_show_logo();
+		logo_drawn = 1;
+		logo_active = 1;
+	}
+	return 1;
+}
+
+static int sgiarcs_blank(struct vc_data *c, int blank)
+{
+	unsigned long flags;
+
+	SGIARCS_START(flags);
+	SGIARCS_CALL1(BlankScreen, blank);
+	SGIARCS_END(flags);
+	return 1;
+}
+
+static int sgiarcs_set_font(int unit, struct console_font_op *op)
+{
+	int w = op->width;
+	int h = op->height;
+	int size = h * op->charcount;
+	int i;
+	unsigned char *new_data, *data = op->data, *p;
+
+	/* ladis: when I grow up, there will be a day... and more sizes will
+	 * be supported ;-) */
+	if ((w != 8) || (h != 16)
+	    || (op->charcount != 256 && op->charcount != 512))
+		return -EINVAL;
+
+	if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size,
+	     GFP_USER))) return -ENOMEM;
+
+	new_data += FONT_EXTRA_WORDS * sizeof(int);
+	FNTSIZE(new_data) = size;
+	FNTCHARCNT(new_data) = op->charcount;
+	REFCOUNT(new_data) = 0;	/* usage counter */
+
+	p = new_data;
+	for (i = 0; i < op->charcount; i++) {
+		memcpy(p, data, h);
+		data += 32;
+		p += h;
+	}
+
+	/* check if font is already used by other console */
+	for (i = 0; i < MAX_NR_CONSOLES; i++) {
+		if (font_data[i] != FONT_DATA
+		    && FNTSIZE(font_data[i]) == size
+		    && !memcmp(font_data[i], new_data, size)) {
+			kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
+			/* current font is the same as the new one */
+			if (i == unit)
+				return 0;
+			new_data = font_data[i];
+			break;
+		}
+	}
+	/* old font is user font */
+	if (font_data[unit] != FONT_DATA) {
+		if (--REFCOUNT(font_data[unit]) == 0)
+			kfree(font_data[unit] -
+			      FONT_EXTRA_WORDS * sizeof(int));
+	}
+	REFCOUNT(new_data)++;
+	font_data[unit] = new_data;
+
+	return 0;
+}
+
+static int sgiarcs_set_def_font(int unit, struct console_font_op *op)
+{
+	if (font_data[unit] != FONT_DATA) {
+		if (--REFCOUNT(font_data[unit]) == 0)
+			kfree(font_data[unit] -
+			      FONT_EXTRA_WORDS * sizeof(int));
+		font_data[unit] = FONT_DATA;
+	}
+
+	return 0;
+}
+
+static int sgiarcs_font_op(struct vc_data *vc, struct console_font_op *op)
+{
+	int unit = vc->vc_num;
+
+	switch (op->op) {
+	case KD_FONT_OP_SET:
+		return sgiarcs_set_font(unit, op);
+	case KD_FONT_OP_SET_DEFAULT:
+		return sgiarcs_set_def_font(unit, op);
+	default:
+		return -ENOSYS;
+	}
+}
+
+static int sgiarcs_set_palette(struct vc_data *vc, unsigned char *table)
+{
+	return -EINVAL;
+}
+
+static int sgiarcs_scrolldelta(struct vc_data *vc, int lines)
+{
+	/* there is (nearly) no off-screen memory, so we can't scroll back */
+	return 0;
+}
+
+static int sgiarcs_scroll(struct vc_data *vc, int t, int b, int dir,
+			  int lines)
+{
+	int count, x, y;
+	unsigned short *s, *d;
+	unsigned short chattr;
+
+	sgiarcs_cursor(vc, CM_ERASE);
+
+	if (logo_active)	{
+		logo_active = 0;
+		sgiarcs_clear_screen(sgi_promgfx->width - LOGO_W, 0, sgi_promgfx->width - 1, LOGO_H - 1, 0);
+	}
+	count = (b - t - lines) * vc->vc_cols;
+	if (dir == SM_UP) {
+		x = 0;
+		y = t;
+		s = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * (t + lines));
+		d = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * t);
+		while (count--) {
+			chattr = scr_readw(s++);
+			if (chattr != scr_readw(d)) {
+				sgiarcs_putc(vc, chattr, y, x);
+				scr_writew(chattr, d);
+			}
+			d++;
+			if (++x == vc->vc_cols) {
+				x = 0;
+				y++;
+			}
+		}
+		d = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * (b - lines));
+		x = 0;
+		y = b - lines;
+		for (count = 0; count < (lines * vc->vc_cols); count++) {
+			if (scr_readw(d) != vc->vc_video_erase_char) {
+				sgiarcs_putc(vc, vc->vc_video_erase_char,
+					     y, x);
+				scr_writew(vc->vc_video_erase_char, d);
+			}
+			d++;
+			if (++x == vc->vc_cols) {
+				x = 0;
+				y++;
+			}
+		}
+	} else {
+		x = vc->vc_cols - 1;
+		y = b - 1;
+		s = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * (b - lines) - 2);
+		d = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * b - 2);
+		while (count--) {
+			chattr = scr_readw(s--);
+			if (chattr != scr_readw(d)) {
+				sgiarcs_putc(vc, chattr, y, x);
+				scr_writew(chattr, d);
+			}
+			d--;
+			if (x-- == 0) {
+				x = vc->vc_cols - 1;
+				y--;
+			}
+		}
+		d = (unsigned short *) (vc->vc_origin +
+					vc->vc_size_row * t);
+		x = 0;
+		y = t;
+		for (count = 0; count < (lines * vc->vc_cols); count++) {
+			if (scr_readw(d) != vc->vc_video_erase_char) {
+				sgiarcs_putc(vc, vc->vc_video_erase_char,
+					     y, x);
+				scr_writew(vc->vc_video_erase_char, d);
+			}
+			d++;
+			if (++x == vc->vc_cols) {
+				x = 0;
+				y++;
+			}
+		}
+	}
+	return 1;
+}
+
+static void sgiarcs_bmove(struct vc_data *vc, int sy, int sx, int dy,
+			  int dx, int h, int w)
+{
+	if (sy != dy)
+		panic("sgiarcs_bmove width sy != dy");
+
+	if (((sy <= cursor_y) && (cursor_y < sy+h) &&
+	      (sx <= cursor_x) && (cursor_x < sx+w)) ||
+	     ((dy <= cursor_y) && (cursor_y < dy+h) &&
+	      (dx <= cursor_x) && (cursor_x < dx+w)))
+		sgiarcs_cursor(vc, CM_ERASE);
+
+	while (h-- > 0) {
+		unsigned short *d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * dy + dx * 2);
+		unsigned short *s = d + (dx - sx);
+		unsigned short *start = d;
+		unsigned short *ls = d;
+		unsigned short *le = d + w;
+		unsigned short c;
+		int x = dx;
+		unsigned short attr = 1;
+
+		do {
+			c = scr_readw(d);
+			if (attr != (c & 0xff00)) {
+			    attr = c & 0xff00; 
+			    if (d > start) { 
+				sgiarcs_putcs(vc, start, d - start, dy, x);
+				x += d - start;
+				start = d;
+			    }
+			}
+			if (s >= ls && s < le && c == scr_readw(s)) {
+			    if (d > start) {
+				sgiarcs_putcs(vc, start, d - start, dy, x);
+				x += d - start + 1;
+				start = d + 1;
+			    } else {
+				x++;
+				start++;
+			    }
+			}
+			s++;
+			d++;
+		} while (d < le);
+		if (d > start)
+		    sgiarcs_putcs(vc, start, d - start, dy, x);
+		sy++;
+		dy++;
+	}
+}
+
+static int sgiarcs_dummy(struct vc_data *c)
+{
+	return 0;
+}
+
+#define DUMMY (void *) sgiarcs_dummy
+
+const struct consw sgiarcs_con = {
+    con_startup:	sgiarcs_startup,
+    con_init:		sgiarcs_init,
+    con_deinit:		DUMMY,
+    con_clear:		sgiarcs_clear,
+    con_putc:		sgiarcs_putc,
+    con_putcs:		sgiarcs_putcs,
+    con_cursor:		sgiarcs_cursor,
+    con_scroll:		sgiarcs_scroll,
+    con_bmove:		sgiarcs_bmove,
+    con_switch:		sgiarcs_switch,
+    con_blank:		sgiarcs_blank,
+    con_font_op:	sgiarcs_font_op,
+    con_set_palette:	sgiarcs_set_palette,
+    con_scrolldelta:	sgiarcs_scrolldelta,
+    con_set_origin:	DUMMY,
+    con_save_screen:	DUMMY
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+	if (!sgiarcs_startup())
+		printk("Error loading SGIARCS Console driver\n");
+	else
+		printk("Loading SGIARCS Console Driver\n");
+	take_over_console(&sgiarcs_con, 0, MAX_NR_CONSOLES - 1, 1);
+
+	return 0;
+}
+
+int cleanup_module(void)
+{
+	int i;
+
+	printk("Unloading SGIARCS Console Driver\n");
+	/* free memory used by user font */
+	for (i = 0; i < MAX_NR_CONSOLES; i++)
+		sgiarcs_set_def_font(i, NULL);
+
+	return 0;
+}
+#endif
--- ./include/asm-mips/sgi/sgimc.h.orig	2002-05-13 23:09:29.000000000 +0200
+++ ./include/asm-mips/sgi/sgimc.h	2002-05-13 23:09:43.000000000 +0200
@@ -224,5 +224,6 @@
 #define SGIMC_SEG1_SIZE_IP26_IP28   0x20000000 /* 512MB */
 
 extern void sgimc_init(void);
+extern void sgimc_exp064(void);
 
 #endif /* _ASM_SGI_SGIMC_H */
--- ./include/asm-mips/sgiarcs.h.orig	2002-05-11 16:31:51.000000000 +0200
+++ ./include/asm-mips/sgiarcs.h	2002-05-11 20:29:08.000000000 +0200
@@ -360,6 +360,58 @@
 	int             smax;              /* Max # of symbols. */
 };
 
+struct sgi_pvector {
+	LONG		ioctl;
+	LONG		get_nvram_tab;
+	LONG		load_abs;
+	LONG		invoke_abs;
+	LONG		exec_abs;
+	LONG		fs_register;
+	LONG		fs_unregister;
+	LONG		signal;
+	LONG		get_promgfx;
+};
+
+struct sgi_promgfx {
+	LONG		gfxvector;
+	LONG		gfxaddr;
+	LONG		unknown1[8];
+	LONG		width;
+	LONG		height;
+	LONG		unknown2[10];
+	LONG		state;
+};
+
+struct sgi_promgfxinfo {
+	int		addr;
+	int		width;
+	int		height;
+};
+
+struct sgi_gfxvector {
+	LONG		BlankScreen;
+	LONG		SetColor;
+	LONG		SetCmap;
+	LONG		DrawBlock;
+	LONG		DrawPixel;
+	LONG		SetPos;
+	LONG		DrawBitmap;
+	LONG		MoveBlock;
+	LONG		InitScreen;
+	LONG		SetCursor;
+};
+
+struct sgi_gfxbitmap {
+	USHORT *	bitmap;
+	USHORT		width;
+	USHORT		height;
+	SHORT		offx;
+	SHORT		offy;
+	SHORT		movex;
+	SHORT		movey;
+	USHORT		shortsperline;
+};
+
 /*
  * Macros for calling a 32-bit ARC implementation from 64-bit code
  */
@@ -370,9 +422,9 @@
 	"$2","$3","$4","$5","$6","$7","$8","$9","$10","$11",		\
 	"$12","$13","$14","$15","$16","$24","25","$31"
 
-#define ARC_CALL0(dest)							\
+#define ARC_CALL0_VEC(vec,dest)							\
 ({	long __res;							\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"jalr\t%1\n\t"							\
@@ -384,10 +436,10 @@
 	(unsigned long) __res;						\
 })
 
-#define ARC_CALL1(dest,a1)						\
+#define ARC_CALL1_VEC(vec,dest,a1)						\
 ({	long __res;							\
 	register signed int __a1 __asm__("$4") = (int) (long) (a1);	\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"jalr\t%1\n\t"							\
@@ -399,11 +451,11 @@
 	(unsigned long) __res;						\
 })
 
-#define ARC_CALL2(dest,a1,a2)						\
+#define ARC_CALL2_VEC(vec,dest,a1,a2)						\
 ({	long __res;							\
 	register signed int __a1 __asm__("$4") = (int) (long) (a1);	\
 	register signed int __a2 __asm__("$5") = (int) (long) (a2);	\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"jalr\t%1\n\t"							\
@@ -415,12 +467,12 @@
 	__res;								\
 })
 
-#define ARC_CALL3(dest,a1,a2,a3)					\
+#define ARC_CALL3_VEC(vec,dest,a1,a2,a3)					\
 ({	long __res;							\
 	register signed int __a1 __asm__("$4") = (int) (long) (a1);	\
 	register signed int __a2 __asm__("$5") = (int) (long) (a2);	\
 	register signed int __a3 __asm__("$6") = (int) (long) (a3);	\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"jalr\t%1\n\t"							\
@@ -432,13 +484,13 @@
 	__res;								\
 })
 
-#define ARC_CALL4(dest,a1,a2,a3,a4)					\
+#define ARC_CALL4_VEC(vec,dest,a1,a2,a3,a4)					\
 ({	long __res;							\
 	register signed int __a1 __asm__("$4") = (int) (long) (a1);	\
 	register signed int __a2 __asm__("$5") = (int) (long) (a2);	\
 	register signed int __a3 __asm__("$6") = (int) (long) (a3);	\
 	register signed int __a4 __asm__("$7") = (int) (long) (a4);	\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"jalr\t%1\n\t"							\
@@ -451,14 +503,14 @@
 	__res;								\
 })
 
-#define ARC_CALL5(dest,a1,a2,a3,a4,a5)					\
+#define ARC_CALL5_VEC(vec,dest,a1,a2,a3,a4,a5)					\
 ({	long __res;							\
 	register signed int __a1 __asm__("$4") = (int) (long) (a1);	\
 	register signed int __a2 __asm__("$5") = (int) (long) (a2);	\
 	register signed int __a3 __asm__("$6") = (int) (long) (a3);	\
 	register signed int __a4 __asm__("$7") = (int) (long) (a4);	\
 	register signed int __a5 = (a5);				\
-	long __vec = (long) romvec->dest;				\
+	long __vec = (long) (vec)->dest;				\
 	__asm__ __volatile__(						\
 	"dsubu\t$29, 32\n\t"						\
 	"sw\t%6, 16($29)\n\t"						\
@@ -478,57 +530,57 @@
 #if (defined(CONFIG_MIPS32) && defined(CONFIG_ARC32)) ||		\
     (defined(CONFIG_MIPS64) && defined(CONFIG_ARC32))
 
-#define ARC_CALL0(dest)							\
+#define ARC_CALL0_VEC(vec,dest)							\
 ({	long __res;							\
-	long (*__vec)(void) = (void *) romvec->dest;			\
+	long (*__vec)(void) = (void *) (vec)->dest;			\
 									\
 	__res = __vec();						\
 	__res;								\
 })
 
-#define ARC_CALL1(dest,a1)						\
+#define ARC_CALL1_VEC(vec,dest,a1)						\
 ({	long __res;							\
 	long __a1 = (long) (a1);					\
-	long (*__vec)(long) = (void *) romvec->dest;			\
+	long (*__vec)(long) = (void *) (vec)->dest;			\
 									\
 	__res = __vec(__a1);						\
 	__res;								\
 })
 
-#define ARC_CALL2(dest,a1,a2)						\
+#define ARC_CALL2_VEC(vec,dest,a1,a2)						\
 ({	long __res;							\
 	long __a1 = (long) (a1);					\
 	long __a2 = (long) (a2);					\
-	long (*__vec)(long, long) = (void *) romvec->dest;		\
+	long (*__vec)(long, long) = (void *) (vec)->dest;		\
 									\
 	__res = __vec(__a1, __a2);					\
 	__res;								\
 })
 
-#define ARC_CALL3(dest,a1,a2,a3)					\
+#define ARC_CALL3_VEC(vec,dest,a1,a2,a3)					\
 ({	long __res;							\
 	long __a1 = (long) (a1);					\
 	long __a2 = (long) (a2);					\
 	long __a3 = (long) (a3);					\
-	long (*__vec)(long, long, long)	= (void *) romvec->dest;	\
+	long (*__vec)(long, long, long)	= (void *) (vec)->dest;	\
 									\
 	__res = __vec(__a1, __a2, __a3);				\
 	__res;								\
 })
 
-#define ARC_CALL4(dest,a1,a2,a3,a4)					\
+#define ARC_CALL4_VEC(vec,dest,a1,a2,a3,a4)					\
 ({	long __res;							\
 	long __a1 = (long) (a1);					\
 	long __a2 = (long) (a2);					\
 	long __a3 = (long) (a3);					\
 	long __a4 = (long) (a4);					\
-	long (*__vec)(long, long, long, long) = (void *) romvec->dest;	\
+	long (*__vec)(long, long, long, long) = (void *) (vec)->dest;	\
 									\
 	__res = __vec(__a1, __a2, __a3, __a4);				\
 	__res;								\
 })
 
-#define ARC_CALL5(dest,a1,a2,a3,a4,a5)					\
+#define ARC_CALL5_VEC(vec,dest,a1,a2,a3,a4,a5)					\
 ({	long __res;							\
 	long __a1 = (long) (a1);					\
 	long __a2 = (long) (a2);					\
@@ -536,11 +588,18 @@
 	long __a4 = (long) (a4);					\
 	long __a5 = (long) (a5);					\
 	long (*__vec)(long, long, long, long, long);			\
-	__vec = (void *) romvec->dest;					\
+	__vec = (void *) (vec)->dest;					\
 									\
 	__res = __vec(__a1, __a2, __a3, __a4, __a5);			\
 	__res;								\
 })
 #endif /* both kernel and ARC either 32-bit or 64-bit */
 
+#define ARC_CALL0(dest) ARC_CALL0_VEC(romvec,dest)
+#define ARC_CALL1(dest,a1) ARC_CALL1_VEC(romvec,dest,a1)
+#define ARC_CALL2(dest,a1,a2) ARC_CALL2_VEC(romvec,dest,a1,a2)
+#define ARC_CALL3(dest,a1,a2,a3) ARC_CALL3_VEC(romvec,dest,a1,a2,a3)
+#define ARC_CALL4(dest,a1,a2,a3,a4) ARC_CALL4_VEC(romvec,dest,a1,a2,a3,a4)
+#define ARC_CALL5(dest,a1,a2,a3,a4,a5) ARC_CALL5_VEC(romvec,dest,a1,a2,a3,a4,a5)
+
 #endif /* _ASM_SGIARCS_H */