root/sys/arch/vax/vsa/gpx.c
/*      $NetBSD: gpx.c,v 1.4 2024/09/30 00:34:04 rin Exp $ */
/*      $OpenBSD: gpx.c,v 1.25 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) 1988 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.
 *
 *      @(#)qd.c        7.1 (Berkeley) 6/28/91
 */

/************************************************************************
*                                                                       *
*                       Copyright (c) 1985-1988 by                      *
*               Digital Equipment Corporation, Maynard, MA              *
*                       All rights reserved.                            *
*                                                                       *
*   This software is furnished under a license and may be used and      *
*   copied  only  in accordance with the terms of such license and      *
*   with the  inclusion  of  the  above  copyright  notice.   This      *
*   software  or  any  other copies thereof may not be provided or      *
*   otherwise made available to any other person.  No title to and      *
*   ownership of the software is hereby transferred.                    *
*                                                                       *
*   The information in this software is subject to change  without      *
*   notice  and should not be construed as a commitment by Digital      *
*   Equipment Corporation.                                              *
*                                                                       *
*   Digital assumes no responsibility for the use  or  reliability      *
*   of its software on equipment which is not supplied by Digital.      *
*                                                                       *
*************************************************************************/

/*
 * Driver for the GPX color option on VAXstation 3100, based on the
 * MicroVAX II qdss driver.
 *
 * The frame buffer memory itself is not directly accessible (unlike
 * the on-board monochrome smg frame buffer), and writes through the
 * Dragon chip can only happen in multiples of 16 pixels, horizontally.
 *
 * Because of this limitation, the font image is copied to offscreen
 * memory (which there is plenty of), and screen to screen blt operations
 * are done for everything.
 */

#include "dzkbd.h"
#include "wsdisplay.h"

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

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

#include <dev/cons.h>

#include <dev/dec/dzreg.h>
#include <dev/dec/dzvar.h>
#include <dev/dec/dzkbdvar.h>

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

#if 0
#include <dev/ic/bt458reg.h>
#include <dev/ic/dc503reg.h>
#endif

#define GPXADDR         0x3c000000      /* base address on VAXstation 3100 */

#define GPX_ADDER_OFFSET        0x0000
#define GPX_VDAC_OFFSET         0x0300
#define GPX_CURSOR_OFFSET       0x0400  /* DC503 */
#define GPX_READBACK_OFFSET     0x0500

#define GPX_WIDTH       1024
#define GPX_VISHEIGHT   864
#define GPX_HEIGHT      2048

/* XXX these should be in <dev/ic/bt458reg.h> */
/*
 * Brooktree Bt451, Bt457, Bt458 register definitions
 */
#define BT_OV0  0x00            /* overlay 0 */
#define BT_OV1  0x01            /* overlay 1 */
#define BT_OV2  0x02            /* overlay 2 */
#define BT_OV3  0x03            /* overlay 3 */
#define BT_RMR  0x04            /* read mask */
#define BT_BMR  0x05            /* blink mask */
#define BT_CR   0x06            /* control */
#define BT_CTR  0x07            /* control/test */

#define BTCR_MPLX_5             0x80    /* multiplex select, 5:1 */
#define BTCR_MPLX_4             0x00    /* multiplex select, 4:1 */
#define BTCR_RAMENA             0x40    /* use color palette RAM */
#define BTCR_BLINK_M            0x30    /* blink mask */
#define BTCR_BLINK_1648         0x00    /*  16 on, 48 off */
#define BTCR_BLINK_1616         0x10    /*  16 on, 16 off */
#define BTCR_BLINK_3232         0x20    /*  32 on, 32 off */
#define BTCR_BLINK_6464         0x30    /*  64 on, 64 off */
#define BTCR_BLINKENA_OV1       0x08    /* OV1 blink enable */
#define BTCR_BLINKENA_OV0       0x04    /* OV0 blink enable */
#define BTCR_DISPENA_OV1        0x02    /* OV1 display enable */
#define BTCR_DISPENA_OV0        0x01    /* OV0 display enable */

#define BTCTR_R_ENA             0x01    /* red channel enable */
#define BTCTR_G_ENA             0x02    /* green channel enable */
#define BTCTR_B_ENA             0x04    /* blue channel enable */
#define BTCTR_NIB_M             0x08    /* nibble mask: */
#define BTCTR_NIB_LOW           0x08    /*  low */
#define BTCTR_NIB_HIGH          0x00    /*  high */

/* 4 plane option RAMDAC */
struct  ramdac4 {
        uint16_t        colormap[16];
        uint8_t         unknown[0x20];
        uint16_t        cursormap[4];
        uint8_t         unknown2[0x18];
        uint16_t        control;
#define RAMDAC4_INIT    0x0047
#define RAMDAC4_ENABLE  0x0002
};

/* 8 plane option RAMDAC - Bt458 or compatible */
struct  ramdac8 {
        uint16_t        address;
        uint16_t        cmapdata;
        uint16_t        control;
        uint16_t        omapdata;
};

