
#ifdef GRF_CL5426

/*
 * Graphics routines for Cirrus CL GD 5426 boards,
 *
 * This code offers low-level routines to access Cirrus Cl GD 5426
 * graphics-boards from within NetBSD for the Amiga.
 * No warranties for any kind of function at all - this
 * code may crash your hardware and scratch your harddisk.  Use at your
 * own risk.  Freely distributable.
 *
 * Modified for Cirrus CL GD 5426 from
 * Lutz Vieweg's retina driver by Kari Mettinen 08/94
 * Contributions by Ill, ScottE, MiL & Ezy.
 *
 * Thanks to Village Tronic Marketing Gmbh for providing me with
 * a Picasso-II board.
 * Thanks for Integrated Electronics Oy Ab for providing me with
 * Cirrus CL GD 542x family documentation.
 *
 */

#include <sys/param.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/cpu.h>
#include <dev/cons.h>
#include <amiga/amiga/device.h>
#include <amiga/dev/grfioctl.h>
#include <amiga/dev/grfvar.h>
#include <amiga/dev/grf_clreg.h>
#include <amiga/dev/zbusvar.h>

static int cl_mondefok __P((struct grfvideo_mode *mdp));
static void cl_boardinit();
static void CompFQ __P((u_int fq, u_char *num, u_char *denom));
static int cl_load_mon __P((struct grf_softc *gp, struct grfvideo_mode *gv));
static int cl_getvmode __P((struct grf_softc *gp, struct grfvideo_mode *vm));
static int cl_setvmode __P((struct grf_softc *gp, unsigned int mode, int txtonly));
static int cl_toggle __P((struct grf_softc *gp,unsigned short));  
static int cl_getcmap __P((struct grf_softc *gfp, struct grf_colormap *cmap));
static int cl_putcmap __P((struct grf_softc *gfp, struct grf_colormap *cmap));    
static void cl_off __P((struct grf_softc *gp));

int cl_ioctl __P((register struct grf_softc *gp, int cmd, void *data));
void grfclattach __P((struct device *, struct device *, void *));
int  grfclprint  __P((void *, char *));
int  grfclmatch  __P((struct device *, struct cfdata *, void *));
#define  grfcl_cnprobe()   CN_DEAD

/* Graphics display definitions. */
/* these are filled by 'clconfig' on boot */
#define monitor_def_max 8
static struct grfvideo_mode monitor_def[8] =
	{{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}};
static int monitor_default = 0;
static struct grfvideo_mode *monitor_current;

#define MAXPIXELCLOCK 115000000 /* safety */

int  		cltype = 0;   /* Picasso, Spectrum or Piccolo */
unsigned char	pass_toggle;  /* passthru status tracker */


/* because all 5426-boards have 2 configdev entries, one for 
 * framebuffer mem and the other for regs, we have to hold onto
 * the pointers globally until we match on both.  This and 'cltype'
 * are the primary obsticles to multiple board support, but if you
 * have multiple boards you have bigger problems than grf_cl.
 */
static void *cl_fbaddr = 0;	/* framebuffer */
static void *cl_regaddr = 0;    /* registers */
static int cl_fbsize;           /* framebuffer size */

/* For now, until loadbsd is fixed, or we find a reliable method
 * of detecting 1mb boards, leave out this patchable constant.
 */
int cl_default_fbsize = 1024*1024*2;  

/* standard driver stuff */
struct cfdriver grfclcd = {
	NULL, "grfcl", (cfmatch_t)grfclmatch, grfclattach,
	DV_DULL, sizeof(struct grf_softc), NULL, 0
};
static struct cfdata *cfdata;

