/* $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;
}