#ifndef lint
static char sccsid[] = "@(#)cgfour.c 1.1 94/10/31 SMI";
#endif

/*
 * Copyright 1986 by Sun Microsystems, Inc.
 */

/*
 * Color frame buffer with overlay plane (cg4) driver
 */

#include "cgfour.h"
#include "win.h"

#include <sys/param.h>
#include <sys/time.h>
#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/map.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vmmac.h>

#include <sun/fbio.h>

#include <sundev/mbvar.h>
#include <sundev/cg4reg.h>
#include <sundev/p4reg.h>

#include <machine/eeprom.h>
#include <machine/enable.h>
#include <machine/mmu.h>
#include <machine/pte.h>

#include <mon/sunromvec.h>

#include <pixrect/pixrect.h>
#include <pixrect/pr_impl_util.h>
#include <pixrect/pr_planegroups.h>
#include <pixrect/cg4var.h>

/* system enable register */
#ifdef sun3x
extern u_short enablereg;
#else
extern u_char enablereg;
#endif sun3x

#ifdef CG4A
/* colormap update firsthalf, toolate error counts */
int cg4_firsthalf = 0;
int cg4_toolate = 0;
#endif CG4A

/* driver per-unit data */
struct cg4_softc {
	short		type;		/* type code (0 = A, 1 = B) */
	short		flags;		/* internal flags */
#define	CG4_UPDATE_PENDING	1	/* colormap update in progress */
#undef	CG4_OVERLAY_CMAP		/* also defined in cg4var.h (urk) */
#define	CG4_OVERLAY_CMAP	2	/* has overlay colormap */
#define	CG4_OWNER_WANTS_COLOR	4	/* auto-init should display color */
	caddr_t		fb[CG4_NFBS];	/* pointers to frame buffer sections */
	int		w, h;		/* resolution */
	int		size;		/* total size of frame buffer */
	int		bw2size;	/* total size of overlay plane */
	struct proc	*owner;		/* owner of the frame buffer */
	struct fbgattr	gattr;		/* current attributes */
	u_long		*p4reg;		/* pointer to P4 register */

	/* color map stuff */
	union cg4_cmap {
#ifdef CG4A
		struct cg4a_cmap a;
#endif CG4A
		struct cg4b_cmap b;
	} *cmap;			/* pointer to hardware colormap */
	union {				/* shadow overlay color map */
		u_char	omap_char[3][2];
	} omap_image;
#define	omap_red	omap_image.omap_char[0]
#define	omap_green	omap_image.omap_char[1]
#define	omap_blue	omap_image.omap_char[2]
#define	omap_rgb	omap_image.omap_char[0]
	u_short		cmap_index;	/* colormap update index */
	u_short		cmap_count;	/* colormap update count */
	union {				/* shadow color map */
		u_long	cmap_long[CG4_CMAP_ENTRIES * 3 / sizeof(u_long)];
		u_char	cmap_char[3][CG4_CMAP_ENTRIES];
	} cmap_image;
#define	cmap_red	cmap_image.cmap_char[0]
#define	cmap_green	cmap_image.cmap_char[1]
#define	cmap_blue	cmap_image.cmap_char[2]
#define	cmap_rgb	cmap_image.cmap_char[0]

#if NWIN > 0
	Pixrect		pr;
	struct cg4_data	cgd;
#endif NWIN > 0
} cg4_softc[NCGFOUR];

/* probe size -- enough for the P4 register + colormap/status register */
#define	CG4_PROBESIZE	(NBPG + sizeof *cg4_softc[0].cmap)

/* Mainbus device data */
int cgfourprobe(), cgfourattach(), cgfourpoll();
struct  mb_device *cgfourinfo[NCGFOUR];
struct  mb_driver cgfourdriver = {
	cgfourprobe, 0, cgfourattach, 0, 0, cgfourpoll,
	CG4_PROBESIZE, "cgfour", cgfourinfo, 0, 0, 0, 0
};

/* default structure for FBIOGTYPE ioctl */
static struct fbtype cg4typedefault =  {
/*	type           h  w  depth cms size */
	FBTYPE_SUN2BW, 0, 0, 1,    2,   0
};

/* default structure for FBIOGATTR ioctl */
static struct fbgattr cg4attrdefault =  {
/*	real_type         owner */
	FBTYPE_SUN4COLOR, 0,
/* fbtype: type             h  w  depth cms  size */
	{ FBTYPE_SUN4COLOR, 0, 0, 8,    256,  0 }, 
/* fbsattr: flags           emu_type       dev_specific */
	{ FB_ATTR_AUTOINIT, FBTYPE_SUN2BW, { 0 } },
/*        emu_types */
	{ FBTYPE_SUN4COLOR, FBTYPE_SUN2BW, -1, -1}
};