int
grfclmatch(pdp, cfp, auxp)
	struct device *pdp;
	struct cfdata *cfp;
	void *auxp;
{
	struct zbus_args *zap;
	static int regprod, fbprod;

	zap = auxp;

	if (amiga_realconfig == 0)
		return(0);

	/* Grab the first board we encounter as the preferred one.  
	 * This will allow one board to work in a multiple 5426 board 
	 * system, but not multiple boards at the same time.  
	 */
	if (cltype == 0) {
		switch (zap->manid) {
		case PICASSO:
			regprod = 12;
			fbprod = 11;
			break;
		case SPECTRUM:
			regprod = 2;
			fbprod = 1;
			break;
		case PICCOLO:
			regprod = 6;
			fbprod = 5;
			break;
		default:
			return(0);
		}
		cltype = zap->manid;
	} else {
		if (cltype != zap->manid) {
			return(0);
		}
	}

	/* Configure either registers or framebuffer in any order
	 */
	if (zap->prodid == regprod)
		cl_regaddr = zap->va;
	else if (zap->prodid == fbprod) {
		/* picasso fb memory hack (what for?) */
		if (cltype == PICASSO)
			zap->va = (void *)zbusmap(zap->pa, zap->size);
		cl_fbaddr = zap->va;
		cl_fbsize = zap->size;
	} else {
		printf("grfcl: Bad product id\n");
		return(0);
	}
	
	return(1);
}

void
grfclattach(pdp, dp, auxp)
	struct device *pdp, *dp;
	void *auxp;
{
	static struct grf_softc congrf;
	struct zbus_args *zap;
	struct grf_softc *gp;
	int x;

	zap = auxp;

	printf("\n");

	/* make sure both halves have matched */
	if (!cl_regaddr || !cl_fbaddr)
		return;

	/* do all that messy console/grf stuff */
	if (dp == NULL)
		gp = &congrf;
	else
		gp = (struct grf_softc *)dp;

	if (dp != NULL && congrf.g_regkva != 0) {
		/*
		 * inited earlier, just copy (not device struct)
		 */
		bcopy(&congrf.g_display, &gp->g_display,
		    (char *)&gp[1] - (char *)&gp->g_display);
	} else {
		gp->g_regkva = (volatile caddr_t)cl_regaddr;
		gp->g_fbkva = (volatile caddr_t)cl_fbaddr;

		gp->g_unit = GRF_CL5426_UNIT;
		gp->g_mode = cl_mode;
		gp->g_conpri = grfcl_cnprobe();
		gp->g_flags = GF_ALIVE;

		/* wakeup the board */
		cl_boardinit(gp);

		/* XXX - find *correct* size of z3 board */
		if (cl_fbsize > 0x200000) {
			cl_fbsize = cl_default_fbsize;
		}

	}

	/*
	 * attach grf
	 */
	if (amiga_config_found(cfdata, &gp->g_device, gp, grfclprint)) {
		printf("grfcl: %dMB ", cl_fbsize/0x100000);
		switch (cltype) {
		case PICASSO:
			printf("Picasso II");
			break;
		case SPECTRUM:
			printf("Spectrum");
			break;
		case PICCOLO:
			printf("Piccolo");
			break;
		}
		printf(" being used\n");
	} else {
		printf("grfcl unattached!!\n");
	}
}

int
grfclprint(auxp, pnp)
	void *auxp;
	char *pnp;
{
	if (pnp)
		printf("ite at %s, NOT!", pnp);
	return(UNCONF);
}

void
cl_boardinit(gp)
struct grf_softc	*gp;
{
	unsigned char *ba = gp->g_regkva;
	int x;
	void *bah;

	/* wakeup board and flip passthru OFF */

	RegWakeup(ba);
	RegOnpass(ba);

	vgaw(ba, 0x46e8, 0x16);
	vgaw(ba, 0x102, 1);
	vgaw(ba, 0x46e8, 0x0e);
	vgaw(ba, 0x3c3, 1);

	/* setup initial unchanging parameters */

	WSeq(ba, SEQ_ID_CLOCKING_MODE, 0x21);	/* 8 dot - display off */
	vgaw(ba, GREG_MISC_OUTPUT_W, 0xe1);     /* mem disable */

	WGfx(ba, GCT_ID_OFFSET_1, 0xec);        /* magic cookie */
	WSeq(ba, SEQ_ID_UNLOCK_EXT, 0x12);  	/* yum! cookies! */

	WSeq(ba, SEQ_ID_DRAM_CNTL, 0xb0);  
	WSeq(ba, SEQ_ID_RESET, 0x03);	    
	WSeq(ba, SEQ_ID_MAP_MASK, 0xff);
	WSeq(ba, SEQ_ID_CHAR_MAP_SELECT, 0x00); 
	WSeq(ba, SEQ_ID_MEMORY_MODE, 0x0e); /* a or 6? */
	WSeq(ba, SEQ_ID_EXT_SEQ_MODE, cltype == PICASSO ? 0x20 : 0x80);
	WSeq(ba, SEQ_ID_EEPROM_CNTL, 0x00);
	WSeq(ba, SEQ_ID_PERF_TUNE, 0x0a);
	WSeq(ba, SEQ_ID_SIG_CNTL, 0x02);

