NetBSD-5.0.2/sys/arch/sgimips/gio/grtwo.c

Compare this file to the similar file:
Show the results in this format:

/* $NetBSD: grtwo.c,v 1.10 2007/03/04 06:00:39 christos Exp $	 */

/*
 * Copyright (c) 2004 Christopher SEKIYA
 * 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. 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.
 *
 * <<Id: LICENSE_GC,v 1.1 2001/10/01 23:24:05 cgd Exp>>
 */

/* wscons driver for SGI GR2 family of framebuffers
 * 
 * Heavily based on the newport wscons driver.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: grtwo.c,v 1.10 2007/03/04 06:00:39 christos Exp $");

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

#include <machine/sysconf.h>

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

#include <sgimips/gio/giovar.h>
#include <sgimips/gio/grtwovar.h>
#include <sgimips/gio/grtworeg.h>

#include <sgimips/dev/int2var.h>

struct grtwo_softc {
	struct device   sc_dev;

	struct grtwo_devconfig *sc_dc;
};

struct grtwo_devconfig {
	u_int32_t        dc_addr;

	bus_space_tag_t iot;
	bus_space_handle_t ioh;

	u_int8_t	boardrev;
	u_int8_t	backendrev;
	int             hq2rev;
	int             ge7rev;
	int             vc1rev;
	int             zbuffer;
	int             cmaprev;
	int             xmaprev;
	int             rexrev;
	int             xres;
	int             yres;
	int             depth;
	int             monitor;

	int             dc_font;
	struct wsdisplay_font *dc_fontdata;
};

static int      grtwo_match(struct device *, struct cfdata *, void *);
static void     grtwo_attach(struct device *, struct device *, void *);

CFATTACH_DECL(grtwo, sizeof(struct grtwo_softc),
	      grtwo_match, grtwo_attach, NULL, NULL);

/* textops */
static void     grtwo_cursor(void *, int, int, int);
static int      grtwo_mapchar(void *, int, unsigned int *);
static void     grtwo_putchar(void *, int, int, u_int, long);
static void     grtwo_copycols(void *, int, int, int, int);
static void     grtwo_erasecols(void *, int, int, int, long);
static void     grtwo_copyrows(void *, int, int, int);
static void     grtwo_eraserows(void *, int, int, long);
static int      grtwo_allocattr(void *, int, int, int, long *);

/* accessops */
static int      grtwo_ioctl(void *, void *, u_long, void *, int, struct lwp *);
static paddr_t  grtwo_mmap(void *, void *, off_t, int);
static int
grtwo_alloc_screen(void *, const struct wsscreen_descr *,
		   void **, int *, int *, long *);
static void     grtwo_free_screen(void *, void *);
static int
                grtwo_show_screen(void *, void *, int, void (*) (void *, int, int), void *);

static int	grtwo_intr0(void *);
static int	grtwo_intr6(void *);

static const struct wsdisplay_emulops grtwo_textops = {
	.cursor = grtwo_cursor,
	.mapchar = grtwo_mapchar,
	.putchar = grtwo_putchar,
	.copycols = grtwo_copycols,
	.erasecols = grtwo_erasecols,
	.copyrows = grtwo_copyrows,
	.eraserows = grtwo_eraserows,
	.allocattr = grtwo_allocattr
};

static const struct wsdisplay_accessops grtwo_accessops = {
	.ioctl = grtwo_ioctl,
	.mmap = grtwo_mmap,
	.alloc_screen = grtwo_alloc_screen,
	.free_screen = grtwo_free_screen,
	.show_screen = grtwo_show_screen,
};

static const struct wsscreen_descr grtwo_screen = {
	.name = "1280x1024",
	.ncols = 160,
	.nrows = 64, /* 40 */
	.textops = &grtwo_textops,
	.fontwidth = 8,
	.fontheight = 16,
	.capabilities = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_REVERSE
};

static const struct wsscreen_descr *_grtwo_screenlist[] = {
	&grtwo_screen
};

static const struct wsscreen_list grtwo_screenlist = {
	sizeof(_grtwo_screenlist) / sizeof(struct wsscreen_descr *),
	_grtwo_screenlist
};

