code:
/*	$OpenBSD: lcg.c,v 1.23 2014/12/23 21:39:12 miod Exp $	*/
/*
 * Copyright (c) 2006 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice, this permission notice, and the disclaimer below
 * appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2003, 2004 Blaz Antonic
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the abovementioned copyrights
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/kernel.h>

#include <machine/vsbus.h>
#include <machine/scb.h>
#include <machine/sid.h>
#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>

#include <vax/vsa/lcgreg.h>

#define	LCG_CONFIG_ADDR	0x200f0010	/* configuration register */
#define	LCG_REG_ADDR	0x20100000	/* registers */
#define	LCG_REG_SIZE	0x4000
#define	LCG_LUT_ADDR	0x21800000	/* colormap */
#define	LCG_LUT_OFFSET	0x0800
#define	LCG_LUT_SIZE	0x0800
#define	LCG_FB_ADDR	0x21801000	/* frame buffer */

int	lcg_match(struct device *, void *, void *);
void	lcg_attach(struct device *, struct device *, void *);

struct	lcg_screen {
	struct rasops_info ss_ri;
	u_int32_t	ss_cfg;
	u_int		ss_width, ss_height, ss_depth;
	u_int		ss_fbsize;		/* visible part only */
	caddr_t		ss_addr;		/* frame buffer address */
	vaddr_t		ss_reg;
	volatile u_int8_t *ss_lut;
	u_int8_t	ss_cmap[256 * 3];
};

/* for console */
struct lcg_screen lcg_consscr;

struct	lcg_softc {
	struct device sc_dev;
	struct lcg_screen *sc_scr;
	int	sc_nscreens;
};

struct cfattach lcg_ca = {
	sizeof(struct lcg_softc), lcg_match, lcg_attach,
};

struct	cfdriver lcg_cd = {
	NULL, "lcg", DV_DULL
};

struct wsscreen_descr lcg_stdscreen = {
	"std",
};

const struct wsscreen_descr *_lcg_scrlist[] = {
	&lcg_stdscreen,
};

const struct wsscreen_list lcg_screenlist = {
	sizeof(_lcg_scrlist) / sizeof(struct wsscreen_descr *),
	_lcg_scrlist,
};

int	lcg_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	lcg_mmap(void *, off_t, int);
int	lcg_alloc_screen(void *, const struct wsscreen_descr *,
	    void **, int *, int *, long *);
void	lcg_free_screen(void *, void *);
int	lcg_show_screen(void *, void *, int,
	    void (*) (void *, int, int), void *);
int	lcg_load_font(void *, void *, struct wsdisplay_font *);
int	lcg_list_font(void *, struct wsdisplay_font *);
void	lcg_burner(void *, u_int, u_int);

const struct wsdisplay_accessops lcg_accessops = {
	.ioctl = lcg_ioctl,
	.mmap = lcg_mmap,
	.alloc_screen = lcg_alloc_screen,
	.free_screen = lcg_free_screen,
	.show_screen = lcg_show_screen,
	.load_font = lcg_load_font,
	.list_font = lcg_list_font,
	.burn_screen = lcg_burner
};

int	lcg_alloc_attr(void *, int, int, int, long *);
int	lcg_getcmap(struct lcg_screen *, struct wsdisplay_cmap *);
void	lcg_loadcmap(struct lcg_screen *, int, int);
int	lcg_probe_screen(u_int32_t, u_int *, u_int *);
int	lcg_putcmap(struct lcg_screen *, struct wsdisplay_cmap *);
void	lcg_resetcmap(struct lcg_screen *);
int	lcg_setup_screen(struct lcg_screen *);

#define	lcg_read_reg(ss, regno) \
	*(volatile u_int32_t *)((ss)->ss_reg + (regno))
#define	lcg_write_reg(ss, regno, val) \
	*(volatile u_int32_t *)((ss)->ss_reg + (regno)) = (val)

int
lcg_match(struct device *parent, void *vcf, void *aux)
{
	struct vsbus_softc *sc = (void *)parent;
	struct vsbus_attach_args *va = aux;
	vaddr_t cfgreg;
	int depth;
#ifdef PARANOIA
	int missing;
	volatile u_int8_t *ch;
#endif

	if (va->va_paddr != LCG_REG_ADDR)
		return (0);

	switch (vax_boardtype) {
	default:
		return (0);

	case VAX_BTYP_46:
		if ((vax_confdata & 0x40) == 0)
			return (0);
		break;
	case VAX_BTYP_48:
		if (vax_cpustype != VAX_STYP_48)
			return (0);
		/* KA48 can't boot without the frame buffer board */
		break;
	}

	/*
	 * Check the configuration register. The frame buffer might not be
	 * an lcg board.
	 */
	cfgreg = vax_map_physmem(LCG_CONFIG_ADDR, 1);
	depth = lcg_probe_screen(*(volatile u_int32_t *)cfgreg, NULL, NULL);
	vax_unmap_physmem(cfgreg, 1);
	if (depth < 0)	/* no lcg frame buffer */
		return (0);

#ifdef PARANOIA
	/*
	 * Check for video memory.
	 * We can not use badaddr() on these models.
	 */
	missing = 0;
	ch = (volatile u_int8_t *)vax_map_physmem(LCG_FB_ADDR, 1);
	*ch = 0x01;
	if ((*ch & 0x01) == 0)
		missing = 1;
	else {
		*ch = 0x00;
		if ((*ch & 0x01) != 0)
			missing = 1;
	}
	vax_unmap_physmem((vaddr_t)ch, 1);
	if (missing != 0)
		return (0);
#endif

	sc->sc_mask = 0x04;	/* XXX - should be generated */
	scb_fake(0x120, 0x15);
	return (20);
}

void
lcg_attach(struct device *parent, struct device *self, void *aux)
{
	struct lcg_softc *sc = (struct lcg_softc *)self;
	struct lcg_screen *ss;
	struct wsemuldisplaydev_attach_args aa;
	vaddr_t tmp;
	u_int32_t cfg;
	int console;
	extern struct consdev wsdisplay_cons;

	console = (vax_confdata & 0x100) == 0 && cn_tab == &wsdisplay_cons;

	/*
	 * Check for a recognized configuration register.
	 * If we do not recognize it, print it and do not attach - so that
	 * this gets noticed...
	 */
	if (!console) {
		tmp = vax_map_physmem(LCG_CONFIG_ADDR, 1);
		if (tmp == 0) {
			printf("\n%s: can not map configuration register\n",
			    self->dv_xname);
			return;
		}
		cfg = *(volatile u_int32_t *)tmp;
		vax_unmap_physmem(tmp, 1);

		if (lcg_probe_screen(cfg, NULL, NULL) <= 0) {
			printf("\n%s:"
			    " unrecognized configuration register %08x\n",
			    self->dv_xname, cfg);
			return;
		}
	}

	if (console) {
		ss = &lcg_consscr;
		sc->sc_nscreens = 1;
	} else {
		ss = malloc(sizeof(*ss), M_DEVBUF, M_NOWAIT | M_ZERO);
		if (ss == NULL) {
			printf(": can not allocate memory\n");
			return;
		}

		ss->ss_cfg = cfg;
		ss->ss_depth = lcg_probe_screen(ss->ss_cfg,
		    &ss->ss_width, &ss->ss_height);
		ss->ss_fbsize =
		    roundup(ss->ss_width * ss->ss_height, PAGE_SIZE);

		ss->ss_addr = (caddr_t)vax_map_physmem(LCG_FB_ADDR,
		    ss->ss_fbsize / VAX_NBPG);
		if (ss->ss_addr == NULL) {
			printf(": can not map frame buffer\n");
			goto fail1;
		}

		ss->ss_reg = vax_map_physmem(LCG_REG_ADDR,
		    LCG_REG_SIZE / VAX_NBPG);
		if (ss->ss_reg == 0L) {
			printf(": can not map registers\n");
			goto fail2;
		}

		ss->ss_lut = (volatile u_int8_t *)vax_map_physmem(LCG_LUT_ADDR +
		    LCG_LUT_OFFSET, LCG_LUT_SIZE / VAX_NBPG);
		if (ss->ss_lut == NULL) {
			printf(": can not map color LUT\n");
			goto fail3;
		}

		if (lcg_setup_screen(ss) != 0) {
			printf(": initialization failed\n");
			goto fail4;
		}
	}
	sc->sc_scr = ss;

	printf(": %dx%dx%d frame buffer\n",
	    ss->ss_width, ss->ss_height, ss->ss_depth);

	aa.console = console;
	aa.scrdata = &lcg_screenlist;
	aa.accessops = &lcg_accessops;
	aa.accesscookie = sc;
	aa.defaultscreens = 0;

	config_found(self, &aa, wsemuldisplaydevprint);
	return;

fail4:
	vax_unmap_physmem((vaddr_t)ss->ss_lut, LCG_LUT_SIZE / VAX_NBPG);
fail3:
	vax_unmap_physmem(ss->ss_reg, LCG_REG_SIZE / VAX_NBPG);
fail2:
	vax_unmap_physmem((vaddr_t)ss->ss_addr, ss->ss_fbsize / VAX_NBPG);
fail1:
	free(ss, M_DEVBUF, sizeof(*ss));
}

/*
 * Determine if we have a recognized frame buffer, its resolution and
 * color depth.
 */
int
lcg_probe_screen(u_int32_t cfg, u_int *width, u_int *height)
{
	u_int w, h, d = 8;

	switch (vax_boardtype) {
	case VAX_BTYP_46:
		switch (cfg & 0xf0) {
		case 0x00:
			return (-1);	/* no hardware */
		case 0x20:
		case 0x60:
			w = 1024; h = 864;
			break;
		case 0x40:
			w = 1024; h = 768;
			break;
		case 0x80:
			d = 4;
			/* FALLTHROUGH */
		case 0x90:
		case 0xb0:
			w = 1280; h = 1024;
			break;
		default:
			return (0);	/* unknown configuration, please help */
		}
		break;
	case VAX_BTYP_48:
		switch (cfg & 0x07) {
		case 0x00:
			return (-1);	/* no hardware */
		case 0x05:
			w = 1280; h = 1024;
			break;
		case 0x06:
			if (vax_confdata & 0x80) {
				w = 1024; h = 768;
			} else {
				w = 640; h = 480;
			}
			break;
		case 0x07:
			if (vax_confdata & 0x80) {
				w = 1024; h = 768;
			} else {
				w = 1024; h = 864;
			}
			break;
		default:
			return (0);	/* unknown configuration, please help */
		}
		break;
	}

	if (width != NULL)
		*width = w;
	if (height != NULL)
		*height = h;

	return (d);
}

/*
 * Initialize anything necessary for an emulating wsdisplay to work (i.e.
 * pick a font, initialize a rasops structure, setup the accessops callbacks.)
 */
int
lcg_setup_screen(struct lcg_screen *ss)
{
	struct rasops_info *ri = &ss->ss_ri;

	bzero(ri, sizeof(*ri));
	/*
	 * Since the frame buffer memory is byte addressed, even in low-bpp
	 * mode, initialize a 8bpp rasops engine. We will report a shorter
	 * colormap if necessary, which will allow X to do TRT.
	 */
	ri->ri_depth = 8;
	ri->ri_width = ss->ss_width;
	ri->ri_height = ss->ss_height;
	ri->ri_stride = ss->ss_width;
	ri->ri_flg = RI_CLEAR | RI_CENTER;
	ri->ri_bits = (void *)ss->ss_addr;
	ri->ri_hw = ss;

	/*
	 * Ask for an unholy big display, rasops will trim this to more
	 * reasonable values.
	 */
	if (rasops_init(ri, 160, 160) != 0)
		return (-1);

	if (ss->ss_depth < 8) {
		ri->ri_ops.alloc_attr = lcg_alloc_attr;
		ri->ri_caps &= ~WSSCREEN_HILIT;
	}

	lcg_stdscreen.ncols = ri->ri_cols;
	lcg_stdscreen.nrows = ri->ri_rows;
	lcg_stdscreen.textops = &ri->ri_ops;
	lcg_stdscreen.fontwidth = ri->ri_font->fontwidth;
	lcg_stdscreen.fontheight = ri->ri_font->fontheight;
	lcg_stdscreen.capabilities = ri->ri_caps;

	lcg_resetcmap(ss);

	return (0);
}

int
lcg_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_cmap *cm;
	int error;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_LCG;
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = ss->ss_height;
		wdf->width = ss->ss_width;
		wdf->depth = 8;
		wdf->cmsize = 1 << ss->ss_depth;
		break;

	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = ss->ss_ri.ri_stride;
		break;

	case WSDISPLAYIO_GETCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = lcg_getcmap(ss, cm);
		if (error != 0)
			return (error);
		break;
	case WSDISPLAYIO_PUTCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = lcg_putcmap(ss, cm);
		if (error != 0)
			return (error);
		lcg_loadcmap(ss, cm->index, cm->count);
		break;

	case WSDISPLAYIO_GVIDEO:
	case WSDISPLAYIO_SVIDEO:
		break;

	default:
		return (-1);
	}

	return (0);
}

