ref: f7a16b5e2cc38aed7985b0144bb2655d14ea6f5a
dir: /sys/src/cmd/aux/vga/mach64.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "pci.h" #include "vga.h" /* * ATI Mach64. Some hope. Kind of like a Mach32. * No support for accelerator so can only do up to 1024x768. * * All ATI Extended Registers are addressed using the modified index * index = (0x02<<6)|(index & 0x3F); * so registers 0x00->0x3F map to 0x80->0xBF, but we will only ever * look at a few in the range 0xA0->0xBF. In this way we can stash * them in the vga->crt[] array. */ enum { Configcntl = 0x6AEC, /* Configuration control */ Configstat = 0x72EC, /* Configuration status */ Memcntl = 0x52EC, /* Memory control */ Scratch1 = 0x46EC, /* Scratch Register (BIOS info) */ }; typedef struct { ulong configcntl; ulong configstat; ulong memcntl; ulong scratch1; } Mach64; /* * There are a number of possible clock generator chips for these * boards. We can divide any frequency by 2 (bit<6> of b8). */ typedef struct { ulong frequency; uchar be; /* <4> - bit<3> of frequency index */ uchar b9; /* <1> - bit<2> of frequency index */ uchar genmo; /* <3:2> - bits <1:0> of frequency index */ } Pclk; enum { Npclkx = 16, /* number of clock entries per table */ }; /* * ATI18811-0 */ static Pclk ati188110[Npclkx] = { { 42950000, 0x00, 0x00, 0x00 }, { 48770000, 0x00, 0x00, 0x04 }, { 92400000, 0x00, 0x00, 0x08 }, { 36000000, 0x00, 0x00, 0x0C }, { 50350000, 0x00, 0x02, 0x00 }, { 56640000, 0x00, 0x02, 0x04 }, { 0, 0x00, 0x02, 0x08 }, { 44900000, 0x00, 0x02, 0x0C }, { 30240000, 0x10, 0x00, 0x00 }, { 32000000, 0x10, 0x00, 0x04 }, { 110000000, 0x10, 0x00, 0x08 }, { 80000000, 0x10, 0x00, 0x0C }, { 39910000, 0x10, 0x02, 0x00 }, { 44900000, 0x10, 0x02, 0x04 }, { 75000000, 0x10, 0x02, 0x08 }, { 65000000, 0x10, 0x02, 0x0C }, }; /* * ATI18811-1, ATI18811-2 * PCLK_TABLE = 0 in Mach64 speak. */ static Pclk ati188111[Npclkx] = { { 100000000, 0x00, 0x00, 0x00 }, { 126000000, 0x00, 0x00, 0x04 }, { 92400000, 0x00, 0x00, 0x08 }, { 36000000, 0x00, 0x00, 0x0C }, { 50350000, 0x00, 0x02, 0x00 }, { 56640000, 0x00, 0x02, 0x04 }, { 0, 0x00, 0x02, 0x08 }, { 44900000, 0x00, 0x02, 0x0C }, { 135000000, 0x10, 0x00, 0x00 }, { 32000000, 0x10, 0x00, 0x04 }, { 110000000, 0x10, 0x00, 0x08 }, { 80000000, 0x10, 0x00, 0x0C }, { 39910000, 0x10, 0x02, 0x00 }, { 44900000, 0x10, 0x02, 0x04 }, { 75000000, 0x10, 0x02, 0x08 }, { 65000000, 0x10, 0x02, 0x0C }, }; /* * ATI18818 * The first four entries are programmable and the default * settings are either those below or those below divided by 2 * (PCLK_TABLE = 1 and PCLK_TABLE = 2 respectively in Mach64 * speak). */ static Pclk ati18818[Npclkx] = { { 50350000, 0x00, 0x00, 0x00 }, { 56640000, 0x00, 0x00, 0x04 }, { 63000000, 0x00, 0x00, 0x08 }, { 72000000, 0x00, 0x00, 0x0C }, { 40000000, 0x00, 0x02, 0x00 }, { 44900000, 0x00, 0x02, 0x04 }, { 49500000, 0x00, 0x02, 0x08 }, { 50000000, 0x00, 0x02, 0x0C }, { 0, 0x10, 0x00, 0x00 }, { 110000000, 0x10, 0x00, 0x04 }, { 126000000, 0x10, 0x00, 0x08 }, { 135000000, 0x10, 0x00, 0x0C }, { 0, 0x10, 0x02, 0x00 }, { 80000000, 0x10, 0x02, 0x04 }, { 75000000, 0x10, 0x02, 0x08 }, { 65000000, 0x10, 0x02, 0x0C }, }; static Pclk *pclkp; /* which clock chip we are using */ static ulong atix; /* index to extended regsiters */ static uchar atixi(uchar index) { outportb(atix, index); return inportb(atix+1); } static void atixo(uchar index, uchar data) { outportw(atix, (data<<8)|index); } static void atixinit(Vga* vga, Ctlr*) { uchar b; /* * Set the I/O address and offset for the ATI * extended registers to something we know about. */ if(atix == 0){ outportw(Grx, (0xCE<<8)|0x50); outportw(Grx, (0x81<<8)|0x51); atix = 0x1CE; } /* * Unlock the ATI Extended Registers. * We leave them unlocked from now on. * Why does this chip have so many * lock bits? */ if((b = atixi(0xB8)) & 0x3F) atixo(0xB8, b & 0xC0); b = atixi(0xAB); atixo(0xAB, b & ~0x18); atixo(0xB4, 0x00); b = atixi(0xB9); atixo(0xB9, b & ~0x80); b = atixi(0xBE); atixo(0xBE, b|0x09); if(vga->private == 0) vga->private = alloc(sizeof(Mach64)); } static void snarf(Vga* vga, Ctlr* ctlr) { int i; Mach64 *mach64; atixinit(vga, ctlr); for(i = 0xA0; i < 0xC0; i++) vga->crt[i] = atixi(i); mach64 = vga->private; mach64->configcntl = inportl(Configcntl); mach64->configstat = inportl(Configstat); mach64->memcntl = inportl(Memcntl); mach64->scratch1 = inportl(Scratch1); /* * Memory size. */ switch(mach64->memcntl & 0x07){ case 0: vga->vmz = 512*1024; break; case 1: vga->vmz = 1024*1024; break; case 2: vga->vmz = 2*1024*1024; break; case 3: vga->vmz = 4*1024*1024; break; case 4: vga->vmz = 6*1024*1024; break; case 5: vga->vmz = 8*1024*1024; break; } ctlr->flag |= Fsnarf; } static void init(Vga* vga, Ctlr* ctlr) { Mode *mode; int f, divisor, index; mode = vga->mode; /* * Must somehow determine which clock chip to use here. * For now, punt and assume ATI18818. */ pclkp = ati18818; if(pclkp == 0) error("%s: can't determine clock chip\n", ctlr->name); if(vga->f[0] == 0) vga->f[0] = vga->mode->frequency; /* * Find a clock frequency close to what we want. * 'Close' is within 1MHz. */ for(divisor = 0, index = 0; index < Npclkx; index++, divisor = 0){ divisor = 1; f = pclkp[index].frequency/divisor; if(f < vga->f[0]+1000000 && f >= vga->f[0]-1000000) break; divisor = 2; f /= divisor; if(f < vga->f[0]+1000000 && f >= vga->f[0]-1000000) break; } if(divisor == 0) error("%s: no suitable clock for %lud\n", ctlr->name, vga->f[0]); vga->d[0] = divisor; vga->i[0] = index; vga->crt[0xB0] &= 0xDA; vga->crt[0xB1] &= 0x87; vga->crt[0xB5] &= 0x7E; vga->crt[0xB6] &= 0xE2; vga->crt[0xB3] &= 0xAF; vga->crt[0xA6] &= 0xFE; vga->crt[0xA7] &= 0xF4; /* * 256-colour linear addressing. */ if(mode->z == 8){ vga->graphics[0x05] = 0x00; vga->attribute[0x10] &= ~0x40; vga->crt[0x13] = (mode->x/8)/2; vga->crt[0x14] = 0x00; vga->crt[0x17] = 0xE3; vga->crt[0xB0] |= 0x20; vga->crt[0xB6] |= 0x04; } vga->attribute[0x11] = 0x00; vga->crt[0xB6] |= 0x01; vga->crt[0xBE] &= ~0x04; /* * Do the clock index bits. */ vga->crt[0xB8] &= 0x3F; vga->crt[0xB9] &= 0xFD; vga->crt[0xBE] &= 0xE5; if(vga->d[0] == 2) vga->crt[0xB8] |= 0x40; vga->crt[0xB9] |= pclkp[vga->i[0]].b9; vga->crt[0xBE] |= pclkp[vga->i[0]].be; vga->misc |= pclkp[vga->i[0]].genmo; if(vga->mode->interlace == 'v') vga->crt[0xBE] |= 0x02; /* * Turn off 128Kb CPU address bit so * we only have a 64Kb aperture at 0xA0000. */ vga->crt[0xBD] &= ~0x04; ctlr->type = mach32.name; /* * The Mach64 can only address 1Mb in VGA mode */ vga->vmz = 1*1024*1024; ctlr->flag |= Finit; } static void load(Vga* vga, Ctlr* ctlr) { /* * We should probably do something here to make sure we that we * have access to all the video memory through the 64Kb VGA aperture * by disabling and linear aperture and memory boundary and then * enabling the VGA controller. * But for now, let's just assume it's ok, the Mach64 documentation * is just as clear as the Mach32 documentation. */ atixo(0xB0, vga->crt[0xB0]); atixo(0xB1, vga->crt[0xB1]); atixo(0xB5, vga->crt[0xB5]); atixo(0xB6, vga->crt[0xB6]); atixo(0xB3, vga->crt[0xB3]); atixo(0xA6, vga->crt[0xA6]); atixo(0xA7, vga->crt[0xA7]); atixo(0xB8, vga->crt[0xB8]); atixo(0xB9, vga->crt[0xB9]); atixo(0xBE, vga->crt[0xBE]); vgao(MiscW, vga->misc); ctlr->flag |= Fload; } static void dump(Vga* vga, Ctlr* ctlr) { int i; Mach64 *mach64; printitem(ctlr->name, "ATIX"); for(i = 0xA0; i < 0xC0; i++) printreg(vga->crt[i]); if((mach64 = vga->private) == 0) return; printitem(ctlr->name, "CONFIGCNTL"); Bprint(&stdout, "%.8lux\n", mach64->configcntl); printitem(ctlr->name, "CONFIGSTAT"); Bprint(&stdout, "%.8lux\n", mach64->configstat); printitem(ctlr->name, "MEMCNTL"); Bprint(&stdout, "%.8lux\n", mach64->memcntl); printitem(ctlr->name, "SCRATCH1"); Bprint(&stdout, "%.8lux\n", mach64->scratch1); } Ctlr mach64 = { "mach64", /* name */ snarf, /* snarf */ 0, /* options */ init, /* init */ load, /* load */ dump, /* dump */ };