@@ 123,67 123,71 @@ TEXT _vund(SB), $-4 /* undefined */
B _vswitch
TEXT _vsvc(SB), $-4 /* reset or SWI or reserved */
- SUB $12, R13
- MOVW R14, 8(R13)
- MOVW CPSR, R14
- MOVW R14, 4(R13)
- MOVW $PsrMsvc, R14
- MOVW R14, (R13)
- MOVW 8(R13), R14
+ SUB $12, R13 /* make room for pc, psr, & type */
+ MOVW R14, 8(R13) /* ureg->pc = interupted PC */
+ MOVW SPSR, R14 /* ureg->psr = SPSR */
+ MOVW R14, 4(R13) /* ... */
+ MOVW $PsrMsvc, R14 /* ureg->type = PsrMsvc */
+ MOVW R14, (R13) /* ... */
B _vsaveu
TEXT _vpab(SB), $-4 /* prefetch abort */
- MOVM.IA [R0-R3], (R13)
- MOVW $PsrMabt, R0
+ MOVM.IA [R0-R3], (R13) /* free some working space */
+ MOVW $PsrMabt, R0 /* r0 = type */
B _vswitch
TEXT _vdab(SB), $-4 /* data abort */
- MOVM.IA [R0-R3], (R13)
- MOVW $(PsrMabt+1), R0
- B _vswitch
+ MOVM.IA [R0-R3], (R13) /* free some working space */
+ MOVW $(PsrMabt+1), R0 /* r0 = type */
+ B _vswitch /*
TEXT _virq(SB), $-4 /* IRQ */
- MOVM.IA [R0-R3], (R13)
- MOVW $PsrMirq, R0
+ MOVM.IA [R0-R3], (R13) /* free some working space */
+ MOVW $PsrMirq, R0 /* r0 = type */
B _vswitch
TEXT _vfiq(SB), $-4 /* FIQ */
- MOVM.IA [R0-R3], (R13)
- MOVW $PsrMfiq, R0
+ MOVM.IA [R0-R3], (R13) /* free some working space */
+ MOVW $PsrMfiq, R0 /* r0 = type */
B _vswitch
+ /*
+ * come here with type in R0 and R13 pointing above saved [r0-r3]
+ */
_vswitch: /* switch to svc, type in R0 */
- MOVW SPSR, R1 /* psr for ureg */
- MOVW R14, R2 /* saved pc for ureg */
+ MOVW SPSR, R1 /* SPSR for ureg */
+ MOVW R14, R2 /* interrupted pc for ureg */
MOVW R13, R3 /* [R0-R3] save area */
- MOVW CPSR, R14 /* switch */
+ /* switch to svc mode, we get new R13 pointing to top of svc stack */
+ MOVW CPSR, R14
BIC $PsrMask, R14
ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
MOVW R14, CPSR
- MOVM.DB.W [R0-R2], (R13) /* top of ureg */
- MOVM.IA (R3), [R0-R3] /* restore [R0-R3] */
+ MOVM.DB.W [R0-R2], (R13) /* set ureg->{pc, psr, type}; r13 points to ureg->type */
+ MOVM.IA (R3), [R0-R3] /* restore [R0-R3] from previous mode's stack */
B _vsaveu
- /* push the registers as in ureg */
+ /*
+ * come here with R13 pointing to ureg->type
+ */
_vsaveu:
- SUB $4, R13 /* save link */
- MOVW R14, (R13)
- MOVM.DB.W.S [R0-R14], (R13) /* save svc registers */
+ MOVM.DB.W.S [R0-R14], (R13) /* save user level registers, r13 points to ureg */
MOVW $setR12(SB), R12 /* the SB from user mode is different */
- MOVW R13, R0 /* argument is &ureg */
+
+ MOVW R13, R0 /* first arg is pointer to ureg */
SUB $8, R13 /* space for argument+link */
BL exception(SB)
-_vrfe:
- ADD $(8+4*15), R13 /* [r0-R14]+argument+link */
- MOVW (R13), R14 /* restore link */
- MOVW 8(R13), R0 /* restore SPSR */
- MOVW R0, SPSR
- MOVM.DB.S (R13), [R0-R14] /* restore registers */
- ADD $12, R13 /* skip saved link+type+SPSR */
+_vrfe:
+ ADD $(8+4*15), R13 /* r13 points to ureg->type */
+ MOVW 8(R13), R14 /* restore link */
+ MOVW 4(R13), R0 /* restore SPSR */
+ MOVW R0, SPSR /* ... */
+ MOVM.DB.S (R13), [R0-R14] /* restore registers */
+ ADD $8, R13 /* skip saved type+SPSR */
RFE /* MOVM.IA.S.W (R13), [R15] */
TEXT splhi(SB), $-4
@@ 12,6 12,28 @@
#include <cursor.h>
#include "screen.h"
+enum {
+ PCIS3 = 0x5333, /* PCI VID */
+
+ SAVAGE3D = 0x8A20, /* PCI DID */
+ SAVAGE3DMV = 0x8A21,
+ SAVAGE4 = 0x8A22,
+ SAVAGEMXMV = 0x8C10,
+ SAVAGEMX = 0x8C11,
+ SAVAGEIXMV = 0x8C12,
+ SAVAGEIX = 0x8C13,
+ SAVAGE2000 = 0x9102,
+
+ VIRGE = 0x5631,
+ VIRGEGX2 = 0x8A10,
+ VIRGEDXGX = 0x8A01,
+ VIRGEVX = 0x883D,
+ VIRGEMX = 0x8C01,
+ VIRGEMXP = 0x8C03,
+
+ AURORA64VPLUS = 0x8812,
+};
+
static int
s3pageset(VGAscr* scr, int page)
{
@@ 50,7 72,7 @@ s3page(VGAscr* scr, int page)
id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
switch(id){
- case 0x8A10: /* ViRGE/GX2 */
+ case VIRGEGX2:
break;
default:
@@ 78,7 100,7 @@ s3linear(VGAscr* scr, int* size, int* align)
mmiosize = 0;
mmiobase = 0;
mmioname = nil;
- if(p = pcimatch(nil, 0x5333, 0)){
+ if(p = pcimatch(nil, PCIS3, 0)){
for(i=0; i<nelem(p->mem); i++){
if(p->mem[i].size >= *size
&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)
@@ 92,7 114,7 @@ s3linear(VGAscr* scr, int* size, int* align)
*size = p->mem[i].size;
id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
- if(id == 0x8A22){ /* find Savage4 mmio */
+ if(id == SAVAGE4){ /* find Savage4 mmio */
/*
* We could assume that the MMIO registers
* will be in the screen segment and just use
@@ 108,6 130,7 @@ s3linear(VGAscr* scr, int* size, int* align)
if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){
mmiobase = p->mem[j].bar & ~0x0F;
mmiosize = 512*1024;
+ scr->mmio = (ulong*)upamalloc(mmiobase, mmiosize, 16*1024*1024);
mmioname = "savage4mmio";
break;
}
@@ 196,12 219,12 @@ s3load(VGAscr* scr, Cursor* curs)
id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
switch(id){
- case 0x5631: /* ViRGE */
- case 0x8A01: /* ViRGE/[DG]X */
- case 0x8A10: /* ViRGE/GX2 */
- case 0x883D: /* ViRGE/VX */
- case 0x8C12: /* Savage4/IX-MV */
- case 0x8A22: /* Savage4 */
+ case VIRGE:
+ case VIRGEDXGX:
+ case VIRGEGX2:
+ case VIRGEVX:
+ case SAVAGEIXMV:
+ case SAVAGE4:
p += scr->storage;
break;
@@ 244,12 267,12 @@ s3load(VGAscr* scr, Cursor* curs)
switch(id){
- case 0x5631: /* ViRGE */
- case 0x8A01: /* ViRGE/[DG]X */
- case 0x8A10: /* ViRGE/GX2 */
- case 0x883D: /* ViRGE/VX */
- case 0x8C12: /* Savage4/IX-MV */
- case 0x8A22: /* Savage4 */
+ case VIRGE:
+ case VIRGEDXGX:
+ case VIRGEGX2:
+ case VIRGEVX:
+ case SAVAGEIXMV:
+ case SAVAGE4:
break;
default:
@@ 526,9 549,11 @@ s3blank(int blank)
vgaxo(Seqx, CursorSyncCtl, x);
}
+
static void
s3drawinit(VGAscr *scr)
{
+ extern void savageinit(VGAscr*); /* vgasavage.c */
ulong id;
id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
@@ 542,13 567,17 @@ s3drawinit(VGAscr *scr)
* above.
*/
switch(id){
- case 0x5631: /* ViRGE */
- case 0x883D: /* ViRGE/VX */
- case 0x8A10: /* ViRGE/GX2 */
+ case VIRGE: /* ViRGE */
+ case VIRGEVX: /* ViRGE/VX */
+ case VIRGEGX2: /* ViRGE/GX2 */
scr->mmio = (ulong*)(scr->aperture+0x1000000);
scr->fill = hwfill;
scr->scroll = hwscroll;
/* scr->blank = hwblank; */
+ break;
+ case SAVAGE4:
+ savageinit(scr);
+ break;
}
}
@@ 0,0 1,490 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+ PCIS3 = 0x5333, /* PCI VID */
+
+ SAVAGE3D = 0x8A20, /* PCI DID */
+ SAVAGE3DMV = 0x8A21,
+ SAVAGE4 = 0x8A22,
+ SAVAGEMXMV = 0x8C10,
+ SAVAGEMX = 0x8C11,
+ SAVAGEIXMV = 0x8C12,
+ SAVAGEIX = 0x8C13,
+ SAVAGE2000 = 0x9102,
+
+ VIRGE = 0x5631,
+ VIRGEGX2 = 0x8A10,
+ VIRGEDXGX = 0x8A01,
+ VIRGEVX = 0x883D,
+ VIRGEMX = 0x8C01,
+ VIRGEMXP = 0x8C03,
+
+ AURORA64VPLUS = 0x8812,
+};
+
+/*
+ * Savage4 et al. acceleration.
+ *
+ * This is based only on the Savage4 documentation.
+ * It is expected to work on other Savage cards as well,
+ * but has not been tried.
+ *
+ * There are five ways to access the 2D graphics engine registers:
+ * - Old MMIO non-packed format
+ * - Old MMIO packed format
+ * - New MMIO non-packed format
+ * - New MMIO packed format
+ * - Burst Command Interface (BCI)
+ *
+ * Of these, the manual hints that the first three are deprecated,
+ * and it does not document any of those three well enough to use.
+ *
+ * I have tried for many hours with no success to understand the BCI
+ * interface well enough to use it. It is not well documented, and the
+ * XFree86 driver seems to completely contradict what little documentation
+ * there is.
+ *
+ * This leaves the packed new MMIO.
+ * The manual contradicts itself here, claming that the registers
+ * start at 0x2008100 as well as at 0x0008100 from the base of the
+ * mmio segment. Since the segment is only 512k, we assume that
+ * the latter is the correct offset.
+ *
+ * According to the manual, only 16-bit reads of the 2D registers
+ * are supported: 32-bit reads will return garbage in the upper word.
+ * 32-bit writes must be enabled explicitly.
+ *
+ * 32-bit reads of the status registers seem just fine.
+ */
+
+/* 2D graphics engine registers */
+enum {
+ SubsystemStatus = 0x8504, /* Subsystem Status: read only */
+ /* read only: whether we get interrupts on various events */
+ VsyncInt = 1<<0, /* vertical sync */
+ GeBusyInt = 1<<1, /* 2D graphics engine busy */
+ BfifoFullInt = 1<<2, /* BIU FIFO full */
+ BfifoEmptyInt = 1<<3, /* BIU FIFO empty */
+ CfifoFullInt = 1<<4, /* command FIFO full */
+ CfifoEmptyInt = 1<<5, /* command FIFO empty */
+ BciInt = 1<<6, /* BCI */
+ LpbInt = 1<<7, /* LPB */
+ CbHiInt = 1<<16, /* COB upper threshold */
+ CbLoInt = 1<<17, /* COB lower threshold */
+
+ SubsystemCtl = 0x8504, /* Subsystem Control: write only */
+ /* clear interrupts for various events */
+ VsyncClr = 1<<0,
+ GeBusyClr = 1<<1,
+ BfifoFullClr = 1<<2,
+ BfifoEmptyClr = 1<<3,
+ CfifoFullClr = 1<<4,
+ CfifoEmptyClr = 1<<5,
+ BciClr = 1<<6,
+ LpbClr = 1<<7,
+ CbHiClr = 1<<16,
+ CbLoClr = 1<<17,
+
+ /* enable interrupts for various events */
+ VsyncEna = 1<<8,
+ Busy2DEna = 1<<9,
+ BfifoFullEna = 1<<10,
+ BfifoEmptyEna = 1<<11,
+ CfifoFullEna = 1<<12,
+ CfifoEmptyEna = 1<<13,
+ SubsysBciEna = 1<<14,
+ CbHiEna = 1<<24,
+ CbLoEna = 1<<25,
+
+ /* 2D graphics engine software reset */
+ GeSoftReset = 1<<15,
+
+ FifoStatus = 0x8508, /* FIFO status: read only */
+ CwbEmpty = 1<<0, /* command write buffer empty */
+ CrbEmpty = 1<<1, /* command read buffer empty */
+ CobEmpty = 1<<2, /* command overflow buffer empty */
+ CfifoEmpty = 1<<3, /* command FIFO empty */
+ CwbFull = 1<<8, /* command write buffer full */
+ CrbFull = 1<<9, /* command read buffer full */
+ CobFull = 1<<10, /* command overflow buffer full */
+ CfifoFull = 1<<11, /* command FIFO full */
+
+ AdvFunCtl = 0x850C, /* Advanced Function Control: read/write */
+ GeEna = 1<<0, /* enable 2D/3D engine */
+ /*
+ * according to the manual, BigPixel should be
+ * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are
+ * used to figure out bpp example. however, it does bad things
+ * to the screen in 8bpp mode.
+ */
+ BigPixel = 1<<2, /* 8 or more bpp enhanced mode */
+ LaEna = 1<<3, /* linear addressing ena: or'ed with CR58_4 */
+ Mclk_2 = 0<<8, /* 2D engine clock divide: MCLK/2 */
+ Mclk_4 = 1<<8, /* " MCLK/4 */
+ Mclk = 2<<8, /* " MCLK */
+ /* Mclk = 3<<8, /* " MCLK */
+ Ic33mhz = 1<<16, /* Internal clock 33 MHz (instead of 66) */
+
+ WakeupReg = 0x8510, /* Wakeup: read/write */
+ WakeupBit = 1<<0, /* wake up: or'ed with 3C3_0 */
+
+ SourceY = 0x8100, /* UL corner of bitblt source */
+ SourceX = 0x8102, /* " */
+ RectY = 0x8100, /* UL corner of rectangle fill */
+ RectX = 0x8102, /* " */
+ DestY = 0x8108, /* UL corner of bitblt dest */
+ DestX = 0x810A, /* " */
+ Height = 0x8148, /* bitblt, image xfer rectangle height */
+ Width = 0x814A, /* bitblt, image xfer rectangle width */
+
+ StartY = 0x8100, /* Line draw: first point*/
+ StartX = 0x8102, /* " */
+ /*
+ * For line draws, the following must be programmed:
+ * axial step constant = 2*min(|dx|,|dy|)
+ * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)]
+ * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1
+ * [sic] when start X < end X
+ * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy|
+ * [sic] when start X >= end X
+ */
+ AxialStep = 0x8108,
+ DiagonalStep = 0x810A,
+ LineError = 0x8110,
+ MinorLength = 0x8148, /* pixel count along minor axis */
+ MajorLength = 0x814A, /* pixel count along major axis */
+
+ DrawCmd = 0x8118, /* Drawing Command: write only */
+ CmdMagic = 0<<1,
+ AcrossPlane = 1<<1, /* across the plane mode */
+ LastPixelOff = 1<<2, /* last pixel of line or vector draw not drawn */
+ Radial = 1<<3, /* enable radial direction (else axial) */
+ DoDraw = 1<<4, /* draw pixels (else only move current pos) */
+
+ DrawRight = 1<<5, /* axial drawing direction: left to right */
+ /* DrawLeft = 0<<5, */
+ MajorY = 1<<6,
+ /* MajorX = 0<<6, */
+ DrawDown = 1<<7,
+ /* DrawUp = 0<<7, */
+ Degree0 = 0<<5, /* drawing direction when Radial */
+ Degree45 = 1<<5,
+ /* ... */
+ Degree315 = 7<<5,
+
+ UseCPUData = 1<<8,
+
+ /* image write bus transfer width */
+ Bus8 = 0<<9,
+ Bus16 = 1<<9,
+ /*
+ * in Bus32 mode, doubleword bits beyond the image rect width are
+ * discarded. each line starts on a new doubleword.
+ * Bus32AP is intended for across-the-plane mode and
+ * rounds to byte boundaries instead.
+ */
+ Bus32 = 2<<9,
+ Bus32AP = 3<<9,
+
+ CmdNop = 0<<13, /* nop */
+ CmdLine = 1<<13, /* draw line */
+ CmdFill = 2<<13, /* fill rectangle */
+ CmdBitblt = 6<<13, /* bitblt */
+ CmdPatblt = 7<<13, /* 8x8 pattern blt */
+
+ SrcGBD = 0<<16,
+ SrcPBD = 1<<16,
+ SrcSBD = 2<<16,
+
+ DstGBD = 0<<18,
+ DstPBD = 1<<18,
+ DstSBD = 2<<18,
+
+ /* color sources, controls */
+ BgColor = 0x8120, /* Background Color: read/write */
+ FgColor = 0x8124, /* Foreground Color: read/write */
+ BitplaneWmask = 0x8128, /* Bitplane Write Mask: read/write */
+ BitplaneRmask = 0x812C, /* Bitplane Read Mask: read/write */
+ CmpColor = 0x8130, /* Color Compare: read/write */
+ BgMix = 0x8134,
+ FgMix = 0x8136,
+ MixNew = 7,
+ SrcBg = 0<<5,
+ SrcFg = 1<<5,
+ SrcCPU = 2<<5,
+ SrcDisp = 3<<5,
+
+ /* clipping rectangle */
+ TopScissors = 0x8138, /* Top Scissors: write only */
+ LeftScissors = 0x813A, /* Left Scissors: write only */
+ BottomScissors = 0x813C, /* Bottom Scissors: write only */
+ RightScissors = 0x813E, /* Right Scissors: write only */
+
+ /*
+ * Registers with Magic were indirectly accessed in older modes.
+ * It is not clear whether the Magic is necessary.
+ * In the older modes, writes to these registers were pipelined,
+ * so that you had to issue an engine command and wait for engine
+ * idle before reading a write back. It is not clear if this is
+ * still the case either.
+ */
+ PixCtl = 0x8140, /* Pixel Control: write only */
+ PixMagic = 0xA<<12,
+ PixMixFg = 0<<6, /* foreground mix register always */
+ PixMixCPU = 2<<6, /* CPU data determines mix register */
+ PixMixDisp = 3<<6, /* display data determines mix register */
+
+ MfMisc2Ctl = 0x8142, /* Multifunction Control Misc. 2: write only */
+ MfMisc2Magic = 0xD<<12,
+ DstShift = 0, /* 3 bits: destination base address in MB */
+ SrcShift = 4, /* 3 bits: source base address in MB */
+ WaitFifoEmpty = 2<<8, /* wait for write FIFO empty between draws */
+
+ MfMiscCtl = 0x8144, /* Multifunction Control Misc: write only */
+ MfMiscMagic = 0xE<<12,
+ UseHighBits = 1<<4, /* select upper 16 bits for 32-bit reg access */
+ ClipInvert = 1<<5, /* only touch pixels outside clip rectangle */
+ SkipSame = 0<<6, /* ignore pixels with color CmpColor */
+ SkipDifferent = 1<<7, /* ignore pixels not color CmpColor */
+ CmpEna = 1<<8, /* enable color compare */
+ W32Ena = 1<<9, /* enable 32-bit register write */
+ ClipDis = 1<<11, /* disable clipping */
+
+ /*
+ * The bitmap descriptor 1 registers contain the starting
+ * address of the bitmap (in bytes).
+ * The bitmap descriptor 2 registesr contain stride (in pixels)
+ * in the lower 16 bits, depth (in bits) in the next 8 bits,
+ * and whether block write is disabled.
+ */
+ GBD1 = 0x8168, /* Global Bitmap Descriptor 1: read/write */
+ GBD2 = 0x816C, /* Global Bitmap Descriptor 2: read/write */
+ /* GBD2-only bits */
+ BDS64 = 1<<0, /* bitmap descriptor size 64 bits */
+ GBDBciEna = 1<<3, /* BCI enable */
+ /* generic BD2 bits */
+ BlockWriteDis = 1<<28,
+ StrideShift = 0,
+ DepthShift = 16,
+
+ PBD1 = 0x8170, /* Primary Bitmap Descriptor: read/write */
+ PBD2 = 0x8174,
+ SBD1 = 0x8178, /* Secondary Bitmap Descriptor: read/write */
+ SBD2 = 0x817C,
+};
+
+/* mastered data transfer registers */
+
+/* configuration/status registers */
+enum {
+ XStatus0 = 0x48C00, /* Status Word 0: read only */
+ /* rev. A silicon differs from rev. B; use AltStatus0 */
+ CBEMaskA = 0x1FFFF, /* filled command buffer entries */
+ CBEShiftA = 0,
+ BciIdleA = 1<<17, /* BCI idle */
+ Ge3IdleA = 1<<18, /* 3D engine idle */
+ Ge2IdleA = 1<<19, /* 2D engine idle */
+ McpIdleA = 1<<20, /* motion compensation processor idle */
+ MeIdleA = 1<<22, /* master engine idle */
+ PfPendA = 1<<23, /* page flip pending */
+
+ CBEMaskB = 0x1FFFFF,
+ CBEShiftB = 0,
+ BciIdleB = 1<<25,
+ Ge3IdleB = 1<<26,
+ Ge2IdleB = 1<<27,
+ McpIdleB = 1<<28,
+ MeIdleB = 1<<30,
+ PfPendB = 1<<31,
+
+ AltStatus0 = 0x48C60, /* Alternate Status Word 0: read only */
+ CBEMask = 0x1FFFF,
+ CBEShift = 0,
+ /* the Savage4 manual says bits 17..23 for these, like Status0 */
+ /* empirically, they are bits 21..26 */
+ BciIdle = 1<<21,
+ Ge3Idle = 1<<22,
+ Ge2Idle = 1<<23,
+ McpIdle = 1<<24,
+ MeIdle = 1<<25,
+ PfPend = 1<<26,
+
+ XStatus1 = 0x48C04, /* Status Word 1: read only */
+ /* contains event tag 1, event tag 0, both 16 bits */
+
+ XStatus2 = 0x48C08, /* Status Word 2: read only */
+ ScanMask = 0x3FF, /* current scan line */
+ ScanShift = 0,
+ VRTMask = 0x7F100, /* vert retrace count */
+ VRTShift = 11,
+
+ CbThresh = 0x48C10, /* Command Buffer Thresholds: read/write */
+ CobOff = 0x48C14, /* Command Overflow Buffer: read/write */
+
+ CobPtr = 0x48C18, /* Command Overflow Buffer Pointers: read/write */
+ CobEna = 1<<2, /* command overflow buffer enable */
+ CobBciEna = 1<<3, /* BCI function enable */
+ CbeMask = 0xFFFF8000, /* no. of entries in command buffer */
+ CbeShift = 15,
+
+ AltStatus1 = 0x48C64, /* Alternate Status Word 1: read onnly */
+ /* contains current texture surface tag, vertex buffer tag */
+
+};
+
+enum {
+ Maxloop = 1<<23
+};
+
+static void
+savagewaitidle(VGAscr *scr)
+{
+ long x;
+ ulong *statw;
+
+ statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
+
+ for(x=0; x<Maxloop; x++)
+ if(*statw & Ge2Idle)
+ return;
+}
+
+static int
+savagefill(VGAscr *scr, Rectangle r, ulong sval)
+{
+ uchar *mmio;
+
+ mmio = (uchar*)scr->mmio;
+
+ *(ulong*)(mmio+FgColor) = sval;
+ *(ulong*)(mmio+BgColor) = sval;
+ *(ulong*)(mmio+BgMix) = SrcFg|MixNew;
+ *(ulong*)(mmio+FgMix) = SrcFg|MixNew;
+ *(ushort*)(mmio+RectY) = r.min.y;
+ *(ushort*)(mmio+RectX) = r.min.x;
+ *(ushort*)(mmio+Width) = Dx(r)-1;
+ *(ushort*)(mmio+Height) = Dy(r)-1;
+ *(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown;
+ savagewaitidle(scr);
+ return 1;
+}
+
+static int
+savagescroll(VGAscr *scr, Rectangle r, Rectangle sr)
+{
+ uchar *mmio;
+ ulong cmd;
+ Point dp, sp;
+
+ cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD;
+
+ if(r.min.x <= sr.min.x){
+ cmd |= DrawRight;
+ dp.x = r.min.x;
+ sp.x = sr.min.x;
+ }else{
+ dp.x = r.max.x-1;
+ sp.x = sr.max.x-1;
+ }
+
+ if(r.min.y <= sr.min.y){
+ cmd |= DrawDown;
+ dp.y = r.min.y;
+ sp.y = sr.min.y;
+ }else{
+ dp.y = r.max.y-1;
+ sp.y = sr.max.y-1;
+ }
+
+ mmio = (uchar*)scr->mmio;
+
+ *(ushort*)(mmio+SourceX) = sp.x;
+ *(ushort*)(mmio+SourceY) = sp.y;
+ *(ushort*)(mmio+DestX) = dp.x;
+ *(ushort*)(mmio+DestY) = dp.y;
+ *(ushort*)(mmio+Width) = Dx(r)-1;
+ *(ushort*)(mmio+Height) = Dy(r)-1;
+ *(ulong*)(mmio+BgMix) = SrcDisp|MixNew;
+ *(ulong*)(mmio+FgMix) = SrcDisp|MixNew;
+ *(ulong*)(mmio+DrawCmd) = cmd;
+ savagewaitidle(scr);
+ return 1;
+}
+
+void
+savageinit(VGAscr *scr)
+{
+ uchar *mmio;
+ ulong bd;
+
+ mmio = (uchar*)scr->mmio;
+
+ if(mmio == nil) {
+ print("savageinit: no mmio\n");
+ return;
+ }
+ /* 2D graphics engine software reset */
+ *(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
+ delay(2);
+ *(ushort*)(mmio+SubsystemCtl) = 0;
+ savagewaitidle(scr);
+
+ /* disable BCI as much as possible */
+ *(ushort*)(mmio+CobPtr) &= ~CobBciEna;
+ *(ushort*)(mmio+GBD2) &= ~GBDBciEna;
+ savagewaitidle(scr);
+
+ /* enable 32-bit writes, disable clipping */
+ *(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis;
+ savagewaitidle(scr);
+
+ /* enable all read, write planes */
+ *(ulong*)(mmio+BitplaneRmask) = ~0;
+ *(ulong*)(mmio+BitplaneWmask) = ~0;
+ savagewaitidle(scr);
+
+ /* turn on linear access, 2D engine */
+ *(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna;
+ savagewaitidle(scr);
+
+ /* set bitmap descriptors */
+ bd = (scr->gscreen->depth<<DepthShift) |
+ (Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis;
+
+ *(ulong*)(mmio+GBD1) = 0;
+ *(ulong*)(mmio+GBD2) = bd | BDS64;
+
+ *(ulong*)(mmio+PBD1) = 0;
+ *(ulong*)(mmio+PBD2) = bd;
+
+ *(ulong*)(mmio+SBD1) = 0;
+ *(ulong*)(mmio+SBD2) = bd;
+
+ /*
+ * For some reason, the GBD needs to get programmed twice,
+ * once before the PBD, SBD, and once after.
+ * This empirically makes it get set right.
+ * I would like to better understand the ugliness
+ * going on here.
+ */
+ *(ulong*)(mmio+GBD1) = 0;
+ *(ulong*)(mmio+GBD2) = bd;
+ *(ushort*)(mmio+GBD2+2) = bd>>16;
+ savagewaitidle(scr);
+
+ scr->fill = savagefill;
+ scr->scroll = savagescroll;
+}
+