paddr_t
lcg_mmap(void *v, off_t offset, int prot)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;

	if (offset >= ss->ss_fbsize || offset < 0)
		return (-1);

	return (LCG_FB_ADDR + offset);
}

int
lcg_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	if (sc->sc_nscreens > 0)
		return (ENOMEM);

	*cookiep = ri;
	*curxp = *curyp = 0;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, defattrp);
	sc->sc_nscreens++;

	return (0);
}

void
lcg_free_screen(void *v, void *cookie)
{
	struct lcg_softc *sc = v;

	sc->sc_nscreens--;
}

int
lcg_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{
	return (0);
}

int
lcg_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_load_font(ri, emulcookie, font);
}

int
lcg_list_font(void *v, struct wsdisplay_font *font)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_list_font(ri, font);
}

void
lcg_burner(void *v, u_int on, u_int flags)
{
	struct lcg_softc *sc = v;
	struct lcg_screen *ss = sc->sc_scr;
	u_int32_t vidcfg;

	vidcfg = lcg_read_reg(ss, LCG_REG_VIDEO_CONFIG);
	if (on)
		vidcfg |= VIDEO_ENABLE_VIDEO | VIDEO_SYNC_ENABLE;
	else {
		vidcfg &= ~VIDEO_ENABLE_VIDEO;
		if (flags & WSDISPLAY_BURN_VBLANK)
			vidcfg &= ~VIDEO_SYNC_ENABLE;
	}
	lcg_write_reg(ss, LCG_REG_VIDEO_CONFIG, vidcfg);
}

/*
 * Attribute allocator for 4bpp frame buffers.
 * In such modes, highlighting is not available.
 */
int
lcg_alloc_attr(void *cookie, int fg, int bg, int flg, long *attr)
{
	extern int rasops_alloc_cattr(void *, int, int, int, long *);

	if ((flg & (WSATTR_BLINK | WSATTR_HILIT)) != 0)
		return (EINVAL);

	return (rasops_alloc_cattr(cookie, fg, bg, flg, attr));
}

/*
 * Colormap handling routines
 */

int
lcg_getcmap(struct lcg_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count, i;
	u_int colcount = 1 << ss->ss_depth;
	int error;
	u_int8_t ramp[256], *c, *r;

	if (index >= colcount || count > colcount - index)
		return (EINVAL);

	/* extract reds */
	c = ss->ss_cmap + 0 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->red, count)) != 0)
		return (error);

	/* extract greens */
	c = ss->ss_cmap + 1 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->green, count)) != 0)
		return (error);

	/* extract blues */
	c = ss->ss_cmap + 2 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->blue, count)) != 0)
		return (error);

	return (0);
}

int
lcg_putcmap(struct lcg_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count;
	u_int colcount = 1 << ss->ss_depth;
	int i, error;
	u_int8_t r[256], g[256], b[256], *nr, *ng, *nb, *c;

	if (index >= colcount || count > colcount - index)
		return (EINVAL);

	if ((error = copyin(cm->red, r, count)) != 0)
		return (error);
	if ((error = copyin(cm->green, g, count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, b, count)) != 0)
		return (error);

	nr = r, ng = g, nb = b;
	c = ss->ss_cmap + index * 3;
	for (i = count; i != 0; i--) {
		*c++ = *nr++;
		*c++ = *ng++;
		*c++ = *nb++;
	}

	return (0);
}

/* Fill the given colormap (LUT) entry.  */
#define lcg_set_lut_entry(lutptr, cmap, idx, shift)			\
do {									\
	*(lutptr)++ = LUT_ADRS_REG;					\
	*(lutptr)++ = (idx);						\
	*(lutptr)++ = LUT_COLOR_AUTOINC;				\
	*(lutptr)++ = (*(cmap)++) >> (shift);				\
	*(lutptr)++ = LUT_COLOR_AUTOINC;				\
	*(lutptr)++ = (*(cmap)++) >> (shift);				\
	*(lutptr)++ = LUT_COLOR_AUTOINC;				\
	*(lutptr)++ = (*(cmap)++) >> (shift);				\
} while (0)

void
lcg_loadcmap(struct lcg_screen *ss, int from, int count)
{
	const u_int8_t *cmap;
	u_int i;
	volatile u_int8_t *lutptr;
	u_int32_t vidcfg;

	/* partial updates ignored for now */
	cmap = ss->ss_cmap;
	lutptr = ss->ss_lut;
	if (ss->ss_depth == 8) {
		for (i = 0; i < 256; i++) {
			lcg_set_lut_entry(lutptr, cmap, i, 0);
		}
	} else {
		for (i = 0; i < 16; i++) {
			lcg_set_lut_entry(lutptr, cmap, i, 4);
		}
	}

	/*
	 * Wait for retrace
	 */
	while (((vidcfg = lcg_read_reg(ss, LCG_REG_VIDEO_CONFIG)) &
	    VIDEO_VSTATE) != VIDEO_VSYNC)
		DELAY(1);

	vidcfg &= ~(VIDEO_SHIFT_SEL | VIDEO_MEM_REFRESH_SEL_MASK |
	    VIDEO_LUT_SHIFT_SEL);
	/* Do full loads if width is 1024 or 2048, split loads otherwise. */
	if (ss->ss_width == 1024 || ss->ss_width == 2048)
		vidcfg |= VIDEO_SHIFT_SEL | (1 << VIDEO_MEM_REFRESH_SEL_SHIFT) |
		    VIDEO_LUT_SHIFT_SEL;
	else
		vidcfg |= (2 << VIDEO_MEM_REFRESH_SEL_SHIFT);
	vidcfg |= VIDEO_LUT_LOAD_SIZE;	/* 2KB lut */
	lcg_write_reg(ss, LCG_REG_VIDEO_CONFIG, vidcfg);
	lcg_write_reg(ss, LCG_REG_LUT_CONSOLE_SEL, LUT_SEL_COLOR);
	lcg_write_reg(ss, LCG_REG_LUT_COLOR_BASE_W, LCG_LUT_OFFSET);
	/* Wait for end of retrace */
	while (((vidcfg = lcg_read_reg(ss, LCG_REG_VIDEO_CONFIG)) &
	    VIDEO_VSTATE) == VIDEO_VSYNC)
		DELAY(1);
	lcg_write_reg(ss, LCG_REG_LUT_CONSOLE_SEL, LUT_SEL_CONSOLE);
}

void
lcg_resetcmap(struct lcg_screen *ss)
{
	if (ss->ss_depth == 8)
		bcopy(rasops_cmap, ss->ss_cmap, sizeof(ss->ss_cmap));
	else {
		bcopy(rasops_cmap, ss->ss_cmap, 8 * 3);
		bcopy(rasops_cmap + 0xf8 * 3, ss->ss_cmap + 8 * 3, 8 * 3);
	}
	lcg_loadcmap(ss, 0, 1 << ss->ss_depth);
}

/*
 * Console support code
 */

int	lcgcnprobe(void);
int	lcgcninit(void);

int
lcgcnprobe()
{
	extern vaddr_t virtual_avail;
	u_int32_t cfg;
	vaddr_t tmp;
#ifdef PARANOIA
	volatile u_int8_t *ch;
	int rc;
#endif

	switch (vax_boardtype) {
	case VAX_BTYP_46:
		if ((vax_confdata & 0x40) == 0)
			return (0);	/* no frame buffer */
		break;
	case VAX_BTYP_48:
		if (vax_cpustype != VAX_STYP_48)
			return (0);
		break;
	default:
		return (0);
	}

	if ((vax_confdata & 0x100) != 0)
		return (0); /* doesn't use graphics console */

	tmp = virtual_avail;
	ioaccess(tmp, vax_trunc_page(LCG_CONFIG_ADDR), 1);
	cfg = *(volatile u_int32_t *)(tmp + (LCG_CONFIG_ADDR & VAX_PGOFSET));
	iounaccess(tmp, 1);

	if (lcg_probe_screen(cfg, NULL, NULL) <= 0)
		return (0);	/* no lcg or unsupported configuration */

#ifdef PARANOIA
	/*
	 * Check for video memory.
	 * We can not use badaddr() on these models.
	 */
	rc = 0;
	ioaccess(tmp, LCG_FB_ADDR, 1);
	ch = (volatile u_int8_t *)tmp;
	*ch = 0x01;
	if ((*ch & 0x01) != 0) {
		*ch = 0x00;
		if ((*ch & 0x01) == 0)
			rc = 1;
	}
	iounaccess(tmp, 1);
	if (rc == 0)
		return (0);
#endif

	return (1);
}

/*
 * Called very early to setup the glass tty as console.
 * Because it's called before the VM system is initialized, virtual memory
 * for the framebuffer can be stolen directly without disturbing anything.
 */
int
lcgcninit()
{
	struct lcg_screen *ss = &lcg_consscr;
	extern vaddr_t virtual_avail;
	vaddr_t ova;
	long defattr;
	struct rasops_info *ri;

	ova = virtual_avail;
	ioaccess(virtual_avail, vax_trunc_page(LCG_CONFIG_ADDR), 1);
	ss->ss_cfg = *(volatile u_int32_t *)
	    (virtual_avail + (LCG_CONFIG_ADDR & VAX_PGOFSET));
	iounaccess(virtual_avail, 1);

	ss->ss_depth = lcg_probe_screen(ss->ss_cfg,
	    &ss->ss_width, &ss->ss_height);

	ss->ss_fbsize = roundup(ss->ss_width * ss->ss_height, PAGE_SIZE);

	ss->ss_addr = (caddr_t)virtual_avail;
	ioaccess(virtual_avail, LCG_FB_ADDR, ss->ss_fbsize / VAX_NBPG);
	virtual_avail += ss->ss_fbsize;

	ss->ss_reg = virtual_avail;
	ioaccess(virtual_avail, LCG_REG_ADDR, LCG_REG_SIZE / VAX_NBPG);
	virtual_avail += LCG_REG_SIZE;

	ss->ss_lut = (volatile u_int8_t *)virtual_avail;
	ioaccess(virtual_avail, LCG_LUT_ADDR + LCG_LUT_OFFSET,
	    LCG_LUT_SIZE / VAX_NBPG);
	virtual_avail += LCG_LUT_SIZE;

	virtual_avail = round_page(virtual_avail);

	/* this had better not fail */
	if (lcg_setup_screen(ss) != 0) {
		iounaccess((vaddr_t)ss->ss_lut, LCG_LUT_SIZE / VAX_NBPG);
		iounaccess((vaddr_t)ss->ss_reg, LCG_REG_SIZE / VAX_NBPG);
		iounaccess((vaddr_t)ss->ss_addr, ss->ss_fbsize / VAX_NBPG);
		virtual_avail = ova;
		return (1);
	}

	ri = &ss->ss_ri;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&lcg_stdscreen, ri, 0, 0, defattr);

	return (0);
}


code:
/*	$OpenBSD: lcgreg.h,v 1.4 2008/06/26 05:42:14 ray Exp $	*/
/* $NetBSD: lcgreg.h,v 1.4 2005/12/11 12:19:34 christos Exp $ */

/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Matt Thomas of 3am Software Foundry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * The registers of the LCG used in the VS4000/60 and VS4000/VLC.
 * All relative to 0x20100000
 */

/* Memory Control, Flow Control, Configuration Registers
 */
#define	LCG_REG_MEM_CONFIG		0x001800
#define	LCG_REG_MEM_STATUS		0x001804
#define	LCG_REG_MEM_CURRENT_STATE	0x001808
#define	LCG_REG_MEM_ERROR		0x00180c
#define	LCG_REG_SLOW_CONTROL_STATUS	0x001810

/* Video Control Registers
 */