static struct grtwo_devconfig grtwo_console_dc;
static int      grtwo_is_console = 0;

#define GR2_ATTR_ENCODE(fg,bg)	(((fg) << 8) | (bg))
#define GR2_ATTR_BG(a)		((a) & 0xff)
#define GR2_ATTR_FG(a)		(((a) >> 8) & 0xff)

static const u_int16_t grtwo_cursor_data[128] = {
	/* Bit 0 */
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0xff00, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,

	/* Bit 1 */
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
	0x0000, 0x0000,
};

static const u_int8_t grtwo_defcmap[8 * 3] = {
	/* Normal colors */
	0x00, 0x00, 0x00,	/* black */
	0x7f, 0x00, 0x00,	/* red */
	0x00, 0x7f, 0x00,	/* green */
	0x7f, 0x7f, 0x00,	/* brown */
	0x00, 0x00, 0x7f,	/* blue */
	0x7f, 0x00, 0x7f,	/* magenta */
	0x00, 0x7f, 0x7f,	/* cyan */
	0xc7, 0xc7, 0xc7,	/* white - XXX too dim? */
};

static void 
grtwo_wait_gfifo(struct grtwo_devconfig * dc)
{
	int2_wait_fifo(1);
}

static inline void
grtwo_set_color(bus_space_tag_t iot, bus_space_handle_t ioh, int color)
{
	bus_space_write_4(iot, ioh, GR2_FIFO_COLOR, color);
}

/* Helper functions */
static void
grtwo_fill_rectangle(struct grtwo_devconfig * dc, int x1, int y1, int x2,
		     int y2, u_int8_t color)
{
	int remaining;
	int from_y;
	int to_y;

	/* gr2 sees coordinate 0,0 as the lower left corner, and 1279,1023
	   as the upper right.  To keep things consistent, we shall flip the
	   y axis. */

	/* There appears to be a limit to the number of vertical lines that we
	   can run through the the graphics engine at one go.  This probably has
	   something to do with vertical refresh.  Single-row fills are okay,
	   multiple-row screw up the board in exciting ways.  The copy_rectangle
	   workaround doesn't work for fills. */

	/* Coordinates, not length.  Remember that! */

	to_y = min(dc->yres - 1 - y1, dc->yres - 1 - y2);
	from_y = max(dc->yres - 1 - y1, dc->yres - 1 - y2);

	remaining = to_y - from_y;

	grtwo_wait_gfifo(dc);
	grtwo_set_color(dc->iot, dc->ioh, color);

	while (remaining) {
		if (remaining <= 32)
		{
			delay(10000);
			grtwo_wait_gfifo(dc);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_RECTI2D, x1);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, from_y);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x2);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, from_y + remaining);
			break;
		} else {
			delay(100000);
			grtwo_wait_gfifo(dc);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_RECTI2D, x1);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, from_y);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x2);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, from_y + remaining);
			from_y += 32;
			remaining -=32;
		}
	}
}

static void
grtwo_copy_rectangle(struct grtwo_devconfig * dc, int x1, int y1, int x2,
		     int y2, int width, int height)
{
	int             length = (width + 3) >> 2;
	int             lines = 4864 / length;
	int             from_y;
	int             to_y;
	int		temp_height;

	if ((y2 <= y1) || (height < lines)) {
		grtwo_wait_gfifo(dc);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_RECTCOPY, length);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, lines);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x1);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, y1);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, width);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, height);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x2);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, y2);
	} else {
		from_y = y1 + height - lines;
		to_y = y2 + height - lines;
		temp_height = MIN(height, lines);

		while (temp_height) {
			grtwo_wait_gfifo(dc);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_RECTCOPY, length);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, lines);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x1);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, from_y);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, width);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, temp_height);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, x2);
			bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, to_y);
			height -= temp_height;
			height = MIN(height, lines);
			from_y -= temp_height;
			to_y -= temp_height;
		}
	}
}

