Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c
new file mode 100644
index 0000000..a304042
--- /dev/null
+++ b/drivers/video/cirrusfb.c
@@ -0,0 +1,3326 @@
+/*
+ * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
+ *
+ * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
+ *
+ * Contributors (thanks, all!)
+ *
+ *	David Eger:
+ *	Overhaul for Linux 2.6
+ *
+ *      Jeff Rugen:
+ *      Major contributions;  Motorola PowerStack (PPC and PCI) support,
+ *      GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
+ *
+ *	Geert Uytterhoeven:
+ *	Excellent code review.
+ *
+ *	Lars Hecking:
+ *	Amiga updates and testing.
+ *
+ * Original cirrusfb author:  Frank Neumann
+ *
+ * Based on retz3fb.c and cirrusfb.c:
+ *      Copyright (C) 1997 Jes Sorensen
+ *      Copyright (C) 1996 Frank Neumann
+ *
+ ***************************************************************
+ *
+ * Format this code with GNU indent '-kr -i8 -pcs' options.
+ *
+ * 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.
+ *
+ */
+
+#define CIRRUSFB_VERSION "2.0-pre2"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/selection.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_ZORRO
+#include <linux/zorro.h>
+#endif
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#endif
+#ifdef CONFIG_PPC_PREP
+#include <asm/processor.h>
+#define isPReP (_machine == _MACH_prep)
+#else
+#define isPReP 0
+#endif
+
+#include "video/vga.h"
+#include "video/cirrus.h"
+
+
+/*****************************************************************
+ *
+ * debugging and utility macros
+ *
+ */
+
+/* enable debug output? */
+/* #define CIRRUSFB_DEBUG 1 */
+
+/* disable runtime assertions? */
+/* #define CIRRUSFB_NDEBUG */
+
+/* debug output */
+#ifdef CIRRUSFB_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/* debugging assertions */
+#ifndef CIRRUSFB_NDEBUG
+#define assert(expr) \
+        if(!(expr)) { \
+        printk( "Assertion failed! %s,%s,%s,line=%d\n",\
+        #expr,__FILE__,__FUNCTION__,__LINE__); \
+        }
+#else
+#define assert(expr)
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define TRUE  1
+#define FALSE 0
+
+#define MB_ (1024*1024)
+#define KB_ (1024)
+
+#define MAX_NUM_BOARDS 7
+
+
+/*****************************************************************
+ *
+ * chipset information
+ *
+ */
+
+/* board types */
+typedef enum {
+	BT_NONE = 0,
+	BT_SD64,
+	BT_PICCOLO,
+	BT_PICASSO,
+	BT_SPECTRUM,
+	BT_PICASSO4,	/* GD5446 */
+	BT_ALPINE,	/* GD543x/4x */
+	BT_GD5480,
+	BT_LAGUNA,	/* GD546x */
+} cirrusfb_board_t;
+
+
+/*
+ * per-board-type information, used for enumerating and abstracting
+ * chip-specific information
+ * NOTE: MUST be in the same order as cirrusfb_board_t in order to
+ * use direct indexing on this array
+ * NOTE: '__initdata' cannot be used as some of this info
+ * is required at runtime.  Maybe separate into an init-only and
+ * a run-time table?
+ */
+static const struct cirrusfb_board_info_rec {
+	char *name;		/* ASCII name of chipset */
+	long maxclock[5];		/* maximum video clock */
+	/* for  1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
+	unsigned init_sr07 : 1;	/* init SR07 during init_vgachip() */
+	unsigned init_sr1f : 1; /* write SR1F during init_vgachip() */
+	unsigned scrn_start_bit19 : 1; /* construct bit 19 of screen start address */
+
+	/* initial SR07 value, then for each mode */
+	unsigned char sr07;
+	unsigned char sr07_1bpp;
+	unsigned char sr07_1bpp_mux;
+	unsigned char sr07_8bpp;
+	unsigned char sr07_8bpp_mux;
+
+	unsigned char sr1f;	/* SR1F VGA initial register value */
+} cirrusfb_board_info[] = {
+	[BT_SD64] = {
+		.name			= "CL SD64",
+		.maxclock		= {
+			/* guess */
+			/* the SD64/P4 have a higher max. videoclock */
+			140000, 140000, 140000, 140000, 140000,
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= TRUE,
+		.sr07			= 0xF0,
+		.sr07_1bpp		= 0xF0,
+		.sr07_8bpp		= 0xF1,
+		.sr1f			= 0x20
+	},
+	[BT_PICCOLO] = {
+		.name			= "CL Piccolo",
+		.maxclock		= {
+			/* guess */
+			90000, 90000, 90000, 90000, 90000
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= FALSE,
+		.sr07			= 0x80,
+		.sr07_1bpp		= 0x80,
+		.sr07_8bpp		= 0x81,
+		.sr1f			= 0x22
+	},
+	[BT_PICASSO] = {
+		.name			= "CL Picasso",
+		.maxclock		= {
+			/* guess */
+			90000, 90000, 90000, 90000, 90000
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= FALSE,
+		.sr07			= 0x20,
+		.sr07_1bpp		= 0x20,
+		.sr07_8bpp		= 0x21,
+		.sr1f			= 0x22
+	},
+	[BT_SPECTRUM] = {
+		.name			= "CL Spectrum",
+		.maxclock		= {
+			/* guess */
+			90000, 90000, 90000, 90000, 90000
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= FALSE,
+		.sr07			= 0x80,
+		.sr07_1bpp		= 0x80,
+		.sr07_8bpp		= 0x81,
+		.sr1f			= 0x22
+	},
+	[BT_PICASSO4] = {
+		.name			= "CL Picasso4",
+		.maxclock		= {
+			135100, 135100, 85500, 85500, 0
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= FALSE,
+		.scrn_start_bit19	= TRUE,
+		.sr07			= 0x20,
+		.sr07_1bpp		= 0x20,
+		.sr07_8bpp		= 0x21,
+		.sr1f			= 0
+	},
+	[BT_ALPINE] = {
+		.name			= "CL Alpine",
+		.maxclock		= {
+			/* for the GD5430.  GD5446 can do more... */
+			85500, 85500, 50000, 28500, 0
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= TRUE,
+		.sr07			= 0xA0,
+		.sr07_1bpp		= 0xA1,
+		.sr07_1bpp_mux		= 0xA7,
+		.sr07_8bpp		= 0xA1,
+		.sr07_8bpp_mux		= 0xA7,
+		.sr1f			= 0x1C
+	},
+	[BT_GD5480] = {
+		.name			= "CL GD5480",
+		.maxclock		= {
+			135100, 200000, 200000, 135100, 135100
+		},
+		.init_sr07		= TRUE,
+		.init_sr1f		= TRUE,
+		.scrn_start_bit19	= TRUE,
+		.sr07			= 0x10,
+		.sr07_1bpp		= 0x11,
+		.sr07_8bpp		= 0x11,
+		.sr1f			= 0x1C
+	},
+	[BT_LAGUNA] = {
+		.name			= "CL Laguna",
+		.maxclock		= {
+			/* guess */
+			135100, 135100, 135100, 135100, 135100,
+		},
+		.init_sr07		= FALSE,
+		.init_sr1f		= FALSE,
+		.scrn_start_bit19	= TRUE,
+	}
+};
+
+
+#ifdef CONFIG_PCI
+#define CHIP(id, btype) \
+	{ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_##id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
+
+static struct pci_device_id cirrusfb_pci_table[] = {
+	CHIP( CIRRUS_5436,	BT_ALPINE ),
+	CHIP( CIRRUS_5434_8,	BT_ALPINE ),
+	CHIP( CIRRUS_5434_4,	BT_ALPINE ),
+	CHIP( CIRRUS_5430,	BT_ALPINE ), /* GD-5440 has identical id */
+	CHIP( CIRRUS_7543,	BT_ALPINE ),
+	CHIP( CIRRUS_7548,	BT_ALPINE ),
+	CHIP( CIRRUS_5480,	BT_GD5480 ), /* MacPicasso probably */
+	CHIP( CIRRUS_5446,	BT_PICASSO4 ), /* Picasso 4 is a GD5446 */
+	CHIP( CIRRUS_5462,	BT_LAGUNA ), /* CL Laguna */
+	CHIP( CIRRUS_5464,	BT_LAGUNA ), /* CL Laguna 3D */
+	CHIP( CIRRUS_5465,	BT_LAGUNA ), /* CL Laguna 3DA*/
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
+#undef CHIP
+#endif /* CONFIG_PCI */
+
+
+#ifdef CONFIG_ZORRO
+static const struct zorro_device_id cirrusfb_zorro_table[] = {
+	{
+		.id		= ZORRO_PROD_HELFRICH_SD64_RAM,
+		.driver_data	= BT_SD64,
+	}, {
+		.id		= ZORRO_PROD_HELFRICH_PICCOLO_RAM,
+		.driver_data	= BT_PICCOLO,
+	}, {
+		.id		= ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
+		.driver_data	= BT_PICASSO,
+	}, {
+		.id		= ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
+		.driver_data	= BT_SPECTRUM,
+	}, {
+		.id		= ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
+		.driver_data	= BT_PICASSO4,
+	},
+	{ 0 }
+};
+
+static const struct {
+	zorro_id id2;
+	unsigned long size;
+} cirrusfb_zorro_table2[] = {
+	[BT_SD64] = {
+		.id2	= ZORRO_PROD_HELFRICH_SD64_REG,
+		.size	= 0x400000
+	},
+	[BT_PICCOLO] = {
+		.id2	= ZORRO_PROD_HELFRICH_PICCOLO_REG,
+		.size	= 0x200000
+	},
+	[BT_PICASSO] = {
+		.id2	= ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
+		.size	= 0x200000
+	},
+	[BT_SPECTRUM] = {
+		.id2	= ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
+		.size	= 0x200000
+	},
+	[BT_PICASSO4] = {
+		.id2	= 0,
+		.size	= 0x400000
+	}
+};
+#endif /* CONFIG_ZORRO */
+
+
+struct cirrusfb_regs {
+	__u32 line_length;	/* in BYTES! */
+	__u32 visual;
+	__u32 type;
+
+	long freq;
+	long nom;
+	long den;
+	long div;
+	long multiplexing;
+	long mclk;
+	long divMCLK;
+
+	long HorizRes;		/* The x resolution in pixel */
+	long HorizTotal;
+	long HorizDispEnd;
+	long HorizBlankStart;
+	long HorizBlankEnd;
+	long HorizSyncStart;
+	long HorizSyncEnd;
+
+	long VertRes;		/* the physical y resolution in scanlines */
+	long VertTotal;
+	long VertDispEnd;
+	long VertSyncStart;
+	long VertSyncEnd;
+	long VertBlankStart;
+	long VertBlankEnd;
+};
+
+
+
+#ifdef CIRRUSFB_DEBUG
+typedef enum {
+        CRT,
+        SEQ
+} cirrusfb_dbg_reg_class_t;
+#endif                          /* CIRRUSFB_DEBUG */
+
+
+
+
+/* info about board */
+struct cirrusfb_info {
+	struct fb_info *info;
+
+	u8 __iomem *fbmem;
+	u8 __iomem *regbase;
+	u8 __iomem *mem;
+	unsigned long size;
+	cirrusfb_board_t btype;
+	unsigned char SFR;	/* Shadow of special function register */
+
+	unsigned long fbmem_phys;
+	unsigned long fbregs_phys;
+
+	struct cirrusfb_regs currentmode;
+	int blank_mode;
+
+	u32	pseudo_palette[17];
+	struct { u8 red, green, blue, pad; } palette[256];
+
+#ifdef CONFIG_ZORRO
+	struct zorro_dev *zdev;
+#endif
+#ifdef CONFIG_PCI
+	struct pci_dev *pdev;
+#endif
+	void (*unmap)(struct cirrusfb_info *cinfo);
+};
+
+
+static unsigned cirrusfb_def_mode = 1;
+static int noaccel = 0;
+
+/*
+ *    Predefined Video Modes
+ */
+
+static const struct {
+	const char *name;
+	struct fb_var_screeninfo var;
+} cirrusfb_predefined[] = {
+	{
+		/* autodetect mode */
+		.name	= "Autodetect",
+	}, {
+		/* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
+		.name	= "640x480",
+		.var	= {
+			.xres		= 640,
+			.yres		= 480,
+			.xres_virtual	= 640,
+			.yres_virtual	= 480,
+			.bits_per_pixel	= 8,
+			.red		= { .length = 8 },
+			.green		= { .length = 8 },
+			.blue		= { .length = 8 },
+			.width		= -1,
+			.height		= -1,
+			.pixclock	= 40000,
+			.left_margin	= 48,
+			.right_margin	= 16,
+			.upper_margin	= 32,
+			.lower_margin	= 8,
+			.hsync_len	= 96,
+			.vsync_len	= 4,
+			.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+			.vmode		= FB_VMODE_NONINTERLACED
+		 }
+	}, {
+		/* 800x600, 48 kHz, 76 Hz, 50 MHz PixClock */
+		.name	= "800x600",
+		.var	= {
+			.xres		= 800,
+			.yres		= 600,
+			.xres_virtual	= 800,
+			.yres_virtual	= 600,
+			.bits_per_pixel	= 8,
+			.red		= { .length = 8 },
+			.green		= { .length = 8 },
+			.blue		= { .length = 8 },
+			.width		= -1,
+			.height		= -1,
+			.pixclock	= 20000,
+			.left_margin	= 128,
+			.right_margin	= 16,
+			.upper_margin	= 24,
+			.lower_margin	= 2,
+			.hsync_len	= 96,
+			.vsync_len	= 6,
+			.vmode		= FB_VMODE_NONINTERLACED
+		 }
+	}, {
+		/*
+		 * Modeline from XF86Config:
+		 * Mode "1024x768" 80  1024 1136 1340 1432  768 770 774 805
+		 */
+		/* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
+		.name	= "1024x768",
+		.var	= {
+			.xres		= 1024,
+			.yres		= 768,
+			.xres_virtual	= 1024,
+			.yres_virtual	= 768,
+			.bits_per_pixel	= 8,
+			.red		= { .length = 8 },
+			.green		= { .length = 8 },
+			.blue		= { .length = 8 },
+			.width		= -1,
+			.height		= -1,
+			.pixclock	= 12500,
+			.left_margin	= 144,
+			.right_margin	= 32,
+			.upper_margin	= 30,
+			.lower_margin	= 2,
+			.hsync_len	= 192,
+			.vsync_len	= 6,
+			.vmode		= FB_VMODE_NONINTERLACED
+		}
+	}
+};
+
+#define NUM_TOTAL_MODES    ARRAY_SIZE(cirrusfb_predefined)
+
+/****************************************************************************/
+/**** BEGIN PROTOTYPES ******************************************************/
+
+
+/*--- Interface used by the world ------------------------------------------*/
+static int cirrusfb_init (void);
+#ifndef MODULE
+static int cirrusfb_setup (char *options);
+#endif
+
+static int cirrusfb_open (struct fb_info *info, int user);
+static int cirrusfb_release (struct fb_info *info, int user);
+static int cirrusfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+			       unsigned blue, unsigned transp,
+			       struct fb_info *info);
+static int cirrusfb_check_var (struct fb_var_screeninfo *var,
+			       struct fb_info *info);
+static int cirrusfb_set_par (struct fb_info *info);
+static int cirrusfb_pan_display (struct fb_var_screeninfo *var,
+				 struct fb_info *info);
+static int cirrusfb_blank (int blank_mode, struct fb_info *info);
+static void cirrusfb_fillrect (struct fb_info *info, const struct fb_fillrect *region);
+static void cirrusfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+static void cirrusfb_imageblit(struct fb_info *info, const struct fb_image *image);
+
+/* function table of the above functions */
+static struct fb_ops cirrusfb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_open	= cirrusfb_open,
+	.fb_release	= cirrusfb_release,
+	.fb_setcolreg	= cirrusfb_setcolreg,
+	.fb_check_var	= cirrusfb_check_var,
+	.fb_set_par	= cirrusfb_set_par,
+	.fb_pan_display = cirrusfb_pan_display,
+	.fb_blank	= cirrusfb_blank,
+	.fb_fillrect	= cirrusfb_fillrect,
+	.fb_copyarea	= cirrusfb_copyarea,
+	.fb_imageblit	= cirrusfb_imageblit,
+	.fb_cursor	= soft_cursor,
+};
+
+/*--- Hardware Specific Routines -------------------------------------------*/
+static int cirrusfb_decode_var (const struct fb_var_screeninfo *var,
+				struct cirrusfb_regs *regs,
+				const struct fb_info *info);
+/*--- Internal routines ----------------------------------------------------*/
+static void init_vgachip (struct cirrusfb_info *cinfo);
+static void switch_monitor (struct cirrusfb_info *cinfo, int on);
+static void WGen (const struct cirrusfb_info *cinfo,
+		  int regnum, unsigned char val);
+static unsigned char RGen (const struct cirrusfb_info *cinfo, int regnum);
+static void AttrOn (const struct cirrusfb_info *cinfo);
+static void WHDR (const struct cirrusfb_info *cinfo, unsigned char val);
+static void WSFR (struct cirrusfb_info *cinfo, unsigned char val);
+static void WSFR2 (struct cirrusfb_info *cinfo, unsigned char val);
+static void WClut (struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
+		   unsigned char green,
+		   unsigned char blue);
+#if 0
+static void RClut (struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
+		   unsigned char *green,
+		   unsigned char *blue);
+#endif
+static void cirrusfb_WaitBLT (u8 __iomem *regbase);
+static void cirrusfb_BitBLT (u8 __iomem *regbase, int bits_per_pixel,
+			     u_short curx, u_short cury,
+			     u_short destx, u_short desty,
+			     u_short width, u_short height,
+			     u_short line_length);
+static void cirrusfb_RectFill (u8 __iomem *regbase, int bits_per_pixel,
+			       u_short x, u_short y,
+			       u_short width, u_short height,
+			       u_char color, u_short line_length);
+
+static void bestclock (long freq, long *best,
+		       long *nom, long *den,
+		       long *div, long maxfreq);
+
+#ifdef CIRRUSFB_DEBUG
+static void cirrusfb_dump (void);
+static void cirrusfb_dbg_reg_dump (caddr_t regbase);
+static void cirrusfb_dbg_print_regs (caddr_t regbase, cirrusfb_dbg_reg_class_t reg_class,...);
+static void cirrusfb_dbg_print_byte (const char *name, unsigned char val);
+#endif /* CIRRUSFB_DEBUG */
+
+/*** END   PROTOTYPES ********************************************************/
+/*****************************************************************************/
+/*** BEGIN Interface Used by the World ***************************************/
+
+static int opencount = 0;
+
+/*--- Open /dev/fbx ---------------------------------------------------------*/
+static int cirrusfb_open (struct fb_info *info, int user)
+{
+	if (opencount++ == 0)
+		switch_monitor (info->par, 1);
+	return 0;
+}
+
+/*--- Close /dev/fbx --------------------------------------------------------*/
+static int cirrusfb_release (struct fb_info *info, int user)
+{
+	if (--opencount == 0)
+		switch_monitor (info->par, 0);
+	return 0;
+}
+
+/**** END   Interface used by the World *************************************/
+/****************************************************************************/
+/**** BEGIN Hardware specific Routines **************************************/
+
+/* Get a good MCLK value */
+static long cirrusfb_get_mclk (long freq, int bpp, long *div)
+{
+	long mclk;
+
+	assert (div != NULL);
+
+	/* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
+	 * Assume a 64-bit data path for now.  The formula is:
+	 * ((B * PCLK * 2)/W) * 1.2
+	 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
+	mclk = ((bpp / 8) * freq * 2) / 4;
+	mclk = (mclk * 12) / 10;
+	if (mclk < 50000)
+		mclk = 50000;
+	DPRINTK ("Use MCLK of %ld kHz\n", mclk);
+
+	/* Calculate value for SR1F.  Multiply by 2 so we can round up. */
+	mclk = ((mclk * 16) / 14318);
+	mclk = (mclk + 1) / 2;
+	DPRINTK ("Set SR1F[5:0] to 0x%lx\n", mclk);
+
+	/* Determine if we should use MCLK instead of VCLK, and if so, what we
+	   * should divide it by to get VCLK */
+	switch (freq) {
+	case 24751 ... 25249:
+		*div = 2;
+		DPRINTK ("Using VCLK = MCLK/2\n");
+		break;
+	case 49501 ... 50499:
+		*div = 1;
+		DPRINTK ("Using VCLK = MCLK\n");
+		break;
+	default:
+		*div = 0;
+		break;
+	}
+
+	return mclk;
+}
+
+static int cirrusfb_check_var(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	struct cirrusfb_info *cinfo = info->par;
+	int nom, den;		/* translyting from pixels->bytes */
+	int yres, i;
+	static struct { int xres, yres; } modes[] =
+	{ { 1600, 1280 },
+	  { 1280, 1024 },
+	  { 1024, 768 },
+	  { 800, 600 },
+	  { 640, 480 },
+	  { -1, -1 } };
+
+	switch (var->bits_per_pixel) {
+	case 0 ... 1:
+		var->bits_per_pixel = 1;
+		nom = 4;
+		den = 8;
+		break;		/* 8 pixel per byte, only 1/4th of mem usable */
+	case 2 ... 8:
+		var->bits_per_pixel = 8;
+		nom = 1;
+		den = 1;
+		break;		/* 1 pixel == 1 byte */
+	case 9 ... 16:
+		var->bits_per_pixel = 16;
+		nom = 2;
+		den = 1;
+		break;		/* 2 bytes per pixel */
+	case 17 ... 24:
+		var->bits_per_pixel = 24;
+		nom = 3;
+		den = 1;
+		break;		/* 3 bytes per pixel */
+	case 25 ... 32:
+		var->bits_per_pixel = 32;
+		nom = 4;
+		den = 1;
+		break;		/* 4 bytes per pixel */
+	default:
+		printk ("cirrusfb: mode %dx%dx%d rejected...color depth not supported.\n",
+			var->xres, var->yres, var->bits_per_pixel);
+		DPRINTK ("EXIT - EINVAL error\n");
+		return -EINVAL;
+	}
+
+	if (var->xres * nom / den * var->yres > cinfo->size) {
+		printk ("cirrusfb: mode %dx%dx%d rejected...resolution too high to fit into video memory!\n",
+			var->xres, var->yres, var->bits_per_pixel);
+		DPRINTK ("EXIT - EINVAL error\n");
+		return -EINVAL;
+	}
+
+	/* use highest possible virtual resolution */
+	if (var->xres_virtual == -1 &&
+	    var->yres_virtual == -1) {
+		printk ("cirrusfb: using maximum available virtual resolution\n");
+		for (i = 0; modes[i].xres != -1; i++) {
+			if (modes[i].xres * nom / den * modes[i].yres < cinfo->size / 2)
+				break;
+		}
+		if (modes[i].xres == -1) {
+			printk ("cirrusfb: could not find a virtual resolution that fits into video memory!!\n");
+			DPRINTK ("EXIT - EINVAL error\n");
+			return -EINVAL;
+		}
+		var->xres_virtual = modes[i].xres;
+		var->yres_virtual = modes[i].yres;
+
+		printk ("cirrusfb: virtual resolution set to maximum of %dx%d\n",
+			var->xres_virtual, var->yres_virtual);
+	}
+
+	if (var->xres_virtual < var->xres)
+		var->xres_virtual = var->xres;
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+
+	if (var->xoffset < 0)
+		var->xoffset = 0;
+	if (var->yoffset < 0)
+		var->yoffset = 0;
+
+	/* truncate xoffset and yoffset to maximum if too high */
+	if (var->xoffset > var->xres_virtual - var->xres)
+		var->xoffset = var->xres_virtual - var->xres - 1;
+	if (var->yoffset > var->yres_virtual - var->yres)
+		var->yoffset = var->yres_virtual - var->yres - 1;
+
+	switch (var->bits_per_pixel) {
+	case 1:
+		var->red.offset = 0;
+		var->red.length = 1;
+		var->green.offset = 0;
+		var->green.length = 1;
+		var->blue.offset = 0;
+		var->blue.length = 1;
+		break;
+
+	case 8:
+		var->red.offset = 0;
+		var->red.length = 6;
+		var->green.offset = 0;
+		var->green.length = 6;
+		var->blue.offset = 0;
+		var->blue.length = 6;
+		break;
+
+	case 16:
+		if(isPReP) {
+			var->red.offset = 2;
+			var->green.offset = -3;
+			var->blue.offset = 8;
+		} else {
+			var->red.offset = 10;
+			var->green.offset = 5;
+			var->blue.offset = 0;
+		}
+		var->red.length = 5;
+		var->green.length = 5;
+		var->blue.length = 5;
+		break;
+
+	case 24:
+		if(isPReP) {
+			var->red.offset = 8;
+			var->green.offset = 16;
+			var->blue.offset = 24;
+		} else {
+			var->red.offset = 16;
+			var->green.offset = 8;
+			var->blue.offset = 0;
+		}
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+
+	case 32:
+		if(isPReP) {
+			var->red.offset = 8;
+			var->green.offset = 16;
+			var->blue.offset = 24;
+		} else {
+			var->red.offset = 16;
+			var->green.offset = 8;
+			var->blue.offset = 0;
+		}
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+
+	default:
+		DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
+		assert (FALSE);
+		/* should never occur */
+		break;
+	}
+
+	var->red.msb_right =
+	    var->green.msb_right =
+	    var->blue.msb_right =
+	    var->transp.offset =
+	    var->transp.length =
+	    var->transp.msb_right = 0;
+
+	yres = var->yres;
+	if (var->vmode & FB_VMODE_DOUBLE)
+		yres *= 2;
+	else if (var->vmode & FB_VMODE_INTERLACED)
+		yres = (yres + 1) / 2;
+
+	if (yres >= 1280) {
+		printk (KERN_WARNING "cirrusfb: ERROR: VerticalTotal >= 1280; special treatment required! (TODO)\n");
+		DPRINTK ("EXIT - EINVAL error\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cirrusfb_decode_var (const struct fb_var_screeninfo *var,
+				struct cirrusfb_regs *regs,
+				const struct fb_info *info)
+{
+	long freq;
+	long maxclock;
+	int maxclockidx = 0;
+	struct cirrusfb_info *cinfo = info->par;
+	int xres, hfront, hsync, hback;
+	int yres, vfront, vsync, vback;
+
+	switch(var->bits_per_pixel) {
+	case 1:
+		regs->line_length = var->xres_virtual / 8;
+		regs->visual = FB_VISUAL_MONO10;
+		maxclockidx = 0;
+		break;
+
+	case 8:
+		regs->line_length = var->xres_virtual;
+		regs->visual = FB_VISUAL_PSEUDOCOLOR;
+		maxclockidx = 1;
+		break;
+
+	case 16:
+		regs->line_length = var->xres_virtual * 2;
+		regs->visual = FB_VISUAL_DIRECTCOLOR;
+		maxclockidx = 2;
+		break;
+
+	case 24:
+		regs->line_length = var->xres_virtual * 3;
+		regs->visual = FB_VISUAL_DIRECTCOLOR;
+		maxclockidx = 3;
+		break;
+
+	case 32:
+		regs->line_length = var->xres_virtual * 4;
+		regs->visual = FB_VISUAL_DIRECTCOLOR;
+		maxclockidx = 4;
+		break;
+
+	default:
+		DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
+		assert (FALSE);
+		/* should never occur */
+		break;
+	}
+
+	regs->type = FB_TYPE_PACKED_PIXELS;
+
+	/* convert from ps to kHz */
+	freq = 1000000000 / var->pixclock;
+
+	DPRINTK ("desired pixclock: %ld kHz\n", freq);
+
+	maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
+	regs->multiplexing = 0;
+
+	/* If the frequency is greater than we can support, we might be able
+	 * to use multiplexing for the video mode */
+	if (freq > maxclock) {
+		switch (cinfo->btype) {
+		case BT_ALPINE:
+		case BT_GD5480:
+			regs->multiplexing = 1;
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: ERROR: Frequency greater than maxclock (%ld kHz)\n", maxclock);
+			DPRINTK ("EXIT - return -EINVAL\n");
+			return -EINVAL;
+		}
+	}
+#if 0
+	/* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
+	 * the VCLK is double the pixel clock. */
+	switch (var->bits_per_pixel) {
+	case 16:
+	case 32:
+		if (regs->HorizRes <= 800)
+			freq /= 2;	/* Xbh has this type of clock for 32-bit */
+		break;
+	}
+#endif
+
+	bestclock (freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
+		   maxclock);
+	regs->mclk = cirrusfb_get_mclk (freq, var->bits_per_pixel, &regs->divMCLK);
+
+	xres = var->xres;
+	hfront = var->right_margin;
+	hsync = var->hsync_len;
+	hback = var->left_margin;
+
+	yres = var->yres;
+	vfront = var->lower_margin;
+	vsync = var->vsync_len;
+	vback = var->upper_margin;
+
+	if (var->vmode & FB_VMODE_DOUBLE) {
+		yres *= 2;
+		vfront *= 2;
+		vsync *= 2;
+		vback *= 2;
+	} else if (var->vmode & FB_VMODE_INTERLACED) {
+		yres = (yres + 1) / 2;
+		vfront = (vfront + 1) / 2;
+		vsync = (vsync + 1) / 2;
+		vback = (vback + 1) / 2;
+	}
+	regs->HorizRes = xres;
+	regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
+	regs->HorizDispEnd = xres / 8 - 1;
+	regs->HorizBlankStart = xres / 8;
+	regs->HorizBlankEnd = regs->HorizTotal + 5;	/* does not count with "-5" */
+	regs->HorizSyncStart = (xres + hfront) / 8 + 1;
+	regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
+
+	regs->VertRes = yres;
+	regs->VertTotal = yres + vfront + vsync + vback - 2;
+	regs->VertDispEnd = yres - 1;
+	regs->VertBlankStart = yres;
+	regs->VertBlankEnd = regs->VertTotal;
+	regs->VertSyncStart = yres + vfront - 1;
+	regs->VertSyncEnd = yres + vfront + vsync - 1;
+
+	if (regs->VertRes >= 1024) {
+		regs->VertTotal /= 2;
+		regs->VertSyncStart /= 2;
+		regs->VertSyncEnd /= 2;
+		regs->VertDispEnd /= 2;
+	}
+	if (regs->multiplexing) {
+		regs->HorizTotal /= 2;
+		regs->HorizSyncStart /= 2;
+		regs->HorizSyncEnd /= 2;
+		regs->HorizDispEnd /= 2;
+	}
+
+	return 0;
+}
+
+
+static void cirrusfb_set_mclk (const struct cirrusfb_info *cinfo, int val, int div)
+{
+	assert (cinfo != NULL);
+
+	if (div == 2) {
+		/* VCLK = MCLK/2 */
+		unsigned char old = vga_rseq (cinfo->regbase, CL_SEQR1E);
+		vga_wseq (cinfo->regbase, CL_SEQR1E, old | 0x1);
+		vga_wseq (cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
+	} else if (div == 1) {
+		/* VCLK = MCLK */
+		unsigned char old = vga_rseq (cinfo->regbase, CL_SEQR1E);
+		vga_wseq (cinfo->regbase, CL_SEQR1E, old & ~0x1);
+		vga_wseq (cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
+	} else {
+		vga_wseq (cinfo->regbase, CL_SEQR1F, val & 0x3f);
+	}
+}
+
+/*************************************************************************
+	cirrusfb_set_par_foo()
+
+	actually writes the values for a new video mode into the hardware,
+**************************************************************************/
+static int cirrusfb_set_par_foo (struct fb_info *info)
+{
+	struct cirrusfb_info *cinfo = info->par;
+	struct fb_var_screeninfo *var = &info->var;
+	struct cirrusfb_regs regs;
+	u8 __iomem *regbase = cinfo->regbase;
+	unsigned char tmp;
+	int offset = 0, err;
+	const struct cirrusfb_board_info_rec *bi;
+
+	DPRINTK ("ENTER\n");
+	DPRINTK ("Requested mode: %dx%dx%d\n",
+	       var->xres, var->yres, var->bits_per_pixel);
+	DPRINTK ("pixclock: %d\n", var->pixclock);
+
+	init_vgachip (cinfo);
+
+	err = cirrusfb_decode_var(var, &regs, info);
+	if(err) {
+		/* should never happen */
+		DPRINTK("mode change aborted.  invalid var.\n");
+		return -EINVAL;
+	}
+
+	bi = &cirrusfb_board_info[cinfo->btype];
+
+
+	/* unlock register VGA_CRTC_H_TOTAL..CRT7 */
+	vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, 0x20);	/* previously: 0x00) */
+
+	/* if debugging is enabled, all parameters get output before writing */
+	DPRINTK ("CRT0: %ld\n", regs.HorizTotal);
+	vga_wcrt (regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
+
+	DPRINTK ("CRT1: %ld\n", regs.HorizDispEnd);
+	vga_wcrt (regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
+
+	DPRINTK ("CRT2: %ld\n", regs.HorizBlankStart);
+	vga_wcrt (regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
+
+	DPRINTK ("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);	/*  + 128: Compatible read */
+	vga_wcrt (regbase, VGA_CRTC_H_BLANK_END, 128 + (regs.HorizBlankEnd % 32));
+
+	DPRINTK ("CRT4: %ld\n", regs.HorizSyncStart);
+	vga_wcrt (regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
+
+	tmp = regs.HorizSyncEnd % 32;
+	if (regs.HorizBlankEnd & 32)
+		tmp += 128;
+	DPRINTK ("CRT5: %d\n", tmp);
+	vga_wcrt (regbase, VGA_CRTC_H_SYNC_END, tmp);
+
+	DPRINTK ("CRT6: %ld\n", regs.VertTotal & 0xff);
+	vga_wcrt (regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
+
+	tmp = 16;		/* LineCompare bit #9 */
+	if (regs.VertTotal & 256)
+		tmp |= 1;
+	if (regs.VertDispEnd & 256)
+		tmp |= 2;
+	if (regs.VertSyncStart & 256)
+		tmp |= 4;
+	if (regs.VertBlankStart & 256)
+		tmp |= 8;
+	if (regs.VertTotal & 512)
+		tmp |= 32;
+	if (regs.VertDispEnd & 512)
+		tmp |= 64;
+	if (regs.VertSyncStart & 512)
+		tmp |= 128;
+	DPRINTK ("CRT7: %d\n", tmp);
+	vga_wcrt (regbase, VGA_CRTC_OVERFLOW, tmp);
+
+	tmp = 0x40;		/* LineCompare bit #8 */
+	if (regs.VertBlankStart & 512)
+		tmp |= 0x20;
+	if (var->vmode & FB_VMODE_DOUBLE)
+		tmp |= 0x80;
+	DPRINTK ("CRT9: %d\n", tmp);
+	vga_wcrt (regbase, VGA_CRTC_MAX_SCAN, tmp);
+
+	DPRINTK ("CRT10: %ld\n", regs.VertSyncStart & 0xff);
+	vga_wcrt (regbase, VGA_CRTC_V_SYNC_START, (regs.VertSyncStart & 0xff));
+
+	DPRINTK ("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
+	vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, (regs.VertSyncEnd % 16 + 64 + 32));
+
+	DPRINTK ("CRT12: %ld\n", regs.VertDispEnd & 0xff);
+	vga_wcrt (regbase, VGA_CRTC_V_DISP_END, (regs.VertDispEnd & 0xff));
+
+	DPRINTK ("CRT15: %ld\n", regs.VertBlankStart & 0xff);
+	vga_wcrt (regbase, VGA_CRTC_V_BLANK_START, (regs.VertBlankStart & 0xff));
+
+	DPRINTK ("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
+	vga_wcrt (regbase, VGA_CRTC_V_BLANK_END, (regs.VertBlankEnd & 0xff));
+
+	DPRINTK ("CRT18: 0xff\n");
+	vga_wcrt (regbase, VGA_CRTC_LINE_COMPARE, 0xff);
+
+	tmp = 0;
+	if (var->vmode & FB_VMODE_INTERLACED)
+		tmp |= 1;
+	if (regs.HorizBlankEnd & 64)
+		tmp |= 16;
+	if (regs.HorizBlankEnd & 128)
+		tmp |= 32;
+	if (regs.VertBlankEnd & 256)
+		tmp |= 64;
+	if (regs.VertBlankEnd & 512)
+		tmp |= 128;
+
+	DPRINTK ("CRT1a: %d\n", tmp);
+	vga_wcrt (regbase, CL_CRT1A, tmp);
+
+	/* set VCLK0 */
+	/* hardware RefClock: 14.31818 MHz */
+	/* formula: VClk = (OSC * N) / (D * (1+P)) */
+	/* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
+
+	vga_wseq (regbase, CL_SEQRB, regs.nom);
+	tmp = regs.den << 1;
+	if (regs.div != 0)
+		tmp |= 1;
+
+	if ((cinfo->btype == BT_SD64) ||
+	    (cinfo->btype == BT_ALPINE) ||
+	    (cinfo->btype == BT_GD5480))
+		tmp |= 0x80;	/* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
+
+	DPRINTK ("CL_SEQR1B: %ld\n", (long) tmp);
+	vga_wseq (regbase, CL_SEQR1B, tmp);
+
+	if (regs.VertRes >= 1024)
+		/* 1280x1024 */
+		vga_wcrt (regbase, VGA_CRTC_MODE, 0xc7);
+	else
+		/* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
+		 * address wrap, no compat. */
+		vga_wcrt (regbase, VGA_CRTC_MODE, 0xc3);
+
+/* HAEH?        vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, 0x20);  * previously: 0x00  unlock VGA_CRTC_H_TOTAL..CRT7 */
+
+	/* don't know if it would hurt to also program this if no interlaced */
+	/* mode is used, but I feel better this way.. :-) */
+	if (var->vmode & FB_VMODE_INTERLACED)
+		vga_wcrt (regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
+	else
+		vga_wcrt (regbase, VGA_CRTC_REGS, 0x00);	/* interlace control */
+
+	vga_wseq (regbase, VGA_SEQ_CHARACTER_MAP, 0);
+
+	/* adjust horizontal/vertical sync type (low/high) */
+	tmp = 0x03;		/* enable display memory & CRTC I/O address for color mode */
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+		tmp |= 0x40;
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+		tmp |= 0x80;
+	WGen (cinfo, VGA_MIS_W, tmp);
+
+	vga_wcrt (regbase, VGA_CRTC_PRESET_ROW, 0);	/* Screen A Preset Row-Scan register */
+	vga_wcrt (regbase, VGA_CRTC_CURSOR_START, 0);	/* text cursor on and start line */
+	vga_wcrt (regbase, VGA_CRTC_CURSOR_END, 31);	/* text cursor end line */
+
+	/******************************************************
+	 *
+	 * 1 bpp
+	 *
+	 */
+
+	/* programming for different color depths */
+	if (var->bits_per_pixel == 1) {
+		DPRINTK ("cirrusfb: preparing for 1 bit deep display\n");
+		vga_wgfx (regbase, VGA_GFX_MODE, 0);	/* mode register */
+
+		/* SR07 */
+		switch (cinfo->btype) {
+		case BT_SD64:
+		case BT_PICCOLO:
+		case BT_PICASSO:
+		case BT_SPECTRUM:
+		case BT_PICASSO4:
+		case BT_ALPINE:
+		case BT_GD5480:
+			DPRINTK (" (for GD54xx)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				  regs.multiplexing ?
+					bi->sr07_1bpp_mux : bi->sr07_1bpp);
+			break;
+
+		case BT_LAGUNA:
+			DPRINTK (" (for GD546x)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				vga_rseq (regbase, CL_SEQR7) & ~0x01);
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: unknown Board\n");
+			break;
+		}
+
+		/* Extended Sequencer Mode */
+		switch (cinfo->btype) {
+		case BT_SD64:
+			/* setting the SEQRF on SD64 is not necessary (only during init) */
+			DPRINTK ("(for SD64)\n");
+			vga_wseq (regbase, CL_SEQR1F, 0x1a);		/*  MCLK select */
+			break;
+
+		case BT_PICCOLO:
+			DPRINTK ("(for Piccolo)\n");
+/* ### ueberall 0x22? */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ##vorher 1c MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* evtl d0 bei 1 bit? avoid FIFO underruns..? */
+			break;
+
+		case BT_PICASSO:
+			DPRINTK ("(for Picasso)\n");
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ##vorher 22 MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xd0);	/* ## vorher d0 avoid FIFO underruns..? */
+			break;
+
+		case BT_SPECTRUM:
+			DPRINTK ("(for Spectrum)\n");
+/* ### ueberall 0x22? */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ##vorher 1c MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* evtl d0? avoid FIFO underruns..? */
+			break;
+
+		case BT_PICASSO4:
+		case BT_ALPINE:
+		case BT_GD5480:
+		case BT_LAGUNA:
+			DPRINTK (" (for GD54xx)\n");
+			/* do nothing */
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: unknown Board\n");
+			break;
+		}
+
+		WGen (cinfo, VGA_PEL_MSK, 0x01);	/* pixel mask: pass-through for first plane */
+		if (regs.multiplexing)
+			WHDR (cinfo, 0x4a);	/* hidden dac reg: 1280x1024 */
+		else
+			WHDR (cinfo, 0);	/* hidden dac: nothing */
+		vga_wseq (regbase, VGA_SEQ_MEMORY_MODE, 0x06);	/* memory mode: odd/even, ext. memory */
+		vga_wseq (regbase, VGA_SEQ_PLANE_WRITE, 0x01);	/* plane mask: only write to first plane */
+		offset = var->xres_virtual / 16;
+	}
+
+	/******************************************************
+	 *
+	 * 8 bpp
+	 *
+	 */
+
+	else if (var->bits_per_pixel == 8) {
+		DPRINTK ("cirrusfb: preparing for 8 bit deep display\n");
+		switch (cinfo->btype) {
+		case BT_SD64:
+		case BT_PICCOLO:
+		case BT_PICASSO:
+		case BT_SPECTRUM:
+		case BT_PICASSO4:
+		case BT_ALPINE:
+		case BT_GD5480:
+			DPRINTK (" (for GD54xx)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				  regs.multiplexing ?
+					bi->sr07_8bpp_mux : bi->sr07_8bpp);
+			break;
+
+		case BT_LAGUNA:
+			DPRINTK (" (for GD546x)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				vga_rseq (regbase, CL_SEQR7) | 0x01);
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: unknown Board\n");
+			break;
+		}
+
+		switch (cinfo->btype) {
+		case BT_SD64:
+			vga_wseq (regbase, CL_SEQR1F, 0x1d);		/* MCLK select */
+			break;
+
+		case BT_PICCOLO:
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ### vorher 1c MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			break;
+
+		case BT_PICASSO:
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ### vorher 1c MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			break;
+
+		case BT_SPECTRUM:
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* ### vorher 1c MCLK select */
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			break;
+
+		case BT_PICASSO4:
+#ifdef CONFIG_ZORRO
+			vga_wseq (regbase, CL_SEQRF, 0xb8);	/* ### INCOMPLETE!! */
+#endif
+/*          vga_wseq (regbase, CL_SEQR1F, 0x1c); */
+			break;
+
+		case BT_ALPINE:
+			DPRINTK (" (for GD543x)\n");
+			cirrusfb_set_mclk (cinfo, regs.mclk, regs.divMCLK);
+			/* We already set SRF and SR1F */
+			break;
+
+		case BT_GD5480:
+		case BT_LAGUNA:
+			DPRINTK (" (for GD54xx)\n");
+			/* do nothing */
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: unknown Board\n");
+			break;
+		}
+
+		vga_wgfx (regbase, VGA_GFX_MODE, 64);	/* mode register: 256 color mode */
+		WGen (cinfo, VGA_PEL_MSK, 0xff);	/* pixel mask: pass-through all planes */
+		if (regs.multiplexing)
+			WHDR (cinfo, 0x4a);	/* hidden dac reg: 1280x1024 */
+		else
+			WHDR (cinfo, 0);	/* hidden dac: nothing */
+		vga_wseq (regbase, VGA_SEQ_MEMORY_MODE, 0x0a);	/* memory mode: chain4, ext. memory */
+		vga_wseq (regbase, VGA_SEQ_PLANE_WRITE, 0xff);	/* plane mask: enable writing to all 4 planes */
+		offset = var->xres_virtual / 8;
+	}
+
+	/******************************************************
+	 *
+	 * 16 bpp
+	 *
+	 */
+
+	else if (var->bits_per_pixel == 16) {
+		DPRINTK ("cirrusfb: preparing for 16 bit deep display\n");
+		switch (cinfo->btype) {
+		case BT_SD64:
+			vga_wseq (regbase, CL_SEQR7, 0xf7);	/* Extended Sequencer Mode: 256c col. mode */
+			vga_wseq (regbase, CL_SEQR1F, 0x1e);		/* MCLK select */
+			break;
+
+		case BT_PICCOLO:
+			vga_wseq (regbase, CL_SEQR7, 0x87);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_PICASSO:
+			vga_wseq (regbase, CL_SEQR7, 0x27);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_SPECTRUM:
+			vga_wseq (regbase, CL_SEQR7, 0x87);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_PICASSO4:
+			vga_wseq (regbase, CL_SEQR7, 0x27);
+/*          vga_wseq (regbase, CL_SEQR1F, 0x1c);  */
+			break;
+
+		case BT_ALPINE:
+			DPRINTK (" (for GD543x)\n");
+			if (regs.HorizRes >= 1024)
+				vga_wseq (regbase, CL_SEQR7, 0xa7);
+			else
+				vga_wseq (regbase, CL_SEQR7, 0xa3);
+			cirrusfb_set_mclk (cinfo, regs.mclk, regs.divMCLK);
+			break;
+
+		case BT_GD5480:
+			DPRINTK (" (for GD5480)\n");
+			vga_wseq (regbase, CL_SEQR7, 0x17);
+			/* We already set SRF and SR1F */
+			break;
+
+		case BT_LAGUNA:
+			DPRINTK (" (for GD546x)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				vga_rseq (regbase, CL_SEQR7) & ~0x01);
+			break;
+
+		default:
+			printk (KERN_WARNING "CIRRUSFB: unknown Board\n");
+			break;
+		}
+
+		vga_wgfx (regbase, VGA_GFX_MODE, 64);	/* mode register: 256 color mode */
+		WGen (cinfo, VGA_PEL_MSK, 0xff);	/* pixel mask: pass-through all planes */
+#ifdef CONFIG_PCI
+		WHDR (cinfo, 0xc0);	/* Copy Xbh */
+#elif defined(CONFIG_ZORRO)
+		/* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
+		WHDR (cinfo, 0xa0);	/* hidden dac reg: nothing special */
+#endif
+		vga_wseq (regbase, VGA_SEQ_MEMORY_MODE, 0x0a);	/* memory mode: chain4, ext. memory */
+		vga_wseq (regbase, VGA_SEQ_PLANE_WRITE, 0xff);	/* plane mask: enable writing to all 4 planes */
+		offset = var->xres_virtual / 4;
+	}
+
+	/******************************************************
+	 *
+	 * 32 bpp
+	 *
+	 */
+
+	else if (var->bits_per_pixel == 32) {
+		DPRINTK ("cirrusfb: preparing for 24/32 bit deep display\n");
+		switch (cinfo->btype) {
+		case BT_SD64:
+			vga_wseq (regbase, CL_SEQR7, 0xf9);	/* Extended Sequencer Mode: 256c col. mode */
+			vga_wseq (regbase, CL_SEQR1F, 0x1e);		/* MCLK select */
+			break;
+
+		case BT_PICCOLO:
+			vga_wseq (regbase, CL_SEQR7, 0x85);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_PICASSO:
+			vga_wseq (regbase, CL_SEQR7, 0x25);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_SPECTRUM:
+			vga_wseq (regbase, CL_SEQR7, 0x85);
+			vga_wseq (regbase, CL_SEQRF, 0xb0);	/* Fast Page-Mode writes */
+			vga_wseq (regbase, CL_SEQR1F, 0x22);		/* MCLK select */
+			break;
+
+		case BT_PICASSO4:
+			vga_wseq (regbase, CL_SEQR7, 0x25);
+/*          vga_wseq (regbase, CL_SEQR1F, 0x1c);  */
+			break;
+
+		case BT_ALPINE:
+			DPRINTK (" (for GD543x)\n");
+			vga_wseq (regbase, CL_SEQR7, 0xa9);
+			cirrusfb_set_mclk (cinfo, regs.mclk, regs.divMCLK);
+			break;
+
+		case BT_GD5480:
+			DPRINTK (" (for GD5480)\n");
+			vga_wseq (regbase, CL_SEQR7, 0x19);
+			/* We already set SRF and SR1F */
+			break;
+
+		case BT_LAGUNA:
+			DPRINTK (" (for GD546x)\n");
+			vga_wseq (regbase, CL_SEQR7,
+				vga_rseq (regbase, CL_SEQR7) & ~0x01);
+			break;
+
+		default:
+			printk (KERN_WARNING "cirrusfb: unknown Board\n");
+			break;
+		}
+
+		vga_wgfx (regbase, VGA_GFX_MODE, 64);	/* mode register: 256 color mode */
+		WGen (cinfo, VGA_PEL_MSK, 0xff);	/* pixel mask: pass-through all planes */
+		WHDR (cinfo, 0xc5);	/* hidden dac reg: 8-8-8 mode (24 or 32) */
+		vga_wseq (regbase, VGA_SEQ_MEMORY_MODE, 0x0a);	/* memory mode: chain4, ext. memory */
+		vga_wseq (regbase, VGA_SEQ_PLANE_WRITE, 0xff);	/* plane mask: enable writing to all 4 planes */
+		offset = var->xres_virtual / 4;
+	}
+
+	/******************************************************
+	 *
+	 * unknown/unsupported bpp
+	 *
+	 */
+
+	else {
+		printk (KERN_ERR "cirrusfb: What's this?? requested color depth == %d.\n",
+			var->bits_per_pixel);
+	}
+
+	vga_wcrt (regbase, VGA_CRTC_OFFSET, offset & 0xff);
+	tmp = 0x22;
+	if (offset & 0x100)
+		tmp |= 0x10;	/* offset overflow bit */
+
+	vga_wcrt (regbase, CL_CRT1B, tmp);	/* screen start addr #16-18, fastpagemode cycles */
+
+	if (cinfo->btype == BT_SD64 ||
+	    cinfo->btype == BT_PICASSO4 ||
+	    cinfo->btype == BT_ALPINE ||
+	    cinfo->btype == BT_GD5480)
+		vga_wcrt (regbase, CL_CRT1D, 0x00);	/* screen start address bit 19 */
+
+	vga_wcrt (regbase, VGA_CRTC_CURSOR_HI, 0);	/* text cursor location high */
+	vga_wcrt (regbase, VGA_CRTC_CURSOR_LO, 0);	/* text cursor location low */
+	vga_wcrt (regbase, VGA_CRTC_UNDERLINE, 0);	/* underline row scanline = at very bottom */
+
+	vga_wattr (regbase, VGA_ATC_MODE, 1);	/* controller mode */
+	vga_wattr (regbase, VGA_ATC_OVERSCAN, 0);		/* overscan (border) color */
+	vga_wattr (regbase, VGA_ATC_PLANE_ENABLE, 15);	/* color plane enable */
+	vga_wattr (regbase, CL_AR33, 0);	/* pixel panning */
+	vga_wattr (regbase, VGA_ATC_COLOR_PAGE, 0);	/* color select */
+
+	/* [ EGS: SetOffset(); ] */
+	/* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
+	AttrOn (cinfo);
+
+	vga_wgfx (regbase, VGA_GFX_SR_VALUE, 0);	/* set/reset register */
+	vga_wgfx (regbase, VGA_GFX_SR_ENABLE, 0);		/* set/reset enable */
+	vga_wgfx (regbase, VGA_GFX_COMPARE_VALUE, 0);	/* color compare */
+	vga_wgfx (regbase, VGA_GFX_DATA_ROTATE, 0);	/* data rotate */
+	vga_wgfx (regbase, VGA_GFX_PLANE_READ, 0);	/* read map select */
+	vga_wgfx (regbase, VGA_GFX_MISC, 1);	/* miscellaneous register */
+	vga_wgfx (regbase, VGA_GFX_COMPARE_MASK, 15);	/* color don't care */
+	vga_wgfx (regbase, VGA_GFX_BIT_MASK, 255);	/* bit mask */
+
+	vga_wseq (regbase, CL_SEQR12, 0x0);	/* graphics cursor attributes: nothing special */
+
+	/* finally, turn on everything - turn off "FullBandwidth" bit */
+	/* also, set "DotClock%2" bit where requested */
+	tmp = 0x01;
+
+/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
+    if (var->vmode & FB_VMODE_CLOCK_HALVE)
+	tmp |= 0x08;
+*/
+
+	vga_wseq (regbase, VGA_SEQ_CLOCK_MODE, tmp);
+	DPRINTK ("CL_SEQR1: %d\n", tmp);
+
+	cinfo->currentmode = regs;
+	info->fix.type = regs.type;
+	info->fix.visual = regs.visual;
+	info->fix.line_length = regs.line_length;
+
+	/* pan to requested offset */
+	cirrusfb_pan_display (var, info);
+
+#ifdef CIRRUSFB_DEBUG
+	cirrusfb_dump ();
+#endif
+
+	DPRINTK ("EXIT\n");
+	return 0;
+}
+
+/* for some reason incomprehensible to me, cirrusfb requires that you write
+ * the registers twice for the settings to take..grr. -dte */
+static int cirrusfb_set_par (struct fb_info *info)
+{
+	cirrusfb_set_par_foo (info);
+	return cirrusfb_set_par_foo (info);
+}
+
+static int cirrusfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+			       unsigned blue, unsigned transp,
+			       struct fb_info *info)
+{
+	struct cirrusfb_info *cinfo = info->par;
+
+	if (regno > 255)
+		return -EINVAL;
+
+	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+		u32 v;
+		red >>= (16 - info->var.red.length);
+		green >>= (16 - info->var.green.length);
+		blue >>= (16 - info->var.blue.length);
+
+		if (regno>=16)
+			return 1;
+		v = (red << info->var.red.offset) |
+		    (green << info->var.green.offset) |
+		    (blue << info->var.blue.offset);
+
+		switch (info->var.bits_per_pixel) {
+			case 8:
+				((u8*)(info->pseudo_palette))[regno] = v;
+				break;
+			case 16:
+				((u16*)(info->pseudo_palette))[regno] = v;
+				break;
+			case 24:
+			case 32:
+				((u32*)(info->pseudo_palette))[regno] = v;
+				break;
+		}
+		return 0;
+	}
+
+	cinfo->palette[regno].red = red;
+	cinfo->palette[regno].green = green;
+	cinfo->palette[regno].blue = blue;
+
+	if (info->var.bits_per_pixel == 8) {
+			WClut (cinfo, regno, red >> 10, green >> 10, blue >> 10);
+	}
+
+	return 0;
+
+}
+
+/*************************************************************************
+	cirrusfb_pan_display()
+
+	performs display panning - provided hardware permits this
+**************************************************************************/
+static int cirrusfb_pan_display (struct fb_var_screeninfo *var,
+				 struct fb_info *info)
+{
+	int xoffset = 0;
+	int yoffset = 0;
+	unsigned long base;
+	unsigned char tmp = 0, tmp2 = 0, xpix;
+	struct cirrusfb_info *cinfo = info->par;
+
+	DPRINTK ("ENTER\n");
+	DPRINTK ("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
+
+	/* no range checks for xoffset and yoffset,   */
+	/* as fb_pan_display has already done this */
+	if (var->vmode & FB_VMODE_YWRAP)
+		return -EINVAL;
+
+	info->var.xoffset = var->xoffset;
+	info->var.yoffset = var->yoffset;
+
+	xoffset = var->xoffset * info->var.bits_per_pixel / 8;
+	yoffset = var->yoffset;
+
+	base = yoffset * cinfo->currentmode.line_length + xoffset;
+
+	if (info->var.bits_per_pixel == 1) {
+		/* base is already correct */
+		xpix = (unsigned char) (var->xoffset % 8);
+	} else {
+		base /= 4;
+		xpix = (unsigned char) ((xoffset % 4) * 2);
+	}
+
+        cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
+
+	/* lower 8 + 8 bits of screen start address */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_START_LO, (unsigned char) (base & 0xff));
+	vga_wcrt (cinfo->regbase, VGA_CRTC_START_HI, (unsigned char) (base >> 8));
+
+	/* construct bits 16, 17 and 18 of screen start address */
+	if (base & 0x10000)
+		tmp |= 0x01;
+	if (base & 0x20000)
+		tmp |= 0x04;
+	if (base & 0x40000)
+		tmp |= 0x08;
+
+	tmp2 = (vga_rcrt (cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;	/* 0xf2 is %11110010, exclude tmp bits */
+	vga_wcrt (cinfo->regbase, CL_CRT1B, tmp2);
+
+	/* construct bit 19 of screen start address */
+	if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
+		tmp2 = 0;
+		if (base & 0x80000)
+			tmp2 = 0x80;
+		vga_wcrt (cinfo->regbase, CL_CRT1D, tmp2);
+	}
+
+	/* write pixel panning value to AR33; this does not quite work in 8bpp */
+	/* ### Piccolo..? Will this work? */
+	if (info->var.bits_per_pixel == 1)
+		vga_wattr (cinfo->regbase, CL_AR33, xpix);
+
+	cirrusfb_WaitBLT (cinfo->regbase);
+
+	DPRINTK ("EXIT\n");
+	return (0);
+}
+
+
+static int cirrusfb_blank (int blank_mode, struct fb_info *info)
+{
+	/*
+	 *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
+	 *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
+	 *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
+	 *  to e.g. a video mode which doesn't support it. Implements VESA suspend
+	 *  and powerdown modes on hardware that supports disabling hsync/vsync:
+	 *    blank_mode == 2: suspend vsync
+	 *    blank_mode == 3: suspend hsync
+	 *    blank_mode == 4: powerdown
+	 */
+	unsigned char val;
+	struct cirrusfb_info *cinfo = info->par;
+	int current_mode = cinfo->blank_mode;
+
+	DPRINTK ("ENTER, blank mode = %d\n", blank_mode);
+
+	if (info->state != FBINFO_STATE_RUNNING ||
+	    current_mode == blank_mode) {
+		DPRINTK ("EXIT, returning 0\n");
+		return 0;
+	}
+
+	/* Undo current */
+	if (current_mode == FB_BLANK_NORMAL ||
+	    current_mode == FB_BLANK_UNBLANK) {
+		/* unblank the screen */
+		val = vga_rseq (cinfo->regbase, VGA_SEQ_CLOCK_MODE);
+		vga_wseq (cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);	/* clear "FullBandwidth" bit */
+		/* and undo VESA suspend trickery */
+		vga_wgfx (cinfo->regbase, CL_GRE, 0x00);
+	}
+
+	/* set new */
+	if(blank_mode > FB_BLANK_NORMAL) {
+		/* blank the screen */
+		val = vga_rseq (cinfo->regbase, VGA_SEQ_CLOCK_MODE);
+		vga_wseq (cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);	/* set "FullBandwidth" bit */
+	}
+
+	switch (blank_mode) {
+	case FB_BLANK_UNBLANK:
+	case FB_BLANK_NORMAL:
+		break;
+	case FB_BLANK_VSYNC_SUSPEND:
+		vga_wgfx (cinfo->regbase, CL_GRE, 0x04);
+		break;
+	case FB_BLANK_HSYNC_SUSPEND:
+		vga_wgfx (cinfo->regbase, CL_GRE, 0x02);
+		break;
+	case FB_BLANK_POWERDOWN:
+		vga_wgfx (cinfo->regbase, CL_GRE, 0x06);
+		break;
+	default:
+		DPRINTK ("EXIT, returning 1\n");
+		return 1;
+	}
+
+	cinfo->blank_mode = blank_mode;
+	DPRINTK ("EXIT, returning 0\n");
+
+	/* Let fbcon do a soft blank for us */
+	return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
+}
+/**** END   Hardware specific Routines **************************************/
+/****************************************************************************/
+/**** BEGIN Internal Routines ***********************************************/
+
+static void init_vgachip (struct cirrusfb_info *cinfo)
+{
+	const struct cirrusfb_board_info_rec *bi;
+
+	DPRINTK ("ENTER\n");
+
+	assert (cinfo != NULL);
+
+	bi = &cirrusfb_board_info[cinfo->btype];
+
+	/* reset board globally */
+	switch (cinfo->btype) {
+	case BT_PICCOLO:
+		WSFR (cinfo, 0x01);
+		udelay (500);
+		WSFR (cinfo, 0x51);
+		udelay (500);
+		break;
+	case BT_PICASSO:
+		WSFR2 (cinfo, 0xff);
+		udelay (500);
+		break;
+	case BT_SD64:
+	case BT_SPECTRUM:
+		WSFR (cinfo, 0x1f);
+		udelay (500);
+		WSFR (cinfo, 0x4f);
+		udelay (500);
+		break;
+	case BT_PICASSO4:
+		vga_wcrt (cinfo->regbase, CL_CRT51, 0x00);	/* disable flickerfixer */
+		mdelay (100);
+		vga_wgfx (cinfo->regbase, CL_GR2F, 0x00);	/* from Klaus' NetBSD driver: */
+		vga_wgfx (cinfo->regbase, CL_GR33, 0x00);	/* put blitter into 542x compat */
+		vga_wgfx (cinfo->regbase, CL_GR31, 0x00);	/* mode */
+		break;
+
+	case BT_GD5480:
+		vga_wgfx (cinfo->regbase, CL_GR2F, 0x00);	/* from Klaus' NetBSD driver: */
+		break;
+
+	case BT_ALPINE:
+		/* Nothing to do to reset the board. */
+		break;
+
+	default:
+		printk (KERN_ERR "cirrusfb: Warning: Unknown board type\n");
+		break;
+	}
+
+	assert (cinfo->size > 0); /* make sure RAM size set by this point */
+
+	/* the P4 is not fully initialized here; I rely on it having been */
+	/* inited under AmigaOS already, which seems to work just fine    */
+	/* (Klaus advised to do it this way)                              */
+
+	if (cinfo->btype != BT_PICASSO4) {
+		WGen (cinfo, CL_VSSM, 0x10);	/* EGS: 0x16 */
+		WGen (cinfo, CL_POS102, 0x01);
+		WGen (cinfo, CL_VSSM, 0x08);	/* EGS: 0x0e */
+
+		if (cinfo->btype != BT_SD64)
+			WGen (cinfo, CL_VSSM2, 0x01);
+
+		vga_wseq (cinfo->regbase, CL_SEQR0, 0x03);	/* reset sequencer logic */
+
+		vga_wseq (cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);	/* FullBandwidth (video off) and 8/9 dot clock */
+		WGen (cinfo, VGA_MIS_W, 0xc1);	/* polarity (-/-), disable access to display memory, VGA_CRTC_START_HI base address: color */
+
+/*      vga_wgfx (cinfo->regbase, CL_GRA, 0xce);    "magic cookie" - doesn't make any sense to me.. */
+		vga_wseq (cinfo->regbase, CL_SEQR6, 0x12);	/* unlock all extension registers */
+
+		vga_wgfx (cinfo->regbase, CL_GR31, 0x04);	/* reset blitter */
+
+		switch (cinfo->btype) {
+		case BT_GD5480:
+			vga_wseq (cinfo->regbase, CL_SEQRF, 0x98);
+			break;
+		case BT_ALPINE:
+			break;
+		case BT_SD64:
+			vga_wseq (cinfo->regbase, CL_SEQRF, 0xb8);
+			break;
+		default:
+			vga_wseq (cinfo->regbase, CL_SEQR16, 0x0f);
+			vga_wseq (cinfo->regbase, CL_SEQRF, 0xb0);
+			break;
+		}
+	}
+	vga_wseq (cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);	/* plane mask: nothing */
+	vga_wseq (cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);	/* character map select: doesn't even matter in gx mode */
+	vga_wseq (cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);	/* memory mode: chain-4, no odd/even, ext. memory */
+
+	/* controller-internal base address of video memory */
+	if (bi->init_sr07)
+		vga_wseq (cinfo->regbase, CL_SEQR7, bi->sr07);
+
+	/*  vga_wseq (cinfo->regbase, CL_SEQR8, 0x00); *//* EEPROM control: shouldn't be necessary to write to this at all.. */
+
+	vga_wseq (cinfo->regbase, CL_SEQR10, 0x00);		/* graphics cursor X position (incomplete; position gives rem. 3 bits */
+	vga_wseq (cinfo->regbase, CL_SEQR11, 0x00);		/* graphics cursor Y position (..."... ) */
+	vga_wseq (cinfo->regbase, CL_SEQR12, 0x00);		/* graphics cursor attributes */
+	vga_wseq (cinfo->regbase, CL_SEQR13, 0x00);		/* graphics cursor pattern address */
+
+	/* writing these on a P4 might give problems..  */
+	if (cinfo->btype != BT_PICASSO4) {
+		vga_wseq (cinfo->regbase, CL_SEQR17, 0x00);		/* configuration readback and ext. color */
+		vga_wseq (cinfo->regbase, CL_SEQR18, 0x02);		/* signature generator */
+	}
+
+	/* MCLK select etc. */
+	if (bi->init_sr1f)
+		vga_wseq (cinfo->regbase, CL_SEQR1F, bi->sr1f);
+
+	vga_wcrt (cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);	/* Screen A preset row scan: none */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);	/* Text cursor start: disable text cursor */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);	/* Text cursor end: - */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_START_HI, 0x00);	/* Screen start address high: 0 */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_START_LO, 0x00);	/* Screen start address low: 0 */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);	/* text cursor location high: 0 */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);	/* text cursor location low: 0 */
+
+	vga_wcrt (cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);	/* Underline Row scanline: - */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_MODE, 0xc3);	/* mode control: timing enable, byte mode, no compat modes */
+	vga_wcrt (cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);	/* Line Compare: not needed */
+	/* ### add 0x40 for text modes with > 30 MHz pixclock */
+	vga_wcrt (cinfo->regbase, CL_CRT1B, 0x02);	/* ext. display controls: ext.adr. wrap */
+
+	vga_wgfx (cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);	/* Set/Reset registes: - */
+	vga_wgfx (cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);	/* Set/Reset enable: - */
+	vga_wgfx (cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);	/* Color Compare: - */
+	vga_wgfx (cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);	/* Data Rotate: - */
+	vga_wgfx (cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);	/* Read Map Select: - */
+	vga_wgfx (cinfo->regbase, VGA_GFX_MODE, 0x00);	/* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
+	vga_wgfx (cinfo->regbase, VGA_GFX_MISC, 0x01);	/* Miscellaneous: memory map base address, graphics mode */
+	vga_wgfx (cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);	/* Color Don't care: involve all planes */
+	vga_wgfx (cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);	/* Bit Mask: no mask at all */
+	if (cinfo->btype == BT_ALPINE)
+		vga_wgfx (cinfo->regbase, CL_GRB, 0x20);	/* (5434 can't have bit 3 set for bitblt) */
+	else
+		vga_wgfx (cinfo->regbase, CL_GRB, 0x28);	/* Graphics controller mode extensions: finer granularity, 8byte data latches */
+
+	vga_wgfx (cinfo->regbase, CL_GRC, 0xff);	/* Color Key compare: - */
+	vga_wgfx (cinfo->regbase, CL_GRD, 0x00);	/* Color Key compare mask: - */
+	vga_wgfx (cinfo->regbase, CL_GRE, 0x00);	/* Miscellaneous control: - */
+	/*  vga_wgfx (cinfo->regbase, CL_GR10, 0x00); *//* Background color byte 1: - */
+/*  vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
+
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE0, 0x00);	/* Attribute Controller palette registers: "identity mapping" */
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
+	vga_wattr (cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
+
+	vga_wattr (cinfo->regbase, VGA_ATC_MODE, 0x01);	/* Attribute Controller mode: graphics mode */
+	vga_wattr (cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);	/* Overscan color reg.: reg. 0 */
+	vga_wattr (cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);	/* Color Plane enable: Enable all 4 planes */
+/* ###  vga_wattr (cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
+	vga_wattr (cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);	/* Color Select: - */
+
+	WGen (cinfo, VGA_PEL_MSK, 0xff);	/* Pixel mask: no mask */
+
+	if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
+		WGen (cinfo, VGA_MIS_W, 0xc3);	/* polarity (-/-), enable display mem, VGA_CRTC_START_HI i/o base = color */
+
+	vga_wgfx (cinfo->regbase, CL_GR31, 0x04);	/* BLT Start/status: Blitter reset */
+	vga_wgfx (cinfo->regbase, CL_GR31, 0x00);	/* - " -           : "end-of-reset" */
+
+	/* misc... */
+	WHDR (cinfo, 0);	/* Hidden DAC register: - */
+
+	printk (KERN_DEBUG "cirrusfb: This board has %ld bytes of DRAM memory\n", cinfo->size);
+	DPRINTK ("EXIT\n");
+	return;
+}
+
+static void switch_monitor (struct cirrusfb_info *cinfo, int on)
+{
+#ifdef CONFIG_ZORRO /* only works on Zorro boards */
+	static int IsOn = 0;	/* XXX not ok for multiple boards */
+
+	DPRINTK ("ENTER\n");
+
+	if (cinfo->btype == BT_PICASSO4)
+		return;		/* nothing to switch */
+	if (cinfo->btype == BT_ALPINE)
+		return;		/* nothing to switch */
+	if (cinfo->btype == BT_GD5480)
+		return;		/* nothing to switch */
+	if (cinfo->btype == BT_PICASSO) {
+		if ((on && !IsOn) || (!on && IsOn))
+			WSFR (cinfo, 0xff);
+
+		DPRINTK ("EXIT\n");
+		return;
+	}
+	if (on) {
+		switch (cinfo->btype) {
+		case BT_SD64:
+			WSFR (cinfo, cinfo->SFR | 0x21);
+			break;
+		case BT_PICCOLO:
+			WSFR (cinfo, cinfo->SFR | 0x28);
+			break;
+		case BT_SPECTRUM:
+			WSFR (cinfo, 0x6f);
+			break;
+		default: /* do nothing */ break;
+		}
+	} else {
+		switch (cinfo->btype) {
+		case BT_SD64:
+			WSFR (cinfo, cinfo->SFR & 0xde);
+			break;
+		case BT_PICCOLO:
+			WSFR (cinfo, cinfo->SFR & 0xd7);
+			break;
+		case BT_SPECTRUM:
+			WSFR (cinfo, 0x4f);
+			break;
+		default: /* do nothing */ break;
+		}
+	}
+
+	DPRINTK ("EXIT\n");
+#endif /* CONFIG_ZORRO */
+}
+
+
+/******************************************/
+/* Linux 2.6-style  accelerated functions */
+/******************************************/
+
+static void cirrusfb_prim_fillrect(struct cirrusfb_info *cinfo,
+				   const struct fb_fillrect *region)
+{
+	int m; /* bytes per pixel */
+	if(cinfo->info->var.bits_per_pixel == 1) {
+		cirrusfb_RectFill(cinfo->regbase, cinfo->info->var.bits_per_pixel,
+				  region->dx / 8, region->dy,
+				  region->width / 8, region->height,
+				  region->color,
+				  cinfo->currentmode.line_length);
+	} else {
+		m = ( cinfo->info->var.bits_per_pixel + 7 ) / 8;
+		cirrusfb_RectFill(cinfo->regbase, cinfo->info->var.bits_per_pixel,
+				  region->dx * m, region->dy,
+				  region->width * m, region->height,
+				  region->color,
+				  cinfo->currentmode.line_length);
+	}
+	return;
+}
+
+static void cirrusfb_fillrect (struct fb_info *info, const struct fb_fillrect *region)
+{
+	struct cirrusfb_info *cinfo = info->par;
+	struct fb_fillrect modded;
+	int vxres, vyres;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_fillrect(info, region);
+		return;
+	}
+
+	vxres = info->var.xres_virtual;
+	vyres = info->var.yres_virtual;
+
+	memcpy(&modded, region, sizeof(struct fb_fillrect));
+
+	if(!modded.width || !modded.height ||
+	   modded.dx >= vxres || modded.dy >= vyres)
+		return;
+
+	if(modded.dx + modded.width  > vxres) modded.width  = vxres - modded.dx;
+	if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+
+	cirrusfb_prim_fillrect(cinfo, &modded);
+}
+
+static void cirrusfb_prim_copyarea(struct cirrusfb_info *cinfo,
+				   const struct fb_copyarea *area)
+{
+	int m; /* bytes per pixel */
+	if(cinfo->info->var.bits_per_pixel == 1) {
+		cirrusfb_BitBLT(cinfo->regbase, cinfo->info->var.bits_per_pixel,
+				area->sx / 8, area->sy,
+				area->dx / 8, area->dy,
+				area->width / 8, area->height,
+				cinfo->currentmode.line_length);
+	} else {
+		m = ( cinfo->info->var.bits_per_pixel + 7 ) / 8;
+		cirrusfb_BitBLT(cinfo->regbase, cinfo->info->var.bits_per_pixel,
+				area->sx * m, area->sy,
+				area->dx * m, area->dy,
+				area->width * m, area->height,
+				cinfo->currentmode.line_length);
+	}
+	return;
+}
+
+
+static void cirrusfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+	struct cirrusfb_info *cinfo = info->par;
+	struct fb_copyarea modded;
+	u32 vxres, vyres;
+	modded.sx = area->sx;
+	modded.sy = area->sy;
+	modded.dx = area->dx;
+	modded.dy = area->dy;
+	modded.width  = area->width;
+	modded.height = area->height;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_copyarea(info, area);
+		return;
+	}
+
+	vxres = info->var.xres_virtual;
+	vyres = info->var.yres_virtual;
+
+	if(!modded.width || !modded.height ||
+	   modded.sx >= vxres || modded.sy >= vyres ||
+	   modded.dx >= vxres || modded.dy >= vyres)
+		return;
+
+	if(modded.sx + modded.width > vxres)  modded.width = vxres - modded.sx;
+	if(modded.dx + modded.width > vxres)  modded.width = vxres - modded.dx;
+	if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy;
+	if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+
+	cirrusfb_prim_copyarea(cinfo, &modded);
+}
+
+static void cirrusfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct cirrusfb_info *cinfo = info->par;
+
+        cirrusfb_WaitBLT(cinfo->regbase);
+	cfb_imageblit(info, image);
+}
+
+
+#ifdef CONFIG_PPC_PREP
+#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
+#define PREP_IO_BASE    ((volatile unsigned char *) 0x80000000)
+static void get_prep_addrs (unsigned long *display, unsigned long *registers)
+{
+	DPRINTK ("ENTER\n");
+
+	*display = PREP_VIDEO_BASE;
+	*registers = (unsigned long) PREP_IO_BASE;
+
+	DPRINTK ("EXIT\n");
+}
+
+#endif				/* CONFIG_PPC_PREP */
+
+
+#ifdef CONFIG_PCI
+static int release_io_ports = 0;
+
+/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
+ * based on the DRAM bandwidth bit and DRAM bank switching bit.  This
+ * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
+ * seem to have. */
+static unsigned int cirrusfb_get_memsize (u8 __iomem *regbase)
+{
+	unsigned long mem;
+	unsigned char SRF;
+
+	DPRINTK ("ENTER\n");
+
+	SRF = vga_rseq (regbase, CL_SEQRF);
+	switch ((SRF & 0x18)) {
+	    case 0x08: mem = 512 * 1024; break;
+	    case 0x10: mem = 1024 * 1024; break;
+		/* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
+		   * on the 5430. */
+	    case 0x18: mem = 2048 * 1024; break;
+	    default: printk ("CLgenfb: Unknown memory size!\n");
+		mem = 1024 * 1024;
+	}
+	if (SRF & 0x80) {
+		/* If DRAM bank switching is enabled, there must be twice as much
+		   * memory installed. (4MB on the 5434) */
+		mem *= 2;
+	}
+	/* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
+
+	DPRINTK ("EXIT\n");
+	return mem;
+}
+
+
+
+static void get_pci_addrs (const struct pci_dev *pdev,
+			   unsigned long *display, unsigned long *registers)
+{
+	assert (pdev != NULL);
+	assert (display != NULL);
+	assert (registers != NULL);
+
+	DPRINTK ("ENTER\n");
+
+	*display = 0;
+	*registers = 0;
+
+	/* This is a best-guess for now */
+
+	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
+		*display = pci_resource_start(pdev, 1);
+		*registers = pci_resource_start(pdev, 0);
+	} else {
+		*display = pci_resource_start(pdev, 0);
+		*registers = pci_resource_start(pdev, 1);
+	}
+
+	assert (*display != 0);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static void cirrusfb_pci_unmap (struct cirrusfb_info *cinfo)
+{
+	struct pci_dev *pdev = cinfo->pdev;
+
+	iounmap(cinfo->fbmem);
+#if 0 /* if system didn't claim this region, we would... */
+	release_mem_region(0xA0000, 65535);
+#endif
+	if (release_io_ports)
+		release_region(0x3C0, 32);
+	pci_release_regions(pdev);
+	framebuffer_release(cinfo->info);
+	pci_disable_device(pdev);
+}
+#endif /* CONFIG_PCI */
+
+
+#ifdef CONFIG_ZORRO
+static void __devexit cirrusfb_zorro_unmap (struct cirrusfb_info *cinfo)
+{
+	zorro_release_device(cinfo->zdev);
+
+	if (cinfo->btype == BT_PICASSO4) {
+		cinfo->regbase -= 0x600000;
+		iounmap ((void *)cinfo->regbase);
+		iounmap ((void *)cinfo->fbmem);
+	} else {
+		if (zorro_resource_start(cinfo->zdev) > 0x01000000)
+			iounmap ((void *)cinfo->fbmem);
+	}
+	framebuffer_release(cinfo->info);
+}
+#endif /* CONFIG_ZORRO */
+
+static int cirrusfb_set_fbinfo(struct cirrusfb_info *cinfo)
+{
+	struct fb_info *info = cinfo->info;
+	struct fb_var_screeninfo *var = &info->var;
+
+	info->par = cinfo;
+	info->pseudo_palette = cinfo->pseudo_palette;
+	info->flags = FBINFO_DEFAULT
+		    | FBINFO_HWACCEL_XPAN
+		    | FBINFO_HWACCEL_YPAN
+		    | FBINFO_HWACCEL_FILLRECT
+		    | FBINFO_HWACCEL_COPYAREA;
+	if (noaccel)
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	info->fbops = &cirrusfb_ops;
+	info->screen_base = cinfo->fbmem;
+	if (cinfo->btype == BT_GD5480) {
+		if (var->bits_per_pixel == 16)
+			info->screen_base += 1 * MB_;
+		if (var->bits_per_pixel == 24 || var->bits_per_pixel == 32)
+			info->screen_base += 2 * MB_;
+	}
+
+	/* Fill fix common fields */
+	strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
+		sizeof(info->fix.id));
+
+	/* monochrome: only 1 memory plane */
+	/* 8 bit and above: Use whole memory area */
+	info->fix.smem_start = cinfo->fbmem_phys;
+	info->fix.smem_len   = (var->bits_per_pixel == 1) ? cinfo->size / 4 : cinfo->size;
+	info->fix.type       = cinfo->currentmode.type;
+	info->fix.type_aux   = 0;
+	info->fix.visual     = cinfo->currentmode.visual;
+	info->fix.xpanstep   = 1;
+	info->fix.ypanstep   = 1;
+	info->fix.ywrapstep  = 0;
+	info->fix.line_length = cinfo->currentmode.line_length;
+
+	/* FIXME: map region at 0xB8000 if available, fill in here */
+	info->fix.mmio_start = cinfo->fbregs_phys;
+	info->fix.mmio_len   = 0;
+	info->fix.accel = FB_ACCEL_NONE;
+
+	fb_alloc_cmap(&info->cmap, 256, 0);
+
+	return 0;
+}
+
+static int cirrusfb_register(struct cirrusfb_info *cinfo)
+{
+	struct fb_info *info;
+	int err;
+	cirrusfb_board_t btype;
+
+	DPRINTK ("ENTER\n");
+
+	printk (KERN_INFO "cirrusfb: Driver for Cirrus Logic based graphic boards, v" CIRRUSFB_VERSION "\n");
+
+	info = cinfo->info;
+	btype = cinfo->btype;
+
+	/* sanity checks */
+	assert (btype != BT_NONE);
+
+	DPRINTK ("cirrusfb: (RAM start set to: 0x%p)\n", cinfo->fbmem);
+
+	/* Make pretend we've set the var so our structures are in a "good" */
+	/* state, even though we haven't written the mode to the hw yet...  */
+	info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
+	info->var.activate = FB_ACTIVATE_NOW;
+
+	err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
+	if (err < 0) {
+		/* should never happen */
+		DPRINTK("choking on default var... umm, no good.\n");
+		goto err_unmap_cirrusfb;
+	}
+
+	/* set all the vital stuff */
+	cirrusfb_set_fbinfo(cinfo);
+
+	err = register_framebuffer(info);
+	if (err < 0) {
+		printk (KERN_ERR "cirrusfb: could not register fb device; err = %d!\n", err);
+		goto err_dealloc_cmap;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_dealloc_cmap:
+	fb_dealloc_cmap(&info->cmap);
+err_unmap_cirrusfb:
+	cinfo->unmap(cinfo);
+	return err;
+}
+
+static void __devexit cirrusfb_cleanup (struct fb_info *info)
+{
+	struct cirrusfb_info *cinfo = info->par;
+	DPRINTK ("ENTER\n");
+
+	switch_monitor (cinfo, 0);
+
+	unregister_framebuffer (info);
+	fb_dealloc_cmap (&info->cmap);
+	printk ("Framebuffer unregistered\n");
+	cinfo->unmap(cinfo);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+#ifdef CONFIG_PCI
+static int cirrusfb_pci_register (struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	struct cirrusfb_info *cinfo;
+	struct fb_info *info;
+	cirrusfb_board_t btype;
+	unsigned long board_addr, board_size;
+	int ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
+		goto err_out;
+	}
+
+	info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
+	if (!info) {
+		printk(KERN_ERR "cirrusfb: could not allocate memory\n");
+		ret = -ENOMEM;
+		goto err_disable;
+	}
+
+	cinfo = info->par;
+	cinfo->info = info;
+	cinfo->pdev = pdev;
+	cinfo->btype = btype = (cirrusfb_board_t) ent->driver_data;
+
+	DPRINTK (" Found PCI device, base address 0 is 0x%lx, btype set to %d\n",
+		pdev->resource[0].start, btype);
+	DPRINTK (" base address 1 is 0x%lx\n", pdev->resource[1].start);
+
+	if(isPReP) {
+		pci_write_config_dword (pdev, PCI_BASE_ADDRESS_0, 0x00000000);
+#ifdef CONFIG_PPC_PREP
+		get_prep_addrs (&board_addr, &cinfo->fbregs_phys);
+#endif
+		/* PReP dies if we ioremap the IO registers, but it works w/out... */
+		cinfo->regbase = (char __iomem *) cinfo->fbregs_phys;
+	} else {
+		DPRINTK ("Attempt to get PCI info for Cirrus Graphics Card\n");
+		get_pci_addrs (pdev, &board_addr, &cinfo->fbregs_phys);
+		cinfo->regbase = NULL;		/* FIXME: this forces VGA.  alternatives? */
+	}
+
+	DPRINTK ("Board address: 0x%lx, register address: 0x%lx\n", board_addr, cinfo->fbregs_phys);
+
+	board_size = (btype == BT_GD5480) ?
+		32 * MB_ : cirrusfb_get_memsize (cinfo->regbase);
+
+	ret = pci_request_regions(pdev, "cirrusfb");
+	if (ret <0) {
+		printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n",
+		       board_addr);
+		goto err_release_fb;
+	}
+#if 0 /* if the system didn't claim this region, we would... */
+	if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
+		printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
+,
+		       0xA0000L);
+		ret = -EBUSY;
+		goto err_release_regions;
+	}
+#endif
+	if (request_region(0x3C0, 32, "cirrusfb"))
+		release_io_ports = 1;
+
+	cinfo->fbmem = ioremap(board_addr, board_size);
+	if (!cinfo->fbmem) {
+		ret = -EIO;
+		goto err_release_legacy;
+	}
+
+	cinfo->fbmem_phys = board_addr;
+	cinfo->size = board_size;
+	cinfo->unmap = cirrusfb_pci_unmap;
+
+	printk (" RAM (%lu kB) at 0xx%lx, ", cinfo->size / KB_, board_addr);
+	printk ("Cirrus Logic chipset on PCI bus\n");
+	pci_set_drvdata(pdev, info);
+
+	return cirrusfb_register(cinfo);
+
+err_release_legacy:
+	if (release_io_ports)
+		release_region(0x3C0, 32);
+#if 0
+	release_mem_region(0xA0000, 65535);
+err_release_regions:
+#endif
+	pci_release_regions(pdev);
+err_release_fb:
+	framebuffer_release(info);
+err_disable:
+	pci_disable_device(pdev);
+err_out:
+	return ret;
+}
+
+static void __devexit cirrusfb_pci_unregister (struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	DPRINTK ("ENTER\n");
+
+	cirrusfb_cleanup (info);
+
+	DPRINTK ("EXIT\n");
+}
+
+static struct pci_driver cirrusfb_pci_driver = {
+	.name		= "cirrusfb",
+	.id_table	= cirrusfb_pci_table,
+	.probe		= cirrusfb_pci_register,
+	.remove		= __devexit_p(cirrusfb_pci_unregister),
+#ifdef CONFIG_PM
+#if 0
+	.suspend	= cirrusfb_pci_suspend,
+	.resume		= cirrusfb_pci_resume,
+#endif
+#endif
+};
+#endif /* CONFIG_PCI */
+
+
+#ifdef CONFIG_ZORRO
+static int cirrusfb_zorro_register(struct zorro_dev *z,
+				   const struct zorro_device_id *ent)
+{
+	struct cirrusfb_info *cinfo;
+	struct fb_info *info;
+	cirrusfb_board_t btype;
+	struct zorro_dev *z2 = NULL;
+	unsigned long board_addr, board_size, size;
+	int ret;
+
+	btype = ent->driver_data;
+	if (cirrusfb_zorro_table2[btype].id2)
+		z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
+	size = cirrusfb_zorro_table2[btype].size;
+	printk(KERN_INFO "cirrusfb: %s board detected; ",
+	       cirrusfb_board_info[btype].name);
+
+	info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
+	if (!info) {
+		printk (KERN_ERR "cirrusfb: could not allocate memory\n");
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	cinfo = info->par;
+	cinfo->info = info;
+	cinfo->btype = btype;
+
+	assert (z > 0);
+	assert (z2 >= 0);
+	assert (btype != BT_NONE);
+
+	cinfo->zdev = z;
+	board_addr = zorro_resource_start(z);
+	board_size = zorro_resource_len(z);
+	cinfo->size = size;
+
+	if (!zorro_request_device(z, "cirrusfb")) {
+		printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n",
+		       board_addr);
+		ret = -EBUSY;
+		goto err_release_fb;
+	}
+
+	printk (" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
+
+	ret = -EIO;
+
+	if (btype == BT_PICASSO4) {
+		printk (" REG at $%lx\n", board_addr + 0x600000);
+
+		/* To be precise, for the P4 this is not the */
+		/* begin of the board, but the begin of RAM. */
+		/* for P4, map in its address space in 2 chunks (### TEST! ) */
+		/* (note the ugly hardcoded 16M number) */
+		cinfo->regbase = ioremap (board_addr, 16777216);
+		if (!cinfo->regbase)
+			goto err_release_region;
+
+		DPRINTK ("cirrusfb: Virtual address for board set to: $%p\n", cinfo->regbase);
+		cinfo->regbase += 0x600000;
+		cinfo->fbregs_phys = board_addr + 0x600000;
+
+		cinfo->fbmem_phys = board_addr + 16777216;
+		cinfo->fbmem = ioremap (cinfo->fbmem_phys, 16777216);
+		if (!cinfo->fbmem)
+			goto err_unmap_regbase;
+	} else {
+		printk (" REG at $%lx\n", (unsigned long) z2->resource.start);
+
+		cinfo->fbmem_phys = board_addr;
+		if (board_addr > 0x01000000)
+			cinfo->fbmem = ioremap (board_addr, board_size);
+		else
+			cinfo->fbmem = (caddr_t) ZTWO_VADDR (board_addr);
+		if (!cinfo->fbmem)
+			goto err_release_region;
+
+		/* set address for REG area of board */
+		cinfo->regbase = (caddr_t) ZTWO_VADDR (z2->resource.start);
+		cinfo->fbregs_phys = z2->resource.start;
+
+		DPRINTK ("cirrusfb: Virtual address for board set to: $%p\n", cinfo->regbase);
+	}
+	cinfo->unmap = cirrusfb_zorro_unmap;
+
+	printk (KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
+	zorro_set_drvdata(z, info);
+
+	return cirrusfb_register(cinfo);
+
+err_unmap_regbase:
+	/* Parental advisory: explicit hack */
+	iounmap(cinfo->regbase - 0x600000);
+err_release_region:
+	release_region(board_addr, board_size);
+err_release_fb:
+	framebuffer_release(info);
+err_out:
+	return ret;
+}
+
+void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
+{
+	struct fb_info *info = zorro_get_drvdata(z);
+	DPRINTK ("ENTER\n");
+
+	cirrusfb_cleanup (info);
+
+	DPRINTK ("EXIT\n");
+}
+
+static struct zorro_driver cirrusfb_zorro_driver = {
+	.name		= "cirrusfb",
+	.id_table	= cirrusfb_zorro_table,
+	.probe		= cirrusfb_zorro_register,
+	.remove		= __devexit_p(cirrusfb_zorro_unregister),
+};
+#endif /* CONFIG_ZORRO */
+
+static int __init cirrusfb_init(void)
+{
+	int error = 0;
+
+#ifndef MODULE
+	char *option = NULL;
+
+	if (fb_get_options("cirrusfb", &option))
+		return -ENODEV;
+	cirrusfb_setup(option);
+#endif
+
+#ifdef CONFIG_ZORRO
+	error |= zorro_module_init(&cirrusfb_zorro_driver);
+#endif
+#ifdef CONFIG_PCI
+	error |= pci_register_driver(&cirrusfb_pci_driver);
+#endif
+	return error;
+}
+
+
+
+#ifndef MODULE
+static int __init cirrusfb_setup(char *options) {
+	char *this_opt, s[32];
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep (&options, ",")) != NULL) {	
+		if (!*this_opt) continue;
+
+		DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
+
+		for (i = 0; i < NUM_TOTAL_MODES; i++) {
+			sprintf (s, "mode:%s", cirrusfb_predefined[i].name);
+			if (strcmp (this_opt, s) == 0)
+				cirrusfb_def_mode = i;
+		}
+		if (!strcmp(this_opt, "noaccel"))
+			noaccel = 1;
+	}
+	return 0;
+}
+#endif
+
+
+    /*
+     *  Modularization
+     */
+
+MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
+MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
+MODULE_LICENSE("GPL");
+
+static void __exit cirrusfb_exit (void)
+{
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&cirrusfb_pci_driver);
+#endif
+#ifdef CONFIG_ZORRO
+	zorro_unregister_driver(&cirrusfb_zorro_driver);
+#endif
+}
+
+module_init(cirrusfb_init);
+
+#ifdef MODULE
+module_exit(cirrusfb_exit);
+#endif
+
+
+/**********************************************************************/
+/* about the following functions - I have used the same names for the */
+/* functions as Markus Wild did in his Retina driver for NetBSD as    */
+/* they just made sense for this purpose. Apart from that, I wrote    */
+/* these functions myself.                                            */
+/**********************************************************************/
+
+/*** WGen() - write into one of the external/general registers ***/
+static void WGen (const struct cirrusfb_info *cinfo,
+		  int regnum, unsigned char val)
+{
+	unsigned long regofs = 0;
+
+	if (cinfo->btype == BT_PICASSO) {
+		/* Picasso II specific hack */
+/*              if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D || regnum == CL_VSSM2) */
+		if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
+			regofs = 0xfff;
+	}
+
+	vga_w (cinfo->regbase, regofs + regnum, val);
+}
+
+/*** RGen() - read out one of the external/general registers ***/
+static unsigned char RGen (const struct cirrusfb_info *cinfo, int regnum)
+{
+	unsigned long regofs = 0;
+
+	if (cinfo->btype == BT_PICASSO) {
+		/* Picasso II specific hack */
+/*              if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D || regnum == CL_VSSM2) */
+		if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
+			regofs = 0xfff;
+	}
+
+	return vga_r (cinfo->regbase, regofs + regnum);
+}
+
+/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
+static void AttrOn (const struct cirrusfb_info *cinfo)
+{
+	assert (cinfo != NULL);
+
+	DPRINTK ("ENTER\n");
+
+	if (vga_rcrt (cinfo->regbase, CL_CRT24) & 0x80) {
+		/* if we're just in "write value" mode, write back the */
+		/* same value as before to not modify anything */
+		vga_w (cinfo->regbase, VGA_ATT_IW,
+		       vga_r (cinfo->regbase, VGA_ATT_R));
+	}
+	/* turn on video bit */
+/*      vga_w (cinfo->regbase, VGA_ATT_IW, 0x20); */
+	vga_w (cinfo->regbase, VGA_ATT_IW, 0x33);
+
+	/* dummy write on Reg0 to be on "write index" mode next time */
+	vga_w (cinfo->regbase, VGA_ATT_IW, 0x00);
+
+	DPRINTK ("EXIT\n");
+}
+
+/*** WHDR() - write into the Hidden DAC register ***/
+/* as the HDR is the only extension register that requires special treatment
+ * (the other extension registers are accessible just like the "ordinary"
+ * registers of their functional group) here is a specialized routine for
+ * accessing the HDR
+ */
+static void WHDR (const struct cirrusfb_info *cinfo, unsigned char val)
+{
+	unsigned char dummy;
+
+	if (cinfo->btype == BT_PICASSO) {
+		/* Klaus' hint for correct access to HDR on some boards */
+		/* first write 0 to pixel mask (3c6) */
+		WGen (cinfo, VGA_PEL_MSK, 0x00);
+		udelay (200);
+		/* next read dummy from pixel address (3c8) */
+		dummy = RGen (cinfo, VGA_PEL_IW);
+		udelay (200);
+	}
+	/* now do the usual stuff to access the HDR */
+
+	dummy = RGen (cinfo, VGA_PEL_MSK);
+	udelay (200);
+	dummy = RGen (cinfo, VGA_PEL_MSK);
+	udelay (200);
+	dummy = RGen (cinfo, VGA_PEL_MSK);
+	udelay (200);
+	dummy = RGen (cinfo, VGA_PEL_MSK);
+	udelay (200);
+
+	WGen (cinfo, VGA_PEL_MSK, val);
+	udelay (200);
+
+	if (cinfo->btype == BT_PICASSO) {
+		/* now first reset HDR access counter */
+		dummy = RGen (cinfo, VGA_PEL_IW);
+		udelay (200);
+
+		/* and at the end, restore the mask value */
+		/* ## is this mask always 0xff? */
+		WGen (cinfo, VGA_PEL_MSK, 0xff);
+		udelay (200);
+	}
+}
+
+
+/*** WSFR() - write to the "special function register" (SFR) ***/
+static void WSFR (struct cirrusfb_info *cinfo, unsigned char val)
+{
+#ifdef CONFIG_ZORRO
+	assert (cinfo->regbase != NULL);
+	cinfo->SFR = val;
+	z_writeb (val, cinfo->regbase + 0x8000);
+#endif
+}
+
+/* The Picasso has a second register for switching the monitor bit */
+static void WSFR2 (struct cirrusfb_info *cinfo, unsigned char val)
+{
+#ifdef CONFIG_ZORRO
+	/* writing an arbitrary value to this one causes the monitor switcher */
+	/* to flip to Amiga display */
+	assert (cinfo->regbase != NULL);
+	cinfo->SFR = val;
+	z_writeb (val, cinfo->regbase + 0x9000);
+#endif
+}
+
+
+/*** WClut - set CLUT entry (range: 0..63) ***/
+static void WClut (struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
+	    unsigned char green, unsigned char blue)
+{
+	unsigned int data = VGA_PEL_D;
+
+	/* address write mode register is not translated.. */
+	vga_w (cinfo->regbase, VGA_PEL_IW, regnum);
+
+	if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
+	    cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
+		/* but DAC data register IS, at least for Picasso II */
+		if (cinfo->btype == BT_PICASSO)
+			data += 0xfff;
+		vga_w (cinfo->regbase, data, red);
+		vga_w (cinfo->regbase, data, green);
+		vga_w (cinfo->regbase, data, blue);
+	} else {
+		vga_w (cinfo->regbase, data, blue);
+		vga_w (cinfo->regbase, data, green);
+		vga_w (cinfo->regbase, data, red);
+	}
+}
+
+
+#if 0
+/*** RClut - read CLUT entry (range 0..63) ***/
+static void RClut (struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
+	    unsigned char *green, unsigned char *blue)
+{
+	unsigned int data = VGA_PEL_D;
+
+	vga_w (cinfo->regbase, VGA_PEL_IR, regnum);
+
+	if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
+	    cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
+		if (cinfo->btype == BT_PICASSO)
+			data += 0xfff;
+		*red = vga_r (cinfo->regbase, data);
+		*green = vga_r (cinfo->regbase, data);
+		*blue = vga_r (cinfo->regbase, data);
+	} else {
+		*blue = vga_r (cinfo->regbase, data);
+		*green = vga_r (cinfo->regbase, data);
+		*red = vga_r (cinfo->regbase, data);
+	}
+}
+#endif
+
+
+/*******************************************************************
+	cirrusfb_WaitBLT()
+
+	Wait for the BitBLT engine to complete a possible earlier job
+*********************************************************************/
+
+/* FIXME: use interrupts instead */
+static void cirrusfb_WaitBLT (u8 __iomem *regbase)
+{
+	/* now busy-wait until we're done */
+	while (vga_rgfx (regbase, CL_GR31) & 0x08)
+		/* do nothing */ ;
+}
+
+/*******************************************************************
+	cirrusfb_BitBLT()
+
+	perform accelerated "scrolling"
+********************************************************************/
+
+static void cirrusfb_BitBLT (u8 __iomem *regbase, int bits_per_pixel,
+			     u_short curx, u_short cury, u_short destx, u_short desty,
+			     u_short width, u_short height, u_short line_length)
+{
+	u_short nwidth, nheight;
+	u_long nsrc, ndest;
+	u_char bltmode;
+
+	DPRINTK ("ENTER\n");
+
+	nwidth = width - 1;
+	nheight = height - 1;
+
+	bltmode = 0x00;
+	/* if source adr < dest addr, do the Blt backwards */
+	if (cury <= desty) {
+		if (cury == desty) {
+			/* if src and dest are on the same line, check x */
+			if (curx < destx)
+				bltmode |= 0x01;
+		} else
+			bltmode |= 0x01;
+	}
+	if (!bltmode) {
+		/* standard case: forward blitting */
+		nsrc = (cury * line_length) + curx;
+		ndest = (desty * line_length) + destx;
+	} else {
+		/* this means start addresses are at the end, counting backwards */
+		nsrc = cury * line_length + curx + nheight * line_length + nwidth;
+		ndest = desty * line_length + destx + nheight * line_length + nwidth;
+	}
+
+	/*
+	   run-down of registers to be programmed:
+	   destination pitch
+	   source pitch
+	   BLT width/height
+	   source start
+	   destination start
+	   BLT mode
+	   BLT ROP
+	   VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
+	   start/stop
+	 */
+
+        cirrusfb_WaitBLT(regbase);
+
+	/* pitch: set to line_length */
+	vga_wgfx (regbase, CL_GR24, line_length & 0xff);	/* dest pitch low */
+	vga_wgfx (regbase, CL_GR25, (line_length >> 8));	/* dest pitch hi */
+	vga_wgfx (regbase, CL_GR26, line_length & 0xff);	/* source pitch low */
+	vga_wgfx (regbase, CL_GR27, (line_length >> 8));	/* source pitch hi */
+
+	/* BLT width: actual number of pixels - 1 */
+	vga_wgfx (regbase, CL_GR20, nwidth & 0xff);	/* BLT width low */
+	vga_wgfx (regbase, CL_GR21, (nwidth >> 8));	/* BLT width hi */
+
+	/* BLT height: actual number of lines -1 */
+	vga_wgfx (regbase, CL_GR22, nheight & 0xff);	/* BLT height low */
+	vga_wgfx (regbase, CL_GR23, (nheight >> 8));	/* BLT width hi */
+
+	/* BLT destination */
+	vga_wgfx (regbase, CL_GR28, (u_char) (ndest & 0xff));	/* BLT dest low */
+	vga_wgfx (regbase, CL_GR29, (u_char) (ndest >> 8));	/* BLT dest mid */
+	vga_wgfx (regbase, CL_GR2A, (u_char) (ndest >> 16));	/* BLT dest hi */
+
+	/* BLT source */
+	vga_wgfx (regbase, CL_GR2C, (u_char) (nsrc & 0xff));	/* BLT src low */
+	vga_wgfx (regbase, CL_GR2D, (u_char) (nsrc >> 8));		/* BLT src mid */
+	vga_wgfx (regbase, CL_GR2E, (u_char) (nsrc >> 16));	/* BLT src hi */
+
+	/* BLT mode */
+	vga_wgfx (regbase, CL_GR30, bltmode);	/* BLT mode */
+
+	/* BLT ROP: SrcCopy */
+	vga_wgfx (regbase, CL_GR32, 0x0d);		/* BLT ROP */
+
+	/* and finally: GO! */
+	vga_wgfx (regbase, CL_GR31, 0x02);		/* BLT Start/status */
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/*******************************************************************
+	cirrusfb_RectFill()
+
+	perform accelerated rectangle fill
+********************************************************************/
+
+static void cirrusfb_RectFill (u8 __iomem *regbase, int bits_per_pixel,
+		     u_short x, u_short y, u_short width, u_short height,
+		     u_char color, u_short line_length)
+{
+	u_short nwidth, nheight;
+	u_long ndest;
+	u_char op;
+
+	DPRINTK ("ENTER\n");
+
+	nwidth = width - 1;
+	nheight = height - 1;
+
+	ndest = (y * line_length) + x;
+
+        cirrusfb_WaitBLT(regbase);
+
+	/* pitch: set to line_length */
+	vga_wgfx (regbase, CL_GR24, line_length & 0xff);	/* dest pitch low */
+	vga_wgfx (regbase, CL_GR25, (line_length >> 8));	/* dest pitch hi */
+	vga_wgfx (regbase, CL_GR26, line_length & 0xff);	/* source pitch low */
+	vga_wgfx (regbase, CL_GR27, (line_length >> 8));	/* source pitch hi */
+
+	/* BLT width: actual number of pixels - 1 */
+	vga_wgfx (regbase, CL_GR20, nwidth & 0xff);	/* BLT width low */
+	vga_wgfx (regbase, CL_GR21, (nwidth >> 8));	/* BLT width hi */
+
+	/* BLT height: actual number of lines -1 */
+	vga_wgfx (regbase, CL_GR22, nheight & 0xff);		/* BLT height low */
+	vga_wgfx (regbase, CL_GR23, (nheight >> 8));		/* BLT width hi */
+
+	/* BLT destination */
+	vga_wgfx (regbase, CL_GR28, (u_char) (ndest & 0xff));	/* BLT dest low */
+	vga_wgfx (regbase, CL_GR29, (u_char) (ndest >> 8));	/* BLT dest mid */
+	vga_wgfx (regbase, CL_GR2A, (u_char) (ndest >> 16));		/* BLT dest hi */
+
+	/* BLT source: set to 0 (is a dummy here anyway) */
+	vga_wgfx (regbase, CL_GR2C, 0x00);	/* BLT src low */
+	vga_wgfx (regbase, CL_GR2D, 0x00);	/* BLT src mid */
+	vga_wgfx (regbase, CL_GR2E, 0x00);	/* BLT src hi */
+
+	/* This is a ColorExpand Blt, using the */
+	/* same color for foreground and background */
+	vga_wgfx (regbase, VGA_GFX_SR_VALUE, color);	/* foreground color */
+	vga_wgfx (regbase, VGA_GFX_SR_ENABLE, color);	/* background color */
+
+	op = 0xc0;
+	if (bits_per_pixel == 16) {
+		vga_wgfx (regbase, CL_GR10, color);	/* foreground color */
+		vga_wgfx (regbase, CL_GR11, color);	/* background color */
+		op = 0x50;
+		op = 0xd0;
+	} else if (bits_per_pixel == 32) {
+		vga_wgfx (regbase, CL_GR10, color);	/* foreground color */
+		vga_wgfx (regbase, CL_GR11, color);	/* background color */
+		vga_wgfx (regbase, CL_GR12, color);	/* foreground color */
+		vga_wgfx (regbase, CL_GR13, color);	/* background color */
+		vga_wgfx (regbase, CL_GR14, 0);	/* foreground color */
+		vga_wgfx (regbase, CL_GR15, 0);	/* background color */
+		op = 0x50;
+		op = 0xf0;
+	}
+	/* BLT mode: color expand, Enable 8x8 copy (faster?) */
+	vga_wgfx (regbase, CL_GR30, op);	/* BLT mode */
+
+	/* BLT ROP: SrcCopy */
+	vga_wgfx (regbase, CL_GR32, 0x0d);	/* BLT ROP */
+
+	/* and finally: GO! */
+	vga_wgfx (regbase, CL_GR31, 0x02);	/* BLT Start/status */
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/**************************************************************************
+ * bestclock() - determine closest possible clock lower(?) than the
+ * desired pixel clock
+ **************************************************************************/
+static void bestclock (long freq, long *best, long *nom,
+		       long *den, long *div, long maxfreq)
+{
+	long n, h, d, f;
+
+	assert (best != NULL);
+	assert (nom != NULL);
+	assert (den != NULL);
+	assert (div != NULL);
+	assert (maxfreq > 0);
+
+	*nom = 0;
+	*den = 0;
+	*div = 0;
+
+	DPRINTK ("ENTER\n");
+
+	if (freq < 8000)
+		freq = 8000;
+
+	if (freq > maxfreq)
+		freq = maxfreq;
+
+	*best = 0;
+	f = freq * 10;
+
+	for (n = 32; n < 128; n++) {
+		d = (143181 * n) / f;
+		if ((d >= 7) && (d <= 63)) {
+			if (d > 31)
+				d = (d / 2) * 2;
+			h = (14318 * n) / d;
+			if (abs (h - freq) < abs (*best - freq)) {
+				*best = h;
+				*nom = n;
+				if (d < 32) {
+					*den = d;
+					*div = 0;
+				} else {
+					*den = d / 2;
+					*div = 1;
+				}
+			}
+		}
+		d = ((143181 * n) + f - 1) / f;
+		if ((d >= 7) && (d <= 63)) {
+			if (d > 31)
+				d = (d / 2) * 2;
+			h = (14318 * n) / d;
+			if (abs (h - freq) < abs (*best - freq)) {
+				*best = h;
+				*nom = n;
+				if (d < 32) {
+					*den = d;
+					*div = 0;
+				} else {
+					*den = d / 2;
+					*div = 1;
+				}
+			}
+		}
+	}
+
+	DPRINTK ("Best possible values for given frequency:\n");
+	DPRINTK ("        best: %ld kHz  nom: %ld  den: %ld  div: %ld\n",
+		 freq, *nom, *den, *div);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/* -------------------------------------------------------------------------
+ *
+ * debugging functions
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#ifdef CIRRUSFB_DEBUG
+
+/**
+ * cirrusfb_dbg_print_byte
+ * @name: name associated with byte value to be displayed
+ * @val: byte value to be displayed
+ *
+ * DESCRIPTION:
+ * Display an indented string, along with a hexidecimal byte value, and
+ * its decoded bits.  Bits 7 through 0 are listed in left-to-right
+ * order.
+ */
+
+static
+void cirrusfb_dbg_print_byte (const char *name, unsigned char val)
+{
+	DPRINTK ("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
+		 name, val,
+		 val & 0x80 ? '1' : '0',
+		 val & 0x40 ? '1' : '0',
+		 val & 0x20 ? '1' : '0',
+		 val & 0x10 ? '1' : '0',
+		 val & 0x08 ? '1' : '0',
+		 val & 0x04 ? '1' : '0',
+		 val & 0x02 ? '1' : '0',
+		 val & 0x01 ? '1' : '0');
+}
+
+
+/**
+ * cirrusfb_dbg_print_regs
+ * @base: If using newmmio, the newmmio base address, otherwise %NULL
+ * @reg_class: type of registers to read: %CRT, or %SEQ
+ *
+ * DESCRIPTION:
+ * Dumps the given list of VGA CRTC registers.  If @base is %NULL,
+ * old-style I/O ports are queried for information, otherwise MMIO is
+ * used at the given @base address to query the information.
+ */
+
+static
+void cirrusfb_dbg_print_regs (caddr_t regbase, cirrusfb_dbg_reg_class_t reg_class,...)
+{
+	va_list list;
+	unsigned char val = 0;
+	unsigned reg;
+	char *name;
+
+	va_start (list, reg_class);
+
+	name = va_arg (list, char *);
+	while (name != NULL) {
+		reg = va_arg (list, int);
+
+		switch (reg_class) {
+		case CRT:
+			val = vga_rcrt (regbase, (unsigned char) reg);
+			break;
+		case SEQ:
+			val = vga_rseq (regbase, (unsigned char) reg);
+			break;
+		default:
+			/* should never occur */
+			assert (FALSE);
+			break;
+		}
+
+		cirrusfb_dbg_print_byte (name, val);
+
+		name = va_arg (list, char *);
+	}
+
+	va_end (list);
+}
+
+
+/**
+ * cirrusfb_dump
+ * @cirrusfbinfo:
+ *
+ * DESCRIPTION:
+ */
+
+static
+void cirrusfb_dump (void)
+{
+	cirrusfb_dbg_reg_dump (NULL);
+}
+
+
+/**
+ * cirrusfb_dbg_reg_dump
+ * @base: If using newmmio, the newmmio base address, otherwise %NULL
+ *
+ * DESCRIPTION:
+ * Dumps a list of interesting VGA and CIRRUSFB registers.  If @base is %NULL,
+ * old-style I/O ports are queried for information, otherwise MMIO is
+ * used at the given @base address to query the information.
+ */
+
+static
+void cirrusfb_dbg_reg_dump (caddr_t regbase)
+{
+	DPRINTK ("CIRRUSFB VGA CRTC register dump:\n");
+
+	cirrusfb_dbg_print_regs (regbase, CRT,
+			   "CR00", 0x00,
+			   "CR01", 0x01,
+			   "CR02", 0x02,
+			   "CR03", 0x03,
+			   "CR04", 0x04,
+			   "CR05", 0x05,
+			   "CR06", 0x06,
+			   "CR07", 0x07,
+			   "CR08", 0x08,
+			   "CR09", 0x09,
+			   "CR0A", 0x0A,
+			   "CR0B", 0x0B,
+			   "CR0C", 0x0C,
+			   "CR0D", 0x0D,
+			   "CR0E", 0x0E,
+			   "CR0F", 0x0F,
+			   "CR10", 0x10,
+			   "CR11", 0x11,
+			   "CR12", 0x12,
+			   "CR13", 0x13,
+			   "CR14", 0x14,
+			   "CR15", 0x15,
+			   "CR16", 0x16,
+			   "CR17", 0x17,
+			   "CR18", 0x18,
+			   "CR22", 0x22,
+			   "CR24", 0x24,
+			   "CR26", 0x26,
+			   "CR2D", 0x2D,
+			   "CR2E", 0x2E,
+			   "CR2F", 0x2F,
+			   "CR30", 0x30,
+			   "CR31", 0x31,
+			   "CR32", 0x32,
+			   "CR33", 0x33,
+			   "CR34", 0x34,
+			   "CR35", 0x35,
+			   "CR36", 0x36,
+			   "CR37", 0x37,
+			   "CR38", 0x38,
+			   "CR39", 0x39,
+			   "CR3A", 0x3A,
+			   "CR3B", 0x3B,
+			   "CR3C", 0x3C,
+			   "CR3D", 0x3D,
+			   "CR3E", 0x3E,
+			   "CR3F", 0x3F,
+			   NULL);
+
+	DPRINTK ("\n");
+
+	DPRINTK ("CIRRUSFB VGA SEQ register dump:\n");
+
+	cirrusfb_dbg_print_regs (regbase, SEQ,
+			   "SR00", 0x00,
+			   "SR01", 0x01,
+			   "SR02", 0x02,
+			   "SR03", 0x03,
+			   "SR04", 0x04,
+			   "SR08", 0x08,
+			   "SR09", 0x09,
+			   "SR0A", 0x0A,
+			   "SR0B", 0x0B,
+			   "SR0D", 0x0D,
+			   "SR10", 0x10,
+			   "SR11", 0x11,
+			   "SR12", 0x12,
+			   "SR13", 0x13,
+			   "SR14", 0x14,
+			   "SR15", 0x15,
+			   "SR16", 0x16,
+			   "SR17", 0x17,
+			   "SR18", 0x18,
+			   "SR19", 0x19,
+			   "SR1A", 0x1A,
+			   "SR1B", 0x1B,
+			   "SR1C", 0x1C,
+			   "SR1D", 0x1D,
+			   "SR1E", 0x1E,
+			   "SR1F", 0x1F,
+			   NULL);
+
+	DPRINTK ("\n");
+}
+
+#endif				/* CIRRUSFB_DEBUG */
+