#define	LCG_REG_VIDEO_CONFIG		0x001e00
#define	VIDEO_VSTATE				0xc0000000
#define	VIDEO_VFRONT_PORCH			0x00000000
#define	VIDEO_VSYNC				0x40000000
#define	VIDEO_VBACK_PORCH			0x80000000
#define	VIDEO_VACTIVE				0xc0000000
#define	VIDEO_HSTATE				0x30000000
#define	VIDEO_HFRONT_PORCH			0x00000000
#define	VIDEO_HSYNC				0x10000000
#define	VIDEO_HBACK_PORCH			0x20000000
#define	VIDEO_HACTIVE				0x30000000
#define	VIDEO_CONSOLE_LUT			0x02000000
#define	VIDEO_CONTROL_LUT			0x01000000
#define	VIDEO_CURSOR_ACTIVE			0x00400000
#define	VIDEO_CURSOR_SCANLINE			0x003f0000
#define	VIDEO_RESET				0x00008000
#define	VIDEO_LUT_LOAD_SIZE			0x00002000
#define	VIDEO_SYNC_ENABLE			0x00001000
#define	VIDEO_LUT_SHIFT_SEL			0x00000800
#define	VIDEO_CLOCK_SEL				0x00000400
#define	VIDEO_MEM_REFRESH_SEL_MASK		0x00000300
#define	VIDEO_MEM_REFRESH_SEL_SHIFT	8
#define	VIDEO_REFRESH_SEL			0x000000c0
#define	VIDEO_SHIFT_SEL				0x00000020
#define	VIDEO_CURSOR_PIN_TYPE			0x00000010
#define	VIDEO_LUT_LOAD_ENABLE			0x00000008
#define	VIDEO_CURSOR_ENABLE			0x00000004
#define	VIDEO_ENABLE_VIDEO			0x00000002
#define	VIDEO_TIMING_ENABLE			0x00000001
#define	LCG_REG_VIDEO_HTIMING		0x001e10
#define	LCG_REG_VIDEO_VTIMING		0x001e14
#define	LCG_REG_VIDEO_TIMING		0x001e18
#define	LCG_REG_VIDEO_X			0x000e30
#define	LCG_REG_VIDEO_Y			0x000e30
#define	LCG_REG_VIDEO_REFRESH_BASE	0x000e34
#define	LCG_REG_VIDEO_REFRESH_SHIFT	0x000e40
#define	LCG_REG_VIDEO_LUT_LOAD_COUNT	0x000e40
#define	LCG_REG_CURSOR_SCANLINE_LW0	0x000e50
#define	LCG_REG_CURSOR_SCANLINE_LW1	0x000e54
#define	LCG_REG_CURSOR_SCANLINE_LW2	0x000e58
#define	LCG_REG_CURSOR_SCANLINE_LW3	0x000e5c
#define	LCG_REG_CURSOR_BASE		0x000e80
#define	LCG_REG_CURSOR_XY		0x000e84
#define	LCG_REG_CURSOR_X		0x000e84
#define	LCG_REG_CURSOR_Y		0x000e84
#define	LCG_REG_LUT_CONSOLE_SEL		0x000ee0
#define	LUT_SEL_CONSOLE				0x00000000
#define	LUT_SEL_COLOR				0x00000001
#define	LCG_REG_LUT_COLOR_BASE_W	0x0006e4
#define	LCG_REG_LUT_COLOR_BASE_R	0x0006e4
#define	LCG_REG_LUT_CONTROL_BASE	0x000ee8
#define	LCG_REG_VIDEO_COUNTER_TEST	0x000f00
#define	LCG_REG_MEM_REFRESH_BASE	0x000f04

/* Graphics Control and VM Registers
 */
#define	LCG_REG_LCG_GO			0x000c80
#define	GO_VM					0x00000008
#define	GO_AG					0x00000002
#define	GO_FIFO					0x00000001
#define	LCG_REG_NEXT_ADDRESS		0x001334
#define	LCG_REG_PA_SPTE_PTE		0x001338
#define	LCG_REG_TB_INVALIDATE_SINGLE	0x001a00
#define	LCG_REG_TB_INVALIDATE_ALL	0x001a08
#define	LCG_REG_TB_INVALIDATE_STATUS	0x001a10
#define	LCG_REG_TB_STATUS		0x001c00
#define	LCG_REG_TB_VPN_COUNT		0x001c04
#define	LCG_REG_TB_DEST_VPN		0x001c14
#define	LCG_REG_TB_SOURCE_VPN		0x001c18
#define	LCG_REG_TB_STENCIL_VPN		0x001c1c
#define	LCG_REG_TB_DEST_DATA_PFN_R	0x001c24
#define	LCG_REG_TB_DEST_DATA_PFN_W	0x001c24
#define	LCG_REG_TB_SOURCE_DATA_PFN_R	0x001c28
#define	LCG_REG_TB_SOURCE_DATA_PFN_W	0x001c28
#define	LCG_REG_TB_STENCIL_DATA_PFN_R	0x001c2c
#define	LCG_REG_TB_STENCIL_DATA_PFN_W	0x001c2c
#define	LCG_REG_TB_DEST_PRE_PFN_R	0x001c34
#define	LCG_REG_TB_DEST_PRE_PFN_W	0x001c34
#define	LCG_REG_TB_SOURCE_PTE_PFN_R	0x001c38
#define	LCG_REG_TB_SOURCE_PTE_PFN_W	0x001c38
#define	LCG_REG_TB_STENCIL_PTE_PFN_R	0x001c3c
#define	LCG_REG_TB_STENCIL_PTE_PFN_W	0x001c3c
#define	LCG_REG_GRAPHICS_CONFIG		0x001c90
#define	LCG_REG_GRAPHICS_INT_STATUS	0x001c94
#define	LCG_REG_GRAPHICS_INT_SET_ENABLE	0x001c98
#define	LCG_REG_GRAPHICS_INT_CLR_ENABLE	0x001c9c
#define	LCG_REG_GRAPHICS_SUB_STATUS	0x001ca0
#define	GSS_AG_BUSY				0x80000000
#define	GSS_SHORT_CIRCUIT			0x20000000
#define	GSS_VALID_PACKET			0x10000000
#define	GSS_1ST_LONGWORD			0x08000000
#define	GSS_FIFO_ARBITRATE			0x04000000
#define	GSS_FIFO_COMMANDER			0x03000000
#define	GSS_AG_BACKWARDS			0x00800000
#define	GSS_AG_VIRTUAL				0x00400000
#define	GSS_AG_ARBITRATE			0x00200000
#define	GSS_AG_READ_ID				0x00100000
#define	GSS_AG_ACCESS_TYPE			0x000c0000
#define	GSS_AG_ACCESS_SIZE			0x00030000
#define	GSS_RESIDUE_LW0				0x00008000
#define	GSS_RESIDUE_LW1				0x00004000
#define	GSS_RESIDUE_LW2				0x00002000
#define	GSS_FIFO_TAIL_BITS			0x00001800
#define	GSS_FIFO_IDU				0x00000400
#define	GSS_EXECUTING_CLIP			0x00000200
#define	GSS_FIFO_BPT_STALL			0x00000100
#define	GSS_FIFO_WFSYNC_STALL			0x00000080
#define	GSS_FIFO_AGBUSY_STALL			0x00000040
#define	GSS_ADRS_BPT_VIRTUAL			0x00000010
#define	GSS_VM					0x00000008
#define	GSS_FIFO_IDLE				0x00000004
#define	GSS_AG_ACCESS				0x00000002
#define	GSS_FIFO				0x00000001
#define	LCG_REG_GRAPHICS_CONTROL	0x001ca4
#define	CTRL_RESET				0x80000000
#define	CTRL_AG					0x40000000
#define	CTRL_CLIP_LIST				0x20000000
#define	CTRL_FIFO				0x10000000
#define	CTRL_SHORT_CIRCUIT			0x08000000
#define	CTRL_VM					0x04000000
#define	CTRL_VM_PROTECTION			0x02000000
#define	CTRL_OPT_INTERFACE			0x01000000
#define	CTRL_OPT_TIMEOUT_SEL			0x00c00000
#define	CTRL_OPT_RESET				0x00200000
#define	CTRL_OPTION_NORESET			0x00100000
#define	CTRL_AG_ACCESS_BPT_ARM			0x00000800
#define	CTRL_PACKET_BPT_ARM			0x00000200
#define	CTRL_ADDRESS_BPT_ARM			0x00000100
#define	LCG_REG_BREAKPT_ADDRESS		0x001cb0
#define	LCG_REG_BREAKPT_VIRTUAL		0x001cb0
#define	LCG_REG_WRITE_PROTECT_LOW_HIGH	0x001cc0
#define	LCG_REG_WRITE_PROTECT_LOW	0x001cc0
#define	LCG_REG_WRITE_PROTECT_HIGH	0x001cc0
#define	LCG_REG_MAX_VIRTUAL_ADDRESS	0x002350
#define	LCG_REG_PA_SPTE_POBR		0x002354

/* Clip List / Command FIFO Registers
 */
#define	LCG_REG_CLIP_LIST_OFFSET	0x0004e4
#define	LCG_REG_CLIP_LIST_BASE		0x0004e4
#define	LCG_REG_CLIP_LIST		0x0004e4
#define	LCG_REG_FIFO_MASKS		0x000570
#define	FIFO_16K				0x00000000
#define	FIFO_32K				0x00004000
#define	FIFO_64K				0x0000c000
#define	FIFO_AFULL_AT_64			0x00000000
#define	FIFO_AFULL_AT_128			0x00002000
#define	FIFO_AFULL_AT_256			0x00003000
#define	FIFO_AFULL_AT_512			0x00003800
#define	FIFO_AFULL_AT_1024			0x00003c00
#define	FIFO_AFULL_AT_2048			0x00003e00
#define	FIFO_AFULL_AT_4096			0x00003f00
#define	FIFO_AEMPTY_AT_32			0x00000000
#define	FIFO_AEMPTY_AT_64			0x00000080
#define	FIFO_AEMPTY_AT_128			0x00000040
#define	FIFO_AEMPTY_AT_256			0x00000020
#define	FIFO_AEMPTY_AT_512			0x00000010
#define	FIFO_AEMPTY_AT_1024			0x00000008
#define	FIFO_AEMPTY_AT_2048			0x00000004
#define	FIFO_AEMPTY_AT_4096			0x00000002
#define	FIFO_AEMPTY_AT_8192			0x00000001
#define	LCG_REG_FIFO_HEAD_OFFSET	0x000574
#define	LCG_REG_FIFO_BASE		0x000574
#define	LCG_REG_FIFO_HEAD		0x000574
#define	LCG_REG_FIFO_TAIL_OFFSET	0x000578
#define	LCG_REG_FIFO_BASE2		0x000578
#define	LCG_REG_FIFO_TAIL		0x000578
#define	LCG_REG_CLIP_LIST_SAVE_OFFSET	0x000ce4
#define	LCG_REG_FIFO_RESIDUE_LW0	0x000d04
#define	LCG_REG_FIFO_RESIDUE_LW1	0x000d08
#define	LCG_REG_FIFO_RESIDUE_LW2	0x000d0c
#define	LCG_REG_FIFO_LENGTH		0x000d70
#define	LCG_REG_FIFO_SAVE_HEAD_OFFSET	0x000d74
#define	LCG_REG_FIFO_WINDOW_BASE	0x080000
#define	LCG_REG_FIFO_WINDOW_END		0x100000

/* Graphics Data Buffer and Pixel SLU Registers
 */
#define	LCG_REG_LOGICAL_FUNCTION	0x000220
#define	LCG_REG_PLANE_MASK		0x000234
#define	LCG_REG_SOURCE_PLANE_INDEX	0x00026c
#define	LCG_REG_FOREGROUND_PIXEL	0x0002c0
#define	LCG_REG_BACKGROUND_PIXEL	0x0004c0
#define	LCG_REG_GDB_LW0			0x000d80
#define	LCG_REG_GDB_LW1			0x000d84
#define	LCG_REG_GDB_LW2			0x000d88
#define	LCG_REG_GDB_LW3			0x000d8c
#define	LCG_REG_GDB_LW4			0x000d90
#define	LCG_REG_GDB_LW5			0x000d94
#define	LCG_REG_GDB_LW6			0x000d98
#define	LCG_REG_GDB_LW7			0x000d9c
#define	LCG_REG_SLU_STATE		0x000da0

/* Address Generator Registers
 */
