Date: Thu, 3 Apr 1997 09:44:01 +0200 (MET DST)
From: Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>
To: Linux/m68k <linux-m68k@phil.uni-sb.de>
Subject: L68K: Support for multiple frame buffer devices
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: linux-m68k@phil.uni-sb.de


These patches add basic support for multiple concurrent frame buffer devices.
Patches are relative to Linux/m68k 2.1.29-yesterday.

Changes:

  - fbcon_startup probes for all possible frame buffer devices. Each driver is
    assumed to initialize and register all found devices (e.g. for multiple
    gfx boards of the same type or with the same chipset).

  - New frame buffer device: vfb (CONFIG_FB_VIRTUAL). This is a virtual frame
    buffer device that operates on a piece of memory of 1 MB and thus it is
    _not_ visible on your monitor. Almost all video modes are possible :-)
    The following display depths are supported:

      - 1 bpp monochrome
      - 8 bpp chunky pseudocolor
      - 16 bpp chunky truecolor

    24 and 32 bpp chunky truecolor aren't supported yet because fbcon.c can't
    handle it (yet).

    Vfb is intended to play with multiple frame buffers. AGA users could run a
    truecolor X server on this device and convert the image data to HAM8 on the
    real frame buffer device at regular times :-)

    Hmmm, I'm just thinking of something: if you could supply the address of
    the memory to be used (video RAM on a real video board?) instead of
    allocating it, this would be very similar to the Atari external frame
    buffer.
 
  - By default all consoles appear on the first frame buffer device found. This
    mapping can be changed using ioctls (on an arbitrary frame buffer device
    node). The sample program con2fbmap.c is provided for your convenience.
    Usage:

      o display mapping for console <con>:

	  con2fbmap <con>

      o map console <con> onto frame buffer <fb>:

          con2fbmap <con> <fb>

    Consoles are numbered 1-63 (0 means `all consoles' and can be used to
    map all consoles to the same frame buffer). Frame buffer devices are
    numbered 0-7.

  - All different arrays of the form

        struct display disp[MAX_NR_CONSOLES]

    in each frame buffer device are replaced by one array fb_display in
    fbcon.c, which is used by all frame buffer devices. register_framebuffer()
    now takes a structure instead of separate variables.

  - video_setup() doesn't `propagate' to cyberfb/retz3fb/clgen anymore.

  - I also updated atafb, cyberfb, retz3fb. These do compile but are untested.
    I tried to update clgen but I couldn't find some importants things (e.g.
    fb_info and disp). Franky?

  - I changed the ioctl numbers for the (currently unused) cursor operations,
    since Franky recycled them for monitor settings.


Caveats/Questions/TODO:

  - It was tested with amifb and vfb only.

  - Fix broken frame buffer devices (at least clgen).

  - What should we do with video_setup()????

  - Loadable frame buffer devices (yes!)

  - Dynamic console mapping. Changing the console-to-fb mapping for a console
    that's already in use doesn't work yet. Thus you must set the mapping
    before you use the console for the first time.

  - GSP-based boards (e.g. DMI Resolver) don't fit in the frame buffer concept.

  - Have some discussions on linux-m68k@phil.uni-sb.de

Have fun!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fb.h>


const char *fbpath = "/dev/fb0current";		/* any frame buffer will do */

const char *programname;

void Usage(void)
{
    fprintf(stderr, "\nUsage: %s console [framebuffer]\n\n", programname);
    exit(1);
}

int main(int argc, char *argv[])
{
    int do_write = 0;
    int fd;
    struct fb_con2fbmap map;

    programname = argv[0];
    switch (argc) {
	case 3:
	    do_write = 1;
	    map.framebuffer = atoi(argv[2]);
	case 2:
	    map.console = atoi(argv[1]);
	    break;
	default:
	    Usage();
    }
    if ((fd = open(fbpath, O_RDONLY)) == -1) {
	fprintf(stderr, "open %s: %s\n", fbpath, strerror(errno));
	exit(1);
    }
    if (do_write) {
	if (ioctl(fd, FBIOPUT_CON2FBMAP, &map)) {
	    fprintf(stderr, "ioctl FBIOPUT_CON2FBMAP: %s\n", strerror(errno));
	    exit(1);
	}
    } else {
	if (ioctl(fd, FBIOGET_CON2FBMAP, &map)) {
	    fprintf(stderr, "ioctl FBIOGET_CON2FBMAP: %s\n", strerror(errno));
	    exit(1);
	}
	printf("console %d is mapped to framebuffer %d\n", map.console,
	       map.framebuffer);
    }
    close(fd);
    exit(0);
}

--- linux-2.1.29/arch/m68k/amiga/amifb.c.orig	Wed Mar 19 22:07:22 1997
+++ linux-2.1.29/arch/m68k/amiga/amifb.c	Wed Apr  2 23:05:23 1997
@@ -55,13 +55,11 @@
 #include <asm/irq.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
+#include <asm/setup.h>
 #include <linux/fb.h>
 
 #define DEBUG
 
-#define FB_MODES_SHIFT    5 /* 32 modes per framebuffer */
-#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
-
 #if !defined(CONFIG_AMIFB_OCS) && !defined(CONFIG_AMIFB_ECS) && !defined(CONFIG_AMIFB_AGA)
 #define CONFIG_AMIFB_OCS   /* define at least one fb driver, this will change later */
 #endif
@@ -733,10 +731,9 @@
 
 static int currcon = 0;
 
-static struct display disp[MAX_NR_CONSOLES];
+static struct display disp;
 static struct fb_info fb_info;
 
-static int node;		/* node of the /dev/fb?current file */
 
 	/*
 	 * The minimum period for audio depends on htotal (for OCS/ECS/AGA)
@@ -1243,7 +1240,7 @@
 	 * Interface to the low level console driver
 	 */
 
-struct fb_info *amiga_fb_init(long *mem_start);
+int amiga_fb_init(u_long *mem_start);
 static int amifbcon_switch(int con);
 static int amifbcon_updatevar(int con);
 static void amifbcon_blank(int blank);
@@ -1311,37 +1308,6 @@
 
 extern unsigned short ami_intena_vals[];
 
-	/*
-	 * Support for Graphics Boards
-	 */
-
-#ifdef CONFIG_FB_CYBER			/* Cybervision */
-extern int Cyber_probe(void);
-extern void Cyber_video_setup(char *options, int *ints);
-extern struct fb_info *Cyber_fb_init(long *mem_start);
-
-static int amifb_Cyber = 0;
-#endif
-
-#ifdef CONFIG_FB_RETINAZ3			/* RetinaZ3 */
-extern int retz3_probe(void);
-extern void retz3_video_setup(char *options, int *ints);
-extern struct fb_info *retz3_fb_init(long *mem_start);
-
-static int amifb_retz3 = 0;
-#endif
-
-#ifdef CONFIG_FB_CLGEN			/* Cirrus Logic boards */
-extern void clgen_fb_init(long *mem_start);
-#endif
-
-#ifdef CONFIG_GSP_RESOLVER			/* DMI Resolver */
-extern int resolver_probe(void);
-extern void resolver_video_setup(char *options, int *ints);
-extern struct fb_info *resolver_fb_init(long *mem_start);
-
-static int amifb_resolver = 0;
-#endif
 
 static struct fb_ops amiga_fb_ops = {
 	amiga_fb_get_fix, amiga_fb_get_var, amiga_fb_set_var, amiga_fb_get_cmap,
@@ -1355,35 +1321,6 @@
 	int i;
 	char mcap_spec[80];
 
-	/*
-	 * Check for a Graphics Board
-	 */
-
-#ifdef CONFIG_FB_CYBER
-	if (options && *options)
-		if (!strncmp(options, "cyber", 5) && Cyber_probe()) {
-			amifb_Cyber = 1;
-			Cyber_video_setup(options, ints);
-			return;
-		}
-#endif
-#ifdef CONFIG_FB_RETINAZ3
-	if (options && *options)
-		if (!strncmp(options, "retz3", 5) && retz3_probe()) {
-			amifb_retz3 = 1;
-			retz3_video_setup(options, ints);
-			return;
-		}
-#endif
-#ifdef CONFIG_GSP_RESOLVER
-	if (options && *options)
-		if (!strncmp(options, "resolver", 5) && resolver_probe()) {
-			amifb_resolver = 1;
-			resolver_video_setup(options, ints);
-			return;
-		}
-#endif
-
 	mcap_spec[0] = '\0';
 	fb_info.fontname[0] = '\0';
 
@@ -1540,7 +1477,7 @@
 	else {
 		int err;
 
-		if ((err = ami_decode_var(&disp[con].var, &par)))
+		if ((err = ami_decode_var(&fb_display[con].var, &par)))
 			return err;
 	}
 	return ami_encode_fix(fix, &par);
@@ -1560,7 +1497,7 @@
 		ami_get_par(&par);
 		err = ami_encode_var(var, &par);
 	} else
-		*var = disp[con].var;
+		*var = fb_display[con].var;
 	return err;
 }
 
@@ -1574,6 +1511,11 @@
 	int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
 	struct amiga_fb_par par;
 
+	struct display *display;
+	if (con >= 0)
+		display = &fb_display[con];
+	else
+		display = &disp;	/* used during initialization */
 
 	/*
 	 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
@@ -1582,44 +1524,44 @@
 
 	if (var->vmode & FB_VMODE_CONUPDATE) {
 		var->vmode |= FB_VMODE_YWRAP;
-		var->xoffset = disp[con].var.xoffset;
-		var->yoffset = disp[con].var.yoffset;
+		var->xoffset = display->var.xoffset;
+		var->yoffset = display->var.yoffset;
 	}
 	if ((err = ami_decode_var(var, &par)))
 		return err;
 	ami_encode_var(var, &par);
 	if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-		oldxres = disp[con].var.xres;
-		oldyres = disp[con].var.yres;
-		oldvxres = disp[con].var.xres_virtual;
-		oldvyres = disp[con].var.yres_virtual;
-		oldbpp = disp[con].var.bits_per_pixel;
-		disp[con].var = *var;
+		oldxres = display->var.xres;
+		oldyres = display->var.yres;
+		oldvxres = display->var.xres_virtual;
+		oldvyres = display->var.yres_virtual;
+		oldbpp = display->var.bits_per_pixel;
+		display->var = *var;
 		if (oldxres != var->xres || oldyres != var->yres ||
 		    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
 		    oldbpp != var->bits_per_pixel) {
 			struct fb_fix_screeninfo fix;
 
 			ami_encode_fix(&fix, &par);
-			disp[con].screen_base = (u_char *)fix.smem_start;
-			disp[con].visual = fix.visual;
-			disp[con].type = fix.type;
-			disp[con].type_aux = fix.type_aux;
-			disp[con].ypanstep = fix.ypanstep;
-			disp[con].ywrapstep = fix.ywrapstep;
-			disp[con].line_length = fix.line_length;
-			disp[con].can_soft_blank = 1;
-			disp[con].inverse = amifb_inverse;
+			display->screen_base = (u_char *)fix.smem_start;
+			display->visual = fix.visual;
+			display->type = fix.type;
+			display->type_aux = fix.type_aux;
+			display->ypanstep = fix.ypanstep;
+			display->ywrapstep = fix.ywrapstep;
+			display->line_length = fix.line_length;
+			display->can_soft_blank = 1;
+			display->inverse = amifb_inverse;
 			if (fb_info.changevar)
 				(*fb_info.changevar)(con);
 		}
 		if (oldbpp != var->bits_per_pixel) {
-			if ((err = alloc_cmap(&disp[con].cmap, 0, 0)))
+			if ((err = alloc_cmap(&display->cmap, 0, 0)))
 				return err;
 			do_install_cmap(con);
 		}
 		if (con == currcon)
-			ami_set_var(&disp[con].var);
+			ami_set_var(&display->var);
 	}
 	return 0;
 }
@@ -1633,25 +1575,25 @@
 static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con, int fbidx)
 {
 	if (var->vmode & FB_VMODE_YWRAP) {
-		if (var->yoffset<0 || var->yoffset >= disp[con].var.yres_virtual || var->xoffset)
+		if (var->yoffset<0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset)
 			return -EINVAL;
 	} else {
 		/*
 		 * TODO: There will be problems when xpan!=1, so some columns
 		 * on the right side will never be seen
 		 */
-		if (var->xoffset+disp[con].var.xres > upx(16<<maxfmode, disp[con].var.xres_virtual) ||
-		    var->yoffset+disp[con].var.yres > disp[con].var.yres_virtual)
+		if (var->xoffset+fb_display[con].var.xres > upx(16<<maxfmode, fb_display[con].var.xres_virtual) ||
+		    var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 			return -EINVAL;
 	}
 	if (con == currcon)
 		ami_pan_var(var);