static void
grtwo_cmap_setrgb(struct grtwo_devconfig * dc, int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	grtwo_wait_gfifo(dc);
	bus_space_write_1(dc->iot, dc->ioh, XMAPALL_ADDRHI,
			  ((index & 0x1f00) >> 8) );
	bus_space_write_1(dc->iot, dc->ioh, XMAPALL_ADDRLO,
			  (index & 0xff));
	bus_space_write_1(dc->iot, dc->ioh, XMAPALL_CLUT, r);
	bus_space_write_1(dc->iot, dc->ioh, XMAPALL_CLUT, g);
	bus_space_write_1(dc->iot, dc->ioh, XMAPALL_CLUT, b);
}

static void
grtwo_setup_hw(struct grtwo_devconfig * dc)
{
	int             i = 0;

	/* Get various revisions */
	dc->boardrev = (~(bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD0))) & GR2_REVISION_RD0_VERSION_MASK;

	/*
	 * boards prior to rev 4 have a pretty whacky config scheme.
         * what is doubly weird is that i have a rev 2 board, but the rev 4
	 * probe routines work just fine.
	 * we'll trust SGI, though, and separate things a bit.  it's only
	 * critical for the display depth calculation.
	 */

	if (dc->boardrev < 4) {
		dc->backendrev = ~(bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD2) & GR2_REVISION_RD2_BACKEND_REV) >> 2;
		if (dc->backendrev == 0)
			return;
		dc->zbuffer = !(bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD1) & GR2_REVISION_RD1_ZBUFFER);
		if ( (bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD3) & GR2_REVISION_RD3_VMA) != 3)
		  i++;
		if ( (bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD3) & GR2_REVISION_RD3_VMB) != 0x0c)
		  i++;
		if ( (bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD3) & GR2_REVISION_RD3_VMC) != 0x30)
		  i++;
		dc->depth = 8 * i;
		dc->monitor =
			((bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD2) & 0x03) << 1) |
			(bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD1) & 0x01);
	} else {
		dc->backendrev = ~(bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD1)) & 0x03;
		if (dc->backendrev == 0)
			return;
		dc->zbuffer = bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD1) & GR2_REVISION4_RD1_ZBUFFER;
		dc->depth = ((bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD1) & GR2_REVISION4_RD1_24BPP) >> 4) ? 24 : 8;
		dc->monitor = (bus_space_read_4(dc->iot, dc->ioh, GR2_REVISION_RD0) & GR2_REVISION4_RD0_MONITOR_MASK) >> 4;
	}

	dc->hq2rev = (bus_space_read_4(dc->iot, dc->ioh, HQ2_VERSION) & HQ2_VERSION_MASK) >> HQ2_VERSION_SHIFT;
	dc->ge7rev = (bus_space_read_4(dc->iot, dc->ioh, GE7_REVISION) & GE7_REVISION_MASK) >> 5;
	/* dc->vc1rev = vc1_read_ireg(dc, 5) & 0x07; */

	/* gr2 supports 1280x1024 only */
	dc->xres = 1280;
	dc->yres = 1024;

#if 0
	/* Setup cursor glyph */

	bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRHI,
		(VC1_SRAM_CURSOR0_BASE >> 8) & 0xff);
	bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRLO,
		VC1_SRAM_CURSOR0_BASE & 0xff);
	for (i = 0; i < 128; i++)
		bus_space_write_4(dc->iot, dc->ioh, VC1_SRAM, grtwo_cursor_data[i]);

	bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRHI,
		(VC1_CURSOR_EP >> 8) & 0xff);
	bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRLO,
		VC1_CURSOR_EP & 0xff);
	bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND, VC1_SRAM_CURSOR0_BASE);
	bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND, 0);
	bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND, 0);
	bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND, 0);

	/* Turn on cursor function, display, DID */
	bus_space_write_4(dc->iot, dc->ioh, VC1_SYSCTL,
		VC1_SYSCTL_VC1 | VC1_SYSCTL_DID |
		VC1_SYSCTL_CURSOR | VC1_SYSCTL_CURSOR_DISPLAY);
#endif
	
	/* Setup CMAP */
	for (i = 0; i < 8; i++)
		grtwo_cmap_setrgb(dc, i, grtwo_defcmap[i * 3],
			grtwo_defcmap[i * 3 + 1], grtwo_defcmap[i * 3 + 2]);
}

/* Attach routines */
static int
grtwo_match(struct device * parent, struct cfdata * self, void *aux)
{
	struct gio_attach_args *ga = aux;

	if (ga->ga_addr != 0x1f000000 && ga->ga_addr != 0x1f400000 &&
	    ga->ga_addr != 0x1f600000)
		return (0);

	/*
	 * grtwo doesn't have anything that even vaguely resembles a product
	 * ID.  Instead, we determine presence by looking at the HQ2 "mystery"
	 * register, which contains a magic number.
	 */
	if ( platform.badaddr((void *) (ga->ga_ioh + HQ2_MYSTERY),
	    sizeof(u_int32_t)) )
		return 0;

	if ( (bus_space_read_4(ga->ga_iot, ga->ga_ioh, HQ2_MYSTERY)) != 0xdeadbeef)
		return 0;

	return 1;
}

static void
grtwo_attach_common(struct grtwo_devconfig * dc, struct gio_attach_args * ga)
{
	dc->dc_addr = ga->ga_addr;

	dc->iot = ga->ga_iot;
	dc->ioh = ga->ga_ioh;
	int i = 0;

	wsfont_init();

	dc->dc_font = wsfont_find(NULL, 8, 16, 0, WSDISPLAY_FONTORDER_L2R,
				  WSDISPLAY_FONTORDER_L2R);

	if (dc->dc_font < 0)
		panic("grtwo_attach_common: no suitable fonts");

	if (wsfont_lock(dc->dc_font, &dc->dc_fontdata))
		panic("grtwo_attach_common: unable to lock font data");

	grtwo_setup_hw(dc);

	/* Large fills are broken.  For now, clear the screen line-by-line. */
	for (i = 0; i < 64; i++)
		grtwo_eraserows(dc, i, 1, 0);

	/* If large fills worked, we'd do this instead:
	grtwo_fill_rectangle(dc, 0, 0, dc->xres - 1, dc->yres - 1, 0);
	*/
}

static void
grtwo_attach(struct device * parent, struct device * self, void *aux)
{
	struct gio_attach_args *ga = aux;
	struct grtwo_softc *sc = (void *) self;
	struct wsemuldisplaydev_attach_args wa;

	if (grtwo_is_console && ga->ga_addr == grtwo_console_dc.dc_addr) {
		wa.console = 1;
		sc->sc_dc = &grtwo_console_dc;
	} else {
		wa.console = 0;
		sc->sc_dc = malloc(sizeof(struct grtwo_devconfig),
				   M_DEVBUF, M_WAITOK | M_ZERO);
		if (sc->sc_dc == NULL)
			panic("grtwo_attach: out of memory");

		grtwo_attach_common(sc->sc_dc, ga);
	}

	aprint_naive(": Display adapter\n");

	aprint_normal(": GR2 (board rev %x, monitor %d, depth %d)\n",
	      sc->sc_dc->boardrev, sc->sc_dc->monitor, sc->sc_dc->depth);

	wa.scrdata = &grtwo_screenlist;
	wa.accessops = &grtwo_accessops;
	wa.accesscookie = sc->sc_dc;

        if ((cpu_intr_establish(0, IPL_TTY, grtwo_intr0, sc)) == NULL)
                printf(": unable to establish interrupt!\n");

        if ((cpu_intr_establish(6, IPL_TTY, grtwo_intr6, sc)) == NULL)
                printf(": unable to establish interrupt!\n");

	config_found(&sc->sc_dev, &wa, wsemuldisplaydevprint);
}

int
grtwo_cnattach(struct gio_attach_args * ga)
{
	long            defattr = GR2_ATTR_ENCODE(WSCOL_WHITE, WSCOL_BLACK);

	if (!grtwo_match(NULL, NULL, ga)) {
		return ENXIO;
	}

	grtwo_attach_common(&grtwo_console_dc, ga);
	wsdisplay_cnattach(&grtwo_screen, &grtwo_console_dc, 0, 0, defattr);

	grtwo_is_console = 1;

	return 0;
}