#define	LCG_REG_CLIP_MIN_Y		0x000244
#define	LCG_REG_CLIP_MIN_MAX_X		0x000248
#define	LCG_REG_CLIP_MIN_X		0x000248
#define	LCG_REG_CLIP_MAX_X		0x000248
#define	LCG_REG_CLIP_MAX_Y		0x00024c
#define	LCG_REG_DEST_X_BIAS		0x000250
#define	LCG_REG_DEST_Y_ORIGIN		0x000254
#define	LCG_REG_DEST_Y_STEP		0x000258
#define	LCG_REG_SOURCE_X_BIAS		0x000260
#define	LCG_REG_SOURCE_Y_BASE		0x000264
#define	LCG_REG_SOURCE_Y_STEP_WIDTH	0x000268
#define	LCG_REG_SOURCE_Y_STEP		0x000268
#define	LCG_REG_SOURCE_WIDTH		0x000268
#define	LCG_REG_STENCIL_X_BIAS		0x000270
#define	LCG_REG_STENCIL_Y_BASE		0x000274
#define	LCG_REG_STENCIL_Y_STEP		0x000278
#define	LCG_REG_DEST_Y_BASE		0x000284
#define	LCG_REG_DEST_X			0x000290
#define	LCG_REG_DEST_WIDTH_HEIGHT	0x000294
#define	LCG_REG_DEST_WIDTH		0x000294
#define	LCG_REG_DEST_HEIGHT		0x000294
#define	LCG_REG_AG_STATUS2		0x000320
#define	LCG_REG_AG_CURRENT_STATE	0x000320
#define	LCG_REG_CURRENT_OPCODE		0x000320
#define	LCG_REG_OP_ACTION_CODE		0x000320
#define	LCG_REG_AG_STATUS		0x000324
#define	LCG_REG_NEXT_X			0x000330
#define	LCG_REG_CLIP_X_DIFF		0x000330
#define	LCG_REG_SOURCE_X_BIAS0		0x000460
#define	LCG_REG_SOURCE_WIDTH0		0x000468
#define	LCG_REG_DEST_X0			0x000490
#define	LCG_REG_DEST_WIDTH0		0x000494
#define	LCG_REG_TILE_ROTATION		0x000660
#define	LCG_REG_TILE_WIDTH		0x000668

/*
 * LUT data bits
 */
#define	LUT_ADRS_REG		0x00	/* write to address register */
#define	LUT_COLOR_AUTOINC	0x01	/* write to LUT and autoincrement */

license: unknown


code:
/*	$OpenBSD: lcspx.c,v 1.21 2014/12/23 21:39:12 miod Exp $	*/
/*
 * Copyright (c) 2006 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice, this permission notice, and the disclaimer below
 * appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2004 Blaz Antonic
 * All rights reserved.
 *
 * This software contains code written by Michael L. Hitch.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the abovementioned copyrights
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/kernel.h>

#include <machine/nexus.h>
#include <machine/vsbus.h>
#include <machine/scb.h>
#include <machine/sid.h>
#include <machine/cpu.h>
#include <machine/ka420.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>

#include <dev/ic/bt463reg.h>	/* actually it's a 459 here... */

#define	GPXADDR			0x3c000000	/* XXX */
#define	GPX_ADDER_OFFSET	0x0000		/* XXX */
#include <vax/qbus/qdreg.h>			/* XXX */

#define	LCSPX_REG_ADDR		0x39302000	/* registers */
#define	LCSPX_REG_SIZE		    0x2000
#define	LCSPX_REG1_ADDR		0x39b00000	/* more registers */
#define	LCSPX_RAMDAC_ADDR	0x39b10000	/* RAMDAC */
#define	LCSPX_RAMDAC_INTERLEAVE	0x00004000
#define	LCSPX_FB_ADDR		0x38000000	/* frame buffer */

void	lcspx_attach(struct device *, struct device *, void *);
int	lcspx_vsbus_match(struct device *, void *, void *);
int	lcspx_vxtbus_match(struct device *, void *, void *);

struct	lcspx_screen {
	struct rasops_info ss_ri;
	u_int		ss_fbsize;
	caddr_t		ss_addr;		/* frame buffer address */
	volatile u_int8_t *ss_ramdac[4];
	vaddr_t		ss_reg;
	u_int8_t	ss_cmap[256 * 3];
};

#define	lcspx_reg_read(ss, reg) \
	*(volatile u_int32_t *)((ss)->ss_reg + (reg))
#define	lcspx_reg_write(ss, reg, val) \
	*(volatile u_int32_t *)((ss)->ss_reg + (reg)) = (val)

/* for console */
struct lcspx_screen lcspx_consscr;

struct	lcspx_softc {
	struct device sc_dev;
	struct lcspx_screen *sc_scr;
	int	sc_nscreens;
};

struct cfattach lcspx_vsbus_ca = {
	sizeof(struct lcspx_softc), lcspx_vsbus_match, lcspx_attach,
};

struct cfattach lcspx_vxtbus_ca = {
	sizeof(struct lcspx_softc), lcspx_vxtbus_match, lcspx_attach,
};

struct	cfdriver lcspx_cd = {
	NULL, "lcspx", DV_DULL
};

struct wsscreen_descr lcspx_stdscreen = {
	"std",
};

const struct wsscreen_descr *_lcspx_scrlist[] = {
	&lcspx_stdscreen,
};

const struct wsscreen_list lcspx_screenlist = {
	sizeof(_lcspx_scrlist) / sizeof(struct wsscreen_descr *),
	_lcspx_scrlist,
};

int	lcspx_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	lcspx_mmap(void *, off_t, int);
int	lcspx_alloc_screen(void *, const struct wsscreen_descr *,
	    void **, int *, int *, long *);
void	lcspx_free_screen(void *, void *);
int	lcspx_show_screen(void *, void *, int,
	    void (*) (void *, int, int), void *);
int	lcspx_load_font(void *, void *, struct wsdisplay_font *);
int	lcspx_list_font(void *, struct wsdisplay_font *);

const struct wsdisplay_accessops lcspx_accessops = {
	.ioctl = lcspx_ioctl,
	.mmap = lcspx_mmap,
	.alloc_screen = lcspx_alloc_screen,
	.free_screen = lcspx_free_screen,
	.show_screen = lcspx_show_screen,
	.load_font = lcspx_load_font,
	.list_font = lcspx_list_font
};

int	lcspx_getcmap(struct lcspx_screen *, struct wsdisplay_cmap *);
void	lcspx_loadcmap(struct lcspx_screen *, int, int);
int	lcspx_putcmap(struct lcspx_screen *, struct wsdisplay_cmap *);
static __inline__
void	lcspx_ramdac_wraddr(struct lcspx_screen *, u_int);
void	lcspx_resetcmap(struct lcspx_screen *);
int	lcspx_setup_screen(struct lcspx_screen *, u_int, u_int);

int
lcspx_vsbus_match(struct device *parent, void *vcf, void *aux)
{
	extern int oldvsbus;
	struct vsbus_softc *sc = (void *)parent;
	struct vsbus_attach_args *va = aux;
	volatile struct adder *adder;
	u_short status;

	if (va->va_paddr != LCSPX_REG_ADDR)
		return (0);

	switch (vax_boardtype) {
	default:
		return (0);

	case VAX_BTYP_420:
	case VAX_BTYP_43:
		/* not present on microvaxes */
		if ((vax_confdata & KA420_CFG_MULTU) != 0)
			return (0);

		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
			return (0);

		/*
		 * We can not check for video memory at the SPX address,
		 * because if no SPX board is installed, the probe will
		 * just hang.
		 * Instead, check for a GPX board and skip probe if the
		 * video option really is a GPX.
		 */
		adder = (volatile struct adder *)
		    vax_map_physmem(GPXADDR + GPX_ADDER_OFFSET, 1);
		if (adder == NULL)
			return (0);
		adder->status = 0;
		status = adder->status;
		vax_unmap_physmem((vaddr_t)adder, 1);
		if (status != offsetof(struct adder, status))
			return (0);

		/* XXX do not attach if not console... yet */
		if ((vax_confdata & KA420_CFG_L3CON) != 0)
			return (0);

		break;

	case VAX_BTYP_49:
		if ((vax_confdata & 0x12) != 0x02)
			return (0);

		break;
	}

	sc->sc_mask = 0x04;	/* XXX - should be generated */
	scb_fake(0x120, oldvsbus ? 0x14 : 0x15);
	return (20);
}

int
lcspx_vxtbus_match(struct device *parent, void *vcf, void *aux)
{
	struct bp_conf *bp = aux;
	int missing;
	volatile u_int8_t *ch;

	if (strcmp(bp->type, lcspx_cd.cd_name) != 0)
		return (0);

	/*
	 * Check for video memory at SPX address.
	 */
	missing = 0;
	ch = (volatile u_int8_t *)vax_map_physmem(LCSPX_FB_ADDR, 1);
	*ch = 0x01;
	if ((*ch & 0x01) == 0)
		missing = 1;
	else {
		*ch = 0x00;
		if ((*ch & 0x01) != 0)
			missing = 1;
	}
	vax_unmap_physmem((vaddr_t)ch, 1);

	return (missing ? 0 : 1);
}

void
lcspx_attach(struct device *parent, struct device *self, void *aux)
{
	struct lcspx_softc *sc = (struct lcspx_softc *)self;
	struct lcspx_screen *ss;
	struct wsemuldisplaydev_attach_args aa;
	int i, console;
	vaddr_t reg1;
	u_int32_t magic;
	u_int width, height;
	extern struct consdev wsdisplay_cons;

	if (cn_tab == &wsdisplay_cons) {
		switch (vax_boardtype) {
		case VAX_BTYP_420:
		case VAX_BTYP_43:
			console = (vax_confdata & KA420_CFG_L3CON) == 0;
			break;
		case VAX_BTYP_49:
			console = (vax_confdata & 8) == 0;
			break;
		default: /* VXT2000 */
			console = (vax_confdata & 2) != 0;
			break;
		}
	} else
		console = 0;
	if (console) {
		ss = &lcspx_consscr;
		sc->sc_nscreens = 1;
	} else {
		ss = malloc(sizeof(*ss), M_DEVBUF, M_NOWAIT | M_ZERO);
		if (ss == NULL) {
			printf(": can not allocate memory\n");
			return;
		}

		switch (vax_boardtype) {
		case VAX_BTYP_420:
		case VAX_BTYP_43:
			/*
			 * See ``EVIL KLUGE ALERT!'' comments in
			 * lcspxcninit() at the end of this file
			 * for meaningless details.
			 */
			reg1 = vax_map_physmem(LCSPX_REG1_ADDR, 1);
			if (reg1 == 0L) {
				printf(": can not map registers\n");
				goto fail1;
			}
			magic = *(u_int32_t *)(reg1 + 0x11c);
			vax_unmap_physmem(reg1, 1);

			if (magic & 0x80) {
				width = 1280;
				height = 1024;
			} else {
				width = 1024;
				height = 864;
			}
			break;
		default:
			width = 1280;
			height = 1024;
			break;
		}

		ss->ss_reg = vax_map_physmem(LCSPX_REG_ADDR,
		    LCSPX_REG_SIZE / VAX_NBPG);
		if (ss->ss_reg == 0L) {
			printf(": can not map registers\n");
			goto fail1;
		}

		for (i = 0; i < 4; i++) {
			ss->ss_ramdac[i] = (volatile u_int8_t *)vax_map_physmem(
			    LCSPX_RAMDAC_ADDR + i * LCSPX_RAMDAC_INTERLEAVE, 1);
			if (ss->ss_ramdac[i] == NULL) {
				printf(": can not map RAMDAC registers\n");
				goto fail2;
			}
		}

		ss->ss_fbsize = width * height;
		ss->ss_addr = (caddr_t)vax_map_physmem(LCSPX_FB_ADDR,
		    ss->ss_fbsize / VAX_NBPG);
		if (ss->ss_addr == NULL) {
			printf(": can not map frame buffer\n");
			goto fail3;
		}

		if (lcspx_setup_screen(ss, width, height) != 0) {
			printf(": initialization failed\n");
			goto fail3;
		}
	}
	sc->sc_scr = ss;

	printf("\n%s: %dx%dx8 frame buffer\n", self->dv_xname,
	    ss->ss_ri.ri_width, ss->ss_ri.ri_height);

	aa.console = console;
	aa.scrdata = &lcspx_screenlist;
	aa.accessops = &lcspx_accessops;
	aa.accesscookie = sc;
	aa.defaultscreens = 0;

	config_found(self, &aa, wsemuldisplaydevprint);
	return;

fail3:
	vax_unmap_physmem((vaddr_t)ss->ss_addr, ss->ss_fbsize / VAX_NBPG);
fail2:
	for (i = 0; i < 4; i++)
		if (ss->ss_ramdac[i] != NULL)
			vax_unmap_physmem((vaddr_t)ss->ss_ramdac[i], 1);
	vax_unmap_physmem(ss->ss_reg, LCSPX_REG_SIZE / VAX_NBPG);
fail1:
	free(ss, M_DEVBUF, sizeof(*ss));
}

static __inline__ void
lcspx_ramdac_wraddr(struct lcspx_screen *ss, u_int addr)
{
	*(ss->ss_ramdac[BT463_REG_ADDR_LOW]) = addr & 0xff;
	*(ss->ss_ramdac[BT463_REG_ADDR_HIGH]) = (addr >> 8) & 0xff;
}

/*
 * Initialize anything necessary for an emulating wsdisplay to work (i.e.
 * pick a font, initialize a rasops structure, setup the accessops callbacks.)
 */
