patch-2.1.112 linux/drivers/video/clgenfb.c

Next file: linux/drivers/video/clgenfb.h
Previous file: linux/drivers/video/cgthreefb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.111/linux/drivers/video/clgenfb.c linux/drivers/video/clgenfb.c
@@ -0,0 +1,2122 @@
+/*
+ * Based on retz3fb.c and clgen.c
+ */
+ 
+#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/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/zorro.h>
+#include <linux/init.h>
+#include <asm/amigahw.h>
+#include <asm/pgtable.h>
+#include <asm/delay.h>
+#include "fbcon.h"
+
+#include "fbcon.h"
+#include "fbcon-mfb.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+#include "fbcon-cfb24.h"
+#include "fbcon-cfb32.h"
+
+#include "clgenfb.h"
+
+#define CLGEN_VERSION "1.4 ?"
+/* #define DEBUG if(1) */
+#define DEBUG if(0)
+
+#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+
+/* zorro IDs */
+#define ZORRO_PROD_HELFRICH_SD64_RAM				0x08930A00
+#define ZORRO_PROD_HELFRICH_SD64_REG				0x08930B00
+#define ZORRO_PROD_HELFRICH_PICCOLO_RAM				0x08930500
+#define ZORRO_PROD_HELFRICH_PICCOLO_REG				0x08930600
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM	0x08770B00
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG	0x08770C00
+#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM			0x08910200
+#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG			0x08910100
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3			0x08771800
+
+/* board types */
+#define BT_NONE     0
+#define BT_SD64     1
+#define BT_PICCOLO  2
+#define BT_PICASSO  3
+#define BT_SPECTRUM 4
+#define BT_PICASSO4 5
+
+#define MAX_NUM_BOARDS 7
+
+#define TRUE  1
+#define FALSE 0 
+
+struct clgenfb_par
+{
+    struct fb_var_screeninfo var;
+
+    __u32 line_length;  /* in BYTES! */
+    __u32 visual;
+    __u32 type;
+
+    long freq;
+    long nom;
+    long den;
+    long div;
+
+    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;
+};
+
+/* info about board */
+struct clgenfb_info
+{
+    struct fb_info_gen gen;
+
+    int keyRAM;  /* RAM, REG zorro board keys */
+    int keyREG;
+    unsigned long fbmem;
+    volatile unsigned char *regs;
+    unsigned long mem;
+    unsigned long size;
+    int btype;
+    int smallboard;
+    unsigned char SFR; /* Shadow of special function register */
+    
+    struct clgenfb_par currentmode;
+};
+
+static struct display disp;
+
+static struct clgenfb_info boards[MAX_NUM_BOARDS];  /* the boards */
+static struct clgenfb_info *fb_info=NULL;  /* pointer to current board */
+
+/*
+ *    Predefined Video Modes
+ */
+
+static struct fb_videomode clgenfb_predefined[] __initdata =
+{
+	{   "Autodetect", /* autodetect mode */
+	    { 0 }
+	},
+
+	{   "640x480", /* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
+	    { 
+		640, 480, 640, 480, 0, 0, 8, 0, 
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 
+		0, 0, -1, -1, FB_ACCEL_NONE, 40000, 32, 32, 33, 10, 96, 2,
+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	    }
+	},
+
+	/* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
+	/* 
+	    Modeline from XF86Config:
+	    Mode "1024x768" 80  1024 1136 1340 1432  768 770 774 805
+	*/
+	{
+	    "1024x768",
+	    { 
+		1024, 768, 1024, 768, 0, 0, 8, 0, 
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 
+		0, 0, -1, -1, FB_ACCEL_NONE, 12500, 92, 112, 31, 2, 204, 4,
+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	    }
+	}
+};
+
+#define NUM_TOTAL_MODES    arraysize(clgenfb_predefined)
+static struct fb_var_screeninfo clgenfb_default;
+
+/*
+ *    Frame Buffer Name
+ */
+
+static char clgenfb_name[16] = "CLgen";
+
+/****************************************************************************/
+/**** BEGIN PROTOTYPES ******************************************************/
+
+/*--- Interface used by the world ------------------------------------------*/
+void clgenfb_init(void);
+void clgenfb_setup(char *options, int *ints);
+int clgenfb_open(struct fb_info *info, int user);
+int clgenfb_release(struct fb_info *info, int user);
+int clgenfb_ioctl(struct inode *inode, struct file *file, 
+		  unsigned int cmd, unsigned long arg, int con, 
+		  struct fb_info *info);
+
+/* function table of the above functions */
+static struct fb_ops clgenfb_ops =
+{
+    clgenfb_open,
+    clgenfb_release,
+    fbgen_get_fix,   /* using the generic functions */
+    fbgen_get_var,   /* makes things much easier... */
+    fbgen_set_var,
+    fbgen_get_cmap,
+    fbgen_set_cmap,
+    fbgen_pan_display,
+    clgenfb_ioctl,
+    NULL
+};
+
+/*--- Hardware Specific Routines -------------------------------------------*/
+static void clgen_detect(void);
+static int  clgen_encode_fix(struct fb_fix_screeninfo *fix, const void *par,
+			     struct fb_info_gen *info);
+static int  clgen_decode_var(const struct fb_var_screeninfo *var, void *par,
+			     struct fb_info_gen *info);
+static int  clgen_encode_var(struct fb_var_screeninfo *var, const void *par,
+			     struct fb_info_gen *info);
+static void clgen_get_par(void *par, struct fb_info_gen *info);
+static void clgen_set_par(const void *par, struct fb_info_gen *info);
+static int  clgen_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			   unsigned *blue, unsigned *transp,
+			   struct fb_info *info);
+static int  clgen_setcolreg(unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *info);
+static int  clgen_pan_display(const struct fb_var_screeninfo *var,
+			      struct fb_info_gen *info);
+static int  clgen_blank(int blank_mode, struct fb_info_gen *info);
+
+static struct display_switch *clgen_get_dispsw(const void *par,
+					       struct fb_info_gen *info);
+
+/* function table of the above functions */
+static struct fbgen_hwswitch clgen_hwswitch = 
+{
+    clgen_detect,
+    clgen_encode_fix,
+    clgen_decode_var,
+    clgen_encode_var,
+    clgen_get_par,
+    clgen_set_par,
+    clgen_getcolreg, 
+    clgen_setcolreg,
+    clgen_pan_display,
+    clgen_blank,
+    clgen_get_dispsw
+};
+
+/* Text console acceleration */
+
+#ifdef FBCON_HAS_CFB8
+static void fbcon_clgen8_bmove(struct display *p, int sy, int sx, 
+				int dy, int dx, int height, int width);
+static void fbcon_clgen8_clear(struct vc_data *conp, struct display *p, 
+				int sy, int sx, int height, int width);
+
+static struct display_switch fbcon_clgen_8 = {
+    fbcon_cfb8_setup,
+    fbcon_clgen8_bmove,
+    fbcon_clgen8_clear,
+    fbcon_cfb8_putc,
+    fbcon_cfb8_putcs,
+    fbcon_cfb8_revc,
+    NULL,
+    NULL,
+    fbcon_cfb8_clear_margins,
+    FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
+};
+#endif
+
+
+
+/*--- Internal routines ----------------------------------------------------*/
+static void init_vgachip(void);
+static void switch_monitor(int on);
+
+static void		WGen( int regnum, unsigned char val );
+static unsigned char	RGen( int regnum );
+static void		WSeq( unsigned char regnum, unsigned char val );
+static unsigned char	RSeq( unsigned char regnum );
+static void		WCrt( unsigned char regnum, unsigned char val );
+static unsigned char	RCrt( unsigned char regnum );
+static void		WGfx( unsigned char regnum, unsigned char val );
+static unsigned char	RGfx( unsigned char regnum );
+static void		WAttr( unsigned char regnum, unsigned char val );
+static void		AttrOn( void );
+static unsigned char	RAttr( unsigned char regnum );
+static void		WHDR( unsigned char val );
+static unsigned char	RHDR( void );
+static void		WSFR( unsigned char val );
+static void		WSFR2( unsigned char val );
+static void		WClut( unsigned char regnum, unsigned char red, 
+						     unsigned char green, 
+						     unsigned char blue );
+static void		RClut( unsigned char regnum, unsigned char *red, 
+						     unsigned char *green, 
+						     unsigned char *blue );
+static void		clgen_WaitBLT( void );
+static void		clgen_BitBLT (u_short curx, u_short cury,
+				      u_short destx, u_short desty,
+				      u_short width, u_short height, 
+				      u_short line_length);
+static void		clgen_RectFill (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);
+
+/*** END   PROTOTYPES ********************************************************/
+/*****************************************************************************/
+/*** BEGIN Interface Used by the World ***************************************/
+
+static int opencount = 0;
+
+/*--- Open /dev/fbx ---------------------------------------------------------*/
+int clgenfb_open(struct fb_info *info, int user)
+{
+    MOD_INC_USE_COUNT;
+    if (opencount++ == 0) switch_monitor(1);
+    return 0;
+}
+
+/*--- Close /dev/fbx --------------------------------------------------------*/
+int clgenfb_release(struct fb_info *info, int user)
+{
+    if (--opencount == 0) switch_monitor(0);
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+/*--- handle /dev/fbx ioctl calls ------------------------------------------*/
+int clgenfb_ioctl(struct inode *inode, struct file *file,
+                  unsigned int cmd, unsigned long arg, int con,
+                  struct fb_info *info)
+{
+    printk(">clgenfb_ioctl()\n");
+    /* Nothing exciting here... */
+    printk("<clgenfb_ioctl()\n");
+    return -EINVAL;
+}
+
+/**** END   Interface used by the World *************************************/
+/****************************************************************************/
+/**** BEGIN Hardware specific Routines **************************************/
+
+static void clgen_detect(void)
+{
+    printk(">clgen_detect()\n");
+    printk("<clgen_detect()\n");
+}
+
+static int clgen_encode_fix(struct fb_fix_screeninfo *fix, const void *par,
+			    struct fb_info_gen *info)
+{
+    struct clgenfb_par  *_par  = (struct clgenfb_par*) par;
+    struct clgenfb_info *_info = (struct clgenfb_info*)info;
+    
+    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+    strcpy(fix->id, clgenfb_name);
+	
+    fix->smem_start	= (char*)_info->fbmem;
+    
+    /* monochrome: only 1 memory plane */
+    /* 8 bit and above: Use whole memory area */
+    fix->smem_len       = _par->var.bits_per_pixel == 1 ? _info->size / 4 
+							: _info->size;
+    fix->type		= _par->type;
+    fix->type_aux	= 0;
+    fix->visual		= _par->visual;
+    fix->xpanstep	= 1;
+    fix->ypanstep	= 1;
+    fix->ywrapstep	= 0;
+    fix->line_length	= _par->line_length;
+    fix->mmio_start	= (char *)_info->regs;
+    fix->mmio_len	= 0x10000;
+    fix->accel		= FB_ACCEL_NONE;
+
+    return 0;
+}
+
+static int clgen_decode_var(const struct fb_var_screeninfo *var, void *par,
+			    struct fb_info_gen *info)
+{
+    long freq;
+    int  xres, hfront, hsync, hback;
+    int  yres, vfront, vsync, vback;
+    int  nom,den; /* translyting from pixels->bytes */
+    int  i;
+    static struct
+    {
+	int xres,yres;
+    }
+    modes[] = { {1600,1280}, {1280,1024}, {1024,768}, 
+		{800,600}, {640,480}, {-1,-1} };
+
+    struct clgenfb_par *_par = (struct clgenfb_par *)par;
+
+    fb_info = (struct clgenfb_info*)info;
+    printk("clgen_decode_var()\n");
+
+    printk("Requested: %dx%dx%d\n", var->xres,var->yres,var->bits_per_pixel);
+    printk("  virtual: %dx%d\n", var->xres_virtual,var->yres_virtual);
+    printk("   offset: (%d,%d)\n", var->xoffset, var->yoffset);
+    printk("grayscale: %d\n", var->grayscale);
+#if 0
+    printk(" activate: 0x%x\n", var->activate);
+    printk(" pixclock: %d\n", var->pixclock);
+    printk("  htiming: %d;%d  %d\n", var->left_margin,var->right_margin,var->hsync_len);
+    printk("  vtiming: %d;%d  %d\n", var->upper_margin,var->lower_margin,var->vsync_len);
+    printk("     sync: 0x%x\n", var->sync);
+    printk("    vmode: 0x%x\n", var->vmode);
+#endif
+
+    _par->var = *var;
+
+    switch (var->bits_per_pixel)
+    {
+    case 1:  nom = 4; den = 8; break;   /* 8 pixel per byte, only 1/4th of mem usable */
+    case 8:  nom = 1; den = 1; break;   /* 1 pixel == 1 byte */
+    case 16: nom = 2; den = 1; break;   /* 2 bytes per pixel */
+    case 24: nom = 3; den = 1; break;   /* 3 bytes per pixel */
+    case 32: nom = 4; den = 1; break;	/* 4 bytes per pixel */
+    default:
+	printk("clgen: mode %dx%dx%d rejected...color depth not supported.\n",
+		var->xres, var->yres, var->bits_per_pixel);
+	return -EINVAL;
+    }
+
+    if (_par->var.xres*nom/den * _par->var.yres > fb_info->size)
+    {
+	printk("clgen: mode %dx%dx%d rejected...resolution too high to fit into video memory!\n",
+		var->xres, var->yres, var->bits_per_pixel);
+	return -EINVAL;
+    }
+    
+    /* use highest possible virtual resolution */
+    if (_par->var.xres_virtual == -1 &&
+	_par->var.xres_virtual == -1)
+    {
+	printk("clgen: using maximum available virtual resolution\n");
+	for (i=0; modes[i].xres != -1; i++)
+	{
+	    if (modes[i].xres*nom/den * modes[i].yres < fb_info->size/2)
+		break;
+	}
+	if (modes[i].xres == -1)
+	{
+	    printk("clgen: could not find a virtual resolution that fits into video memory!!\n");
+	    return -EINVAL;
+	}
+	_par->var.xres_virtual = modes[i].xres;
+	_par->var.yres_virtual = modes[i].yres;
+
+	printk("clgen: virtual resolution set to maximum of %dx%d\n",
+		_par->var.xres_virtual, _par->var.yres_virtual);
+    }
+    else if (_par->var.xres_virtual == -1)
+    {
+    }
+    else if (_par->var.yres_virtual == -1)
+    {
+    }
+
+    if (_par->var.xoffset < 0) _par->var.xoffset = 0;
+    if (_par->var.yoffset < 0) _par->var.yoffset = 0;
+	
+    /* truncate xoffset and yoffset to maximum if too high */
+    if (_par->var.xoffset > _par->var.xres_virtual-_par->var.xres)
+	_par->var.xoffset = _par->var.xres_virtual-_par->var.xres -1;
+
+    if (_par->var.yoffset > _par->var.yres_virtual-_par->var.yres)
+	_par->var.yoffset = _par->var.yres_virtual-_par->var.yres -1;
+
+    switch (var->bits_per_pixel)
+    {
+    case 1:
+	_par->line_length	= _par->var.xres_virtual / 8;
+	_par->visual		= FB_VISUAL_MONO10;
+	break;
+
+    case 8:
+	_par->line_length 	= _par->var.xres_virtual;
+	_par->visual		= FB_VISUAL_PSEUDOCOLOR;
+	break;
+
+    case 16:
+	_par->line_length	= _par->var.xres_virtual * 2;
+	_par->visual		= FB_VISUAL_DIRECTCOLOR;
+	break;
+
+    case 24:
+	_par->line_length	= _par->var.xres_virtual * 3;
+	_par->visual		= FB_VISUAL_DIRECTCOLOR;
+	break;
+
+    case 32:
+	_par->line_length	= _par->var.xres_virtual * 4;
+	_par->visual		= FB_VISUAL_DIRECTCOLOR;
+	break;
+    }
+    _par->type		= FB_TYPE_PACKED_PIXELS;
+
+    /* convert from ps to kHz */
+    freq = 1000000000 / var->pixclock;
+
+    DEBUG printk("desired pixclock: %ld kHz\n", freq);	
+
+    /* the SD64/P4 have a higher max. videoclock */
+    bestclock(freq, &_par->freq, &_par->nom, &_par->den, &_par->div, 
+		fb_info->btype == BT_SD64 || fb_info->btype == BT_PICASSO4
+		? 140000 : 90000);
+
+    DEBUG printk("Best possible values for given frequency: best: %ld kHz  nom: %ld  den: %ld  div: %ld\n", 
+		 _par->freq, _par->nom, _par->den, _par->div);
+
+    xres   = _par->var.xres;
+    hfront = _par->var.right_margin;
+    hsync  = _par->var.hsync_len;
+    hback  = _par->var.left_margin;
+
+    yres   = _par->var.yres;
+    vfront = _par->var.lower_margin;
+    vsync  = _par->var.vsync_len;
+    vback  = _par->var.upper_margin;
+    
+    if (_par->var.vmode & FB_VMODE_DOUBLE)
+    {
+    	yres   *= 2;
+    	vfront *= 2;
+    	vsync  *= 2;
+    	vback  *= 2;
+    }
+    else if (_par->var.vmode & FB_VMODE_INTERLACED)
+    {
+    	yres   = ++yres   / 2;
+    	vfront = ++vfront / 2;
+    	vsync  = ++vsync  / 2;
+    	vback  = ++vback  / 2;
+    }
+
+    _par->HorizRes        = xres;
+    _par->HorizTotal      = (xres + hfront + hsync + hback)/8 - 5;
+    _par->HorizDispEnd    = xres/8 - 1;
+    _par->HorizBlankStart = xres/8;
+    _par->HorizBlankEnd   = _par->HorizTotal+5; /* does not count with "-5" */
+    _par->HorizSyncStart  = (xres + hfront)/8 + 1;
+    _par->HorizSyncEnd    = (xres + hfront + hsync)/8 + 1;
+
+    _par->VertRes         = yres;
+    _par->VertTotal       = yres + vfront + vsync + vback -2;
+    _par->VertDispEnd     = yres - 1;
+    _par->VertBlankStart  = yres;
+    _par->VertBlankEnd    = _par->VertTotal;
+    _par->VertSyncStart   = yres + vfront - 1;
+    _par->VertSyncEnd     = yres + vfront + vsync - 1;
+
+    if (_par->VertTotal >= 1024)
+    {
+	printk(KERN_WARNING "clgen: ERROR: VerticalTotal >= 1024; special treatment required! (TODO)\n");
+	return -EINVAL;
+    }
+
+    return 0;
+}
+
+
+static int clgen_encode_var(struct fb_var_screeninfo *var, const void *par,
+			    struct fb_info_gen *info)
+{
+    *var = ((struct clgenfb_par*)par)->var;
+    return 0;
+}
+
+/* get current video mode */
+static void clgen_get_par(void *par, struct fb_info_gen *info)
+{
+    struct clgenfb_par *_par  = (struct clgenfb_par*)par;
+    struct clgenfb_info*_info = (struct clgenfb_info*)fb_info;
+
+    *_par = _info->currentmode;   
+}
+
+/*************************************************************************
+	clgen_set_par()
+
+	actually writes the values for a new video mode into the hardware,
+**************************************************************************/
+static void clgen_set_par(const void *par, struct fb_info_gen *info)
+{
+    unsigned char tmp;
+    int offset = 0;
+    struct clgenfb_par *_par = (struct clgenfb_par*)par;
+
+    printk(KERN_INFO">clgen_set_par()\n");
+    printk(KERN_INFO"Requested mode: %dx%dx%d\n",
+		    _par->var.xres, _par->var.yres, _par->var.bits_per_pixel);
+    printk(KERN_INFO"pixclock: %d\n", _par->var.pixclock);
+    
+    fb_info = (struct clgenfb_info *)info;
+
+    /* unlock register CRT0..CRT7 */
+    WCrt(CRT11, 0x20); /* previously: 0x00) */
+
+    /* if DEBUG is set, all parameters get output before writing */
+    DEBUG printk("CRT0: %ld\n", _par->HorizTotal); 
+    WCrt(CRT0, _par->HorizTotal);
+
+    DEBUG printk("CRT1: %ld\n", _par->HorizDispEnd); 
+    WCrt(CRT1, _par->HorizDispEnd);
+
+    DEBUG printk("CRT2: %ld\n", _par->HorizBlankStart); 
+    WCrt(CRT2, _par->HorizBlankStart);
+
+    DEBUG printk("CRT3: 128+%ld\n", _par->HorizBlankEnd % 32); /*  + 128: Compatible read */
+    WCrt(CRT3, 128 + (_par->HorizBlankEnd % 32));
+
+    DEBUG printk("CRT4: %ld\n", _par->HorizSyncStart);
+    WCrt(CRT4, _par->HorizSyncStart);
+
+    tmp = _par->HorizSyncEnd % 32;
+    if (_par->HorizBlankEnd & 32)	
+    	tmp += 128;
+    DEBUG printk("CRT5: %d\n", tmp); 
+    WCrt(CRT5, tmp);
+
+    DEBUG printk("CRT6: %ld\n", _par->VertTotal & 0xff); 
+    WCrt(CRT6, (_par->VertTotal & 0xff));
+
+    tmp = 16;  /* LineCompare bit #9 */
+    if (_par->VertTotal      & 256) tmp |= 1;
+    if (_par->VertDispEnd    & 256) tmp |= 2;
+    if (_par->VertSyncStart  & 256) tmp |= 4;
+    if (_par->VertBlankStart & 256) tmp |= 8;
+    if (_par->VertTotal      & 512) tmp |= 32;
+    if (_par->VertDispEnd    & 512) tmp |= 64;
+    if (_par->VertSyncStart  & 512) tmp |= 128;
+    DEBUG printk("CRT7: %d\n", tmp);
+    WCrt(CRT7, tmp);
+
+    tmp = 0x40; /* LineCompare bit #8 */
+    if (_par->VertBlankStart & 512)		tmp |= 0x20;
+    if (_par->var.vmode & FB_VMODE_DOUBLE)	tmp |= 0x80;
+    DEBUG printk("CRT9: %d\n", tmp);
+    WCrt(CRT9, tmp);
+
+    DEBUG printk("CRT10: %ld\n", _par->VertSyncStart & 0xff);
+    WCrt(CRT10, (_par->VertSyncStart & 0xff));
+
+    DEBUG printk("CRT11: 64+32+%ld\n", _par->VertSyncEnd % 16);
+    WCrt(CRT11, (_par->VertSyncEnd % 16 + 64 + 32));
+
+    DEBUG printk("CRT12: %ld\n", _par->VertDispEnd & 0xff);
+    WCrt(CRT12, (_par->VertDispEnd & 0xff));
+
+    DEBUG printk("CRT15: %ld\n", _par->VertBlankStart & 0xff);
+    WCrt(CRT15, (_par->VertBlankStart & 0xff));
+
+    DEBUG printk("CRT16: %ld\n", _par->VertBlankEnd & 0xff);
+    WCrt(CRT16, (_par->VertBlankEnd & 0xff));
+
+    DEBUG printk("CRT18: 0xff\n");
+    WCrt(CRT18, 0xff);
+
+    tmp = 0;
+    if (_par->var.vmode & FB_VMODE_INTERLACED)	tmp |= 1;
+    if (_par->HorizBlankEnd & 64)		tmp |= 16;
+    if (_par->HorizBlankEnd & 128)		tmp |= 32;
+    if (_par->VertBlankEnd  & 256)		tmp |= 64;
+    if (_par->VertBlankEnd  & 512)		tmp |= 128;
+    
+    DEBUG printk("CRT1a: %d\n", tmp);
+    WCrt(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 */
+
+    WSeq(SEQRB, _par->nom);
+    tmp = _par->den<<1; 
+    if (_par->div != 0) tmp |= 1;
+
+    if (fb_info->btype == BT_SD64)
+    	tmp |= 0x80; /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
+    
+    WSeq(SEQR1B, tmp);
+
+    WCrt(CRT17, 0xc3); /* mode control: CRTC enable, ROTATE(?), 16bit address wrap, no compat. */
+
+/* HAEH?	WCrt(CRT11, 0x20);  * previously: 0x00  unlock CRT0..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 (_par->var.vmode & FB_VMODE_INTERLACED)
+    	WCrt(CRT19, _par->HorizTotal / 2);
+    else
+    	WCrt(CRT19, 0x00);	/* interlace control */
+
+    WSeq(SEQR3, 0);
+
+    /* adjust horizontal/vertical sync type (low/high) */
+    tmp = 0x03; /* enable display memory & CRTC I/O address for color mode */
+    if (_par->var.sync & FB_SYNC_HOR_HIGH_ACT)  tmp |= 0x40;
+    if (_par->var.sync & FB_SYNC_VERT_HIGH_ACT) tmp |= 0x80;
+    WGen(MISC_W, tmp);
+
+    WCrt(CRT8,    0);      /* Screen A Preset Row-Scan register */
+    WCrt(CRTA,    0);      /* text cursor on and start line */
+    WCrt(CRTB,   31);      /* text cursor end line */
+
+    /* programming for different color depths */
+    if (_par->var.bits_per_pixel == 1)
+    {
+	DEBUG printk(KERN_INFO "clgen: preparing for 1 bit deep display\n");
+#if 0
+    	/* restore first 2 color registers for mono mode */
+    	WClut( 0, 0x00, 0x00, 0x00);  /* background: black */
+	WClut( 1, 0xff, 0xff, 0xff);  /* foreground: white */
+#endif
+	WGfx(GR5,     0);      /* mode register */
+	
+	/* Extended Sequencer Mode */
+	switch(fb_info->btype)
+	{
+	case BT_SD64:
+	    /* setting the SEQRF on SD64 is not necessary (only during init) */
+	    DEBUG printk(KERN_INFO "(for SD64)\n");
+	    WSeq(SEQR7,  0xf0);
+	    WSeq(SEQR1F, 0x1a);     /*  MCLK select */
+	    break;
+
+	case BT_PICCOLO:
+	    DEBUG printk(KERN_INFO "(for Piccolo)\n");
+	    WSeq(SEQR7, 0x80);
+/* ### ueberall 0x22? */
+	    WSeq(SEQR1F, 0x22);     /* ##vorher 1c MCLK select */
+	    WSeq(SEQRF, 0xb0);    /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
+	    break;
+
+	case BT_PICASSO:
+	    DEBUG printk(KERN_INFO "(for Picasso)\n");
+	    WSeq(SEQR7, 0x20);
+	    WSeq(SEQR1F, 0x22);     /* ##vorher 22 MCLK select */
+	    WSeq(SEQRF, 0xd0);    /* ## vorher d0 avoid FIFO underruns..? */
+	    break;
+
+	case BT_SPECTRUM:
+	    DEBUG printk(KERN_INFO "(for Spectrum)\n");
+	    WSeq(SEQR7, 0x80);
+/* ### ueberall 0x22? */
+	    WSeq(SEQR1F, 0x22);     /* ##vorher 1c MCLK select */
+	    WSeq(SEQRF, 0xb0);    /* evtl d0? avoid FIFO underruns..? */
+	    break;
+
+	case BT_PICASSO4:
+	    DEBUG printk(KERN_INFO "(for Picasso 4)\n");
+	    WSeq(SEQR7, 0x20);
+/*	    WSeq(SEQR1F, 0x1c); */
+/* SEQRF not being set here...	WSeq(SEQRF, 0xd0); */
+	    break;
+
+	default:
+	    printk(KERN_WARNING "clgen: unknown Board\n");
+	    break;
+	}
+
+	WGen(M_3C6,0x01);     /* pixel mask: pass-through for first plane */
+	WHDR(0);              /* hidden dac reg: nothing special */
+	WSeq(SEQR4,  0x06);     /* memory mode: odd/even, ext. memory */
+	WSeq(SEQR2, 0x01);      /* plane mask: only write to first plane */
+	offset 			= _par->var.xres_virtual / 16;
+    }
+    else if (_par->var.bits_per_pixel == 8)
+    {
+	DEBUG printk(KERN_INFO "clgen: preparing for 8 bit deep display\n");
+	switch(fb_info->btype)
+	{
+	case BT_SD64:
+	    WSeq(SEQR7,  0xf1); /* Extended Sequencer Mode: 256c col. mode */
+	    WSeq(SEQR1F, 0x1d);     /* MCLK select */
+	    break;
+
+	case BT_PICCOLO:
+	    WSeq(SEQR7, 0x81);
+	    WSeq(SEQR1F, 0x22);     /* ### vorher 1c MCLK select */
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    break;
+
+	case BT_PICASSO:
+	    WSeq(SEQR7, 0x21);
+	    WSeq(SEQR1F, 0x22);     /* ### vorher 1c MCLK select */
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    break;
+
+	case BT_SPECTRUM:
+	    WSeq(SEQR7, 0x81);
+	    WSeq(SEQR1F, 0x22);     /* ### vorher 1c MCLK select */
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    break;
+
+	case BT_PICASSO4:
+	    WSeq(SEQR7, 0x21);
+	    WSeq(SEQRF, 0xb8); /* ### INCOMPLETE!! */
+/*	    WSeq(SEQR1F, 0x1c); */
+	    break;
+
+	default:
+	    printk(KERN_WARNING "clgen: unknown Board\n");
+	    break;
+	}
+
+	WGfx(GR5,    64);     /* mode register: 256 color mode */
+	WGen(M_3C6,0xff);     /* pixel mask: pass-through all planes */
+	WHDR(0);              /* hidden dac reg: nothing special */
+	WSeq(SEQR4,  0x0a);     /* memory mode: chain4, ext. memory */
+	WSeq(SEQR2,  0xff);     /* plane mask: enable writing to all 4 planes */
+	offset 			= _par->var.xres_virtual / 8;
+    }
+    else if (_par->var.bits_per_pixel == 16)
+    {
+	DEBUG printk(KERN_INFO "clgen: preparing for 16 bit deep display\n");
+	switch(fb_info->btype)
+	{
+	case BT_SD64:
+	    WSeq(SEQR7,  0xf7); /* Extended Sequencer Mode: 256c col. mode */
+	    WSeq(SEQR1F, 0x1e);     /* MCLK select */
+	    break;
+
+	case BT_PICCOLO:
+	    WSeq(SEQR7, 0x87);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_PICASSO:
+	    WSeq(SEQR7, 0x27);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_SPECTRUM:
+	    WSeq(SEQR7, 0x87);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_PICASSO4:
+	    WSeq(SEQR7, 0x27);
+/*	    WSeq(SEQR1F, 0x1c);  */
+	    break;
+
+	default:
+	    printk(KERN_WARNING "CLGEN: unknown Board\n");
+	    break;
+	}
+
+	WGfx(GR5,    64);     /* mode register: 256 color mode */
+	WGen(M_3C6,0xff);     /* pixel mask: pass-through all planes */
+	WHDR(0xa0);           /* hidden dac reg: nothing special */
+	WSeq(SEQR4,  0x0a);     /* memory mode: chain4, ext. memory */
+	WSeq(SEQR2,  0xff);     /* plane mask: enable writing to all 4 planes */
+	offset 			= _par->var.xres_virtual / 4;
+    }
+    else if (_par->var.bits_per_pixel == 32)
+    {
+	DEBUG printk(KERN_INFO "clgen: preparing for 24/32 bit deep display\n");
+	switch(fb_info->btype)
+	{
+	case BT_SD64:
+	    WSeq(SEQR7,  0xf9); /* Extended Sequencer Mode: 256c col. mode */
+	    WSeq(SEQR1F, 0x1e);     /* MCLK select */
+	    break;
+
+	case BT_PICCOLO:
+	    WSeq(SEQR7, 0x85);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_PICASSO:
+	    WSeq(SEQR7, 0x25);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_SPECTRUM:
+	    WSeq(SEQR7, 0x85);
+	    WSeq(SEQRF, 0xb0);    /* Fast Page-Mode writes */
+	    WSeq(SEQR1F, 0x22);     /* MCLK select */
+	    break;
+
+	case BT_PICASSO4:
+	    WSeq(SEQR7, 0x25);
+/*	    WSeq(SEQR1F, 0x1c);  */
+	    break;
+
+	default:
+	    printk(KERN_WARNING "clgen: unknown Board\n");
+	    break;
+	}
+
+	WGfx(GR5,    64);     /* mode register: 256 color mode */
+	WGen(M_3C6,0xff);     /* pixel mask: pass-through all planes */
+	WHDR(0xc5);           /* hidden dac reg: 8-8-8 mode (24 or 32) */
+	WSeq(SEQR4,  0x0a);     /* memory mode: chain4, ext. memory */
+	WSeq(SEQR2,  0xff);     /* plane mask: enable writing to all 4 planes */
+	offset			= _par->var.xres_virtual / 4;
+    }
+    else
+	printk(KERN_ERR "clgen: What's this?? requested color depth == %d.\n",
+		_par->var.bits_per_pixel);
+
+    WCrt(CRT13, offset & 0xff);
+    tmp = 0x22;
+    if (offset & 0x100) tmp |= 0x10; /* offset overflow bit */
+    	
+    WCrt(CRT1B,tmp);    /* screen start addr #16-18, fastpagemode cycles */
+
+    if (fb_info->btype == BT_SD64 || fb_info->btype == BT_PICASSO4)
+    	WCrt(CRT1D, 0x00);   /* screen start address bit 19 */
+
+    WCrt(CRTE,    0);      /* text cursor location high */
+    WCrt(CRTF,    0);      /* text cursor location low */
+    WCrt(CRT14,   0);      /* underline row scanline = at very bottom */
+
+    WAttr(AR10,  1);      /* controller mode */
+    WAttr(AR11,  0);      /* overscan (border) color */
+    WAttr(AR12, 15);      /* color plane enable */
+    WAttr(AR33,  0);      /* pixel panning */
+    WAttr(AR14,  0);      /* color select */
+
+    /* [ EGS: SetOffset(); ] */
+    /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
+    AttrOn();
+
+    WGfx(GR0,    0);      /* set/reset register */
+    WGfx(GR1,    0);      /* set/reset enable */
+    WGfx(GR2,    0);      /* color compare */
+    WGfx(GR3,    0);      /* data rotate */
+    WGfx(GR4,    0);      /* read map select */
+    WGfx(GR6,    1);      /* miscellaneous register */
+    WGfx(GR7,   15);      /* color don't care */
+    WGfx(GR8,  255);      /* bit mask */
+
+    WSeq(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;
+*/
+
+    WSeq(SEQR1, tmp);
+    DEBUG printk("SEQR1: %d\n", tmp);
+
+#if 0
+    DEBUG printk(KERN_INFO "clgen: clearing display...");
+    clgen_RectFill(0, 0, _par->HorizRes, _par->VertRes, 0, _par->line_length);
+    clgen_WaitBLT();
+    DEBUG printk("done.\n");
+#endif
+
+    fb_info->currentmode = *_par;
+
+    printk("virtual offset: (%d,%d)\n", _par->var.xoffset,_par->var.yoffset);
+    /* pan to requested offset */
+    clgen_pan_display (&fb_info->currentmode.var, (struct fb_info_gen*)fb_info);
+
+    DEBUG printk("<clgen_set_par()\n");
+    return;
+}
+
+static int clgen_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			   unsigned *blue, unsigned *transp,
+			   struct fb_info *info)
+{
+    unsigned char bred, bgreen, bblue;
+
+    if (regno > 255 || regno < 0)
+	return (1);
+
+    fb_info = (struct clgenfb_info *)info;
+
+    RClut(regno, &bred, &bgreen, &bblue);
+
+    *red    = (u_int)bred;
+    *green  = (u_int)bgreen;
+    *blue   = (u_int)bblue;
+    *transp = 0;
+    return (0);
+}
+
+static int clgen_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp, 
+			   struct fb_info *info)
+{
+    if (regno > 255 || regno < 0)
+	return (1);
+
+    fb_info = (struct clgenfb_info *)info;
+    
+    /* "transparent" stuff is completely ignored. */
+    WClut(regno, (red & 0xff), (green & 0xff), (blue & 0xff));
+
+    return (0);
+}
+
+/*************************************************************************
+	clgen_pan_display()
+
+	performs display panning - provided hardware permits this
+**************************************************************************/
+static int clgen_pan_display(const struct fb_var_screeninfo *var,
+			     struct fb_info_gen *info)
+{
+    int xoffset = 0;
+    int yoffset = 0;
+    unsigned long base;
+    unsigned char tmp = 0, tmp2 = 0, xpix;
+
+    fb_info = (struct clgenfb_info*)fb_info;
+
+    /* no range checks for xoffset and yoffset,   */
+    /* as fbgen_pan_display has already done this */
+
+    fb_info->currentmode.var.xoffset = var->xoffset;
+    fb_info->currentmode.var.yoffset = var->yoffset;
+
+    xoffset = var->xoffset * fb_info->currentmode.var.bits_per_pixel / 8;
+    yoffset = var->yoffset;
+
+    base = yoffset * fb_info->currentmode.line_length + xoffset;
+
+    if (fb_info->currentmode.var.bits_per_pixel == 1)
+    {
+	/* base is already correct */
+	xpix = (unsigned char)(var->xoffset % 8);
+    }
+    else
+    {
+	base /= 4;
+	xpix = (unsigned char)((xoffset % 4) * 2);
+    }
+
+    /* lower 8 + 8 bits of screen start address */
+    WCrt(CRTD, (unsigned char)(base & 0xff));
+    WCrt(CRTC, (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 = (RCrt(CRT1B) & 0xf2) | tmp; /* 0xf2 is %11110010, exclude tmp bits */
+    WCrt(CRT1B, tmp2);
+    /* construct bit 19 of screen start address (only on SD64) */
+    if (fb_info->btype == BT_SD64 ||
+	fb_info->btype == BT_PICASSO4)
+    {
+	tmp2 = 0;
+	if (base & 0x80000) tmp2 = 0x80;
+	WCrt(CRT1D, tmp2);
+    }
+
+    /* write pixel panning value to AR33; this does not quite work in 8bpp */
+    /* ### Piccolo..? Will this work? */
+    if (fb_info->currentmode.var.bits_per_pixel == 1)
+	WAttr(AR33, xpix);
+
+    return(0);
+}
+
+
+static int clgen_blank(int blank_mode, struct fb_info_gen *info)
+{
+    unsigned char val;
+    printk(">clgen_blank(%d)\n",blank_mode);
+
+    fb_info = (struct clgenfb_info *)info;
+
+    val = RSeq(SEQR1);
+    if (blank_mode)
+	WSeq(SEQR1, val | 0x20); /* set "FullBandwidth" bit */
+    else
+	WSeq(SEQR1, val & 0xdf); /* clear "FullBandwidth" bit */
+
+    printk("<clgen_blank()\n");
+    return 0;
+}
+
+/**** END   Hardware specific Routines **************************************/
+/****************************************************************************/
+/**** BEGIN Internal Routines ***********************************************/
+
+static void init_vgachip(void)
+{
+    printk(">init_vgachip()\n");
+
+    /* reset board globally */
+    switch(fb_info->btype)
+    {
+    case BT_SD64:     WSFR(0x1f);  udelay(500); WSFR(0x4f); udelay(500); break;
+    case BT_PICCOLO:  WSFR(0x01);  udelay(500); WSFR(0x51); udelay(500); break;
+    case BT_PICASSO:  WSFR2(0xff); udelay(500);				 break;
+    case BT_SPECTRUM: WSFR(0x1f);  udelay(500); WSFR(0x4f); udelay(500); break;
+    case BT_PICASSO4: 
+	WCrt(CRT51, 0x00); /* disable flickerfixer */
+	udelay(100000);
+	WGfx(GR2F, 0x00); /* from Klaus' NetBSD driver: */
+	WGfx(GR33, 0x00); /* put blitter into 542x compat */
+	WGfx(GR31, 0x00); /* mode */
+	break;
+
+    default:
+	printk(KERN_ERR "clgen: Warning: Unknown board type\n");
+	break;
+    }
+
+    /* "pre-set" a RAMsize; if the test succeeds, double it */
+    if (fb_info->btype == BT_SD64 ||
+        fb_info->btype == BT_PICASSO4)
+        fb_info->size = 0x400000;
+    else
+        fb_info->size = 0x200000;
+
+    /* assume it's a "large memory" board (2/4 MB) */
+    fb_info->smallboard = FALSE;
+
+    /* 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 (fb_info->btype != BT_PICASSO4)
+    {
+	WGen(VSSM, 0x10);  /* EGS: 0x16 */
+	WGen(POS102, 0x01);
+	WGen(VSSM, 0x08);  /* EGS: 0x0e */
+
+	if(fb_info->btype != BT_SD64)
+		WGen(VSSM2, 0x01);
+
+	WSeq(SEQR0, 0x03);  /* reset sequencer logic */
+
+	WSeq(SEQR1, 0x21);  /* FullBandwidth (video off) and 8/9 dot clock */
+	WGen(MISC_W, 0xc1);  /* polarity (-/-), disable access to display memory, CRTC base address: color */
+
+/*	WGfx(GRA, 0xce);    "magic cookie" - doesn't make any sense to me.. */
+	WSeq(SEQR6, 0x12);   /* unlock all extension registers */
+
+	WGfx(GR31, 0x04);  /* reset blitter */
+
+	if (fb_info->btype == BT_SD64)
+	{
+	    WSeq(SEQRF, 0xb8);  /* 4 MB Ram SD64, disable CRT fifo(!), 64 bit bus */
+	}
+	else
+	{
+	    WSeq(SEQR16, 0x0f); /* Perf. Tuning: Fix value..(?) */
+	    WSeq(SEQRF, 0xb0); /* 2 MB DRAM, 8level write buffer, 32bit bus */
+	}
+    }
+
+    WSeq(SEQR2, 0xff);  /* plane mask: nothing */
+    WSeq(SEQR3, 0x00);  /* character map select: doesn't even matter in gx mode */
+    WSeq(SEQR4, 0x0e);  /* memory mode: chain-4, no odd/even, ext. memory */
+
+    /* controller-internal base address of video memory */
+    switch(fb_info->btype)
+    {
+    case BT_SD64:     WSeq(SEQR7, 0xf0); break;
+    case BT_PICCOLO:  WSeq(SEQR7, 0x80); break;
+    case BT_SPECTRUM: WSeq(SEQR7, 0x80); break;
+    case BT_PICASSO:  WSeq(SEQR7, 0x20); break;
+    case BT_PICASSO4: WSeq(SEQR7, 0x20); break;
+    }
+
+/*  WSeq(SEQR8, 0x00);*/  /* EEPROM control: shouldn't be necessary to write to this at all.. */
+
+    WSeq(SEQR10, 0x00); /* graphics cursor X position (incomplete; position gives rem. 3 bits */
+    WSeq(SEQR11, 0x00); /* graphics cursor Y position (..."... ) */
+    WSeq(SEQR12, 0x00); /* graphics cursor attributes */
+    WSeq(SEQR13, 0x00); /* graphics cursor pattern address */
+
+    /* writing these on a P4 might give problems..  */
+    if (fb_info->btype != BT_PICASSO4)
+    {
+	WSeq(SEQR17, 0x00); /* configuration readback and ext. color */
+	WSeq(SEQR18, 0x02); /* signature generator */
+    }
+
+    /* MCLK select etc. */
+    switch(fb_info->btype)
+    {
+    case BT_PICCOLO:
+    case BT_PICASSO:
+    case BT_SPECTRUM:  WSeq(SEQR1F, 0x22); break;
+    case BT_SD64:      WSeq(SEQR1F, 0x20); break;
+    case BT_PICASSO4:/*WSeq(SEQR1F, 0x1c); */ break;
+    }
+
+    WCrt(CRT8, 0x00);  /* Screen A preset row scan: none */
+    WCrt(CRTA, 0x20);  /* Text cursor start: disable text cursor */
+    WCrt(CRTB, 0x00);  /* Text cursor end: - */
+    WCrt(CRTC, 0x00);  /* Screen start address high: 0 */
+    WCrt(CRTD, 0x00);  /* Screen start address low: 0 */
+    WCrt(CRTE, 0x00);  /* text cursor location high: 0 */
+    WCrt(CRTF, 0x00);  /* text cursor location low: 0 */
+
+    WCrt(CRT14, 0x00); /* Underline Row scanline: - */
+    WCrt(CRT17, 0xc3); /* mode control: timing enable, byte mode, no compat modes */
+    WCrt(CRT18, 0x00); /* Line Compare: not needed */
+    /* ### add 0x40 for text modes with > 30 MHz pixclock */
+    WCrt(CRT1B, 0x02); /* ext. display controls: ext.adr. wrap */
+
+    WGfx(GR0, 0x00); /* Set/Reset registes: - */
+    WGfx(GR1, 0x00); /* Set/Reset enable: - */
+    WGfx(GR2, 0x00); /* Color Compare: - */
+    WGfx(GR3, 0x00); /* Data Rotate: - */
+    WGfx(GR4, 0x00); /* Read Map Select: - */
+    WGfx(GR5, 0x00); /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
+    WGfx(GR6, 0x01); /* Miscellaneous: memory map base address, graphics mode */
+    WGfx(GR7, 0x0f); /* Color Don't care: involve all planes */
+    WGfx(GR8, 0xff); /* Bit Mask: no mask at all */
+    WGfx(GRB, 0x28); /* Graphics controller mode extensions: finer granularity, 8byte data latches */
+
+    WGfx(GRC, 0xff); /* Color Key compare: - */
+    WGfx(GRD, 0x00); /* Color Key compare mask: - */
+    WGfx(GRE, 0x00); /* Miscellaneous control: - */
+/*  WGfx(GR10, 0x00);*/ /* Background color byte 1: - */
+/*  WGfx(GR11, 0x00); */
+
+    WAttr(AR0, 0x00); /* Attribute Controller palette registers: "identity mapping" */
+    WAttr(AR1, 0x01);
+    WAttr(AR2, 0x02);
+    WAttr(AR3, 0x03);
+    WAttr(AR4, 0x04);
+    WAttr(AR5, 0x05);
+    WAttr(AR6, 0x06);
+    WAttr(AR7, 0x07);
+    WAttr(AR8, 0x08);
+    WAttr(AR9, 0x09);
+    WAttr(ARA, 0x0a);
+    WAttr(ARB, 0x0b);
+    WAttr(ARC, 0x0c);
+    WAttr(ARD, 0x0d);
+    WAttr(ARE, 0x0e);
+    WAttr(ARF, 0x0f);
+
+    WAttr(AR10, 0x01); /* Attribute Controller mode: graphics mode */
+    WAttr(AR11, 0x00); /* Overscan color reg.: reg. 0 */
+    WAttr(AR12, 0x0f); /* Color Plane enable: Enable all 4 planes */
+/* ### 	WAttr(AR33, 0x00); * Pixel Panning: - */
+    WAttr(AR14, 0x00); /* Color Select: - */
+
+    WGen(M_3C6, 0xff); /* Pixel mask: no mask */
+
+    WGen(MISC_W, 0xc3); /* polarity (-/-), enable display mem, CRTC i/o base = color */
+
+    WGfx(GR31, 0x04); /* BLT Start/status: Blitter reset */
+    WGfx(GR31, 0x00); /* - " -           : "end-of-reset" */
+
+    /* CLUT setup */
+    WClut( 0, 0x00, 0x00, 0x00);  /* background: black */
+    WClut( 1, 0xff, 0xff, 0xff);  /* foreground: white */
+    WClut( 2, 0x00, 0x80, 0x00);
+    WClut( 3, 0x00, 0x80, 0x80);
+    WClut( 4, 0x80, 0x00, 0x00);
+    WClut( 5, 0x80, 0x00, 0x80);
+    WClut( 6, 0x80, 0x40, 0x00);
+    WClut( 7, 0x80, 0x80, 0x80);
+    WClut( 8, 0x40, 0x40, 0x40);
+    WClut( 9, 0x40, 0x40, 0xc0);
+    WClut(10, 0x40, 0xc0, 0x40);
+    WClut(11, 0x40, 0xc0, 0xc0);
+    WClut(12, 0xc0, 0x40, 0x40);
+    WClut(13, 0xc0, 0x40, 0xc0);
+    WClut(14, 0xc0, 0xc0, 0x40);
+    WClut(15, 0xc0, 0xc0, 0xc0);
+
+    /* the rest a grey ramp */
+    {
+	int i;
+
+	for (i = 16; i < 256; i++)
+	    WClut(i, i, i, i);
+    }
+
+
+    /* misc... */
+    WHDR(0); /* Hidden DAC register: - */
+
+#if 0
+    /* check for 1/2 MB Piccolo/Picasso/Spectrum resp. 2/4 MB SD64 */
+    /* DRAM register has already been pre-set for "large", so it is*/
+    /* only modified if we find that this is a "small" version */
+    {
+	unsigned volatile char *ram = fb_info->fbmem;
+	int i, flag = 0;
+
+	ram += (fb_info->size >> 1);
+
+	for (i = 0; i < 256; i++)
+	    ram[i] = (unsigned char)i;
+
+	for (i = 0; i < 256; i++)
+	{
+	    if (ram[i] != i)
+		flag = 1;
+	}
+
+	/* if the DRAM test failed, halve RAM value */
+	if (flag)
+	{
+	    fb_info->size /= 2;
+	    fb_info->smallboard = TRUE;
+	    switch(fb_info->btype)
+	    {
+	    case BT_SD64:     WSeq(SEQRF, 0x38); break; /* 2 MB Ram SD64 */
+	    case BT_PICASSO4: WSeq(SEQRF, 0x38); break; /* ### like SD64? */
+	    case BT_PICCOLO:
+	    case BT_PICASSO:
+	    case BT_SPECTRUM: WSeq(SEQRF, 0x30); break; /* 1 MB DRAM */
+	    default:
+		printk(KERN_WARNING "clgen: Uuhh..could not determine RAM size!\n");
+	    }
+	}
+
+    }
+#endif
+    printk(KERN_INFO "clgen: This board has %ld bytes of DRAM memory\n", fb_info->size);
+    printk("<init_vgachip()\n");
+    return;
+}
+
+static void switch_monitor(int on)
+{
+    static int IsOn = 0;    /* XXX not ok for multiple boards */
+
+    if (fb_info->btype == BT_PICASSO4) return; /* nothing to switch */
+    if (fb_info->btype == BT_PICASSO)
+    {
+	if ((on && !IsOn) || (!on && IsOn))
+	    WSFR(0xff);
+	return;
+    }    
+    if (on)
+        switch(fb_info->btype)
+	{
+        case BT_SD64:     WSFR(fb_info->SFR | 0x21);	break;
+        case BT_PICCOLO:  WSFR(fb_info->SFR | 0x28);	break;
+        case BT_SPECTRUM: WSFR(0x6f);			break;
+	}
+    else
+        switch(fb_info->btype)
+	{
+        case BT_SD64:     WSFR(fb_info->SFR & 0xde);	break;
+        case BT_PICCOLO:  WSFR(fb_info->SFR & 0xd7);	break;
+        case BT_SPECTRUM: WSFR(0x4f);			break;
+	}
+}
+
+static struct display_switch *clgen_get_dispsw(const void *par,
+					     struct fb_info_gen *info)
+{
+    struct clgenfb_par *_par = (struct clgenfb_par*) par;
+
+    printk("clgen_get_dispsw(): ");
+    switch (_par->var.bits_per_pixel)
+    {
+#ifdef FBCON_HAS_MFB
+    case 1:
+	printk("monochrome\n");
+	return &fbcon_mfb;
+#endif
+#ifdef FBCON_HAS_CFB8
+    case 8:
+	printk("8 bit color depth\n");
+	return &fbcon_clgen_8;
+#endif
+#ifdef FBCON_HAS_CFB16
+    case 16:
+	printk("16 bit color depth\n");
+	return &fbcon_cfb16;
+#endif
+#ifdef FBCON_HAS_CFB24
+    case 24:
+	printk("24 bit color depth\n");
+	return &fbcon_cfb24;
+#endif
+#ifdef FBCON_HAS_CFB32
+    case 32:
+	printk("32 bit color depth\n");
+	return &fbcon_cfb32;
+#endif
+
+    default:
+	printk("unsupported color depth\n");
+	return NULL;
+    }
+}
+
+static void fbcon_clgen8_bmove(struct display *p, int sy, int sx, 
+				int dy, int dx, int height, int width)
+{
+    sx     *= p->fontwidth;
+    sy     *= p->fontheight;
+    dx     *= p->fontwidth;
+    dy     *= p->fontheight;
+    width  *= p->fontwidth;
+    height *= p->fontheight;
+
+    fb_info = (struct clgenfb_info*)p->fb_info;
+
+    clgen_BitBLT((unsigned short)sx, (unsigned short)sy,
+		 (unsigned short)dx, (unsigned short)dy,
+		 (unsigned short)width, (unsigned short)height,
+		 fb_info->currentmode.line_length);
+    clgen_WaitBLT();
+}
+
+static void fbcon_clgen8_clear(struct vc_data *conp, struct display *p, 
+				int sy, int sx, int height, int width)
+{
+    unsigned short col;
+    
+    fb_info = (struct clgenfb_info*)p->fb_info;
+
+    sx     *= p->fontwidth;
+    sy     *= p->fontheight;
+    width  *= p->fontwidth;
+    height *= p->fontheight;
+
+    col = attr_bgcol_ec(p, conp);
+    col &= 0xff;
+
+    clgen_RectFill((unsigned short)sx, (unsigned short)sy,
+		    (unsigned short)width,(unsigned short)height,
+		     col, fb_info->currentmode.line_length);
+    clgen_WaitBLT();
+}
+
+
+/********************************************************************/
+/* clgenfb_init() - master initialization function                  */
+/********************************************************************/
+__initfunc(void clgenfb_init(void))
+{
+    const struct ConfigDev *cd  = NULL;
+    const struct ConfigDev *cd2 = NULL;
+    int err;
+    int btype;
+    int key,key2;
+    unsigned long board_addr,board_size;
+
+    printk(">clgenfb_init()\n");
+    printk(KERN_INFO "clgen: Driver for Cirrus Logic based graphic boards, v" CLGEN_VERSION "\n");
+
+    btype = -1;
+
+    if ((key = zorro_find(ZORRO_PROD_HELFRICH_SD64_RAM, 0, 0)))
+    {
+	key2 = zorro_find(ZORRO_PROD_HELFRICH_SD64_REG, 0, 0);
+        btype = BT_SD64;
+        printk(KERN_INFO "clgen: SD64 board detected; ");
+    }
+    else if ((key = zorro_find(ZORRO_PROD_HELFRICH_PICCOLO_RAM, 0, 0)))
+    {
+        key2      = zorro_find(ZORRO_PROD_HELFRICH_PICCOLO_REG, 0, 0);
+    	btype = BT_PICCOLO;
+    	printk(KERN_INFO "clgen: Piccolo board detected; ");
+    }
+    else if ((key = zorro_find(ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM, 0, 0)))
+    {
+        key2      = zorro_find(ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG, 0, 0);
+	btype = BT_PICASSO;
+    	printk(KERN_INFO "clgen: Picasso II board detected; ");
+    }
+    else if ((key = zorro_find(ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM, 0, 0)))
+    {
+        key2      = zorro_find(ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG, 0, 0);
+        btype = BT_SPECTRUM;
+        printk(KERN_INFO "clgen: Spectrum board detected; ");
+    }
+    else if ((key = zorro_find(ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3, 0, 0)))
+    {
+        btype = BT_PICASSO4;
+        printk(KERN_INFO "clgen: Picasso 4 board detected; ");
+    }
+    else
+    {
+	printk(KERN_NOTICE "clgen: no supported board found.\n");
+	return;
+    }
+    
+    fb_info = &boards[0]; /* FIXME support multiple boards ...*/
+    
+    fb_info->keyRAM = key;
+    fb_info->keyREG = key2;
+    fb_info->btype  = btype;
+    
+    cd = zorro_get_board(key);
+    board_addr = (unsigned long)cd->cd_BoardAddr;
+    board_size = (unsigned long)cd->cd_BoardSize;
+    printk(" RAM (%lu MB) at $%lx, ", board_size/0x100000, board_addr);
+
+    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) */
+	fb_info->regs = (unsigned char *)kernel_map(board_addr, 16777216, 
+	                                      KERNELMAP_NOCACHE_SER, NULL);
+        DEBUG printk(KERN_INFO "clgen: Virtual address for board set to: $%p\n", fb_info->regs);
+	fb_info->regs += 0x600000;
+
+	fb_info->fbmem = kernel_map(board_addr + 16777216, 16777216, 
+			      KERNELMAP_NOCACHE_SER, NULL);
+	DEBUG printk(KERN_INFO "clgen: (RAM start set to: $%lx)\n", fb_info->fbmem);
+    }
+    else
+    {
+        cd2 = zorro_get_board(key2);
+        printk(" REG at $%lx\n", (unsigned long)cd2->cd_BoardAddr);
+
+        if (board_addr > 0x01000000)
+	    fb_info->fbmem = kernel_map(board_addr, board_size, 
+				  KERNELMAP_NOCACHE_SER, NULL);
+	else
+	    fb_info->fbmem = ZTWO_VADDR(board_addr);
+
+        /* set address for REG area of board */
+	fb_info->regs = (unsigned char *)ZTWO_VADDR(cd2->cd_BoardAddr);
+
+        DEBUG printk(KERN_INFO "clgen: Virtual address for board set to: $%p\n", fb_info->regs);
+	DEBUG printk(KERN_INFO "clgen: (RAM start set to: $%lx)\n", fb_info->fbmem);
+    }
+
+    init_vgachip();
+
+    /* set up a few more things, register framebuffer driver etc */
+    fb_info->gen.parsize         = sizeof(struct clgenfb_par);
+    fb_info->gen.fbhw            = &clgen_hwswitch;
+    strcpy (fb_info->gen.info.modename, clgenfb_name);
+    fb_info->gen.info.node       = -1;
+    fb_info->gen.info.fbops      = &clgenfb_ops;
+    fb_info->gen.info.disp       = &disp;
+    fb_info->gen.info.changevar  = NULL;
+    fb_info->gen.info.switch_con = &fbgen_switch;
+    fb_info->gen.info.updatevar  = &fbgen_update_var;
+    fb_info->gen.info.blank      = &fbgen_blank;
+    
+    /* mark this board as "autoconfigured" */
+    zorro_config_board(key, 0);
+    if (btype != BT_PICASSO4)
+	zorro_config_board(key2, 0);
+
+    /* now that we know the board has been registered n' stuff, we */
+    /* can finally initialize it to a default mode (640x480) */
+    clgenfb_default = clgenfb_predefined[1].var;
+    clgenfb_default.activate = FB_ACTIVATE_NOW;
+    clgenfb_default.yres_virtual = 480*3; /* for fast scrolling (YPAN-Mode) */
+    err = fbgen_do_set_var(&clgenfb_default, 1, &fb_info->gen);
+
+    if (err)
+	return;
+
+    disp.var = clgenfb_default;
+    fbgen_set_disp(-1, &fb_info->gen);
+    fbgen_install_cmap(0, &fb_info->gen);
+
+    err = register_framebuffer(&fb_info->gen.info);
+    if (err)
+    {
+	printk(KERN_ERR "clgen: ERROR - could not register fb device; err = %d!\n", err);
+	return;
+    }
+
+    printk("<clgenfb_init()\n");
+    return;
+}
+
+    /*
+     *  Cleanup
+     */
+
+void clgenfb_cleanup(struct clgenfb_info *info)
+{
+    printk(">clgenfb_cleanup()\n");
+
+    fb_info = info;
+
+    switch_monitor(0);
+
+    zorro_unconfig_board(info->keyRAM, 0);
+    if (fb_info->btype != BT_PICASSO4)
+	zorro_unconfig_board(info->keyREG, 0);
+
+    unregister_framebuffer(&info->gen.info);
+    printk("Framebuffer unregistered\n");
+    printk("<clgenfb_cleanup()\n");
+}
+
+
+/* A strtok which returns empty strings, too */
+static char *strtoke(char *s,const char *ct)
+{
+	char *sbegin, *send;
+	static char *ssave = NULL;
+
+	sbegin  = s ? s : ssave;
+	if (!sbegin)
+		return NULL;
+	if (*sbegin == '\0') {
+		ssave = NULL;
+		return NULL;
+	}
+	send = strpbrk(sbegin, ct);
+	if (send && *send != '\0')
+		*send++ = '\0';
+	ssave = send;
+	return sbegin;
+}
+
+/*****************************************************************/
+/* clgenfb_setup() might be used later for parsing possible      */
+/* arguments to the video= bootstrap parameter. Right now, there */
+/* is nothing I do here.                                         */
+/*****************************************************************/
+__initfunc(void clgenfb_setup(char *options, int *ints))
+{
+//    char *this_opt;
+
+//    printk("clgenfb_setup(): options: %s\n", options);	
+}
+
+
+    /*
+     *  Modularization
+     */
+
+#ifdef MODULE
+int init_module(void)
+{
+    printk("init_module()\n");
+    clgenfb_init(0);
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    printk("module_cleanup()\n");
+    clgenfb_cleanup(fb_info);
+}
+#endif /* MODULE */
+
+
+
+/**********************************************************************/
+/* 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 ***/
+void WGen(int regnum, unsigned char val)
+{
+	unsigned volatile char *reg = fb_info->regs + regnum;
+
+	if(fb_info->btype == BT_PICASSO)
+	{
+		/* Picasso II specific hack */
+/*		if (regnum == M_3C7_W || regnum == M_3C9 || regnum == VSSM2) */
+		if (regnum == M_3C7_W || regnum == M_3C9)
+			reg += 0xfff;
+	}
+
+	*reg = val;
+}
+
+/*** RGen() - read out one of the external/general registers ***/
+unsigned char RGen(int regnum)
+{
+	unsigned volatile char *reg = fb_info->regs + regnum;
+
+	if(fb_info->btype == BT_PICASSO)
+	{
+		/* Picasso II specific hack */
+/*		if (regnum == M_3C7_W || regnum == M_3C9 || regnum == VSSM2) */
+		if (regnum == M_3C7_W || regnum == M_3C9)
+			reg += 0xfff;
+	}
+
+	return *reg;
+}
+
+/*** WSeq() - write into a register of the sequencer ***/
+void WSeq(unsigned char regnum, unsigned char val)
+{
+	fb_info->regs[SEQRX]   = regnum;
+	fb_info->regs[SEQRX+1] = val;
+}
+
+/*** RSeq() - read out one of the Sequencer registers ***/
+unsigned char RSeq(unsigned char regnum)
+{
+	fb_info->regs[SEQRX] = regnum;
+	return fb_info->regs[SEQRX+1];
+}
+
+/*** WCrt() - write into a register of the CRT controller ***/
+void WCrt(unsigned char regnum, unsigned char val)
+{
+	fb_info->regs[CRTX]   = regnum;
+	fb_info->regs[CRTX+1] = val;
+}
+
+/*** RCrt() - read out one of the CRT controller registers ***/
+unsigned char RCrt(unsigned char regnum)
+{
+	fb_info->regs[CRTX] = regnum;
+	return fb_info->regs[CRTX+1];
+}
+
+/*** WGfx() - write into a register of the Gfx controller ***/
+void WGfx(unsigned char regnum, unsigned char val)
+{
+	fb_info->regs[GRX]   = regnum;
+	fb_info->regs[GRX+1] = val;
+}
+
+/*** RGfx() - read out one of the Gfx controller registers ***/
+unsigned char RGfx(unsigned char regnum)
+{
+	fb_info->regs[GRX] = regnum;
+	return fb_info->regs[GRX+1];
+}
+
+/*** WAttr() - write into a register of the Attribute controller ***/
+void WAttr(unsigned char regnum, unsigned char val)
+{
+	/* if the next access to the attribute controller is a data write access, */
+	/* simply write back the information that was already there before, so that */
+	/* the next write access after that will be an index write. */
+	if (RCrt(CRT24) & 0x80)
+		/* can't use WAttr() here - we would go into a recursive loop otherwise */
+		fb_info->regs[ARX] = fb_info->regs[ARX+1];
+
+	if (RCrt(CRT24) & 0x80)
+		printk(KERN_WARNING "clgen: *** AttrIdx BAD!***\n");
+
+	/* now, first set index and after that the value - both to the same address (!) */
+	fb_info->regs[ARX] = regnum;
+	fb_info->regs[ARX] = val;
+}
+
+/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
+void AttrOn()
+{
+	if (RCrt(CRT24) & 0x80)
+		/* if we're just in "write value" mode, write back the */
+		/* same value as before to not modify anything */
+		fb_info->regs[ARX] = fb_info->regs[ARX+1];
+
+	/* turn on video bit */
+/*	fb_info->regs[ARX] = 0x20; */
+	fb_info->regs[ARX] = 0x33;
+
+	/* dummy write on Reg0 to be on "write index" mode next time */
+	fb_info->regs[ARX] = 0x00;
+}
+
+/*** RAttr() - read out a register of the Attribute controller ***/
+unsigned char RAttr(unsigned char regnum)
+{
+	/* (explanation see above in WAttr() ) */
+	if (RCrt(CRT24) & 0x80)
+		fb_info->regs[ARX] = fb_info->regs[ARX+1];
+
+	fb_info->regs[ARX] = regnum;
+	return fb_info->regs[ARX+1];
+}
+
+
+/*** 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
+ */
+void WHDR(unsigned char val)
+{
+	unsigned char dummy;
+
+	if(fb_info->btype == BT_PICASSO)
+	{
+		/* Klaus' hint for correct access to HDR on some boards */
+		/* first write 0 to pixel mask (3c6) */
+		WGen(M_3C6, 0x00); udelay(200);
+		/* next read dummy from pixel address (3c8) */
+		dummy = RGen(M_3C8); udelay(200);
+	}
+
+	/* now do the usual stuff to access the HDR */
+
+	dummy = RGen(M_3C6); udelay(200);
+	dummy = RGen(M_3C6); udelay(200);
+	dummy = RGen(M_3C6); udelay(200);
+	dummy = RGen(M_3C6); udelay(200);
+
+	WGen(M_3C6, val); udelay(200);
+
+	if(fb_info->btype == BT_PICASSO)
+	{
+		/* now first reset HDR access counter */
+		dummy = RGen(M_3C8); udelay(200);
+
+		/* and at the end, restore the mask value */
+		/* ## is this mask always 0xff? */
+		WGen(M_3C6, 0xff); udelay(200);
+	}
+}
+
+/*** RHDR() - read out the Hidden DAC register ***/
+/* I hope this does not break on the GD5428 - cannot test it. */
+/* (Is there any board for the Amiga that uses the 5428 ?) */
+unsigned char RHDR()
+{
+	unsigned char dummy;
+
+	dummy = RGen(M_3C6);
+	dummy = RGen(M_3C6);
+	dummy = RGen(M_3C6);
+	dummy = RGen(M_3C6);
+
+	return RGen(M_3C6);
+}
+
+
+/*** WSFR() - write to the "special function register" (SFR) ***/
+void WSFR(unsigned char val)
+{
+	fb_info->SFR          = val;
+	fb_info->regs[0x8000] = val;
+}
+
+/* The Picasso has a second register for switching the monitor bit */
+void WSFR2(unsigned char val)
+{
+	/* writing an arbitrary value to this one causes the monitor switcher */
+	/* to flip to Amiga display */
+	fb_info->SFR          = val;
+	fb_info->regs[0x9000] = val;
+}
+
+/*** WClut - set CLUT entry (range: 0..255 is automat. shifted to 0..63) ***/
+void WClut(unsigned char regnum, unsigned char red, unsigned char green, unsigned char blue)
+{
+	unsigned int data = 0x3c9;
+
+	/* address write mode register is not translated.. */
+	fb_info->regs[0x3c8] = regnum;
+
+	if(fb_info->btype == BT_PICASSO || fb_info->btype == BT_PICASSO4)
+	{
+		/* but DAC data register IS, at least for Picasso II */
+		if(fb_info->btype == BT_PICASSO)
+			data += 0xfff;
+		fb_info->regs[data] = (red   >> 2);
+		fb_info->regs[data] = (green >> 2);
+		fb_info->regs[data] = (blue  >> 2);
+	}
+	else
+	{
+		fb_info->regs[data] = (blue  >> 2);
+		fb_info->regs[data] = (green >> 2);
+		fb_info->regs[data] = (red   >> 2);
+	}
+}
+
+/*** RClut - read CLUT entry and convert to 0..255 range ***/
+void RClut(unsigned char regnum, unsigned char *red, unsigned char *green, unsigned char *blue)
+{
+	unsigned int data = 0x3c9;
+
+	fb_info->regs[0x3c7] = regnum;
+
+	if(fb_info->btype == BT_PICASSO || fb_info->btype == BT_PICASSO4)
+	{
+		if(fb_info->btype == BT_PICASSO)
+			data += 0xfff;
+		*red   = fb_info->regs[data] << 2;
+		*green = fb_info->regs[data] << 2;
+		*blue  = fb_info->regs[data] << 2;
+	}
+	else
+	{
+		*blue  = fb_info->regs[data] << 2;
+		*green = fb_info->regs[data] << 2;
+		*red   = fb_info->regs[data] << 2;
+	}
+}
+
+
+/*******************************************************************
+	clgen_WaitBLT()
+
+	Wait for the BitBLT engine to complete a possible earlier job
+*********************************************************************/
+
+void clgen_WaitBLT()
+{
+	/* now busy-wait until we're done */
+	while (RGfx(GR31) & 0x08)
+		;
+}
+
+/*******************************************************************
+	clgen_BitBLT()
+
+	perform accelerated "scrolling"
+********************************************************************/
+
+void clgen_BitBLT (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;
+
+	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;
+	}
+
+//	clgen_WaitBLT(); /* ### NOT OK for multiple boards! */
+
+	/*
+		run-down of registers to be programmed:
+		destination pitch
+		source pitch
+		BLT width/height
+		source start
+		destination start
+		BLT mode
+		BLT ROP
+		GR0 / GR1: "fill color"
+		start/stop
+	*/
+
+	/* pitch: set to line_length */
+	WGfx(GR24, line_length & 0xff);	/* dest pitch low */
+	WGfx(GR25, (line_length >> 8));	/* dest pitch hi */
+	WGfx(GR26, line_length & 0xff);	/* source pitch low */
+	WGfx(GR27, (line_length >> 8));	/* source pitch hi */
+
+	/* BLT width: actual number of pixels - 1 */
+	WGfx(GR20, nwidth & 0xff);	/* BLT width low */
+	WGfx(GR21, (nwidth >> 8));	/* BLT width hi */
+
+	/* BLT height: actual number of lines -1 */
+	WGfx(GR22, nheight & 0xff);	/* BLT height low */
+	WGfx(GR23, (nheight >> 8));	/* BLT width hi */
+
+	/* BLT destination */
+	WGfx(GR28, (u_char)(ndest & 0xff));	/* BLT dest low */
+	WGfx(GR29, (u_char)(ndest >> 8));	/* BLT dest mid */
+	WGfx(GR2A, (u_char)(ndest >> 16));	/* BLT dest hi */
+
+	/* BLT source */
+	WGfx(GR2C, (u_char)(nsrc & 0xff));	/* BLT src low */
+	WGfx(GR2D, (u_char)(nsrc >> 8));	/* BLT src mid */
+	WGfx(GR2E, (u_char)(nsrc >> 16));	/* BLT src hi */
+
+	/* BLT mode */
+	WGfx(GR30, bltmode);	/* BLT mode */
+
+	/* BLT ROP: SrcCopy */
+	WGfx(GR32, 0x0d);	/* BLT ROP */
+
+	/* and finally: GO! */
+	WGfx(GR31, 0x02);	/* BLT Start/status */
+}
+
+/*******************************************************************
+	clgen_RectFill()
+
+	perform accelerated rectangle fill
+********************************************************************/
+
+void clgen_RectFill (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;
+
+	nwidth = width - 1;
+	nheight = height - 1;
+
+	ndest = (y * line_length) + x;
+
+//	clgen_WaitBLT(); /* ### NOT OK for multiple boards! */
+
+	/* pitch: set to line_length */
+	WGfx(GR24, line_length & 0xff);	/* dest pitch low */
+	WGfx(GR25, (line_length >> 8));	/* dest pitch hi */
+	WGfx(GR26, line_length & 0xff);	/* source pitch low */
+	WGfx(GR27, (line_length >> 8));	/* source pitch hi */
+
+	/* BLT width: actual number of pixels - 1 */
+	WGfx(GR20, nwidth & 0xff);	/* BLT width low */
+	WGfx(GR21, (nwidth >> 8));	/* BLT width hi */
+
+	/* BLT height: actual number of lines -1 */
+	WGfx(GR22, nheight & 0xff);	/* BLT height low */
+	WGfx(GR23, (nheight >> 8));	/* BLT width hi */
+
+	/* BLT destination */
+	WGfx(GR28, (u_char)(ndest & 0xff));	/* BLT dest low */
+	WGfx(GR29, (u_char)(ndest >> 8));	/* BLT dest mid */
+	WGfx(GR2A, (u_char)(ndest >> 16));	/* BLT dest hi */
+
+	/* BLT source: set to 0 (is a dummy here anyway) */
+	WGfx(GR2C, 0x00);	/* BLT src low */
+	WGfx(GR2D, 0x00);	/* BLT src mid */
+	WGfx(GR2E, 0x00);	/* BLT src hi */
+
+	/* This is a ColorExpand Blt, using the */
+	/* same color for foreground and background */
+	WGfx(GR0, color);	/* foreground color */
+	WGfx(GR1, color);	/* background color */
+
+	/* BLT mode: color expand, Enable 8x8 copy (faster?) */
+	WGfx(GR30, 0xc0);	/* BLT mode */
+
+	/* BLT ROP: SrcCopy */
+	WGfx(GR32, 0x0d);	/* BLT ROP */
+
+	/* and finally: GO! */
+	WGfx(GR31, 0x02);	/* BLT Start/status */
+}
+
+/**************************************************************************
+ * bestclock() - determine closest possible clock lower(?) than the
+ * desired pixel clock
+ **************************************************************************/
+#define abs(x) ((x)<0 ? -(x) : (x))
+static void bestclock(long freq, long *best, long *nom,
+		      long *den, long *div,  long maxfreq)
+{
+    long n, h, d, f;
+
+    *nom = 0;
+    *den = 0;
+    *div = 0;
+
+    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;
+	        }
+	    }
+	}
+    }
+}
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov