root/sys/arch/atari/dev/grfabs_et.c
/*      $NetBSD: grfabs_et.c,v 1.37 2023/01/06 10:28:28 tsutsui Exp $   */

/*
 * Copyright (c) 1996 Leo Weppelman.
 * 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.
 *
 * 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.
 */

/*
 * Most of the lower-level et4000 stuff was derived from:
 *      .../amiga/dev/grf_et.c
 *
 * Which was copyrighted by:
 *      Copyright (c) 1996 Tobias Abt
 *      Copyright (c) 1995 Ezra Story
 *      Copyright (c) 1995 Kari Mettinen
 *      Copyright (c) 1994 Markus Wild
 *      Copyright (c) 1994 Lutz Vieweg
 *
 * Thanks guys!
 *
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: grfabs_et.c,v 1.37 2023/01/06 10:28:28 tsutsui Exp $");

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

#include <uvm/uvm_extern.h>

/*
 * For PCI probing...
 */
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <machine/iomap.h>
#include <machine/video.h>
#include <machine/mfp.h>
#include <machine/cpu.h>
#include <atari/atari/device.h>
#include <atari/dev/grfioctl.h>
#include <atari/dev/grfabs_reg.h>
#include <atari/dev/grfabs_et.h>
#include <atari/dev/grf_etreg.h>

#define SAVEBUF_SIZE    (32*1024 + sizeof(save_area_t))

/*
 * Allow a 16Kb io-region and a 4MB frame buffer to be mapped. This
 * is more or less required by the XFree server.
 */
#define REG_MAPPABLE    (16 * 1024)
#define FRAME_MAPPABLE  (4 * 1024 * 1024)
#define VGA_MAPPABLE    (128 * 1024)
#define VGA_BASE        0xa0000

/*
 * Linear memory base, near the end of the pci area
 */
#define PCI_LINMEMBASE  0x0e000000

/*
 * Function decls
 */
static void       init_view(view_t *, bmap_t *, dmode_t *, box_t *);
static colormap_t *alloc_colormap(dmode_t *);
static void       et_display_view(view_t *);
static view_t     *et_alloc_view(dmode_t *, dimen_t *, u_char);
static void       et_free_view(view_t *);
static void       et_loadmode(struct grfvideo_mode *, et_sv_reg_t *);
static void       et_remove_view(view_t *);
static void       et_save_view(view_t *);
static int        et_use_colormap(view_t *, colormap_t *);

/*
 * Our function switch table
 */
struct grfabs_sw et_vid_sw = {
        et_display_view,
        et_alloc_view,
        et_free_view,
        et_remove_view,
        et_save_view,
        et_use_colormap
};

static struct grfvideo_mode hw_modes[] = {
    {
        0, "", 22450000,                /* num, descr, pix-clock        */
        640, 400, 4,                    /* width, height, depth         */
        632/8, 672/8, 688/8, 808/8, 768/8,/* HBS, HBE, HSS, HSE, HT     */
        399, 450, 408, 413, 449         /* VBS, VBE, VSS, VSE, VT       */
    },
    {
        0, "", 25175000,                /* num, descr, pix-clock        */
        640, 480, 4,                    /* width, height, depth         */
        632/8, 672/8, 688/8, 752/8, 752/8,/* HBS, HBE, HSS, HSE, HT     */
        481, 522, 490, 498, 522         /* VBS, VBE, VSS, VSE, VT       */
    }
};

static dmode_t vid_modes[] = {
    { { NULL, NULL },
        "640x400", { 640, 400 }, 1, (void*)&hw_modes[0], &et_vid_sw },
    { { NULL, NULL },
        "640x480", { 640, 480 }, 1, (void*)&hw_modes[1], &et_vid_sw },
    { { NULL, NULL }, NULL,  }
};

#define ET_NUMCLOCKS    32

static u_int et_clockfreqs[ET_NUMCLOCKS] = {
         6293750,  7080500,  7875000,  8125000,
         9000000,  9375000, 10000000, 11225000,
        12587500, 14161000, 15750000, 16250000,
        18000000, 18750000, 20000000, 22450000,
        25175000, 28322000, 31500000, 32500000,
        36000000, 37500000, 40000000, 44900000,
        50350000, 56644000, 63000000, 65000000,
        72000000, 75000000, 80000000, 89800000
};

static bmap_t   con_bm; /* XXX */

struct grfabs_et_priv {
        pcitag_t                pci_tag;
        void                    *regkva;
        void                    *memkva;
        u_int                   linbase;
        int                     regsz;
        int                     memsz;
        int                     board_type;
} et_priv;

/*
 * Board types:
 */
#define BT_ET4000               1
#define BT_ET6000               2

/*
 * XXX: called from ite console init routine.
 * Initialize list of possible video modes.
 */
void
et_probe_video(MODES *modelp)
{
        dmode_t *dm;
        int     i;

        for (i = 0; (dm = &vid_modes[i])->name != NULL; i++) {
                LIST_INSERT_HEAD(modelp, dm, link);
        }
}

static void
et_display_view(view_t *v)
{
        dmode_t         *dm = v->mode;
        bmap_t          *bm = v->bitmap;
        int             sv_size;
        u_short         *src, *dst;
        save_area_t     *sa;

        if (dm->current_view && (dm->current_view != v)) {
                /*
                 * Mark current view for this mode as no longer displayed
                 */
                dm->current_view->flags &= ~VF_DISPLAY;
        }
        dm->current_view = v;
        v->flags |= VF_DISPLAY;

        if ((sa = (save_area_t*)v->save_area) == NULL)
                return; /* XXX: Can't happen.... */

        /*
         * Restore register settings and turn the plane pointer
         * to the card-memory
         */
        et_hwrest(&sa->sv_regs);
        bm->plane = et_priv.memkva;

        et_use_colormap(v, v->colormap);

        /*
         * Copy the backing store to card-memory
         */
        sv_size = sa->fb_size;
        src     = sa->sv_fb;
        dst     = (u_short *)bm->plane;
        while (sv_size--)
                *dst++ = *src++;
}

void
et_remove_view(view_t *v)
{
        dmode_t *mode = v->mode;

        if (mode->current_view == v) {
#if 0
                if (v->flags & VF_DISPLAY)
                        panic("Cannot shutdown display"); /* XXX */
#endif
                mode->current_view = NULL;
        }
        v->flags &= ~VF_DISPLAY;
}

void
et_save_view(view_t *v)
{
        bmap_t          *bm = v->bitmap;
        u_char          font_height;
        int             sv_size;
        u_short         *src, *dst;
        save_area_t     *sa;
        volatile u_char *ba;

        if (!atari_realconfig)
                return;

        ba = et_priv.regkva;

        if (RGfx(ba, GCT_ID_MISC) & 1) {
#if 0 /* XXX: Can't use printf here.... */
                printf("et_save_view: Don't know how to save"
                        " a graphics mode\n");
#endif
                return;
        }
        if (v->save_area == NULL)
                v->save_area = malloc(SAVEBUF_SIZE, M_DEVBUF, M_NOWAIT);

        /*
         * Calculate the size of the copy
         */
        font_height = RCrt(ba, CRT_ID_MAX_ROW_ADDRESS) & 0x1f;
        sv_size = bm->bytes_per_row * (bm->rows / (font_height + 1));
        sv_size = uimin(SAVEBUF_SIZE, sv_size);

        /*
         * Save all we need to know....
         */
        sa  = (save_area_t *)v->save_area;
        et_hwsave(&sa->sv_regs);
        sa->fb_size = sv_size;
        src = (u_short *)bm->plane;
        dst = sa->sv_fb;
        while (sv_size--)
                *dst++ = *src++;
        bm->plane = (u_char *)sa->sv_fb;
}

void
et_free_view(view_t *v)
{

        if (v) {
                et_remove_view(v);
                if (v->colormap != &gra_con_cmap)
                        free(v->colormap, M_DEVBUF);
                if (v->save_area != NULL)
                        free(v->save_area, M_DEVBUF);
                if (v != &gra_con_view) {
                        free(v->bitmap, M_DEVBUF);
                        free(v, M_DEVBUF);
                }
        }
}

static int
et_use_colormap(view_t *v, colormap_t *cm)
{
        return (0); /* XXX: Nothing here for now... */
}