int
lcspx_setup_screen(struct lcspx_screen *ss, u_int width, u_int height)
{
	struct rasops_info *ri = &ss->ss_ri;

	bzero(ri, sizeof(*ri));
	ri->ri_depth = 8;
	ri->ri_width = width;
	ri->ri_height = height;
	ri->ri_stride = width;
	ri->ri_flg = RI_CLEAR | RI_CENTER;
	ri->ri_bits = (void *)ss->ss_addr;
	ri->ri_hw = ss;

	/*
	 * Enable all planes for reading and writing
	 */
	lcspx_reg_write(ss, 0x1170, 0xffffffff);
	lcspx_reg_write(ss, 0x1174, 0xffffffff);
	lcspx_ramdac_wraddr(ss, 0x0204);	/* plane mask */
	*(ss->ss_ramdac[BT463_REG_IREG_DATA]) = 0xff;

	/*
	 * Ask for an unholy big display, rasops will trim this to more
	 * reasonable values.
	 */
	if (rasops_init(ri, 160, 160) != 0)
		return (-1);

	lcspx_resetcmap(ss);

	lcspx_stdscreen.ncols = ri->ri_cols;
	lcspx_stdscreen.nrows = ri->ri_rows;
	lcspx_stdscreen.textops = &ri->ri_ops;
	lcspx_stdscreen.fontwidth = ri->ri_font->fontwidth;
	lcspx_stdscreen.fontheight = ri->ri_font->fontheight;
	lcspx_stdscreen.capabilities = ri->ri_caps;

	return (0);
}

int
lcspx_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct lcspx_softc *sc = v;
	struct lcspx_screen *ss = sc->sc_scr;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_cmap *cm;
	int error;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_LCSPX;
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = ss->ss_ri.ri_height;
		wdf->width = ss->ss_ri.ri_width;
		wdf->depth = 8;
		wdf->cmsize = 256;
		break;

	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = ss->ss_ri.ri_stride;
		break;

	case WSDISPLAYIO_GETCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = lcspx_getcmap(ss, cm);
		if (error != 0)
			return (error);
		break;
	case WSDISPLAYIO_PUTCMAP:
		cm = (struct wsdisplay_cmap *)data;
		error = lcspx_putcmap(ss, cm);
		if (error != 0)
			return (error);
		lcspx_loadcmap(ss, cm->index, cm->count);
		break;

	case WSDISPLAYIO_GVIDEO:
	case WSDISPLAYIO_SVIDEO:
		break;

	default:
		return (-1);
	}

	return (0);
}

paddr_t
lcspx_mmap(void *v, off_t offset, int prot)
{
	struct lcspx_softc *sc = v;
	struct lcspx_screen *ss = sc->sc_scr;

	if (offset >= ss->ss_fbsize || offset < 0)
		return (-1);

	return (LCSPX_FB_ADDR + offset);
}

int
lcspx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
	struct lcspx_softc *sc = v;
	struct lcspx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	if (sc->sc_nscreens > 0)
		return (ENOMEM);

	*cookiep = ri;
	*curxp = *curyp = 0;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, defattrp);
	sc->sc_nscreens++;

	return (0);
}

void
lcspx_free_screen(void *v, void *cookie)
{
	struct lcspx_softc *sc = v;

	sc->sc_nscreens--;
}

int
lcspx_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{
	return (0);
}

int
lcspx_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
{
	struct lcspx_softc *sc = v;
	struct lcspx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_load_font(ri, emulcookie, font);
}

int
lcspx_list_font(void *v, struct wsdisplay_font *font)
{
	struct lcspx_softc *sc = v;
	struct lcspx_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_list_font(ri, font);
}

/*
 * Colormap handling routines
 */

int
lcspx_getcmap(struct lcspx_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count, i;
	int error;
	u_int8_t ramp[256], *c, *r;

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

	/* extract reds */
	c = ss->ss_cmap + 0 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->red, count)) != 0)
		return (error);

	/* extract greens */
	c = ss->ss_cmap + 1 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->green, count)) != 0)
		return (error);

	/* extract blues */
	c = ss->ss_cmap + 2 + index * 3;
	for (i = count, r = ramp; i != 0; i--)
		*r++ = *c, c += 3;
	if ((error = copyout(ramp, cm->blue, count)) != 0)
		return (error);

	return (0);
}

int
lcspx_putcmap(struct lcspx_screen *ss, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index, count = cm->count;
	int i, error;
	u_int8_t r[256], g[256], b[256], *nr, *ng, *nb, *c;

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

	if ((error = copyin(cm->red, r, count)) != 0)
		return (error);
	if ((error = copyin(cm->green, g, count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, b, count)) != 0)
		return (error);

	nr = r, ng = g, nb = b;
	c = ss->ss_cmap + index * 3;
	for (i = count; i != 0; i--) {
		*c++ = *nr++;
		*c++ = *ng++;
		*c++ = *nb++;
	}

	return (0);
}

void
lcspx_loadcmap(struct lcspx_screen *ss, int from, int count)
{
	u_int8_t *cmap = ss->ss_cmap;
	int i;

	cmap += from * 3;
	for (i = from; i < from + count; i++) {
		/*
		 * Reprogram the index every iteration, because the RAMDAC
		 * may not be in autoincrement mode. XXX fix this
		 */
		lcspx_ramdac_wraddr(ss, i);
		*(ss->ss_ramdac[BT463_REG_CMAP_DATA]) = *cmap++;
		*(ss->ss_ramdac[BT463_REG_CMAP_DATA]) = *cmap++;
		*(ss->ss_ramdac[BT463_REG_CMAP_DATA]) = *cmap++;
	}
}

void
lcspx_resetcmap(struct lcspx_screen *ss)
{
	bcopy(rasops_cmap, ss->ss_cmap, sizeof(ss->ss_cmap));
	lcspx_loadcmap(ss, 0, 256);
}

/*
 * Console support code
 */

int	lcspxcnprobe(void);
int	lcspxcninit(void);

int
lcspxcnprobe()
{
	extern vaddr_t virtual_avail;
	volatile struct adder *adder;
	volatile u_int8_t *ch;
	u_short status;
	int rc;

	switch (vax_boardtype) {
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		if ((vax_confdata & KA420_CFG_L3CON) != 0)
			break; /* doesn't use graphics console */

		/* not present on microvaxes */
		if ((vax_confdata & KA420_CFG_MULTU) != 0)
			break;

		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
			break;

		/*
		 * We can not check for video memory at the SPX address,
		 * because if no SPX board is installed, the probe will
		 * just hang.
		 * Instead, check for a GPX board and skip probe if the
		 * video option really is a GPX.
		 */
		ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1);
		adder = (volatile struct adder *)virtual_avail;
		adder->status = 0;
		status = adder->status;
		iounaccess(virtual_avail, 1);
		if (status != offsetof(struct adder, status))
			break;

		return (1);

	case VAX_BTYP_49:
		if ((vax_confdata & 8) != 0)
			break; /* doesn't use graphics console */

		if ((vax_confdata & 0x12) != 0x02)
			break;

		return (1);

	case VAX_BTYP_VXT:
		if ((vax_confdata & 2) == 0)
			break; /* doesn't use graphics console */

		/*
		 * Check for video memory at SPX address.
		 */
		rc = 0;
		ioaccess(virtual_avail, LCSPX_FB_ADDR, 1);
		ch = (volatile u_int8_t *)virtual_avail;
		*ch = 0x01;
		if ((*ch & 0x01) != 0) {
			*ch = 0x00;
			if ((*ch & 0x01) == 0)
				rc = 1;
		}
		iounaccess(virtual_avail, 1);

		if (rc == 0)
			break;

		return (1);

	default:
		break;
	}

	return (0);
}

/*
 * Called very early to setup the glass tty as console.
 * Because it's called before the VM system is initialized, virtual memory
 * for the framebuffer can be stolen directly without disturbing anything.
 */
int
lcspxcninit()
{
	struct lcspx_screen *ss = &lcspx_consscr;
	extern vaddr_t virtual_avail;
	int i;
	u_int width, height;
	vaddr_t ova, reg1;
	u_int32_t magic;
	long defattr;
	struct rasops_info *ri;

	ova = virtual_avail;

	switch (vax_boardtype) {
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		/*
		 * XXX EVIL KLUGE ALERT!
		 *
		 * The spx jumper settings do not show up in vax_confdata.
		 * I don't know which spx register sports their values,
		 * but I have noticed that, after the ROM has initialized
		 * the board, bit 0x80 at 39b0011c will reflect the
		 * resolution setting.
		 *
		 * This register is not read-only, so one could DEPOSIT
		 * a bogus value in it from the console before starting
		 * OpenBSD.  If you do this, you deserve to be bitten
		 * if things go wrong.
		 */
		reg1 = virtual_avail;
		ioaccess(virtual_avail, LCSPX_REG1_ADDR, 1);
		magic = *(u_int32_t *)(reg1 + 0x11c);
		iounaccess(virtual_avail, 1);

		if (magic & 0x80) {
			width = 1280;
			height = 1024;
		} else {
			width = 1024;
			height = 864;
		}
		break;
	default:
		width = 1280;
		height = 1024;
		break;
	}

	ss->ss_fbsize = width * height;
	ss->ss_addr = (caddr_t)virtual_avail;
	ioaccess(virtual_avail, LCSPX_FB_ADDR, ss->ss_fbsize / VAX_NBPG);
	virtual_avail += ss->ss_fbsize;

	ss->ss_reg = virtual_avail;
	ioaccess(virtual_avail, LCSPX_REG_ADDR, LCSPX_REG_SIZE / VAX_NBPG);
	virtual_avail += LCSPX_REG_SIZE;

	for (i = 0; i < 4; i++) {
		ss->ss_ramdac[i] = (volatile u_int8_t *)virtual_avail;
		ioaccess(virtual_avail,
		    LCSPX_RAMDAC_ADDR + i * LCSPX_RAMDAC_INTERLEAVE, 1);
		virtual_avail += VAX_NBPG;
	}

	virtual_avail = round_page(virtual_avail);

	/* this had better not fail */
	if (lcspx_setup_screen(ss, width, height) != 0) {
		for (i = 3; i >= 0; i--)
			iounaccess((vaddr_t)ss->ss_ramdac[i], 1);
		iounaccess(ss->ss_reg, LCSPX_REG_SIZE / VAX_NBPG);
		iounaccess((vaddr_t)ss->ss_addr, ss->ss_fbsize / VAX_NBPG);
		virtual_avail = ova;
		return (1);
	}

	ri = &ss->ss_ri;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&lcspx_stdscreen, ri, 0, 0, defattr);

	return (0);
}


code:
/*	$OpenBSD: maskbits.h,v 1.1 2006/08/05 10:00:30 miod Exp $	*/

/*-
 * Copyright (c) 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)maskbits.h	8.2 (Berkeley) 3/21/94
 */

/*
 * Derived from X11R4
 */

/* the following notes use the following conventions:
SCREEN LEFT				SCREEN RIGHT
in this file and maskbits.c, left and right refer to screen coordinates,
NOT bit numbering in registers.

rasops_lmask[n]
	bits[0,n-1] = 0	bits[n,31] = 1
rasops_rmask[n] =
	bits[0,n-1] = 1	bits[n,31] = 0

maskbits(x, w, startmask, endmask, nlw)
	for a span of width w starting at position x, returns
a mask for ragged bits at start, mask for ragged bits at end,
and the number of whole longwords between the ends.

*/

#define maskbits(x, w, startmask, endmask, nlw)				\
do {									\
	startmask = rasops_lmask[(x) & 0x1f];				\
	endmask = rasops_rmask[((x) + (w)) & 0x1f];			\
	if (startmask)							\
		nlw = (((w) - (32 - ((x) & 0x1f))) >> 5);		\
	else								\
		nlw = (w) >> 5;						\
} while (0)

#define	FASTGETBITS(psrc,x,w,dst)					\
	__asm__ ("extzv %1,%2,%3,%0"					\
		: "=g" (dst)						\
		: "g" (x), "g" (w), "m" (*(char *)(psrc)))

#define	FASTPUTBITS(src, x, w, pdst)					\
	__asm__ ("insv %3,%1,%2,%0"					\
		: "=m" (*(char *)(pdst))				\
		: "g" (x), "g" (w), "g" (src))

#define	RR_CLEAR	0x00
#define	RR_SET		0x01
#define	RR_COPY		0x02

#define getandputrop(psrc, srcbit, dstbit, width, pdst, rop)		\
do {									\
	unsigned int _tmpdst;						\
	switch (rop) {							\
	case RR_CLEAR:							\
		_tmpdst = 0;						\
		break;							\
	case RR_SET:							\
		_tmpdst = ~0;						\
		break;							\
	default:							\
		FASTGETBITS(psrc, srcbit, width, _tmpdst);		\
		break;							\
	}								\
	FASTPUTBITS(_tmpdst, dstbit, width, pdst);			\
} while (0)

