xref: /plan9/sys/src/cmd/aux/vga/mach64xx.c (revision a84536681645e23c630ce4ef2e5c3b284d4c590b)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier #include "pci.h"
69a747e4fSDavid du Colombier #include "vga.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier /*
97dd7cddfSDavid du Colombier  * ATI Mach64 family.
107dd7cddfSDavid du Colombier  */
117dd7cddfSDavid du Colombier enum {
127dd7cddfSDavid du Colombier 	HTotalDisp,
137dd7cddfSDavid du Colombier 	HSyncStrtWid,
147dd7cddfSDavid du Colombier 	VTotalDisp,
157dd7cddfSDavid du Colombier 	VSyncStrtWid,
167dd7cddfSDavid du Colombier 	VlineCrntVline,
177dd7cddfSDavid du Colombier 	OffPitch,
187dd7cddfSDavid du Colombier 	IntCntl,
197dd7cddfSDavid du Colombier 	CrtcGenCntl,
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier 	OvrClr,
227dd7cddfSDavid du Colombier 	OvrWidLR,
237dd7cddfSDavid du Colombier 	OvrWidTB,
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier 	CurClr0,
267dd7cddfSDavid du Colombier 	CurClr1,
277dd7cddfSDavid du Colombier 	CurOffset,
287dd7cddfSDavid du Colombier 	CurHVposn,
297dd7cddfSDavid du Colombier 	CurHVoff,
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier 	ScratchReg0,
327dd7cddfSDavid du Colombier 	ScratchReg1,	/* Scratch Register (BIOS info) */
337dd7cddfSDavid du Colombier 	ClockCntl,
347dd7cddfSDavid du Colombier 	BusCntl,
357dd7cddfSDavid du Colombier 	MemCntl,
367dd7cddfSDavid du Colombier 	ExtMemCntl,
377dd7cddfSDavid du Colombier 	MemVgaWpSel,
387dd7cddfSDavid du Colombier 	MemVgaRpSel,
397dd7cddfSDavid du Colombier 	DacRegs,
407dd7cddfSDavid du Colombier 	DacCntl,
417dd7cddfSDavid du Colombier 	GenTestCntl,
427dd7cddfSDavid du Colombier 	ConfigCntl,	/* Configuration control */
437dd7cddfSDavid du Colombier 	ConfigChipId,
447dd7cddfSDavid du Colombier 	ConfigStat0,	/* Configuration status 0 */
457dd7cddfSDavid du Colombier 	ConfigStat1,	/* Configuration status 1 */
4659cc4ca5SDavid du Colombier 	ConfigStat2,
477dd7cddfSDavid du Colombier 	DspConfig,	/* Rage */
487dd7cddfSDavid du Colombier 	DspOnOff,	/* Rage */
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier 	DpBkgdClr,
517dd7cddfSDavid du Colombier 	DpChainMsk,
527dd7cddfSDavid du Colombier 	DpFrgdClr,
537dd7cddfSDavid du Colombier 	DpMix,
547dd7cddfSDavid du Colombier 	DpPixWidth,
557dd7cddfSDavid du Colombier 	DpSrc,
567dd7cddfSDavid du Colombier 	DpWriteMsk,
577dd7cddfSDavid du Colombier 
587dd7cddfSDavid du Colombier 	LcdIndex,
597dd7cddfSDavid du Colombier 	LcdData,
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier 	Nreg,
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	TvIndex = 0x1D,
647dd7cddfSDavid du Colombier 	TvData = 0x27,
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier 	LCD_ConfigPanel = 0,
677dd7cddfSDavid du Colombier 	LCD_GenCtrl,
687dd7cddfSDavid du Colombier 	LCD_DstnCntl,
697dd7cddfSDavid du Colombier 	LCD_HfbPitchAddr,
707dd7cddfSDavid du Colombier 	LCD_HorzStretch,
717dd7cddfSDavid du Colombier 	LCD_VertStretch,
727dd7cddfSDavid du Colombier 	LCD_ExtVertStretch,
737dd7cddfSDavid du Colombier 	LCD_LtGio,
747dd7cddfSDavid du Colombier 	LCD_PowerMngmnt,
757dd7cddfSDavid du Colombier 	LCD_ZvgPio,
767dd7cddfSDavid du Colombier 	Nlcd,
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier 
7959cc4ca5SDavid du Colombier static char* iorname[Nreg] = {
8059cc4ca5SDavid du Colombier 	"HTotalDisp",
8159cc4ca5SDavid du Colombier 	"HSyncStrtWid",
8259cc4ca5SDavid du Colombier 	"VTotalDisp",
8359cc4ca5SDavid du Colombier 	"VSyncStrtWid",
8459cc4ca5SDavid du Colombier 	"VlineCrntVline",
8559cc4ca5SDavid du Colombier 	"OffPitch",
8659cc4ca5SDavid du Colombier 	"IntCntl",
8759cc4ca5SDavid du Colombier 	"CrtcGenCntl",
887dd7cddfSDavid du Colombier 
8959cc4ca5SDavid du Colombier 	"OvrClr",
9059cc4ca5SDavid du Colombier 	"OvrWidLR",
9159cc4ca5SDavid du Colombier 	"OvrWidTB",
9259cc4ca5SDavid du Colombier 
9359cc4ca5SDavid du Colombier 	"CurClr0",
9459cc4ca5SDavid du Colombier 	"CurClr1",
9559cc4ca5SDavid du Colombier 	"CurOffset",
9659cc4ca5SDavid du Colombier 	"CurHVposn",
9759cc4ca5SDavid du Colombier 	"CurHVoff",
9859cc4ca5SDavid du Colombier 
9959cc4ca5SDavid du Colombier 	"ScratchReg0",
10059cc4ca5SDavid du Colombier 	"ScratchReg1",
10159cc4ca5SDavid du Colombier 	"ClockCntl",
10259cc4ca5SDavid du Colombier 	"BusCntl",
10359cc4ca5SDavid du Colombier 	"MemCntl",
10459cc4ca5SDavid du Colombier 	"ExtMemCntl",
10559cc4ca5SDavid du Colombier 	"MemVgaWpSel",
10659cc4ca5SDavid du Colombier 	"MemVgaRpSel",
10759cc4ca5SDavid du Colombier 	"DacRegs",
10859cc4ca5SDavid du Colombier 	"DacCntl",
10959cc4ca5SDavid du Colombier 	"GenTestCntl",
11059cc4ca5SDavid du Colombier 	"ConfigCntl",
11159cc4ca5SDavid du Colombier 	"ConfigChipId",
11259cc4ca5SDavid du Colombier 	"ConfigStat0",
11359cc4ca5SDavid du Colombier 	"ConfigStat1",
11459cc4ca5SDavid du Colombier 	"ConfigStat2",
11559cc4ca5SDavid du Colombier 	"DspConfig",
11659cc4ca5SDavid du Colombier 	"DspOnOff",
11759cc4ca5SDavid du Colombier 
11859cc4ca5SDavid du Colombier 	"DpBkgdClr",
11959cc4ca5SDavid du Colombier 	"DpChainMsk",
12059cc4ca5SDavid du Colombier 	"DpFrgdClr",
12159cc4ca5SDavid du Colombier 	"DpMix",
12259cc4ca5SDavid du Colombier 	"DpPixWidth",
12359cc4ca5SDavid du Colombier 	"DpSrc",
12459cc4ca5SDavid du Colombier 	"DpWriteMsk",
12559cc4ca5SDavid du Colombier 
12659cc4ca5SDavid du Colombier 	"LcdIndex",
12759cc4ca5SDavid du Colombier 	"LcdData",
12859cc4ca5SDavid du Colombier };
12959cc4ca5SDavid du Colombier 
13059cc4ca5SDavid du Colombier static char* lcdname[Nlcd] = {
13159cc4ca5SDavid du Colombier 	"LCD ConfigPanel",
13259cc4ca5SDavid du Colombier 	"LCD GenCntl",
13359cc4ca5SDavid du Colombier 	"LCD DstnCntl",
13459cc4ca5SDavid du Colombier 	"LCD HfbPitchAddr",
13559cc4ca5SDavid du Colombier 	"LCD HorzStretch",
13659cc4ca5SDavid du Colombier 	"LCD VertStretch",
13759cc4ca5SDavid du Colombier 	"LCD ExtVertStretch",
13859cc4ca5SDavid du Colombier 	"LCD LtGio",
13959cc4ca5SDavid du Colombier 	"LCD PowerMngmnt",
14059cc4ca5SDavid du Colombier 	"LCD ZvgPio"
14159cc4ca5SDavid du Colombier };
14259cc4ca5SDavid du Colombier 
14359cc4ca5SDavid du Colombier /*
14459cc4ca5SDavid du Colombier  * Crummy hack: all io register offsets
14559cc4ca5SDavid du Colombier  * here get IOREG or'ed in, so that we can
14659cc4ca5SDavid du Colombier  * tell the difference between an uninitialized
14759cc4ca5SDavid du Colombier  * array entry and HTotalDisp.
14859cc4ca5SDavid du Colombier  */
14959cc4ca5SDavid du Colombier enum {
15059cc4ca5SDavid du Colombier 	IOREG = 0x10000,
15159cc4ca5SDavid du Colombier };
1527dd7cddfSDavid du Colombier static ushort ioregs[Nreg] = {
15359cc4ca5SDavid du Colombier  [HTotalDisp]		IOREG|0x0000,
15459cc4ca5SDavid du Colombier  [HSyncStrtWid]	IOREG|0x0100,
15559cc4ca5SDavid du Colombier  [VTotalDisp]		IOREG|0x0200,
15659cc4ca5SDavid du Colombier  [VSyncStrtWid]		IOREG|0x0300,
15759cc4ca5SDavid du Colombier  [VlineCrntVline]	IOREG|0x0400,
15859cc4ca5SDavid du Colombier  [OffPitch]			IOREG|0x0500,
15959cc4ca5SDavid du Colombier  [IntCntl]			IOREG|0x0600,
16059cc4ca5SDavid du Colombier  [CrtcGenCntl]		IOREG|0x0700,
16159cc4ca5SDavid du Colombier  [OvrClr]			IOREG|0x0800,
16259cc4ca5SDavid du Colombier  [OvrWidLR]		IOREG|0x0900,
16359cc4ca5SDavid du Colombier  [OvrWidTB]		IOREG|0x0A00,
16459cc4ca5SDavid du Colombier  [CurClr0]			IOREG|0x0B00,
16559cc4ca5SDavid du Colombier  [CurClr1]			IOREG|0x0C00,
16659cc4ca5SDavid du Colombier  [CurOffset]		IOREG|0x0D00,
16759cc4ca5SDavid du Colombier  [CurHVposn]		IOREG|0x0E00,
16859cc4ca5SDavid du Colombier  [CurHVoff]		IOREG|0x0F00,
16959cc4ca5SDavid du Colombier  [ScratchReg0]		IOREG|0x1000,
17059cc4ca5SDavid du Colombier  [ScratchReg1]		IOREG|0x1100,
17159cc4ca5SDavid du Colombier  [ClockCntl]		IOREG|0x1200,
17259cc4ca5SDavid du Colombier  [BusCntl]			IOREG|0x1300,
17359cc4ca5SDavid du Colombier  [MemCntl]		IOREG|0x1400,
17459cc4ca5SDavid du Colombier  [MemVgaWpSel]	IOREG|0x1500,
17559cc4ca5SDavid du Colombier  [MemVgaRpSel]	IOREG|0x1600,
17659cc4ca5SDavid du Colombier  [DacRegs]		IOREG|0x1700,
17759cc4ca5SDavid du Colombier  [DacCntl]			IOREG|0x1800,
17859cc4ca5SDavid du Colombier  [GenTestCntl]		IOREG|0x1900,
17959cc4ca5SDavid du Colombier  [ConfigCntl]		IOREG|0x1A00,
18059cc4ca5SDavid du Colombier  [ConfigChipId]		IOREG|0x1B00,
18159cc4ca5SDavid du Colombier  [ConfigStat0]		IOREG|0x1C00,
18259cc4ca5SDavid du Colombier  [ConfigStat1]		IOREG|0x1D00,
18359cc4ca5SDavid du Colombier /* [GpIo]				IOREG|0x1E00, */
18459cc4ca5SDavid du Colombier /* [HTotalDisp]			IOREG|0x1F00, 	duplicate, says XFree86 */
1857dd7cddfSDavid du Colombier };
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier static ushort pciregs[Nreg] = {
1887dd7cddfSDavid du Colombier   [HTotalDisp]		0x00,
1897dd7cddfSDavid du Colombier   [HSyncStrtWid]	0x01,
1907dd7cddfSDavid du Colombier   [VTotalDisp]		0x02,
1917dd7cddfSDavid du Colombier   [VSyncStrtWid]        0x03,
1927dd7cddfSDavid du Colombier   [VlineCrntVline]      0x04,
1937dd7cddfSDavid du Colombier   [OffPitch]		0x05,
1947dd7cddfSDavid du Colombier   [IntCntl]		0x06,
1957dd7cddfSDavid du Colombier   [CrtcGenCntl]		0x07,
1967dd7cddfSDavid du Colombier   [DspConfig]		0x08,
1977dd7cddfSDavid du Colombier   [DspOnOff]		0x09,
1987dd7cddfSDavid du Colombier   [OvrClr]		0x10,
1997dd7cddfSDavid du Colombier   [OvrWidLR]		0x11,
2007dd7cddfSDavid du Colombier   [OvrWidTB]		0x12,
2017dd7cddfSDavid du Colombier   [CurClr0]		0x18,
2027dd7cddfSDavid du Colombier   [CurClr1]		0x19,
2037dd7cddfSDavid du Colombier   [CurOffset]		0x1A,
2047dd7cddfSDavid du Colombier   [CurHVposn]		0x1B,
2057dd7cddfSDavid du Colombier   [CurHVoff]		0x1C,
2067dd7cddfSDavid du Colombier   [ScratchReg0]		0x20,
2077dd7cddfSDavid du Colombier   [ScratchReg1]		0x21,
2087dd7cddfSDavid du Colombier   [ClockCntl]		0x24,
2097dd7cddfSDavid du Colombier   [BusCntl]		0x28,
2107dd7cddfSDavid du Colombier   [LcdIndex]		0x29,
2117dd7cddfSDavid du Colombier   [LcdData]		0x2A,
2127dd7cddfSDavid du Colombier   [ExtMemCntl]		0x2B,
2137dd7cddfSDavid du Colombier   [MemCntl]		0x2C,
2147dd7cddfSDavid du Colombier   [MemVgaWpSel]		0x2D,
2157dd7cddfSDavid du Colombier   [MemVgaRpSel]		0x2E,
2167dd7cddfSDavid du Colombier   [DacRegs]		0x30,
2177dd7cddfSDavid du Colombier   [DacCntl]		0x31,
2187dd7cddfSDavid du Colombier   [GenTestCntl]		0x34,
2197dd7cddfSDavid du Colombier   [ConfigCntl]		0x37,
2207dd7cddfSDavid du Colombier   [ConfigChipId]	0x38,
2217dd7cddfSDavid du Colombier   [ConfigStat0]		0x39,
22259cc4ca5SDavid du Colombier   [ConfigStat1]		0x25,	/* rsc: was 0x3A, but that's not what the LT manual says */
22359cc4ca5SDavid du Colombier   [ConfigStat2]		0x26,
2247dd7cddfSDavid du Colombier   [DpBkgdClr]		0xB0,
2257dd7cddfSDavid du Colombier   [DpChainMsk]		0xB3,
2267dd7cddfSDavid du Colombier   [DpFrgdClr]		0xB1,
2277dd7cddfSDavid du Colombier   [DpMix]		0xB5,
2287dd7cddfSDavid du Colombier   [DpPixWidth]		0xB4,
2297dd7cddfSDavid du Colombier   [DpSrc]		0xB6,
2307dd7cddfSDavid du Colombier   [DpWriteMsk]		0xB2,
2317dd7cddfSDavid du Colombier };
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier enum {
2347dd7cddfSDavid du Colombier 	PLLm		= 0x02,
2357dd7cddfSDavid du Colombier 	PLLp		= 0x06,
2367dd7cddfSDavid du Colombier 	PLLn0		= 0x07,
2377dd7cddfSDavid du Colombier 	PLLn1		= 0x08,
2387dd7cddfSDavid du Colombier 	PLLn2		= 0x09,
2397dd7cddfSDavid du Colombier 	PLLn3		= 0x0A,
2407dd7cddfSDavid du Colombier 	PLLx            = 0x0B,         /* external divisor (Rage) */
2417dd7cddfSDavid du Colombier 
2427dd7cddfSDavid du Colombier 	Npll		= 32,
2437dd7cddfSDavid du Colombier 	Ntv		= 1,		/* actually 256, but not used */
2447dd7cddfSDavid du Colombier };
2457dd7cddfSDavid du Colombier 
2467dd7cddfSDavid du Colombier typedef struct Mach64xx	Mach64xx;
2477dd7cddfSDavid du Colombier struct Mach64xx {
2487dd7cddfSDavid du Colombier 	ulong	io;
2497dd7cddfSDavid du Colombier 	Pcidev*	pci;
2507dd7cddfSDavid du Colombier 	int	bigmem;
2517dd7cddfSDavid du Colombier 	int	lcdon;
25259cc4ca5SDavid du Colombier 	int	lcdpanelid;
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	ulong	reg[Nreg];
2557dd7cddfSDavid du Colombier 	ulong	lcd[Nlcd];
2567dd7cddfSDavid du Colombier 	ulong	tv[Ntv];
2577dd7cddfSDavid du Colombier 	uchar	pll[Npll];
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier 	ulong	(*ior32)(Mach64xx*, int);
2607dd7cddfSDavid du Colombier 	void	(*iow32)(Mach64xx*, int, ulong);
2617dd7cddfSDavid du Colombier };
2627dd7cddfSDavid du Colombier 
26359cc4ca5SDavid du Colombier static ulong
portior32(Mach64xx * mp,int r)26459cc4ca5SDavid du Colombier portior32(Mach64xx* mp, int r)
26559cc4ca5SDavid du Colombier {
26659cc4ca5SDavid du Colombier 	if((ioregs[r] & IOREG) == 0)
26759cc4ca5SDavid du Colombier 		return ~0;
26859cc4ca5SDavid du Colombier 
26959cc4ca5SDavid du Colombier 	return inportl(((ioregs[r] & ~IOREG)<<2)+mp->io);
27059cc4ca5SDavid du Colombier }
27159cc4ca5SDavid du Colombier 
27259cc4ca5SDavid du Colombier static void
portiow32(Mach64xx * mp,int r,ulong l)27359cc4ca5SDavid du Colombier portiow32(Mach64xx* mp, int r, ulong l)
27459cc4ca5SDavid du Colombier {
27559cc4ca5SDavid du Colombier 	if((ioregs[r] & IOREG) == 0)
27659cc4ca5SDavid du Colombier 		return;
27759cc4ca5SDavid du Colombier 
27859cc4ca5SDavid du Colombier 	outportl(((ioregs[r] & ~IOREG)<<2)+mp->io, l);
27959cc4ca5SDavid du Colombier }
28059cc4ca5SDavid du Colombier 
28159cc4ca5SDavid du Colombier static ulong
pciior32(Mach64xx * mp,int r)28259cc4ca5SDavid du Colombier pciior32(Mach64xx* mp, int r)
28359cc4ca5SDavid du Colombier {
28459cc4ca5SDavid du Colombier 	return inportl((pciregs[r]<<2)+mp->io);
28559cc4ca5SDavid du Colombier }
28659cc4ca5SDavid du Colombier 
28759cc4ca5SDavid du Colombier static void
pciiow32(Mach64xx * mp,int r,ulong l)28859cc4ca5SDavid du Colombier pciiow32(Mach64xx* mp, int r, ulong l)
28959cc4ca5SDavid du Colombier {
29059cc4ca5SDavid du Colombier 	outportl((pciregs[r]<<2)+mp->io, l);
29159cc4ca5SDavid du Colombier }
29259cc4ca5SDavid du Colombier 
2937dd7cddfSDavid du Colombier static uchar
pllr(Mach64xx * mp,int r)2947dd7cddfSDavid du Colombier pllr(Mach64xx* mp, int r)
2957dd7cddfSDavid du Colombier {
2967dd7cddfSDavid du Colombier 	int io;
2977dd7cddfSDavid du Colombier 
29859cc4ca5SDavid du Colombier 	if(mp->ior32 == portior32)
29959cc4ca5SDavid du Colombier 		io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
30059cc4ca5SDavid du Colombier 	else
30159cc4ca5SDavid du Colombier 		io = (pciregs[ClockCntl]<<2)+mp->io;
30259cc4ca5SDavid du Colombier 
3037dd7cddfSDavid du Colombier 	outportb(io+1, r<<2);
3047dd7cddfSDavid du Colombier 	return inportb(io+2);
3057dd7cddfSDavid du Colombier }
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier static void
pllw(Mach64xx * mp,int r,uchar b)3087dd7cddfSDavid du Colombier pllw(Mach64xx* mp, int r, uchar b)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier 	int io;
3117dd7cddfSDavid du Colombier 
31259cc4ca5SDavid du Colombier 	if(mp->ior32 == portior32)
31359cc4ca5SDavid du Colombier 		io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
31459cc4ca5SDavid du Colombier 	else
31559cc4ca5SDavid du Colombier 		io = (pciregs[ClockCntl]<<2)+mp->io;
31659cc4ca5SDavid du Colombier 
3177dd7cddfSDavid du Colombier 	outportb(io+1, (r<<2)|0x02);
3187dd7cddfSDavid du Colombier 	outportb(io+2, b);
3197dd7cddfSDavid du Colombier }
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier static ulong
lcdr32(Mach64xx * mp,ulong r)3227dd7cddfSDavid du Colombier lcdr32(Mach64xx *mp, ulong r)
3237dd7cddfSDavid du Colombier {
3247dd7cddfSDavid du Colombier 	ulong or;
3257dd7cddfSDavid du Colombier 
3267dd7cddfSDavid du Colombier 	or = mp->ior32(mp, LcdIndex);
3277dd7cddfSDavid du Colombier 	mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
3287dd7cddfSDavid du Colombier 	return mp->ior32(mp, LcdData);
3297dd7cddfSDavid du Colombier }
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier static void
lcdw32(Mach64xx * mp,ulong r,ulong v)3327dd7cddfSDavid du Colombier lcdw32(Mach64xx *mp, ulong r, ulong v)
3337dd7cddfSDavid du Colombier {
3347dd7cddfSDavid du Colombier 	ulong or;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 	or = mp->ior32(mp, LcdIndex);
3377dd7cddfSDavid du Colombier 	mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
3387dd7cddfSDavid du Colombier 	mp->iow32(mp, LcdData, v);
3397dd7cddfSDavid du Colombier }
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier static ulong
tvr32(Mach64xx * mp,ulong r)3427dd7cddfSDavid du Colombier tvr32(Mach64xx *mp, ulong r)
3437dd7cddfSDavid du Colombier {
3447dd7cddfSDavid du Colombier 	outportb(mp->io+(TvIndex<<2), r&0x0F);
3457dd7cddfSDavid du Colombier 	return inportl(mp->io+(TvData<<2));
3467dd7cddfSDavid du Colombier }
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier static void
tvw32(Mach64xx * mp,ulong r,ulong v)3497dd7cddfSDavid du Colombier tvw32(Mach64xx *mp, ulong r, ulong v)
3507dd7cddfSDavid du Colombier {
3517dd7cddfSDavid du Colombier 	outportb(mp->io+(TvIndex<<2), r&0x0F);
3527dd7cddfSDavid du Colombier 	outportl(mp->io+(TvData<<2), v);
3537dd7cddfSDavid du Colombier }
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier static int smallmem[] = {
3567dd7cddfSDavid du Colombier 	   512*1024,	  1024*1024,	 2*1024*1024,	 4*1024*1024,
3577dd7cddfSDavid du Colombier 	6*1024*1024,	8*1024*1024,	12*1024*1024,	16*1024*1024,
3587dd7cddfSDavid du Colombier };
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier static int bigmem[] = {
3617dd7cddfSDavid du Colombier 	    512*1024,	  2*512*1024,	  3*512*1024,	  4*512*1024,
3627dd7cddfSDavid du Colombier 	  5*512*1024,	  6*512*1024,	  7*512*1024,	  8*512*1024,
3637dd7cddfSDavid du Colombier 	 5*1024*1024,	 6*1024*1024,	 7*1024*1024,	 8*1024*1024,
3647dd7cddfSDavid du Colombier 	10*1024*1024,	12*1024*1024,	14*1024*1024,	16*1024*1024,
3657dd7cddfSDavid du Colombier };
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier static void
snarf(Vga * vga,Ctlr * ctlr)3687dd7cddfSDavid du Colombier snarf(Vga* vga, Ctlr* ctlr)
3697dd7cddfSDavid du Colombier {
3707dd7cddfSDavid du Colombier 	Mach64xx *mp;
3717dd7cddfSDavid du Colombier 	int i;
3727dd7cddfSDavid du Colombier 	ulong v;
3737dd7cddfSDavid du Colombier 
3747dd7cddfSDavid du Colombier 	if(vga->private == nil){
3757dd7cddfSDavid du Colombier 		vga->private = alloc(sizeof(Mach64xx));
3767dd7cddfSDavid du Colombier 		mp = vga->private;
3777dd7cddfSDavid du Colombier 		mp->io = 0x2EC;
3787dd7cddfSDavid du Colombier 		mp->ior32 = portior32;
3797dd7cddfSDavid du Colombier 		mp->iow32 = portiow32;
3807dd7cddfSDavid du Colombier 		mp->pci = pcimatch(0, 0x1002, 0);
3817dd7cddfSDavid du Colombier 		if (mp->pci) {
3827dd7cddfSDavid du Colombier 			if(v = mp->pci->mem[1].bar & ~0x3) {
3837dd7cddfSDavid du Colombier 				mp->io = v;
38459cc4ca5SDavid du Colombier 				mp->ior32 = pciior32;
38559cc4ca5SDavid du Colombier 				mp->iow32 = pciiow32;
3867dd7cddfSDavid du Colombier 			}
3877dd7cddfSDavid du Colombier 		}
3887dd7cddfSDavid du Colombier 	}
3897dd7cddfSDavid du Colombier 
3907dd7cddfSDavid du Colombier 	mp = vga->private;
3917dd7cddfSDavid du Colombier 	for(i = 0; i < Nreg; i++)
3927dd7cddfSDavid du Colombier 		mp->reg[i] = mp->ior32(mp, i);
3937dd7cddfSDavid du Colombier 
3947dd7cddfSDavid du Colombier 	for(i = 0; i < Npll; i++)
3957dd7cddfSDavid du Colombier 		mp->pll[i] = pllr(mp, i);
3967dd7cddfSDavid du Colombier 
3977dd7cddfSDavid du Colombier 	switch(mp->reg[ConfigChipId] & 0xFFFF){
3987dd7cddfSDavid du Colombier 	default:
39959cc4ca5SDavid du Colombier 		mp->lcdpanelid = 0;
4007dd7cddfSDavid du Colombier 		break;
401223a736eSDavid du Colombier 	case ('L'<<8)|'B':		/* 4C42: Rage LTPro AGP */
4027dd7cddfSDavid du Colombier 	case ('L'<<8)|'I':		/* 4C49: Rage 3D LTPro */
4037dd7cddfSDavid du Colombier 	case ('L'<<8)|'M':		/* 4C4D: Rage Mobility */
4047dd7cddfSDavid du Colombier 	case ('L'<<8)|'P':		/* 4C50: Rage 3D LTPro */
4057dd7cddfSDavid du Colombier 		for(i = 0; i < Nlcd; i++)
4067dd7cddfSDavid du Colombier 			mp->lcd[i] = lcdr32(mp, i);
4077dd7cddfSDavid du Colombier 		if(mp->lcd[LCD_GenCtrl] & 0x02)
4087dd7cddfSDavid du Colombier 			mp->lcdon = 1;
40959cc4ca5SDavid du Colombier 		mp->lcdpanelid = ((mp->reg[ConfigStat2]>>14) & 0x1F);
4107dd7cddfSDavid du Colombier 		break;
4117dd7cddfSDavid du Colombier 	}
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier 	/*
4147dd7cddfSDavid du Colombier 	 * Check which memory size map we are using.
4157dd7cddfSDavid du Colombier 	 */
4167dd7cddfSDavid du Colombier 	mp->bigmem = 0;
4177dd7cddfSDavid du Colombier 	switch(mp->reg[ConfigChipId] & 0xFFFF){
4187dd7cddfSDavid du Colombier 		case ('G'<<8)|'B':	/* 4742: 264GT PRO */
4197dd7cddfSDavid du Colombier 		case ('G'<<8)|'D':	/* 4744: 264GT PRO */
4207dd7cddfSDavid du Colombier 		case ('G'<<8)|'I':	/* 4749: 264GT PRO */
42159cc4ca5SDavid du Colombier 		case ('G'<<8)|'M':	/* 474D: Rage XL */
4227dd7cddfSDavid du Colombier 		case ('G'<<8)|'P':	/* 4750: 264GT PRO */
4237dd7cddfSDavid du Colombier 		case ('G'<<8)|'Q':	/* 4751: 264GT PRO */
4249a747e4fSDavid du Colombier 		case ('G'<<8)|'R':	/* 4752: */
4257dd7cddfSDavid du Colombier 		case ('G'<<8)|'U':	/* 4755: 264GT DVD */
4267dd7cddfSDavid du Colombier 		case ('G'<<8)|'V':	/* 4756: Rage2C */
4277dd7cddfSDavid du Colombier 		case ('G'<<8)|'Z':	/* 475A: Rage2C */
4287dd7cddfSDavid du Colombier 		case ('V'<<8)|'U':	/* 5655: 264VT3 */
42959cc4ca5SDavid du Colombier 		case ('V'<<8)|'V':	/* 5656: 264VT4 */
430223a736eSDavid du Colombier 		case ('L'<<8)|'B':	/* 4C42: Rage LTPro AGP */
4317dd7cddfSDavid du Colombier 		case ('L'<<8)|'I':	/* 4C49: Rage 3D LTPro */
4327dd7cddfSDavid du Colombier 		case ('L'<<8)|'M':	/* 4C4D: Rage Mobility */
4337dd7cddfSDavid du Colombier 		case ('L'<<8)|'P':	/* 4C50: Rage 3D LTPro */
4347dd7cddfSDavid du Colombier 			mp->bigmem = 1;
4357dd7cddfSDavid du Colombier 			break;
4367dd7cddfSDavid du Colombier 		case ('G'<<8)|'T':	/* 4754: 264GT[B] */
4377dd7cddfSDavid du Colombier 		case ('V'<<8)|'T':	/* 5654: 264VT/GT/VTB */
4387dd7cddfSDavid du Colombier 			/*
4397dd7cddfSDavid du Colombier 			 * Only the VTB and GTB use the new memory encoding,
4407dd7cddfSDavid du Colombier 			 * and they are identified by a nonzero ChipVersion,
4417dd7cddfSDavid du Colombier 			 * apparently.
4427dd7cddfSDavid du Colombier 			 */
4437dd7cddfSDavid du Colombier 			if((mp->reg[ConfigChipId] >> 24) & 0x7)
4447dd7cddfSDavid du Colombier 				mp->bigmem = 1;
4457dd7cddfSDavid du Colombier 			break;
4467dd7cddfSDavid du Colombier 	}
4477dd7cddfSDavid du Colombier 
4487dd7cddfSDavid du Colombier 	/*
4497dd7cddfSDavid du Colombier 	 * Memory size and aperture. It's recommended
4507dd7cddfSDavid du Colombier 	 * to use an 8Mb aperture on a 16Mb boundary.
4517dd7cddfSDavid du Colombier 	 */
4527dd7cddfSDavid du Colombier 	if(mp->bigmem)
4537dd7cddfSDavid du Colombier 		vga->vmz = bigmem[mp->reg[MemCntl] & 0x0F];
4547dd7cddfSDavid du Colombier 	else
4557dd7cddfSDavid du Colombier 		vga->vmz = smallmem[mp->reg[MemCntl] & 0x07];
4567dd7cddfSDavid du Colombier 	vga->vma = 16*1024*1024;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 	switch(mp->reg[ConfigCntl]&0x3){
4597dd7cddfSDavid du Colombier 	case 0:
46014414594SDavid du Colombier 		vga->apz = 16*1024*1024;	/* empirical -rsc */
46114414594SDavid du Colombier 		break;
4627dd7cddfSDavid du Colombier 	case 1:
4637dd7cddfSDavid du Colombier 		vga->apz = 4*1024*1024;
4647dd7cddfSDavid du Colombier 		break;
4657dd7cddfSDavid du Colombier 	case 2:
4667dd7cddfSDavid du Colombier 		vga->apz = 8*1024*1024;
4677dd7cddfSDavid du Colombier 		break;
4687dd7cddfSDavid du Colombier 	case 3:
46959cc4ca5SDavid du Colombier 		vga->apz = 2*1024*1024;	/* empirical: mach64GX -rsc */
47059cc4ca5SDavid du Colombier 		break;
4717dd7cddfSDavid du Colombier 	}
4727dd7cddfSDavid du Colombier 
4737dd7cddfSDavid du Colombier 	ctlr->flag |= Fsnarf;
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier static void
options(Vga *,Ctlr * ctlr)4777dd7cddfSDavid du Colombier options(Vga*, Ctlr* ctlr)
4787dd7cddfSDavid du Colombier {
4797dd7cddfSDavid du Colombier 	ctlr->flag |= Hlinear|Foptions;
4807dd7cddfSDavid du Colombier }
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier static void
clock(Vga * vga,Ctlr * ctlr)4837dd7cddfSDavid du Colombier clock(Vga* vga, Ctlr* ctlr)
4847dd7cddfSDavid du Colombier {
4857dd7cddfSDavid du Colombier 	int clk, m, n, p;
4867dd7cddfSDavid du Colombier 	double f, q;
4877dd7cddfSDavid du Colombier 	Mach64xx *mp;
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 	mp = vga->private;
4907dd7cddfSDavid du Colombier 
4917dd7cddfSDavid du Colombier 	/*
4927dd7cddfSDavid du Colombier 	 * Don't compute clock timings for LCD panels.
4937dd7cddfSDavid du Colombier 	 * Just use what's already there.  We can't just use
4947dd7cddfSDavid du Colombier 	 * the frequency in the vgadb for this because
4957dd7cddfSDavid du Colombier 	 * the frequency being programmed into the PLLs
4967dd7cddfSDavid du Colombier 	 * is not the frequency being used to compute the DSP
4977dd7cddfSDavid du Colombier 	 * settings.  The DSP-relevant frequency is the one
4987dd7cddfSDavid du Colombier 	 * we keep in /lib/vgadb.
4997dd7cddfSDavid du Colombier 	 */
5007dd7cddfSDavid du Colombier 	if(mp->lcdon){
5017dd7cddfSDavid du Colombier 		clk = mp->reg[ClockCntl] & 0x03;
5027dd7cddfSDavid du Colombier 		n = mp->pll[7+clk];
5037dd7cddfSDavid du Colombier 		p = (mp->pll[6]>>(clk*2)) & 0x03;
5047dd7cddfSDavid du Colombier 		p |= (mp->pll[11]>>(2+clk)) & 0x04;
5057dd7cddfSDavid du Colombier 		switch(p){
5067dd7cddfSDavid du Colombier 		case 0:
5077dd7cddfSDavid du Colombier 		case 1:
5087dd7cddfSDavid du Colombier 		case 2:
5097dd7cddfSDavid du Colombier 		case 3:
5107dd7cddfSDavid du Colombier 			p = 1<<p;
5117dd7cddfSDavid du Colombier 			break;
5127dd7cddfSDavid du Colombier 		case 4+0:
5137dd7cddfSDavid du Colombier 			p = 3;
5147dd7cddfSDavid du Colombier 			break;
5157dd7cddfSDavid du Colombier 		case 4+2:
5167dd7cddfSDavid du Colombier 			p = 6;
5177dd7cddfSDavid du Colombier 			break;
5187dd7cddfSDavid du Colombier 		case 4+3:
5197dd7cddfSDavid du Colombier 			p = 12;
5207dd7cddfSDavid du Colombier 			break;
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier 		default:
5237dd7cddfSDavid du Colombier 		case 4+1:
5247dd7cddfSDavid du Colombier 			p = -1;
5257dd7cddfSDavid du Colombier 			break;
5267dd7cddfSDavid du Colombier 		}
5277dd7cddfSDavid du Colombier 		m = mp->pll[PLLm];
5287dd7cddfSDavid du Colombier 		f = (2.0*RefFreq*n)/(m*p) + 0.5;
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier 		vga->m[0] = m;
5317dd7cddfSDavid du Colombier 		vga->p[0] = p;
5327dd7cddfSDavid du Colombier 		vga->n[0] = n;
5337dd7cddfSDavid du Colombier 		vga->f[0] = f;
5347dd7cddfSDavid du Colombier 		return;
5357dd7cddfSDavid du Colombier 	}
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier 	if(vga->f[0] == 0)
5387dd7cddfSDavid du Colombier 		vga->f[0] = vga->mode->frequency;
5397dd7cddfSDavid du Colombier 	f = vga->f[0];
5407dd7cddfSDavid du Colombier 
5417dd7cddfSDavid du Colombier 	/*
5427dd7cddfSDavid du Colombier 	 * To generate a specific output frequency, the reference (m),
5437dd7cddfSDavid du Colombier 	 * feedback (n), and post dividers (p) must be loaded with the
5447dd7cddfSDavid du Colombier 	 * appropriate divide-down ratios. In the following r is the
5457dd7cddfSDavid du Colombier 	 * XTALIN frequency (usually RefFreq) and t is the target frequency
5467dd7cddfSDavid du Colombier 	 * (vga->f).
5477dd7cddfSDavid du Colombier 	 *
5487dd7cddfSDavid du Colombier 	 * Use the maximum reference divider left by the BIOS for now,
5497dd7cddfSDavid du Colombier 	 * otherwise MCLK might be a concern. It can be calculated as
5507dd7cddfSDavid du Colombier 	 * follows:
5517dd7cddfSDavid du Colombier 	 * 			    Upper Limit of PLL Lock Range
5527dd7cddfSDavid du Colombier 	 *	Minimum PLLREFCLK = -----------------------------
5537dd7cddfSDavid du Colombier 	 *				      (2*255)
5547dd7cddfSDavid du Colombier 	 *
5557dd7cddfSDavid du Colombier 	 *				       XTALIN
5567dd7cddfSDavid du Colombier 	 *			m = Floor[-----------------]
5577dd7cddfSDavid du Colombier 	 *				  Minimum PLLREFCLK
5587dd7cddfSDavid du Colombier 	 *
5597dd7cddfSDavid du Colombier 	 * For an upper limit of 135MHz and XTALIN of 14.318MHz m
5607dd7cddfSDavid du Colombier 	 * would be 54.
5617dd7cddfSDavid du Colombier 	 */
5627dd7cddfSDavid du Colombier 	m = mp->pll[PLLm];
5637dd7cddfSDavid du Colombier 	vga->m[0] = m;
5647dd7cddfSDavid du Colombier 
5657dd7cddfSDavid du Colombier 	/*
5667dd7cddfSDavid du Colombier 	 * The post divider may be 1, 2, 4 or 8 and is determined by
5677dd7cddfSDavid du Colombier 	 * calculating
5687dd7cddfSDavid du Colombier 	 *		 t*m
5697dd7cddfSDavid du Colombier 	 *	    q = -----
5707dd7cddfSDavid du Colombier 	 *		(2*r)
5717dd7cddfSDavid du Colombier 	 * and using the result to look-up p.
5727dd7cddfSDavid du Colombier 	 */
5737dd7cddfSDavid du Colombier 	q = (f*m)/(2*RefFreq);
5747dd7cddfSDavid du Colombier 	if(ctlr->flag&Uenhanced){
5757dd7cddfSDavid du Colombier 	  if(q > 255 || q < 10.6666666667)
5767dd7cddfSDavid du Colombier 		error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
5777dd7cddfSDavid du Colombier 	  if(q > 127.5)
5787dd7cddfSDavid du Colombier 		p = 1;
5797dd7cddfSDavid du Colombier 	  else if(q > 85)
5807dd7cddfSDavid du Colombier 		p = 2;
5817dd7cddfSDavid du Colombier 	  else if(q > 63.75)
5827dd7cddfSDavid du Colombier 		p = 3;
5837dd7cddfSDavid du Colombier 	  else if(q > 42.5)
5847dd7cddfSDavid du Colombier 		p = 4;
5857dd7cddfSDavid du Colombier 	  else if(q > 31.875)
5867dd7cddfSDavid du Colombier 		p = 6;
5877dd7cddfSDavid du Colombier 	  else if(q > 21.25)
5887dd7cddfSDavid du Colombier 		p = 8;
5897dd7cddfSDavid du Colombier 	  else
5907dd7cddfSDavid du Colombier 		p = 12;
5917dd7cddfSDavid du Colombier 	}else{
5927dd7cddfSDavid du Colombier 	  if(q > 255 || q < 16)
5937dd7cddfSDavid du Colombier 		error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
5947dd7cddfSDavid du Colombier 	  if(q >= 127.5)
5957dd7cddfSDavid du Colombier 		p = 1;
5967dd7cddfSDavid du Colombier 	  else if(q >= 63.5)
5977dd7cddfSDavid du Colombier 		p = 2;
5987dd7cddfSDavid du Colombier 	  else if(q >= 31.5)
5997dd7cddfSDavid du Colombier 		p = 4;
6007dd7cddfSDavid du Colombier 	  else
6017dd7cddfSDavid du Colombier 		p = 8;
6027dd7cddfSDavid du Colombier 	}
6037dd7cddfSDavid du Colombier 	vga->p[0] = p;
6047dd7cddfSDavid du Colombier 
6057dd7cddfSDavid du Colombier 	/*
6067dd7cddfSDavid du Colombier 	 * The feedback divider should be kept in the range 0x80 to 0xFF
6077dd7cddfSDavid du Colombier 	 * and is found from
6087dd7cddfSDavid du Colombier 	 *	n = q*p
6097dd7cddfSDavid du Colombier 	 * rounded to the nearest whole number.
6107dd7cddfSDavid du Colombier 	 */
6117dd7cddfSDavid du Colombier 	vga->n[0] = (q*p)+0.5;
6127dd7cddfSDavid du Colombier }
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier typedef struct Meminfo	Meminfo;
6157dd7cddfSDavid du Colombier struct Meminfo {
6167dd7cddfSDavid du Colombier 	int latency;
6177dd7cddfSDavid du Colombier 	int latch;
6187dd7cddfSDavid du Colombier 	int trp;		/* filled in from card */
6197dd7cddfSDavid du Colombier 	int trcd;		/* filled in from card */
6207dd7cddfSDavid du Colombier 	int tcrd;		/* filled in from card */
6217dd7cddfSDavid du Colombier 	int tras;		/* filled in from card */
6227dd7cddfSDavid du Colombier };
6237dd7cddfSDavid du Colombier 
6247dd7cddfSDavid du Colombier enum {
6257dd7cddfSDavid du Colombier 	Mdram,
6267dd7cddfSDavid du Colombier 	Medo,
6277dd7cddfSDavid du Colombier 	Msdram,
6287dd7cddfSDavid du Colombier 	Mwram,
6297dd7cddfSDavid du Colombier };
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier /*
6327dd7cddfSDavid du Colombier  * The manuals and documentation are silent on which settings
6337dd7cddfSDavid du Colombier  * to use for Mwdram, or how to tell which to use.
6347dd7cddfSDavid du Colombier  */
6357dd7cddfSDavid du Colombier static Meminfo meminfo[] = {
6367dd7cddfSDavid du Colombier [Mdram]		{ 1, 0 },
6377dd7cddfSDavid du Colombier [Medo]		{ 1, 2 },
6387dd7cddfSDavid du Colombier [Msdram]	{ 3, 1 },
6397dd7cddfSDavid du Colombier [Mwram]		{ 1, 3 },	/* non TYPE_A */
6407dd7cddfSDavid du Colombier };
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier static ushort looplatencytab[2][2] = {
6437dd7cddfSDavid du Colombier 	{ 8, 6 },		/* DRAM: ≤1M, > 1M */
6447dd7cddfSDavid du Colombier 	{ 9, 8 },		/* SDRAM: ≤1M, > 1M */
6457dd7cddfSDavid du Colombier };
6467dd7cddfSDavid du Colombier 
6477dd7cddfSDavid du Colombier static ushort cyclesperqwordtab[2][2] = {
6487dd7cddfSDavid du Colombier 	{ 3, 2 },		/* DRAM: ≤1M, > 1M */
6497dd7cddfSDavid du Colombier 	{ 2, 1 },		/* SDRAM: ≤1M, > 1M */
6507dd7cddfSDavid du Colombier };
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier static int memtype[] = {
6537dd7cddfSDavid du Colombier 	-1,			/* disable memory access */
6547dd7cddfSDavid du Colombier 	Mdram,			/* basic DRAM */
6557dd7cddfSDavid du Colombier 	Medo,			/* EDO */
6567dd7cddfSDavid du Colombier 	Medo,			/* hyper page DRAM or EDO */
6577dd7cddfSDavid du Colombier 	Msdram,			/* SDRAM */
6587dd7cddfSDavid du Colombier 	Msdram,			/* SGRAM */
6597dd7cddfSDavid du Colombier 	Mwram,
6607dd7cddfSDavid du Colombier 	Mwram
6617dd7cddfSDavid du Colombier };
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier /*
6647dd7cddfSDavid du Colombier  * Calculate various memory parameters so that the card
6657dd7cddfSDavid du Colombier  * fetches the right bytes at the right time.  I don't claim to
6667dd7cddfSDavid du Colombier  * understand the actual calculations very well.
6677dd7cddfSDavid du Colombier  *
6687dd7cddfSDavid du Colombier  * This is remarkably useful on laptops, since knowledge of
6697dd7cddfSDavid du Colombier  * x lets us find the frequency that the screen is really running
6707dd7cddfSDavid du Colombier  * at, which is not necessarily in the VCLKs.
6717dd7cddfSDavid du Colombier  */
6727dd7cddfSDavid du Colombier static void
setdsp(Vga * vga,Ctlr *)6737dd7cddfSDavid du Colombier setdsp(Vga* vga, Ctlr*)
6747dd7cddfSDavid du Colombier {
6757dd7cddfSDavid du Colombier 	Mach64xx *mp;
6767dd7cddfSDavid du Colombier 	Meminfo *mem;
6777dd7cddfSDavid du Colombier 	ushort table, memclk, memtyp;
6787dd7cddfSDavid du Colombier 	int i, prec, xprec, fprec;
6797dd7cddfSDavid du Colombier 	ulong t;
6807dd7cddfSDavid du Colombier 	double pw, x, fifosz, fifoon, fifooff;
6817dd7cddfSDavid du Colombier 	ushort dspon, dspoff;
6827dd7cddfSDavid du Colombier 	int afifosz, lat, ncycle, pfc, rcc;
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	mp = vga->private;
6857dd7cddfSDavid du Colombier 
6867dd7cddfSDavid du Colombier 	/*
6877dd7cddfSDavid du Colombier 	 * Get video ram configuration from BIOS and chip
6887dd7cddfSDavid du Colombier 	 */
6897dd7cddfSDavid du Colombier 	table = *(ushort*)readbios(sizeof table, 0xc0048);
6907dd7cddfSDavid du Colombier 	trace("rom table offset %uX\n", table);
6917dd7cddfSDavid du Colombier 	table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
6927dd7cddfSDavid du Colombier 	trace("freq table offset %uX\n", table);
6937dd7cddfSDavid du Colombier 	memclk = *(ushort*)readbios(sizeof memclk, 0xc0000+table+18);
6947dd7cddfSDavid du Colombier 	trace("memclk %ud\n", memclk);
6957dd7cddfSDavid du Colombier 	memtyp = memtype[mp->reg[ConfigStat0]&07];
6967dd7cddfSDavid du Colombier 	mem = &meminfo[memtyp];
6977dd7cddfSDavid du Colombier 
6987dd7cddfSDavid du Colombier 	/*
6997dd7cddfSDavid du Colombier 	 * First we need to calculate x, the number of
7007dd7cddfSDavid du Colombier 	 * XCLKs that one QWORD occupies in the display FIFO.
7017dd7cddfSDavid du Colombier 	 *
7027dd7cddfSDavid du Colombier 	 * For some reason, x gets stretched out if LCD stretching
7037dd7cddfSDavid du Colombier 	 * is turned on.
7047dd7cddfSDavid du Colombier 	 */
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier 	x = ((double)memclk*640000.0) /
7077dd7cddfSDavid du Colombier 		((double)vga->mode->frequency * (double)vga->mode->z);
7087dd7cddfSDavid du Colombier 	if(mp->lcd[LCD_HorzStretch] & (1<<31))
7097dd7cddfSDavid du Colombier 		x *= 4096.0 / (double)(mp->lcd[LCD_HorzStretch] & 0xFFFF);
7107dd7cddfSDavid du Colombier 
7117dd7cddfSDavid du Colombier 	trace("memclk %d... x %f...", memclk, x);
7127dd7cddfSDavid du Colombier 	/*
7137dd7cddfSDavid du Colombier 	 * We have 14 bits to specify x in.  Decide where to
7147dd7cddfSDavid du Colombier 	 * put the decimal (err, binary) point by counting how
7157dd7cddfSDavid du Colombier 	 * many significant bits are in the integer portion of x.
7167dd7cddfSDavid du Colombier 	 */
7177dd7cddfSDavid du Colombier 	t = x;
7187dd7cddfSDavid du Colombier 	for(i=31; i>=0; i--)
7197dd7cddfSDavid du Colombier 		if(t & (1<<i))
7207dd7cddfSDavid du Colombier 			break;
7217dd7cddfSDavid du Colombier 	xprec = i+1;
7227dd7cddfSDavid du Colombier 	trace("t %lud... xprec %d...", t, xprec);
7237dd7cddfSDavid du Colombier 
7247dd7cddfSDavid du Colombier 	/*
7257dd7cddfSDavid du Colombier 	 * The maximum FIFO size is the number of XCLKs per QWORD
7267dd7cddfSDavid du Colombier 	 * multiplied by 32, for some reason.  We have 11 bits to
7277dd7cddfSDavid du Colombier 	 * specify fifosz.
7287dd7cddfSDavid du Colombier 	 */
7297dd7cddfSDavid du Colombier 	fifosz = x * 32.0;
7307dd7cddfSDavid du Colombier 	trace("fifosz %f...", fifosz);
7317dd7cddfSDavid du Colombier 	t = fifosz;
7327dd7cddfSDavid du Colombier 	for(i=31; i>=0; i--)
7337dd7cddfSDavid du Colombier 		if(t & (1<<i))
7347dd7cddfSDavid du Colombier 			break;
7357dd7cddfSDavid du Colombier 	fprec = i+1;
7367dd7cddfSDavid du Colombier 	trace("fprec %d...", fprec);
7377dd7cddfSDavid du Colombier 
7387dd7cddfSDavid du Colombier 	/*
7397dd7cddfSDavid du Colombier 	 * Precision is specified as 3 less than the number of bits
7407dd7cddfSDavid du Colombier 	 * in the integer part of x, and 5 less than the number of bits
7417dd7cddfSDavid du Colombier 	 * in the integer part of fifosz.
7427dd7cddfSDavid du Colombier 	 *
7437dd7cddfSDavid du Colombier 	 * It is bounded by zero and seven.
7447dd7cddfSDavid du Colombier 	 */
7457dd7cddfSDavid du Colombier 	prec = (xprec-3 > fprec-5) ? xprec-3 : fprec-5;
7467dd7cddfSDavid du Colombier 	if(prec < 0)
7477dd7cddfSDavid du Colombier 		prec = 0;
7487dd7cddfSDavid du Colombier 	if(prec > 7)
7497dd7cddfSDavid du Colombier 		prec = 7;
7507dd7cddfSDavid du Colombier 
7517dd7cddfSDavid du Colombier 	xprec = prec+3;
7527dd7cddfSDavid du Colombier 	fprec = prec+5;
7537dd7cddfSDavid du Colombier 	trace("prec %d...", prec);
7547dd7cddfSDavid du Colombier 
7557dd7cddfSDavid du Colombier 	/*
7567dd7cddfSDavid du Colombier 	 * Actual fifo size
7577dd7cddfSDavid du Colombier 	 */
7587dd7cddfSDavid du Colombier 	afifosz = (1<<fprec) / x;
7597dd7cddfSDavid du Colombier 	if(afifosz > 32)
7607dd7cddfSDavid du Colombier 		afifosz = 32;
7617dd7cddfSDavid du Colombier 
76259cc4ca5SDavid du Colombier 	fifooff = ceil(x*(afifosz-1));
7637dd7cddfSDavid du Colombier 
7647dd7cddfSDavid du Colombier 	/*
7657dd7cddfSDavid du Colombier 	 * I am suspicious of this table, lifted from ATI docs,
7667dd7cddfSDavid du Colombier 	 * because it doesn't agree with the Windows drivers.
7677dd7cddfSDavid du Colombier 	 * We always get 0x0A for lat+2 while Windows uses 0x08.
7687dd7cddfSDavid du Colombier 	 */
7697dd7cddfSDavid du Colombier 	lat = looplatencytab[memtyp > 1][vga->vmz > 1*1024*1024];
7707dd7cddfSDavid du Colombier 	trace("afifosz %d...fifooff %f...", afifosz, fifooff);
7717dd7cddfSDavid du Colombier 
7727dd7cddfSDavid du Colombier 	/*
7737dd7cddfSDavid du Colombier 	 * Page fault clock
7747dd7cddfSDavid du Colombier 	 */
7757dd7cddfSDavid du Colombier 	t = mp->reg[MemCntl];
7767dd7cddfSDavid du Colombier 	mem->trp = (t>>8)&3;	/* RAS precharge time */
7777dd7cddfSDavid du Colombier 	mem->trcd = (t>>10)&3;	/* RAS to CAS delay */
7787dd7cddfSDavid du Colombier 	mem->tcrd = (t>>12)&1;	/* CAS to RAS delay */
7797dd7cddfSDavid du Colombier 	mem->tras = (t>>16)&7;	/* RAS low minimum pulse width */
7807dd7cddfSDavid du Colombier 	pfc = mem->trp + 1 + mem->trcd + 1 + mem->tcrd;
7817dd7cddfSDavid du Colombier 	trace("pfc %d...", pfc);
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 	/*
7847dd7cddfSDavid du Colombier 	 * Maximum random access cycle clock.
7857dd7cddfSDavid du Colombier 	 */
7867dd7cddfSDavid du Colombier 	ncycle = cyclesperqwordtab[memtyp > 1][vga->vmz > 1*1024*1024];
7877dd7cddfSDavid du Colombier 	rcc = mem->trp + 1 + mem->tras + 1;
7887dd7cddfSDavid du Colombier 	if(rcc < pfc+ncycle)
7897dd7cddfSDavid du Colombier 		rcc = pfc+ncycle;
7907dd7cddfSDavid du Colombier 	trace("rcc %d...", rcc);
7917dd7cddfSDavid du Colombier 
79259cc4ca5SDavid du Colombier 	fifoon = (rcc > floor(x)) ? rcc : floor(x);
79359cc4ca5SDavid du Colombier 	fifoon += (3.0 * rcc) - 1 + pfc + ncycle;
7947dd7cddfSDavid du Colombier 	trace("fifoon %f...\n", fifoon);
7957dd7cddfSDavid du Colombier 	/*
7967dd7cddfSDavid du Colombier 	 * Now finally put the bits together.
7977dd7cddfSDavid du Colombier 	 * x is stored in a 14 bit field with xprec bits of integer.
7987dd7cddfSDavid du Colombier 	 */
7997dd7cddfSDavid du Colombier 	pw = x * (1<<(14-xprec));
8007dd7cddfSDavid du Colombier 	mp->reg[DspConfig] = (ulong)pw | (((lat+2)&0xF)<<16) | ((prec&7)<<20);
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	/*
8037dd7cddfSDavid du Colombier 	 * These are stored in an 11 bit field with fprec bits of integer.
8047dd7cddfSDavid du Colombier 	 */
8057dd7cddfSDavid du Colombier 	dspon  = (ushort)fifoon << (11-fprec);
8067dd7cddfSDavid du Colombier 	dspoff = (ushort)fifooff << (11-fprec);
8077dd7cddfSDavid du Colombier 	mp->reg[DspOnOff] = ((dspon&0x7ff) << 16) | (dspoff&0x7ff);
8087dd7cddfSDavid du Colombier }
8097dd7cddfSDavid du Colombier 
8107dd7cddfSDavid du Colombier static void
init(Vga * vga,Ctlr * ctlr)8117dd7cddfSDavid du Colombier init(Vga* vga, Ctlr* ctlr)
8127dd7cddfSDavid du Colombier {
8137dd7cddfSDavid du Colombier 	Mode *mode;
8147dd7cddfSDavid du Colombier 	Mach64xx *mp;
815223a736eSDavid du Colombier 	int p, x, y;
8167dd7cddfSDavid du Colombier 
8177dd7cddfSDavid du Colombier 	mode = vga->mode;
8187dd7cddfSDavid du Colombier 	if((mode->x > 640 || mode->y > 480) && mode->z == 1)
8197dd7cddfSDavid du Colombier 		error("%s: no support for 1-bit mode other than 640x480x1\n",
8207dd7cddfSDavid du Colombier 			ctlr->name);
8217dd7cddfSDavid du Colombier 
8227dd7cddfSDavid du Colombier 	mp = vga->private;
8237dd7cddfSDavid du Colombier 	if(mode->z > 8 && mp->pci == nil)
8247dd7cddfSDavid du Colombier 		error("%s: no support for >8-bit color without PCI\n",
8257dd7cddfSDavid du Colombier 			ctlr->name);
8267dd7cddfSDavid du Colombier 
8277dd7cddfSDavid du Colombier 	/*
8287dd7cddfSDavid du Colombier 	 * Check for Rage chip
8297dd7cddfSDavid du Colombier 	 */
8307dd7cddfSDavid du Colombier 	switch (mp->reg[ConfigChipId]&0xffff) {
8317dd7cddfSDavid du Colombier 		case ('G'<<8)|'B':	/* 4742: 264GT PRO */
8327dd7cddfSDavid du Colombier 		case ('G'<<8)|'D':	/* 4744: 264GT PRO */
8337dd7cddfSDavid du Colombier 		case ('G'<<8)|'I':	/* 4749: 264GT PRO */
83459cc4ca5SDavid du Colombier 		case ('G'<<8)|'M':	/* 474D: Rage XL */
8357dd7cddfSDavid du Colombier 		case ('G'<<8)|'P':	/* 4750: 264GT PRO */
8367dd7cddfSDavid du Colombier 		case ('G'<<8)|'Q':	/* 4751: 264GT PRO */
8379a747e4fSDavid du Colombier 		case ('G'<<8)|'R':	/* 4752: */
8387dd7cddfSDavid du Colombier 		case ('G'<<8)|'U':	/* 4755: 264GT DVD */
8397dd7cddfSDavid du Colombier 		case ('G'<<8)|'V':	/* 4756: Rage2C */
8407dd7cddfSDavid du Colombier 		case ('G'<<8)|'Z':	/* 475A: Rage2C */
8417dd7cddfSDavid du Colombier 		case ('V'<<8)|'U':	/* 5655: 264VT3 */
84259cc4ca5SDavid du Colombier 		case ('V'<<8)|'V':	/* 5656: 264VT4 */
8437dd7cddfSDavid du Colombier 		case ('G'<<8)|'T':	/* 4754: 264GT[B] */
8447dd7cddfSDavid du Colombier 		case ('V'<<8)|'T':	/* 5654: 264VT/GT/VTB */
845223a736eSDavid du Colombier 		case ('L'<<8)|'B':	/* 4C42: Rage LTPro AGP */
8467dd7cddfSDavid du Colombier 		case ('L'<<8)|'I':	/* 4C49: 264LT PRO */
8477dd7cddfSDavid du Colombier 		case ('L'<<8)|'M':	/* 4C4D: Rage Mobility */
8487dd7cddfSDavid du Colombier 		case ('L'<<8)|'P':	/* 4C50: 264LT PRO */
8497dd7cddfSDavid du Colombier 			ctlr->flag |= Uenhanced;
8507dd7cddfSDavid du Colombier 			break;
8517dd7cddfSDavid du Colombier 	}
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier 	/*
8547dd7cddfSDavid du Colombier 	 * Always use VCLK2.
8557dd7cddfSDavid du Colombier 	 */
8567dd7cddfSDavid du Colombier 	clock(vga, ctlr);
8577dd7cddfSDavid du Colombier 	mp->pll[PLLn2] = vga->n[0];
8587dd7cddfSDavid du Colombier 	mp->pll[PLLp] &= ~(0x03<<(2*2));
8597dd7cddfSDavid du Colombier 	switch(vga->p[0]){
8607dd7cddfSDavid du Colombier 	case 1:
8617dd7cddfSDavid du Colombier 	case 3:
8627dd7cddfSDavid du Colombier 		p = 0;
8637dd7cddfSDavid du Colombier 		break;
8647dd7cddfSDavid du Colombier 
8657dd7cddfSDavid du Colombier 	case 2:
8667dd7cddfSDavid du Colombier 		p = 1;
8677dd7cddfSDavid du Colombier 		break;
8687dd7cddfSDavid du Colombier 
8697dd7cddfSDavid du Colombier 	case 4:
8707dd7cddfSDavid du Colombier 	case 6:
8717dd7cddfSDavid du Colombier 		p = 2;
8727dd7cddfSDavid du Colombier 		break;
8737dd7cddfSDavid du Colombier 
8747dd7cddfSDavid du Colombier 	case 8:
8757dd7cddfSDavid du Colombier 	case 12:
8767dd7cddfSDavid du Colombier 		p = 3;
8777dd7cddfSDavid du Colombier 		break;
8787dd7cddfSDavid du Colombier 
8797dd7cddfSDavid du Colombier 	default:
8807dd7cddfSDavid du Colombier 		p = 3;
8817dd7cddfSDavid du Colombier 		break;
8827dd7cddfSDavid du Colombier 	}
8837dd7cddfSDavid du Colombier 	mp->pll[PLLp] |= p<<(2*2);
8847dd7cddfSDavid du Colombier 	if ((1<<p) != vga->p[0])
8857dd7cddfSDavid du Colombier 		mp->pll[PLLx] |= 1<<(4+2);
8867dd7cddfSDavid du Colombier 	else
8877dd7cddfSDavid du Colombier 		mp->pll[PLLx] &= ~(1<<(4+2));
8887dd7cddfSDavid du Colombier 	mp->reg[ClockCntl] = 2;
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier 	mp->reg[ConfigCntl] = 0;
8917dd7cddfSDavid du Colombier 
8927dd7cddfSDavid du Colombier 	mp->reg[CrtcGenCntl] = 0x02000000|(mp->reg[CrtcGenCntl] & ~0x01400700);
8937dd7cddfSDavid du Colombier 	switch(mode->z){
8947dd7cddfSDavid du Colombier 	default:
8957dd7cddfSDavid du Colombier 	case 1:
8967dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x00000100;
8977dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00000000;
8987dd7cddfSDavid du Colombier 		break;
8997dd7cddfSDavid du Colombier 	case 8:
9007dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x01000200;
9017dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00020202;
9027dd7cddfSDavid du Colombier 		break;
9037dd7cddfSDavid du Colombier 	case 15:
9047dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x01000300;
9057dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00030303;
9067dd7cddfSDavid du Colombier 		break;
9077dd7cddfSDavid du Colombier 	case 16:
9087dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x01000400;
9097dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00040404;
9107dd7cddfSDavid du Colombier 		break;
9117dd7cddfSDavid du Colombier 	case 24:
9127dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x01000500;
9137dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00050505;
9147dd7cddfSDavid du Colombier 		break;
9157dd7cddfSDavid du Colombier 	case 32:
9167dd7cddfSDavid du Colombier 		mp->reg[CrtcGenCntl] |= 0x01000600;
9177dd7cddfSDavid du Colombier 		mp->reg[DpPixWidth] = 0x00060606;
9187dd7cddfSDavid du Colombier 		break;
9197dd7cddfSDavid du Colombier 	}
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier 	mp->reg[HTotalDisp] = (((mode->x>>3)-1)<<16)|((mode->ht>>3)-1);
9227dd7cddfSDavid du Colombier 	mp->reg[HSyncStrtWid] = (((mode->ehs - mode->shs)>>3)<<16)
9237dd7cddfSDavid du Colombier 				|((mode->shs>>3)-1);
9247dd7cddfSDavid du Colombier 	if(mode->hsync == '-')
9257dd7cddfSDavid du Colombier 		mp->reg[HSyncStrtWid] |= 0x00200000;
9267dd7cddfSDavid du Colombier 	mp->reg[VTotalDisp] = ((mode->y-1)<<16)|(mode->vt-1);
9277dd7cddfSDavid du Colombier 	mp->reg[VSyncStrtWid] = ((mode->vre - mode->vrs)<<16)|(mode->vrs-1);
9287dd7cddfSDavid du Colombier 	if(mode->vsync == '-')
9297dd7cddfSDavid du Colombier 		mp->reg[VSyncStrtWid] |= 0x00200000;
9307dd7cddfSDavid du Colombier 	mp->reg[IntCntl] = 0;
9317dd7cddfSDavid du Colombier 
9327dd7cddfSDavid du Colombier 	/*
9337dd7cddfSDavid du Colombier 	 * This used to set it to (mode->x/(8*2))<<22 for depths < 8,
9347dd7cddfSDavid du Colombier 	 * but from the manual that seems wrong to me.  -rsc
9357dd7cddfSDavid du Colombier 	 */
9367dd7cddfSDavid du Colombier 	mp->reg[OffPitch] = (vga->virtx/8)<<22;
9377dd7cddfSDavid du Colombier 
9387dd7cddfSDavid du Colombier 	mp->reg[OvrClr] = Pblack;
9397dd7cddfSDavid du Colombier 
9407dd7cddfSDavid du Colombier 	if(vga->linear && mode->z != 1)
9417dd7cddfSDavid du Colombier 		ctlr->flag |= Ulinear;
9427dd7cddfSDavid du Colombier 
9437dd7cddfSDavid du Colombier 	/*
9447dd7cddfSDavid du Colombier 	 * Heuristic fiddling on LT PRO.
9457dd7cddfSDavid du Colombier 	 * Do this before setdsp so the stretching is right.
9467dd7cddfSDavid du Colombier 	 */
9477dd7cddfSDavid du Colombier 	if(mp->lcdon){
9487dd7cddfSDavid du Colombier 		/* use non-shadowed registers */
9497dd7cddfSDavid du Colombier 		mp->lcd[LCD_GenCtrl] &= ~0x00000404;
9507dd7cddfSDavid du Colombier 		mp->lcd[LCD_ConfigPanel] |= 0x00004000;
9517dd7cddfSDavid du Colombier 
9527dd7cddfSDavid du Colombier 		mp->lcd[LCD_VertStretch] = 0;
953223a736eSDavid du Colombier 		y = ((mp->lcd[LCD_ExtVertStretch]>>11) & 0x7FF)+1;
954223a736eSDavid du Colombier 		if(mode->y < y){
955223a736eSDavid du Colombier 			x = (mode->y*1024)/y;
9567dd7cddfSDavid du Colombier 			mp->lcd[LCD_VertStretch] = 0xC0000000|x;
9577dd7cddfSDavid du Colombier 		}
9587dd7cddfSDavid du Colombier 		mp->lcd[LCD_ExtVertStretch] &= ~0x00400400;
959223a736eSDavid du Colombier 
960223a736eSDavid du Colombier 		/*
961223a736eSDavid du Colombier 		 * The x value doesn't seem to be available on all
962223a736eSDavid du Colombier 		 * chips so intuit it from the y value which seems to
963223a736eSDavid du Colombier 		 * be reliable.
964223a736eSDavid du Colombier 		 */
965223a736eSDavid du Colombier 		mp->lcd[LCD_HorzStretch] &= ~0xC00000FF;
966223a736eSDavid du Colombier 		x = (mp->lcd[LCD_HorzStretch]>>20) & 0xFF;
967223a736eSDavid du Colombier 		if(x == 0){
968223a736eSDavid du Colombier 			switch(y){
969223a736eSDavid du Colombier 			default:
970223a736eSDavid du Colombier 				break;
971223a736eSDavid du Colombier 			case 480:
972223a736eSDavid du Colombier 				x = 640;
973223a736eSDavid du Colombier 				break;
974223a736eSDavid du Colombier 			case 600:
975223a736eSDavid du Colombier 				x = 800;
976223a736eSDavid du Colombier 				break;
977223a736eSDavid du Colombier 			case 768:
978223a736eSDavid du Colombier 				x = 1024;
979223a736eSDavid du Colombier 				break;
980223a736eSDavid du Colombier 			case 1024:
981223a736eSDavid du Colombier 				x = 1280;
982223a736eSDavid du Colombier 				break;
983223a736eSDavid du Colombier 			}
984223a736eSDavid du Colombier 		}
985223a736eSDavid du Colombier 		else
986223a736eSDavid du Colombier 			x = (x+1)*8;
987223a736eSDavid du Colombier 		if(mode->x < x){
988223a736eSDavid du Colombier 			x = (mode->x*4096)/x;
989223a736eSDavid du Colombier 			mp->lcd[LCD_HorzStretch] |= 0xC0000000|x;
990223a736eSDavid du Colombier 		}
9917dd7cddfSDavid du Colombier 	}
9927dd7cddfSDavid du Colombier 
9937dd7cddfSDavid du Colombier 	if(ctlr->flag&Uenhanced)
9947dd7cddfSDavid du Colombier 		setdsp(vga, ctlr);
9957dd7cddfSDavid du Colombier 
9967dd7cddfSDavid du Colombier 	ctlr->flag |= Finit;
9977dd7cddfSDavid du Colombier }
9987dd7cddfSDavid du Colombier 
9997dd7cddfSDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)10007dd7cddfSDavid du Colombier load(Vga* vga, Ctlr* ctlr)
10017dd7cddfSDavid du Colombier {
10027dd7cddfSDavid du Colombier 	Mach64xx *mp;
10037dd7cddfSDavid du Colombier 	int i;
10047dd7cddfSDavid du Colombier 
10057dd7cddfSDavid du Colombier 	mp = vga->private;
10067dd7cddfSDavid du Colombier 
10077dd7cddfSDavid du Colombier 	/*
10087dd7cddfSDavid du Colombier 	 * Unlock the CRTC and LCD registers.
10097dd7cddfSDavid du Colombier 	 */
10107dd7cddfSDavid du Colombier 	mp->iow32(mp, CrtcGenCntl, mp->ior32(mp, CrtcGenCntl)&~0x00400000);
10117dd7cddfSDavid du Colombier 	if(mp->lcdon)
10127dd7cddfSDavid du Colombier 		lcdw32(mp, LCD_GenCtrl, mp->lcd[LCD_GenCtrl]|0x80000000);
10137dd7cddfSDavid du Colombier 
10147dd7cddfSDavid du Colombier 	/*
10157dd7cddfSDavid du Colombier 	 * Always use an aperture on a 16Mb boundary.
10167dd7cddfSDavid du Colombier 	 */
10177dd7cddfSDavid du Colombier 	if(ctlr->flag & Ulinear)
10187dd7cddfSDavid du Colombier 		mp->reg[ConfigCntl] = ((vga->vmb/(4*1024*1024))<<4)|0x02;
10197dd7cddfSDavid du Colombier 
10207dd7cddfSDavid du Colombier 	mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
10217dd7cddfSDavid du Colombier 
10227dd7cddfSDavid du Colombier 	mp->iow32(mp, GenTestCntl, 0);
10237dd7cddfSDavid du Colombier 	mp->iow32(mp, GenTestCntl, 0x100);
10247dd7cddfSDavid du Colombier 
10257dd7cddfSDavid du Colombier 	if((ctlr->flag&Uenhanced) == 0)
10267dd7cddfSDavid du Colombier 	  mp->iow32(mp, MemCntl, mp->reg[MemCntl] & ~0x70000);
10277dd7cddfSDavid du Colombier 	mp->iow32(mp, BusCntl, mp->reg[BusCntl]);
10287dd7cddfSDavid du Colombier 	mp->iow32(mp, HTotalDisp, mp->reg[HTotalDisp]);
10297dd7cddfSDavid du Colombier 	mp->iow32(mp, HSyncStrtWid, mp->reg[HSyncStrtWid]);
10307dd7cddfSDavid du Colombier 	mp->iow32(mp, VTotalDisp, mp->reg[VTotalDisp]);
10317dd7cddfSDavid du Colombier 	mp->iow32(mp, VSyncStrtWid, mp->reg[VSyncStrtWid]);
10327dd7cddfSDavid du Colombier 	mp->iow32(mp, IntCntl, mp->reg[IntCntl]);
10337dd7cddfSDavid du Colombier 	mp->iow32(mp, OffPitch, mp->reg[OffPitch]);
10347dd7cddfSDavid du Colombier 	if(mp->lcdon){
10357dd7cddfSDavid du Colombier 		for(i=0; i<Nlcd; i++)
10367dd7cddfSDavid du Colombier 			lcdw32(mp, i, mp->lcd[i]);
10377dd7cddfSDavid du Colombier 	}
10387dd7cddfSDavid du Colombier 
10397dd7cddfSDavid du Colombier 	mp->iow32(mp, GenTestCntl, mp->reg[GenTestCntl]);
10407dd7cddfSDavid du Colombier 	mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
10417dd7cddfSDavid du Colombier 	mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
10427dd7cddfSDavid du Colombier 	mp->iow32(mp, OvrClr, mp->reg[OvrClr]);
10437dd7cddfSDavid du Colombier 	mp->iow32(mp, OvrWidLR, mp->reg[OvrWidLR]);
10447dd7cddfSDavid du Colombier 	mp->iow32(mp, OvrWidTB, mp->reg[OvrWidTB]);
10457dd7cddfSDavid du Colombier 	if(ctlr->flag&Uenhanced){
10467dd7cddfSDavid du Colombier 	  mp->iow32(mp, DacRegs, mp->reg[DacRegs]);
10477dd7cddfSDavid du Colombier 	  mp->iow32(mp, DacCntl, mp->reg[DacCntl]);
10487dd7cddfSDavid du Colombier 	  mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]&~0x02000000);
10497dd7cddfSDavid du Colombier 	  mp->iow32(mp, DspOnOff, mp->reg[DspOnOff]);
10507dd7cddfSDavid du Colombier 	  mp->iow32(mp, DspConfig, mp->reg[DspConfig]);
10517dd7cddfSDavid du Colombier 	  mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
10527dd7cddfSDavid du Colombier 	  pllw(mp, PLLx, mp->pll[PLLx]);
10537dd7cddfSDavid du Colombier 	}
10547dd7cddfSDavid du Colombier 	pllw(mp, PLLn2, mp->pll[PLLn2]);
10557dd7cddfSDavid du Colombier 	pllw(mp, PLLp, mp->pll[PLLp]);
10567dd7cddfSDavid du Colombier 	pllw(mp, PLLn3, mp->pll[PLLn3]);
10577dd7cddfSDavid du Colombier 
10587dd7cddfSDavid du Colombier 	mp->iow32(mp, ClockCntl, mp->reg[ClockCntl]);
10597dd7cddfSDavid du Colombier 	mp->iow32(mp, ClockCntl, 0x40|mp->reg[ClockCntl]);
10607dd7cddfSDavid du Colombier 
10617dd7cddfSDavid du Colombier 	mp->iow32(mp, DpPixWidth, mp->reg[DpPixWidth]);
10627dd7cddfSDavid du Colombier 
10637dd7cddfSDavid du Colombier 	if(vga->mode->z > 8){
10647dd7cddfSDavid du Colombier 		int sh, i;
10657dd7cddfSDavid du Colombier 		/*
10667dd7cddfSDavid du Colombier 		 * We need to initialize the palette, since the DACs use it
10677dd7cddfSDavid du Colombier 		 * in true color modes.  First see if the card supports an
10687dd7cddfSDavid du Colombier 		 * 8-bit DAC.
10697dd7cddfSDavid du Colombier 		 */
10707dd7cddfSDavid du Colombier 		mp->iow32(mp, DacCntl, mp->reg[DacCntl] | 0x100);
10717dd7cddfSDavid du Colombier 		if(mp->ior32(mp, DacCntl)&0x100){
10727dd7cddfSDavid du Colombier 			/* card appears to support it */
10737dd7cddfSDavid du Colombier 			vgactlw("palettedepth", "8");
10747dd7cddfSDavid du Colombier 			mp->reg[DacCntl] |= 0x100;
10757dd7cddfSDavid du Colombier 		}
10767dd7cddfSDavid du Colombier 
10777dd7cddfSDavid du Colombier 		if(mp->reg[DacCntl] & 0x100)
10787dd7cddfSDavid du Colombier 			sh = 0;	/* 8-bit DAC */
10797dd7cddfSDavid du Colombier 		else
10807dd7cddfSDavid du Colombier 			sh = 2;	/* 6-bit DAC */
10817dd7cddfSDavid du Colombier 
10827dd7cddfSDavid du Colombier 		for(i=0; i<256; i++)
10837dd7cddfSDavid du Colombier 			setpalette(i, i>>sh, i>>sh, i>>sh);
10847dd7cddfSDavid du Colombier 	}
10857dd7cddfSDavid du Colombier 
10867dd7cddfSDavid du Colombier 	ctlr->flag |= Fload;
10877dd7cddfSDavid du Colombier }
10887dd7cddfSDavid du Colombier 
10897dd7cddfSDavid du Colombier static void
pixelclock(Vga * vga,Ctlr * ctlr)10907dd7cddfSDavid du Colombier pixelclock(Vga* vga, Ctlr* ctlr)
10917dd7cddfSDavid du Colombier {
10927dd7cddfSDavid du Colombier 	Mach64xx *mp;
10937dd7cddfSDavid du Colombier 	ushort table, s;
10947dd7cddfSDavid du Colombier 	int memclk, ref_freq, ref_divider, min_freq, max_freq;
10957dd7cddfSDavid du Colombier 	int feedback, nmult, pd, post, value;
10967dd7cddfSDavid du Colombier 	int clock;
10977dd7cddfSDavid du Colombier 
10987dd7cddfSDavid du Colombier 	/*
10997dd7cddfSDavid du Colombier 	 * Find the pixel clock from the BIOS and current
11007dd7cddfSDavid du Colombier 	 * settings. Lifted from the ATI-supplied example code.
11017dd7cddfSDavid du Colombier 	 * The clocks stored in the BIOS table are in kHz/10.
11027dd7cddfSDavid du Colombier 	 *
11037dd7cddfSDavid du Colombier 	 * This is the clock LCDs use in vgadb to set the DSP
11047dd7cddfSDavid du Colombier 	 * values.
11057dd7cddfSDavid du Colombier 	 */
11067dd7cddfSDavid du Colombier 	mp = vga->private;
11077dd7cddfSDavid du Colombier 
11087dd7cddfSDavid du Colombier 	/*
11097dd7cddfSDavid du Colombier 	 * GetPLLInfo()
11107dd7cddfSDavid du Colombier 	 */
11117dd7cddfSDavid du Colombier 	table = *(ushort*)readbios(sizeof table, 0xc0048);
11127dd7cddfSDavid du Colombier 	trace("rom table offset %uX\n", table);
11137dd7cddfSDavid du Colombier 	table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
11147dd7cddfSDavid du Colombier 	trace("freq table offset %uX\n", table);
11157dd7cddfSDavid du Colombier 	s = *(ushort*)readbios(sizeof s, 0xc0000+table+18);
11167dd7cddfSDavid du Colombier 	memclk = s*10000;
11177dd7cddfSDavid du Colombier 	trace("memclk %ud\n", memclk);
11187dd7cddfSDavid du Colombier 	s = *(ushort*)readbios(sizeof s, 0xc0000+table+8);
11197dd7cddfSDavid du Colombier 	ref_freq = s*10000;
11207dd7cddfSDavid du Colombier 	trace("ref_freq %ud\n", ref_freq);
11217dd7cddfSDavid du Colombier 	s = *(ushort*)readbios(sizeof s, 0xc0000+table+10);
11227dd7cddfSDavid du Colombier 	ref_divider = s;
11237dd7cddfSDavid du Colombier 	trace("ref_divider %ud\n", ref_divider);
11247dd7cddfSDavid du Colombier 	s = *(ushort*)readbios(sizeof s, 0xc0000+table+2);
11257dd7cddfSDavid du Colombier 	min_freq = s*10000;
11267dd7cddfSDavid du Colombier 	trace("min_freq %ud\n", min_freq);
11277dd7cddfSDavid du Colombier 	s = *(ushort*)readbios(sizeof s, 0xc0000+table+4);
11287dd7cddfSDavid du Colombier 	max_freq = s*10000;
11297dd7cddfSDavid du Colombier 	trace("max_freq %ud\n", max_freq);
11307dd7cddfSDavid du Colombier 
11317dd7cddfSDavid du Colombier 	/*
11327dd7cddfSDavid du Colombier 	 * GetDivider()
11337dd7cddfSDavid du Colombier 	 */
11347dd7cddfSDavid du Colombier 	pd = mp->pll[PLLp] & 0x03;
11357dd7cddfSDavid du Colombier 	value = (mp->pll[PLLx] & 0x10)>>2;
11367dd7cddfSDavid du Colombier 	trace("pd %uX value %uX (|%d)\n", pd, value, value|pd);
11377dd7cddfSDavid du Colombier 	value |= pd;
11387dd7cddfSDavid du Colombier 	post = 0;
11397dd7cddfSDavid du Colombier 	switch(value){
11407dd7cddfSDavid du Colombier 	case 0:
11417dd7cddfSDavid du Colombier 		post = 1;
11427dd7cddfSDavid du Colombier 		break;
11437dd7cddfSDavid du Colombier 	case 1:
11447dd7cddfSDavid du Colombier 		post = 2;
11457dd7cddfSDavid du Colombier 		break;
11467dd7cddfSDavid du Colombier 	case 2:
11477dd7cddfSDavid du Colombier 		post = 4;
11487dd7cddfSDavid du Colombier 		break;
11497dd7cddfSDavid du Colombier 	case 3:
11507dd7cddfSDavid du Colombier 		post = 8;
11517dd7cddfSDavid du Colombier 		break;
11527dd7cddfSDavid du Colombier 	case 4:
11537dd7cddfSDavid du Colombier 		post = 3;
11547dd7cddfSDavid du Colombier 		break;
11557dd7cddfSDavid du Colombier 	case 5:
11567dd7cddfSDavid du Colombier 		post = 0;
11577dd7cddfSDavid du Colombier 		break;
11587dd7cddfSDavid du Colombier 	case 6:
11597dd7cddfSDavid du Colombier 		post = 6;
11607dd7cddfSDavid du Colombier 		break;
11617dd7cddfSDavid du Colombier 	case 7:
11627dd7cddfSDavid du Colombier 		post = 12;
11637dd7cddfSDavid du Colombier 		break;
11647dd7cddfSDavid du Colombier 	}
11657dd7cddfSDavid du Colombier 	trace("post = %d\n", post);
11667dd7cddfSDavid du Colombier 
11677dd7cddfSDavid du Colombier 	feedback = mp->pll[PLLn0];
11687dd7cddfSDavid du Colombier 	if(mp->pll[PLLx] & 0x08)
11697dd7cddfSDavid du Colombier 		nmult = 4;
11707dd7cddfSDavid du Colombier 	else
11717dd7cddfSDavid du Colombier 		nmult = 2;
11727dd7cddfSDavid du Colombier 
11737dd7cddfSDavid du Colombier 	clock = (ref_freq/10000)*nmult*feedback;
11747dd7cddfSDavid du Colombier 	clock /= ref_divider*post;
11757dd7cddfSDavid du Colombier 	clock *= 10000;
11767dd7cddfSDavid du Colombier 
11777dd7cddfSDavid du Colombier 	Bprint(&stdout, "%s pixel clock = %ud\n", ctlr->name, clock);
11787dd7cddfSDavid du Colombier }
11797dd7cddfSDavid du Colombier 
118059cc4ca5SDavid du Colombier static void dumpmach64bios(Mach64xx*);
118159cc4ca5SDavid du Colombier 
11827dd7cddfSDavid du Colombier static void
dump(Vga * vga,Ctlr * ctlr)11837dd7cddfSDavid du Colombier dump(Vga* vga, Ctlr* ctlr)
11847dd7cddfSDavid du Colombier {
11857dd7cddfSDavid du Colombier 	Mach64xx *mp;
11867dd7cddfSDavid du Colombier 	int i, m, n, p;
11877dd7cddfSDavid du Colombier 	double f;
118859cc4ca5SDavid du Colombier 	static int first = 1;
11897dd7cddfSDavid du Colombier 
11907dd7cddfSDavid du Colombier 	if((mp = vga->private) == 0)
11917dd7cddfSDavid du Colombier 		return;
11927dd7cddfSDavid du Colombier 
119359cc4ca5SDavid du Colombier 	Bprint(&stdout, "%s pci %p io %lux %s\n", ctlr->name,
119459cc4ca5SDavid du Colombier 		mp->pci, mp->io, mp->ior32 == pciior32 ? "pciregs" : "ioregs");
11957dd7cddfSDavid du Colombier 	if(mp->pci)
11967dd7cddfSDavid du Colombier 		Bprint(&stdout, "%s ccru %ux\n", ctlr->name, mp->pci->ccru);
11977dd7cddfSDavid du Colombier 	for(i = 0; i < Nreg; i++)
11987dd7cddfSDavid du Colombier 		Bprint(&stdout, "%s %-*s%.8luX\n",
11997dd7cddfSDavid du Colombier 			ctlr->name, 20, iorname[i], mp->reg[i]);
12007dd7cddfSDavid du Colombier 
12017dd7cddfSDavid du Colombier 	printitem(ctlr->name, "PLL");
12027dd7cddfSDavid du Colombier 	for(i = 0; i < Npll; i++)
12037dd7cddfSDavid du Colombier 		printreg(mp->pll[i]);
12047dd7cddfSDavid du Colombier 	Bprint(&stdout, "\n");
12057dd7cddfSDavid du Colombier 
12067dd7cddfSDavid du Colombier 	switch(mp->reg[ConfigChipId] & 0xFFFF){
12077dd7cddfSDavid du Colombier 	default:
12087dd7cddfSDavid du Colombier 		break;
1209223a736eSDavid du Colombier 	case ('L'<<8)|'B':		/* 4C42: Rage LTPro AGP */
12107dd7cddfSDavid du Colombier 	case ('L'<<8)|'I':		/* 4C49: Rage 3D LTPro */
12117dd7cddfSDavid du Colombier 	case ('L'<<8)|'M':		/* 4C4D: Rage Mobility */
12127dd7cddfSDavid du Colombier 	case ('L'<<8)|'P':		/* 4C50: Rage 3D LTPro */
12137dd7cddfSDavid du Colombier 		for(i = 0; i < Nlcd; i++)
12147dd7cddfSDavid du Colombier 			Bprint(&stdout, "%s %-*s%.8luX\n",
12157dd7cddfSDavid du Colombier 				ctlr->name, 20, lcdname[i], mp->lcd[i]);
12167dd7cddfSDavid du Colombier 		break;
12177dd7cddfSDavid du Colombier 	}
12187dd7cddfSDavid du Colombier 
12197dd7cddfSDavid du Colombier 	/*
12207dd7cddfSDavid du Colombier 	 *     (2*r*n)
12217dd7cddfSDavid du Colombier 	 * f = -------
12227dd7cddfSDavid du Colombier 	 *	(m*p)
12237dd7cddfSDavid du Colombier 	 */
12247dd7cddfSDavid du Colombier 	m = mp->pll[2];
12257dd7cddfSDavid du Colombier 	for(i = 0; i < 4; i++){
12267dd7cddfSDavid du Colombier 		n = mp->pll[7+i];
12277dd7cddfSDavid du Colombier 		p = (mp->pll[6]>>(i*2)) & 0x03;
12287dd7cddfSDavid du Colombier 		p |= (mp->pll[11]>>(2+i)) & 0x04;
12297dd7cddfSDavid du Colombier 		switch(p){
12307dd7cddfSDavid du Colombier 		case 0:
12317dd7cddfSDavid du Colombier 		case 1:
12327dd7cddfSDavid du Colombier 		case 2:
12337dd7cddfSDavid du Colombier 		case 3:
12347dd7cddfSDavid du Colombier 			p = 1<<p;
12357dd7cddfSDavid du Colombier 			break;
12367dd7cddfSDavid du Colombier 		case 4+0:
12377dd7cddfSDavid du Colombier 			p = 3;
12387dd7cddfSDavid du Colombier 			break;
12397dd7cddfSDavid du Colombier 		case 4+2:
12407dd7cddfSDavid du Colombier 			p = 6;
12417dd7cddfSDavid du Colombier 			break;
12427dd7cddfSDavid du Colombier 		case 4+3:
12437dd7cddfSDavid du Colombier 			p = 12;
12447dd7cddfSDavid du Colombier 			break;
12457dd7cddfSDavid du Colombier 
12467dd7cddfSDavid du Colombier 		default:
12477dd7cddfSDavid du Colombier 		case 4+1:
12487dd7cddfSDavid du Colombier 			p = -1;
12497dd7cddfSDavid du Colombier 			break;
12507dd7cddfSDavid du Colombier 		}
125159cc4ca5SDavid du Colombier 		if(m*p == 0)
125259cc4ca5SDavid du Colombier 			Bprint(&stdout, "unknown VCLK%d\n", i);
125359cc4ca5SDavid du Colombier 		else {
12547dd7cddfSDavid du Colombier 			f = (2.0*RefFreq*n)/(m*p) + 0.5;
12557dd7cddfSDavid du Colombier 			Bprint(&stdout, "%s VCLK%d\t%ud\n", ctlr->name, i, (int)f);
12567dd7cddfSDavid du Colombier 		}
125759cc4ca5SDavid du Colombier 	}
12587dd7cddfSDavid du Colombier 
12597dd7cddfSDavid du Colombier 	pixelclock(vga, ctlr);
126059cc4ca5SDavid du Colombier 
126159cc4ca5SDavid du Colombier 	if(first) {
126259cc4ca5SDavid du Colombier 		first = 0;
126359cc4ca5SDavid du Colombier 		dumpmach64bios(mp);
126459cc4ca5SDavid du Colombier 	}
126559cc4ca5SDavid du Colombier }
126659cc4ca5SDavid du Colombier 
126759cc4ca5SDavid du Colombier enum {
126859cc4ca5SDavid du Colombier 	ClockFixed=0,
126959cc4ca5SDavid du Colombier 	ClockIcs2595,
127059cc4ca5SDavid du Colombier 	ClockStg1703,
127159cc4ca5SDavid du Colombier 	ClockCh8398,
127259cc4ca5SDavid du Colombier 	ClockInternal,
127359cc4ca5SDavid du Colombier 	ClockAtt20c408,
127459cc4ca5SDavid du Colombier 	ClockIbmrgb514
127559cc4ca5SDavid du Colombier };
127659cc4ca5SDavid du Colombier 
127759cc4ca5SDavid du Colombier /*
127859cc4ca5SDavid du Colombier  * mostly derived from the xfree86 probe routines.
127959cc4ca5SDavid du Colombier  */
128059cc4ca5SDavid du Colombier static void
dumpmach64bios(Mach64xx * mp)128159cc4ca5SDavid du Colombier dumpmach64bios(Mach64xx *mp)
128259cc4ca5SDavid du Colombier {
128359cc4ca5SDavid du Colombier 	int i, romtable, clocktable, freqtable, lcdtable, lcdpanel;
1284*a8453668SDavid du Colombier 	uchar bios[0x10000];
128559cc4ca5SDavid du Colombier 
128659cc4ca5SDavid du Colombier 	memmove(bios, readbios(sizeof bios, 0xC0000), sizeof bios);
128759cc4ca5SDavid du Colombier 
128859cc4ca5SDavid du Colombier 	/* find magic string */
128959cc4ca5SDavid du Colombier 	for(i=0; i<1024; i++)
129059cc4ca5SDavid du Colombier 		if(strncmp((char*)bios+i, " 761295520", 10) == 0)
129159cc4ca5SDavid du Colombier 			break;
129259cc4ca5SDavid du Colombier 
129359cc4ca5SDavid du Colombier 	if(i==1024) {
129459cc4ca5SDavid du Colombier 		Bprint(&stdout, "no ATI bios found\n");
129559cc4ca5SDavid du Colombier 		return;
129659cc4ca5SDavid du Colombier 	}
129759cc4ca5SDavid du Colombier 
129859cc4ca5SDavid du Colombier 	/* this is horribly endian dependent.  sorry. */
129959cc4ca5SDavid du Colombier 	romtable = *(ushort*)(bios+0x48);
130059cc4ca5SDavid du Colombier 	if(romtable+0x12 > sizeof(bios)) {
130159cc4ca5SDavid du Colombier 		Bprint(&stdout, "couldn't find ATI rom table\n");
130259cc4ca5SDavid du Colombier 		return;
130359cc4ca5SDavid du Colombier 	}
130459cc4ca5SDavid du Colombier 
130559cc4ca5SDavid du Colombier 	clocktable = *(ushort*)(bios+romtable+0x10);
130659cc4ca5SDavid du Colombier 	if(clocktable+0x0C > sizeof(bios)) {
130759cc4ca5SDavid du Colombier 		Bprint(&stdout, "couldn't find ATI clock table\n");
130859cc4ca5SDavid du Colombier 		return;
130959cc4ca5SDavid du Colombier 	}
131059cc4ca5SDavid du Colombier 
1311*a8453668SDavid du Colombier 	freqtable = *(ushort*)(bios+clocktable-2);
131259cc4ca5SDavid du Colombier 	if(freqtable+0x20 > sizeof(bios)) {
131359cc4ca5SDavid du Colombier 		Bprint(&stdout, "couldn't find ATI frequency table\n");
131459cc4ca5SDavid du Colombier 		return;
131559cc4ca5SDavid du Colombier 	}
131659cc4ca5SDavid du Colombier 
131759cc4ca5SDavid du Colombier 	Bprint(&stdout, "ATI BIOS rom 0x%x freq 0x%x clock 0x%x\n", romtable, freqtable, clocktable);
131859cc4ca5SDavid du Colombier 	Bprint(&stdout, "clocks:");
131959cc4ca5SDavid du Colombier 	for(i=0; i<16; i++)
132059cc4ca5SDavid du Colombier 		Bprint(&stdout, " %d", *(ushort*)(bios+freqtable+2*i));
132159cc4ca5SDavid du Colombier 	Bprint(&stdout, "\n");
132259cc4ca5SDavid du Colombier 
132359cc4ca5SDavid du Colombier 	Bprint(&stdout, "programmable clock: %d\n", bios[clocktable]);
132459cc4ca5SDavid du Colombier 	Bprint(&stdout, "clock to program: %d\n", bios[clocktable+6]);
132559cc4ca5SDavid du Colombier 
132659cc4ca5SDavid du Colombier 	if(*(ushort*)(bios+clocktable+8) != 1430) {
132759cc4ca5SDavid du Colombier 		Bprint(&stdout, "reference numerator: %d\n", *(ushort*)(bios+clocktable+8)*10);
132859cc4ca5SDavid du Colombier 		Bprint(&stdout, "reference denominator: 1\n");
132959cc4ca5SDavid du Colombier 	} else {
133059cc4ca5SDavid du Colombier 		Bprint(&stdout, "default reference numerator: 157500\n");
133159cc4ca5SDavid du Colombier 		Bprint(&stdout, "default reference denominator: 11\n");
133259cc4ca5SDavid du Colombier 	}
133359cc4ca5SDavid du Colombier 
133459cc4ca5SDavid du Colombier 	switch(bios[clocktable]) {
133559cc4ca5SDavid du Colombier 	case ClockIcs2595:
133659cc4ca5SDavid du Colombier 		Bprint(&stdout, "ics2595\n");
133759cc4ca5SDavid du Colombier 		Bprint(&stdout, "reference divider: %d\n", *(ushort*)(bios+clocktable+0x0A));
133859cc4ca5SDavid du Colombier 		break;
133959cc4ca5SDavid du Colombier 	case ClockStg1703:
134059cc4ca5SDavid du Colombier 		Bprint(&stdout, "stg1703\n");
134159cc4ca5SDavid du Colombier 		break;
134259cc4ca5SDavid du Colombier 	case ClockCh8398:
134359cc4ca5SDavid du Colombier 		Bprint(&stdout, "ch8398\n");
134459cc4ca5SDavid du Colombier 		break;
134559cc4ca5SDavid du Colombier 	case ClockInternal:
134659cc4ca5SDavid du Colombier 		Bprint(&stdout, "internal clock\n");
134759cc4ca5SDavid du Colombier 		Bprint(&stdout, "reference divider in plls\n");
134859cc4ca5SDavid du Colombier 		break;
134959cc4ca5SDavid du Colombier 	case ClockAtt20c408:
135059cc4ca5SDavid du Colombier 		Bprint(&stdout, "att 20c408\n");
135159cc4ca5SDavid du Colombier 		break;
135259cc4ca5SDavid du Colombier 	case ClockIbmrgb514:
135359cc4ca5SDavid du Colombier 		Bprint(&stdout, "ibm rgb514\n");
135459cc4ca5SDavid du Colombier 		Bprint(&stdout, "clock to program = 7\n");
135559cc4ca5SDavid du Colombier 		break;
135659cc4ca5SDavid du Colombier 	default:
135759cc4ca5SDavid du Colombier 		Bprint(&stdout, "unknown clock\n");
135859cc4ca5SDavid du Colombier 		break;
135959cc4ca5SDavid du Colombier 	}
136059cc4ca5SDavid du Colombier 
136159cc4ca5SDavid du Colombier 	USED(mp);
136259cc4ca5SDavid du Colombier 	if(1 || mp->lcdpanelid) {
136359cc4ca5SDavid du Colombier 		lcdtable = *(ushort*)(bios+0x78);
136459cc4ca5SDavid du Colombier 		if(lcdtable+5 > sizeof bios || lcdtable+bios[lcdtable+5] > sizeof bios) {
136559cc4ca5SDavid du Colombier 			Bprint(&stdout, "can't find lcd bios table\n");
136659cc4ca5SDavid du Colombier 			goto NoLcd;
136759cc4ca5SDavid du Colombier 		}
136859cc4ca5SDavid du Colombier 
136959cc4ca5SDavid du Colombier 		lcdpanel = *(ushort*)(bios+lcdtable+0x0A);
137059cc4ca5SDavid du Colombier 		if(lcdpanel+0x1D > sizeof bios /*|| bios[lcdpanel] != mp->lcdpanelid*/) {
137159cc4ca5SDavid du Colombier 			Bprint(&stdout, "can't find lcd bios table0\n");
137259cc4ca5SDavid du Colombier 			goto NoLcd;
137359cc4ca5SDavid du Colombier 		}
137459cc4ca5SDavid du Colombier 
137559cc4ca5SDavid du Colombier 		Bprint(&stdout, "panelid %d x %d y %d\n", bios[lcdpanel], *(ushort*)(bios+lcdpanel+0x19), *(ushort*)(bios+lcdpanel+0x1B));
137659cc4ca5SDavid du Colombier 	}
137759cc4ca5SDavid du Colombier NoLcd:;
13787dd7cddfSDavid du Colombier }
13797dd7cddfSDavid du Colombier 
13807dd7cddfSDavid du Colombier Ctlr mach64xx = {
13817dd7cddfSDavid du Colombier 	"mach64xx",			/* name */
13827dd7cddfSDavid du Colombier 	snarf,				/* snarf */
13837dd7cddfSDavid du Colombier 	0,				/* options */
13847dd7cddfSDavid du Colombier 	init,				/* init */
13857dd7cddfSDavid du Colombier 	load,				/* load */
13867dd7cddfSDavid du Colombier 	dump,				/* dump */
13877dd7cddfSDavid du Colombier };
13887dd7cddfSDavid du Colombier 
13897dd7cddfSDavid du Colombier Ctlr mach64xxhwgc = {
13907dd7cddfSDavid du Colombier 	"mach64xxhwgc",			/* name */
13917dd7cddfSDavid du Colombier 	0,				/* snarf */
13927dd7cddfSDavid du Colombier 	0,				/* options */
13937dd7cddfSDavid du Colombier 	0,				/* init */
13947dd7cddfSDavid du Colombier 	0,				/* load */
13957dd7cddfSDavid du Colombier 	0,				/* dump */
13967dd7cddfSDavid du Colombier };
13977dd7cddfSDavid du Colombier 
139859cc4ca5SDavid du Colombier 
1399