static view_t *
et_alloc_view(dmode_t *mode, dimen_t *dim, u_char depth)
{
        view_t          *v;
        bmap_t          *bm;
        box_t           box;
        save_area_t     *sa;

        if (!atari_realconfig) {
                v  = &gra_con_view;
                bm = &con_bm;
        } else {
                v  = malloc(sizeof(*v), M_DEVBUF, M_WAITOK);
                bm = malloc(sizeof(*bm), M_DEVBUF, M_WAITOK);
        }
        v->bitmap = bm;

        /*
         * Initialize the bitmap
         */
        bm->plane         = et_priv.memkva;
        bm->vga_address   = (void *)kvtop(et_priv.memkva);
        bm->vga_base      = VGA_BASE;
        bm->hw_address    = (void *)(PCI_MEM_PHYS | et_priv.linbase);
        bm->lin_base      = et_priv.linbase;
        bm->regs          = et_priv.regkva;
        bm->hw_regs       = (void *)kvtop(et_priv.regkva);
        bm->reg_size      = REG_MAPPABLE;
        bm->phys_mappable = FRAME_MAPPABLE;
        bm->vga_mappable  = VGA_MAPPABLE;

        bm->bytes_per_row = (mode->size.width * depth) / NBBY;
        bm->rows          = mode->size.height;
        bm->depth         = depth;

        /*
         * Allocate a save_area.
         * Note: If atari_realconfig is false, no save area is (can be)
         * allocated. This means that the plane is the video memory,
         * which is what's wanted in this case.
         */
        if (atari_realconfig) {
                v->save_area = malloc(SAVEBUF_SIZE, M_DEVBUF, M_WAITOK);
                sa           = (save_area_t*)v->save_area;
                sa->fb_size  = 0;
                bm->plane    = (u_char *)sa->sv_fb;
                et_loadmode(mode->data, &sa->sv_regs);
        } else
                v->save_area = NULL;

        v->colormap = alloc_colormap(mode);
        if (v->colormap) {
                INIT_BOX(&box,0,0,mode->size.width,mode->size.height);
                init_view(v, bm, mode, &box);
                return (v);
        }
        if (v != &gra_con_view) {
                free(v, M_DEVBUF);
                free(bm, M_DEVBUF);
        }
        return (NULL);
}

static void
init_view(view_t *v, bmap_t *bm, dmode_t *mode, box_t *dbox)
{
        v->bitmap    = bm;
        v->mode      = mode;
        v->flags     = 0;
        memcpy(&v->display, dbox, sizeof(box_t));
}

/* XXX: No more than a stub... */
static colormap_t *
alloc_colormap(dmode_t *dm)
{
        colormap_t      *cm;
        int             i;

        cm = &gra_con_cmap;
        cm->entry = gra_con_colors;

        cm->first = 0;
        cm->size  = 2;

        for (i = 0; i < 2; i++)
                cm->entry[i] = gra_def_color16[i % 16];
        return (cm);
}

/*
 * Go look for a VGA card on the PCI-bus. This search is a
 * stripped down version of the PCI-probe. It only looks on
 * bus0 for et4000/et6000 cards. The first card found is used.
 */
int
et_probe_card(void)
{
        pci_chipset_tag_t       pc = NULL; /* XXX */
        pcitag_t                tag;
        int                     device, found, id, maxndevs;

        found    = 0;
        tag      = 0;
        id       = 0;
        maxndevs = pci_bus_maxdevs(pc, 0);

        for (device = 0; !found && (device < maxndevs); device++) {

                tag = pci_make_tag(pc, 0, device, 0);
                id  = pci_conf_read(pc, tag, PCI_ID_REG);
                if (id == 0 || id == 0xffffffff)
                        continue;
                switch (PCI_PRODUCT(id)) {
                        case PCI_PRODUCT_TSENG_ET6000:
                        case PCI_PRODUCT_TSENG_ET4000_W32P_A:
                        case PCI_PRODUCT_TSENG_ET4000_W32P_B:
                        case PCI_PRODUCT_TSENG_ET4000_W32P_C:
                        case PCI_PRODUCT_TSENG_ET4000_W32P_D:
                                found = 1;
                                break;
                        default:
                                break;
                }
        }
        if (!found)
                return (0);

        if (PCI_PRODUCT(id) ==  PCI_PRODUCT_TSENG_ET6000)
                et_priv.board_type = BT_ET6000;
        else {
#ifdef ET4000_HAS_2MB_MEM
                volatile u_char *ba;
#endif

                et_priv.board_type = BT_ET4000;

#ifdef ET4000_HAS_2MB_MEM
                /* set KEY to access the tseng private registers */
                ba = (volatile void *)pci_io_addr;
                vgaw(ba, GREG_HERCULESCOMPAT, 0x03);
                vgaw(ba, GREG_DISPMODECONTROL, 0xa0);

                /* enable memory interleave */
                WCrt(ba, CRT_ID_RASCAS_CONFIG, 0xa0);
                WCrt(ba, CRT_ID_VIDEO_CONFIG2, 0x89);
#endif
        }

        et_priv.pci_tag = tag;

        /*
         * The things below are setup in atari_init.c
         */
        et_priv.regkva  = (void *)pci_io_addr;
        et_priv.memkva  = (void *)pci_mem_addr;
        et_priv.linbase = PCI_LINMEMBASE; /* XXX pci_conf_read??? */
        et_priv.memsz   = PCI_VGA_SIZE;
        et_priv.regsz   = PCI_IO_SIZE;

        if (!atari_realconfig) {
                et_loadmode(&hw_modes[0], NULL);
                return (1);
        }

        return (1);
}