/* frame buffer description table */
static struct cg4_fbdesc {
	short	depth;			/* depth, bits */
	short	group;			/* plane group */
	int	allplanes;		/* initial plane mask */
} cg4_fbdesc[CG4_NFBS + 1] = {
	{ 1, PIXPG_OVERLAY,		  0 },
	{ 1, PIXPG_OVERLAY_ENABLE,	  0 },
	{ 8, PIXPG_8BIT_COLOR,		255 }
};
#define	CG4_FBINDEX_OVERLAY	0
#define	CG4_FBINDEX_ENABLE	1
#define	CG4_FBINDEX_COLOR	2

/* initial active frame buffer */
#ifndef	CG4_INITFB
#define	CG4_INITFB	CG4_FBINDEX_COLOR
#endif

/* macros */
typedef IF68000(u_long, u_char) CMAP_T;

#define ITEMSIN(array)  (sizeof (array) / sizeof (array)[0])
#define	ENDOF(array)	((array) + ITEMSIN(array))

/* enable/disable interrupt */
#define	cg4_int_enable(softc)	_STMT( \
	if ((softc)->p4reg) \
		*(softc)->p4reg |= P4_REG_INTEN | P4_REG_INTCLR; \
	setintrenable(1);)

#define	cg4_int_disable(softc)	_STMT( \
	setintrenable(0); \
	if ((softc)->p4reg) { \
		int x; \
		x = spl4(); \
		*(softc)->p4reg &= ~P4_REG_INTEN; \
		(void) splx(x); \
	} )

/* check if color map update is pending */
#define	cgfour_update_pending(softc)	((softc)->flags & CG4_UPDATE_PENDING)

/* 
 * Compute color map update parameters: starting index and count.
 * If count is already nonzero, adjust values as necessary.
 * Zero count argument indicates overlay color map update desired
 * (may cause bus error if hardware doesn't have overlay color map).
 */
#ifdef lint
#define	cgfour_update_cmap(softc, index, count)	\
	(softc)->cmap_index = (index) + (count);
#else lint
#define	cgfour_update_cmap(softc, index, count)	\
	if (count) \
		if ((softc)->cmap_count) { \
			if ((index) + (count) > \
				(softc)->cmap_count += (softc)->cmap_index) \
				(softc)->cmap_count = (index) + (count); \
			if ((index) < (softc)->cmap_index) \
				(softc)->cmap_index = (index); \
			(softc)->cmap_count -= (softc)->cmap_index; \
		} \
		else { \
			(softc)->cmap_index = (index); \
			(softc)->cmap_count = (count); \
		} \
	(softc)->flags |= CG4_UPDATE_PENDING
#endif lint

/* forward references */
static void cgfour_set_enable_plane();
static void cgfour_reset_cmap();
static void cgfour_cmap_bcopy();
static void cgfour_reset_b();
#ifdef CG4A
static void cgfourintr_a();
#endif CG4A
static void cgfourintr_b();


#if NWIN > 0

/* SunWindows specific stuff */

/* kernel pixrect ops vector */
struct pixrectops cg4_ops = {
	mem_rop,
	cg4_putcolormap,
	cg4_putattributes,
#	ifdef _PR_IOCTL_KERNEL_DEFINED
        cg4_ioctl
#	endif
};