-	disp[con].var.xoffset = var->xoffset;
-	disp[con].var.yoffset = var->yoffset;
+	fb_display[con].var.xoffset = var->xoffset;
+	fb_display[con].var.yoffset = var->yoffset;
 	if (var->vmode & FB_VMODE_YWRAP)
-		disp[con].var.vmode |= FB_VMODE_YWRAP;
+		fb_display[con].var.vmode |= FB_VMODE_YWRAP;
 	else
-		disp[con].var.vmode &= ~FB_VMODE_YWRAP;
+		fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
 	return 0;
 }
 
@@ -1662,11 +1604,11 @@
 static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx)
 {
 	if (con == currcon) /* current console? */
-		return do_fb_get_cmap(cmap, &disp[con].var, kspc);
-	else if (disp[con].cmap.len) /* non default colormap? */
-		copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
+		return do_fb_get_cmap(cmap, &fb_display[con].var, kspc);
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
+		copy_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
 		          cmap, kspc ? 0 : 2);
 	return 0;
 }
@@ -1679,15 +1621,15 @@
 {
 	int err;
 
-	if (!disp[con].cmap.len) {		/* no colormap allocated? */
-		if ((err = alloc_cmap(&disp[con].cmap,
-		                      1<<disp[con].var.bits_per_pixel, 0)))
+	if (!fb_display[con].cmap.len) {		/* no colormap allocated? */
+		if ((err = alloc_cmap(&fb_display[con].cmap,
+		                      1<<fb_display[con].var.bits_per_pixel, 0)))
 			return err;
 	}
 	if (con == currcon)			/* current console? */
-		return do_fb_set_cmap(cmap, &disp[con].var, kspc);
+		return do_fb_set_cmap(cmap, &fb_display[con].var, kspc);
 	else
-		copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+		copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
 }
 
@@ -1836,26 +1778,18 @@
 	 * Initialisation
 	 */
 
-struct fb_info *amiga_fb_init(long *mem_start)
+int amiga_fb_init(u_long *mem_start)
 {
 	int err, tag, i;
 	u_long chipptr;
 
+	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO))
+		return -ENODEV;
+
 	/*
-	 * Check for a Graphics Board
+	 * TODO: where should we put this? The DMI Resolver doesn't have a
+	 *	 frame buffer accessible by the CPU
 	 */
-
-#ifdef CONFIG_FB_CYBER
-	if (amifb_Cyber)
-		return Cyber_fb_init(mem_start);
-#endif
-#ifdef CONFIG_FB_RETINAZ3
-	if (amifb_retz3){
-		custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
-				DMAF_BLITTER | DMAF_SPRITE;
-		return retz3_fb_init(mem_start);
-	}
-#endif
 #ifdef CONFIG_GSP_RESOLVER
 	if (amifb_resolver){
 		custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
@@ -1863,14 +1797,6 @@
 		return NULL;
 	}
 #endif
-
-	/*
-	 * Use the Builtin Chipset
-	 */
-
-	if (!AMIGAHW_PRESENT(AMI_VIDEO))
-		return NULL;
-
 	custom.dmacon = DMAF_ALL | DMAF_MASTER;
 
 	switch (amiga_chipset) {
@@ -1938,7 +1864,7 @@
 			strcat(amiga_fb_name, "Unknown");
 			goto default_chipset;
 #else /* CONFIG_AMIFB_OCS */
-			panic("Unknown graphics chipset, no default driver");
+			return 1;	/* -ENODEV?? */;
 #endif /* CONFIG_AMIFB_OCS */
 			break;
 	}
@@ -1964,10 +1890,21 @@
 		}
 	}
 
-	err = register_framebuffer(amiga_fb_name, &node, &amiga_fb_ops,
-	                           NUM_TOTAL_MODES, amiga_fb_predefined);
+	strcpy(fb_info.modename, amiga_fb_name);
+	fb_info.changevar = NULL;
+	fb_info.node = -1;
+	fb_info.fbops = &amiga_fb_ops;
+	fb_info.fbvar_num = NUM_TOTAL_MODES;
+	fb_info.fbvar = amiga_fb_predefined;
+	fb_info.disp = &disp;
+	fb_info.switch_con = &amifbcon_switch;
+	fb_info.updatevar = &amifbcon_updatevar;
+	fb_info.blank = &amifbcon_blank;
+	fb_info.setcmap = &amifbcon_setcmap;
+
+	err = register_framebuffer(&fb_info);
 	if (err < 0)
-		panic("Cannot register frame buffer");
+		return(err);
 
 	chipptr = chipalloc(videomemorysize+
 	                    SPRITEMEMSIZE+
@@ -2009,43 +1946,33 @@
 	custom.intena = IF_VERTB;
 	custom.intena = IF_SETCLR | IF_COPER;
 
-	strcpy(fb_info.modename, amiga_fb_name);
-	fb_info.changevar = NULL;
-	fb_info.disp = disp;
-	fb_info.switch_con = &amifbcon_switch;
-	fb_info.updatevar = &amifbcon_updatevar;
-	fb_info.blank = &amifbcon_blank;
-	fb_info.setcmap = &amifbcon_setcmap;
-
-	amiga_fb_set_var(&amiga_fb_predefined[0], 0, GET_FB_IDX(node));
+	amiga_fb_set_var(&amiga_fb_predefined[0], -1, GET_FB_IDX(fb_info.node));
 
-#ifdef CONFIG_FB_CLGEN
-	clgen_fb_init(mem_start);
-#endif
-
-	return &fb_info;
+	printk("%s frame buffer device, using %ldK of video memory\n",
+	       fb_info.modename, videomemorysize>>10);
+	return 0;
 }
 
 static int amifbcon_switch(int con)
 {
 	/* Do we have to save the colormap? */
-	if (disp[currcon].cmap.len)
-		do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1);
+	if (fb_display[currcon].cmap.len)
+		do_fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1);
 
 	currcon = con;
-	ami_set_var(&disp[con].var);
+	ami_set_var(&fb_display[con].var);
 	/* Install new colormap */
 	do_install_cmap(con);
 	return 0;
 }
 
 	/*
-	 * Update the `var' structure (called by amicon.c)
+	 * Update the `var' structure (called by fbon.c)
 	 */
 
 static int amifbcon_updatevar(int con)
 {
-	ami_pan_var(&disp[con].var);
+	ami_pan_var(&fb_display[con].var);
 	return 0;
 }
 
@@ -2064,7 +1991,7 @@
 
 static int amifbcon_setcmap(struct fb_cmap *cmap, int con)
 {
-	return(amiga_fb_set_cmap(cmap, 1, con, GET_FB_IDX(node)));
+	return(amiga_fb_set_cmap(cmap, 1, con, GET_FB_IDX(fb_info.node)));
 }
 
 /* ---------------------------- Generic routines ---------------------------- */
@@ -2183,11 +2110,11 @@
 {
 	if (con != currcon)
 		return;
-	if (disp[con].cmap.len)
-		do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1);
+	if (fb_display[con].cmap.len)
+		do_fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1);
 	else
-		do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
-		                                    &disp[con].var, 1);
+		do_fb_set_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
+		                                    &fb_display[con].var, 1);
 }
 
 static void memcpy_fs(int fsfromto, void *to, void *from, int len)
--- linux-2.1.29/arch/m68k/amiga/config.c.orig	Tue Mar 25 21:36:41 1997
+++ linux-2.1.29/arch/m68k/amiga/config.c	Mon Mar 31 21:08:13 1997
@@ -76,7 +76,6 @@
 static void amiga_reset (void);
 static void amiga_waitbut(void);
 extern struct consw fb_con;
-extern struct fb_info *amiga_fb_init(long *);
 extern void zorro_init(void);
 static void amiga_savekmsg_init(void);
 static void amiga_mem_console_write(const char *b, unsigned int count);
@@ -337,7 +336,6 @@
   mach_reset           = amiga_reset;
   waitbut              = amiga_waitbut;
   conswitchp           = &fb_con;
-  mach_fb_init         = amiga_fb_init;
   mach_debug_init      = amiga_debug_init;
   mach_video_setup     = amiga_video_setup;
   kd_mksound           = amiga_mksound;
--- linux-2.1.29/arch/m68k/amiga/cyberfb.c.orig	Wed Mar 19 23:16:56 1997
+++ linux-2.1.29/arch/m68k/amiga/cyberfb.c	Wed Apr  2 23:20:11 1997
@@ -37,10 +37,6 @@
 #include <linux/fb.h>
 #include "s3blit.h"
 
-/* FN */
-#define FB_MODES_SHIFT    5 /* 32 modes per framebuffer */
-#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
-
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
 struct Cyber_fb_par {
@@ -54,7 +50,7 @@
 static int current_par_valid = 0;
 static int currcon = 0;
 
-static struct display disp[MAX_NR_CONSOLES];
+static struct display disp;
 static struct fb_info fb_info;
 
 static int node;        /* node of the /dev/fb?current file */
@@ -236,7 +232,7 @@
     *    Interface to the low level console driver
     */
 
-struct fb_info *Cyber_fb_init(long *mem_start); /* Through amiga_fb_init() */
+int Cyber_fb_init(long *mem_start);
 static int Cyberfb_switch(int con);
 static int Cyberfb_updatevar(int con);
 static void Cyberfb_blank(int blank);
@@ -888,11 +884,11 @@
 {
    if (con != currcon)
       return;
-   if (disp[con].cmap.len)
-      do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1);
+   if (fb_display[con].cmap.len)
+      do_fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1);
    else
-      do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
-                                          &disp[con].var, 1);
+      do_fb_set_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
+                                          &fb_display[con].var, 1);
 }
 
 
@@ -983,7 +979,7 @@
    if (con == -1)
       Cyber_fb_get_par(&par);
    else
-      error = fbhw->decode_var(&disp[con].var, &par);
+      error = fbhw->decode_var(&fb_display[con].var, &par);
    return(error ? error : fbhw->encode_fix(fix, &par));
 }
 
@@ -1001,7 +997,7 @@
       Cyber_fb_get_par(&par);
       error = fbhw->encode_var(var, &par);
    } else
-      *var = disp[con].var;
+      *var = fb_display[con].var;
    return(error);
 }
 
@@ -1009,19 +1005,25 @@
 static void Cyber_fb_set_disp(int con)
 {
    struct fb_fix_screeninfo fix;
+   struct display *display;
+
+   if (con >= 0)
+      display = &fb_display[con];
+   else
+      display = &disp;        /* used during initialization */
 
 /* ### FN: Needs fixes later */
    Cyber_fb_get_fix(&fix, con, 0);
    if (con == -1)
       con = 0;
-   disp[con].screen_base = (u_char *)fix.smem_start;
-   disp[con].visual = fix.visual;
-   disp[con].type = fix.type;
-   disp[con].type_aux = fix.type_aux;
-   disp[con].ypanstep = fix.ypanstep;
-   disp[con].ywrapstep = fix.ywrapstep;
-   disp[con].can_soft_blank = 1;
-   disp[con].inverse = Cyberfb_inverse;
+   display->screen_base = (u_char *)fix.smem_start;
+   display->visual = fix.visual;
+   display->type = fix.type;
+   display->type_aux = fix.type_aux;
+   display->ypanstep = fix.ypanstep;
+   display->ywrapstep = fix.ywrapstep;
+   display->can_soft_blank = 1;
+   display->inverse = Cyberfb_inverse;
 }
 
 
@@ -1036,18 +1038,18 @@
    if ((err = do_fb_set_var(var, con == currcon)))
       return(err);
    if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-      oldxres = disp[con].var.xres;
-      oldyres = disp[con].var.yres;
-      oldvxres = disp[con].var.xres_virtual;
-      oldvyres = disp[con].var.yres_virtual;
-      oldbpp = disp[con].var.bits_per_pixel;
-      disp[con].var = *var;
+      oldxres = fb_display[con].var.xres;
+      oldyres = fb_display[con].var.yres;
+      oldvxres = fb_display[con].var.xres_virtual;
+      oldvyres = fb_display[con].var.yres_virtual;
+      oldbpp = fb_display[con].var.bits_per_pixel;
+      fb_display[con].var = *var;
       if (oldxres != var->xres || oldyres != var->yres ||
           oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
           oldbpp != var->bits_per_pixel) {
          Cyber_fb_set_disp(con);
          (*fb_info.changevar)(con);
-         alloc_cmap(&disp[con].cmap, 0, 0);
+         alloc_cmap(&fb_display[con].cmap, 0, 0);
          do_install_cmap(con);
       }
    }