#define getunalignedword(psrc, x, dst)					\
do {									\
        int _tmp;							\
        FASTGETBITS(psrc, x, 32, _tmp);					\
        dst = _tmp;							\
} while (0)


code:
/* $OpenBSD: ncr.c,v 1.28 2010/09/20 06:33:48 matthew Exp $ */
/*	$NetBSD: ncr.c,v 1.32 2000/06/25 16:00:43 ragge Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Adam Glass, David Jones, Gordon W. Ross, and Jens A. Nilsson.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This file contains the machine-dependent parts of the NCR-5380
 * controller. The machine-independent parts are in ncr5380sbc.c.
 *
 * Jens A. Nilsson.
 *
 * Credits:
 *
 * This code is based on arch/sun3/dev/si*
 * Written by David Jones, Gordon Ross, and Adam Glass.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/disk.h>
#include <sys/proc.h>

#include <uvm/uvm_extern.h>

#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
#include <scsi/sdvar.h>

#include <dev/ic/ncr5380reg.h>
#include <dev/ic/ncr5380var.h>

#include <machine/cpu.h>
#include <machine/vsbus.h>
#include <machine/bus.h>
#include <machine/sid.h>
#include <machine/scb.h>
#include <machine/clock.h>

#define MIN_DMA_LEN 128

struct si_dma_handle {
	int	dh_flags;
#define SIDH_BUSY	1
#define SIDH_OUT	2
	caddr_t dh_addr;
	int	dh_len;
	struct	proc *dh_proc;
};

struct si_softc {
	struct	ncr5380_softc	ncr_sc;
	struct	evcount		ncr_intrcnt;
	int			ncr_cvec;
	caddr_t ncr_addr;
	int	ncr_off;
	int	ncr_dmaaddr;
	int	ncr_dmacount;
	int	ncr_dmadir;

	/* Pointers to bus_space */
	bus_space_tag_t     sc_regt;
	bus_space_handle_t  sc_regh;

	struct	si_dma_handle ncr_dma[SCI_OPENINGS];
	struct	vsbus_dma sc_vd;
	int	onlyscsi;	/* This machine needs no queueing */
};

static int ncr_dmasize;

static	int si_match(struct device *, void *, void *);
static	void si_attach(struct device *, struct device *, void *);
static	void si_minphys(struct buf *, struct scsi_link *);

static	void si_dma_alloc(struct ncr5380_softc *);
static	void si_dma_free(struct ncr5380_softc *);
static	void si_dma_setup(struct ncr5380_softc *);
static	void si_dma_start(struct ncr5380_softc *);
static	void si_dma_poll(struct ncr5380_softc *);
static	void si_dma_stop(struct ncr5380_softc *);
static	void si_dma_go(void *);

#define NCR5380_READ(sc, reg)	bus_space_read_1(sc->sc_regt,		\
	0, sc->ncr_sc.reg)
#define NCR5380_WRITE(sc, reg, val)	bus_space_write_1(sc->sc_regt,	\
	0, sc->ncr_sc.reg, val)

struct scsi_adapter	si_ops = {
	ncr5380_scsi_cmd,	/* scsi_cmd() */
	si_minphys,		/* scsi_minphys() */
	NULL,			/* probe_dev() */
	NULL			/* free_dev() */
};

struct cfattach ncr_ca = {
	sizeof(struct si_softc), si_match, si_attach
};

struct cfdriver ncr_cd = {
	NULL, "ncr", DV_DULL
};

static int
si_match(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{
	struct vsbus_attach_args *va = aux;
	volatile char *si_csr = (char *) va->va_addr;

	if (vax_boardtype == VAX_BTYP_49 || vax_boardtype == VAX_BTYP_46
	    || vax_boardtype == VAX_BTYP_48 || vax_boardtype == VAX_BTYP_1303)
		return 0;
	/* This is the way Linux autoprobes the interrupt MK-990321 */
	si_csr[12] = 0;
	si_csr[16] = 0x80;
	si_csr[0] = 0x80;
	si_csr[4] = 5; /* 0xcf */
	DELAY(100000);
	return 1;
}

static void
si_attach(parent, self, aux)
	struct device	*parent, *self;
	void		*aux;
{
	struct vsbus_attach_args *va = aux;
	struct si_softc *sc = (struct si_softc *) self;
	struct ncr5380_softc *ncr_sc = &sc->ncr_sc;
	struct scsibus_attach_args saa;
	int tweak, target;

	printf("\n");

	/* enable interrupts on vsbus too */
	scb_vecalloc(va->va_cvec, (void (*)(void *)) ncr5380_intr, sc,
	    SCB_ISTACK, &sc->ncr_intrcnt);
	sc->ncr_cvec = va->va_cvec;
	evcount_attach(&sc->ncr_intrcnt, self->dv_xname, &sc->ncr_cvec);

	/*
	 * DMA area mapin.
	 * On VS3100, split the 128K block between the two devices.
	 * On VS2000, don't care for now.
	 */
#define DMASIZE (64*1024)
	if (va->va_paddr & 0x100) { /* Secondary SCSI controller */
		sc->ncr_off = DMASIZE;
		sc->onlyscsi = 1;
	}
	sc->ncr_addr = (caddr_t)va->va_dmaaddr;
	ncr_dmasize = min(va->va_dmasize, MAXPHYS);

	/*
	 * MD function pointers used by the MI code.
	 */
	ncr_sc->sc_dma_alloc = si_dma_alloc;
	ncr_sc->sc_dma_free  = si_dma_free;
	ncr_sc->sc_dma_setup = si_dma_setup;
	ncr_sc->sc_dma_start = si_dma_start;
	ncr_sc->sc_dma_poll  = si_dma_poll;
	ncr_sc->sc_dma_stop  = si_dma_stop;

	/* DMA control register offsets */
	sc->ncr_dmaaddr = 32;	/* DMA address in buffer, longword */
	sc->ncr_dmacount = 64;	/* DMA count register */
	sc->ncr_dmadir = 68;	/* Direction of DMA transfer */

	ncr_sc->sc_pio_out = ncr5380_pio_out;
	ncr_sc->sc_pio_in =  ncr5380_pio_in;

	ncr_sc->sc_min_dma_len = MIN_DMA_LEN;

	/*
	 * Initialize fields used by the MI code.
	 */
/*	sc->sc_regt =  Unused on VAX */
	sc->sc_regh = vax_map_physmem(va->va_paddr, 1);

	/* Register offsets */
	ncr_sc->sci_r0 = (void *)sc->sc_regh+0;
	ncr_sc->sci_r1 = (void *)sc->sc_regh+4;
	ncr_sc->sci_r2 = (void *)sc->sc_regh+8;
	ncr_sc->sci_r3 = (void *)sc->sc_regh+12;
	ncr_sc->sci_r4 = (void *)sc->sc_regh+16;
	ncr_sc->sci_r5 = (void *)sc->sc_regh+20;
	ncr_sc->sci_r6 = (void *)sc->sc_regh+24;
	ncr_sc->sci_r7 = (void *)sc->sc_regh+28;

	ncr_sc->sc_no_disconnect = 0xff;

	/*
	 * Get the SCSI chip target address out of NVRAM.
	 * This do not apply to the VS2000.
	 */
	tweak = clk_tweak + (va->va_paddr & 0x100 ? 3 : 0);
	if (vax_boardtype == VAX_BTYP_410)
		target = 7;
	else
		target = (clk_page[0xbc/2] >> tweak) & 7;

	ncr_sc->sc_link.adapter_softc =	sc;
	ncr_sc->sc_link.adapter_target = target;
	ncr_sc->sc_link.adapter = &si_ops;
	ncr_sc->sc_link.openings = 4;

	/*
	 * Init the vsbus DMA resource queue struct */
	sc->sc_vd.vd_go = si_dma_go;
	sc->sc_vd.vd_arg = sc;

	bzero(&saa, sizeof(saa));
	saa.saa_sc_link = &(ncr_sc->sc_link);

	/*
	 * Initialize si board itself.
	 */
	ncr5380_init(ncr_sc);
	ncr5380_reset_scsibus(ncr_sc);
	DELAY(2000000);
	config_found(&(ncr_sc->sc_dev), &saa, scsiprint);

}

/*
 * Adjust the max transfer size. The DMA buffer is only 16k on VS2000.
 */
static void
si_minphys(struct buf *bp, struct scsi_link *sl)
{
	if (bp->b_bcount > ncr_dmasize)
		bp->b_bcount = ncr_dmasize;
	minphys(bp);
}

void
si_dma_alloc(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct scsi_xfer *xs = sr->sr_xs;
	struct si_dma_handle *dh;
	struct buf *bp;
	int xlen, i;

#ifdef DIAGNOSTIC
	if (sr->sr_dma_hand != NULL)
		panic("si_dma_alloc: already have DMA handle");
#endif

	/* Polled transfers shouldn't allocate a DMA handle. */
	if (sr->sr_flags & SR_IMMED)
		return;

	xlen = ncr_sc->sc_datalen;

	/* Make sure our caller checked sc_min_dma_len. */
	if (xlen < MIN_DMA_LEN)
		panic("si_dma_alloc: len=0x%x", xlen);

	/*
	 * Find free PDMA handle.  Guaranteed to find one since we
	 * have as many PDMA handles as the driver has processes.
	 * (instances?)
	 */
	 for (i = 0; i < SCI_OPENINGS; i++) {
		if ((sc->ncr_dma[i].dh_flags & SIDH_BUSY) == 0)
			goto found;
	}
	panic("ncr: no free PDMA handles");
found:
	dh = &sc->ncr_dma[i];
	dh->dh_flags = SIDH_BUSY;
	dh->dh_addr = ncr_sc->sc_dataptr;
	dh->dh_len = xlen;
	bp = xs->bp;
	if (bp != NULL)
		dh->dh_proc = (bp->b_flags & B_PHYS ? bp->b_proc : NULL);
	else
		dh->dh_proc = NULL;

	/* Remember dest buffer parameters */
	if (xs->flags & SCSI_DATA_OUT)
		dh->dh_flags |= SIDH_OUT;

	sr->sr_dma_hand = dh;
}

void
si_dma_free(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;

#ifdef DIAGNOSTIC
	if (dh == NULL)
		panic("si_dma_free: no DMA handle");
#endif

	if (ncr_sc->sc_state & NCR_DOINGDMA)
		panic("si_dma_free: free while DMA in progress");

	if (dh->dh_flags & SIDH_BUSY)
		dh->dh_flags = 0;
	else
		printf("si_dma_free: free'ing unused buffer\n");

	sr->sr_dma_hand = NULL;
}

void
si_dma_setup(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	/* Do nothing here */
}

void
si_dma_start(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;

	/* Just put on queue; will call go() from below */
	if (sc->onlyscsi)
		si_dma_go(ncr_sc);
	else
		vsbus_dma_start(&sc->sc_vd);
}

/*
 * go() routine called when another transfer somewhere is finished.
 */
void
si_dma_go(arg)
	void *arg;
{
	struct ncr5380_softc *ncr_sc = arg;
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;

	/*
	 * Set the VAX-DMA-specific registers, and copy the data if
	 * it is directed "outbound".
	 */
	if (dh->dh_flags & SIDH_OUT) {
		vsbus_copyfromproc(dh->dh_proc, dh->dh_addr,
		    sc->ncr_addr + sc->ncr_off, dh->dh_len);
		bus_space_write_1(sc->sc_regt, sc->sc_regh,
		    sc->ncr_dmadir, 0);
	} else {
		bus_space_write_1(sc->sc_regt, sc->sc_regh,
		    sc->ncr_dmadir, 1);
	}
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    sc->ncr_dmacount, -dh->dh_len);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    sc->ncr_dmaaddr, sc->ncr_off);
	/*
	 * Now from the 5380-internal DMA registers.
	 */
	if (dh->dh_flags & SIDH_OUT) {
		NCR5380_WRITE(sc, sci_tcmd, PHASE_DATA_OUT);
		NCR5380_WRITE(sc, sci_icmd, SCI_ICMD_DATA);
		NCR5380_WRITE(sc, sci_mode, NCR5380_READ(sc, sci_mode)
		    | SCI_MODE_DMA | SCI_MODE_DMA_IE);
		NCR5380_WRITE(sc, sci_dma_send, 0);
	} else {
		NCR5380_WRITE(sc, sci_tcmd, PHASE_DATA_IN);
		NCR5380_WRITE(sc, sci_icmd, 0);
		NCR5380_WRITE(sc, sci_mode, NCR5380_READ(sc, sci_mode)
		    | SCI_MODE_DMA | SCI_MODE_DMA_IE);
		NCR5380_WRITE(sc, sci_irecv, 0);
	}
	ncr_sc->sc_state |= NCR_DOINGDMA;
}