struct  gpx_screen {
        struct rasops_info ss_ri;
        int             ss_console;
        u_int           ss_depth;
        u_int           ss_gpr;         /* font glyphs per row */
        struct adder    *ss_adder;
        void            *ss_vdac;
        uint8_t         ss_cmap[256 * 3];
#if 0
        struct dc503reg *ss_cursor;
        uint16_t        ss_curcmd;
#endif
};

struct  gpx_softc {
        device_t sc_dev;
        struct gpx_screen *sc_scr;
        int     sc_nscreens;
};

static int gpx_match(device_t, cfdata_t, void *);
static void gpx_attach(device_t, device_t, void *);

static int gpx_ioctl(void *, void *, u_long, void *, int, struct lwp *);
static paddr_t gpx_mmap(void *, void *, off_t, int);
static int gpx_alloc_screen(void *, const struct wsscreen_descr *,
    void **, int *, int *, long *);
static void gpx_free_screen(void *, void *);
static int gpx_show_screen(void *, void *, int,
    void (*) (void *, int, int), void *);

static void gpx_putchar(void *, int, int, u_int, long);
static void gpx_copycols(void *, int, int, int, int);
static void gpx_erasecols(void *, int, int, int, long);
static void gpx_copyrows(void *, int, int, int);
static void gpx_eraserows(void *, int, int, long);
static void gpx_do_cursor(struct rasops_info *);

static int gpx_wait(struct gpx_screen *, int);
static int gpx_viper_write(struct gpx_screen *, u_int, uint16_t);
static void gpx_reset_viper(struct gpx_screen *);
static void gpx_clear_screen(struct gpx_screen *);
static int gpx_setup_screen(struct gpx_screen *);
static void gpx_upload_font(struct gpx_screen *);
static void gpx_copyrect(struct gpx_screen *, int, int, int, int, int, int);
static void gpx_fillrect(struct gpx_screen *, int, int, int, int, long, u_int);
static int gpx_getcmap(struct gpx_screen *, struct wsdisplay_cmap *);
static int gpx_putcmap(struct gpx_screen *, struct wsdisplay_cmap *);
static void gpx_loadcmap(struct gpx_screen *, int, int);
static void gpx_resetcmap(struct gpx_screen *);

/* for console */
static struct gpx_screen gpx_consscr;

CFATTACH_DECL_NEW(gpx, sizeof(struct gpx_softc),
    gpx_match, gpx_attach, NULL, NULL);

static struct wsscreen_descr gpx_stdscreen = {
        "std",
};

static const struct wsscreen_descr *_gpx_scrlist[] = {
        &gpx_stdscreen,
};

static const struct wsscreen_list gpx_screenlist = {
        sizeof(_gpx_scrlist) / sizeof(struct wsscreen_descr *),
        _gpx_scrlist,
};

static const struct wsdisplay_accessops gpx_accessops = {
        .ioctl = gpx_ioctl,
        .mmap = gpx_mmap,
        .alloc_screen = gpx_alloc_screen,
        .free_screen = gpx_free_screen,
        .show_screen = gpx_show_screen,
        .load_font = NULL
};

/*
 * Autoconf glue
 */