cg4_putcolormap(pr, index, count, red, green, blue)
	Pixrect *pr;
	int index, count;
	unsigned char *red, *green, *blue;
{
	register struct cg4_softc *softc = &cg4_softc[cg4_d(pr)->fd];
	register u_int rindex = (u_int) index;
	register u_int rcount = (u_int) count;
	register u_char *map;
	register u_int entries;

	switch (softc->cgd.active) {
	case CG4_FBINDEX_COLOR:
		map = softc->cmap_rgb;
		entries = CG4_CMAP_ENTRIES;
		break;

	case CG4_FBINDEX_OVERLAY:
		if (softc->flags & CG4_OVERLAY_CMAP) {
			map = softc->omap_rgb;
			entries = 2;
			break;
		}
		/* fall through */

	default:
		if (mem_putcolormap(&softc->pr, index, count, 
			red, green, blue))
			return PIX_ERR;

		softc->cgd.fb[softc->cgd.active].mprp.mpr.md_flags =
			softc->cgd.mprp.mpr.md_flags;

		return 0;
	}

	/* check arguments */
	if (rindex >= entries || 
		rindex + rcount > entries) 
		return PIX_ERR;

	if (rcount == 0)
		return 0;

	/* lock out updates of the hardware colormap */
	if (cgfour_update_pending(softc))
		cg4_int_disable(softc);

	switch (softc->type) {
	case 0:
#ifdef CG4A
		map += rindex;
		bcopy((caddr_t) red,   (caddr_t) map, rcount);
		map += entries;
		bcopy((caddr_t) green, (caddr_t) map, rcount);
		map += entries;
		bcopy((caddr_t) blue,  (caddr_t) map, rcount);
		break;
	case 1:
#endif CG4A
		map += rindex * 3;
		cgfour_cmap_bcopy(red,   map++, rcount);
		cgfour_cmap_bcopy(green, map++, rcount);
		cgfour_cmap_bcopy(blue,  map,   rcount);
		break;
	}

	/* overlay colormap update */
	if (entries <= 2)
		rcount = 0;

	cgfour_update_cmap(softc, rindex, rcount);

	/* enable interrupt so we can load the hardware colormap */
	cg4_int_enable(softc);

	return 0;
}

#endif	NWIN > 0

/*
 * Determine if a cgfour exists at the given address.
 */
/*ARGSUSED*/
cgfourprobe(reg, unit)
	caddr_t reg;
	int unit;
{
	static struct cg4_ptab {
		int	cmap_addr;		/* color map address */
		int	fb_addr[CG4_NFBS];	/* frame buffer addresses */
	} cg4_ptab[CG4_NTYPES] = {
#ifdef CG4A
	{	CG4A_PGT_CMAP | btop(CG4A_ADDR_CMAP), {
	  	CG4A_PGT_FB | btop(CG4A_ADDR_OVERLAY), 
		CG4A_PGT_FB | btop(CG4A_ADDR_ENABLE), 
		CG4A_PGT_FB | btop(CG4A_ADDR_COLOR)
	} },
#endif CG4A
	{	CG4B_PGT_CMAP | btop(CG4B_ADDR_CMAP), {
		CG4B_PGT_FB | btop(CG4B_ADDR_OVERLAY), 
		CG4B_PGT_FB | btop(CG4B_ADDR_ENABLE), 
		CG4B_PGT_FB | btop(CG4B_ADDR_COLOR) 
	} } };

	register struct cg4_softc *softc = &cg4_softc[unit];
	register struct cg4_ptab *ptab = cg4_ptab;
	int pages1, pages8;
	u_long kmx, rmalloc();

	/* make sure this unit is not in use */
	if (softc->cmap)
		return 0;

	/* probe the colormap and determine the cg4 type */
	{
		register union cg4_cmap *cmap = 
			(union cg4_cmap *) (reg + NBPG);

		softc->type = 0;

		while (1) {
			fbmapin((caddr_t) cmap, ptab->cmap_addr, sizeof *cmap);

			if (peekc((char *) cmap) != -1) 
				break;

			if (++ptab >= ENDOF(cg4_ptab))
				return 0;

			softc->type++;
		}

		softc->cmap = cmap;
	}

	/* 
	 * Determine frame buffer resolution:
	 *
	 * 1. If this is a P4 frame buffer, use the P4 type code.
	 * 2. Otherwise, assume it's a 3/110.  Use the EEPROM size code
	 *    to guess whether it's 1152 x 900 or 1024 x 1024.
	 */
	{
		int id;

		if ((id = p4probe(reg, &softc->w, &softc->h)) >= 0) {
			if (id != P4_ID_COLOR8P1)
				return 0;

			softc->p4reg = (u_long *) reg;
		}
		else {
			softc->p4reg = 0;

			softc->w = 1152;
			softc->h = 900;

			if (EEPROM->ee_diag.eed_scrsize == EED_SCR_1024X1024) {
				softc->w = 1024;
				softc->h = 1024;
			}
		}

		pages1 = btoc(mpr_linebytes(softc->w, 1) * softc->h);
		pages8 = btoc(mpr_linebytes(softc->w, 8) * softc->h);
	}

	softc->size = 0;
	softc->bw2size = ctob(pages1);

	/* map frame buffer sections */
	{
		register caddr_t fbaddr;
		register int fb;
		register int s;

		/* allocate kernel space for all sections */
		s = splhigh();
		kmx = rmalloc(kernelmap, (long)(pages1 + pages1 + pages8));
		(void)splx(s);
		if (kmx == 0) {
			printf("cgfourprobe: out of kernelmap\n");
			fbmapout((caddr_t) softc->cmap, sizeof *softc->cmap);
			return (0);
		}

		/* compute virtual addr. of first section */
		fbaddr = (caddr_t) kmxtob(kmx);

		/* map each frame buffer section */
		for (fb = 0; fb < CG4_NFBS; fb++) {
			register int pages;

			pages = cg4_fbdesc[fb].depth == 1 ? pages1 : pages8;

			fbmapin(fbaddr, ptab->fb_addr[fb], ctob(pages));

			softc->fb[fb] = fbaddr;
			softc->size += ctob(pages);

			if (peekc((char *) fbaddr) != -1) {
				fbaddr += ctob(pages);
				continue;
			}

			/* this section missing, map out everything */
			fbmapout((caddr_t) softc->cmap, sizeof *softc->cmap);
			fbmapout(softc->fb[0], softc->size);

			s = splhigh();
			rmfree(kernelmap, 
				(long)(pages1 + pages1 + pages8), kmx);
			(void)splx(s);

			return (0);
		}
	}

	/* probe succeeded */
	return CG4_PROBESIZE;
}


