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