	WSeq(ba, SEQ_ID_MCLK_SELECT, 0x22);

	WCrt(ba, CRT_ID_PRESET_ROW_SCAN, 0x00);
	WCrt(ba, CRT_ID_CURSOR_START, 0x20);
	WCrt(ba, CRT_ID_CURSOR_END, 0x20);
	WCrt(ba, CRT_ID_START_ADDR_HIGH, 0x20);
	WCrt(ba, CRT_ID_START_ADDR_LOW, 0x20);
	WCrt(ba, CRT_ID_CURSOR_LOC_HIGH, 0x20);
	WCrt(ba, CRT_ID_CURSOR_LOC_LOW, 0x20);

	WCrt(ba, CRT_ID_UNDERLINE_LOC, 0x00);
	WCrt(ba, CRT_ID_MODE_CONTROL, 0xc3); /* a3 */
	WCrt(ba, CRT_ID_LINE_COMPARE, 0x00); /* ff */
	WCrt(ba, CRT_ID_EXT_DISP_CNTL, 0x22);
	WSeq(ba, SEQ_ID_CURSOR_STORE, 0x00);

	WGfx(ba, GCT_ID_SET_RESET, 0x00);
	WGfx(ba, GCT_ID_ENABLE_SET_RESET, 0x00);
	WGfx(ba, GCT_ID_DATA_ROTATE, 0x00);
	WGfx(ba, GCT_ID_READ_MAP_SELECT, 0x00);
	WGfx(ba, GCT_ID_GRAPHICS_MODE, 0x00);
	WGfx(ba, GCT_ID_MISC, 0x01);
	WGfx(ba, GCT_ID_COLOR_XCARE, 0x0f);
	WGfx(ba, GCT_ID_BITMASK, 0xff);
	WGfx(ba, GCT_ID_MODE_EXT, 0x28);

	for (x=0; x< 0x10; x++)
	    WAttr(ba, x, x);
	WAttr(ba, ACT_ID_ATTR_MODE_CNTL, 0x01);
	WAttr(ba, ACT_ID_OVERSCAN_COLOR, 0x00);
	WAttr(ba, ACT_ID_COLOR_PLANE_ENA, 0x0f);
	WAttr(ba, ACT_ID_HOR_PEL_PANNING, 0x00);
	WAttr(ba, ACT_ID_COLOR_SELECT, 0x00);
	WAttr(ba, 0x34, 0x00);	  /* EGS does this, why? */

	vgaw(ba, VDAC_MASK, 0xff);
	vgaw(ba, GREG_MISC_OUTPUT_W, 0xc3);

	WGfx(ba, GCT_ID_BLT_STAT_START, 0x40);
	WGfx(ba, GCT_ID_BLT_STAT_START, 0x00);

	/* colors initially set to greyscale */

	vgaw(ba, VDAC_ADDRESS_W, 0);
	for (x = 255; x >= 0 ; x--) {
		vgaw(ba, VDAC_DATA, x);
		vgaw(ba, VDAC_DATA, x);
		vgaw(ba, VDAC_DATA, x);
	}
}


int
cl_getvmode(gp, vm)
	struct grf_softc *gp;
	struct grfvideo_mode *vm;
{
	struct grfvideo_mode *gv;

	if (vm->mode_num && vm->mode_num > monitor_def_max)
		return(EINVAL);

	if (!vm->mode_num)
		vm->mode_num = (monitor_current - monitor_def) + 1;

	gv = monitor_def + (vm->mode_num - 1);

	if (gv->mode_num == 0)
		return(EINVAL);

	bcopy(gv, vm, sizeof(struct grfvideo_mode));

	return(0);
}


int
cl_setvmode(gp, mode, txtonly)
	struct grf_softc *gp;
	unsigned mode;
	int txtonly;
{
	struct grfvideo_mode *gv;
	int error;

	if (!mode || (mode > monitor_def_max) || txtonly)
		return(EINVAL);

	if (monitor_def[mode-1].mode_num == 0)
		return(EINVAL);

	monitor_current = monitor_def + (mode - 1);
	error = cl_load_mon (gp, monitor_current) ? 0 : EINVAL;
	return(error);
}