cgfourattach(md)
	register struct mb_device *md;
{
	register struct cg4_softc *softc = &cg4_softc[md->md_unit];

	softc->flags = 0;
	softc->owner = NULL;

 	softc->gattr = cg4attrdefault;
	softc->gattr.fbtype.fb_height = softc->h;
	softc->gattr.fbtype.fb_width =  softc->w;
	softc->gattr.fbtype.fb_size = softc->size;

	/* 
	 * Initialize hardware colormap and software colormap images.
	 * It might make sense to read the hardware colormap here. 
	 */
#ifdef CG4A
	if (softc->type) {
#endif CG4A
		cgfour_reset_b(softc);
		softc->flags = CG4_OVERLAY_CMAP;
#ifdef CG4A
	}
#endif CG4A
	cgfour_reset_cmap(softc, softc->omap_rgb, 2);
	cgfour_reset_cmap(softc, softc->cmap_rgb, CG4_CMAP_ENTRIES);
	cgfour_update_cmap(softc, 0, CG4_CMAP_ENTRIES);
}

cgfouropen(dev, flag)
	dev_t dev;
	int flag;
{
	return fbopen(dev, flag, NCGFOUR, cgfourinfo);
}

/*ARGSUSED*/
cgfourclose(dev, flag)
	dev_t dev;
	int flag;
{
	register struct cg4_softc *softc = &cg4_softc[minor(dev)];

	softc->flags &= CG4_UPDATE_PENDING | CG4_OVERLAY_CMAP;
	softc->owner = NULL;

 	softc->gattr = cg4attrdefault;
	softc->gattr.fbtype.fb_height = softc->h;
	softc->gattr.fbtype.fb_width =  softc->w;
	softc->gattr.fbtype.fb_size = softc->size;

	/* re-initialize overlay colormap (this is a hack!) */
	if (softc->flags & CG4_OVERLAY_CMAP) {
		if (cgfour_update_pending(softc))
			cg4_int_disable(softc);
		cgfour_reset_cmap(softc, softc->omap_rgb, 2);
		cgfour_update_cmap(softc, 0, 0);
		cg4_int_enable(softc);
	}
}

/*ARGSUSED*/
cgfourmmap(dev, off, prot)
	dev_t dev;
	off_t off;
	int prot;
{
	register struct cg4_softc *softc = &cg4_softc[minor(dev)];

	/* 
	 * Initialize overlay enable plane if necessary.
	 *
	 * If the owner wants color (as inferred from its use of the
	 * FBIOGATTR ioctl), set to display color planes.
	 * (This is a hack.)
	 *
	 * Note this will work for a non-owner process as long as
	 * the autoinit bit is set.
	 */
	if (softc->gattr.sattr.flags & FB_ATTR_AUTOINIT) {
		cgfour_set_enable_plane(softc->fb[CG4_FBINDEX_ENABLE], 
			softc->w, softc->h,
			softc->flags & CG4_OWNER_WANTS_COLOR ? 0 : 1);

		softc->gattr.sattr.flags &= ~FB_ATTR_AUTOINIT;
	}

	if ((u_int) off >= softc->size)
		return -1;

	return fbgetpage(softc->fb[0] + off);
}