static void
et_loadmode(struct grfvideo_mode *mode, et_sv_reg_t *regs)
{
        unsigned short  HDE, VDE;
        int             lace, dblscan;
        int             uplim, lowlim;
        int             i;
        unsigned char   clock, tmp;
        volatile u_char *ba;
        et_sv_reg_t     loc_regs;

        if (regs == NULL)
                regs = &loc_regs;

        ba  = et_priv.regkva;
        HDE = mode->disp_width / 8 - 1;
        VDE = mode->disp_height - 1;

        /* figure out whether lace or dblscan is needed */

        uplim   = mode->disp_height + (mode->disp_height / 4);
        lowlim  = mode->disp_height - (mode->disp_height / 4);
        lace    = (((mode->vtotal * 2) > lowlim)
                   && ((mode->vtotal * 2) < uplim)) ? 1 : 0;
        dblscan = (((mode->vtotal / 2) > lowlim)
                   && ((mode->vtotal / 2) < uplim)) ? 1 : 0;

        /* adjustments */
        if (lace)
                VDE /= 2;

        regs->misc_output = 0x23; /* Page 0, Color mode */
        regs->seg_sel     = 0x00;
        regs->state_ctl   = 0x00;

        regs->seq[SEQ_ID_RESET]           = 0x03; /* reset off          */
        regs->seq[SEQ_ID_CLOCKING_MODE]   = 0x21; /* Turn off screen    */
        regs->seq[SEQ_ID_MAP_MASK]        = 0xff; /* CPU writes all planes*/
        regs->seq[SEQ_ID_CHAR_MAP_SELECT] = 0x00; /* Char. generator 0  */
        regs->seq[SEQ_ID_MEMORY_MODE]     = 0x0e; /* Seq. Memory mode   */

        /*
         * Set the clock...
         */
        for (clock = ET_NUMCLOCKS-1; clock > 0; clock--) {
                if (et_clockfreqs[clock] <= mode->pixel_clock)
                        break;
        }
        regs->misc_output |= (clock & 3) << 2;
        regs->aux_mode     = 0xb4 | ((clock & 8) << 3);
        regs->compat_6845  = (clock & 4) ? 0x0a : 0x08;

        /*
         * The display parameters...
         */
        regs->crt[CRT_ID_HOR_TOTAL]        =  mode->htotal;
        regs->crt[CRT_ID_HOR_DISP_ENA_END] = ((HDE >= mode->hblank_start)
                                                ? mode->hblank_stop - 1
                                                : HDE);
        regs->crt[CRT_ID_START_HOR_BLANK]  = mode->hblank_start;
        regs->crt[CRT_ID_END_HOR_BLANK]    = (mode->hblank_stop & 0x1f) | 0x80;
        regs->crt[CRT_ID_START_HOR_RETR]   = mode->hsync_start;
        regs->crt[CRT_ID_END_HOR_RETR]     = (mode->hsync_stop & 0x1f)
                                                | ((mode->hblank_stop & 0x20)
                                                        ? 0x80 : 0x00);
        regs->crt[CRT_ID_VER_TOTAL]        = mode->vtotal;
        regs->crt[CRT_ID_START_VER_RETR]   = mode->vsync_start;
        regs->crt[CRT_ID_END_VER_RETR]     = (mode->vsync_stop & 0x0f) | 0x30;
        regs->crt[CRT_ID_VER_DISP_ENA_END] = VDE;
        regs->crt[CRT_ID_START_VER_BLANK]  = mode->vblank_start;
        regs->crt[CRT_ID_END_VER_BLANK]    = mode->vblank_stop;
        regs->crt[CRT_ID_MODE_CONTROL]     = 0xab;
        regs->crt[CRT_ID_START_ADDR_HIGH]  = 0x00;
        regs->crt[CRT_ID_START_ADDR_LOW]   = 0x00;
        regs->crt[CRT_ID_LINE_COMPARE]     = 0xff;
        regs->crt[CRT_ID_UNDERLINE_LOC]    = 0x00;
        regs->crt[CRT_ID_PRESET_ROW_SCAN]  = 0x00;
        regs->crt[CRT_ID_OFFSET]           = mode->disp_width/16;
        regs->crt[CRT_ID_MAX_ROW_ADDRESS]  =
                0x40 |
                (dblscan ? 0x80 : 0x00) |
                ((mode->vblank_start & 0x200) ? 0x20 : 0x00);
        regs->crt[CRT_ID_OVERFLOW] =
                0x10 |
                ((mode->vtotal       & 0x100) ? 0x01 : 0x00) |
                ((VDE                & 0x100) ? 0x02 : 0x00) |
                ((mode->vsync_start  & 0x100) ? 0x04 : 0x00) |
                ((mode->vblank_start & 0x100) ? 0x08 : 0x00) |
                ((mode->vtotal       & 0x200) ? 0x20 : 0x00) |
                ((VDE                & 0x200) ? 0x40 : 0x00) |
                ((mode->vsync_start  & 0x200) ? 0x80 : 0x00);
        regs->overfl_high =
                0x10 |
                ((mode->vblank_start & 0x400) ? 0x01 : 0x00) |
                ((mode->vtotal       & 0x400) ? 0x02 : 0x00) |
                ((VDE                & 0x400) ? 0x04 : 0x00) |
                ((mode->vsync_start  & 0x400) ? 0x08 : 0x00) |
                (lace ? 0x80 : 0x00);
        regs->hor_overfl =
                ((mode->htotal       & 0x100) ? 0x01 : 0x00) |
                ((mode->hblank_start & 0x100) ? 0x04 : 0x00) |
                ((mode->hsync_start  & 0x100) ? 0x10 : 0x00);

        regs->grf[GCT_ID_SET_RESET]        = 0x00;
        regs->grf[GCT_ID_ENABLE_SET_RESET] = 0x00;
        regs->grf[GCT_ID_COLOR_COMPARE]    = 0x00;
        regs->grf[GCT_ID_DATA_ROTATE]      = 0x00;
        regs->grf[GCT_ID_READ_MAP_SELECT]  = 0x00;
        regs->grf[GCT_ID_GRAPHICS_MODE]    = mode->depth == 1 ? 0x00: 0x40;
        regs->grf[GCT_ID_MISC]             = 0x01;
        regs->grf[GCT_ID_COLOR_XCARE]      = 0x0f;
        regs->grf[GCT_ID_BITMASK]          = 0xff;

        for (i = 0; i < 0x10; i++)
                regs->attr[i] = i;
        regs->attr[ACT_ID_ATTR_MODE_CNTL]  = 0x01;
        regs->attr[ACT_ID_OVERSCAN_COLOR]  = 0x00;
        regs->attr[ACT_ID_COLOR_PLANE_ENA] = 0x0f;
        regs->attr[ACT_ID_HOR_PEL_PANNING] = 0x00;
        regs->attr[ACT_ID_COLOR_SELECT]    = 0x00;
        regs->attr[ACT_ID_MISCELLANEOUS]   = 0x00;

        /*
         * XXX: This works for depth == 4. I need some better docs
         * to fix the other modes....
         */
        /*
         * What we need would be probe functions for RAMDAC/clock chip
         */
        vgar(ba, VDAC_ADDRESS);         /* clear old state */
        vgar(ba, VDAC_MASK);
        vgar(ba, VDAC_MASK);
        vgar(ba, VDAC_MASK);
        vgar(ba, VDAC_MASK);

        vgaw(ba, VDAC_MASK, 0);         /* set to palette */
        vgar(ba, VDAC_ADDRESS);         /* clear state */

        vgaw(ba, VDAC_MASK, 0xff);
        /*
         * End of depth stuff
         */

        /*
         * Compute Hsync & Vsync polarity
         * Note: This seems to be some kind of a black art :-(
         */
        tmp = regs->misc_output & 0x3f;
#if 1 /* This is according to my BW monitor & Xfree... */
        if (VDE < 400)
                tmp |= 0x40;    /* -hsync +vsync */
        else if (VDE < 480)
                tmp |= 0xc0;    /* -hsync -vsync */
#else /* This is according to my color monitor.... */
        if (VDE < 400)
                tmp |= 0x00;    /* +hsync +vsync */
        else if (VDE < 480)
                tmp |= 0x80;    /* +hsync -vsync */
#endif
        /* I'm unable to try the rest.... */
        regs->misc_output = tmp;

        if (regs == &loc_regs)
                et_hwrest(regs);
}