void
cl_off(gp)
struct grf_softc *gp;
{
	char *ba = gp->g_regkva;

	/* we'll put the pass-through on for cc ite and set Full Bandwidth
	 * bit on just in case it didn't work...but then it doesn't matter
	 * does it? =)
	 */
	RegOnpass(ba);
	WSeq(ba, SEQ_ID_CLOCKING_MODE, 0x21);
}

/*
 * Change the mode of the display.
 * Return a UNIX error number or 0 for success.
 */
cl_mode(gp, cmd, arg, a2, a3)
	register struct grf_softc *gp;
	int cmd;
	void *arg;
	int a2, a3;
{
	switch (cmd) {
	case GM_GRFON:
		cl_setvmode (gp, monitor_default + 1, 0);
		return(0);

	case GM_GRFOFF:
		cl_off(gp);
		return(0);

	case GM_GRFCONFIG:
		return(0);

	case GM_GRFGETVMODE:
		return(cl_getvmode (gp, (struct grfvideo_mode *) arg));

	case GM_GRFSETVMODE:
		return(cl_setvmode (gp, *(unsigned *) arg, 0));

	case GM_GRFGETNUMVM:
		*(int *)arg = monitor_def_max;
		return(0);

	case GM_GRFIOCTL:
		return(cl_ioctl (gp, (int) arg, (caddr_t) a2));

	default:
		break;
	}

	return(EINVAL);
}

int
cl_ioctl (gp, cmd, data)
	register struct grf_softc *gp;
	int cmd;
	void *data;
{
	switch (cmd) {
	case GRFIOCGSPRITEPOS:
		/*return(cl_getspritepos (gp, (struct grf_position *) data));*/
		break;

	case GRFIOCSSPRITEPOS:
		/*return(cl_setspritepos (gp, (struct grf_position *) data));*/
		break;

	case GRFIOCSSPRITEINF:
		/*return(cl_setspriteinfo (gp, (struct grf_spriteinfo *) data));*/
		break;

	case GRFIOCGSPRITEINF:
		/*return(cl_getspriteinfo (gp, (struct grf_spriteinfo *) data));*/
		break;

	case GRFIOCGSPRITEMAX:
		/*return(cl_getspritemax (gp, (struct grf_position *) data));*/
		break;

	case GRFIOCGETCMAP:
		return(cl_getcmap (gp, (struct grf_colormap *) data));

	case GRFIOCPUTCMAP:
		return(cl_putcmap (gp, (struct grf_colormap *) data));

	case GRFIOCBITBLT:
		/*return(cl_bitblt (gp, (struct grf_bitblt *) data));*/
		break;

	case GRFTOGGLE:
		return(cl_toggle (gp,0));

	case GRFIOCSETMON:
		return(cl_setmonitor (gp, (struct grfvideo_mode *)data));

	}
	return(EINVAL);
}

int
cl_setmonitor(gp, gv)
	struct grf_softc *gp;
	struct grfvideo_mode *gv;
{
	struct grfvideo_mode *md;    	

	if (!gv->mode_num || gv->mode_num > monitor_def_max)
		return(EINVAL);

	if ((gv->depth == 24 && (gv->pixel_clock*3) > MAXPIXELCLOCK) ||
		(gv->pixel_clock > MAXPIXELCLOCK))
		return(EINVAL);

	md = monitor_def + (gv->mode_num-1);

	bcopy(gv, md, sizeof(struct grfvideo_mode));
	return(0);
}

int
cl_getcmap (gfp, cmap)
	struct grf_softc *gfp;
	struct grf_colormap *cmap;
{
	volatile unsigned char *ba;
	u_char red[256], green[256], blue[256], *rp, *gp, *bp;
	short x;
	int error;

	if (cmap->count == 0 || cmap->index >= 256)
		return 0;

	if (cmap->index + cmap->count > 256)
		cmap->count = 256 - cmap->index;

	ba = gfp->g_regkva;
	/* first read colors out of the chip, then copyout to userspace */
	vgaw (ba, VDAC_ADDRESS_W, cmap->index);
	x = cmap->count - 1;

/* Some sort 'o Magic. Spectrum has some changes on the board to speed
 * up 15 and 16Bit modes. They can access these modes with easy-to-programm
 * rgbrgbrgb instead of rrrgggbbb. Side effect: when in 8Bit mode, rgb
 * is swapped to bgr. I wonder if we need to check for 8Bit though, ill
 */