/*ARGSUSED*/
cgfourioctl(dev, cmd, data, flag)
	dev_t dev;
	int cmd;
	caddr_t data;
	int flag;
{
	register struct cg4_softc *softc = &cg4_softc[minor(dev)];
	static u_char cmapbuf[CG4_CMAP_ENTRIES];

	switch (cmd) {

	case FBIOPUTCMAP: 
	case FBIOGETCMAP: {
		register struct fbcmap *cmap = (struct fbcmap *) data;
		register u_int index = (u_int) cmap->index;
		register u_int count = (u_int) cmap->count;
		register u_char *map;
		register u_int entries;

		switch (PIX_ATTRGROUP(index)) {
		case 0:
		case PIXPG_8BIT_COLOR:
			map = softc->cmap_rgb;
			entries = CG4_CMAP_ENTRIES;
			break;

		case PIXPG_OVERLAY:
			if (softc->flags & CG4_OVERLAY_CMAP) {
				map = softc->omap_rgb;
				entries = 2;
				break;
			}
			/* fall through */

		default:
			return EINVAL;
		}

		if ((index &= PIX_ALL_PLANES) >= entries || 
			index + count > entries)
			return EINVAL;

		if (count == 0)
			return 0;

		if (cmd == FBIOPUTCMAP) { 
			register int error;

			if (cgfour_update_pending(softc))
				cg4_int_disable(softc);

			switch (softc->type) {
			case 0:
#ifdef CG4A
				error = copyin((caddr_t) cmap->red, 
					(caddr_t) (map += index), count) ||
					copyin((caddr_t) cmap->green, 
					(caddr_t) (map += entries), count) ||
					copyin((caddr_t) cmap->blue, 
					(caddr_t) (map += entries), count);
				break;
			case 1:
#endif CG4A
				map += index * 3;

				if (error = copyin((caddr_t) cmap->red, 
					(caddr_t) cmapbuf, count))
					break;
				cgfour_cmap_bcopy(cmapbuf, map++, count);

				if (error = copyin((caddr_t) cmap->green, 
					(caddr_t) cmapbuf, count))
					break;
				cgfour_cmap_bcopy(cmapbuf, map++, count);

				if (error = copyin((caddr_t) cmap->blue, 
					(caddr_t) cmapbuf, count))
					break;
				cgfour_cmap_bcopy(cmapbuf, map, count);

				break;
			}

			if (error) {
				if (cgfour_update_pending(softc)) 
					cg4_int_enable(softc);
				return EFAULT;
			}

			/* overlay colormap update */
			if (entries <= 2)
				count = 0;

			cgfour_update_cmap(softc, index, count);
			cg4_int_enable(softc);
		}
		else {
			/* FBIOGETCMAP */
			switch (softc->type) {
			case 0:
#ifdef CG4A
				if (copyout((caddr_t) (map += index),
					(caddr_t) cmap->red, count) ||
					copyout((caddr_t) (map += entries),
					(caddr_t) cmap->green, count) ||
					copyout((caddr_t) (map += entries),
					(caddr_t) cmap->blue, count))
					return EFAULT;

				break;
			case 1: 
#endif CG4A
				map += index * 3;
				cgfour_cmap_bcopy(cmapbuf, map++, -count);
				if (copyout((caddr_t) cmapbuf,
					(caddr_t) cmap->red, count))
					return EFAULT;

				cgfour_cmap_bcopy(cmapbuf, map++, -count);
				if (copyout((caddr_t) cmapbuf,
					(caddr_t) cmap->green, count))
					return EFAULT;

				cgfour_cmap_bcopy(cmapbuf, map, -count);
				if (copyout((caddr_t) cmapbuf,
					(caddr_t) cmap->blue, count))
					return EFAULT;

				break;
			}
		}
	}
	break;

	case FBIOSATTR: {
		register struct fbsattr *sattr = (struct fbsattr *) data;

#ifdef ONLY_OWNER_CAN_SATTR
		/* this can only happen for the owner */
		if (softc->owner != u.u_procp)
			return ENOTTY;
#endif ONLY_OWNER_CAN_SATTR

		softc->gattr.sattr.flags = sattr->flags;

		if (sattr->emu_type != -1)
			softc->gattr.sattr.emu_type = sattr->emu_type;

		if (sattr->flags & FB_ATTR_DEVSPECIFIC) {
			bcopy((char *) sattr->dev_specific,
				(char *) softc->gattr.sattr.dev_specific,
				sizeof sattr->dev_specific);

			if (softc->gattr.sattr.dev_specific
				[FB_ATTR_CG4_SETOWNER_CMD] == 1) {
				struct proc *pfind();
				register struct proc *newowner = 0;

				if (softc->gattr.sattr.dev_specific
					[FB_ATTR_CG4_SETOWNER_PID] > 0 &&
					(newowner = pfind(
					softc->gattr.sattr.dev_specific
						[FB_ATTR_CG4_SETOWNER_PID]))) {
					softc->owner = newowner;
					softc->gattr.owner = newowner->p_pid;
				}

				softc->gattr.sattr.dev_specific
					[FB_ATTR_CG4_SETOWNER_CMD] = 0;
				softc->gattr.sattr.dev_specific
					[FB_ATTR_CG4_SETOWNER_PID] = 0;

				if (!newowner)
					return ESRCH;
			}
		}
	}
	break;

	case FBIOGATTR: {
		register struct fbgattr *gattr = (struct fbgattr *) data;

		/* set owner if not owned or previous owner is dead */
		if (softc->owner == NULL || 
			softc->owner->p_stat == NULL ||
			softc->owner->p_pid != softc->gattr.owner) {

			softc->owner = u.u_procp;
			softc->gattr.owner = u.u_procp->p_pid;
			softc->gattr.sattr.flags |= FB_ATTR_AUTOINIT;
		}

		*gattr = softc->gattr;

		if (u.u_procp == softc->owner) {
			gattr->owner = 0;
			softc->flags |= CG4_OWNER_WANTS_COLOR;
		}
	}
	break;

	case FBIOGTYPE: {
		register struct fbtype *fb = (struct fbtype *) data;

		/* set owner if not owned or previous owner is dead */
		if (softc->owner == NULL || 
			softc->owner->p_stat == NULL || 
			softc->owner->p_pid != softc->gattr.owner) {

			softc->owner = u.u_procp;
			softc->gattr.owner = u.u_procp->p_pid;
			softc->gattr.sattr.flags |= FB_ATTR_AUTOINIT;
		}

		switch(softc->gattr.sattr.emu_type) {
		case FBTYPE_SUN2BW:
			*fb = cg4typedefault;
			fb->fb_height = softc->h;
			fb->fb_width  = softc->w;
			fb->fb_size = softc->bw2size;
			break;

		case FBTYPE_SUN4COLOR:
		default:
			*fb = softc->gattr.fbtype;
			break;
		}
	}
	break;

#if NWIN > 0

	case FBIOGPIXRECT: {
		struct fbpixrect *fbpr = (struct fbpixrect *) data;
		register struct cg4fb *fbp;
		register struct cg4_fbdesc *descp;
		register int i;

		/* "Allocate" pixrect and private data */
		fbpr->fbpr_pixrect = &softc->pr;
		softc->pr.pr_data = (caddr_t) &softc->cgd;

		/* initialize pixrect */
		softc->pr.pr_ops = &cg4_ops;
		softc->pr.pr_size.x = softc->w;
		softc->pr.pr_size.y = softc->h;

		/* initialize private data */
		softc->cgd.flags = 0;
		softc->cgd.planes = 0;
		softc->cgd.fd = minor(dev);

		for (fbp = softc->cgd.fb, descp = cg4_fbdesc, i = 0; 
			i < CG4_NFBS; fbp++, descp++, i++) {
			fbp->group = descp->group;
			fbp->depth = descp->depth;
			fbp->mprp.mpr.md_linebytes = 
				mpr_linebytes(softc->w, descp->depth);
			fbp->mprp.mpr.md_image = (short *) softc->fb[i];
			fbp->mprp.mpr.md_offset.x = 0;
			fbp->mprp.mpr.md_offset.y = 0;
			fbp->mprp.mpr.md_primary = 0;
			fbp->mprp.mpr.md_flags = descp->allplanes != 0
				? MP_DISPLAY | MP_PLANEMASK
				: MP_DISPLAY;
			fbp->mprp.planes = descp->allplanes;
		}

		/* set up pixrect initial state */
		{
			int initplanes = 
				PIX_GROUP(cg4_fbdesc[CG4_INITFB].group) |
				cg4_fbdesc[CG4_INITFB].allplanes;

			(void) cg4_putattributes(&softc->pr, &initplanes);
		}

		/* enable video */
		if (softc->p4reg)
			*softc->p4reg |= P4_REG_VIDEO;
		else
			setvideoenable(1);
	}
	break;

#endif NWIN > 0

	case FBIOSVIDEO: {
		register int on = * (int *) data & FBVIDEO_ON;

		if (softc->p4reg)
			*softc->p4reg = *softc->p4reg & 
				~(P4_REG_INTCLR | P4_REG_VIDEO) |
				(on ? P4_REG_VIDEO : 0);
		else
			setvideoenable((u_int) on);
	}
	break;

	/* get video flags */
	case FBIOGVIDEO:
		* (int *) data = 
			(softc->p4reg ?
				*softc->p4reg & P4_REG_VIDEO :
				enablereg & ENA_VIDEO) ?
				FBVIDEO_ON : FBVIDEO_OFF;
		break;

	default:
		return ENOTTY;

	} /* switch(cmd) */

	return 0;
}