@@ -1063,11 +1065,11 @@
 static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx)
 {
    if (con == currcon) /* current console? */
-      return(do_fb_get_cmap(cmap, &disp[con].var, kspc));
-   else if (disp[con].cmap.len) /* non default colormap? */
-      copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
+      return(do_fb_get_cmap(cmap, &fb_display[con].var, kspc));
+   else if (fb_display[con].cmap.len) /* non default colormap? */
+      copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
    else
-      copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel), cmap,
+      copy_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel), cmap,
                 kspc ? 0 : 2);
    return(0);
 }
@@ -1081,15 +1083,15 @@
 {
    int err;
 
-   if (!disp[con].cmap.len) {       /* no colormap allocated? */
-      if ((err = alloc_cmap(&disp[con].cmap, 1<<disp[con].var.bits_per_pixel,
+   if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
+      if ((err = alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel,
                             0)))
          return(err);
    }
    if (con == currcon)              /* current console? */
-      return(do_fb_set_cmap(cmap, &disp[con].var, kspc));
+      return(do_fb_set_cmap(cmap, &fb_display[con].var, kspc));
    else
-      copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+      copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
    return(0);
 }
 
@@ -1181,7 +1183,7 @@
     *    Initialization
     */
 
-struct fb_info *Cyber_fb_init(long *mem_start)
+int Cyber_fb_init(long *mem_start)
 {
    int err;
    struct Cyber_fb_par par;
@@ -1190,37 +1192,44 @@
 
    fbhw = &Cyber_switch;
 
-   err = register_framebuffer(Cyber_fb_name, &node, &Cyber_fb_ops,
-                              NUM_TOTAL_MODES, Cyber_fb_predefined);
-   if (err < 0)
-      panic("Cannot register frame buffer\n");
-
-   fbhw->init();
-   fbhw->decode_var(&Cyber_fb_predefined[Cyberfb_mode], &par);
-   fbhw->encode_var(&Cyber_fb_predefined[0], &par);
-
    strcpy(fb_info.modename, Cyber_fb_name);
-   fb_info.disp = disp;
+   fb_info.changevar = NULL;
+   fb_info.node = -1;
+   fb_info.fbops = &Cyber_fb_ops;
+   fb_info.fbvar_num = NUM_TOTAL_MODES;
+   fb_info.fbvar = Cyber_fb_predefined;
+   fb_info.disp = &disp;
    fb_info.switch_con = &Cyberfb_switch;
    fb_info.updatevar = &Cyberfb_updatevar;
    fb_info.blank = &Cyberfb_blank;
    fb_info.setcmap = &Cyberfb_setcmap;
 
+   err = register_framebuffer(&fb_info);
+   if (err < 0)
+      return err;
+
+   fbhw->init();
+   fbhw->decode_var(&Cyber_fb_predefined[Cyberfb_mode], &par);
+   fbhw->encode_var(&Cyber_fb_predefined[0], &par);
+
    do_fb_set_var(&Cyber_fb_predefined[0], 1);
-   Cyber_fb_get_var(&disp[0].var, -1, GET_FB_IDX(node));
+   Cyber_fb_get_var(&fb_display[0].var, -1, GET_FB_IDX(node));
    Cyber_fb_set_disp(-1);
    do_install_cmap(0);
-   return(&fb_info);
+
+   printk("%s frame buffer device, using %ldK of video memory\n",
+	  fb_info.modename, CyberSize>>10);
+   return(0);
 }
 
 
 static int Cyberfb_switch(int con)
 {
    /* Do we have to save the colormap? */
-   if (disp[currcon].cmap.len)
-      do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1);
+   if (fb_display[currcon].cmap.len)
+      do_fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1);
 
-   do_fb_set_var(&disp[con].var, 1);
+   do_fb_set_var(&fb_display[con].var, 1);
    currcon = con;
    /* Install new colormap */
    do_install_cmap(con);
--- linux-2.1.29/arch/m68k/amiga/retz3fb.c.orig	Wed Mar 19 23:36:42 1997
+++ linux-2.1.29/arch/m68k/amiga/retz3fb.c	Wed Apr  2 23:11:57 1997
@@ -50,10 +50,6 @@
 #define PAT_MEM_SIZE 16*3
 #define PAT_MEM_OFF  (4*1024*1024 - PAT_MEM_SIZE)
 
-/* FN */
-#define FB_MODES_SHIFT    5 /* 32 modes per framebuffer */
-#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
-
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
 struct retz3_fb_par {
@@ -100,11 +96,9 @@
 static int current_par_valid = 0;
 static int currcon = 0;
 
-static struct display disp[MAX_NR_CONSOLES];
+static struct display disp;
 static struct fb_info fb_info;
 
-static int node;        /* node of the /dev/fb?current file */
-
 
 /*
  *    Switch for Chipset Independency
@@ -335,7 +329,7 @@
  *    Interface to the low level console driver
  */
 
-struct fb_info *retz3_fb_init(long *mem_start); /* Through amiga_fb_init() */
+int retz3_fb_init(long *mem_start);
 static int z3fb_switch(int con);
 static int z3fb_updatevar(int con);
 static void z3fb_blank(int blank);
@@ -1380,11 +1374,11 @@
 {
 	if (con != currcon)
 		return;
-	if (disp[con].cmap.len)
-		do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1);
+	if (fb_display[con].cmap.len)
+		do_fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1);
 	else
-		do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
-                                          &disp[con].var, 1);
+		do_fb_set_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
+                                          &fb_display[con].var, 1);
 }
 
 
@@ -1476,7 +1470,7 @@
 	if (con == -1)
 		retz3_fb_get_par(&par);
 	else
-		error = fbhw->decode_var(&disp[con].var, &par);
+		error = fbhw->decode_var(&fb_display[con].var, &par);
 	return(error ? error : fbhw->encode_fix(fix, &par));
 }
 
@@ -1494,7 +1488,7 @@
 		retz3_fb_get_par(&par);
 		error = fbhw->encode_var(var, &par);
 	} else
-		*var = disp[con].var;
+		*var = fb_display[con].var;
 	return error;
 }
 
@@ -1502,18 +1496,24 @@
 static void retz3_fb_set_disp(int con)
 {
 	struct fb_fix_screeninfo fix;
+	struct display *display;
 
-	retz3_fb_get_fix(&fix, con, GET_FB_IDX(node));
+	if (con >= 0)
+		display = &fb_display[con];
+	else
+		display = &disp;	/* used during initialization */
+
+	retz3_fb_get_fix(&fix, con, GET_FB_IDX(fb_info.node));
 	if (con == -1)
 		con = 0;
-	disp[con].screen_base = (unsigned char *)fix.smem_start;
-	disp[con].visual = fix.visual;
-	disp[con].type = fix.type;
-	disp[con].type_aux = fix.type_aux;
-	disp[con].ypanstep = fix.ypanstep;
-	disp[con].ywrapstep = fix.ywrapstep;
-	disp[con].can_soft_blank = 1;
-	disp[con].inverse = z3fb_inverse;
+	display->screen_base = (unsigned char *)fix.smem_start;
+	display->visual = fix.visual;
+	display->type = fix.type;
+	display->type_aux = fix.type_aux;
+	display->ypanstep = fix.ypanstep;
+	display->ywrapstep = fix.ywrapstep;
+	display->can_soft_blank = 1;
+	display->inverse = z3fb_inverse;
 }
 
 
@@ -1528,19 +1528,19 @@
 	if ((err = do_fb_set_var(var, con == currcon)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-		oldxres = disp[con].var.xres;
-		oldyres = disp[con].var.yres;
-		oldvxres = disp[con].var.xres_virtual;
-		oldvyres = disp[con].var.yres_virtual;
-		oldbpp = disp[con].var.bits_per_pixel;
-		disp[con].var = *var;
+		oldxres = fb_display[con].var.xres;
+		oldyres = fb_display[con].var.yres;
+		oldvxres = fb_display[con].var.xres_virtual;
+		oldvyres = fb_display[con].var.yres_virtual;
+		oldbpp = fb_display[con].var.bits_per_pixel;
+		fb_display[con].var = *var;
 		if (oldxres != var->xres || oldyres != var->yres ||
 		    oldvxres != var->xres_virtual ||
 		    oldvyres != var->yres_virtual ||
 		    oldbpp != var->bits_per_pixel) {
 			retz3_fb_set_disp(con);
 			(*fb_info.changevar)(con);
-			alloc_cmap(&disp[con].cmap, 0, 0);
+			alloc_cmap(&fb_display[con].cmap, 0, 0);
 			do_install_cmap(con);
 		}
 	}
@@ -1556,11 +1556,11 @@
 static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx)
 {
 	if (con == currcon) /* current console? */
-		return(do_fb_get_cmap(cmap, &disp[con].var, kspc));
-	else if (disp[con].cmap.len) /* non default colormap? */
-		copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
+		return(do_fb_get_cmap(cmap, &fb_display[con].var, kspc));
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
+		copy_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
 			  cmap, kspc ? 0 : 2);
 	return 0;
 }
@@ -1574,15 +1574,15 @@
 {
 	int err;
 
-	if (!disp[con].cmap.len) {       /* no colormap allocated? */
-		if ((err = alloc_cmap(&disp[con].cmap,
-				      1<<disp[con].var.bits_per_pixel, 0)))
+	if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
+		if ((err = alloc_cmap(&fb_display[con].cmap,
+				      1<<fb_display[con].var.bits_per_pixel, 0)))
 			return err;
 	}
 	if (con == currcon)              /* current console? */
-		return(do_fb_set_cmap(cmap, &disp[con].var, kspc));
+		return(do_fb_set_cmap(cmap, &fb_display[con].var, kspc));
 	else
-		copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+		copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
 }
 
@@ -1678,7 +1678,7 @@
  *    Initialization
  */
 
-struct fb_info *retz3_fb_init(long *mem_start)
+int retz3_fb_init(long *mem_start)
 {
 	int err;
 	struct retz3_fb_par par;
@@ -1687,10 +1687,21 @@
 
 	fbhw = &retz3_switch;
 
-	err = register_framebuffer(retz3_fb_name, &node, &retz3_fb_ops,
-				   NUM_TOTAL_MODES, retz3_fb_predefined);
+	strcpy(fb_info.modename, retz3_fb_name);
+	fb_info.changevar = NULL;
+	fb_info.node = -1;
+	fb_info.fbops = &retz3_fb_ops;
+	fb_info.fbvar_num = NUM_TOTAL_MODES;
+	fb_info.fbvar = retz3_fb_predefined;
+	fb_info.disp = &disp;
+	fb_info.switch_con = &z3fb_switch;
+	fb_info.updatevar = &z3fb_updatevar;
+	fb_info.blank = &z3fb_blank;
+	fb_info.setcmap = &z3fb_setcmap;
+
+	err = register_framebuffer(&fb_info);
 	if (err < 0)
-		panic("Cannot register frame buffer\n");
+		return err;
 
 	fbhw->init();
 
@@ -1700,29 +1711,25 @@
 	fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], &par);
 	fbhw->encode_var(&retz3_fb_predefined[0], &par);
 
-	strcpy(fb_info.modename, retz3_fb_name);
-	fb_info.disp = disp;
-	fb_info.switch_con = &z3fb_switch;
-	fb_info.updatevar = &z3fb_updatevar;
-	fb_info.blank = &z3fb_blank;
-	fb_info.setcmap = &z3fb_setcmap;
-
 	do_fb_set_var(&retz3_fb_predefined[0], 0);
-	retz3_fb_get_var(&disp[0].var, -1, GET_FB_IDX(node));
+	retz3_fb_get_var(&fb_display[0].var, -1, GET_FB_IDX(fb_info.node));
 	retz3_fb_set_disp(-1);
 	do_install_cmap(0);
 
-	return &fb_info;
+	printk("%s frame buffer device, using %ldK of video memory\n",
+	       fb_info.modename, z3_size>>10);
+
+	return 0;
 }
 
 
 static int z3fb_switch(int con)
 {
 	/* Do we have to save the colormap? */
-	if (disp[currcon].cmap.len)
-		do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1);
+	if (fb_display[currcon].cmap.len)
+		do_fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1);
 
-	do_fb_set_var(&disp[con].var, 1);
+	do_fb_set_var(&fb_display[con].var, 1);
 	currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con);
@@ -1759,7 +1766,7 @@
 
 static int z3fb_setcmap(struct fb_cmap *cmap, int con)
 {
-	return(retz3_fb_set_cmap(cmap, 1, con, GET_FB_IDX(node)));
+	return(retz3_fb_set_cmap(cmap, 1, con, GET_FB_IDX(fb_info.node)));
 }
 
 