	switch (cltype) {  
	case SPECTRUM: 
	case PICCOLO:
		rp = blue + cmap->index;  
		gp = green + cmap->index;
		bp = red + cmap->index;
		break;
	case PICASSO:
		rp = red + cmap->index;  
		gp = green + cmap->index;
		bp = blue + cmap->index;
		break;
	}

	do {
		*rp++ = vgar (ba, VDAC_DATA) << 2;
		*gp++ = vgar (ba, VDAC_DATA) << 2;
		*bp++ = vgar (ba, VDAC_DATA) << 2;
	} while (x-- > 0);

	if (!(error = copyout (red + cmap->index, cmap->red, cmap->count))
	    && !(error = copyout (green + cmap->index, cmap->green, cmap->count))
	    && !(error = copyout (blue + cmap->index, cmap->blue, cmap->count)))
		return(0);

	return(error);
}

int
cl_putcmap (gfp, cmap)
	struct grf_softc *gfp;
	struct grf_colormap *cmap;
{
	volatile unsigned char *ba;
	u_char red[256], green[256], blue[256], *rp, *gp, *bp;
	short x;
	int error;

	if (cmap->count == 0 || cmap->index >= 256)
		return(0);

	if (cmap->index + cmap->count > 256)
		cmap->count = 256 - cmap->index;

	/* first copy the colors into kernelspace */
	if (!(error = copyin (cmap->red, red + cmap->index, cmap->count))
	    && !(error = copyin (cmap->green, green + cmap->index, cmap->count))
	    && !(error = copyin (cmap->blue, blue + cmap->index, cmap->count))) {
		ba = gfp->g_regkva;
		vgaw (ba, VDAC_ADDRESS_W, cmap->index);
		x = cmap->count - 1;

/* Spectrum & piccolo convenience mode.  See above. */

		switch (cltype) { 
		case SPECTRUM: 
		case PICCOLO:
			rp = blue + cmap->index;
			gp = green + cmap->index;
			bp = red + cmap->index;
			break;
		case PICASSO:
			rp = red + cmap->index;
			gp = green + cmap->index;
			bp = blue + cmap->index;
			break;
		}   

		do {
			vgaw (ba, VDAC_DATA, *rp++ >> 2);
			vgaw (ba, VDAC_DATA, *gp++ >> 2);
			vgaw (ba, VDAC_DATA, *bp++ >> 2);
		} while (x-- > 0);
		return(0);
	}
	else
		return(error);
}


int
cl_toggle(gp,wopp)
	struct grf_softc *gp;
	unsigned short wopp; /* don't need that one yet, ill */
{
	volatile unsigned char *ba;

	ba = gp->g_regkva;

	if (pass_toggle) {
		RegOffpass(ba);
	} else {
		/* This was in the original.. is it needed? */
		if (cltype == PICASSO || cltype == PICCOLO)
			RegWakeup(ba);
		RegOnpass(ba);
	}
	return(0);
}

static void
CompFQ(fq,num,denom)
u_int fq;
u_char *num;
u_char *denom;
{
#define OSC	14318180
/* OK, here's what we're doing here:
 * 
 *             OSC * NUMERATOR
 *	VCLK = -------------------  Hz
 *             DENOMINATOR * (1+P)
 *
 * so we're given VCLK and we should give out some useful
 * values....
 *
 * NUMERATOR is 6 bits wide 
 * DENOMINATOR is 5 bits wide with bit P in the same char as bit 0.
 *
 * We run through all the possible combinations (64*32+64*16) and
 * return the values which deviate the least from the chosen frequency.
 * 
 */
#define OSC     14318180
#define count(n,d,p)    ((OSC * n)/(d * (1+p)))

	unsigned char n, d, p, minn, mind, minp;
	unsigned long err, minerr;

/*
numer = 0x00 - 0x3f
denom = 0x00 - 0x1f (1) 0x20 - 0x3e (even)
*/

	/* find lowest error in 3072 iterations. 
	 */
	minerr = fq;
	minn = 0;
	mind = 0;
	p = 0;

	for (d=1; d < 0x20; d++) {
		for (n=1; n < 0x40; n++) {
			err = abs(count(n, d, p) - fq);
			if (err < minerr) {
				minerr = err;
				minn = n;
				mind = d;
				minp = p;
			}
		}
		if (d == 0x1f && p == 0) {
			p = 1;
			d = 0x0f;
		}
	}

	*num = minn;
	*denom = (mind << 1) | minp;
	if (minerr > 500000)
	    printf("Warning: CompFQ minimum error = %d\n", minerr);
	return;
}