cgfourpoll()
{
	register int serviced = 0;
	register struct cg4_softc *softc;

	/* 
	 * Look for any frame buffers that were expecting an interrupt.
	 * For P4 frame buffers, check the interrupt status bit too.
	 */
	for (softc = cg4_softc; softc < ENDOF(cg4_softc); softc++)
		if (cgfour_update_pending(softc)) {
			switch (softc->type) {
			case 0:
#ifdef CG4A
				cgfourintr_a(softc); 
				serviced++; 
				break;
			case 1:
#endif CG4A
				if (!softc->p4reg ||
					*softc->p4reg & P4_REG_INT)
					cgfourintr_b(softc); 
					serviced++;
				}
				break;
			}
		/* catch stray P4 interrupts */
		else if (softc->p4reg && 
			*softc->p4reg & P4_REG_INT) {
			cg4_int_disable(softc);
			serviced++;
		}

	return serviced;
}

#ifdef CG4A

static void
cgfourintr_a(softc) 
	register struct cg4_softc *softc;
{
	register struct cg4a_cmap *cmap = &softc->cmap->a;

	/* make sure we're in the first half of vertical blanking period */
	if (cmap->status & CG4A_STATUS_FIRSTHALF) {
		register LOOP_T count = softc->cmap_count;
		register u_long *in  = &softc->cmap_image.cmap_long[0];
		register CMAP_T *out = (CMAP_T *) cmap->red;

		/* loading most or all of colormap, use unrolled loop */
#ifdef mc68000
		if (count >= 160) {
			count = CG4_CMAP_ENTRIES * 3 / sizeof *out / 8 - 1;

			PR_LOOPVP(count,
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++;
				*out++ = *in++);
		}
		/* small to medium colormap load, use rolled loop */
		else 
#endif mc68000
		{
			register int index = softc->cmap_index;

			/* convert count to longs */
			count = (count + (index & 3) + 3) >> 2;

			/* round index to long boundary */
			index &= ~3;

			PTR_INCR(u_long *, in, index);
			PTR_INCR(CMAP_T *, out, index);

			index = ((CG4_CMAP_ENTRIES >> 2) - count) << 2;
			count--;

			PR_LOOPP(3 - 1,
				PR_LOOPP(count,
					IF68000(
						*out++ = *in++
						,
						register u_long tmp;

						tmp = *in++;
						*out++ = tmp >> 24;
						*out++ = tmp >> 16;
						*out++ = tmp >> 8;
						*out++ = tmp));
				PTR_INCR(u_long *, in, index);
				PTR_INCR(CMAP_T *, out, index));
		}

		/* update toolate statistics */
		if (cmap->status & CG4A_STATUS_TOOLATE)
			cg4_toolate++;

		softc->cmap_count = 0;
		softc->flags &= ~CG4_UPDATE_PENDING;
		setintrenable(0);
	}
	/* missed first half of blanking, catch the next retrace */
	else {
		cg4_firsthalf++;
		setintrenable(0);
		setintrenable(1);
	}
}
#endif CG4A