--- linux-2.1.29/arch/m68k/amiga/clgen.c.orig	Wed Mar 19 23:27:04 1997
+++ linux-2.1.29/arch/m68k/amiga/clgen.c	Wed Apr  2 23:26:01 1997
@@ -88,9 +88,7 @@
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
 /* copied from drivers/char/fbmem.c :-\ */
-#define FB_MODES_SHIFT 5
 #define GET_INODE(i) MKDEV(FB_MAJOR, (i) << FB_MODES_SHIFT)
-#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
 #define GET_FB_VAR_IDX(node) (MINOR(node) & ((1 << FB_MODES_SHIFT)-1))
 
 /* board types */
@@ -293,7 +291,7 @@
 static int clgen_get_monitorspec(struct fb_monitorspec *spec, int val, int fbidx);
 static int clgen_put_monitorspec(struct fb_monitorspec *spec, int val, int fbidx);
 int					find_clboard( int fbidx );
-void				clgen_fb_init( long *mem_start );
+int				clgen_fb_init( long *mem_start );
 
 /********************************************/
 /* init_vgachip() - initialize the VGA chip */
@@ -2131,7 +2129,7 @@
 /********************************************************************/
 /* clgen_fb_init() - master initialization function                 */
 /********************************************************************/
-void clgen_fb_init(long *mem_start)
+int clgen_fb_init(long *mem_start)
 {
 	u_int key, key2;
 	const struct ConfigDev *cd, *cd2;
@@ -2251,7 +2249,7 @@
 			if (err)
 			{
 				printk(KERN_ERR "clgen: WARNING - could not register fb device; err = %d!\n", err);
-				return;
+				return err;
 			}
 
 			/* create fbnum from inode */
@@ -2303,5 +2301,6 @@
 	} while (btype != -1);
 
 	/* done */
+	return 0;
 }
 
--- linux-2.1.29/arch/m68k/atari/config.c.orig	Tue Mar 25 20:29:26 1997
+++ linux-2.1.29/arch/m68k/atari/config.c	Wed Apr  2 22:37:55 1997
@@ -85,7 +85,6 @@
 #endif
 static void atari_waitbut (void);
 extern struct consw fb_con;
-extern struct fb_info *atari_fb_init(long *);
 static void (*atari_debug_init(void))(const char*, unsigned int);
 extern void atari_video_setup(char *, int *);
 
@@ -270,7 +269,6 @@
 #endif
     conswitchp	         = &fb_con;
     waitbut		 = atari_waitbut;
-    mach_fb_init         = atari_fb_init;
     mach_max_dma_address = 0xffffff;
     mach_debug_init	 = atari_debug_init;
     mach_video_setup	 = atari_video_setup;
@@ -933,7 +931,7 @@
 
 static void ata_scc_console_write (const char *str, unsigned int count)
 {
-    while (cout--) {
+    while (count--) {
 	if (*str == '\n')
 	    ata_scc_out( '\r' );
 	ata_scc_out( *str++ );
--- linux-2.1.29/arch/m68k/atari/atafb.c.orig	Wed Mar 19 22:08:45 1997
+++ linux-2.1.29/arch/m68k/atari/atafb.c	Wed Apr  2 22:53:22 1997
@@ -57,6 +57,7 @@
 
 #include <linux/fb.h>
 #include <asm/atarikb.h>
+#include <asm/setup.h>
 
 #define SWITCH_ACIA 0x01		/* modes for switch on OverScan */
 #define SWITCH_SND6 0x40
@@ -71,8 +72,6 @@
 
 static int default_par=0;	/* default resolution (0=none) */
 
-static int node;		/* node of the /dev/fb?current file */
-
 static unsigned long default_mem_req=0;
 
 static int hwscroll=-1;
@@ -154,7 +153,7 @@
 
 static int mono_moni=0;
 
-static struct display disp[MAX_NR_CONSOLES];
+static struct display disp;
 
 
 #ifdef ATAFB_EXT
@@ -1621,7 +1620,7 @@
 							   struct atari_fb_par *par )
 {
 	int xoffset;
-	int bpp = disp[currcon].var.bits_per_pixel;
+	int bpp = fb_display[currcon].var.bits_per_pixel;
 
 	if (bpp == 1)
 		var->xoffset = up(var->xoffset, 32);
@@ -1632,13 +1631,13 @@
 		var->xoffset = up(var->xoffset, 2);
 	}
 	par->hw.falcon.line_offset = bpp *
-	       	(disp[currcon].var.xres_virtual - disp[currcon].var.xres) / 16;
+	       	(fb_display[currcon].var.xres_virtual - fb_display[currcon].var.xres) / 16;
 	if (par->hw.falcon.xoffset)
 		par->hw.falcon.line_offset -= bpp;
 	xoffset = var->xoffset - par->hw.falcon.xoffset;
 
 	par->screen_base = screen_base +
-	        (var->yoffset * disp[currcon].var.xres_virtual + xoffset) * bpp / 8;
+	        (var->yoffset * fb_display[currcon].var.xres_virtual + xoffset) * bpp / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -2334,8 +2333,8 @@
 	else
 		var->xoffset = up(var->xoffset, 16);
 	par->screen_base = screen_base +
-	        (var->yoffset * disp[currcon].var.xres_virtual + var->xoffset)
-	        * disp[currcon].var.bits_per_pixel / 8;
+	        (var->yoffset * fb_display[currcon].var.xres_virtual + var->xoffset)
+	        * fb_display[currcon].var.bits_per_pixel / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -2409,8 +2408,8 @@
 static int
 fb_update_var(int con)
 {
-	int off=disp[con].var.yoffset*disp[con].var.xres_virtual*
-			disp[con].var.bits_per_pixel>>3;
+	int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual*
+			fb_display[con].var.bits_per_pixel>>3;
 
 	current_par.screen_base=screen_base + off;
 
@@ -2578,11 +2577,11 @@
 {
 	if (con != currcon)
 		return;
-	if (disp[con].cmap.len)
-		do_fb_set_cmap(&disp[con].cmap, &(disp[con].var), 1);
+	if (fb_display[con].cmap.len)
+		do_fb_set_cmap(&fb_display[con].cmap, &(fb_display[con].var), 1);
 	else
 		do_fb_set_cmap(get_default_colormap(
-				disp[con].var.bits_per_pixel), &(disp[con].var), 1);		
+				fb_display[con].var.bits_per_pixel), &(fb_display[con].var), 1);		
 }
 
 static void
@@ -2667,7 +2666,7 @@
 	if (con == -1)
 		atari_fb_get_par(&par);
 	else
-		fbhw->decode_var(&disp[con].var,&par);
+		fbhw->decode_var(&fb_display[con].var,&par);
 	return fbhw->encode_fix(fix, &par);
 }
 	
@@ -2680,7 +2679,7 @@
 		fbhw->encode_var(var, &par);
 	}
 	else
-		*var=disp[con].var;
+		*var=fb_display[con].var;
 	return 0;
 }
 