int
cl_mondefok(mdp)
	struct grfvideo_mode *mdp;
{
	if (mdp->mode_num == 0 || mdp->pixel_clock > MAXPIXELCLOCK)
		return(0);

	switch(mdp->depth) {
	case 8:
	case 16:
	case 24:
		return(1);
	default:
		return(0);
	}
}

int
cl_load_mon(gp, gv)
	struct grf_softc *gp;
	struct grfvideo_mode *gv;
{
	struct grfinfo *gi;
	volatile unsigned char *ba;
	volatile unsigned char *fb;
	unsigned char num0,denom0;
	unsigned short HT,HDE,HBS,HBE,HSS,HSE,VDE,VBS,VBE,VSS,VSE,VT;
	unsigned short clkdiv, clksel;
	char LACE, DBLSCAN;
	int uplim, lowlim;

	if (!cl_mondefok(gv)) {
		printf("mondef not ok\n");
		return(0);
	}

	ba = gp->g_regkva;
	fb = gp->g_fbkva;

	/* provide all needed information in grf device-independant
	 * locations */
	gp->g_data 		= (caddr_t) gv;
	gi = &gp->g_display;
	gi->gd_regaddr	 	= (caddr_t) ztwopa (ba);
	gi->gd_regsize		= 64*1024;
	gi->gd_fbaddr		= (caddr_t) kvtop (fb);
	gi->gd_fbsize		= cl_fbsize;
	gi->gd_colors		= 1 << gv->depth;
	gi->gd_planes		= gv->depth;
	gi->gd_fbwidth	    	= gv->disp_width;
	gi->gd_fbheight	        = gv->disp_height;
	gi->gd_fbx	        = 0;
	gi->gd_fby	        = 0;
	gi->gd_dwidth	        = gv->disp_width;
	gi->gd_dheight	        = gv->disp_height;
	gi->gd_dx	        = 0;
	gi->gd_dy	        = 0;

	/* get display mode parameters
	 *
	 * grfvideo_mode works slightly different from MonDef:
	 *  	* vertical and horizontal parameters are  real 
	 *        parameters (need no adjustment)
	 *  	* there's no lace or doublescan flag, so we need
	 *  	  to figure it out ourselves.
	 */
	HBS = gv->hblank_start;
	HBE = gv->hblank_stop;
	HSS = gv->hsync_start;
	HSE = gv->hsync_stop;
	HT  = gv->htotal;
	VBS = gv->vblank_start;
	VSS = gv->vsync_start;
	VSE = gv->vsync_stop;
	VBE = gv->vblank_stop;
	VT  = gv->vtotal;

	HDE = (gv->disp_width+3)/8 - 1; /*HBS;*/
	VDE = gv->disp_height-1;

	/* figure out whether lace or dblscan is needed */

	uplim = gv->disp_height + (gv->disp_height / 4);
	lowlim = gv->disp_height - (gv->disp_height / 4); 
	LACE = (((VT*2) > lowlim) && ((VT*2) < uplim)) ? 1 : 0;
	DBLSCAN = (((VT/2) > lowlim) && ((VT/2) < uplim)) ? 1 : 0;

	/* adjustments */

	if (LACE)
		VDE /= 2;

	/* Set clock */

	CompFQ((gv->depth == 24) ? gv->pixel_clock*3 : gv->pixel_clock, 
		&num0, &denom0);
	WSeq(ba, SEQ_ID_VCLK_0_NUM, num0);
	WSeq(ba, SEQ_ID_VCLK_0_DENOM, denom0);

	/* load display parameters into board */

