summaryrefslogtreecommitdiff
path: root/sys/src/cmd/aux/vga/mach64xx.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/aux/vga/mach64xx.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/aux/vga/mach64xx.c')
-rwxr-xr-xsys/src/cmd/aux/vga/mach64xx.c1398
1 files changed, 1398 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/vga/mach64xx.c b/sys/src/cmd/aux/vga/mach64xx.c
new file mode 100755
index 000000000..b2c549eac
--- /dev/null
+++ b/sys/src/cmd/aux/vga/mach64xx.c
@@ -0,0 +1,1398 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#include "pci.h"
+#include "vga.h"
+
+/*
+ * ATI Mach64 family.
+ */
+enum {
+ HTotalDisp,
+ HSyncStrtWid,
+ VTotalDisp,
+ VSyncStrtWid,
+ VlineCrntVline,
+ OffPitch,
+ IntCntl,
+ CrtcGenCntl,
+
+ OvrClr,
+ OvrWidLR,
+ OvrWidTB,
+
+ CurClr0,
+ CurClr1,
+ CurOffset,
+ CurHVposn,
+ CurHVoff,
+
+ ScratchReg0,
+ ScratchReg1, /* Scratch Register (BIOS info) */
+ ClockCntl,
+ BusCntl,
+ MemCntl,
+ ExtMemCntl,
+ MemVgaWpSel,
+ MemVgaRpSel,
+ DacRegs,
+ DacCntl,
+ GenTestCntl,
+ ConfigCntl, /* Configuration control */
+ ConfigChipId,
+ ConfigStat0, /* Configuration status 0 */
+ ConfigStat1, /* Configuration status 1 */
+ ConfigStat2,
+ DspConfig, /* Rage */
+ DspOnOff, /* Rage */
+
+ DpBkgdClr,
+ DpChainMsk,
+ DpFrgdClr,
+ DpMix,
+ DpPixWidth,
+ DpSrc,
+ DpWriteMsk,
+
+ LcdIndex,
+ LcdData,
+
+ Nreg,
+
+ TvIndex = 0x1D,
+ TvData = 0x27,
+
+ LCD_ConfigPanel = 0,
+ LCD_GenCtrl,
+ LCD_DstnCntl,
+ LCD_HfbPitchAddr,
+ LCD_HorzStretch,
+ LCD_VertStretch,
+ LCD_ExtVertStretch,
+ LCD_LtGio,
+ LCD_PowerMngmnt,
+ LCD_ZvgPio,
+ Nlcd,
+};
+
+static char* iorname[Nreg] = {
+ "HTotalDisp",
+ "HSyncStrtWid",
+ "VTotalDisp",
+ "VSyncStrtWid",
+ "VlineCrntVline",
+ "OffPitch",
+ "IntCntl",
+ "CrtcGenCntl",
+
+ "OvrClr",
+ "OvrWidLR",
+ "OvrWidTB",
+
+ "CurClr0",
+ "CurClr1",
+ "CurOffset",
+ "CurHVposn",
+ "CurHVoff",
+
+ "ScratchReg0",
+ "ScratchReg1",
+ "ClockCntl",
+ "BusCntl",
+ "MemCntl",
+ "ExtMemCntl",
+ "MemVgaWpSel",
+ "MemVgaRpSel",
+ "DacRegs",
+ "DacCntl",
+ "GenTestCntl",
+ "ConfigCntl",
+ "ConfigChipId",
+ "ConfigStat0",
+ "ConfigStat1",
+ "ConfigStat2",
+ "DspConfig",
+ "DspOnOff",
+
+ "DpBkgdClr",
+ "DpChainMsk",
+ "DpFrgdClr",
+ "DpMix",
+ "DpPixWidth",
+ "DpSrc",
+ "DpWriteMsk",
+
+ "LcdIndex",
+ "LcdData",
+};
+
+static char* lcdname[Nlcd] = {
+ "LCD ConfigPanel",
+ "LCD GenCntl",
+ "LCD DstnCntl",
+ "LCD HfbPitchAddr",
+ "LCD HorzStretch",
+ "LCD VertStretch",
+ "LCD ExtVertStretch",
+ "LCD LtGio",
+ "LCD PowerMngmnt",
+ "LCD ZvgPio"
+};
+
+/*
+ * Crummy hack: all io register offsets
+ * here get IOREG or'ed in, so that we can
+ * tell the difference between an uninitialized
+ * array entry and HTotalDisp.
+ */
+enum {
+ IOREG = 0x10000,
+};
+static ushort ioregs[Nreg] = {
+ [HTotalDisp] IOREG|0x0000,
+ [HSyncStrtWid] IOREG|0x0100,
+ [VTotalDisp] IOREG|0x0200,
+ [VSyncStrtWid] IOREG|0x0300,
+ [VlineCrntVline] IOREG|0x0400,
+ [OffPitch] IOREG|0x0500,
+ [IntCntl] IOREG|0x0600,
+ [CrtcGenCntl] IOREG|0x0700,
+ [OvrClr] IOREG|0x0800,
+ [OvrWidLR] IOREG|0x0900,
+ [OvrWidTB] IOREG|0x0A00,
+ [CurClr0] IOREG|0x0B00,
+ [CurClr1] IOREG|0x0C00,
+ [CurOffset] IOREG|0x0D00,
+ [CurHVposn] IOREG|0x0E00,
+ [CurHVoff] IOREG|0x0F00,
+ [ScratchReg0] IOREG|0x1000,
+ [ScratchReg1] IOREG|0x1100,
+ [ClockCntl] IOREG|0x1200,
+ [BusCntl] IOREG|0x1300,
+ [MemCntl] IOREG|0x1400,
+ [MemVgaWpSel] IOREG|0x1500,
+ [MemVgaRpSel] IOREG|0x1600,
+ [DacRegs] IOREG|0x1700,
+ [DacCntl] IOREG|0x1800,
+ [GenTestCntl] IOREG|0x1900,
+ [ConfigCntl] IOREG|0x1A00,
+ [ConfigChipId] IOREG|0x1B00,
+ [ConfigStat0] IOREG|0x1C00,
+ [ConfigStat1] IOREG|0x1D00,
+/* [GpIo] IOREG|0x1E00, */
+/* [HTotalDisp] IOREG|0x1F00, duplicate, says XFree86 */
+};
+
+static ushort pciregs[Nreg] = {
+ [HTotalDisp] 0x00,
+ [HSyncStrtWid] 0x01,
+ [VTotalDisp] 0x02,
+ [VSyncStrtWid] 0x03,
+ [VlineCrntVline] 0x04,
+ [OffPitch] 0x05,
+ [IntCntl] 0x06,
+ [CrtcGenCntl] 0x07,
+ [DspConfig] 0x08,
+ [DspOnOff] 0x09,
+ [OvrClr] 0x10,
+ [OvrWidLR] 0x11,
+ [OvrWidTB] 0x12,
+ [CurClr0] 0x18,
+ [CurClr1] 0x19,
+ [CurOffset] 0x1A,
+ [CurHVposn] 0x1B,
+ [CurHVoff] 0x1C,
+ [ScratchReg0] 0x20,
+ [ScratchReg1] 0x21,
+ [ClockCntl] 0x24,
+ [BusCntl] 0x28,
+ [LcdIndex] 0x29,
+ [LcdData] 0x2A,
+ [ExtMemCntl] 0x2B,
+ [MemCntl] 0x2C,
+ [MemVgaWpSel] 0x2D,
+ [MemVgaRpSel] 0x2E,
+ [DacRegs] 0x30,
+ [DacCntl] 0x31,
+ [GenTestCntl] 0x34,
+ [ConfigCntl] 0x37,
+ [ConfigChipId] 0x38,
+ [ConfigStat0] 0x39,
+ [ConfigStat1] 0x25, /* rsc: was 0x3A, but that's not what the LT manual says */
+ [ConfigStat2] 0x26,
+ [DpBkgdClr] 0xB0,
+ [DpChainMsk] 0xB3,
+ [DpFrgdClr] 0xB1,
+ [DpMix] 0xB5,
+ [DpPixWidth] 0xB4,
+ [DpSrc] 0xB6,
+ [DpWriteMsk] 0xB2,
+};
+
+enum {
+ PLLm = 0x02,
+ PLLp = 0x06,
+ PLLn0 = 0x07,
+ PLLn1 = 0x08,
+ PLLn2 = 0x09,
+ PLLn3 = 0x0A,
+ PLLx = 0x0B, /* external divisor (Rage) */
+
+ Npll = 32,
+ Ntv = 1, /* actually 256, but not used */
+};
+
+typedef struct Mach64xx Mach64xx;
+struct Mach64xx {
+ ulong io;
+ Pcidev* pci;
+ int bigmem;
+ int lcdon;
+ int lcdpanelid;
+
+ ulong reg[Nreg];
+ ulong lcd[Nlcd];
+ ulong tv[Ntv];
+ uchar pll[Npll];
+
+ ulong (*ior32)(Mach64xx*, int);
+ void (*iow32)(Mach64xx*, int, ulong);
+};
+
+static ulong
+portior32(Mach64xx* mp, int r)
+{
+ if((ioregs[r] & IOREG) == 0)
+ return ~0;
+
+ return inportl(((ioregs[r] & ~IOREG)<<2)+mp->io);
+}
+
+static void
+portiow32(Mach64xx* mp, int r, ulong l)
+{
+ if((ioregs[r] & IOREG) == 0)
+ return;
+
+ outportl(((ioregs[r] & ~IOREG)<<2)+mp->io, l);
+}
+
+static ulong
+pciior32(Mach64xx* mp, int r)
+{
+ return inportl((pciregs[r]<<2)+mp->io);
+}
+
+static void
+pciiow32(Mach64xx* mp, int r, ulong l)
+{
+ outportl((pciregs[r]<<2)+mp->io, l);
+}
+
+static uchar
+pllr(Mach64xx* mp, int r)
+{
+ int io;
+
+ if(mp->ior32 == portior32)
+ io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
+ else
+ io = (pciregs[ClockCntl]<<2)+mp->io;
+
+ outportb(io+1, r<<2);
+ return inportb(io+2);
+}
+
+static void
+pllw(Mach64xx* mp, int r, uchar b)
+{
+ int io;
+
+ if(mp->ior32 == portior32)
+ io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
+ else
+ io = (pciregs[ClockCntl]<<2)+mp->io;
+
+ outportb(io+1, (r<<2)|0x02);
+ outportb(io+2, b);
+}
+
+static ulong
+lcdr32(Mach64xx *mp, ulong r)
+{
+ ulong or;
+
+ or = mp->ior32(mp, LcdIndex);
+ mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
+ return mp->ior32(mp, LcdData);
+}
+
+static void
+lcdw32(Mach64xx *mp, ulong r, ulong v)
+{
+ ulong or;
+
+ or = mp->ior32(mp, LcdIndex);
+ mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
+ mp->iow32(mp, LcdData, v);
+}
+
+static ulong
+tvr32(Mach64xx *mp, ulong r)
+{
+ outportb(mp->io+(TvIndex<<2), r&0x0F);
+ return inportl(mp->io+(TvData<<2));
+}
+
+static void
+tvw32(Mach64xx *mp, ulong r, ulong v)
+{
+ outportb(mp->io+(TvIndex<<2), r&0x0F);
+ outportl(mp->io+(TvData<<2), v);
+}
+
+static int smallmem[] = {
+ 512*1024, 1024*1024, 2*1024*1024, 4*1024*1024,
+ 6*1024*1024, 8*1024*1024, 12*1024*1024, 16*1024*1024,
+};
+
+static int bigmem[] = {
+ 512*1024, 2*512*1024, 3*512*1024, 4*512*1024,
+ 5*512*1024, 6*512*1024, 7*512*1024, 8*512*1024,
+ 5*1024*1024, 6*1024*1024, 7*1024*1024, 8*1024*1024,
+ 10*1024*1024, 12*1024*1024, 14*1024*1024, 16*1024*1024,
+};
+
+static void
+snarf(Vga* vga, Ctlr* ctlr)
+{
+ Mach64xx *mp;
+ int i;
+ ulong v;
+
+ if(vga->private == nil){
+ vga->private = alloc(sizeof(Mach64xx));
+ mp = vga->private;
+ mp->io = 0x2EC;
+ mp->ior32 = portior32;
+ mp->iow32 = portiow32;
+ mp->pci = pcimatch(0, 0x1002, 0);
+ if (mp->pci) {
+ if(v = mp->pci->mem[1].bar & ~0x3) {
+ mp->io = v;
+ mp->ior32 = pciior32;
+ mp->iow32 = pciiow32;
+ }
+ }
+ }
+
+ mp = vga->private;
+ for(i = 0; i < Nreg; i++)
+ mp->reg[i] = mp->ior32(mp, i);
+
+ for(i = 0; i < Npll; i++)
+ mp->pll[i] = pllr(mp, i);
+
+ switch(mp->reg[ConfigChipId] & 0xFFFF){
+ default:
+ mp->lcdpanelid = 0;
+ break;
+ case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
+ case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
+ case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
+ case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
+ for(i = 0; i < Nlcd; i++)
+ mp->lcd[i] = lcdr32(mp, i);
+ if(mp->lcd[LCD_GenCtrl] & 0x02)
+ mp->lcdon = 1;
+ mp->lcdpanelid = ((mp->reg[ConfigStat2]>>14) & 0x1F);
+ break;
+ }
+
+ /*
+ * Check which memory size map we are using.
+ */
+ mp->bigmem = 0;
+ switch(mp->reg[ConfigChipId] & 0xFFFF){
+ case ('G'<<8)|'B': /* 4742: 264GT PRO */
+ case ('G'<<8)|'D': /* 4744: 264GT PRO */
+ case ('G'<<8)|'I': /* 4749: 264GT PRO */
+ case ('G'<<8)|'M': /* 474D: Rage XL */
+ case ('G'<<8)|'P': /* 4750: 264GT PRO */
+ case ('G'<<8)|'Q': /* 4751: 264GT PRO */
+ case ('G'<<8)|'R': /* 4752: */
+ case ('G'<<8)|'U': /* 4755: 264GT DVD */
+ case ('G'<<8)|'V': /* 4756: Rage2C */
+ case ('G'<<8)|'Z': /* 475A: Rage2C */
+ case ('V'<<8)|'U': /* 5655: 264VT3 */
+ case ('V'<<8)|'V': /* 5656: 264VT4 */
+ case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
+ case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
+ case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
+ case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
+ mp->bigmem = 1;
+ break;
+ case ('G'<<8)|'T': /* 4754: 264GT[B] */
+ case ('V'<<8)|'T': /* 5654: 264VT/GT/VTB */
+ /*
+ * Only the VTB and GTB use the new memory encoding,
+ * and they are identified by a nonzero ChipVersion,
+ * apparently.
+ */
+ if((mp->reg[ConfigChipId] >> 24) & 0x7)
+ mp->bigmem = 1;
+ break;
+ }
+
+ /*
+ * Memory size and aperture. It's recommended
+ * to use an 8Mb aperture on a 16Mb boundary.
+ */
+ if(mp->bigmem)
+ vga->vmz = bigmem[mp->reg[MemCntl] & 0x0F];
+ else
+ vga->vmz = smallmem[mp->reg[MemCntl] & 0x07];
+ vga->vma = 16*1024*1024;
+
+ switch(mp->reg[ConfigCntl]&0x3){
+ case 0:
+ vga->apz = 16*1024*1024; /* empirical -rsc */
+ break;
+ case 1:
+ vga->apz = 4*1024*1024;
+ break;
+ case 2:
+ vga->apz = 8*1024*1024;
+ break;
+ case 3:
+ vga->apz = 2*1024*1024; /* empirical: mach64GX -rsc */
+ break;
+ }
+
+ ctlr->flag |= Fsnarf;
+}
+
+static void
+options(Vga*, Ctlr* ctlr)
+{
+ ctlr->flag |= Hlinear|Foptions;
+}
+
+static void
+clock(Vga* vga, Ctlr* ctlr)
+{
+ int clk, m, n, p;
+ double f, q;
+ Mach64xx *mp;
+
+ mp = vga->private;
+
+ /*
+ * Don't compute clock timings for LCD panels.
+ * Just use what's already there. We can't just use
+ * the frequency in the vgadb for this because
+ * the frequency being programmed into the PLLs
+ * is not the frequency being used to compute the DSP
+ * settings. The DSP-relevant frequency is the one
+ * we keep in /lib/vgadb.
+ */
+ if(mp->lcdon){
+ clk = mp->reg[ClockCntl] & 0x03;
+ n = mp->pll[7+clk];
+ p = (mp->pll[6]>>(clk*2)) & 0x03;
+ p |= (mp->pll[11]>>(2+clk)) & 0x04;
+ switch(p){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ p = 1<<p;
+ break;
+ case 4+0:
+ p = 3;
+ break;
+ case 4+2:
+ p = 6;
+ break;
+ case 4+3:
+ p = 12;
+ break;
+
+ default:
+ case 4+1:
+ p = -1;
+ break;
+ }
+ m = mp->pll[PLLm];
+ f = (2.0*RefFreq*n)/(m*p) + 0.5;
+
+ vga->m[0] = m;
+ vga->p[0] = p;
+ vga->n[0] = n;
+ vga->f[0] = f;
+ return;
+ }
+
+ if(vga->f[0] == 0)
+ vga->f[0] = vga->mode->frequency;
+ f = vga->f[0];
+
+ /*
+ * To generate a specific output frequency, the reference (m),
+ * feedback (n), and post dividers (p) must be loaded with the
+ * appropriate divide-down ratios. In the following r is the
+ * XTALIN frequency (usually RefFreq) and t is the target frequency
+ * (vga->f).
+ *
+ * Use the maximum reference divider left by the BIOS for now,
+ * otherwise MCLK might be a concern. It can be calculated as
+ * follows:
+ * Upper Limit of PLL Lock Range
+ * Minimum PLLREFCLK = -----------------------------
+ * (2*255)
+ *
+ * XTALIN
+ * m = Floor[-----------------]
+ * Minimum PLLREFCLK
+ *
+ * For an upper limit of 135MHz and XTALIN of 14.318MHz m
+ * would be 54.
+ */
+ m = mp->pll[PLLm];
+ vga->m[0] = m;
+
+ /*
+ * The post divider may be 1, 2, 4 or 8 and is determined by
+ * calculating
+ * t*m
+ * q = -----
+ * (2*r)
+ * and using the result to look-up p.
+ */
+ q = (f*m)/(2*RefFreq);
+ if(ctlr->flag&Uenhanced){
+ if(q > 255 || q < 10.6666666667)
+ error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
+ if(q > 127.5)
+ p = 1;
+ else if(q > 85)
+ p = 2;
+ else if(q > 63.75)
+ p = 3;
+ else if(q > 42.5)
+ p = 4;
+ else if(q > 31.875)
+ p = 6;
+ else if(q > 21.25)
+ p = 8;
+ else
+ p = 12;
+ }else{
+ if(q > 255 || q < 16)
+ error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
+ if(q >= 127.5)
+ p = 1;
+ else if(q >= 63.5)
+ p = 2;
+ else if(q >= 31.5)
+ p = 4;
+ else
+ p = 8;
+ }
+ vga->p[0] = p;
+
+ /*
+ * The feedback divider should be kept in the range 0x80 to 0xFF
+ * and is found from
+ * n = q*p
+ * rounded to the nearest whole number.
+ */
+ vga->n[0] = (q*p)+0.5;
+}
+
+typedef struct Meminfo Meminfo;
+struct Meminfo {
+ int latency;
+ int latch;
+ int trp; /* filled in from card */
+ int trcd; /* filled in from card */
+ int tcrd; /* filled in from card */
+ int tras; /* filled in from card */
+};
+
+enum {
+ Mdram,
+ Medo,
+ Msdram,
+ Mwram,
+};
+
+/*
+ * The manuals and documentation are silent on which settings
+ * to use for Mwdram, or how to tell which to use.
+ */
+static Meminfo meminfo[] = {
+[Mdram] { 1, 0 },
+[Medo] { 1, 2 },
+[Msdram] { 3, 1 },
+[Mwram] { 1, 3 }, /* non TYPE_A */
+};
+
+static ushort looplatencytab[2][2] = {
+ { 8, 6 }, /* DRAM: ≤1M, > 1M */
+ { 9, 8 }, /* SDRAM: ≤1M, > 1M */
+};
+
+static ushort cyclesperqwordtab[2][2] = {
+ { 3, 2 }, /* DRAM: ≤1M, > 1M */
+ { 2, 1 }, /* SDRAM: ≤1M, > 1M */
+};
+
+static int memtype[] = {
+ -1, /* disable memory access */
+ Mdram, /* basic DRAM */
+ Medo, /* EDO */
+ Medo, /* hyper page DRAM or EDO */
+ Msdram, /* SDRAM */
+ Msdram, /* SGRAM */
+ Mwram,
+ Mwram
+};
+
+/*
+ * Calculate various memory parameters so that the card
+ * fetches the right bytes at the right time. I don't claim to
+ * understand the actual calculations very well.
+ *
+ * This is remarkably useful on laptops, since knowledge of
+ * x lets us find the frequency that the screen is really running
+ * at, which is not necessarily in the VCLKs.
+ */
+static void
+setdsp(Vga* vga, Ctlr*)
+{
+ Mach64xx *mp;
+ Meminfo *mem;
+ ushort table, memclk, memtyp;
+ int i, prec, xprec, fprec;
+ ulong t;
+ double pw, x, fifosz, fifoon, fifooff;
+ ushort dspon, dspoff;
+ int afifosz, lat, ncycle, pfc, rcc;
+
+ mp = vga->private;
+
+ /*
+ * Get video ram configuration from BIOS and chip
+ */
+ table = *(ushort*)readbios(sizeof table, 0xc0048);
+ trace("rom table offset %uX\n", table);
+ table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
+ trace("freq table offset %uX\n", table);
+ memclk = *(ushort*)readbios(sizeof memclk, 0xc0000+table+18);
+ trace("memclk %ud\n", memclk);
+ memtyp = memtype[mp->reg[ConfigStat0]&07];
+ mem = &meminfo[memtyp];
+
+ /*
+ * First we need to calculate x, the number of
+ * XCLKs that one QWORD occupies in the display FIFO.
+ *
+ * For some reason, x gets stretched out if LCD stretching
+ * is turned on.
+ */
+
+ x = ((double)memclk*640000.0) /
+ ((double)vga->mode->frequency * (double)vga->mode->z);
+ if(mp->lcd[LCD_HorzStretch] & (1<<31))
+ x *= 4096.0 / (double)(mp->lcd[LCD_HorzStretch] & 0xFFFF);
+
+ trace("memclk %d... x %f...", memclk, x);
+ /*
+ * We have 14 bits to specify x in. Decide where to
+ * put the decimal (err, binary) point by counting how
+ * many significant bits are in the integer portion of x.
+ */
+ t = x;
+ for(i=31; i>=0; i--)
+ if(t & (1<<i))
+ break;
+ xprec = i+1;
+ trace("t %lud... xprec %d...", t, xprec);
+
+ /*
+ * The maximum FIFO size is the number of XCLKs per QWORD
+ * multiplied by 32, for some reason. We have 11 bits to
+ * specify fifosz.
+ */
+ fifosz = x * 32.0;
+ trace("fifosz %f...", fifosz);
+ t = fifosz;
+ for(i=31; i>=0; i--)
+ if(t & (1<<i))
+ break;
+ fprec = i+1;
+ trace("fprec %d...", fprec);
+
+ /*
+ * Precision is specified as 3 less than the number of bits
+ * in the integer part of x, and 5 less than the number of bits
+ * in the integer part of fifosz.
+ *
+ * It is bounded by zero and seven.
+ */
+ prec = (xprec-3 > fprec-5) ? xprec-3 : fprec-5;
+ if(prec < 0)
+ prec = 0;
+ if(prec > 7)
+ prec = 7;
+
+ xprec = prec+3;
+ fprec = prec+5;
+ trace("prec %d...", prec);
+
+ /*
+ * Actual fifo size
+ */
+ afifosz = (1<<fprec) / x;
+ if(afifosz > 32)
+ afifosz = 32;
+
+ fifooff = ceil(x*(afifosz-1));
+
+ /*
+ * I am suspicious of this table, lifted from ATI docs,
+ * because it doesn't agree with the Windows drivers.
+ * We always get 0x0A for lat+2 while Windows uses 0x08.
+ */
+ lat = looplatencytab[memtyp > 1][vga->vmz > 1*1024*1024];
+ trace("afifosz %d...fifooff %f...", afifosz, fifooff);
+
+ /*
+ * Page fault clock
+ */
+ t = mp->reg[MemCntl];
+ mem->trp = (t>>8)&3; /* RAS precharge time */
+ mem->trcd = (t>>10)&3; /* RAS to CAS delay */
+ mem->tcrd = (t>>12)&1; /* CAS to RAS delay */
+ mem->tras = (t>>16)&7; /* RAS low minimum pulse width */
+ pfc = mem->trp + 1 + mem->trcd + 1 + mem->tcrd;
+ trace("pfc %d...", pfc);
+
+ /*
+ * Maximum random access cycle clock.
+ */
+ ncycle = cyclesperqwordtab[memtyp > 1][vga->vmz > 1*1024*1024];
+ rcc = mem->trp + 1 + mem->tras + 1;
+ if(rcc < pfc+ncycle)
+ rcc = pfc+ncycle;
+ trace("rcc %d...", rcc);
+
+ fifoon = (rcc > floor(x)) ? rcc : floor(x);
+ fifoon += (3.0 * rcc) - 1 + pfc + ncycle;
+ trace("fifoon %f...\n", fifoon);
+ /*
+ * Now finally put the bits together.
+ * x is stored in a 14 bit field with xprec bits of integer.
+ */
+ pw = x * (1<<(14-xprec));
+ mp->reg[DspConfig] = (ulong)pw | (((lat+2)&0xF)<<16) | ((prec&7)<<20);
+
+ /*
+ * These are stored in an 11 bit field with fprec bits of integer.
+ */
+ dspon = (ushort)fifoon << (11-fprec);
+ dspoff = (ushort)fifooff << (11-fprec);
+ mp->reg[DspOnOff] = ((dspon&0x7ff) << 16) | (dspoff&0x7ff);
+}
+
+static void
+init(Vga* vga, Ctlr* ctlr)
+{
+ Mode *mode;
+ Mach64xx *mp;
+ int p, x, y;
+
+ mode = vga->mode;
+ if((mode->x > 640 || mode->y > 480) && mode->z == 1)
+ error("%s: no support for 1-bit mode other than 640x480x1\n",
+ ctlr->name);
+
+ mp = vga->private;
+ if(mode->z > 8 && mp->pci == nil)
+ error("%s: no support for >8-bit color without PCI\n",
+ ctlr->name);
+
+ /*
+ * Check for Rage chip
+ */
+ switch (mp->reg[ConfigChipId]&0xffff) {
+ case ('G'<<8)|'B': /* 4742: 264GT PRO */
+ case ('G'<<8)|'D': /* 4744: 264GT PRO */
+ case ('G'<<8)|'I': /* 4749: 264GT PRO */
+ case ('G'<<8)|'M': /* 474D: Rage XL */
+ case ('G'<<8)|'P': /* 4750: 264GT PRO */
+ case ('G'<<8)|'Q': /* 4751: 264GT PRO */
+ case ('G'<<8)|'R': /* 4752: */
+ case ('G'<<8)|'U': /* 4755: 264GT DVD */
+ case ('G'<<8)|'V': /* 4756: Rage2C */
+ case ('G'<<8)|'Z': /* 475A: Rage2C */
+ case ('V'<<8)|'U': /* 5655: 264VT3 */
+ case ('V'<<8)|'V': /* 5656: 264VT4 */
+ case ('G'<<8)|'T': /* 4754: 264GT[B] */
+ case ('V'<<8)|'T': /* 5654: 264VT/GT/VTB */
+ case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
+ case ('L'<<8)|'I': /* 4C49: 264LT PRO */
+ case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
+ case ('L'<<8)|'P': /* 4C50: 264LT PRO */
+ ctlr->flag |= Uenhanced;
+ break;
+ }
+
+ /*
+ * Always use VCLK2.
+ */
+ clock(vga, ctlr);
+ mp->pll[PLLn2] = vga->n[0];
+ mp->pll[PLLp] &= ~(0x03<<(2*2));
+ switch(vga->p[0]){
+ case 1:
+ case 3:
+ p = 0;
+ break;
+
+ case 2:
+ p = 1;
+ break;
+
+ case 4:
+ case 6:
+ p = 2;
+ break;
+
+ case 8:
+ case 12:
+ p = 3;
+ break;
+
+ default:
+ p = 3;
+ break;
+ }
+ mp->pll[PLLp] |= p<<(2*2);
+ if ((1<<p) != vga->p[0])
+ mp->pll[PLLx] |= 1<<(4+2);
+ else
+ mp->pll[PLLx] &= ~(1<<(4+2));
+ mp->reg[ClockCntl] = 2;
+
+ mp->reg[ConfigCntl] = 0;
+
+ mp->reg[CrtcGenCntl] = 0x02000000|(mp->reg[CrtcGenCntl] & ~0x01400700);
+ switch(mode->z){
+ default:
+ case 1:
+ mp->reg[CrtcGenCntl] |= 0x00000100;
+ mp->reg[DpPixWidth] = 0x00000000;
+ break;
+ case 8:
+ mp->reg[CrtcGenCntl] |= 0x01000200;
+ mp->reg[DpPixWidth] = 0x00020202;
+ break;
+ case 15:
+ mp->reg[CrtcGenCntl] |= 0x01000300;
+ mp->reg[DpPixWidth] = 0x00030303;
+ break;
+ case 16:
+ mp->reg[CrtcGenCntl] |= 0x01000400;
+ mp->reg[DpPixWidth] = 0x00040404;
+ break;
+ case 24:
+ mp->reg[CrtcGenCntl] |= 0x01000500;
+ mp->reg[DpPixWidth] = 0x00050505;
+ break;
+ case 32:
+ mp->reg[CrtcGenCntl] |= 0x01000600;
+ mp->reg[DpPixWidth] = 0x00060606;
+ break;
+ }
+
+ mp->reg[HTotalDisp] = (((mode->x>>3)-1)<<16)|((mode->ht>>3)-1);
+ mp->reg[HSyncStrtWid] = (((mode->ehs - mode->shs)>>3)<<16)
+ |((mode->shs>>3)-1);
+ if(mode->hsync == '-')
+ mp->reg[HSyncStrtWid] |= 0x00200000;
+ mp->reg[VTotalDisp] = ((mode->y-1)<<16)|(mode->vt-1);
+ mp->reg[VSyncStrtWid] = ((mode->vre - mode->vrs)<<16)|(mode->vrs-1);
+ if(mode->vsync == '-')
+ mp->reg[VSyncStrtWid] |= 0x00200000;
+ mp->reg[IntCntl] = 0;
+
+ /*
+ * This used to set it to (mode->x/(8*2))<<22 for depths < 8,
+ * but from the manual that seems wrong to me. -rsc
+ */
+ mp->reg[OffPitch] = (vga->virtx/8)<<22;
+
+ mp->reg[OvrClr] = Pblack;
+
+ if(vga->linear && mode->z != 1)
+ ctlr->flag |= Ulinear;
+
+ /*
+ * Heuristic fiddling on LT PRO.
+ * Do this before setdsp so the stretching is right.
+ */
+ if(mp->lcdon){
+ /* use non-shadowed registers */
+ mp->lcd[LCD_GenCtrl] &= ~0x00000404;
+ mp->lcd[LCD_ConfigPanel] |= 0x00004000;
+
+ mp->lcd[LCD_VertStretch] = 0;
+ y = ((mp->lcd[LCD_ExtVertStretch]>>11) & 0x7FF)+1;
+ if(mode->y < y){
+ x = (mode->y*1024)/y;
+ mp->lcd[LCD_VertStretch] = 0xC0000000|x;
+ }
+ mp->lcd[LCD_ExtVertStretch] &= ~0x00400400;
+
+ /*
+ * The x value doesn't seem to be available on all
+ * chips so intuit it from the y value which seems to
+ * be reliable.
+ */
+ mp->lcd[LCD_HorzStretch] &= ~0xC00000FF;
+ x = (mp->lcd[LCD_HorzStretch]>>20) & 0xFF;
+ if(x == 0){
+ switch(y){
+ default:
+ break;
+ case 480:
+ x = 640;
+ break;
+ case 600:
+ x = 800;
+ break;
+ case 768:
+ x = 1024;
+ break;
+ case 1024:
+ x = 1280;
+ break;
+ }
+ }
+ else
+ x = (x+1)*8;
+ if(mode->x < x){
+ x = (mode->x*4096)/x;
+ mp->lcd[LCD_HorzStretch] |= 0xC0000000|x;
+ }
+ }
+
+ if(ctlr->flag&Uenhanced)
+ setdsp(vga, ctlr);
+
+ ctlr->flag |= Finit;
+}
+
+static void
+load(Vga* vga, Ctlr* ctlr)
+{
+ Mach64xx *mp;
+ int i;
+
+ mp = vga->private;
+
+ /*
+ * Unlock the CRTC and LCD registers.
+ */
+ mp->iow32(mp, CrtcGenCntl, mp->ior32(mp, CrtcGenCntl)&~0x00400000);
+ if(mp->lcdon)
+ lcdw32(mp, LCD_GenCtrl, mp->lcd[LCD_GenCtrl]|0x80000000);
+
+ /*
+ * Always use an aperture on a 16Mb boundary.
+ */
+ if(ctlr->flag & Ulinear)
+ mp->reg[ConfigCntl] = ((vga->vmb/(4*1024*1024))<<4)|0x02;
+
+ mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
+
+ mp->iow32(mp, GenTestCntl, 0);
+ mp->iow32(mp, GenTestCntl, 0x100);
+
+ if((ctlr->flag&Uenhanced) == 0)
+ mp->iow32(mp, MemCntl, mp->reg[MemCntl] & ~0x70000);
+ mp->iow32(mp, BusCntl, mp->reg[BusCntl]);
+ mp->iow32(mp, HTotalDisp, mp->reg[HTotalDisp]);
+ mp->iow32(mp, HSyncStrtWid, mp->reg[HSyncStrtWid]);
+ mp->iow32(mp, VTotalDisp, mp->reg[VTotalDisp]);
+ mp->iow32(mp, VSyncStrtWid, mp->reg[VSyncStrtWid]);
+ mp->iow32(mp, IntCntl, mp->reg[IntCntl]);
+ mp->iow32(mp, OffPitch, mp->reg[OffPitch]);
+ if(mp->lcdon){
+ for(i=0; i<Nlcd; i++)
+ lcdw32(mp, i, mp->lcd[i]);
+ }
+
+ mp->iow32(mp, GenTestCntl, mp->reg[GenTestCntl]);
+ mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
+ mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
+ mp->iow32(mp, OvrClr, mp->reg[OvrClr]);
+ mp->iow32(mp, OvrWidLR, mp->reg[OvrWidLR]);
+ mp->iow32(mp, OvrWidTB, mp->reg[OvrWidTB]);
+ if(ctlr->flag&Uenhanced){
+ mp->iow32(mp, DacRegs, mp->reg[DacRegs]);
+ mp->iow32(mp, DacCntl, mp->reg[DacCntl]);
+ mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]&~0x02000000);
+ mp->iow32(mp, DspOnOff, mp->reg[DspOnOff]);
+ mp->iow32(mp, DspConfig, mp->reg[DspConfig]);
+ mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
+ pllw(mp, PLLx, mp->pll[PLLx]);
+ }
+ pllw(mp, PLLn2, mp->pll[PLLn2]);
+ pllw(mp, PLLp, mp->pll[PLLp]);
+ pllw(mp, PLLn3, mp->pll[PLLn3]);
+
+ mp->iow32(mp, ClockCntl, mp->reg[ClockCntl]);
+ mp->iow32(mp, ClockCntl, 0x40|mp->reg[ClockCntl]);
+
+ mp->iow32(mp, DpPixWidth, mp->reg[DpPixWidth]);
+
+ if(vga->mode->z > 8){
+ int sh, i;
+ /*
+ * We need to initialize the palette, since the DACs use it
+ * in true color modes. First see if the card supports an
+ * 8-bit DAC.
+ */
+ mp->iow32(mp, DacCntl, mp->reg[DacCntl] | 0x100);
+ if(mp->ior32(mp, DacCntl)&0x100){
+ /* card appears to support it */
+ vgactlw("palettedepth", "8");
+ mp->reg[DacCntl] |= 0x100;
+ }
+
+ if(mp->reg[DacCntl] & 0x100)
+ sh = 0; /* 8-bit DAC */
+ else
+ sh = 2; /* 6-bit DAC */
+
+ for(i=0; i<256; i++)
+ setpalette(i, i>>sh, i>>sh, i>>sh);
+ }
+
+ ctlr->flag |= Fload;
+}
+
+static void
+pixelclock(Vga* vga, Ctlr* ctlr)
+{
+ Mach64xx *mp;
+ ushort table, s;
+ int memclk, ref_freq, ref_divider, min_freq, max_freq;
+ int feedback, nmult, pd, post, value;
+ int clock;
+
+ /*
+ * Find the pixel clock from the BIOS and current
+ * settings. Lifted from the ATI-supplied example code.
+ * The clocks stored in the BIOS table are in kHz/10.
+ *
+ * This is the clock LCDs use in vgadb to set the DSP
+ * values.
+ */
+ mp = vga->private;
+
+ /*
+ * GetPLLInfo()
+ */
+ table = *(ushort*)readbios(sizeof table, 0xc0048);
+ trace("rom table offset %uX\n", table);
+ table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
+ trace("freq table offset %uX\n", table);
+ s = *(ushort*)readbios(sizeof s, 0xc0000+table+18);
+ memclk = s*10000;
+ trace("memclk %ud\n", memclk);
+ s = *(ushort*)readbios(sizeof s, 0xc0000+table+8);
+ ref_freq = s*10000;
+ trace("ref_freq %ud\n", ref_freq);
+ s = *(ushort*)readbios(sizeof s, 0xc0000+table+10);
+ ref_divider = s;
+ trace("ref_divider %ud\n", ref_divider);
+ s = *(ushort*)readbios(sizeof s, 0xc0000+table+2);
+ min_freq = s*10000;
+ trace("min_freq %ud\n", min_freq);
+ s = *(ushort*)readbios(sizeof s, 0xc0000+table+4);
+ max_freq = s*10000;
+ trace("max_freq %ud\n", max_freq);
+
+ /*
+ * GetDivider()
+ */
+ pd = mp->pll[PLLp] & 0x03;
+ value = (mp->pll[PLLx] & 0x10)>>2;
+ trace("pd %uX value %uX (|%d)\n", pd, value, value|pd);
+ value |= pd;
+ post = 0;
+ switch(value){
+ case 0:
+ post = 1;
+ break;
+ case 1:
+ post = 2;
+ break;
+ case 2:
+ post = 4;
+ break;
+ case 3:
+ post = 8;
+ break;
+ case 4:
+ post = 3;
+ break;
+ case 5:
+ post = 0;
+ break;
+ case 6:
+ post = 6;
+ break;
+ case 7:
+ post = 12;
+ break;
+ }
+ trace("post = %d\n", post);
+
+ feedback = mp->pll[PLLn0];
+ if(mp->pll[PLLx] & 0x08)
+ nmult = 4;
+ else
+ nmult = 2;
+
+ clock = (ref_freq/10000)*nmult*feedback;
+ clock /= ref_divider*post;
+ clock *= 10000;
+
+ Bprint(&stdout, "%s pixel clock = %ud\n", ctlr->name, clock);
+}
+
+static void dumpmach64bios(Mach64xx*);
+
+static void
+dump(Vga* vga, Ctlr* ctlr)
+{
+ Mach64xx *mp;
+ int i, m, n, p;
+ double f;
+ static int first = 1;
+
+ if((mp = vga->private) == 0)
+ return;
+
+ Bprint(&stdout, "%s pci %p io %lux %s\n", ctlr->name,
+ mp->pci, mp->io, mp->ior32 == pciior32 ? "pciregs" : "ioregs");
+ if(mp->pci)
+ Bprint(&stdout, "%s ccru %ux\n", ctlr->name, mp->pci->ccru);
+ for(i = 0; i < Nreg; i++)
+ Bprint(&stdout, "%s %-*s%.8luX\n",
+ ctlr->name, 20, iorname[i], mp->reg[i]);
+
+ printitem(ctlr->name, "PLL");
+ for(i = 0; i < Npll; i++)
+ printreg(mp->pll[i]);
+ Bprint(&stdout, "\n");
+
+ switch(mp->reg[ConfigChipId] & 0xFFFF){
+ default:
+ break;
+ case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
+ case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
+ case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
+ case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
+ for(i = 0; i < Nlcd; i++)
+ Bprint(&stdout, "%s %-*s%.8luX\n",
+ ctlr->name, 20, lcdname[i], mp->lcd[i]);
+ break;
+ }
+
+ /*
+ * (2*r*n)
+ * f = -------
+ * (m*p)
+ */
+ m = mp->pll[2];
+ for(i = 0; i < 4; i++){
+ n = mp->pll[7+i];
+ p = (mp->pll[6]>>(i*2)) & 0x03;
+ p |= (mp->pll[11]>>(2+i)) & 0x04;
+ switch(p){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ p = 1<<p;
+ break;
+ case 4+0:
+ p = 3;
+ break;
+ case 4+2:
+ p = 6;
+ break;
+ case 4+3:
+ p = 12;
+ break;
+
+ default:
+ case 4+1:
+ p = -1;
+ break;
+ }
+ if(m*p == 0)
+ Bprint(&stdout, "unknown VCLK%d\n", i);
+ else {
+ f = (2.0*RefFreq*n)/(m*p) + 0.5;
+ Bprint(&stdout, "%s VCLK%d\t%ud\n", ctlr->name, i, (int)f);
+ }
+ }
+
+ pixelclock(vga, ctlr);
+
+ if(first) {
+ first = 0;
+ dumpmach64bios(mp);
+ }
+}
+
+enum {
+ ClockFixed=0,
+ ClockIcs2595,
+ ClockStg1703,
+ ClockCh8398,
+ ClockInternal,
+ ClockAtt20c408,
+ ClockIbmrgb514
+};
+
+/*
+ * mostly derived from the xfree86 probe routines.
+ */
+static void
+dumpmach64bios(Mach64xx *mp)
+{
+ int i, romtable, clocktable, freqtable, lcdtable, lcdpanel;
+ uchar bios[0x10000];
+
+ memmove(bios, readbios(sizeof bios, 0xC0000), sizeof bios);
+
+ /* find magic string */
+ for(i=0; i<1024; i++)
+ if(strncmp((char*)bios+i, " 761295520", 10) == 0)
+ break;
+
+ if(i==1024) {
+ Bprint(&stdout, "no ATI bios found\n");
+ return;
+ }
+
+ /* this is horribly endian dependent. sorry. */
+ romtable = *(ushort*)(bios+0x48);
+ if(romtable+0x12 > sizeof(bios)) {
+ Bprint(&stdout, "couldn't find ATI rom table\n");
+ return;
+ }
+
+ clocktable = *(ushort*)(bios+romtable+0x10);
+ if(clocktable+0x0C > sizeof(bios)) {
+ Bprint(&stdout, "couldn't find ATI clock table\n");
+ return;
+ }
+
+ freqtable = *(ushort*)(bios+clocktable-2);
+ if(freqtable+0x20 > sizeof(bios)) {
+ Bprint(&stdout, "couldn't find ATI frequency table\n");
+ return;
+ }
+
+ Bprint(&stdout, "ATI BIOS rom 0x%x freq 0x%x clock 0x%x\n", romtable, freqtable, clocktable);
+ Bprint(&stdout, "clocks:");
+ for(i=0; i<16; i++)
+ Bprint(&stdout, " %d", *(ushort*)(bios+freqtable+2*i));
+ Bprint(&stdout, "\n");
+
+ Bprint(&stdout, "programmable clock: %d\n", bios[clocktable]);
+ Bprint(&stdout, "clock to program: %d\n", bios[clocktable+6]);
+
+ if(*(ushort*)(bios+clocktable+8) != 1430) {
+ Bprint(&stdout, "reference numerator: %d\n", *(ushort*)(bios+clocktable+8)*10);
+ Bprint(&stdout, "reference denominator: 1\n");
+ } else {
+ Bprint(&stdout, "default reference numerator: 157500\n");
+ Bprint(&stdout, "default reference denominator: 11\n");
+ }
+
+ switch(bios[clocktable]) {
+ case ClockIcs2595:
+ Bprint(&stdout, "ics2595\n");
+ Bprint(&stdout, "reference divider: %d\n", *(ushort*)(bios+clocktable+0x0A));
+ break;
+ case ClockStg1703:
+ Bprint(&stdout, "stg1703\n");
+ break;
+ case ClockCh8398:
+ Bprint(&stdout, "ch8398\n");
+ break;
+ case ClockInternal:
+ Bprint(&stdout, "internal clock\n");
+ Bprint(&stdout, "reference divider in plls\n");
+ break;
+ case ClockAtt20c408:
+ Bprint(&stdout, "att 20c408\n");
+ break;
+ case ClockIbmrgb514:
+ Bprint(&stdout, "ibm rgb514\n");
+ Bprint(&stdout, "clock to program = 7\n");
+ break;
+ default:
+ Bprint(&stdout, "unknown clock\n");
+ break;
+ }
+
+ USED(mp);
+ if(1 || mp->lcdpanelid) {
+ lcdtable = *(ushort*)(bios+0x78);
+ if(lcdtable+5 > sizeof bios || lcdtable+bios[lcdtable+5] > sizeof bios) {
+ Bprint(&stdout, "can't find lcd bios table\n");
+ goto NoLcd;
+ }
+
+ lcdpanel = *(ushort*)(bios+lcdtable+0x0A);
+ if(lcdpanel+0x1D > sizeof bios /*|| bios[lcdpanel] != mp->lcdpanelid*/) {
+ Bprint(&stdout, "can't find lcd bios table0\n");
+ goto NoLcd;
+ }
+
+ Bprint(&stdout, "panelid %d x %d y %d\n", bios[lcdpanel], *(ushort*)(bios+lcdpanel+0x19), *(ushort*)(bios+lcdpanel+0x1B));
+ }
+NoLcd:;
+}
+
+Ctlr mach64xx = {
+ "mach64xx", /* name */
+ snarf, /* snarf */
+ 0, /* options */
+ init, /* init */
+ load, /* load */
+ dump, /* dump */
+};
+
+Ctlr mach64xxhwgc = {
+ "mach64xxhwgc", /* name */
+ 0, /* snarf */
+ 0, /* options */
+ 0, /* init */
+ 0, /* load */
+ 0, /* dump */
+};
+
+