static int
gpx_match(device_t parent, cfdata_t match, void *aux)
{
        struct vsbus_attach_args *va = aux;
        volatile struct adder *adder;
        vaddr_t tmp;
        u_int depth;
        u_short status;

        switch (vax_boardtype) {
        default:
                return 0;

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

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

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

        /* Check for hardware */
        adder = (volatile struct adder *)
            vax_map_physmem(va->va_paddr + 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;

        /* Check for a recognized color depth */
        tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
        if (tmp == 0L)
                return 0;
        depth = (*(volatile uint16_t *)tmp) & 0x00f0;
        vax_unmap_physmem(tmp, 1);
        if (depth != 0x00f0 && depth != 0x0080)
                return 0;

        /* when already running as console, always fake things */
        if ((vax_confdata & KA420_CFG_L3CON) == 0
#if NWSDISPLAY > 0
            && cn_tab->cn_putc == wsdisplay_cnputc
#endif
        ) {
                struct vsbus_softc *sc = device_private(parent);
                sc->sc_mask = 0x08;
                scb_fake(0x44, 0x15);
        } else {
                adder = (struct adder *)vax_map_physmem(va->va_paddr +
                    GPX_ADDER_OFFSET, 1);
                if (adder == NULL)
                        return 0;
                adder->interrupt_enable = FRAME_SYNC;
                DELAY(100000);  /* enough to get a retrace interrupt */
                adder->interrupt_enable = 0;
                vax_unmap_physmem((vaddr_t)adder, 1);
        }
        return 20;
}

static void
gpx_attach(device_t parent, device_t self, void *aux)
{
        struct gpx_softc *sc = device_private(self);
        struct vsbus_attach_args *va = aux;
        struct gpx_screen *scr;
        struct wsemuldisplaydev_attach_args aa;
        int console;
        vaddr_t tmp;

        sc->sc_dev = self;
        console =
#if NWSDISPLAY > 0
            (vax_confdata & KA420_CFG_L3CON) == 0 &&
            cn_tab->cn_putc == wsdisplay_cnputc;
#else
            (vax_confdata & KA420_CFG_L3CON) == 0;
#endif
        if (console) {
                scr = &gpx_consscr;
                sc->sc_nscreens = 1;
        } else {
                scr = kmem_zalloc(sizeof(*scr), KM_SLEEP);

                tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
                if (tmp == 0L) {
                        printf(": can not probe depth\n");
                        goto bad1;
                }
                scr->ss_depth = (*(uint16_t *)tmp & 0x00f0) == 0x00f0 ? 4 : 8;
                vax_unmap_physmem(tmp, 1);

                scr->ss_adder = (struct adder *)vax_map_physmem(va->va_paddr +
                    GPX_ADDER_OFFSET, 1);
                if (scr->ss_adder == NULL) {
                        aprint_error(": can not map frame buffer registers\n");
                        goto bad1;
                }

                scr->ss_vdac = (void *)vax_map_physmem(va->va_paddr +
                    GPX_VDAC_OFFSET, 1);
                if (scr->ss_vdac == NULL) {
                        aprint_error(": can not map RAMDAC\n");
                        goto bad2;
                }

#if 0
                scr->ss_cursor =
                    (struct dc503reg *)vax_map_physmem(va->va_paddr +
                    GPX_CURSOR_OFFSET, 1);
                if (scr->ss_cursor == NULL) {
                        aprint_error(": can not map cursor chip\n");
                        goto bad3;
                }
#endif

                if (gpx_setup_screen(scr) != 0) {
                        aprint_error(": initialization failed\n");
                        goto bad4;
                }
        }
        sc->sc_scr = scr;

        aprint_normal("\n");
        aprint_normal_dev(self, "%dx%d %d plane color framebuffer\n",
            GPX_WIDTH, GPX_VISHEIGHT, scr->ss_depth);

        aa.console = console;
        aa.scrdata = &gpx_screenlist;
        aa.accessops = &gpx_accessops;
        aa.accesscookie = sc;

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

        return;

 bad4:
#if 0
        vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1);
 bad3:
#endif
        vax_unmap_physmem((vaddr_t)scr->ss_vdac, 1);
 bad2:
        vax_unmap_physmem((vaddr_t)scr->ss_adder, 1);
 bad1:
        kmem_free(scr, sizeof(*scr));
}

/*
 * wsdisplay accessops
 */

static int
gpx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
{
        struct gpx_softc *sc = v;
        struct gpx_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_GPX;
                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_depth;
                wdf->cmsize = 1 << ss->ss_depth;
                break;

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

        case WSDISPLAYIO_GVIDEO:
        case WSDISPLAYIO_SVIDEO:
                break;

        case WSDISPLAYIO_LINEBYTES:     /* no linear mapping */
                return -1;

        default:
                return EPASSTHROUGH;
        }
        return 0;
}

static paddr_t
gpx_mmap(void *v, void *vs, off_t offset, int prot)
{

        return -1;
}

static int
gpx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    int *curxp, int *curyp, long *defattrp)
{
        struct gpx_softc *sc = v;
        struct gpx_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.allocattr(ri, 0, 0, 0, defattrp);
        sc->sc_nscreens++;

        return 0;
}

static void
gpx_free_screen(void *v, void *cookie)
{
        struct gpx_softc *sc = v;

        sc->sc_nscreens--;
}

static int
gpx_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{

        return 0;
}

/*
 * wsdisplay emulops
 */