static void
cgfourintr_b(softc) 
	register struct cg4_softc *softc;
{
	register struct cg4b_cmap *cmap = &softc->cmap->b;

	/* load overlay color map */
	{
		register u_char *in = softc->omap_rgb;
		register u_char *out = &cmap->omap;

		/* background color */
		cmap->addr = 1;
		*out = *in++;
		*out = *in++;
		*out = *in++;

		/* foreground color */
		cmap->addr = 3;
		*out = *in++;
		*out = *in++;
		*out = *in;
	}

	/* load main color map */
	{
		register LOOP_T count = softc->cmap_count;
#ifdef mc68000
		register u_char *in = &softc->cmap_image.cmap_char[0][0];
#else mc68000
		register u_long *in = &softc->cmap_image.cmap_long[0];
		register u_long tmp;
#endif  mc68000

		{
			register LOOP_T index = softc->cmap_index;

			/* convert count to multiple of 12 bytes */
			count = ((count + (index & 3) + 3) >> 2) * 3;

			/* round index to 4 entry boundary */
			index &= ~3;

			cmap->addr = index;
			PTR_INCR(IF68000(u_char *, u_long *), in, index * 3);
		}

		{
			register u_char *out = &cmap->cmap;

			/* copy 4 bytes (4/3 RGB entries) per loop iteration */
			PR_LOOPV(count, IF68000(
				*out = *in++;
				*out = *in++;
				*out = *in++;
				*out = *in++;
				,
				tmp = *in++;
				*out = tmp >> 24;
				*out = tmp >> 16;
				*out = tmp >> 8;
				*out = tmp;
				));
		}
	}

	softc->cmap_count = 0;
	softc->flags &= ~CG4_UPDATE_PENDING;
	cg4_int_disable(softc);
}