@@ -2688,23 +2687,29 @@
 atari_fb_set_disp(int con)
 {
 	struct fb_fix_screeninfo fix;
+	struct display *display;
+
+	if (con >= 0)
+		display = &fb_display[con];
+	else
+		display = &disp;	/* used during initialization */
 
-	atari_fb_get_fix(&fix, con);
+	atari_fb_get_fix(&fix, con, GET_FB_IDX(fb_info.node));
 	if (con == -1)
 		con=0;
-	disp[con].screen_base = (u_char *)fix.smem_start;
-	disp[con].visual = fix.visual;
-	disp[con].type = fix.type;
-	disp[con].type_aux = fix.type_aux;
-	disp[con].ypanstep = fix.ypanstep;
-	disp[con].ywrapstep = fix.ywrapstep;
-	disp[con].line_length = fix.line_length;
+	display->screen_base = (u_char *)fix.smem_start;
+	display->visual = fix.visual;
+	display->type = fix.type;
+	display->type_aux = fix.type_aux;
+	display->ypanstep = fix.ypanstep;
+	display->ywrapstep = fix.ywrapstep;
+	display->line_length = fix.line_length;
 	if (fix.visual != FB_VISUAL_PSEUDOCOLOR &&
 		fix.visual != FB_VISUAL_DIRECTCOLOR)
-		disp[con].can_soft_blank = 0;
+		display->can_soft_blank = 0;
 	else
-		disp[con].can_soft_blank = 1;
-	disp[con].inverse =
+		display->can_soft_blank = 1;
+	display->inverse =
 	    (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
 }
 
@@ -2712,17 +2717,18 @@
 atari_fb_set_var(struct fb_var_screeninfo *var, int con, int fbidx)
 {
 	int err,oldxres,oldyres,oldbpp,oldxres_virtual,
-	    oldyres_virtual,oldyoffset;
+	oldyres_virtual,oldyoffset;
+
 	if ((err=do_fb_set_var(var, con==currcon)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-		oldxres=disp[con].var.xres;
-		oldyres=disp[con].var.yres;
-		oldxres_virtual=disp[con].var.xres_virtual;
-		oldyres_virtual=disp[con].var.yres_virtual;
-		oldbpp=disp[con].var.bits_per_pixel;
-		oldyoffset=disp[con].var.yoffset;
-		disp[con].var=*var;
+		oldxres=fb_display[con].var.xres;
+		oldyres=fb_display[con].var.yres;
+		oldxres_virtual=fb_display[con].var.xres_virtual;
+		oldyres_virtual=fb_display[con].var.yres_virtual;
+		oldbpp=fb_display[con].var.bits_per_pixel;
+		oldyoffset=fb_display[con].var.yoffset;
+		fb_display[con].var=*var;
 		if (oldxres != var->xres || oldyres != var->yres 
 		    || oldxres_virtual != var->xres_virtual
 		    || oldyres_virtual != var->yres_virtual
@@ -2730,7 +2736,7 @@
 		    || oldyoffset != var->yoffset) {
 			atari_fb_set_disp(con);
 			(*fb_info.changevar)(con);
-			alloc_cmap(&disp[con].cmap, 0, 0);
+			alloc_cmap(&fb_display[con].cmap, 0, 0);
 			do_install_cmap(con);
 		}
 	}
@@ -2744,13 +2750,13 @@
 atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx)
 {
 	if (con == currcon) /* current console ? */
-		return do_fb_get_cmap(cmap, &(disp[con].var), kspc);
+		return do_fb_get_cmap(cmap, &(fb_display[con].var), kspc);
 	else
-		if (disp[con].cmap.len) /* non default colormap ? */
-			copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
+		if (fb_display[con].cmap.len) /* non default colormap ? */
+			copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 		else
 			copy_cmap(get_default_colormap(
-			    disp[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+			    fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
 	return 0;
 }
 
@@ -2758,15 +2764,15 @@
 atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx)
 {
 	int err;
-	if (! disp[con].cmap.len) { /* no colormap allocated ? */
-		if ((err = alloc_cmap(&disp[con].cmap, 
-					1 << disp[con].var.bits_per_pixel, 0)))
+	if (! fb_display[con].cmap.len) { /* no colormap allocated ? */
+		if ((err = alloc_cmap(&fb_display[con].cmap, 
+					1 << fb_display[con].var.bits_per_pixel, 0)))
 		return err;
 	}
 	if (con == currcon) /* current console ? */
-		return do_fb_set_cmap(cmap, &(disp[con].var), kspc);
+		return do_fb_set_cmap(cmap, &(fb_display[con].var), kspc);
 	else
-		copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+		copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
 }
 
@@ -2777,8 +2783,8 @@
 	int yoffset = var->yoffset;
 	int err;
 
-	if (   xoffset < 0 || xoffset + disp[con].var.xres > disp[con].var.xres_virtual
-	    || yoffset < 0 || yoffset + disp[con].var.yres > disp[con].var.yres_virtual)
+	if (   xoffset < 0 || xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual
+	    || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 		return -EINVAL;
 
 	if (con == currcon) {
@@ -2789,8 +2795,8 @@
 		else
 			return -EINVAL;
 	}
-	disp[con].var.xoffset = var->xoffset;
-	disp[con].var.yoffset = var->yoffset;
+	fb_display[con].var.xoffset = var->xoffset;
+	fb_display[con].var.yoffset = var->yoffset;
 	return 0;
 }
 
@@ -2889,9 +2895,9 @@
 atafb_switch(int con)
 {
 	/* Do we have to save the colormap ? */
-	if (disp[currcon].cmap.len)
-		do_fb_get_cmap(&disp[currcon].cmap, &(disp[currcon].var), 1);
-	do_fb_set_var(&disp[con].var,1);
+	if (fb_display[currcon].cmap.len)
+		do_fb_get_cmap(&fb_display[currcon].cmap, &(fb_display[currcon].var), 1);
+	do_fb_set_var(&fb_display[con].var,1);
 	currcon=con;
 	/* Install new colormap */
 	do_install_cmap(con);
@@ -2920,19 +2926,19 @@
 		cmap.transp=NULL;
 		cmap.start=0;
 		cmap.len=16;
-		do_fb_set_cmap(&cmap, &(disp[currcon].var), 1);
+		do_fb_set_cmap(&cmap, &(fb_display[currcon].var), 1);
 	}
 	else
 		do_install_cmap(currcon);
 }
 
-static void
+static int
 atafb_setcmap(struct fb_cmap *cmap, int con)
 {
-	return(atari_fb_set_cmap(cmap, 1, con), GET_FB_IDX(node));
+	return(atari_fb_set_cmap(cmap, 1, con, GET_FB_IDX(fb_info.node)));
 }
 
-struct fb_info *
+int
 atari_fb_init(long *mem_start)
 {
 	int err;
@@ -2941,10 +2947,27 @@
 	unsigned long mem_req;
 	struct fb_var_screeninfo *var;
 	
-	err=register_framebuffer("Atari Builtin", &node, &atari_fb_ops, 
-			num_atari_fb_predefined, atari_fb_predefined);
+	if (!MACH_IS_ATARI)
+		return -ENODEV;
+
+	strcpy(fb_info.modename, "Atari Builtin ");
+	fb_info.changevar = NULL;
+	fb_info.node = -1;
+	fb_info.fbops = &atari_fb_ops;
+	fb_info.fbvar_num = num_atari_fb_predefined;
+	fb_info.fbvar = atari_fb_predefined;
+	fb_info.disp=&disp;
+	fb_info.switch_con=&atafb_switch;
+	fb_info.updatevar=&fb_update_var;
+	fb_info.blank=&atafb_blank;
+	fb_info.setcmap=&atafb_setcmap;
+	var=atari_fb_predefined+default_par-1;
+	do_fb_set_var(var,1);
+	strcat(fb_info.modename,fb_var_names[default_par-1][0]);
+
+	err=register_framebuffer(&fb_info);
 	if (err < 0)
-		panic ("Cannot register frame buffer\n");
+		return(err);
 	do {
 #ifdef ATAFB_EXT
 		if (external_addr) {
@@ -3021,22 +3044,14 @@
 	}
 #endif /* ATAFB_EXT */
 
-	strcpy(fb_info.modename, "Atari Builtin ");
-	fb_info.disp=disp;
-	fb_info.switch_con=&atafb_switch;
-	fb_info.updatevar=&fb_update_var;
-	fb_info.blank=&atafb_blank;
-	fb_info.setcmap=&atafb_setcmap;
-	var=atari_fb_predefined+default_par-1;
-	do_fb_set_var(var,1);
-	strcat(fb_info.modename,fb_var_names[default_par-1][0]);
-
-	atari_fb_get_var(&disp[0].var, -1);
+	atari_fb_get_var(&fb_display[0].var, -1, GET_FB_IDX(fb_info.node));
 	atari_fb_set_disp(-1);
 	printk("Determined %dx%d, depth %d\n",
-	       disp[0].var.xres, disp[0].var.yres, disp[0].var.bits_per_pixel );
+	       fb_display[0].var.xres, fb_display[0].var.yres, fb_display[0].var.bits_per_pixel );
 	do_install_cmap(0);
-	return &fb_info;
+	printk("%s frame buffer device, using %dK of video memory\n",
+	       fb_info.modename, screen_len>>10);
+	return 0;
 }
 
 /* a strtok which returns empty strings, too */
--- linux-2.1.29/arch/m68k/kernel/setup.c.orig	Sun Mar 23 22:32:53 1997
+++ linux-2.1.29/arch/m68k/kernel/setup.c	Mon Mar 31 21:20:08 1997
@@ -80,7 +80,6 @@
 int (*mach_set_clock_mmss) (unsigned long) = NULL;
 void (*mach_reset)( void );
 void (*waitbut)(void) = dummy_waitbut;
-struct fb_info *(*mach_fb_init)(long *);
 long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */
 void (*(*mach_debug_init)(void))(const char*, unsigned int) = NULL;
 void (*mach_video_setup) (char *, int *);
--- linux-2.1.29/arch/m68k/console/fbcon.c.orig	Mon Mar 31 13:31:58 1997
+++ linux-2.1.29/arch/m68k/console/fbcon.c	Wed Apr  2 21:32:59 1997
@@ -153,6 +153,25 @@
 #endif
 #endif /* CONFIG_ATARI */
 
+/* Virtual Frame Buffer (1, 8 and 16 bpp) */
+
+#ifdef CONFIG_FB_VIRTUAL
+#ifndef CONFIG_FBCON_8PACKED
+#define CONFIG_FBCON_8PACKED
+#endif
+#ifndef CONFIG_FBCON_16PACKED
+#define CONFIG_FBCON_16PACKED
+#endif
+#if 0
+/* No support for 24 or 32 bpp yet */
+#ifndef CONFIG_FBCON_24PACKED
+#define CONFIG_FBCON_24PACKED
+#endif
+#ifndef CONFIG_FBCON_32PACKED
+#define CONFIG_FBCON_32PACKED
+#endif
+#endif
+#endif /* CONFIG_FB_VIRTUAL */
 
 /* Extra definitions to make the code more readable */
 
@@ -172,8 +191,7 @@
 #endif
 
 
-static struct fb_info *fb_info;
-static struct display *disp;
+struct display fb_display[MAX_NR_CONSOLES];
 
 
 /* ++Geert: Sorry, no hardware cursor support at the moment;
@@ -257,6 +275,19 @@
 
 
    /*
+    *    Frame buffer device initialization routines
+    */
+
+extern int amiga_fb_init(u_long *mem_start);
+extern int amiga_fb_init(u_long *mem_start);
+extern int atari_fb_init(u_long *mem_start);
+extern int Cyber_fb_init(u_long *mem_start);
+extern int retz3_fb_init(u_long *mem_start);
+extern int clgen_fb_init(u_long *mem_start);
+extern int virtual_fb_init(u_long *mem_start);
+
+
+   /*
     *    Internal routines
     */
 
@@ -558,11 +589,37 @@
 static u_long fbcon_startup(u_long kmem_start, const char **display_desc)
 {
    int irqres = 0;
+   int found = 0;
 
-   fb_info = mach_fb_init(&kmem_start);
-   disp = fb_info->disp;
-   *display_desc = fb_info->modename;
-   fb_info->changevar = &fbcon_changevar;
+   /* Probe all frame buffer devices */
+#ifdef CONFIG_AMIGA
+   if (amiga_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+#ifdef CONFIG_ATARI
+   if (atari_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+#ifdef CONFIG_FB_CYBER
+   if (Cyber_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+#ifdef CONFIG_FB_RETINAZ3
+   if (retz3_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+#ifdef CONFIG_FB_CLGEN
+   if (clgen_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+#ifdef CONFIG_FB_VIRTUAL
+   if (virtual_fb_init(&kmem_start) == 0)
+      found = 1;
+#endif
+   if (!found)
+      panic("fbcon_startup: Couldn't find a frame buffer device\n");
+
+   *display_desc = "frame buffer device";
 
 #ifdef CONFIG_AMIGA
    if (MACH_IS_AMIGA) {
@@ -589,38 +646,46 @@
 static void fbcon_init(struct vc_data *conp)
 {
    int unit = conp->vc_num;
+   struct fb_info *info;
+
+   /* on which frame buffer will we open this console? */
+   info = registered_fb[(int)con2fb_map[unit]];
 
-   if (unit)
-      disp[unit] = disp[0];
-   disp[unit].conp = conp;
+   info->changevar = &fbcon_changevar;
+   fb_display[unit] = *(info->disp);	/* copy from default */
+   fb_display[unit].conp = conp;
+   fb_display[unit].fb_info = info;
    fbcon_setup(unit, 1, 1);
 }
 
 
 static int fbcon_deinit(struct vc_data *conp)
 {
-   disp[conp->vc_num].conp = 0;
+   int unit = conp->vc_num;
+
+   fb_display[unit].conp = 0;
    return(0);
 }
 
 
 static int fbcon_changevar(int con)
 {
-   fbcon_setup(con, 1, 0);
+   if (fb_display[con].conp)
+       fbcon_setup(con, 1, 0);
    return(0);
 }
 
 
 static void fbcon_setup(int con, int setcol, int init)
 {
-   struct display *p = &disp[con];
+   struct display *p = &fb_display[con];
    struct vc_data *conp = p->conp;
    int nr_rows, nr_cols;
 
    p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
 
-   if (!fb_info->fontname[0] ||
-       !findsoftfont(fb_info->fontname, &p->fontwidth, &p->fontheight,
+   if (!p->fb_info->fontname[0] ||
+       !findsoftfont(p->fb_info->fontname, &p->fontwidth, &p->fontheight,
                      &p->fontdata) || p->fontwidth != 8)
 	   getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth,
 	                  &p->fontheight, &p->fontdata);
@@ -1458,7 +1523,7 @@
                        int width)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
    u_int y_break;
 
    if (!p->can_soft_blank && console_blanked)
@@ -1485,7 +1550,7 @@
 static int fbcon_putc(struct vc_data *conp, int c, int y, int x)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
 
    if (!p->can_soft_blank && console_blanked)
       return(0);
@@ -1503,7 +1568,7 @@
                        int x)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
 
    if (!p->can_soft_blank && console_blanked)
       return(0);
@@ -1520,7 +1585,7 @@
 static int fbcon_cursor(struct vc_data *conp, int mode)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
 
    /* Avoid flickering if there's no real change. */
    if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
@@ -1558,7 +1623,7 @@
       /* Here no check is possible for console changing. The console
        * switching code should set vbl_cursor_cnt to an appropriate value.
        */
-      p = &disp[fg_console];
+      p = &fb_display[fg_console];
       p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
       cursor_drawn ^= 1;
       vbl_cursor_cnt = cursor_blink_rate;
@@ -1587,7 +1652,7 @@
    p->var.xoffset = 0;
    p->var.yoffset = p->yscroll*p->fontheight;
    p->var.vmode |= FB_VMODE_YWRAP;
-   fb_info->updatevar(unit);
+   p->fb_info->updatevar(unit);
 }
 
 
@@ -1599,7 +1664,7 @@
    p->var.xoffset = 0;
    p->var.yoffset = p->yscroll*p->fontheight;
    p->var.vmode |= FB_VMODE_YWRAP;
-   fb_info->updatevar(unit);
+   p->fb_info->updatevar(unit);
 }
 
 
@@ -1615,7 +1680,7 @@
    p->var.xoffset = 0;
    p->var.yoffset = p->yscroll*p->fontheight;
    p->var.vmode &= ~FB_VMODE_YWRAP;
-   fb_info->updatevar(unit);
+   p->fb_info->updatevar(unit);
 }
 
 
@@ -1631,14 +1696,14 @@
    p->var.xoffset = 0;
    p->var.yoffset = p->yscroll*p->fontheight;
    p->var.vmode &= ~FB_VMODE_YWRAP;
-   fb_info->updatevar(unit);
+   p->fb_info->updatevar(unit);
 }
 
 
 static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
 
    if (!p->can_soft_blank && console_blanked)
       return(0);
@@ -1764,7 +1829,7 @@
                        int height, int width)
 {
    int unit = conp->vc_num;
-   struct display *p = &disp[unit];
+   struct display *p = &fb_display[unit];
 
    if (!p->can_soft_blank && console_blanked)
       return(0);
@@ -1822,15 +1887,19 @@
 
 static int fbcon_switch(struct vc_data *conp)
 {
-   if (fb_info && fb_info->switch_con)
-      (*fb_info->switch_con)(conp->vc_num);
+   int unit = conp->vc_num;
+   struct display *p = &fb_display[unit];
+   struct fb_info *info = p->fb_info;
+
+   if (info && info->switch_con)
+      (*info->switch_con)(conp->vc_num);
    return(0);
 }
 
 
 static int fbcon_blank(int blank)
 {
-   struct display *p = &disp[fg_console];
+   struct display *p = &fb_display[fg_console];
 
    fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
 
@@ -1847,7 +1916,7 @@
          /* Tell console.c that it has to restore the screen itself */
          return(1);
       }
-   (*fb_info->blank)(blank);
+   (*p->fb_info->blank)(blank);
    return(0);
 }
 
@@ -1855,7 +1924,7 @@
 static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
 {
 	int unit = conp->vc_num;
-	struct display *p = &disp[unit];
+	struct display *p = &fb_display[unit];
 	int i, j, size, alloc;
 
 	size = (p->fontwidth+7)/8 * p->fontheight * 256;
@@ -1879,7 +1948,7 @@
 static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data)
 {
 	int unit = conp->vc_num;
-	struct display *p = &disp[unit];
+	struct display *p = &fb_display[unit];
 	int i, j, size, userspace = 1, resize;
 	char *old_data = NULL, *new_data;
 
@@ -1909,7 +1978,7 @@
 			return( -ENOTTY );
 		if (h == unit)
 			return( 0 ); /* nothing to do */
-		op = &disp[h];
+		op = &fb_display[h];
 		if (op->fontdata == p->fontdata)
 			return( 0 ); /* already the same font... */
 
@@ -1987,7 +2056,7 @@
 static int fbcon_set_palette(struct vc_data *conp, unsigned char *table)
 {
     int unit = conp->vc_num;
-    struct display *p = &disp[unit];
+    struct display *p = &fb_display[unit];
     int i, j, k;
     u_char val;
 
@@ -2005,7 +2074,7 @@
     palette_cmap.len = 1<<p->var.bits_per_pixel;
     if (palette_cmap.len > 16)
 	palette_cmap.len = 16;
-    return(fb_info->setcmap(&palette_cmap, unit));
+    return(p->fb_info->setcmap(&palette_cmap, unit));
 }
 
 
@@ -3825,8 +3894,8 @@
 
 	dest = p->screen_base+y*p->fontheight*p->next_line+8*x;
 	cdat = p->fontdata+(c*p->fontheight);
-	fg = disp->fgcol;
-	bg = disp->bgcol;
+	fg = p->fgcol;
+	bg = p->bgcol;
 	reverse = conp->vc_reverse;
 	underline = conp->vc_underline;
 
@@ -3863,8 +3932,8 @@
 	u_char fg, bg;
 
 	dest0 = p->screen_base+y*p->fontheight*p->next_line+8*x;
-	fg = disp->fgcol;
-	bg = disp->bgcol;
+	fg = p->fgcol;
+	bg = p->bgcol;
 	reverse = conp->vc_reverse;
 	underline = conp->vc_underline;
 
@@ -3903,8 +3972,8 @@
 	unsigned int rows;
 	unsigned char fg, bg;
 
-	fg = disp->fgcol;
-	bg = disp->bgcol;
+	fg = p->fgcol;
+	bg = p->bgcol;
 
 	dest = p->screen_base+y*p->fontheight*p->next_line+8*x;
    Cyber_WaitBlit();
@@ -3990,8 +4059,8 @@
 	dest = p->screen_base + y*p->fontheight*p->next_line + x*p->fontwidth;
 	cdat = p->fontdata + c * p->fontheight;
 
-	fg = disp->fgcol;
-	bg = disp->bgcol;
+	fg = p->fgcol;
+	bg = p->bgcol;
 	reverse = conp->vc_reverse;
 	underline = conp->vc_underline;
 
@@ -4027,8 +4096,8 @@
 	unsigned char fg, bg;
 
 	dest0 = p->screen_base + y*p->fontheight*p->next_line + x*p->fontwidth;
-	fg = disp->fgcol;
-	bg = disp->bgcol;
+	fg = p->fgcol;
+	bg = p->bgcol;
 	reverse = conp->vc_reverse;
 	underline = conp->vc_underline;
 
--- linux-2.1.29/arch/m68k/console/vfb.c.orig	Tue Apr  1 22:42:12 1997
+++ linux-2.1.29/arch/m68k/console/vfb.c	Wed Apr  2 22:37:10 1997
@@ -0,0 +1,770 @@
+/*
+ * linux/arch/m68k/kernel/vfb.c -- Virtual frame buffer device
+ *
+ *    Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <linux/fb.h>
+
+
+#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
+
+
+    /*
+     *  RAM we reserve for the Frame Buffer
+     *
+     *  This defines the Maximum Virtual Screen Size
+     *  (Setable per kernel options?)
+     */
+
+#define VIDEOMEMSIZE	(1*1024*1024)	/* 1 MB */
+
+static u_long videomemory, videomemorysize;
+static int currcon = 0;
+static struct display disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char virtual_fb_name[16] = "Virtual FB";
+
+static struct fb_var_screeninfo virtual_fb_predefined[] = {
+
+    /*
+     *  Autodetect (Default) Video Mode
+     */
+
+    {
+	/* 640x480, 8 bpp */
+	640, 480, 640, 480, 0, 0, 8, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2,
+	0, FB_VMODE_NONINTERLACED
+    },
+
+    /*
+     *  User Defined Video Modes (8)
+     */
+
+    { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
+
+#define NUM_USER_MODES		(8)
+#define NUM_TOTAL_MODES		arraysize(virtual_fb_predefined)
+#define NUM_PREDEF_MODES	(1)
+
+
+    /*
+     *  Default Colormaps
+     */
+
+static u_short red2[] =
+    { 0x0000, 0xc000 };
+static u_short green2[] =
+    { 0x0000, 0xc000 };
+static u_short blue2[] =
+    { 0x0000, 0xc000 };
+
+static u_short red16[] =
+    { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000,
+      0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff };
+static u_short green16[] =
+    { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000,
+      0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff };
+static u_short blue16[] =
+    { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000,
+      0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff };
+
+
+static struct fb_cmap default_2_colors =
+    { 0, 2, red2, green2, blue2, NULL };
+static struct fb_cmap default_16_colors =
+    { 0, 16, red16, green16, blue16, NULL };
+
+
+    /*
+     *  Interface used by the world
+     */
+
+void vfb_video_setup(char *options, int *ints);
+
+static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con, int fbidx);
+static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con, int fbidx);
+static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con, int fbidx);
+static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con, int fbidx);
+static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx);
+static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, int fbidx);
+static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                        u_long arg, int con, int fbidx);
+static int virtual_fb_get_monitorspec(struct fb_monitorspec *spec, int val, int fbidx);
+static int virtual_fb_put_monitorspec(struct fb_monitorspec *spec, int val, int fbidx);
+
+
+    /*
+     *  Interface to the low level console driver
+     */
+
+int virtual_fb_init(u_long *mem_start);
+static int vfbcon_switch(int con);
+static int vfbcon_updatevar(int con);
+static void vfbcon_blank(int blank);
+static int vfbcon_setcmap(struct fb_cmap *cmap, int con);
+
+
+    /*
+     *  Internal routines
+     */
+
+static u_long get_line_length(int xres_virtual, int bpp);
+static void vfb_encode_fix(struct fb_fix_screeninfo *fix,
+			   struct fb_var_screeninfo *var);
+static void set_color_bitfields(struct fb_var_screeninfo *var);
+static struct fb_cmap *get_default_colormap(int bpp);
+static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc);
+static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc);
+static void do_install_cmap(int con);
+static void memcpy_fs(int fsfromto, void *to, void *from, int len);
+static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto);
+static int alloc_cmap(struct fb_cmap *cmap, int len, int transp);
+
+
+static struct fb_ops virtual_fb_ops = {
+    virtual_fb_get_fix, virtual_fb_get_var, virtual_fb_set_var,
+    virtual_fb_get_cmap, virtual_fb_set_cmap, virtual_fb_pan_display,
+    virtual_fb_ioctl, virtual_fb_get_monitorspec, virtual_fb_put_monitorspec
+};
+
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			      int fbidx)
+{
+    struct fb_var_screeninfo *var;
+
+    if (con == -1)
+	var = &virtual_fb_predefined[0];
+    else
+	var = &fb_display[con].var;
+    vfb_encode_fix(fix, var);
+    return 0;
+}
+
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con,
+			      int fbidx)
+{
+    if (con == -1)
+	*var = virtual_fb_predefined[0];
+    else
+	*var = fb_display[con].var;
+    set_color_bitfields(var);
+    return 0;
+}
+
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con,
+			      int fbidx)
+{
+    int err, activate = var->activate;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+    u_long line_length;
+
+    struct display *display;
+    if (con >= 0)
+	display = &fb_display[con];
+    else
+	display = &disp;	/* used during initialization */
+
+    /*
+     *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+     *  as FB_VMODE_SMOOTH_XPAN is only used internally
+     */
+
+    if (var->vmode & FB_VMODE_CONUPDATE) {
+	var->vmode |= FB_VMODE_YWRAP;
+	var->xoffset = display->var.xoffset;
+	var->yoffset = display->var.yoffset;
+    }
+
+    /*
+     *  Some very basic checks
+     */
+    if (!var->xres)
+	var->xres = 1;
+    if (!var->yres)
+	var->yres = 1;
+    if (var->xres > var->xres_virtual)
+	var->xres_virtual = var->xres;
+    if (var->yres > var->yres_virtual)
+	var->yres_virtual = var->yres;
+    if (var->bits_per_pixel <= 1)
+	var->bits_per_pixel = 1;
+    else if (var->bits_per_pixel <= 8)
+	var->bits_per_pixel = 8;
+    else if (var->bits_per_pixel <= 16)
+	var->bits_per_pixel = 16;
+#if 0
+    /* fbcon doesn't support this (yet) */
+    else if (var->bits_per_pixel <= 24)
+	var->bits_per_pixel = 24;
+    else if (var->bits_per_pixel <= 32)
+	var->bits_per_pixel = 32;
+#endif
+    else
+	return -EINVAL;
+
+    /*
+     *  Memory limit
+     */
+    line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+    if (line_length*var->yres_virtual > videomemorysize)
+	return -ENOMEM;
+
+    set_color_bitfields(var);
+
+    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+	oldxres = display->var.xres;
+	oldyres = display->var.yres;
+	oldvxres = display->var.xres_virtual;
+	oldvyres = display->var.yres_virtual;
+	oldbpp = display->var.bits_per_pixel;
+	display->var = *var;
+	if (oldxres != var->xres || oldyres != var->yres ||
+	    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+	    oldbpp != var->bits_per_pixel) {
+	    struct fb_fix_screeninfo fix;
+
+	    vfb_encode_fix(&fix, var);
+	    display->screen_base = (u_char *)fix.smem_start;
+	    display->visual = fix.visual;
+	    display->type = fix.type;
+	    display->type_aux = fix.type_aux;
+	    display->ypanstep = fix.ypanstep;
+	    display->ywrapstep = fix.ywrapstep;
+	    display->line_length = fix.line_length;
+	    display->can_soft_blank = 1;
+	    display->inverse = 0;
+	    if (fb_info.changevar)
+		(*fb_info.changevar)(con);
+	}
+	if (oldbpp != var->bits_per_pixel) {
+	    if ((err = alloc_cmap(&display->cmap, 0, 0)))
+		return err;
+	    do_install_cmap(con);
+	}
+    }
+    return 0;
+}
+
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con, int fbidx)
+{
+    if (var->vmode & FB_VMODE_YWRAP) {
+	if (var->yoffset < 0 ||
+	    var->yoffset >= fb_display[con].var.yres_virtual ||
+	    var->xoffset)
+	    return -EINVAL;
+    } else {
+	if (var->xoffset+fb_display[con].var.xres >
+	    fb_display[con].var.xres_virtual ||
+	    var->yoffset+fb_display[con].var.yres >
+	    fb_display[con].var.yres_virtual)
+	    return -EINVAL;
+    }
+    fb_display[con].var.xoffset = var->xoffset;
+    fb_display[con].var.yoffset = var->yoffset;
+    if (var->vmode & FB_VMODE_YWRAP)
+	fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+    else
+	fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+    return 0;
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			       int fbidx)
+{
+    if (con == currcon) /* current console? */
+	return do_fb_get_cmap(cmap, &fb_display[con].var, kspc);
+    else if (fb_display[con].cmap.len) /* non default colormap? */
+	copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+    else
+	copy_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
+		  cmap, kspc ? 0 : 2);
+    return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			       int fbidx)
+{
+    int err;
+
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+	if ((err = alloc_cmap(&fb_display[con].cmap,
+			      1<<fb_display[con].var.bits_per_pixel, 0)))
+	    return err;
+    }
+    if (con == currcon)			/* current console? */
+	return do_fb_set_cmap(cmap, &fb_display[con].var, kspc);
+    else
+	copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+    return 0;
+}
+
+
+    /*
+     *  Virtual Frame Buffer Specific ioctls
+     */
+
+static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			    u_long arg, int con, int fbidx)
+{
+    return -EINVAL;
+}
+
+    /*
+     *  Monitor limit functions
+     */
+
+static int virtual_fb_get_monitorspec(struct fb_monitorspec *spec, int val,
+				      int fbidx)
+{
+    return -EINVAL;
+}
+
+static int virtual_fb_put_monitorspec(struct fb_monitorspec *spec, int val,
+				      int fbidx)
+{
+    return -EINVAL;
+}
+
+
+    /*
+     *  Initialisation
+     */
+
+int virtual_fb_init(u_long *mem_start)
+{
+    int err;
+
+    videomemorysize = VIDEOMEMSIZE;
+    videomemory = *mem_start;
+    *mem_start += videomemorysize;
+    if (!videomemory)
+	return -ENOMEM;
+
+    strcpy(fb_info.modename, virtual_fb_name);
+    fb_info.changevar = NULL;
+    fb_info.node = -1;
+    fb_info.fbops = &virtual_fb_ops;
+    fb_info.fbvar_num = NUM_TOTAL_MODES;
+    fb_info.fbvar = virtual_fb_predefined;
+    fb_info.disp = &disp;
+    fb_info.switch_con = &vfbcon_switch;
+    fb_info.updatevar = &vfbcon_updatevar;
+    fb_info.blank = &vfbcon_blank;
+    fb_info.setcmap = &vfbcon_setcmap;
+
+    err = register_framebuffer(&fb_info);
+    if (err < 0)
+	return(err);
+
+    virtual_fb_set_var(&virtual_fb_predefined[0], -1,
+    		       GET_FB_IDX(fb_info.node));
+
+    printk("Virtual frame buffer device, using %ldK of video memory\n",
+	   videomemorysize>>10);
+    return 0;
+}
+
+
+static int vfbcon_switch(int con)
+{
+    /* Do we have to save the colormap? */
+    if (fb_display[currcon].cmap.len)
+	    do_fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1);
+
+    currcon = con;
+    /* Install new colormap */
+    do_install_cmap(con);
+    return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int vfbcon_updatevar(int con)
+{
+    /* Nothing */
+    return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void vfbcon_blank(int blank)
+{
+    /* Nothing */
+}
+
+    /*
+     *  Set the colormap
+     */
+
+static int vfbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+    return(virtual_fb_set_cmap(cmap, 1, con, GET_FB_IDX(fb_info.node)));
+}
+
+
+static u_long get_line_length(int xres_virtual, int bpp)
+{
+    u_long length;
+    
+    length = (xres_virtual+bpp-1)/bpp;
+    length = (length+31)&-32;
+    length >>= 3;
+    return(length);
+}
+
+static void vfb_encode_fix(struct fb_fix_screeninfo *fix,
+			   struct fb_var_screeninfo *var)
+{
+    strcpy(fix->id, virtual_fb_name);
+    fix->smem_start = videomemory;
+    fix->smem_len = videomemorysize;
+    fix->type = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux = 0;
+    switch (var->bits_per_pixel) {
+	case 1:
+	    fix->visual = FB_VISUAL_MONO01;
+	    break;
+	case 8:
+	    fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	    break;
+	case 16:
+	case 24:
+	case 32:
+	    fix->visual = FB_VISUAL_TRUECOLOR;
+	    break;
+    }
+    fix->ywrapstep = 1;
+    fix->xpanstep = 1;
+    fix->ypanstep = 1;
+    fix->line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+    memset(fix->reserved, 0, sizeof(fix->reserved));
+}
+
+static void set_color_bitfields(struct fb_var_screeninfo *var)
+{
+    switch (var->bits_per_pixel) {
+	case 1:
+	case 8:
+	    var->red.offset = 0;
+	    var->red.length = 8;
+	    var->green.offset = 0;
+	    var->green.length = 8;
+	    var->blue.offset = 0;
+	    var->blue.length = 8;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+	case 16:	/* RGB 565 */
+	    var->red.offset = 0;
+	    var->red.length = 5;
+	    var->green.offset = 5;
+	    var->green.length = 6;
+	    var->blue.offset = 11;
+	    var->blue.length = 5;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+	case 24:	/* RGB 888 */
+	    var->red.offset = 0;
+	    var->red.length = 8;
+	    var->green.offset = 8;
+	    var->green.length = 8;
+	    var->blue.offset = 16;
+	    var->blue.length = 8;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+	case 32:	/* RGBA 8888 */
+	    var->red.offset = 0;
+	    var->red.length = 8;
+	    var->green.offset = 8;
+	    var->green.length = 8;
+	    var->blue.offset = 16;
+	    var->blue.length = 8;
+	    var->transp.offset = 24;
+	    var->transp.length = 8;
+	    break;
+    }
+    var->red.msb_right = 0;
+    var->green.msb_right = 0;
+    var->blue.msb_right = 0;
+    var->transp.msb_right = 0;
+}
+
+
+static struct fb_cmap *get_default_colormap(int bpp)
+{
+    switch (bpp) {
+	case 1:
+	    return &default_2_colors;
+	    break;
+	default:
+	    return &default_16_colors;
+	    break;
+    }
+}
+
+#define CNVT_TOHW(val,width)	((((val)<<(width))+0x7fff-(val))>>16)
+#define CNVT_FROMHW(val,width)	(((width) ? ((((val)<<16)-(val)) / \
+                                            ((1<<(width))-1)) : 0))
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp)
+{
+    if (regno > 255)
+	return 1;
+    *red = palette[regno].red;
+    *green = palette[regno].green;
+    *blue = palette[regno].blue;
+    return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp)
+{
+    if (regno > 255)
+	return 1;
+    palette[regno].red = red;
+    palette[regno].green = green;
+    palette[regno].blue = blue;
+    return 0;
+}
+
+
+static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc)
+{
+    int i, start;
+    u_short *red, *green, *blue, *transp;
+    u_int hred, hgreen, hblue, htransp;
+
+    red = cmap->red;
+    green = cmap->green;
+    blue = cmap->blue;
+    transp = cmap->transp;
+    start = cmap->start;
+    if (start < 0)
+	    return -EINVAL;
+    for (i = 0; i < cmap->len; i++) {
+	if (vfb_getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
+	    return 0;
+	hred = CNVT_FROMHW(hred, var->red.length);
+	hgreen = CNVT_FROMHW(hgreen, var->green.length);
+	hblue = CNVT_FROMHW(hblue, var->blue.length);
+	htransp = CNVT_FROMHW(htransp, var->transp.length);
+	if (kspc) {
+	    *red = hred;
+	    *green = hgreen;
+	    *blue = hblue;
+	    if (transp)
+		*transp = htransp;
+	} else {
+	    put_user(hred, red);
+	    put_user(hgreen, green);
+	    put_user(hblue, blue);
+	    if (transp)
+		put_user(htransp, transp);
+	}
+	red++;
+	green++;
+	blue++;
+	if (transp)
+	    transp++;
+    }
+    return 0;
+}
+
+static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc)
+{
+    int i, start;
+    u_short *red, *green, *blue, *transp;
+    u_int hred, hgreen, hblue, htransp;
+
+    red = cmap->red;
+    green = cmap->green;
+    blue = cmap->blue;
+    transp = cmap->transp;
+    start = cmap->start;
+
+    if (start < 0)
+	return -EINVAL;
+    for (i = 0; i < cmap->len; i++) {
+	if (kspc) {
+	    hred = *red;
+	    hgreen = *green;
+	    hblue = *blue;
+	    htransp = transp ? *transp : 0;
+	} else {
+	    get_user(hred, red);
+	    get_user(hgreen, green);
+	    get_user(hblue, blue);
+	    if (transp)
+		get_user(htransp, transp);
+	    else
+		htransp = 0;
+	}
+	hred = CNVT_TOHW(hred, var->red.length);
+	hgreen = CNVT_TOHW(hgreen, var->green.length);
+	hblue = CNVT_TOHW(hblue, var->blue.length);
+	htransp = CNVT_TOHW(htransp, var->transp.length);
+	red++;
+	green++;
+	blue++;
+	if (transp)
+	    transp++;
+	if (vfb_setcolreg(start++, hred, hgreen, hblue, htransp))
+	    return 0;
+    }
+    return 0;
+}
+
+static void do_install_cmap(int con)
+{
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	do_fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1);
+    else
+	do_fb_set_cmap(get_default_colormap(fb_display[con].var.bits_per_pixel),
+					    &fb_display[con].var, 1);
+}
+
+static void memcpy_fs(int fsfromto, void *to, void *from, int len)
+{
+    switch (fsfromto) {
+	case 0:
+	    memcpy(to, from, len);
+	    return;
+	case 1:
+	    copy_from_user(to, from, len);
+	    return;
+	case 2:
+	    copy_to_user(to, from, len);
+	    return;
+    }
+}
+
+static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+{
+    int size;
+    int tooff = 0, fromoff = 0;
+
+    if (to->start > from->start)
+	fromoff = to->start-from->start;
+    else
+	tooff = from->start-to->start;
+    size = to->len-tooff;
+    if (size > from->len-fromoff)
+	size = from->len-fromoff;
+    if (size < 0)
+	return;
+    size *= sizeof(u_short);
+    memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
+    memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
+    memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
+    if (from->transp && to->transp)
+	memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+}
+
+static int alloc_cmap(struct fb_cmap *cmap, int len, int transp)
+{
+    int size = len*sizeof(u_short);
+
+    if (cmap->len != len) {
+	if (cmap->red)
+	    kfree(cmap->red);
+	if (cmap->green)
+	    kfree(cmap->green);
+	if (cmap->blue)
+	    kfree(cmap->blue);
+	if (cmap->transp)
+	    kfree(cmap->transp);
+	cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
+	cmap->len = 0;
+	if (!len)
+	    return 0;
+	if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
+	    return -1;
+	if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
+	    return -1;
+	if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
+	    return -1;
+	if (transp) {
+	    if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
+		return -1;
+	} else
+	    cmap->transp = NULL;
+    }
+    cmap->start = 0;
+    cmap->len = len;
+    copy_cmap(get_default_colormap(len), cmap, 0);
+    return 0;
+}
--- linux-2.1.29/arch/m68k/console/Makefile.orig	Wed Sep 25 09:47:39 1996
+++ linux-2.1.29/arch/m68k/console/Makefile	Tue Apr  1 23:14:59 1997
@@ -13,6 +13,10 @@
 L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o
 M_OBJS =
 