/*
 * When?
 */
void
si_dma_poll(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	printf("si_dma_poll\n");
}

void
si_dma_stop(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;
	int count, i;

	if (ncr_sc->sc_state & NCR_DOINGDMA)
		ncr_sc->sc_state &= ~NCR_DOINGDMA;

	/*
	 * Sometimes the FIFO buffer isn't drained when the
	 * interrupt is posted. Just loop here and hope that
	 * it will drain soon.
	 */
	for (i = 0; i < 20000; i++) {
		count = bus_space_read_4(sc->sc_regt,
		    sc->sc_regh, sc->ncr_dmacount);
		if (count == 0)
			break;
		DELAY(100);
	}
	if (count == 0) {
		if (((dh->dh_flags & SIDH_OUT) == 0)) {
			vsbus_copytoproc(dh->dh_proc,
			    sc->ncr_addr + sc->ncr_off,
			    dh->dh_addr, dh->dh_len);
		}
		ncr_sc->sc_dataptr += dh->dh_len;
		ncr_sc->sc_datalen -= dh->dh_len;
	}

	NCR5380_WRITE(sc, sci_mode, NCR5380_READ(sc, sci_mode) &
	    ~(SCI_MODE_DMA | SCI_MODE_DMA_IE));
	NCR5380_WRITE(sc, sci_icmd, 0);
	if (sc->onlyscsi == 0)
		vsbus_dma_intr(); /* Try to start more transfers */
}


code:
/*	$OpenBSD: smg.c,v 1.28 2014/12/23 21:39:12 miod Exp $	*/
/*	$NetBSD: smg.c,v 1.21 2000/03/23 06:46:44 thorpej Exp $ */
/*
 * Copyright (c) 2006, Miodrag Vallat
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 1998 Ludd, University of Lule}, Sweden.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed at Ludd, University of
 *	Lule}, Sweden and its contributors.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 1996 Jason R. Thorpe.  All rights reserved.
 * Copyright (c) 1991 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department and Mark Davies of the Department of Computer
 * Science, Victoria University of Wellington, New Zealand.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: grf_hy.c 1.2 93/08/13$
 *
 *	@(#)grf_hy.c	8.4 (Berkeley) 1/12/94
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/kernel.h>

#include <machine/vsbus.h>
#include <machine/sid.h>
#include <machine/cpu.h>
#include <machine/ka420.h>
#include <machine/scb.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <dev/ic/dc503reg.h>

#include <vax/qbus/dzreg.h>
#include <vax/qbus/dzvar.h>
#include <vax/dec/dzkbdvar.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <dev/rasops/rasops_masks.h>

/* Screen hardware defs */
#define SM_XWIDTH	1024
#define SM_YWIDTH	864

#define CUR_XBIAS	216	/* Add to cursor position */
#define CUR_YBIAS	33

int	smg_match(struct device *, void *, void *);
void	smg_attach(struct device *, struct device *, void *);

struct	smg_screen {
	struct rasops_info ss_ri;
	caddr_t		ss_addr;		/* frame buffer address */
	struct dc503reg	*ss_cursor;		/* cursor registers */
	u_int16_t	ss_curcmd;
	struct wsdisplay_curpos ss_curpos, ss_curhot;
	u_int16_t	ss_curimg[PCC_CURSOR_SIZE];
	u_int16_t	ss_curmask[PCC_CURSOR_SIZE];
};

/* for console */
struct smg_screen smg_consscr;

struct	smg_softc {
	struct device sc_dev;
	struct smg_screen *sc_scr;
	int	sc_nscreens;
};

struct cfattach smg_ca = {
	sizeof(struct smg_softc), smg_match, smg_attach,
};

struct	cfdriver smg_cd = {
	NULL, "smg", DV_DULL
};

struct wsscreen_descr smg_stdscreen = {
	"std",
};

const struct wsscreen_descr *_smg_scrlist[] = {
	&smg_stdscreen,
};

const struct wsscreen_list smg_screenlist = {
	sizeof(_smg_scrlist) / sizeof(struct wsscreen_descr *),
	_smg_scrlist,
};

int	smg_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	smg_mmap(void *, off_t, int);
int	smg_alloc_screen(void *, const struct wsscreen_descr *,
	    void **, int *, int *, long *);
void	smg_free_screen(void *, void *);
int	smg_show_screen(void *, void *, int,
	    void (*) (void *, int, int), void *);
int	smg_load_font(void *, void *, struct wsdisplay_font *);
int	smg_list_font(void *, struct wsdisplay_font *);
void	smg_burner(void *, u_int, u_int);

const struct wsdisplay_accessops smg_accessops = {
	.ioctl = smg_ioctl,
	.mmap = smg_mmap,
	.alloc_screen = smg_alloc_screen,
	.free_screen = smg_free_screen,
	.show_screen = smg_show_screen,
	.load_font = smg_load_font,
	.list_font = smg_list_font,
	.burn_screen = smg_burner
};

void	smg_blockmove(struct rasops_info *, u_int, u_int, u_int, u_int, u_int,
	    int);
int	smg_copycols(void *, int, int, int, int);
int	smg_erasecols(void *, int, int, int, long);

int	smg_getcursor(struct smg_screen *, struct wsdisplay_cursor *);
int	smg_setup_screen(struct smg_screen *);
int	smg_setcursor(struct smg_screen *, struct wsdisplay_cursor *);
void	smg_updatecursor(struct smg_screen *, u_int);

int
smg_match(struct device *parent, void *vcf, void *aux)
{
	struct cfdata *cf = vcf;
	struct vsbus_attach_args *va = aux;
	volatile short *curcmd;
	volatile short *cfgtst;
	short tmp, tmp2;
	extern struct consdev wsdisplay_cons;

	switch (vax_boardtype) {
	default:
		return (0);

	case VAX_BTYP_410:
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		if (va->va_paddr != KA420_CUR_BASE)
			return (0);

		/* not present on microvaxes */
		if ((vax_confdata & KA420_CFG_MULTU) != 0)
			return (0);

		/*
		 * If the color option board is present, do not attach
		 * unless we are explicitely asked to via device flags.
		 */
		if ((vax_confdata & KA420_CFG_VIDOPT) != 0 &&
		    (cf->cf_flags & 1) == 0)
			return (0);
		break;
	}

	/* when already running as console, always fake things */
	if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_VIDOPT)) == 0 &&
	    cn_tab == &wsdisplay_cons) {
		struct vsbus_softc *sc = (void *)parent;
		extern int oldvsbus;

		sc->sc_mask = 0x08;
		scb_fake(0x44, oldvsbus ? 0x14 : 0x15);
		return (20);
	} else {
		/*
		 * Try to find the cursor chip by testing the flip-flop.
		 * If nonexistent, no glass tty.
		 */
		curcmd = (short *)va->va_addr;
		cfgtst = (short *)vax_map_physmem(VS_CFGTST, 1);
		curcmd[0] = PCCCMD_HSHI | PCCCMD_FOPB;
		DELAY(300000);
		tmp = cfgtst[0];
		curcmd[0] = PCCCMD_TEST | PCCCMD_HSHI;
		DELAY(300000);
		tmp2 = cfgtst[0];
		vax_unmap_physmem((vaddr_t)cfgtst, 1);

		if (tmp2 != tmp)
			return (20); /* Using periodic interrupt */
		else
			return (0);
	}
}

void
smg_attach(struct device *parent, struct device *self, void *aux)
{
	struct smg_softc *sc = (struct smg_softc *)self;
	struct smg_screen *scr;
	struct wsemuldisplaydev_attach_args aa;
	int console;
	extern struct consdev wsdisplay_cons;

	console = (vax_confdata & (KA420_CFG_L3CON | KA420_CFG_VIDOPT)) == 0 &&
	    cn_tab == &wsdisplay_cons;
	if (console) {
		scr = &smg_consscr;
		sc->sc_nscreens = 1;
	} else {
		scr = malloc(sizeof(*scr), M_DEVBUF, M_NOWAIT | M_ZERO);
		if (scr == NULL) {
			printf(": can not allocate memory\n");
			return;
		}

		scr->ss_addr =
		    (caddr_t)vax_map_physmem(SMADDR, SMSIZE / VAX_NBPG);
		if (scr->ss_addr == NULL) {
			printf(": can not map frame buffer\n");
			free(scr, M_DEVBUF, sizeof(*scr));
			return;
		}

		scr->ss_cursor =
		    (struct dc503reg *)vax_map_physmem(KA420_CUR_BASE, 1);
		if (scr->ss_cursor == NULL) {
			printf(": can not map cursor chip\n");
			vax_unmap_physmem((vaddr_t)scr->ss_addr,
			    SMSIZE / VAX_NBPG);
			free(scr, M_DEVBUF, sizeof(*scr));
			return;
		}

		if (smg_setup_screen(scr) != 0) {
			printf(": initialization failed\n");
			vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1);
			vax_unmap_physmem((vaddr_t)scr->ss_addr,
			    SMSIZE / VAX_NBPG);
			free(scr, M_DEVBUF, sizeof(*scr));
			return;
		}
	}
	sc->sc_scr = scr;

	printf("\n%s: %dx%d on-board monochrome framebuffer\n",
	    self->dv_xname, SM_XWIDTH, SM_YWIDTH);

	aa.console = console;
	aa.scrdata = &smg_screenlist;
	aa.accessops = &smg_accessops;
	aa.accesscookie = sc;
	aa.defaultscreens = 0;

	config_found(self, &aa, wsemuldisplaydevprint);
}

/*
 * Initialize anything necessary for an emulating wsdisplay to work (i.e.
 * pick a font, initialize a rasops structure, setup the accessops callbacks.)
 */
int
smg_setup_screen(struct smg_screen *ss)
{
	struct rasops_info *ri = &ss->ss_ri;

	bzero(ri, sizeof(*ri));
	ri->ri_depth = 1;
	ri->ri_width = SM_XWIDTH;
	ri->ri_height = SM_YWIDTH;
	ri->ri_stride = SM_XWIDTH >> 3;
	ri->ri_flg = RI_CLEAR | RI_CENTER;
	ri->ri_bits = (void *)ss->ss_addr;
	ri->ri_hw = ss;

	/*
	 * Ask for an unholy big display, rasops will trim this to more
	 * reasonable values.
	 */
	if (rasops_init(ri, 160, 160) != 0)
		return (-1);

	ri->ri_ops.copycols = smg_copycols;
	ri->ri_ops.erasecols = smg_erasecols;

	smg_stdscreen.ncols = ri->ri_cols;
	smg_stdscreen.nrows = ri->ri_rows;
	smg_stdscreen.textops = &ri->ri_ops;
	smg_stdscreen.fontwidth = ri->ri_font->fontwidth;
	smg_stdscreen.fontheight = ri->ri_font->fontheight;
	smg_stdscreen.capabilities = ri->ri_caps;

	ss->ss_curcmd = PCCCMD_HSHI;
	ss->ss_cursor->cmdr = ss->ss_curcmd;

	return (0);
}

int
smg_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct smg_softc *sc = v;
	struct smg_screen *ss = sc->sc_scr;
	struct wsdisplay_fbinfo *wdf;
	struct wsdisplay_curpos *pos;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_VAX_MONO;
		break;

	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = ss->ss_ri.ri_height;
		wdf->width = ss->ss_ri.ri_width;
		wdf->depth = ss->ss_ri.ri_depth;
		wdf->cmsize = 0;
		break;

	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = ss->ss_ri.ri_stride;
		break;

	case WSDISPLAYIO_GETCMAP:
	case WSDISPLAYIO_PUTCMAP:
	case WSDISPLAYIO_GVIDEO:
	case WSDISPLAYIO_SVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
		pos = (struct wsdisplay_curpos *)data;
		pos->x = ss->ss_curpos.x;
		pos->y = ss->ss_curpos.y;
		break;
	case WSDISPLAYIO_SCURPOS:
		pos = (struct wsdisplay_curpos *)data;
		ss->ss_curpos.x = pos->x;
		ss->ss_curpos.y = pos->y;
		smg_updatecursor(ss, WSDISPLAY_CURSOR_DOPOS);
		break;
	case WSDISPLAYIO_GCURMAX:
		pos = (struct wsdisplay_curpos *)data;
		pos->x = pos->y = PCC_CURSOR_SIZE;
	case WSDISPLAYIO_GCURSOR:
		return (smg_getcursor(ss, (struct wsdisplay_cursor *)data));
	case WSDISPLAYIO_SCURSOR:
		return (smg_setcursor(ss, (struct wsdisplay_cursor *)data));
		break;

	default:
		return (-1);
	}

	return (0);
}