	WCrt(ba, CRT_ID_HOR_TOTAL, HT);
	WCrt(ba, CRT_ID_HOR_DISP_ENA_END, HDE);
	WCrt(ba, CRT_ID_START_HOR_BLANK, HBS);
	WCrt(ba, CRT_ID_END_HOR_BLANK, HBE & 0x1f);
	WCrt(ba, CRT_ID_START_HOR_RETR, HSS);
	WCrt(ba, CRT_ID_END_HOR_RETR,
		(HSE & 0x1f) | 
		(HBE & 0x20 ? 0x80 : 0x00) );
	WCrt(ba, CRT_ID_VER_TOTAL, VT);
	WCrt(ba, CRT_ID_OVERFLOW,
		0x10 |
		(VT  & 0x100 ? 0x01 : 0x00) |
		(VDE & 0x100 ? 0x02 : 0x00) |
		(VSS & 0x100 ? 0x04 : 0x00) |
		(VBS & 0x100 ? 0x08 : 0x00) |
		(VT  & 0x200 ? 0x20 : 0x00) |
		(VDE & 0x200 ? 0x40 : 0x00) |
		(VSS & 0x200 ? 0x80 : 0x00) );

	WCrt(ba, CRT_ID_CHAR_HEIGHT,
		0x40 |
		(DBLSCAN ? 0x80 : 0x00) |
		(VBS & 0x200 ? 0x20 : 0x00) );

	WCrt(ba, CRT_ID_START_ADDR_HIGH, 0x00);
	WCrt(ba, CRT_ID_START_ADDR_LOW, 0x00);

	WCrt(ba, CRT_ID_START_VER_RETR, VSS);
	WCrt(ba, CRT_ID_END_VER_RETR, VSE % 0x60);
	WCrt(ba, CRT_ID_VER_DISP_ENA_END, VDE);
	WCrt(ba, CRT_ID_START_VER_BLANK, VBS);
	WCrt(ba, CRT_ID_END_VER_BLANK, VBE);

	WCrt(ba, CRT_ID_LINE_COMPARE, 0xff);
	WCrt(ba, CRT_ID_LACE_END, HT/2); /* MW/16 */
	WCrt(ba, CRT_ID_LACE_CNTL,
		(LACE ? 0x01 : 0x00) |
		(HBE & 0x40 ? 0x10 : 0x00) |
		(HBE & 0x80 ? 0x20 : 0x00) |
		(VBE & 0x100 ? 0x40 : 0x00) |
		(VBE & 0x200 ? 0x80 : 0x00) );

	/* depth dependent stuff */

	if (gv->depth == 8) {
		HDE = HDE;
		clkdiv = 0;
	} else if (gv->depth == 16) {
		HDE = HDE * 2;
		clkdiv = 3;
	} else if (gv->depth == 24) {
		HDE = HDE * 3;
		clkdiv = 2;
	}
	WGfx(ba, GCT_ID_GRAPHICS_MODE, 0x40);
	WSeq(ba, SEQ_ID_EXT_SEQ_MODE, 
		0x01 | 
		(cltype == PICASSO ? 0x20: 0x80) |
		(clkdiv << 1) );

	vgaw(ba, VDAC_MASK, 0xff);
	vgar(ba, VDAC_MASK);
	delay(10000);
	vgar(ba, VDAC_MASK);
	delay(10000);
	vgar(ba, VDAC_MASK);
	delay(10000);
	vgar(ba, VDAC_MASK);
	delay(10000);
	switch (gv->depth) {
	case 8:
	    vgaw(ba, VDAC_MASK, 0);
	    break;
	case 16:
	    vgaw(ba, VDAC_MASK, 0xb1); /* a0 */
	    break;
	case 24:
	    vgaw(ba, VDAC_MASK, 0xb5); /* c5 */
	    break;
	}

	WCrt(ba, CRT_ID_OFFSET,  /* HDE & 0xff */
		(gv->depth == 8) ? (gv->disp_width / 8) :
		(gv->depth == 16) ? (gv->disp_width / 4) :
		(gv->depth == 24) ? ((gv->disp_width / 8) * 3) : 0);
	WCrt(ba, CRT_ID_EXT_DISP_CNTL, 
		0x22  
		/*(HDE > 0xff ? 0x10 : 0x00)*/);
	WSeq(ba, SEQ_ID_CURSOR_ATTR, 0);

	WSeq(ba, SEQ_ID_CLOCKING_MODE, 0x01);

	/* Pass-through */

	RegOffpass(ba);

	return(1);
}

#endif	/* GRF_CL5426 */

