📄 mach64xx.c
字号:
#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 ulongportior32(Mach64xx* mp, int r){ if((ioregs[r] & IOREG) == 0) return ~0; return inportl(((ioregs[r] & ~IOREG)<<2)+mp->io);}static voidportiow32(Mach64xx* mp, int r, ulong l){ if((ioregs[r] & IOREG) == 0) return; outportl(((ioregs[r] & ~IOREG)<<2)+mp->io, l);}static ulongpciior32(Mach64xx* mp, int r){ return inportl((pciregs[r]<<2)+mp->io);}static voidpciiow32(Mach64xx* mp, int r, ulong l){ outportl((pciregs[r]<<2)+mp->io, l);}static ucharpllr(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 voidpllw(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 ulonglcdr32(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 voidlcdw32(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 ulongtvr32(Mach64xx *mp, ulong r){ outportb(mp->io+(TvIndex<<2), r&0x0F); return inportl(mp->io+(TvData<<2));}static voidtvw32(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 voidsnarf(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 voidoptions(Vga*, Ctlr* ctlr){ ctlr->flag |= Hlinear|Foptions;}static voidclock(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 voidsetdsp(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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -