root/sys/arch/sgimips/gio/grtwo.c
/* $NetBSD: grtwo.c,v 1.19 2023/12/13 20:53:14 andvar 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.19 2023/12/13 20:53:14 andvar Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kmem.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 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(device_t, cfdata_t, void *);
static void     grtwo_attach(device_t, device_t, void *);

CFATTACH_DECL_NEW(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)

#if 0
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,
};
#endif

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 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 = uimin(dc->yres - 1 - y1, dc->yres - 1 - y2);
        from_y = uimax(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(device_t parent, cfdata_t cf, 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 *)(intptr_t)(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, WSFONT_FIND_BITMAP);

        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(device_t parent, device_t self, void *aux)
{
        struct gio_attach_args *ga = aux;
        struct grtwo_softc *sc = device_private(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 = kmem_zalloc(sizeof(struct grtwo_devconfig),
                                   KM_SLEEP);

                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(self, &wa, wsemuldisplaydevprint, CFARGS_NONE);
}

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;
}