/* enable display of overlay plane or color planes */
static void
cgfour_set_enable_plane(image, w, h, overlay)
	caddr_t image;
	int w, h;
	int overlay;
{
	register u_long *lp = (u_long *) image, on, count;

	/* 1s enable the overlay plane, 0s enable the color planes */
	on = 0;
	if (overlay)
		on = ~on;

	/* compute number of 32-bit longs to be set */
	count = pr_product(mpr_linebytes(w, 1), h) >> 2;

	/* make sure count is a multiple of 8 */
	while ((count & 7) != 0) {
		*lp++ = on;
		count--;
	}

	PR_LOOP(count >> 3,
		*lp++ = on;
		*lp++ = on;
		*lp++ = on;
		*lp++ = on;
		*lp++ = on;
		*lp++ = on;
		*lp++ = on;
		*lp++ = on);
}

/*
 * Initialize a colormap: background = white, all others = black
 */
static void
cgfour_reset_cmap(softc, cmap, entries)
	register struct cg4_softc *softc;
	register u_char *cmap;
	register u_int entries;
{
	bzero((char *) cmap, entries * 3);
	switch (softc->type) {
	case 0:
#ifdef CG4A
		* cmap             = 255;
		*(cmap += entries) = 255;
		*(cmap += entries) = 255;
		break;
	case 1:
#endif CG4A
		*cmap++ = 255;
		*cmap++ = 255;
		*cmap   = 255;
		break;
	}
}

/*
 * Copy colormap entries between red, green, or blue array and
 * interspersed rgb array.
 *
 * count > 0 : copy count bytes from buf to rgb
 * count < 0 : copy -count bytes from rgb to buf
 */
static void
cgfour_cmap_bcopy(bufp, rgb, count)
	register u_char *bufp, *rgb;
	u_int count;
{
	register LOOP_T rcount = count;

	if (--rcount >= 0)
		PR_LOOPVP(rcount,
			*rgb = *bufp++;
			rgb += 3);
	else {
		rcount = -rcount - 2;
		PR_LOOPVP(rcount,
			*bufp++ = *rgb;
			rgb += 3);
	}
}

/* 
 * Initialize type B (Brooktree) color map chip.
 * This should be done by the boot PROM, but who knows?
 */
static void
cgfour_reset_b(softc)
	struct cg4_softc *softc;
{
	register struct cg4b_cmap *cmap = &softc->cmap->b;
	register u_char *p;

	/* control register initial values (addr, data) */
	static u_char ctrltab[4 * 2] = {
		/* read mask = all 1s */
		4, 0xff,

		/* blink mask = all 0s*/
		5, 0,

		/* 
		 * command register
		 *  CR7   0	4:1 multiplexing
		 *  CR6   1	use color palette RAM
		 *  CR5:4 11	blink divisor = 65536
		 *  CR3:2 00	OL blink disabled
		 *  CR1:0 11	OL display enabled
		 */
		6, 0x73,

		/* test register = 0 */
		7, 0
	};

	/* overlay color map initial values */
	static u_char otab[12] = {
		/* OL color 0 (not used) = yellow */
		255, 255,   0,

		/* OL color 1 (overlay plane background) = white */
		255, 255, 255,

		/* OL color 2 (not used) = cyan */
		  0, 255, 255,

		/* OL color 3 (overlay plane foreground) = black */
		  0,   0,   0
	};

	/* load control registers */
	p = ctrltab;
	PR_LOOPP(ITEMSIN(ctrltab) / 2 - 1, 
		cmap->addr = *p++;
		cmap->ctrl = *p++);

	/* load overlay color map */
	cmap->addr = 0;
	p = otab;
	PR_LOOPP(ITEMSIN(otab) - 1, cmap->omap = *p++);
}

