xref: /plan9/sys/src/9/pc/vgasavage.c (revision 19dc9ffe8bf4bcdbaffae4c8f23820bdc027f8e2)
159cc4ca5SDavid du Colombier #include "u.h"
259cc4ca5SDavid du Colombier #include "../port/lib.h"
359cc4ca5SDavid du Colombier #include "mem.h"
459cc4ca5SDavid du Colombier #include "dat.h"
559cc4ca5SDavid du Colombier #include "fns.h"
659cc4ca5SDavid du Colombier #include "io.h"
759cc4ca5SDavid du Colombier #include "../port/error.h"
859cc4ca5SDavid du Colombier 
959cc4ca5SDavid du Colombier #define	Image	IMAGE
1059cc4ca5SDavid du Colombier #include <draw.h>
1159cc4ca5SDavid du Colombier #include <memdraw.h>
1259cc4ca5SDavid du Colombier #include <cursor.h>
1359cc4ca5SDavid du Colombier #include "screen.h"
1459cc4ca5SDavid du Colombier 
1559cc4ca5SDavid du Colombier enum {
1659cc4ca5SDavid du Colombier 	PCIS3		= 0x5333,		/* PCI VID */
1759cc4ca5SDavid du Colombier 
1859cc4ca5SDavid du Colombier 	SAVAGE3D	= 0x8A20,	/* PCI DID */
1959cc4ca5SDavid du Colombier 	SAVAGE3DMV	= 0x8A21,
2059cc4ca5SDavid du Colombier 	SAVAGE4		= 0x8A22,
210b459c2cSDavid du Colombier 	PROSAVAGEP	= 0x8A25,
220b459c2cSDavid du Colombier 	PROSAVAGEK	= 0x8A26,
23*19dc9ffeSDavid du Colombier 	PROSAVAGE8	= 0x8D04,
2459cc4ca5SDavid du Colombier 	SAVAGEMXMV	= 0x8C10,
2559cc4ca5SDavid du Colombier 	SAVAGEMX	= 0x8C11,
2659cc4ca5SDavid du Colombier 	SAVAGEIXMV	= 0x8C12,
2759cc4ca5SDavid du Colombier 	SAVAGEIX	= 0x8C13,
289a747e4fSDavid du Colombier 	SUPERSAVAGEIXC16 = 0x8C2E,
2959cc4ca5SDavid du Colombier 	SAVAGE2000	= 0x9102,
3059cc4ca5SDavid du Colombier 
3159cc4ca5SDavid du Colombier 	VIRGE		= 0x5631,
3259cc4ca5SDavid du Colombier 	VIRGEGX2	= 0x8A10,
3359cc4ca5SDavid du Colombier 	VIRGEDXGX	= 0x8A01,
3459cc4ca5SDavid du Colombier 	VIRGEVX		= 0x883D,
3559cc4ca5SDavid du Colombier 	VIRGEMX		= 0x8C01,
3659cc4ca5SDavid du Colombier 	VIRGEMXP	= 0x8C03,
3759cc4ca5SDavid du Colombier 
3859cc4ca5SDavid du Colombier 	AURORA64VPLUS	= 0x8812,
3959cc4ca5SDavid du Colombier };
4059cc4ca5SDavid du Colombier 
4159cc4ca5SDavid du Colombier /*
4259cc4ca5SDavid du Colombier  * Savage4 et al. acceleration.
4359cc4ca5SDavid du Colombier  *
4459cc4ca5SDavid du Colombier  * This is based only on the Savage4 documentation.
4559cc4ca5SDavid du Colombier  * It is expected to work on other Savage cards as well,
4659cc4ca5SDavid du Colombier  * but has not been tried.
4759cc4ca5SDavid du Colombier  *
4859cc4ca5SDavid du Colombier  * There are five ways to access the 2D graphics engine registers:
4959cc4ca5SDavid du Colombier  * 	- Old MMIO non-packed format
5059cc4ca5SDavid du Colombier  *	- Old MMIO packed format
5159cc4ca5SDavid du Colombier  *	- New MMIO non-packed format
5259cc4ca5SDavid du Colombier  *	- New MMIO packed format
5359cc4ca5SDavid du Colombier  *	- Burst Command Interface (BCI)
5459cc4ca5SDavid du Colombier  *
5559cc4ca5SDavid du Colombier  * Of these, the manual hints that the first three are deprecated,
5659cc4ca5SDavid du Colombier  * and it does not document any of those three well enough to use.
5759cc4ca5SDavid du Colombier  *
5859cc4ca5SDavid du Colombier  * I have tried for many hours with no success to understand the BCI
5959cc4ca5SDavid du Colombier  * interface well enough to use it.  It is not well documented, and the
6059cc4ca5SDavid du Colombier  * XFree86 driver seems to completely contradict what little documentation
6159cc4ca5SDavid du Colombier  * there is.
6259cc4ca5SDavid du Colombier  *
6359cc4ca5SDavid du Colombier  * This leaves the packed new MMIO.
6459cc4ca5SDavid du Colombier  * The manual contradicts itself here, claming that the registers
6559cc4ca5SDavid du Colombier  * start at 0x2008100 as well as at 0x0008100 from the base of the
6659cc4ca5SDavid du Colombier  * mmio segment.  Since the segment is only 512k, we assume that
6759cc4ca5SDavid du Colombier  * the latter is the correct offset.
6859cc4ca5SDavid du Colombier  *
6959cc4ca5SDavid du Colombier  * According to the manual, only 16-bit reads of the 2D registers
7059cc4ca5SDavid du Colombier  * are supported: 32-bit reads will return garbage in the upper word.
7159cc4ca5SDavid du Colombier  * 32-bit writes must be enabled explicitly.
7259cc4ca5SDavid du Colombier  *
7359cc4ca5SDavid du Colombier  * 32-bit reads of the status registers seem just fine.
7459cc4ca5SDavid du Colombier  */
7559cc4ca5SDavid du Colombier 
7659cc4ca5SDavid du Colombier /* 2D graphics engine registers for Savage4; others appear to be mostly the same */
7759cc4ca5SDavid du Colombier enum {
7859cc4ca5SDavid du Colombier 	SubsystemStatus = 0x8504,	/* Subsystem Status: read only */
7959cc4ca5SDavid du Colombier 	  /* read only: whether we get interrupts on various events */
8059cc4ca5SDavid du Colombier 	  VsyncInt		= 1<<0,		/* vertical sync */
8159cc4ca5SDavid du Colombier 	  GeBusyInt		= 1<<1,		/* 2D graphics engine busy */
8259cc4ca5SDavid du Colombier 	  BfifoFullInt	= 1<<2,		/* BIU FIFO full */
8359cc4ca5SDavid du Colombier 	  BfifoEmptyInt	= 1<<3,		/* BIU FIFO empty */
8459cc4ca5SDavid du Colombier 	  CfifoFullInt	= 1<<4,		/* command FIFO full */
8559cc4ca5SDavid du Colombier 	  CfifoEmptyInt	= 1<<5,		/* command FIFO empty */
8659cc4ca5SDavid du Colombier 	  BciInt		= 1<<6, 	/* BCI */
8759cc4ca5SDavid du Colombier 	  LpbInt		= 1<<7, 	/* LPB */
8859cc4ca5SDavid du Colombier 	  CbHiInt		= 1<<16,	/* COB upper threshold */
8959cc4ca5SDavid du Colombier 	  CbLoInt		= 1<<17,	/* COB lower threshold */
9059cc4ca5SDavid du Colombier 
9159cc4ca5SDavid du Colombier 	SubsystemCtl 	= 0x8504,	/* Subsystem Control: write only */
9259cc4ca5SDavid du Colombier 	  /* clear interrupts for various events */
9359cc4ca5SDavid du Colombier 	  VsyncClr		= 1<<0,
9459cc4ca5SDavid du Colombier 	  GeBusyClr		= 1<<1,
9559cc4ca5SDavid du Colombier 	  BfifoFullClr	= 1<<2,
9659cc4ca5SDavid du Colombier 	  BfifoEmptyClr	= 1<<3,
9759cc4ca5SDavid du Colombier 	  CfifoFullClr	= 1<<4,
9859cc4ca5SDavid du Colombier 	  CfifoEmptyClr	= 1<<5,
9959cc4ca5SDavid du Colombier 	  BciClr		= 1<<6,
10059cc4ca5SDavid du Colombier 	  LpbClr		= 1<<7,
10159cc4ca5SDavid du Colombier 	  CbHiClr		= 1<<16,
10259cc4ca5SDavid du Colombier 	  CbLoClr		= 1<<17,
10359cc4ca5SDavid du Colombier 
10459cc4ca5SDavid du Colombier 	  /* enable interrupts for various events */
10559cc4ca5SDavid du Colombier 	  VsyncEna		= 1<<8,
10659cc4ca5SDavid du Colombier 	  Busy2DEna		= 1<<9,
10759cc4ca5SDavid du Colombier 	  BfifoFullEna	= 1<<10,
10859cc4ca5SDavid du Colombier 	  BfifoEmptyEna	= 1<<11,
10959cc4ca5SDavid du Colombier 	  CfifoFullEna	= 1<<12,
11059cc4ca5SDavid du Colombier 	  CfifoEmptyEna	= 1<<13,
11159cc4ca5SDavid du Colombier 	  SubsysBciEna	= 1<<14,
11259cc4ca5SDavid du Colombier 	  CbHiEna		= 1<<24,
11359cc4ca5SDavid du Colombier 	  CbLoEna		= 1<<25,
11459cc4ca5SDavid du Colombier 
11559cc4ca5SDavid du Colombier 	  /* 2D graphics engine software reset */
11659cc4ca5SDavid du Colombier 	  GeSoftReset	= 1<<15,
11759cc4ca5SDavid du Colombier 
11859cc4ca5SDavid du Colombier 	FifoStatus		= 0x8508,	/* FIFO status: read only */
11959cc4ca5SDavid du Colombier 	  CwbEmpty		= 1<<0,		/* command write buffer empty */
12059cc4ca5SDavid du Colombier 	  CrbEmpty		= 1<<1,		/* command read buffer empty */
12159cc4ca5SDavid du Colombier 	  CobEmpty		= 1<<2,		/* command overflow buffer empty */
12259cc4ca5SDavid du Colombier 	  CfifoEmpty	= 1<<3,		/* command FIFO empty */
12359cc4ca5SDavid du Colombier 	  CwbFull		= 1<<8,		/* command write buffer full */
12459cc4ca5SDavid du Colombier 	  CrbFull		= 1<<9,		/* command read buffer full */
12559cc4ca5SDavid du Colombier 	  CobFull		= 1<<10,	/* command overflow buffer full */
12659cc4ca5SDavid du Colombier 	  CfifoFull		= 1<<11,	/* command FIFO full */
12759cc4ca5SDavid du Colombier 
12859cc4ca5SDavid du Colombier 	AdvFunCtl		= 0x850C,	/* Advanced Function Control: read/write */
12959cc4ca5SDavid du Colombier 	  GeEna			= 1<<0,	/* enable 2D/3D engine */
13059cc4ca5SDavid du Colombier 	  /*
13159cc4ca5SDavid du Colombier 	   * according to the manual, BigPixel should be
13259cc4ca5SDavid du Colombier 	   * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are
13359cc4ca5SDavid du Colombier 	   * used to figure out bpp example.  however, it does bad things
13459cc4ca5SDavid du Colombier 	   * to the screen in 8bpp mode.
13559cc4ca5SDavid du Colombier 	   */
13659cc4ca5SDavid du Colombier 	  BigPixel		= 1<<2,		/* 8 or more bpp enhanced mode */
13759cc4ca5SDavid du Colombier 	  LaEna			= 1<<3,		/* linear addressing ena: or'ed with CR58_4 */
13859cc4ca5SDavid du Colombier 	  Mclk_2		= 0<<8,		/* 2D engine clock divide: MCLK/2 */
13959cc4ca5SDavid du Colombier 	  Mclk_4		= 1<<8,		/* " MCLK/4 */
14059cc4ca5SDavid du Colombier 	  Mclk			= 2<<8,		/* " MCLK */
14159cc4ca5SDavid du Colombier 	/* Mclk			= 3<<8,		/* " MCLK */
14259cc4ca5SDavid du Colombier 	  Ic33mhz		= 1<<16,	/* Internal clock 33 MHz (instead of 66) */
14359cc4ca5SDavid du Colombier 
14459cc4ca5SDavid du Colombier 	WakeupReg		= 0x8510,	/* Wakeup: read/write */
14559cc4ca5SDavid du Colombier 	  WakeupBit		= 1<<0,	/* wake up: or'ed with 3C3_0 */
14659cc4ca5SDavid du Colombier 
14759cc4ca5SDavid du Colombier 	SourceY			= 0x8100,	/* UL corner of bitblt source */
14859cc4ca5SDavid du Colombier 	SourceX			= 0x8102,	/* " */
14959cc4ca5SDavid du Colombier 	RectY			= 0x8100,	/* UL corner of rectangle fill */
15059cc4ca5SDavid du Colombier 	RectX			= 0x8102,	/* " */
15159cc4ca5SDavid du Colombier 	DestY			= 0x8108,	/* UL corner of bitblt dest */
15259cc4ca5SDavid du Colombier 	DestX			= 0x810A,	/* " */
15359cc4ca5SDavid du Colombier 	Height			= 0x8148,	/* bitblt, image xfer rectangle height */
15459cc4ca5SDavid du Colombier 	Width			= 0x814A, 	/* bitblt, image xfer rectangle width */
15559cc4ca5SDavid du Colombier 
15659cc4ca5SDavid du Colombier 	StartY			= 0x8100,	/* Line draw: first point*/
15759cc4ca5SDavid du Colombier 	StartX			= 0x8102,	/* " */
15859cc4ca5SDavid du Colombier 	/*
15959cc4ca5SDavid du Colombier 	 * For line draws, the following must be programmed:
16059cc4ca5SDavid du Colombier 	 * axial step constant = 2*min(|dx|,|dy|)
16159cc4ca5SDavid du Colombier 	 * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)]
16259cc4ca5SDavid du Colombier 	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1
16359cc4ca5SDavid du Colombier 	 *	[sic] when start X < end X
16459cc4ca5SDavid du Colombier 	 * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy|
16559cc4ca5SDavid du Colombier 	 *  [sic] when start X >= end X
16659cc4ca5SDavid du Colombier 	 */
16759cc4ca5SDavid du Colombier 	AxialStep		= 0x8108,
16859cc4ca5SDavid du Colombier 	DiagonalStep	= 0x810A,
16959cc4ca5SDavid du Colombier 	LineError		= 0x8110,
17059cc4ca5SDavid du Colombier 	MinorLength		= 0x8148,	/* pixel count along minor axis */
17159cc4ca5SDavid du Colombier 	MajorLength		= 0x814A,	/* pixel count along major axis */
17259cc4ca5SDavid du Colombier 
17359cc4ca5SDavid du Colombier 	DrawCmd			= 0x8118,	/* Drawing Command: write only */
17459cc4ca5SDavid du Colombier 	  CmdMagic		= 0<<1,
17559cc4ca5SDavid du Colombier 	  AcrossPlane	= 1<<1,		/* across the plane mode */
17659cc4ca5SDavid du Colombier 	  LastPixelOff	= 1<<2,		/* last pixel of line or vector draw not drawn */
17759cc4ca5SDavid du Colombier 	  Radial		= 1<<3,		/* enable radial direction (else axial) */
17859cc4ca5SDavid du Colombier 	  DoDraw		= 1<<4,		/* draw pixels (else only move current pos) */
17959cc4ca5SDavid du Colombier 
18059cc4ca5SDavid du Colombier 	  DrawRight		= 1<<5,		/* axial drawing direction: left to right */
18159cc4ca5SDavid du Colombier 	  /* DrawLeft		= 0<<5, */
18259cc4ca5SDavid du Colombier 	  MajorY		= 1<<6,
18359cc4ca5SDavid du Colombier 	  /* MajorX		= 0<<6, */
18459cc4ca5SDavid du Colombier 	  DrawDown		= 1<<7,
18559cc4ca5SDavid du Colombier 	  /* DrawUp		= 0<<7, */
18659cc4ca5SDavid du Colombier 	  Degree0		= 0<<5,		/* drawing direction when Radial */
18759cc4ca5SDavid du Colombier 	  Degree45		= 1<<5,
18859cc4ca5SDavid du Colombier 		/* ... */
18959cc4ca5SDavid du Colombier 	  Degree315		= 7<<5,
19059cc4ca5SDavid du Colombier 
19159cc4ca5SDavid du Colombier 	  UseCPUData	= 1<<8,
19259cc4ca5SDavid du Colombier 
19359cc4ca5SDavid du Colombier 	  /* image write bus transfer width */
19459cc4ca5SDavid du Colombier 	  Bus8			= 0<<9,
19559cc4ca5SDavid du Colombier 	  Bus16			= 1<<9,
19659cc4ca5SDavid du Colombier 	  /*
19759cc4ca5SDavid du Colombier 	   * in Bus32 mode, doubleword bits beyond the image rect width are
19859cc4ca5SDavid du Colombier 	   * discarded.  each line starts on a new doubleword.
19959cc4ca5SDavid du Colombier 	   * Bus32AP is intended for across-the-plane mode and
20059cc4ca5SDavid du Colombier 	   * rounds to byte boundaries instead.
20159cc4ca5SDavid du Colombier 	   */
20259cc4ca5SDavid du Colombier 	  Bus32			= 2<<9,
20359cc4ca5SDavid du Colombier 	  Bus32AP		= 3<<9,
20459cc4ca5SDavid du Colombier 
20559cc4ca5SDavid du Colombier 	  CmdNop		= 0<<13,	/* nop */
20659cc4ca5SDavid du Colombier 	  CmdLine		= 1<<13,	/* draw line */
20759cc4ca5SDavid du Colombier 	  CmdFill		= 2<<13,	/* fill rectangle */
20859cc4ca5SDavid du Colombier 	  CmdBitblt		= 6<<13,	/* bitblt */
20959cc4ca5SDavid du Colombier 	  CmdPatblt		= 7<<13,	/* 8x8 pattern blt */
21059cc4ca5SDavid du Colombier 
21159cc4ca5SDavid du Colombier 	  SrcGBD		= 0<<16,
21259cc4ca5SDavid du Colombier 	  SrcPBD		= 1<<16,
21359cc4ca5SDavid du Colombier 	  SrcSBD		= 2<<16,
21459cc4ca5SDavid du Colombier 
21559cc4ca5SDavid du Colombier 	  DstGBD		= 0<<18,
21659cc4ca5SDavid du Colombier 	  DstPBD		= 1<<18,
21759cc4ca5SDavid du Colombier 	  DstSBD		= 2<<18,
21859cc4ca5SDavid du Colombier 
21959cc4ca5SDavid du Colombier 	/* color sources, controls */
22059cc4ca5SDavid du Colombier 	BgColor			= 0x8120,	/* Background Color: read/write */
22159cc4ca5SDavid du Colombier 	FgColor			= 0x8124,	/* Foreground Color: read/write */
22259cc4ca5SDavid du Colombier 	BitplaneWmask	= 0x8128,	/* Bitplane Write Mask: read/write */
22359cc4ca5SDavid du Colombier 	BitplaneRmask	= 0x812C,	/* Bitplane Read Mask: read/write */
22459cc4ca5SDavid du Colombier 	CmpColor		= 0x8130,	/* Color Compare: read/write */
22559cc4ca5SDavid du Colombier 	BgMix			= 0x8134,
22659cc4ca5SDavid du Colombier 	FgMix			= 0x8136,
22759cc4ca5SDavid du Colombier 	  MixNew		= 7,
22859cc4ca5SDavid du Colombier 	  SrcBg			= 0<<5,
22959cc4ca5SDavid du Colombier 	  SrcFg			= 1<<5,
23059cc4ca5SDavid du Colombier 	  SrcCPU		= 2<<5,
23159cc4ca5SDavid du Colombier 	  SrcDisp		= 3<<5,
23259cc4ca5SDavid du Colombier 
23359cc4ca5SDavid du Colombier 	/* clipping rectangle */
23459cc4ca5SDavid du Colombier 	TopScissors		= 0x8138,	/* Top Scissors: write only */
23559cc4ca5SDavid du Colombier 	LeftScissors	= 0x813A,	/* Left Scissors: write only */
23659cc4ca5SDavid du Colombier 	BottomScissors	= 0x813C,	/* Bottom Scissors: write only */
23759cc4ca5SDavid du Colombier 	RightScissors	= 0x813E,	/* Right Scissors: write only */
23859cc4ca5SDavid du Colombier 
23959cc4ca5SDavid du Colombier 	/*
24059cc4ca5SDavid du Colombier 	 * Registers with Magic were indirectly accessed in older modes.
24159cc4ca5SDavid du Colombier 	 * It is not clear whether the Magic is necessary.
24259cc4ca5SDavid du Colombier 	 * In the older modes, writes to these registers were pipelined,
24359cc4ca5SDavid du Colombier 	 * so that you had to issue an engine command and wait for engine
24459cc4ca5SDavid du Colombier 	 * idle before reading a write back.  It is not clear if this is
24559cc4ca5SDavid du Colombier 	 * still the case either.
24659cc4ca5SDavid du Colombier 	 */
24759cc4ca5SDavid du Colombier 	PixCtl			= 0x8140,	/* Pixel Control: write only */
24859cc4ca5SDavid du Colombier 	  PixMagic		= 0xA<<12,
24959cc4ca5SDavid du Colombier 	  PixMixFg		= 0<<6,		/* foreground mix register always */
25059cc4ca5SDavid du Colombier 	  PixMixCPU		= 2<<6,		/* CPU data determines mix register */
25159cc4ca5SDavid du Colombier 	  PixMixDisp	= 3<<6,		/* display data determines mix register */
25259cc4ca5SDavid du Colombier 
25359cc4ca5SDavid du Colombier 	MfMisc2Ctl		= 0x8142,	/* Multifunction Control Misc. 2: write only */
25459cc4ca5SDavid du Colombier 	  MfMisc2Magic	= 0xD<<12,
25559cc4ca5SDavid du Colombier 	  DstShift		= 0,		/* 3 bits: destination base address in MB */
25659cc4ca5SDavid du Colombier 	  SrcShift		= 4,		/* 3 bits: source base address in MB */
25759cc4ca5SDavid du Colombier 	  WaitFifoEmpty	= 2<<8,		/* wait for write FIFO empty between draws */
25859cc4ca5SDavid du Colombier 
25959cc4ca5SDavid du Colombier 	MfMiscCtl		= 0x8144,	/* Multifunction Control Misc: write only */
26059cc4ca5SDavid du Colombier 	  MfMiscMagic	= 0xE<<12,
26159cc4ca5SDavid du Colombier 	  UseHighBits	= 1<<4,		/* select upper 16 bits for 32-bit reg access */
26259cc4ca5SDavid du Colombier 	  ClipInvert	= 1<<5,		/* only touch pixels outside clip rectangle */
26359cc4ca5SDavid du Colombier 	  SkipSame		= 0<<6,		/* ignore pixels with color CmpColor */
26459cc4ca5SDavid du Colombier 	  SkipDifferent	= 1<<7,		/* ignore pixels not color CmpColor */
26559cc4ca5SDavid du Colombier 	  CmpEna		= 1<<8,		/* enable color compare */
26659cc4ca5SDavid du Colombier 	  W32Ena		= 1<<9,		/* enable 32-bit register write */
26759cc4ca5SDavid du Colombier 	  ClipDis		= 1<<11,	/* disable clipping */
26859cc4ca5SDavid du Colombier 
26959cc4ca5SDavid du Colombier 	/*
27059cc4ca5SDavid du Colombier 	 * The bitmap descriptor 1 registers contain the starting
27159cc4ca5SDavid du Colombier 	 * address of the bitmap (in bytes).
27259cc4ca5SDavid du Colombier 	 * The bitmap descriptor 2 registesr contain stride (in pixels)
27359cc4ca5SDavid du Colombier 	 * in the lower 16 bits, depth (in bits) in the next 8 bits,
27459cc4ca5SDavid du Colombier 	 * and whether block write is disabled.
27559cc4ca5SDavid du Colombier 	 */
27659cc4ca5SDavid du Colombier 	GBD1			= 0x8168,	/* Global Bitmap Descriptor 1: read/write */
27759cc4ca5SDavid du Colombier 	GBD2			= 0x816C,	/* Global Bitmap Descriptor 2: read/write */
27859cc4ca5SDavid du Colombier 	  /* GBD2-only bits */
27959cc4ca5SDavid du Colombier 	  BDS64			= 1<<0,		/* bitmap descriptor size 64 bits */
28059cc4ca5SDavid du Colombier 	  GBDBciEna		= 1<<3,		/* BCI enable */
28159cc4ca5SDavid du Colombier 	  /* generic BD2 bits */
28259cc4ca5SDavid du Colombier 	  BlockWriteDis	= 1<<28,
28359cc4ca5SDavid du Colombier 	  StrideShift	= 0,
28459cc4ca5SDavid du Colombier 	  DepthShift	= 16,
28559cc4ca5SDavid du Colombier 
28659cc4ca5SDavid du Colombier 	PBD1			= 0x8170,	/* Primary Bitmap Descriptor: read/write */
28759cc4ca5SDavid du Colombier 	PBD2			= 0x8174,
28859cc4ca5SDavid du Colombier 	SBD1			= 0x8178,	/* Secondary Bitmap Descriptor: read/write */
28959cc4ca5SDavid du Colombier 	SBD2			= 0x817C,
29059cc4ca5SDavid du Colombier };
29159cc4ca5SDavid du Colombier 
29259cc4ca5SDavid du Colombier /* mastered data transfer registers */
29359cc4ca5SDavid du Colombier 
29459cc4ca5SDavid du Colombier /* configuration/status registers */
29559cc4ca5SDavid du Colombier enum {
29659cc4ca5SDavid du Colombier 	XStatus0			= 0x48C00,	/* Status Word 0: read only */
29759cc4ca5SDavid du Colombier 	  /* rev. A silicon differs from rev. B; use AltStatus0 */
29859cc4ca5SDavid du Colombier 	  CBEMaskA		= 0x1FFFF,	/* filled command buffer entries */
29959cc4ca5SDavid du Colombier 	  CBEShiftA		= 0,
30059cc4ca5SDavid du Colombier 	  BciIdleA		= 1<<17,	/* BCI idle */
30159cc4ca5SDavid du Colombier 	  Ge3IdleA		= 1<<18,	/* 3D engine idle */
30259cc4ca5SDavid du Colombier 	  Ge2IdleA		= 1<<19,	/* 2D engine idle */
30359cc4ca5SDavid du Colombier 	  McpIdleA		= 1<<20,	/* motion compensation processor idle */
30459cc4ca5SDavid du Colombier 	  MeIdleA		= 1<<22,	/* master engine idle */
30559cc4ca5SDavid du Colombier 	  PfPendA		= 1<<23,	/* page flip pending */
30659cc4ca5SDavid du Colombier 
30759cc4ca5SDavid du Colombier 	  CBEMaskB		= 0x1FFFFF,
30859cc4ca5SDavid du Colombier 	  CBEShiftB		= 0,
30959cc4ca5SDavid du Colombier 	  BciIdleB		= 1<<25,
31059cc4ca5SDavid du Colombier 	  Ge3IdleB		= 1<<26,
31159cc4ca5SDavid du Colombier 	  Ge2IdleB		= 1<<27,
31259cc4ca5SDavid du Colombier 	  McpIdleB		= 1<<28,
31359cc4ca5SDavid du Colombier 	  MeIdleB		= 1<<30,
31459cc4ca5SDavid du Colombier 	  PfPendB		= 1<<31,
31559cc4ca5SDavid du Colombier 
31659cc4ca5SDavid du Colombier 	AltStatus0		= 0x48C60,	/* Alternate Status Word 0: read only */
31759cc4ca5SDavid du Colombier 	  CBEMask		= 0x1FFFF,
31859cc4ca5SDavid du Colombier 	  CBEShift		= 0,
31959cc4ca5SDavid du Colombier 	  /* the Savage4 manual says bits 17..23 for these, like Status0 */
32059cc4ca5SDavid du Colombier 	  /* empirically, they are bits 21..26 */
32159cc4ca5SDavid du Colombier 	  BciIdle		= 1<<21,
32259cc4ca5SDavid du Colombier 	  Ge3Idle		= 1<<22,
32359cc4ca5SDavid du Colombier 	  Ge2Idle		= 1<<23,
32459cc4ca5SDavid du Colombier 	  McpIdle		= 1<<24,
32559cc4ca5SDavid du Colombier 	  MeIdle		= 1<<25,
32659cc4ca5SDavid du Colombier 	  PfPend		= 1<<26,
32759cc4ca5SDavid du Colombier 
32859cc4ca5SDavid du Colombier 	XStatus1			= 0x48C04,	/* Status Word 1: read only */
32959cc4ca5SDavid du Colombier 	  /* contains event tag 1, event tag 0, both 16 bits */
33059cc4ca5SDavid du Colombier 
33159cc4ca5SDavid du Colombier 	XStatus2			= 0x48C08,	/* Status Word 2: read only */
33259cc4ca5SDavid du Colombier 	  ScanMask		= 0x3FF,	/* current scan line */
33359cc4ca5SDavid du Colombier 	  ScanShift		= 0,
33459cc4ca5SDavid du Colombier 	  VRTMask		= 0x7F100,	/* vert retrace count */
33559cc4ca5SDavid du Colombier 	  VRTShift		= 11,
33659cc4ca5SDavid du Colombier 
33759cc4ca5SDavid du Colombier 	CbThresh		= 0x48C10,	/* Command Buffer Thresholds: read/write */
33859cc4ca5SDavid du Colombier 	CobOff			= 0x48C14,	/* Command Overflow Buffer: read/write */
33959cc4ca5SDavid du Colombier 
34059cc4ca5SDavid du Colombier 	CobPtr			= 0x48C18,	/* Command Overflow Buffer Pointers: read/write */
34159cc4ca5SDavid du Colombier 	  CobEna		= 1<<2,		/* command overflow buffer enable */
34259cc4ca5SDavid du Colombier 	  CobBciEna		= 1<<3,		/* BCI function enable */
34359cc4ca5SDavid du Colombier 	  CbeMask		= 0xFFFF8000,	/* no. of entries in command buffer */
34459cc4ca5SDavid du Colombier 	  CbeShift		= 15,
34559cc4ca5SDavid du Colombier 
34659cc4ca5SDavid du Colombier 	AltStatus1		= 0x48C64,	/* Alternate Status Word 1: read onnly */
34759cc4ca5SDavid du Colombier 	  /* contains current texture surface tag, vertex buffer tag */
34859cc4ca5SDavid du Colombier 
34959cc4ca5SDavid du Colombier };
35059cc4ca5SDavid du Colombier 
35159cc4ca5SDavid du Colombier struct {
35259cc4ca5SDavid du Colombier 	ulong idletimeout;
3539a747e4fSDavid du Colombier 	ulong tostatw[16];
35459cc4ca5SDavid du Colombier } savagestats;
35559cc4ca5SDavid du Colombier 
35659cc4ca5SDavid du Colombier enum {
3579a747e4fSDavid du Colombier 	Maxloop = 1<<20
35859cc4ca5SDavid du Colombier };
35959cc4ca5SDavid du Colombier 
36059cc4ca5SDavid du Colombier static void
savagewaitidle(VGAscr * scr)36159cc4ca5SDavid du Colombier savagewaitidle(VGAscr *scr)
36259cc4ca5SDavid du Colombier {
36359cc4ca5SDavid du Colombier 	long x;
36459cc4ca5SDavid du Colombier 	ulong *statw, mask, goal;
36559cc4ca5SDavid du Colombier 
36659cc4ca5SDavid du Colombier 	switch(scr->id){
36759cc4ca5SDavid du Colombier 	case SAVAGE4:
3680b459c2cSDavid du Colombier 	case PROSAVAGEP:
3690b459c2cSDavid du Colombier 	case PROSAVAGEK:
370*19dc9ffeSDavid du Colombier 	case PROSAVAGE8:
37159cc4ca5SDavid du Colombier 		/* wait for engine idle and FIFO empty */
37259cc4ca5SDavid du Colombier 		statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
3739a747e4fSDavid du Colombier 		mask = CBEMask | Ge2Idle;
3749a747e4fSDavid du Colombier 		goal = Ge2Idle;
37559cc4ca5SDavid du Colombier 		break;
37659cc4ca5SDavid du Colombier 	/* case SAVAGEMXMV: ? */
37759cc4ca5SDavid du Colombier 	/* case SAVAGEMX: ? */
37859cc4ca5SDavid du Colombier 	/* case SAVAGEIX: ? */
3799a747e4fSDavid du Colombier 	case SUPERSAVAGEIXC16:
38059cc4ca5SDavid du Colombier 	case SAVAGEIXMV:
3819a747e4fSDavid du Colombier 	case SAVAGEMXMV:
38259cc4ca5SDavid du Colombier 		/* wait for engine idle and FIFO empty */
38359cc4ca5SDavid du Colombier 		statw = (ulong*)((uchar*)scr->mmio+XStatus0);
3849a747e4fSDavid du Colombier 		mask = CBEMaskA | Ge2IdleA;
3859a747e4fSDavid du Colombier 		goal = Ge2IdleA;
38659cc4ca5SDavid du Colombier 		break;
38759cc4ca5SDavid du Colombier 	default:
38859cc4ca5SDavid du Colombier 		/*
38959cc4ca5SDavid du Colombier 		 * best we can do: can't print or we'll call ourselves.
39059cc4ca5SDavid du Colombier 		 * savageinit is supposed to not let this happen.
39159cc4ca5SDavid du Colombier 		 */
39259cc4ca5SDavid du Colombier 		return;
39359cc4ca5SDavid du Colombier 	}
39459cc4ca5SDavid du Colombier 
39559cc4ca5SDavid du Colombier 	for(x=0; x<Maxloop; x++)
39659cc4ca5SDavid du Colombier 		if((*statw & mask) == goal)
39759cc4ca5SDavid du Colombier 			return;
39859cc4ca5SDavid du Colombier 
3999a747e4fSDavid du Colombier 	savagestats.tostatw[savagestats.idletimeout++&15] = *statw;
4009a747e4fSDavid du Colombier 	savagestats.tostatw[savagestats.idletimeout++&15] = (ulong)statw;
40159cc4ca5SDavid du Colombier }
40259cc4ca5SDavid du Colombier 
40359cc4ca5SDavid du Colombier static int
savagefill(VGAscr * scr,Rectangle r,ulong sval)40459cc4ca5SDavid du Colombier savagefill(VGAscr *scr, Rectangle r, ulong sval)
40559cc4ca5SDavid du Colombier {
40659cc4ca5SDavid du Colombier 	uchar *mmio;
40759cc4ca5SDavid du Colombier 
40859cc4ca5SDavid du Colombier 	mmio = (uchar*)scr->mmio;
40959cc4ca5SDavid du Colombier 
41059cc4ca5SDavid du Colombier 	*(ulong*)(mmio+FgColor) = sval;
41159cc4ca5SDavid du Colombier 	*(ulong*)(mmio+BgColor) = sval;
41259cc4ca5SDavid du Colombier 	*(ulong*)(mmio+BgMix) = SrcFg|MixNew;
41359cc4ca5SDavid du Colombier 	*(ulong*)(mmio+FgMix) = SrcFg|MixNew;
41459cc4ca5SDavid du Colombier 	*(ushort*)(mmio+RectY) = r.min.y;
41559cc4ca5SDavid du Colombier 	*(ushort*)(mmio+RectX) = r.min.x;
41659cc4ca5SDavid du Colombier 	*(ushort*)(mmio+Width) = Dx(r)-1;
41759cc4ca5SDavid du Colombier 	*(ushort*)(mmio+Height) = Dy(r)-1;
41859cc4ca5SDavid du Colombier 	*(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown;
41959cc4ca5SDavid du Colombier 	savagewaitidle(scr);
42059cc4ca5SDavid du Colombier 	return 1;
42159cc4ca5SDavid du Colombier }
42259cc4ca5SDavid du Colombier 
42359cc4ca5SDavid du Colombier static int
savagescroll(VGAscr * scr,Rectangle r,Rectangle sr)42459cc4ca5SDavid du Colombier savagescroll(VGAscr *scr, Rectangle r, Rectangle sr)
42559cc4ca5SDavid du Colombier {
42659cc4ca5SDavid du Colombier 	uchar *mmio;
42759cc4ca5SDavid du Colombier 	ulong cmd;
42859cc4ca5SDavid du Colombier 	Point dp, sp;
42959cc4ca5SDavid du Colombier 
43059cc4ca5SDavid du Colombier 	cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD;
43159cc4ca5SDavid du Colombier 
43259cc4ca5SDavid du Colombier 	if(r.min.x <= sr.min.x){
43359cc4ca5SDavid du Colombier 		cmd |= DrawRight;
43459cc4ca5SDavid du Colombier 		dp.x = r.min.x;
43559cc4ca5SDavid du Colombier 		sp.x = sr.min.x;
43659cc4ca5SDavid du Colombier 	}else{
43759cc4ca5SDavid du Colombier 		dp.x = r.max.x-1;
43859cc4ca5SDavid du Colombier 		sp.x = sr.max.x-1;
43959cc4ca5SDavid du Colombier 	}
44059cc4ca5SDavid du Colombier 
44159cc4ca5SDavid du Colombier 	if(r.min.y <= sr.min.y){
44259cc4ca5SDavid du Colombier 		cmd |= DrawDown;
44359cc4ca5SDavid du Colombier 		dp.y = r.min.y;
44459cc4ca5SDavid du Colombier 		sp.y = sr.min.y;
44559cc4ca5SDavid du Colombier 	}else{
44659cc4ca5SDavid du Colombier 		dp.y = r.max.y-1;
44759cc4ca5SDavid du Colombier 		sp.y = sr.max.y-1;
44859cc4ca5SDavid du Colombier 	}
44959cc4ca5SDavid du Colombier 
45059cc4ca5SDavid du Colombier 	mmio = (uchar*)scr->mmio;
45159cc4ca5SDavid du Colombier 
45259cc4ca5SDavid du Colombier 	*(ushort*)(mmio+SourceX) = sp.x;
45359cc4ca5SDavid du Colombier 	*(ushort*)(mmio+SourceY) = sp.y;
45459cc4ca5SDavid du Colombier 	*(ushort*)(mmio+DestX) = dp.x;
45559cc4ca5SDavid du Colombier 	*(ushort*)(mmio+DestY) = dp.y;
45659cc4ca5SDavid du Colombier 	*(ushort*)(mmio+Width) = Dx(r)-1;
45759cc4ca5SDavid du Colombier 	*(ushort*)(mmio+Height) = Dy(r)-1;
45859cc4ca5SDavid du Colombier 	*(ulong*)(mmio+BgMix) = SrcDisp|MixNew;
45959cc4ca5SDavid du Colombier 	*(ulong*)(mmio+FgMix) = SrcDisp|MixNew;
46059cc4ca5SDavid du Colombier 	*(ulong*)(mmio+DrawCmd) = cmd;
46159cc4ca5SDavid du Colombier 	savagewaitidle(scr);
46259cc4ca5SDavid du Colombier 	return 1;
46359cc4ca5SDavid du Colombier }
46459cc4ca5SDavid du Colombier 
4659a747e4fSDavid du Colombier static void
savageblank(VGAscr *,int blank)4669a747e4fSDavid du Colombier savageblank(VGAscr*, int blank)
4679a747e4fSDavid du Colombier {
4689a747e4fSDavid du Colombier 	uchar seqD;
4699a747e4fSDavid du Colombier 
4709a747e4fSDavid du Colombier 	/*
4719a747e4fSDavid du Colombier 	 * Will handle DPMS to monitor
4729a747e4fSDavid du Colombier 	 */
4739a747e4fSDavid du Colombier 	vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06);
4749a747e4fSDavid du Colombier 	seqD = vgaxi(Seqx, 0xD);
4759a747e4fSDavid du Colombier 	seqD &= 0x03;
4769a747e4fSDavid du Colombier 	if(blank)
4779a747e4fSDavid du Colombier 		seqD |= 0x50;
4789a747e4fSDavid du Colombier 	vgaxo(Seqx, 0xD, seqD);
4799a747e4fSDavid du Colombier 
4809a747e4fSDavid du Colombier 	/*
4819a747e4fSDavid du Colombier 	 * Will handle LCD
4829a747e4fSDavid du Colombier 	 */
4839a747e4fSDavid du Colombier 	if(blank)
4849a747e4fSDavid du Colombier 		vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10);
4859a747e4fSDavid du Colombier 	else
4869a747e4fSDavid du Colombier 		vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10);
4879a747e4fSDavid du Colombier }
4889a747e4fSDavid du Colombier 
4899a747e4fSDavid du Colombier 
49059cc4ca5SDavid du Colombier void
savageinit(VGAscr * scr)49159cc4ca5SDavid du Colombier savageinit(VGAscr *scr)
49259cc4ca5SDavid du Colombier {
49359cc4ca5SDavid du Colombier 	uchar *mmio;
49459cc4ca5SDavid du Colombier 	ulong bd;
49559cc4ca5SDavid du Colombier 
49659cc4ca5SDavid du Colombier 	/* if you add chip IDs here be sure to update savagewaitidle */
49759cc4ca5SDavid du Colombier 	switch(scr->id){
49859cc4ca5SDavid du Colombier 	case SAVAGE4:
4990b459c2cSDavid du Colombier 	case PROSAVAGEP:
5000b459c2cSDavid du Colombier 	case PROSAVAGEK:
501*19dc9ffeSDavid du Colombier 	case PROSAVAGE8:
50259cc4ca5SDavid du Colombier 	case SAVAGEIXMV:
5039a747e4fSDavid du Colombier 	case SUPERSAVAGEIXC16:
5049a747e4fSDavid du Colombier 	case SAVAGEMXMV:
50559cc4ca5SDavid du Colombier 		break;
50659cc4ca5SDavid du Colombier 	default:
50759cc4ca5SDavid du Colombier 		print("unknown savage %.4lux\n", scr->id);
50859cc4ca5SDavid du Colombier 		return;
50959cc4ca5SDavid du Colombier 	}
51059cc4ca5SDavid du Colombier 
51159cc4ca5SDavid du Colombier 	mmio = (uchar*)scr->mmio;
51259cc4ca5SDavid du Colombier 	if(mmio == nil) {
51359cc4ca5SDavid du Colombier 		print("savageinit: no mmio\n");
51459cc4ca5SDavid du Colombier 		return;
51559cc4ca5SDavid du Colombier 	}
51659cc4ca5SDavid du Colombier 
51759cc4ca5SDavid du Colombier 	/* 2D graphics engine software reset */
51859cc4ca5SDavid du Colombier 	*(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
51959cc4ca5SDavid du Colombier 	delay(2);
52059cc4ca5SDavid du Colombier 	*(ushort*)(mmio+SubsystemCtl) = 0;
52159cc4ca5SDavid du Colombier 	savagewaitidle(scr);
52259cc4ca5SDavid du Colombier 
52359cc4ca5SDavid du Colombier 	/* disable BCI as much as possible */
52459cc4ca5SDavid du Colombier 	*(ushort*)(mmio+CobPtr) &= ~CobBciEna;
52559cc4ca5SDavid du Colombier 	*(ushort*)(mmio+GBD2) &= ~GBDBciEna;
52659cc4ca5SDavid du Colombier 	savagewaitidle(scr);
52759cc4ca5SDavid du Colombier 
52859cc4ca5SDavid du Colombier 	/* enable 32-bit writes, disable clipping */
52959cc4ca5SDavid du Colombier 	*(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis;
53059cc4ca5SDavid du Colombier 	savagewaitidle(scr);
53159cc4ca5SDavid du Colombier 
53259cc4ca5SDavid du Colombier 	/* enable all read, write planes */
53359cc4ca5SDavid du Colombier 	*(ulong*)(mmio+BitplaneRmask) = ~0;
53459cc4ca5SDavid du Colombier 	*(ulong*)(mmio+BitplaneWmask) = ~0;
53559cc4ca5SDavid du Colombier 	savagewaitidle(scr);
53659cc4ca5SDavid du Colombier 
53759cc4ca5SDavid du Colombier 	/* turn on linear access, 2D engine */
53859cc4ca5SDavid du Colombier 	*(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna;
53959cc4ca5SDavid du Colombier 	savagewaitidle(scr);
54059cc4ca5SDavid du Colombier 
54159cc4ca5SDavid du Colombier 	/* set bitmap descriptors */
54259cc4ca5SDavid du Colombier 	bd = (scr->gscreen->depth<<DepthShift) |
5439a747e4fSDavid du Colombier 		(Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis
5449a747e4fSDavid du Colombier 		| BDS64;
54559cc4ca5SDavid du Colombier 
54659cc4ca5SDavid du Colombier 	*(ulong*)(mmio+GBD1) = 0;
5479a747e4fSDavid du Colombier 	*(ulong*)(mmio+GBD2) = bd;
54859cc4ca5SDavid du Colombier 
54959cc4ca5SDavid du Colombier 	*(ulong*)(mmio+PBD1) = 0;
55059cc4ca5SDavid du Colombier 	*(ulong*)(mmio+PBD2) = bd;
55159cc4ca5SDavid du Colombier 
55259cc4ca5SDavid du Colombier 	*(ulong*)(mmio+SBD1) = 0;
55359cc4ca5SDavid du Colombier 	*(ulong*)(mmio+SBD2) = bd;
55459cc4ca5SDavid du Colombier 
55559cc4ca5SDavid du Colombier 	/*
55659cc4ca5SDavid du Colombier 	 * For some reason, the GBD needs to get programmed twice,
55759cc4ca5SDavid du Colombier 	 * once before the PBD, SBD, and once after.
55859cc4ca5SDavid du Colombier 	 * This empirically makes it get set right.
55959cc4ca5SDavid du Colombier 	 * I would like to better understand the ugliness
56059cc4ca5SDavid du Colombier 	 * going on here.
56159cc4ca5SDavid du Colombier 	 */
56259cc4ca5SDavid du Colombier 	*(ulong*)(mmio+GBD1) = 0;
56359cc4ca5SDavid du Colombier 	*(ulong*)(mmio+GBD2) = bd;
56459cc4ca5SDavid du Colombier 	*(ushort*)(mmio+GBD2+2) = bd>>16;
56559cc4ca5SDavid du Colombier 	savagewaitidle(scr);
56659cc4ca5SDavid du Colombier 
56759cc4ca5SDavid du Colombier 	scr->fill = savagefill;
56859cc4ca5SDavid du Colombier 	scr->scroll = savagescroll;
5699a747e4fSDavid du Colombier 	scr->blank = savageblank;
5709a747e4fSDavid du Colombier 	hwblank = 0;
57159cc4ca5SDavid du Colombier }
572