void
et_hwsave(et_sv_reg_t *et_regs)
{
        volatile u_char *ba;
        int             i, s;

        ba = et_priv.regkva;

        s = splhigh();

        /*
         * General VGA registers
         */
        et_regs->misc_output = vgar(ba, GREG_MISC_OUTPUT_R);
        for (i = 0; i < 25; i++)
                et_regs->crt[i]  = RCrt(ba, i);
        for (i = 0; i < 21; i++)
                et_regs->attr[i] = RAttr(ba, i | 0x20);
        for (i = 0; i < 9; i++)
                et_regs->grf[i]  = RGfx(ba, i);
        for (i = 0; i < 5; i++)
                et_regs->seq[i]  = RSeq(ba, i);

        /*
         * ET4000 extensions
         */
        et_regs->ext_start   = RCrt(ba, CTR_ID_EXT_START);
        et_regs->compat_6845 = RCrt(ba, CRT_ID_6845_COMPAT);
        et_regs->overfl_high = RCrt(ba, CRT_ID_OVERFLOW_HIGH);
        et_regs->hor_overfl  = RCrt(ba, CRT_ID_HOR_OVERFLOW);
        et_regs->state_ctl   = RSeq(ba, SEQ_ID_STATE_CONTROL);
        et_regs->aux_mode    = RSeq(ba, SEQ_ID_AUXILIARY_MODE);
        et_regs->seg_sel     = vgar(ba, GREG_SEGMENTSELECT);

        splx(s);
}