+ifdef CONFIG_FB_VIRTUAL
+L_OBJS := $(L_OBJS) vfb.o
+endif
+
 ifdef CONFIG_AMIGA_GSP
 L_OBJS := $(L_OBJS) gspcon.o gspcore.o
 endif
--- linux-2.1.29/arch/m68k/config.in.orig	Wed Mar 19 21:23:21 1997
+++ linux-2.1.29/arch/m68k/config.in	Mon Mar 31 23:07:01 1997
@@ -67,6 +67,7 @@
 #    bool 'A2410 support' CONFIG_GSP_A2410
 #  fi
 fi
+bool 'Virtual Frame Buffer support' CONFIG_FB_VIRTUAL
 endmenu
 
 #
--- linux-2.1.29/arch/m68k/defconfig.orig	Tue Mar 18 23:50:12 1997
+++ linux-2.1.29/arch/m68k/defconfig	Mon Mar 31 23:07:23 1997
@@ -49,6 +49,7 @@
 # CONFIG_AMIGA_GSP is not set
 # CONFIG_GSP_RESOLVER is not set
 # CONFIG_GSP_A2410 is not set
+# CONFIG_FB_VIRTUAL is not set
 
 #
 # Block device driver configuration
--- linux-2.1.29/drivers/char/fbmem.c.orig	Wed Mar 19 22:04:37 1997
+++ linux-2.1.29/drivers/char/fbmem.c	Wed Apr  2 23:01:02 1997
@@ -24,22 +24,16 @@
 
 #include <linux/fb.h>
 
