ref: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
dir: /sys/src/cmd/aux/vga/virge.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "pci.h" #include "vga.h" /* * S3 Trio/ViRGE/Savage4 * Requires work. */ /* on some cards, savage4mem[1] == 8, but i don't know how to tell. -rsc */ static uchar savage4mem[] = { 2, 4, 8, 12, 16, 32, 0, 32}; static void snarf(Vga* vga, Ctlr* ctlr) { int i, id; char *p; /* * The Trio/ViRGE variants have some extra sequencer registers * which need to be unlocked for access. */ vgaxo(Seqx, 0x08, 0x06); s3generic.snarf(vga, ctlr); for(i = 0x08; i < 0x4F; i++) vga->sequencer[i] = vgaxi(Seqx, i); vga->crt[0x2D] = vgaxi(Crtx, 0x2D); vga->crt[0x2E] = vgaxi(Crtx, 0x2E); vga->crt[0x2F] = vgaxi(Crtx, 0x2F); for(i = 0x70; i < 0x99; i++) vga->crt[i] = vgaxi(Crtx, i); id = (vga->crt[0x2D]<<8)|vga->crt[0x2E]; switch(id){ default: trace("Unknown ViRGE/Trio64+ - 0x%4.4uX\n", (vga->crt[0x2D]<<8)|vga->crt[0x2E]); /*FALLTHROUGH*/ case 0x8810: /* Microsoft Virtual PC 2004 */ case 0x8811: /* Trio64+ */ vga->r[1] = 3; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 135000000; trace("Trio64+\n"); break; case 0x8812: /* Aurora64V+ */ vga->r[1] = 3; vga->m[1] = 127; vga->n[1] = 63; vga->f[1] = 135000000; trace("Aurora64V+\n"); break; case 0x8901: /* Trio64V2 */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 170000000; trace("Trio64V2\n"); break; case 0x5631: /* ViRGE */ vga->r[1] = 3; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 135000000; vga->apz = 64*1024*1024; trace("ViRGE\n"); break; case 0x8A01: /* ViRGE/[DG]X */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 170000000; trace("ViRGE/[DG]X\n"); break; case 0x8A10: /* ViRGE/GX2 */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 170000000; vga->apz = 64*1024*1024; trace("ViRGE/GX2\n"); /* * Memory encoding on the ViRGE/GX2 is different. */ switch((vga->crt[0x36]>>6) & 0x03){ case 0x01: vga->vmz = 4*1024*1024; break; case 0x03: vga->vmz = 2*1024*1024; break; } break; case 0x883D: /* ViRGE/VX */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 31; vga->f[1] = 220000000; vga->apz = 64*1024*1024; trace("ViRGE/VX\n"); /* * Memory encoding on the ViRGE/VX is different. */ vga->vmz = (2*(((vga->crt[0x36]>>5) & 0x03)+1)) * 1*1024*1024; break; case 0x8C10: /* Savage MX/MV */ case 0x8C12: /* Savage4/IX-MV */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 127; vga->f[1] = 135000000; /* without clock-doubling */ for(i = 0x50; i < 0x70; i++) vga->sequencer[i] = vgaxi(Seqx, i); vga->apz = 128*1024*1024; vga->vmz = savage4mem[vga->crt[0x36]>>5] * 1024 * 1024; trace("Savage4/IX-MV\n"); break; case 0x8C2E: /* SuperSavage/IXC16 */ /* * This is all guessed, from the Savage4/IX, * inspection of the card (apz), and web pages * talking about the chip (f[1]). It seems to * work on the IBM Thinkpad T23. * * XXX only 1024x768 works, not 1400x1050. */ vga->r[1] = 4; vga->m[1] = 127; vga->n[1] = 127; vga->f[1] = 135000000; /* 300000000 after doubling? */ for(i = 0x50; i < 0x70; i++) vga->sequencer[i] = vgaxi(Seqx, i); vga->apz = 64*1024*1024; vga->vmz = savage4mem[vga->crt[0x36]>>5] * 1024 * 1024; trace("SuperSavage/IXC16\n"); break; case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ case 0x8D04: /* ProSavage DDR, K.Okamoto */ vga->r[1] = 4; vga->m[1] = 511; vga->n[1] = 127; vga->f[1] = 300000000; vga->apz = 128*1024*1024; vga->vmz = savage4mem[vga->crt[0x36]>>5] * 1024 * 1024; trace("Savage4\n"); break; } /* * Work out the part speed-grade from name. Name can have, * e.g. '-135' on the end for 135MHz part. */ if(p = strrchr(ctlr->name, '-')) vga->f[1] = strtoul(p+1, 0, 0) * 1000000; ctlr->flag |= Fsnarf; } static void options(Vga *vga, Ctlr* ctlr) { int id; id = (vga->crt[0x2D]<<8)|(vga->crt[0x2E]); switch(id){ case 0x8C2E: /* SuperSavage/IXC16 */ case 0x8C10: /* Savage MX/MV */ case 0x8C12: /* Savage4/IX-MV */ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ case 0x8D04: /* ProSavabe DDR, K.Okamoto */ /* * Round up so stride is multiple of 16. */ if(vga->virtx%16) vga->virtx = (vga->virtx+15)&~15; break; } ctlr->flag |= Hlinear|Hpclk2x8|Henhanced|Foptions; } static void init(Vga* vga, Ctlr* ctlr) { char *p, *val; Mode *mode; ulong pclk, x; int id, noclockset, width; id = (vga->crt[0x2D]<<8)|vga->crt[0x2E]; mode = vga->mode; /* * double 16bpp horizontal timings on ViRGE/[DG]X * leaves overflow bit-fiddling to s3generic.init */ if((id == 0x8A01) && ((mode->z+7)/8 == 2)){ resyncinit(vga, ctlr, Uenhanced, 0); vga->crt[0x00] = ((mode->ht * 2) >> 3) - 5; vga->crt[0x01] = ((mode->x * 2) >> 3) - 1; vga->crt[0x02] = ((mode->shb * 2) >> 3) - 1; x = (mode->ehb * 2) >> 3; vga->crt[0x03] = 0x80 | (x & 0x1F); vga->crt[0x04] = (mode->shs * 2) >> 3; vga->crt[0x05] = ((mode->ehs * 2) >> 3) & 0x1F; if(x & 0x20) vga->crt[0x05] |= 0x80; } s3generic.init(vga, ctlr); /* * Work out the part speed-grade from name. Name can have, * e.g. '-135' on the end for 135MHz part. */ noclockset = 0; if(p = strrchr(ctlr->name, '-')) vga->f[1] = strtoul(p+1, 0, 0) * 1000000; pclk = vga->f[1]; if(vga->mode->z > 8) width = vga->virtx*(vga->mode->z/8); else width = vga->virtx*(8/vga->mode->z); switch(id){ case 0x8810: /* Microsoft Virtual PC 2004 */ case 0x8811: /* Trio64+ */ /* * Part comes in -135MHz speed grade. In 8-bit mode * the maximum DCLK is 80MHz. In 2x8-bit mode the maximum * DCLK is 135MHz using the internal clock doubler. */ if((ctlr->flag & Hpclk2x8) && vga->mode->z == 8){ if(vga->f[0] > 80000000) ctlr->flag |= Upclk2x8; } else pclk = 80000000; vga->crt[0x67] &= ~0xF2; if(ctlr->flag & Upclk2x8){ vga->sequencer[0x15] |= 0x10; vga->sequencer[0x18] |= 0x80; /* * There's a little strip of the border * appears on the left in resolutions * 1280 and above if the 0x02 bit isn't * set (when it appears on the right...). */ vga->crt[0x67] |= 0x10; } /* * VLB address latch delay. */ if((vga->crt[0x36] & 0x03) == 0x01) vga->crt[0x58] &= ~0x08; /* * Display memory access control. */ vga->crt[0x60] = 0xFF; if(vga->mode->z > 8) error("trio64: depth %d not supported\n", vga->mode->z); break; case 0x8901: /* Trio64V2 */ vga->crt[0x90] = 0; vga->crt[0x91] = 0; break; case 0x8A10: /* ViRGE/GX2 */ vga->crt[0x90] = 0; vga->crt[0x31] |= 0x08; vga->crt[0x13] = (width>>3) & 0xFF; vga->crt[0x51] &= ~0x30; vga->crt[0x51] |= (width>>7) & 0x30; /* * Increase primary FIFO threshold * to reduce flicker and tearing. */ vga->crt[0x85] = 0x0F; /*FALLTHROUGH*/ case 0x5631: /* ViRGE */ case 0x8A01: /* ViRGE/[DG]X */ if(id == 0x8A01){ x = mode->x * ((mode->z + 7) / 8); x = (x + 7) / 8; vga->crt[0x91] = x & 0xFF; vga->crt[0x90] = (x >> 8) | 0x80; } case 0x883D: /* ViRGE/VX */ vga->crt[0x60] &= 0x0F; /*FALLTHROUGH*/ case 0x8812: /* Aurora64V+ */ if(id == 0x8812) /* Aurora64V+ */ noclockset = 1; vga->crt[0x65] = 0; vga->crt[0x66] = 0x89; vga->crt[0x67] = 0; if(id == 0x883D){ /* ViRGE/VX */ /* * Put the VRAM in 1-cycle EDO mode. * If it is not put in this mode, hardware acceleration * will leave little turds on the screen when hwfill is used. */ vga->crt[0x36] &= ~0x0C; if(vga->mode->x > 800 && vga->mode->z == 8) vga->crt[0x67] = 0x10; else vga->crt[0x67] = 0; /* * Adjustments to the generic settings: * heuristic fiddling. * * We used to set crt[0x66] to 0x89, but setting it * to 0x90 seems to allow us (and more importantly the card) * to access more than 2MB of memory. */ vga->crt[0x66] = 0x90; vga->crt[0x58] &= ~0x88; vga->crt[0x58] |= 0x40; if(vga->mode->x > 640 && vga->mode->z >= 8) vga->crt[0x63] |= 0x01; else vga->crt[0x63] &= ~0x01; } /* * The high nibble is the mode; or'ing in 0x02 turns * on support for gamma correction via the DACs, but I * haven't figured out how to turn on the 8-bit DACs, * so gamma correction stays off. */ switch(vga->mode->z){ case 1: case 2: case 4: case 8: default: vga->crt[0x67] |= 0x00; break; case 15: vga->crt[0x67] |= 0x30; break; case 16: vga->crt[0x67] |= 0x50; break; case 24: if(id == 0x8A10) /* GX2 has to be different */ vga->crt[0x67] |= 0x70; else vga->crt[0x67] |= 0xD0; break; case 32: /* * The ViRGE and ViRGE/VX manuals make no mention of * 32-bit mode (which the GX/2 calls 24-bit unpacked mode). */ if(id != 0x8A10) error("32-bit mode only supported on the GX/2\n"); vga->crt[0x67] |= 0xD0; break; } /* * Set new MMIO method */ vga->crt[0x53] &= ~0x18; vga->crt[0x53] |= 0x08; break; case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C10: /* Savage MX/MV */ case 0x8C12: /* Savage4/IX-MV */ /* * Experience shows the -1 (according to the manual) * is incorrect. */ x = width/8 /*-1*/; vga->crt[0x91] = x; vga->crt[0x90] &= ~0x07; vga->crt[0x90] |= (x>>8) & 0x07; case 0x8D04: /* ProSavage DDR, K.Okamoto */ x = mode->x * ((mode->z + 7) / 8); x = (x + 7) / 8; vga->crt[0x91] = x & 0xFF; vga->crt[0x90] = (x >> 8) | 0x80; /*FALLTHROUGH*/ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ /* * The Savage 4 is frustratingly similar to the * ViRGE/GX2, but has enough slight differences * to warrant special treatment. Blog. */ vga->crt[0x66] = 0x89; vga->crt[0x67] = 0; vga->crt[0x85] = 0x02; vga->crt[0x31] |= 0x08; vga->crt[0x13] = (width>>3) & 0xFF; vga->crt[0x51] &= ~0x30; vga->crt[0x51] |= (width>>7) & 0x30; /* pull screen width from GBD for graphics engine. */ vga->crt[0x50] = 0xC1; /* set color mode */ /* * The high nibble is the mode; or'ing in 0x02 turns * on support for gamma correction via the DACs, but I * haven't figured out how to turn on the 8-bit DACs, * so gamma correction stays off. */ switch(vga->mode->z){ default: error("%d-bit mode not supported on savage 4\n", vga->mode->z); case 8: vga->crt[0x67] |= 0x00; vga->crt[0x50] |= 0<<4; break; case 15: vga->crt[0x67] |= 0x20; vga->crt[0x50] |= 1<<4; break; case 16: vga->crt[0x67] |= 0x40; vga->crt[0x50] |= 1<<4; /* * Manual says this should be accompanied by setting * the clock-doubled modes but this seems to be the * right answer. * Should check if using doubled modes tidies any of * this up. */ if(id == 0x8C12 || id == 0x8C2E || id == 0x8C10) vga->crt[0x67] |= 0x10; break; case 32: vga->crt[0x67] |= 0xD0; vga->crt[0x50] |= 3<<4; break; } break; } /* * Clock bits. If the desired video clock is * one of the two standard VGA clocks it can just be * set using bits <3:2> of vga->misc, otherwise we * need to programme the DCLK PLL. */ if(val = dbattr(vga->mode->attr, "noclockset")){ if((noclockset = strtol(val, &p, 0)) == 0 && p == val) error("%s: invalid 'noclockset' attr\n", ctlr->name); } if(vga->f[0] == 0) vga->f[0] = vga->mode->frequency; vga->misc &= ~0x0C; if(vga->f[0] == VgaFreq0){ /* nothing to do */; } else if(vga->f[0] == VgaFreq1) vga->misc |= 0x04; else if(noclockset){ /* * Don't touch the clock on the Aurora64V+ * and optionally on some others. */ vga->misc |= 0x0C; } else{ if(vga->f[0] > pclk) error("%s: invalid pclk - %lud\n", ctlr->name, vga->f[0]); trio64clock(vga, ctlr); switch(id){ case 0x8A10: /* ViRGE/GX2 */ vga->sequencer[0x12] = (vga->r[0]<<6)|vga->n[0]; if(vga->r[0] & 0x04) vga->sequencer[0x29] |= 0x01; else vga->sequencer[0x29] &= ~0x01; break; case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C10: /* Savage MX/MV */ case 0x8C12: /* Savage4/IX-MV */ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ case 0x8D04: /* ProSavage DDR, K.Okamoto */ vga->sequencer[0x12] = (vga->r[0]<<6)|(vga->n[0] & 0x3F); vga->sequencer[0x39] &= ~0x01; vga->sequencer[0x29] &= ~0x1C; if(vga->r[0] & 0x04) vga->sequencer[0x29] |= (1<<2); if(vga->m[0] & 0x100) vga->sequencer[0x29] |= (1<<3); if(vga->n[0] & 0x40) vga->sequencer[0x29] |= (1<<4); break; default: vga->sequencer[0x12] = (vga->r[0]<<5)|vga->n[0]; break; } vga->sequencer[0x13] = vga->m[0]; vga->misc |= 0x0C; } /* * Internal clock generator. */ vga->sequencer[0x15] &= ~0x31; vga->sequencer[0x15] |= 0x02; vga->sequencer[0x18] &= ~0x80; /* * Start display FIFO fetch. */ x = (vga->crt[0]+vga->crt[4]+1)/2; vga->crt[0x3B] = x; if(x & 0x100) vga->crt[0x5D] |= 0x40; /* * Display memory access control. * Calculation of the M-parameter (Crt54) is * memory-system and dot-clock dependent, the * values below are guesses from dumping * registers. * The Savage4 does not document 0x54, * but we leave this anyway. */ if(vga->mode->x <= 800) vga->crt[0x54] = 0xE8; else if(vga->mode->x <= 1024 && id != 0x8C12 && id != 0x8C2E) vga->crt[0x54] = 0xA8; else vga->crt[0x54] = 0x00/*0x48*/; ctlr->flag |= Finit; } static void load(Vga* vga, Ctlr* ctlr) { int id; ushort advfunc; s3generic.load(vga, ctlr); /* * Load the PLL registers if necessary. * Not sure if the variable-delay method of setting the * PLL will work without a write here to vga->misc, * so use the immediate-load method by toggling bit 5 * of Seq15 if necessary. */ vgaxo(Seqx, 0x12, vga->sequencer[0x12]); vgaxo(Seqx, 0x13, vga->sequencer[0x13]); id = (vga->crt[0x2D]<<8)|vga->crt[0x2E]; switch(id){ case 0x883D: /* ViRGE/VX*/ vgaxo(Crtx, 0x36, vga->crt[0x36]); break; case 0x8A10: /* ViRGE/GX2 */ vgaxo(Seqx, 0x29, vga->sequencer[0x29]); break; case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C12: /* Savage4/IX-MV */ vgaxo(Crtx, 0x90, vga->crt[0x90]); vgaxo(Crtx, 0x91, vga->crt[0x91]); /*FALLTHROUGH*/ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ case 0x8D04: /* ProSavage DDR, K.Okamoto */ vgaxo(Seqx, 0x29, vga->sequencer[0x29]); vgaxo(Seqx, 0x39, vga->sequencer[0x39]); break; } if((vga->misc & 0x0C) == 0x0C) vgaxo(Seqx, 0x15, vga->sequencer[0x15]|0x20); vgaxo(Seqx, 0x15, vga->sequencer[0x15]); vgaxo(Seqx, 0x18, vga->sequencer[0x18]); vgaxo(Crtx, 0x60, vga->crt[0x60]); vgaxo(Crtx, 0x63, vga->crt[0x63]); vgaxo(Crtx, 0x65, vga->crt[0x65]); vgaxo(Crtx, 0x66, vga->crt[0x66]); vgaxo(Crtx, 0x67, vga->crt[0x67]); switch(id){ case 0x8810: /* Microsoft Virtual PC 2004 */ case 0x8811: /* Trio64+ */ advfunc = 0x0000; if(ctlr->flag & Uenhanced) advfunc = 0x0001; outportw(0x4AE8, advfunc); break; case 0x8901: /* Trio64V2 */ case 0x8A01: /* ViRGE/[DG]X */ vgaxo(Crtx, 0x90, vga->crt[0x90]); vgaxo(Crtx, 0x91, vga->crt[0x91]); break; case 0x8A10: /* ViRGE/GX2 */ vgaxo(Crtx, 0x90, vga->crt[0x90]); vgaxo(Crtx, 0x31, vga->crt[0x31]); vgaxo(Crtx, 0x13, vga->crt[0x13]); vgaxo(Crtx, 0x51, vga->crt[0x51]); vgaxo(Crtx, 0x85, vga->crt[0x85]); break; case 0x8D04: /* ProSavage DDR, K.Okamoto */ vgaxo(Crtx, 0x90, vga->crt[0x90]); //K.Okamoto vgaxo(Crtx, 0x91, vga->crt[0x91]); //K.Okamoto case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C12: /* Savage4/IX-MV */ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ vgaxo(Crtx, 0x31, vga->crt[0x31]); vgaxo(Crtx, 0x13, vga->crt[0x13]); vgaxo(Crtx, 0x51, vga->crt[0x51]); vgaxo(Crtx, 0x85, vga->crt[0x85]); vgaxo(Crtx, 0x50, vga->crt[0x50]); break; } } static void dump(Vga* vga, Ctlr* ctlr) { int i, id; ulong dclk, m, n, r; s3generic.dump(vga, ctlr); printitem(ctlr->name, "Crt70"); for(i = 0x70; i < 0x99; i++) printreg(vga->crt[i]); printitem(ctlr->name, "Seq08"); for(i = 0x08; i < 0x10; i++) printreg(vga->sequencer[i]); printitem(ctlr->name, "Seq10"); for(i = 0x10; i < 0x50; i++) printreg(vga->sequencer[i]); id = (vga->crt[0x2D]<<8)|vga->crt[0x2E]; switch(id){ default: break; case 0x8812: /* Aurora64V+ */ case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C12: /* Savage4/IX-MV */ printitem(ctlr->name, "Seq50"); for(i = 0x50; i < 0x70; i++) printreg(vga->sequencer[i]); break; } printitem(ctlr->name, "Crt2D"); printreg(vga->crt[0x2D]); printreg(vga->crt[0x2E]); printreg(vga->crt[0x2F]); m = vga->sequencer[0x13] & vga->m[1]; n = vga->sequencer[0x12] & vga->n[1]; r = (vga->sequencer[0x12]>>5) & 0x03; switch(id){ case 0x8812: /* Aurora64V+ */ r = (vga->sequencer[0x12]>>6) & 0x03; break; case 0x8A01: /* ViRGE/[DG]X */ r = (vga->sequencer[0x12]>>5) & 0x07; break; case 0x8A10: /* ViRGE/GX2 */ r = (vga->sequencer[0x12]>>6) & 0x03; r |= (vga->sequencer[0x29] & 0x01)<<2; break; case 0x8C2E: /* SuperSavage/IXC16 (let's try this -rsc) */ case 0x8C12: /* Savage4/IX-MV */ case 0x8A22: /* Savage4 */ case 0x8A25: /* ProSavage PN133 */ case 0x8A26: /* ProSavage KN133 */ case 0x8D04: /* Prosavage DDR, K.Okamoto */ m = vga->sequencer[0x13] & 0xFF; if(vga->sequencer[0x29] & (1<<3)) m |= 0x100; if(vga->sequencer[0x29] & (1<<4)) n |= 0x40; r = (vga->sequencer[0x12]>>6) & 0x03; r |= (vga->sequencer[0x29] & (1<<2)); break; } dclk = (m+2)*RefFreq; dclk /= (n+2)*(1<<r); printitem(ctlr->name, "dclk m n r"); Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n", dclk, m, n, r); m = vga->sequencer[0x11] & 0x7F; n = vga->sequencer[0x10] & 0x1F; r = (vga->sequencer[0x10]>>5) & 0x03; /* might be GX/2 specific */ dclk = (m+2)*RefFreq; dclk /= (n+2)*(1<<r); printitem(ctlr->name, "mclk m n r"); Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n", dclk, m, n, r); } Ctlr virge = { "virge", /* name */ snarf, /* snarf */ options, /* options */ init, /* init */ load, /* load */ dump, /* dump */ };