static void
gpx_putchar(void *v, int row, int col, u_int uc, long attr)
{
        struct rasops_info *ri = v;
        struct gpx_screen *ss = ri->ri_hw;
        struct wsdisplay_font *font = ri->ri_font;
        int dx, dy, sx, sy, fg, bg, ul;

        rasops_unpack_attr(attr, &fg, &bg, &ul);

        /* find where to output the glyph... */
        dx = col * font->fontwidth + ri->ri_xorigin;
        dy = row * font->fontheight + ri->ri_yorigin;
        /* ... and where to pick it from */
        uc -= font->firstchar;
        sx = (uc % ss->ss_gpr) * font->stride * NBBY;
        sy = GPX_HEIGHT - (1 + uc / ss->ss_gpr) * font->fontheight;

        /* setup VIPER operand control registers */
        while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
                continue;
        gpx_viper_write(ss, SRC1_OCR_B,
            EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
        gpx_viper_write(ss, DST_OCR_B,
            EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
        gpx_viper_write(ss, MASK_1, 0xffff);
        gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
        gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
        ss->ss_adder->x_clip_min = 0;
        ss->ss_adder->x_clip_max = GPX_WIDTH;
        ss->ss_adder->y_clip_min = 0;
        ss->ss_adder->y_clip_max = GPX_VISHEIGHT;
        /* load DESTINATION origin and vectors */
        ss->ss_adder->fast_dest_dy = 0;
        ss->ss_adder->slow_dest_dx = 0;
        ss->ss_adder->error_1 = 0;
        ss->ss_adder->error_2 = 0;
        ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
        gpx_wait(ss, RASTEROP_COMPLETE);
        ss->ss_adder->destination_x = dx;
        ss->ss_adder->fast_dest_dx = font->fontwidth;
        ss->ss_adder->destination_y = dy;
        ss->ss_adder->slow_dest_dy = font->fontheight;
        /* load SOURCE origin and vectors */
        ss->ss_adder->source_1_x = sx;
        ss->ss_adder->source_1_y = sy;
        ss->ss_adder->source_1_dx = font->fontwidth;
        ss->ss_adder->source_1_dy = font->fontheight;
        ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;

        if (ul != 0) {
                gpx_fillrect(ss, dx, dy + font->fontheight - 2, font->fontwidth,
                    1, attr, LF_R3);    /* fg fill */
        }
}

static void
gpx_copycols(void *v, int row, int src, int dst, int cnt)
{
        struct rasops_info *ri = v;
        struct gpx_screen *ss = ri->ri_hw;
        struct wsdisplay_font *font = ri->ri_font;
        int sx, y, dx, w, h;

        sx = ri->ri_xorigin + src * font->fontwidth;
        dx = ri->ri_xorigin + dst * font->fontwidth;
        w = cnt * font->fontwidth;
        y = ri->ri_yorigin + row * font->fontheight;
        h = font->fontheight;

        gpx_copyrect(ss, sx, y, dx, y, w, h);
}

static void
gpx_erasecols(void *v, int row, int col, int cnt, long attr)
{
        struct rasops_info *ri = v;
        struct gpx_screen *ss = ri->ri_hw;
        struct wsdisplay_font *font = ri->ri_font;
        int x, y, dx, dy;

        x = ri->ri_xorigin + col * font->fontwidth;
        dx = cnt * font->fontwidth;
        y = ri->ri_yorigin + row * font->fontheight;
        dy = font->fontheight;

        gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
}

static void
gpx_copyrows(void *v, int src, int dst, int cnt)
{
        struct rasops_info *ri = v;
        struct gpx_screen *ss = ri->ri_hw;
        struct wsdisplay_font *font = ri->ri_font;
        int x, sy, dy, w, h;

        x = ri->ri_xorigin;
        w = ri->ri_emustride;
        sy = ri->ri_yorigin + src * font->fontheight;
        dy = ri->ri_yorigin + dst * font->fontheight;
        h = cnt * font->fontheight;

        gpx_copyrect(ss, x, sy, x, dy, w, h);
}

static void
gpx_eraserows(void *v, int row, int cnt, long attr)
{
        struct rasops_info *ri = v;
        struct gpx_screen *ss = ri->ri_hw;
        struct wsdisplay_font *font = ri->ri_font;
        int x, y, dx, dy;

        x = ri->ri_xorigin;
        dx = ri->ri_emustride;
        y = ri->ri_yorigin + row * font->fontheight;
        dy = cnt * font->fontheight;

        gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
}

static void
gpx_do_cursor(struct rasops_info *ri)
{
        struct gpx_screen *ss = ri->ri_hw;
        int x, y, w, h;

        x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
        y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
        w = ri->ri_font->fontwidth;
        h = ri->ri_font->fontheight;

        gpx_fillrect(ss, x, y, w, h, WSCOL_WHITE << 24, LF_R4); /* invert */
}

/*
 * low-level programming routines
 */

static int
gpx_wait(struct gpx_screen *ss, int bits)
{
        int i;

        ss->ss_adder->status = 0;
        for (i = 100000; i != 0; i--) {
                if ((ss->ss_adder->status & bits) == bits)
                        break;
                DELAY(1);
        }

        return i == 0;
}

static int
gpx_viper_write(struct gpx_screen *ss, u_int reg, uint16_t val)
{
        if (gpx_wait(ss, ADDRESS_COMPLETE) == 0 &&
            gpx_wait(ss, TX_READY) == 0) {
                ss->ss_adder->id_data = val;
                ss->ss_adder->command = ID_LOAD | reg;
                return 0;
        }
#ifdef DEBUG
        if (ss->ss_console == 0)        /* don't make things worse! */
                printf("gpx_viper_write failure, reg %x val %x\n", reg, val);
#endif
        return 1;
}

/* Initialize the damned beast. Straight from qdss. */
static void
gpx_reset_viper(struct gpx_screen *ss)
{
        int i;

        ss->ss_adder->interrupt_enable = 0;
        ss->ss_adder->command = CANCEL;
        /* set monitor timing */
        ss->ss_adder->x_scan_count_0 = 0x2800;
        ss->ss_adder->x_scan_count_1 = 0x1020;
        ss->ss_adder->x_scan_count_2 = 0x003a;
        ss->ss_adder->x_scan_count_3 = 0x38f0;
        ss->ss_adder->x_scan_count_4 = 0x6128;
        ss->ss_adder->x_scan_count_5 = 0x093a;
        ss->ss_adder->x_scan_count_6 = 0x313c;
        ss->ss_adder->sync_phase_adj = 0x0100;
        ss->ss_adder->x_scan_conf = 0x00c8;
        /*
         * got a bug in second pass ADDER! lets take care of it...
         *
         * normally, just use the code in the following bug fix code, but to
         * make repeated demos look pretty, load the registers as if there was
         * no bug and then test to see if we are getting sync
         */
        ss->ss_adder->y_scan_count_0 = 0x135f;
        ss->ss_adder->y_scan_count_1 = 0x3363;
        ss->ss_adder->y_scan_count_2 = 0x2366;
        ss->ss_adder->y_scan_count_3 = 0x0388;
        /*
         * if no sync, do the bug fix code
         */
        if (gpx_wait(ss, FRAME_SYNC) != 0) {
                /*
                 * First load all Y scan registers with very short frame and
                 * wait for scroll service.  This guarantees at least one SYNC
                 * to fix the pass 2 Adder initialization bug (synchronizes
                 * XCINCH with DMSEEDH)
                 */
                ss->ss_adder->y_scan_count_0 = 0x01;
                ss->ss_adder->y_scan_count_1 = 0x01;
                ss->ss_adder->y_scan_count_2 = 0x01;
                ss->ss_adder->y_scan_count_3 = 0x01;
                /* delay at least 1 full frame time */
                gpx_wait(ss, FRAME_SYNC);
                gpx_wait(ss, FRAME_SYNC);
                /*
                 * now load the REAL sync values (in reverse order just to
                 * be safe).
                 */
                ss->ss_adder->y_scan_count_3 = 0x0388;
                ss->ss_adder->y_scan_count_2 = 0x2366;
                ss->ss_adder->y_scan_count_1 = 0x3363;
                ss->ss_adder->y_scan_count_0 = 0x135f;
        }
        /* zero the index registers */
        ss->ss_adder->x_index_pending = 0;
        ss->ss_adder->y_index_pending = 0;
        ss->ss_adder->x_index_new = 0;
        ss->ss_adder->y_index_new = 0;
        ss->ss_adder->x_index_old = 0;
        ss->ss_adder->y_index_old = 0;
        ss->ss_adder->pause = 0;
        /* set rasterop mode to normal pen down */
        ss->ss_adder->rasterop_mode =
            DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
        /* set the rasterop registers to default values */
        ss->ss_adder->source_1_dx = 1;
        ss->ss_adder->source_1_dy = 1;
        ss->ss_adder->source_1_x = 0;
        ss->ss_adder->source_1_y = 0;
        ss->ss_adder->destination_x = 0;
        ss->ss_adder->destination_y = 0;
        ss->ss_adder->fast_dest_dx = 1;
        ss->ss_adder->fast_dest_dy = 0;
        ss->ss_adder->slow_dest_dx = 0;
        ss->ss_adder->slow_dest_dy = 1;
        ss->ss_adder->error_1 = 0;
        ss->ss_adder->error_2 = 0;
        /* scale factor = UNITY */
        ss->ss_adder->fast_scale = UNITY;
        ss->ss_adder->slow_scale = UNITY;
        /* set the source 2 parameters */
        ss->ss_adder->source_2_x = 0;
        ss->ss_adder->source_2_y = 0;
        ss->ss_adder->source_2_size = 0x0022;
        /* initialize plane addresses for eight vipers */
        for (i = 0; i < 8; i++) {
                gpx_viper_write(ss, CS_UPDATE_MASK, 1 << i);
                gpx_viper_write(ss, PLANE_ADDRESS, i);
        }
        /* initialize the external registers. */
        gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
        gpx_viper_write(ss, CS_SCROLL_MASK, 0x00ff);
        /* initialize resolution mode */
        gpx_viper_write(ss, MEMORY_BUS_WIDTH, 0x000c);  /* bus width = 16 */
        gpx_viper_write(ss, RESOLUTION_MODE, 0x0000);   /* one bit/pixel */
        /* initialize viper registers */
        gpx_viper_write(ss, SCROLL_CONSTANT,
            SCROLL_ENABLE | VIPER_LEFT | VIPER_UP);
        gpx_viper_write(ss, SCROLL_FILL, 0x0000);
        /* set clipping and scrolling limits to full screen */
        gpx_wait(ss, ADDRESS_COMPLETE);
        ss->ss_adder->x_clip_min = 0;
        ss->ss_adder->x_clip_max = GPX_WIDTH;
        ss->ss_adder->y_clip_min = 0;
        ss->ss_adder->y_clip_max = GPX_HEIGHT;
        ss->ss_adder->scroll_x_min = 0;
        ss->ss_adder->scroll_x_max = GPX_WIDTH;
        ss->ss_adder->scroll_y_min = 0;
        ss->ss_adder->scroll_y_max = GPX_HEIGHT;
        gpx_wait(ss, FRAME_SYNC);       /* wait at LEAST 1 full frame */
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->x_index_pending = 0;
        ss->ss_adder->y_index_pending = 0;
        ss->ss_adder->x_index_new = 0;
        ss->ss_adder->y_index_new = 0;
        ss->ss_adder->x_index_old = 0;
        ss->ss_adder->y_index_old = 0;
        gpx_wait(ss, ADDRESS_COMPLETE);
        gpx_viper_write(ss, LEFT_SCROLL_MASK, 0x0000);
        gpx_viper_write(ss, RIGHT_SCROLL_MASK, 0x0000);
        /* set source and the mask register to all ones */
        gpx_viper_write(ss, SOURCE, 0xffff);
        gpx_viper_write(ss, MASK_1, 0xffff);
        gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
        gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
        /* initialize Operand Control Register banks for fill command */
        gpx_viper_write(ss, SRC1_OCR_A,
            EXT_NONE | INT_M1_M2  | NO_ID | WAIT);
        gpx_viper_write(ss, SRC2_OCR_A,
            EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
        gpx_viper_write(ss, DST_OCR_A,
            EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
        gpx_viper_write(ss, SRC1_OCR_B,
            EXT_NONE | INT_SOURCE | NO_ID | WAIT);
        gpx_viper_write(ss, SRC2_OCR_B,
            EXT_NONE | INT_M1_M2  | NO_ID | NO_WAIT);
        gpx_viper_write(ss, DST_OCR_B,
            EXT_NONE | INT_NONE | NO_ID | NO_WAIT);

        /*
         * Init Logic Unit Function registers.
         */
        /* putchar */
        gpx_viper_write(ss, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
        /* erase{cols,rows} */
        gpx_viper_write(ss, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_ZEROS);
        /* underline */
        gpx_viper_write(ss, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_ONES);
        /* cursor */
        gpx_viper_write(ss, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_NOT_D);
}

/* Clear the whole screen. Straight from qdss. */
static void
gpx_clear_screen(struct gpx_screen *ss)
{

        ss->ss_adder->x_limit = GPX_WIDTH;
        ss->ss_adder->y_limit = GPX_HEIGHT;
        ss->ss_adder->y_offset_pending = 0;
        gpx_wait(ss, FRAME_SYNC);       /* wait at LEAST 1 full frame */
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_offset_pending = GPX_VISHEIGHT;
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_offset_pending = 2 * GPX_VISHEIGHT;
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->y_offset_pending = 0;      /* back to normal */
        gpx_wait(ss, FRAME_SYNC);
        gpx_wait(ss, FRAME_SYNC);
        ss->ss_adder->x_limit = GPX_WIDTH;
        ss->ss_adder->y_limit = GPX_VISHEIGHT;
}

static int
gpx_setup_screen(struct gpx_screen *ss)
{
        struct rasops_info *ri = &ss->ss_ri;
        int cookie;

        memset(ri, 0, sizeof(*ri));
        ri->ri_depth = 8;       /* masquerade as a 8 bit device for rasops */
        ri->ri_width = GPX_WIDTH;
        ri->ri_height = GPX_VISHEIGHT;
        ri->ri_stride = GPX_WIDTH;
        ri->ri_flg = RI_CENTER;         /* no RI_CLEAR as ri_bits is NULL! */
        ri->ri_hw = ss;
        if (ss == &gpx_consscr)
                ri->ri_flg |= RI_NO_AUTO;

        /*
         * We can not let rasops select our font, because we need to use
         * a font with right-to-left bit order on this frame buffer.
         */
        wsfont_init();
        cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_R2L,
            WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
        if (cookie < 0)
                cookie = wsfont_find(NULL, 8, 0, 0, WSDISPLAY_FONTORDER_R2L,
                    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
        if (cookie < 0)
                cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_R2L,
                    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
        if (cookie < 0)
                return -1;
        if (wsfont_lock(cookie, &ri->ri_font) != 0)
                return -1;
        ri->ri_wsfcookie = cookie;

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

        /*
         * Override the rasops emulops.
         */
        ri->ri_ops.copyrows = gpx_copyrows;
        ri->ri_ops.copycols = gpx_copycols;
        ri->ri_ops.eraserows = gpx_eraserows;
        ri->ri_ops.erasecols = gpx_erasecols;
        ri->ri_ops.putchar = gpx_putchar;
        ri->ri_do_cursor = gpx_do_cursor;

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

        /*
         * Initialize RAMDAC.
         */
        if (ss->ss_depth == 8) {
                struct ramdac8 *rd = ss->ss_vdac;
                rd->address = BT_CR;
                rd->control = BTCR_RAMENA | BTCR_BLINK_1648 | BTCR_MPLX_4;
        } else {
                struct ramdac4 *rd = ss->ss_vdac;
                rd->control = RAMDAC4_INIT;
        }

        /*
         * Put the ADDER and VIPER in a good state.
         */
        gpx_reset_viper(ss);

        /*
         * Initialize colormap.
         */
        gpx_resetcmap(ss);

        /*
         * Clear display (including non-visible area), in 864 lines chunks.
         */
        gpx_clear_screen(ss);

        /*
         * Copy our font to the offscreen area.
         */
        gpx_upload_font(ss);

#if 0
        ss->ss_cursor->cmdr = ss->ss_curcmd = PCCCMD_HSHI;
#endif

        return 0;
}

/*
 * Copy the selected wsfont to non-visible frame buffer area.
 * This is necessary since the only way to send data to the frame buffer
 * is through the ID interface, which is slow and needs 16 bit wide data.
 * Adapted from qdss.
 */
static void
gpx_upload_font(struct gpx_screen *ss)
{
        struct rasops_info *ri = &ss->ss_ri;
        struct wsdisplay_font *font = ri->ri_font;
        uint8_t *fontbits, *fb;
        u_int remaining, nchars, row;
        u_int i, j;
        uint16_t data;

        /* setup VIPER operand control registers */

        gpx_viper_write(ss, MASK_1, 0xffff);
        gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
        gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);

        gpx_viper_write(ss, SRC1_OCR_B,
            EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
        gpx_viper_write(ss, SRC2_OCR_B,
            EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
        gpx_viper_write(ss, DST_OCR_B,
            EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);

        ss->ss_adder->rasterop_mode =
            DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
        gpx_wait(ss, RASTEROP_COMPLETE);

        /*
         * Load font data. The font is uploaded in 8 or 16 bit wide cells, on
         * as many ``lines'' as necessary at the end of the display.
         */
        ss->ss_gpr = MIN(GPX_WIDTH / (NBBY * font->stride), font->numchars);
        if ((ss->ss_gpr & 1) != 0)
                ss->ss_gpr--;
        fontbits = font->data;
        for (row = 1, remaining = font->numchars; remaining != 0;
            row++, remaining -= nchars) {
                nchars = MIN(ss->ss_gpr, remaining);

                ss->ss_adder->destination_x = 0;
                ss->ss_adder->destination_y =
                    GPX_HEIGHT - row * font->fontheight;
                ss->ss_adder->fast_dest_dx = nchars * 16;
                ss->ss_adder->slow_dest_dy = font->fontheight;

                /* setup for processor to bitmap xfer */
                gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
                ss->ss_adder->cmd = PBT | OCRB | DTE | LF_R1 | 2; /*XXX why 2?*/

                /* iteratively do the processor to bitmap xfer */
                for (i = font->fontheight; i != 0; i--) {
                        fb = fontbits;
                        fontbits += font->stride;
                        /* PTOB a scan line */
                        for (j = nchars; j != 0; j--) {
                                /* PTOB one scan of a char cell */
                                if (font->stride == 1) {
                                        data = *fb;
                                        fb += font->fontheight;
                                        /*
                                         * Do not access past font memory if
                                         * it has an odd number of characters
                                         * and this is the last pair.
                                         */
                                        if (j != 1 || (nchars & 1) == 0 ||
                                            remaining != nchars) {
                                                data |= ((uint16_t)*fb) << 8;
                                                fb += font->fontheight;
                                        }
                                } else {
                                        data =
                                            fb[0] | (((uint16_t)fb[1]) << 8);
                                        fb += font->fontheight * font->stride;
                                }

                                gpx_wait(ss, TX_READY);
                                ss->ss_adder->id_data = data;
                        }
                }
                fontbits += (nchars - 1) * font->stride * font->fontheight;
        }
}

static void
gpx_copyrect(struct gpx_screen *ss,
    int sx, int sy, int dx, int dy, int w, int h)
{

        while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
                continue;
        gpx_viper_write(ss, MASK_1, 0xffff);
        gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
        gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
        gpx_viper_write(ss, SRC1_OCR_B,
            EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
        gpx_viper_write(ss, DST_OCR_B,
            EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
        ss->ss_adder->fast_dest_dy = 0;
        ss->ss_adder->slow_dest_dx = 0;
        ss->ss_adder->error_1 = 0;
        ss->ss_adder->error_2 = 0;
        ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
        gpx_wait(ss, RASTEROP_COMPLETE);
        ss->ss_adder->destination_x = dx;
        ss->ss_adder->fast_dest_dx = w;
        ss->ss_adder->destination_y = dy;
        ss->ss_adder->slow_dest_dy = h;
        ss->ss_adder->source_1_x = sx;
        ss->ss_adder->source_1_dx = w;
        ss->ss_adder->source_1_y = sy;
        ss->ss_adder->source_1_dy = h;
        ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
}

/*
 * Fill a rectangle with the given attribute and function (i.e. rop).
 */
static void
gpx_fillrect(struct gpx_screen *ss, int x, int y, int dx, int dy, long attr,
    u_int function)
{
        int fg, bg;

        rasops_unpack_attr(attr, &fg, &bg, NULL);

        while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
                continue;
        gpx_viper_write(ss, MASK_1, 0xffff);
        gpx_viper_write(ss, SOURCE, 0xffff);
        gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
        gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
        gpx_viper_write(ss, SRC1_OCR_B,
            EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
        gpx_viper_write(ss, DST_OCR_B,
            EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
        ss->ss_adder->fast_dest_dx = 0;
        ss->ss_adder->fast_dest_dy = 0;
        ss->ss_adder->slow_dest_dx = 0;
        ss->ss_adder->error_1 = 0;
        ss->ss_adder->error_2 = 0;
        ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
        gpx_wait(ss, RASTEROP_COMPLETE);
        ss->ss_adder->destination_x = x;
        ss->ss_adder->fast_dest_dx = dx;
        ss->ss_adder->destination_y = y;
        ss->ss_adder->slow_dest_dy = dy;
        ss->ss_adder->source_1_x = x;
        ss->ss_adder->source_1_dx = dx;
        ss->ss_adder->source_1_y = y;
        ss->ss_adder->source_1_dy = dy;
        ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | function;
}

/*
 * Colormap handling routines
 */

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

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

        if (count == 0)
                return 0;

        /* extract reds */
        c = ss->ss_cmap + 0 + index * 3;
        for (i = count, r = ramp; i != 0; i--)
                *r++ = *c << (8 - ss->ss_depth), 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 << (8 - ss->ss_depth), 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 << (8 - ss->ss_depth), c += 3;
        if ((error = copyout(ramp, cm->blue, count)) != 0)
                return error;

        return 0;
}

static int
gpx_putcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
{
        u_int index = cm->index, count = cm->count;
        u_int colcount = 1 << ss->ss_depth;
        int i, error;
        uint8_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++ >> (8 - ss->ss_depth);
                *c++ = *ng++ >> (8 - ss->ss_depth);
                *c++ = *nb++ >> (8 - ss->ss_depth);
        }

        return 0;
}

static void
gpx_loadcmap(struct gpx_screen *ss, int from, int count)
{
        uint8_t *cmap = ss->ss_cmap;
        int i, color12;

        gpx_wait(ss, FRAME_SYNC);
        if (ss->ss_depth == 8) {
                struct ramdac8 *rd = ss->ss_vdac;

                cmap += from * 3;
                rd->address = from;
                for (i = 0; i < count * 3; i++)
                        rd->cmapdata = *cmap++;
        } else {
                struct ramdac4 *rd = ss->ss_vdac;

                cmap = ss->ss_cmap + from;
                for (i = from; i < from + count; i++) {
                        color12  = (*cmap++ >> 4) << 0;
                        color12 |= (*cmap++ >> 4) << 8;
                        color12 |= (*cmap++ >> 4) << 4;
                        rd->colormap[i] = color12;
                }
        }
}

static void
gpx_resetcmap(struct gpx_screen *ss)
{

        if (ss->ss_depth == 8)
                memcpy(ss->ss_cmap, rasops_cmap, sizeof(ss->ss_cmap));
        else {
                memcpy(ss->ss_cmap, rasops_cmap, 8 * 3);
                memcpy(ss->ss_cmap + 8 * 3, rasops_cmap + 0xf8 * 3, 8 * 3);
        }
        gpx_loadcmap(ss, 0, 1 << ss->ss_depth);

        /*
         * On the 4bit RAMDAC, make the hardware cursor black on black
         */
        if (ss->ss_depth != 8) {
                struct ramdac4 *rd = ss->ss_vdac;

                rd->cursormap[0] = rd->cursormap[1] =
                    rd->cursormap[2] = rd->cursormap[3] = 0x0000;
        }
}

/*
 * Console support code
 */

cons_decl(gpx);

/*
 * 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.
 */
void
gpxcnprobe(struct consdev *cndev)
{
        extern vaddr_t virtual_avail;
        extern const struct cdevsw wsdisplay_cdevsw;
        volatile struct adder *adder;
        vaddr_t tmp;
        int depth;
        u_short status;

        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; /* no color option */

                /* Check for hardware */
                tmp = virtual_avail;
                ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_ADDER_OFFSET), 1);
                adder = (struct adder *)tmp;
                adder->status = 0;
                status = adder->status;
                iounaccess(tmp, 1);
                if (status == offsetof(struct adder, status))
                        return;

                /* Check for a recognized color depth */
                tmp = virtual_avail;
                ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
                depth = *(uint16_t *)
                    (tmp + (GPX_READBACK_OFFSET & VAX_PGOFSET)) & 0x00f0;
                iounaccess(tmp, 1);
                if (depth != 0x00f0 && depth != 0x0080)
                        return;

                cndev->cn_pri = CN_INTERNAL;
                cndev->cn_dev =
                    makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0);
                break;

        default:
                break;
        }
}

/*
 * 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.
 */
void
gpxcninit(struct consdev *cndev)
{
        struct gpx_screen *ss = &gpx_consscr;
        extern vaddr_t virtual_avail;
        vaddr_t ova;
        long defattr;
        struct rasops_info *ri;

        ova = virtual_avail;

        ioaccess(virtual_avail,
            vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
        ss->ss_depth = (0x00f0 & *(uint16_t *)(virtual_avail +
            (GPX_READBACK_OFFSET & VAX_PGOFSET))) == 0x00f0 ? 4 : 8;

        ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1);
        ss->ss_adder = (struct adder *)virtual_avail;
        virtual_avail += VAX_NBPG;

        ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_VDAC_OFFSET), 1);
        ss->ss_vdac = (void *)(virtual_avail + (GPX_VDAC_OFFSET & VAX_PGOFSET));
        virtual_avail += VAX_NBPG;

#if 0
        ioaccess(virtual_avail, GPXADDR + GPX_CURSOR_OFFSET, 1);
        ss->ss_cursor = (struct dc503reg *)virtual_avail;
        virtual_avail += VAX_NBPG;
#endif

        virtual_avail = round_page(virtual_avail);

        /* this had better not fail */
        if (gpx_setup_screen(ss) != 0) {
#if 0
                iounaccess((vaddr_t)ss->ss_cursor, 1);
#endif
                iounaccess((vaddr_t)ss->ss_vdac, 1);
                iounaccess((vaddr_t)ss->ss_adder, 1);
                virtual_avail = ova;
                return;
        }

        ri = &ss->ss_ri;
        ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
        wsdisplay_cnattach(&gpx_stdscreen, ri, 0, 0, defattr);
        cn_tab->cn_pri = CN_INTERNAL;

#if NDZKBD > 0
        dzkbd_cnattach(0); /* Connect keyboard and screen together */
#endif
}