-#define FB_MAJOR	29
-
-#define FB_MODES_SHIFT    5	/* 32 modes per framebuffer */
-#define FB_NUM_MINORS 	256	/* 256 Minors		    */
-#define FB_MAX		(FB_NUM_MINORS / (1 << FB_MODES_SHIFT))
-
 #define GET_INODE(i) MKDEV(FB_MAJOR, (i) << FB_MODES_SHIFT)
-#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
 #define GET_FB_VAR_IDX(node) (MINOR(node) & ((1 << FB_MODES_SHIFT)-1)) 
 
-struct fb_ops *registered_fb[FB_MAX];
-struct fb_var_screeninfo *registered_fb_var[FB_MAX];
-int registered_fb_var_num[FB_MAX];
+struct fb_info *registered_fb[FB_MAX];
+static int num_registered_fb = 0;
 int fb_curr_open[FB_MAX];
 int fb_open_count[FB_MAX];
 
+char con2fb_map[MAX_NR_CONSOLES];
+
 static inline int PROC_CONSOLE(void)
 {
 	if (!current->tty)
@@ -59,7 +53,7 @@
 fb_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
 {
 	unsigned long p = file->f_pos;
-	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)];
+	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]->fbops;
 	struct fb_fix_screeninfo fix;
 	char *base_addr;
 	int copy_size;