void
et_hwrest(et_sv_reg_t *et_regs)
{
        volatile u_char *ba;
        int             i, s;

        ba = et_priv.regkva;

        s = splhigh();

        vgaw(ba, GREG_SEGMENTSELECT, 0);
        vgaw(ba, GREG_MISC_OUTPUT_W, et_regs->misc_output);

        /*
         * General VGA registers
         */
        WSeq(ba, SEQ_ID_RESET, 0x01);
        for (i = 1; i < 5; i++)
                WSeq(ba, i, et_regs->seq[i]);
        WSeq(ba, SEQ_ID_RESET, 0x03);

        /*
         * Make sure we're allowed to write all crt-registers
         */
        WCrt(ba, CRT_ID_END_VER_RETR,
                et_regs->crt[CRT_ID_END_VER_RETR] & 0x7f);
        for (i = 0; i < 25; i++)
                WCrt(ba, i, et_regs->crt[i]);
        for (i = 0; i < 9; i++)
                WGfx(ba, i, et_regs->grf[i]);
        for (i = 0; i < 21; i++)
                WAttr(ba, i | 0x20, et_regs->attr[i]);

        /*
         * ET4000 extensions
         */
        WSeq(ba, SEQ_ID_STATE_CONTROL, et_regs->state_ctl);
        WSeq(ba, SEQ_ID_AUXILIARY_MODE, et_regs->aux_mode);
        WCrt(ba, CTR_ID_EXT_START, et_regs->ext_start);
        WCrt(ba, CRT_ID_6845_COMPAT, et_regs->compat_6845);
        WCrt(ba, CRT_ID_OVERFLOW_HIGH, et_regs->overfl_high);
        WCrt(ba, CRT_ID_HOR_OVERFLOW, et_regs->hor_overfl);
        vgaw(ba, GREG_SEGMENTSELECT, et_regs->seg_sel);

        i = et_regs->seq[SEQ_ID_CLOCKING_MODE] & ~0x20;
        WSeq(ba, SEQ_ID_CLOCKING_MODE, i);

        splx(s);
}