/* wsdisplay textops */
static void
grtwo_cursor(void *c, int on, int row, int col)
{
	struct grtwo_devconfig *dc = (void *) c;
	u_int32_t control;
	control = bus_space_read_4(dc->iot, dc->ioh, VC1_SYSCTL);

	if (!on) {
		bus_space_write_4(dc->iot, dc->ioh, VC1_SYSCTL,
			control & ~VC1_SYSCTL_CURSOR_DISPLAY);
	} else {
		bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRHI, (VC1_CURSOR_XL & 0xff00) >> 8
			);
		bus_space_write_4(dc->iot, dc->ioh, VC1_ADDRLO, VC1_CURSOR_XL & 0xff);
		bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND,
				  col * dc->dc_fontdata->fontwidth);
		bus_space_write_4(dc->iot, dc->ioh, VC1_COMMAND,
				  row * dc->dc_fontdata->fontheight);
		bus_space_write_4(dc->iot, dc->ioh, VC1_SYSCTL,
			control | VC1_SYSCTL_CURSOR_DISPLAY);
	}
}

static int
grtwo_mapchar(void *c, int ch, unsigned int *cp)
{
	struct grtwo_devconfig *dc = (void *) c;

	if (dc->dc_fontdata->encoding != WSDISPLAY_FONTENC_ISO) {
		ch = wsfont_map_unichar(dc->dc_fontdata, ch);

		if (ch < 0)
			goto fail;
	}
	if (ch < dc->dc_fontdata->firstchar ||
	    ch >= dc->dc_fontdata->firstchar + dc->dc_fontdata->numchars)
		goto fail;

	*cp = ch;
	return 5;

fail:
	*cp = ' ';
	return 0;
}

static void
grtwo_putchar(void *c, int row, int col, u_int ch, long attr)
{
	struct grtwo_devconfig *dc = (void *) c;
	struct wsdisplay_font *font = dc->dc_fontdata;
	u_int8_t        *bitmap = (u_int8_t *) font->data + (ch - font->firstchar + 1) * font->fontheight * font->stride;
	u_int32_t        pattern;
	int             i;
	int             x = col * font->fontwidth;
	int             y = dc->yres - ( (row + 1) * font->fontheight);

	/* Set the drawing color */
	grtwo_wait_gfifo(dc);
	grtwo_set_color(dc->iot, dc->ioh, (((attr) >> 8) & 0xff));
	grtwo_wait_gfifo(dc);

	/* Set drawing coordinates */
	grtwo_wait_gfifo(dc);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_CMOV2I, x);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, y);

	/* This works for font sizes < 18 */
	grtwo_wait_gfifo(dc);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DRAWCHAR, font->fontwidth);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, font->fontheight);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 2);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 0); /* x offset */
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 0); /* y offset */
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 0);
	bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 0);

	for (i = 0; i < font->fontheight; i++) {
		/* It appears that writes have to be 16 bits.  An "I tell you
		   two times" sort of thing?  Thanks, SGI */
		pattern = *bitmap | (*bitmap << 8);
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, pattern);
		bitmap -= font->stride;
	}

	/* pad up to 18 */
	for (i = font->fontheight; i < 18; i++)
		bus_space_write_4(dc->iot, dc->ioh, GR2_FIFO_DATA, 0x0000);
}

static void
grtwo_copycols(void *c, int row, int srccol, int dstcol, int ncols)
{
#if 1
	printf("grtwo_copycols: %i %i %i %i\n", row, srccol, dstcol, ncols);
#else
	struct grtwo_devconfig *dc = (void *) c;
	struct wsdisplay_font *font = dc->dc_fontdata;
	grtwo_copy_rectangle(dc,
			     srccol * font->fontwidth,	/* x1 */
			     0,	/* y1 */
			     dstcol * font->fontwidth,	/* x2 */
			     0,	/* y2 */
			     ncols * font->fontwidth,	/* dx */
			     dc->yres );	/* dy */
#endif
}