paddr_t
smg_mmap(void *v, off_t offset, int prot)
{
	if (offset >= SMSIZE || offset < 0)
		return (-1);

	return (SMADDR + offset);
}

int
smg_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
	struct smg_softc *sc = v;
	struct smg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	if (sc->sc_nscreens > 0)
		return (ENOMEM);

	*cookiep = ri;
	*curxp = *curyp = 0;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, defattrp);
	sc->sc_nscreens++;

	return (0);
}

void
smg_free_screen(void *v, void *cookie)
{
	struct smg_softc *sc = v;

	sc->sc_nscreens--;
}

int
smg_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{
	return (0);
}

int
smg_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
{
	struct smg_softc *sc = v;
	struct smg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_load_font(ri, emulcookie, font);
}

int
smg_list_font(void *v, struct wsdisplay_font *font)
{
	struct smg_softc *sc = v;
	struct smg_screen *ss = sc->sc_scr;
	struct rasops_info *ri = &ss->ss_ri;

	return rasops_list_font(ri, font);
}

void
smg_burner(void *v, u_int on, u_int flags)
{
	struct smg_softc *sc = v;
	struct smg_screen *ss = sc->sc_scr;

	ss->ss_cursor->cmdr = on ? ss->ss_curcmd :
	    (ss->ss_curcmd & ~(PCCCMD_FOPA | PCCCMD_ENPA)) | PCCCMD_FOPB;
}

int
smg_getcursor(struct smg_screen *ss, struct wsdisplay_cursor *wdc)
{
	int error;

	if (wdc->which & WSDISPLAY_CURSOR_DOCUR)
		wdc->enable = ss->ss_curcmd & PCCCMD_ENPA ? 1 : 0;
	if (wdc->which & WSDISPLAY_CURSOR_DOPOS) {
		wdc->pos.x = ss->ss_curpos.x;
		wdc->pos.y = ss->ss_curpos.y;
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOHOT) {
		wdc->hot.x = ss->ss_curhot.x;
		wdc->hot.y = ss->ss_curhot.y;
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOCMAP) {
		wdc->cmap.index = 0;
		wdc->cmap.count = 0;
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOSHAPE) {
		wdc->size.x = wdc->size.y = PCC_CURSOR_SIZE;
		error = copyout(ss->ss_curimg, wdc->image,
		    sizeof(ss->ss_curimg));
		if (error != 0)
			return (error);
		error = copyout(ss->ss_curmask, wdc->mask,
		    sizeof(ss->ss_curmask));
		if (error != 0)
			return (error);
	}

	return (0);
}

int
smg_setcursor(struct smg_screen *ss, struct wsdisplay_cursor *wdc)
{
	u_int16_t curfg[PCC_CURSOR_SIZE], curmask[PCC_CURSOR_SIZE];
	int error;

	if (wdc->which & WSDISPLAY_CURSOR_DOCMAP) {
		/* No cursor colormap since we are a B&W device. */
		if (wdc->cmap.count != 0)
			return (EINVAL);
	}

	/*
	 * First, do the userland-kernel data transfers, so that we can fail
	 * if necessary before altering anything.
	 */
	if (wdc->which & WSDISPLAY_CURSOR_DOSHAPE) {
		if (wdc->size.x != PCC_CURSOR_SIZE ||
		    wdc->size.y != PCC_CURSOR_SIZE)
			return (EINVAL);
		error = copyin(wdc->image, curfg, sizeof(curfg));
		if (error != 0)
			return (error);
		error = copyin(wdc->mask, curmask, sizeof(curmask));
		if (error != 0)
			return (error);
	}

	/*
	 * Now update our variables...
	 */
	if (wdc->which & WSDISPLAY_CURSOR_DOCUR) {
		if (wdc->enable)
			ss->ss_curcmd |= PCCCMD_ENPB | PCCCMD_ENPA;
		else
			ss->ss_curcmd &= ~(PCCCMD_ENPB | PCCCMD_ENPA);
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOPOS) {
		ss->ss_curpos.x = wdc->pos.x;
		ss->ss_curpos.y = wdc->pos.y;
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOHOT) {
		ss->ss_curhot.x = wdc->hot.x;
		ss->ss_curhot.y = wdc->hot.y;
	}
	if (wdc->which & WSDISPLAY_CURSOR_DOSHAPE) {
		bcopy(curfg, ss->ss_curimg, sizeof ss->ss_curimg);
		bcopy(curmask, ss->ss_curmask, sizeof ss->ss_curmask);
	}

	/*
	 * ...and update the cursor
	 */
	smg_updatecursor(ss, wdc->which);

	return (0);
}

void
smg_updatecursor(struct smg_screen *ss, u_int which)
{
	u_int i;

	if (which & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
		ss->ss_cursor->xpos =
		    ss->ss_curpos.x - ss->ss_curhot.x + CUR_XBIAS;
		ss->ss_cursor->ypos =
		    ss->ss_curpos.y - ss->ss_curhot.y + CUR_YBIAS;
	}
	if (which & WSDISPLAY_CURSOR_DOSHAPE) {
		ss->ss_cursor->cmdr = ss->ss_curcmd | PCCCMD_LODSA;
		for (i = 0; i < PCC_CURSOR_SIZE; i++)
			ss->ss_cursor->load = ss->ss_curimg[i];
		for (i = 0; i < PCC_CURSOR_SIZE; i++)
			ss->ss_cursor->load = ss->ss_curmask[i];
		ss->ss_cursor->cmdr = ss->ss_curcmd;
	} else
	if (which & WSDISPLAY_CURSOR_DOCUR)
		ss->ss_cursor->cmdr = ss->ss_curcmd;
}

/*
 * Faster console operations
 */

#include <vax/vsa/maskbits.h>

void
smg_blockmove(struct rasops_info *ri, u_int sx, u_int y, u_int dx, u_int cx,
    u_int cy, int rop)
{
	int width;		/* add to get to same position in next line */

	unsigned int *psrcLine, *pdstLine;
				/* pointers to line with current src and dst */
	unsigned int *psrc;	/* pointer to current src longword */
	unsigned int *pdst;	/* pointer to current dst longword */

				/* following used for looping through a line */
	unsigned int startmask, endmask;  /* masks for writing ends of dst */
	int nlMiddle;		/* whole longwords in dst */
	int nl;			/* temp copy of nlMiddle */
	int xoffSrc;		/* offset (>= 0, < 32) from which to
				   fetch whole longwords fetched in src */
	int nstart;		/* number of ragged bits at start of dst */
	int nend;		/* number of ragged bits at end of dst */
	int srcStartOver;	/* pulling nstart bits from src
				   overflows into the next word? */

	width = SM_XWIDTH >> 5;

	/* start at first scanline */
	psrcLine = pdstLine = ((u_int *)ri->ri_bits) + (y * width);

	/* x direction doesn't matter for < 1 longword */
	if (cx <= 32) {
		int srcBit, dstBit;	/* bit offset of src and dst */

		pdstLine += (dx >> 5);
		psrcLine += (sx >> 5);
		psrc = psrcLine;
		pdst = pdstLine;

		srcBit = sx & 0x1f;
		dstBit = dx & 0x1f;

		while (cy--) {
			getandputrop(psrc, srcBit, dstBit, cx, pdst, rop);
			pdst += width;
			psrc += width;
		}
	} else {
		maskbits(dx, cx, startmask, endmask, nlMiddle);
		if (startmask)
			nstart = 32 - (dx & 0x1f);
		else
			nstart = 0;
		if (endmask)
			nend = (dx + cx) & 0x1f;
		else
			nend = 0;

		xoffSrc = ((sx & 0x1f) + nstart) & 0x1f;
		srcStartOver = ((sx & 0x1f) + nstart) > 31;

		if (sx >= dx) {	/* move left to right */
			pdstLine += (dx >> 5);
			psrcLine += (sx >> 5);

			while (cy--) {
				psrc = psrcLine;
				pdst = pdstLine;

				if (startmask) {
					getandputrop(psrc, (sx & 0x1f),
					    (dx & 0x1f), nstart, pdst, rop);
					pdst++;
					if (srcStartOver)
						psrc++;
				}

				/* special case for aligned operations */
				if (xoffSrc == 0) {
					nl = nlMiddle;
					while (nl--) {
						switch (rop) {
						case RR_CLEAR:
							*pdst = 0;
							break;
						case RR_SET:
							*pdst = ~0;
							break;
						default:
							*pdst = *psrc;
							break;
						}
						psrc++;
						pdst++;
					}
				} else {
					nl = nlMiddle + 1;
					while (--nl) {
						switch (rop) {
						case RR_CLEAR:
							*pdst = 0;
							break;
						case RR_SET:
							*pdst = ~0;
							break;
						default:
							getunalignedword(psrc,
							    xoffSrc, *pdst);
							break;
						}
						pdst++;
						psrc++;
					}
				}

				if (endmask) {
					getandputrop(psrc, xoffSrc, 0, nend,
					    pdst, rop);
				}

				pdstLine += width;
				psrcLine += width;
			}
		} else {	/* move right to left */
			pdstLine += ((dx + cx) >> 5);
			psrcLine += ((sx + cx) >> 5);
			/*
			 * If fetch of last partial bits from source crosses
			 * a longword boundary, start at the previous longword
			 */
			if (xoffSrc + nend >= 32)
				--psrcLine;

			while (cy--) {
				psrc = psrcLine;
				pdst = pdstLine;

				if (endmask) {
					getandputrop(psrc, xoffSrc, 0, nend,
					    pdst, rop);
				}

				nl = nlMiddle + 1;
				while (--nl) {
					--psrc;
					--pdst;
					switch (rop) {
					case RR_CLEAR:
						*pdst = 0;
						break;
					case RR_SET:
						*pdst = ~0;
						break;
					default:
						getunalignedword(psrc, xoffSrc,
						    *pdst);
						break;
					}
				}

				if (startmask) {
					if (srcStartOver)
						--psrc;
					--pdst;
					getandputrop(psrc, (sx & 0x1f),
					    (dx & 0x1f), nstart, pdst, rop);
				}

				pdstLine += width;
				psrcLine += width;
			}
		}
	}
}

int
smg_copycols(void *cookie, int row, int src, int dst, int n)
{
	struct rasops_info *ri = cookie;

	n *= ri->ri_font->fontwidth;
	src *= ri->ri_font->fontwidth;
	dst *= ri->ri_font->fontwidth;
	row *= ri->ri_font->fontheight;

	smg_blockmove(ri, src, row, dst, n, ri->ri_font->fontheight,
	    RR_COPY);

	return 0;
}

int
smg_erasecols(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri = cookie;
	int fg, bg;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	num *= ri->ri_font->fontwidth;
	col *= ri->ri_font->fontwidth;
	row *= ri->ri_font->fontheight;

	smg_blockmove(ri, col, row, col, num, ri->ri_font->fontheight,
	    bg == 0 ? RR_CLEAR : RR_SET);

	return 0;
}

/*
 * Console support code
 */

int	smgcnprobe(void);
int	smgcninit(void);

int
smgcnprobe()
{
	switch (vax_boardtype) {
	case VAX_BTYP_410:
	case VAX_BTYP_420:
	case VAX_BTYP_43:
		if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_MULTU)) != 0)
			break; /* doesn't use graphics console */

		if ((vax_confdata & KA420_CFG_VIDOPT) != 0)
			break;	/* there is a color option */

		return (1);

	default:
		break;
	}

	return (0);
}

/*
 * Called very early to setup the glass tty as console.
 * Because it's called before the VM system is initialized, virtual memory
 * for the framebuffer can be stolen directly without disturbing anything.
 */
int
smgcninit()
{
	struct smg_screen *ss = &smg_consscr;
	extern vaddr_t virtual_avail;
	vaddr_t ova;
	long defattr;
	struct rasops_info *ri;

	ova = virtual_avail;

	ss->ss_addr = (caddr_t)virtual_avail;
	ioaccess(virtual_avail, SMADDR, SMSIZE / VAX_NBPG);
	virtual_avail += SMSIZE;

	ss->ss_cursor = (struct dc503reg *)virtual_avail;
	ioaccess(virtual_avail, KA420_CUR_BASE, 1);
	virtual_avail += VAX_NBPG;

	virtual_avail = round_page(virtual_avail);

	/* this had better not fail */
	if (smg_setup_screen(ss) != 0) {
		iounaccess((vaddr_t)ss->ss_addr, SMSIZE / VAX_NBPG);
		iounaccess((vaddr_t)ss->ss_cursor, 1);
		virtual_avail = ova;
		return (1);
	}

	ri = &ss->ss_ri;
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&smg_stdscreen, ri, 0, 0, defattr);

	return (0);
}