@@ -81,7 +75,7 @@
 	 unsigned long count)
 {
 	unsigned long p = file->f_pos;
-	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)];
+	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]->fbops;
 	struct fb_fix_screeninfo fix;
 	char *base_addr;
 	int copy_size;
@@ -98,16 +92,31 @@
 }
 
 
+static void set_con2fb_map(int con, int fb)
+{
+    struct fb_info *old;
+
+    if (fb != con2fb_map[con]) {
+	old = registered_fb[(int)con2fb_map[con]];
+	con2fb_map[con] = fb;
+	if (!registered_fb[fb]->changevar)
+	    registered_fb[fb]->changevar = old->changevar;
+	/* tell console var has changed */
+	if (registered_fb[fb]->changevar)
+	    registered_fb[fb]->changevar(con);
+    }
+}
+
 static int 
 fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	 unsigned long arg)
 {
-	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)];
+	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]->fbops;
 	struct fb_cmap cmap;
 	struct fb_var_screeninfo var;
 	struct fb_fix_screeninfo fix;
 	struct fb_monitorspec mon;
-
+	struct fb_con2fbmap con2fb;
 	int i,fbidx,vidx;
 	
 	if (! fb)
@@ -122,7 +131,7 @@
 		if (! vidx) /* ask device driver for current */
 			i=fb->fb_get_var(&var, PROC_CONSOLE(), fbidx);
 		else
-			var=registered_fb_var[fbidx][vidx-1];
+			var=registered_fb[fbidx]->fbvar[vidx-1];
 		copy_to_user((void *) arg, &var, sizeof(var));
 		return i;
 	case FBIOPUT_VSCREENINFO:
@@ -135,7 +144,7 @@
 		copy_to_user((void *) arg, &var, sizeof(var));
 		vidx=GET_FB_VAR_IDX(inode->i_rdev);
 		if (! i && vidx)
-			registered_fb_var[fbidx][vidx-1]=var;
+			registered_fb[fbidx]->fbvar[vidx-1]=var;
 		return i;
 	case FBIOGET_FSCREENINFO:
 		i = verify_area(VERIFY_WRITE, (void *) arg, 
@@ -197,7 +206,7 @@
 		copy_to_user((void *) arg, &var, sizeof(var));
 		vidx=GET_FB_VAR_IDX(inode->i_rdev);
 		if (! i && vidx)
-			registered_fb_var[fbidx][vidx-1]=var;
+			registered_fb[fbidx]->fbvar[vidx-1]=var;
 		return i;
 	case FBIOGET_MONITORSPEC:
 		i = verify_area(VERIFY_WRITE, (void *) arg,
@@ -216,6 +225,33 @@
 		copy_from_user(&mon, (void *)arg, sizeof(mon));
 		i = fb->fb_put_monitorspec(&mon, PROC_CONSOLE(), fbidx);
 		return i;
+	case FBIOGET_CON2FBMAP:
+		i = verify_area(VERIFY_WRITE, (void *)arg,
+				sizeof(struct fb_con2fbmap));
+		if (i) return i;
+		copy_from_user(&con2fb, (void *)arg, sizeof(con2fb));
+		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+		    return -EINVAL;
+		con2fb.framebuffer = con2fb_map[con2fb.console-1];
+		copy_to_user((void *)arg, &con2fb, sizeof(con2fb));
+		return 0;
+	case FBIOPUT_CON2FBMAP:
+		i = verify_area(VERIFY_READ, (void *)arg,
+				sizeof(struct fb_con2fbmap));
+		if (i) return i;
+		copy_from_user(&con2fb, (void *)arg, sizeof(con2fb));
+		if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
+		    return -EINVAL;
+		if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX ||
+		    !registered_fb[con2fb.framebuffer])
+		    return -EINVAL;
+		if (con2fb.console != 0)
+		    set_con2fb_map(con2fb.console-1, con2fb.framebuffer);
+		else
+		    /* set them all */
+		    for (i = 0; i < MAX_NR_CONSOLES; i++)
+			set_con2fb_map(i, con2fb.framebuffer);
+		return 0;
 	default:
 		fbidx=GET_FB_IDX(inode->i_rdev);
 		return (fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(), fbidx));
@@ -225,7 +261,7 @@
 static int 
 fb_mmap(struct inode *inode, struct file *file, struct vm_area_struct * vma)
 {
-	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)];
+	struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]->fbops;
 	struct fb_fix_screeninfo fix;
 	int fbidx = GET_FB_IDX(inode->i_rdev);
 
@@ -255,17 +291,18 @@
 {
 	int fbidx=GET_FB_IDX(inode->i_rdev);
 	int vidx=GET_FB_VAR_IDX(inode->i_rdev);
-	struct fb_ops *fb = registered_fb[fbidx];
+	struct fb_ops *fb = registered_fb[fbidx]->fbops;
 	int err;
 	
 	if (! vidx)		/* fb?current always succeeds */ 
 		return 0;
-	if (vidx > registered_fb_var_num[fbidx])
+	if (vidx > registered_fb[fbidx]->fbvar_num)
 		return -EINVAL;
 	if (fb_curr_open[fbidx] && fb_curr_open[fbidx] != vidx)
 		return -EBUSY;
  	if (file->f_mode & 2) /* only set parameters if opened writeable */
-		if ((err=fb->fb_set_var(registered_fb_var[fbidx] + vidx-1, PROC_CONSOLE(), fbidx)))
+		if ((err=fb->fb_set_var(&registered_fb[fbidx]->fbvar[vidx-1],
+					PROC_CONSOLE(), fbidx)))
 			return err;
 	fb_curr_open[fbidx] = vidx;
 	fb_open_count[fbidx]++;
@@ -296,31 +333,37 @@
 	NULL		/* fsync 	*/
 };
 
+
 int
-register_framebuffer(char *id, int *node, struct fb_ops *fbops, int fbvar_num, 
-		     struct fb_var_screeninfo *fbvar)
+register_framebuffer(struct fb_info *fb_info)
 {
-	int i;
+	int i, j;
+	if (num_registered_fb == FB_MAX)
+		return -ENXIO;
 	for (i = 0 ; i < FB_MAX; i++)
 		if (! registered_fb[i])
 			break;
-	if (i == FB_MAX)
-		return -ENXIO;
-	registered_fb[i]=fbops;
-	registered_fb_var[i]=fbvar;
-	registered_fb_var_num[i]=fbvar_num;
-	*node=GET_INODE(i);
+	fb_info->node=GET_INODE(i);
+	registered_fb[i] = fb_info;
+	if (!num_registered_fb++)
+		for (j = 0; j < MAX_NR_CONSOLES; j++)
+			con2fb_map[j] = i;
 	return 0;
 }
 
 int
-unregister_framebuffer(int node)
+unregister_framebuffer(const struct fb_info *fb_info)
 {
-	int i=GET_FB_IDX(node);
+	int i, j;
+
+	i = GET_FB_IDX(fb_info->node);
+	for (j = 0; j < MAX_NR_CONSOLES; j++)
+		if (con2fb_map[j] == i)
+			return -EBUSY;
 	if (! registered_fb[i])
 		return -EINVAL; 
 	registered_fb[i]=NULL;
-	registered_fb_var[i]=NULL;
+	num_registered_fb--;
 	return 0;
 }
 
--- linux-2.1.29/include/linux/fb.h.orig	Wed Mar 19 21:22:00 1997
+++ linux-2.1.29/include/linux/fb.h	Wed Apr  2 22:04:36 1997
@@ -3,6 +3,13 @@
 
 /* Definitions of frame buffers						*/
 
+#define FB_MAJOR	29
+
+#define FB_MODES_SHIFT		5	/* 32 modes per framebuffer */
+#define FB_NUM_MINORS		256	/* 256 Minors               */
+#define FB_MAX			(FB_NUM_MINORS / (1 << FB_MODES_SHIFT))
+#define GET_FB_IDX(node)	(MINOR(node) >> FB_MODES_SHIFT)
+
 /* ioctls
    0x46 is 'F'								*/
 #define FBIOGET_VSCREENINFO 	0x4600
@@ -14,6 +21,9 @@
 #define FBIOGET_MONITORSPEC	0x4607
 #define FBIOPUT_MONITORSPEC	0x4608
 #define FBIOSWITCH_MONIBIT	0x4609
+#define FBIOGET_CON2FBMAP	0x460A
+#define FBIOPUT_CON2FBMAP	0x460B
+/* 0x460C-0x4610 are defined below */
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@@ -136,6 +146,11 @@
 	long vfmax;		/* highest vertical frequency in Hz */
 };
 
+struct fb_con2fbmap {
+	int console;
+	int framebuffer;
+};
+
 #ifdef __KERNEL__
 
 #include <linux/fs.h>
@@ -161,9 +176,6 @@
 	int (*fb_put_monitorspec)(struct fb_monitorspec *, int, int);
 };
 
-int register_framebuffer(char *, int *, struct fb_ops *, int, 
-			 struct fb_var_screeninfo *);
-int unregister_framebuffer(int);
 
    /*
     *    This is the interface between the low-level console driver and the
@@ -195,6 +207,7 @@
    /* Filled in by the low-level console driver */
 
    struct vc_data *conp;            /* pointer to console data */
+   struct fb_info *fb_info;         /* frame buffer for this console */
    int vrows;                       /* number of virtual rows */
    int cursor_x;                    /* current cursor position */
    int cursor_y;
@@ -214,7 +227,11 @@
 
 struct fb_info {
    char modename[40];               /* at boottime detected video mode */
-   struct display *disp;            /* pointer to display variables */
+   int node;
+   struct fb_ops *fbops;
+   int fbvar_num;
+   struct fb_var_screeninfo *fbvar;
+   struct display *disp;            /* initial display variable */
    char fontname[40];               /* default font name */
    int (*changevar)(int);           /* tell console var has changed */
    int (*switch_con)(int);          /* tell fb to switch consoles */
@@ -223,6 +240,13 @@
    int (*setcmap)(struct fb_cmap *, int); /* tell fb to set the colormap */
 };
 
+int register_framebuffer(struct fb_info *fb_info);
+int unregister_framebuffer(const struct fb_info *fb_info);
+
+extern struct display fb_display[MAX_NR_CONSOLES];	/* fbcon.c */
+extern struct fb_info *registered_fb[FB_MAX];		/* fbmem.c */
+extern char con2fb_map[MAX_NR_CONSOLES];		/* fbmem.c */
+
 #endif /* __KERNEL__ */
 
 #if 1
@@ -239,11 +263,11 @@
     *    Hardware Cursor
     */
 
-#define FBIOGET_FCURSORINFO     0x4607
-#define FBIOGET_VCURSORINFO     0x4608
-#define FBIOPUT_VCURSORINFO     0x4609
-#define FBIOGET_CURSORSTATE     0x460A
-#define FBIOPUT_CURSORSTATE     0x460B
+#define FBIOGET_FCURSORINFO     0x460C
+#define FBIOGET_VCURSORINFO     0x460D
+#define FBIOPUT_VCURSORINFO     0x460E
+#define FBIOGET_CURSORSTATE     0x460F
+#define FBIOPUT_CURSORSTATE     0x4610
 
 
 struct fb_fix_cursorinfo {
--- linux-2.1.29/include/asm-m68k/machdep.h.orig	Sun Mar 23 22:32:53 1997
+++ linux-2.1.29/include/asm-m68k/machdep.h	Mon Mar 31 21:20:28 1997
@@ -34,7 +34,6 @@
 extern unsigned long (*mach_hd_init) (unsigned long, unsigned long);
 extern void (*mach_hd_setup)(char *, int *);
 extern void (*waitbut)(void);
-extern struct fb_info *(*mach_fb_init)(long *);
 extern long mach_max_dma_address;
 extern void (*(*mach_debug_init)(void))(const char*, unsigned int);
 extern void (*mach_video_setup)(char *, int *);

Greetings,

						Geert

--
Geert Uytterhoeven                     Geert.Uytterhoeven@cs.kuleuven.ac.be
Wavelets, Linux/m68k on Amiga          http://www.cs.kuleuven.ac.be/~geert/
Department of Computer Science -- Katholieke Universiteit Leuven -- Belgium