static void
grtwo_erasecols(void *c, int row, int startcol, int ncols, long attr)
{
	struct grtwo_devconfig *dc = (void *) c;
	struct wsdisplay_font *font = dc->dc_fontdata;

	grtwo_fill_rectangle(dc,
			     startcol * font->fontwidth,	/* x1 */
			     0,	/* y1 */
			     (startcol * font->fontwidth) + ncols * font->fontwidth,	/* x2 */
			     dc->yres,	/* y2 */
			     GR2_ATTR_BG(attr));
}

static void
grtwo_copyrows(void *c, int srcrow, int dstrow, int nrows)
{
	struct grtwo_devconfig *dc = (void *) c;
	struct wsdisplay_font *font = dc->dc_fontdata;

	grtwo_copy_rectangle(dc,
			     0,	/* x1 */
			     srcrow * font->fontheight,	/* y1 */
			     0, /* x2 */
			     dstrow * font->fontheight,	/* y2 */
			     dc->xres,	/* dx */
			     nrows * font->fontheight);
}

static void
grtwo_eraserows(void *c, int startrow, int nrows, long attr)
{
	struct grtwo_devconfig *dc = (void *) c;
	struct wsdisplay_font *font = dc->dc_fontdata;
	grtwo_fill_rectangle(dc,
			     0,	/* x1 */
			     startrow * font->fontheight,	/* y1 */
			     dc->xres,	/* x2 */
			     (startrow * font->fontheight) + nrows * font->fontheight,	/* y2 */
			     GR2_ATTR_BG(attr));
}

static int
grtwo_allocattr(void *c, int fg, int bg, int flags, long *attr)
{
	if (flags & WSATTR_BLINK)
		return EINVAL;

	if ((flags & WSATTR_WSCOLORS) == 0) {
		fg = WSCOL_WHITE;
		bg = WSCOL_BLACK;
	}
	if (flags & WSATTR_HILIT)
		fg += 8;

	if (flags & WSATTR_REVERSE) {
		int             tmp = fg;
		fg = bg;
		bg = tmp;
	}
	*attr = GR2_ATTR_ENCODE(fg, bg);

	return 0;
}

/* wsdisplay accessops */

static int
grtwo_ioctl(void *c, void *vs, u_long cmd, void *data, int flag,
	struct lwp *l)
{
	struct grtwo_softc *sc = c;

#define FBINFO (*(struct wsdisplay_fbinfo*)data)

	switch (cmd) {
	case WSDISPLAYIO_GINFO:
		FBINFO.width = sc->sc_dc->xres;
		FBINFO.height = sc->sc_dc->yres;
		FBINFO.depth = sc->sc_dc->depth;
		FBINFO.cmsize = 1 << FBINFO.depth;
		return 0;
	case WSDISPLAYIO_GTYPE:
		*(u_int *) data = WSDISPLAY_TYPE_GR2;
		return 0;
	}
	return EPASSTHROUGH;
}

static          paddr_t
grtwo_mmap(void *c, void *vs, off_t offset, int prot)
{
	struct grtwo_devconfig *dc = c;

	if (offset >= 0xfffff)
		return -1;

	return mips_btop(dc->dc_addr + offset);
}

static int
grtwo_alloc_screen(void *c, const struct wsscreen_descr * type, void **cookiep,
		   int *cursxp, int *cursyp, long *attrp)
{
	/*
	 * This won't get called for console screen and we don't support
	 * virtual screens
	 */

	return ENOMEM;
}

static void
grtwo_free_screen(void *c, void *cookie)
{
	panic("grtwo_free_screen");
}
static int
grtwo_show_screen(void *c, void *cookie, int waitok,
		  void (*cb) (void *, int, int), void *cbarg)
{
	return 0;
}

static int
grtwo_intr0(void *arg)
{
	/* struct grtwo_devconfig *dc = arg; */
	return 1;
}
	

static int
grtwo_intr6(void *arg)
{
	/* struct grtwo_devconfig *dc = arg; */
	return 1;
}