17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier
5*9a747e4fSDavid du Colombier #include "pci.h"
67dd7cddfSDavid du Colombier #include "vga.h"
77dd7cddfSDavid du Colombier
87dd7cddfSDavid du Colombier /*
97dd7cddfSDavid du Colombier * S3 Trio64.
107dd7cddfSDavid du Colombier */
117dd7cddfSDavid du Colombier static void
snarf(Vga * vga,Ctlr * ctlr)127dd7cddfSDavid du Colombier snarf(Vga* vga, Ctlr* ctlr)
137dd7cddfSDavid du Colombier {
147dd7cddfSDavid du Colombier int i;
157dd7cddfSDavid du Colombier
167dd7cddfSDavid du Colombier /*
177dd7cddfSDavid du Colombier * The Trio has some extra sequencer registers which
187dd7cddfSDavid du Colombier * need to be unlocked for access.
197dd7cddfSDavid du Colombier */
207dd7cddfSDavid du Colombier vgaxo(Seqx, 0x08, 0x06);
217dd7cddfSDavid du Colombier for(i = 0x08; i < 0x19; i++)
227dd7cddfSDavid du Colombier vga->sequencer[i] = vgaxi(Seqx, i);
237dd7cddfSDavid du Colombier vga->crt[0x2D] = vgaxi(Crtx, 0x2D);
247dd7cddfSDavid du Colombier vga->crt[0x2E] = vgaxi(Crtx, 0x2E);
257dd7cddfSDavid du Colombier vga->crt[0x2F] = vgaxi(Crtx, 0x2F);
267dd7cddfSDavid du Colombier
277dd7cddfSDavid du Colombier s3generic.snarf(vga, ctlr);
287dd7cddfSDavid du Colombier ctlr->flag |= Fsnarf;
297dd7cddfSDavid du Colombier }
307dd7cddfSDavid du Colombier
317dd7cddfSDavid du Colombier static void
options(Vga *,Ctlr * ctlr)327dd7cddfSDavid du Colombier options(Vga*, Ctlr* ctlr)
337dd7cddfSDavid du Colombier {
347dd7cddfSDavid du Colombier ctlr->flag |= Hlinear|Hpclk2x8|Henhanced|Foptions;
357dd7cddfSDavid du Colombier }
367dd7cddfSDavid du Colombier
377dd7cddfSDavid du Colombier void
trio64clock(Vga * vga,Ctlr * ctlr)387dd7cddfSDavid du Colombier trio64clock(Vga* vga, Ctlr* ctlr)
397dd7cddfSDavid du Colombier {
407dd7cddfSDavid du Colombier int d;
417dd7cddfSDavid du Colombier ulong f, fmax, fmin, n, m, r;
427dd7cddfSDavid du Colombier double trouble;
437dd7cddfSDavid du Colombier
447dd7cddfSDavid du Colombier /*
4514414594SDavid du Colombier * The max value of R, M, N, and the speed rating of the part vary
467dd7cddfSDavid du Colombier * between parts and are passed to this routine in r[1] and f[1].
477dd7cddfSDavid du Colombier * R F
487dd7cddfSDavid du Colombier * Trio64 3 135000000
497dd7cddfSDavid du Colombier * ViRGE 3 135000000
507dd7cddfSDavid du Colombier * ViRGE/[DG]X 4 170000000
517dd7cddfSDavid du Colombier * ViRGE/GX2 4 170000000
527dd7cddfSDavid du Colombier * ViRGE/VX 4 220000000
537dd7cddfSDavid du Colombier */
547dd7cddfSDavid du Colombier
557dd7cddfSDavid du Colombier /*
567dd7cddfSDavid du Colombier * The PLL frequency is defined by the following equation:
577dd7cddfSDavid du Colombier * (M+2)
587dd7cddfSDavid du Colombier * Fout = ------------- x Fref
597dd7cddfSDavid du Colombier * (N+2) x 2**R
607dd7cddfSDavid du Colombier * where M, N and R have the following contraints:
617dd7cddfSDavid du Colombier * 1) (M+2) x Fref
627dd7cddfSDavid du Colombier * vga->f[1] <= ------------ <= vga->f[1]*2
637dd7cddfSDavid du Colombier * (N+2)
6414414594SDavid du Colombier * 2) 1 <= M <= vga->m[1] (usually 127)
6514414594SDavid du Colombier * 3) 1 <= N <= vga->n[1] (usually 31)
667dd7cddfSDavid du Colombier * 4) 0 <= R <= vga->r[1]
677dd7cddfSDavid du Colombier *
687dd7cddfSDavid du Colombier * First determine R:
697dd7cddfSDavid du Colombier * vga->f[1] < 2**R x Fout <= vga->f[1]*2
707dd7cddfSDavid du Colombier */
717dd7cddfSDavid du Colombier for(r = 0; r <= vga->r[1]; r++){
727dd7cddfSDavid du Colombier f = vga->f[0]*(1<<r);
737dd7cddfSDavid du Colombier if(vga->f[1] < f && f <= vga->f[1]*2)
747dd7cddfSDavid du Colombier vga->r[0] = r;
757dd7cddfSDavid du Colombier }
767dd7cddfSDavid du Colombier if(vga->r[0] > vga->r[1])
777dd7cddfSDavid du Colombier error("%s: pclk %lud out of range\n", ctlr->name, vga->f[0]);
787dd7cddfSDavid du Colombier
797dd7cddfSDavid du Colombier /*
807dd7cddfSDavid du Colombier * Now find the closest match for M and N.
817dd7cddfSDavid du Colombier */
827dd7cddfSDavid du Colombier vga->d[0] = vga->f[0]+1;
8314414594SDavid du Colombier for(n = 1; n <= vga->n[1]; n++){
847dd7cddfSDavid du Colombier trouble = vga->f[0]*(n+2)*(1<<vga->r[0]);
857dd7cddfSDavid du Colombier trouble /= RefFreq;
867dd7cddfSDavid du Colombier m = (trouble+0.5) - 2;
8714414594SDavid du Colombier if(m > vga->m[1])
887dd7cddfSDavid du Colombier continue;
897dd7cddfSDavid du Colombier
907dd7cddfSDavid du Colombier trouble = (m+2)*RefFreq;
917dd7cddfSDavid du Colombier trouble /= (n+2)*(1<<vga->r[0]);
927dd7cddfSDavid du Colombier f = trouble+0.5;
937dd7cddfSDavid du Colombier
947dd7cddfSDavid du Colombier d = vga->f[0] - f;
957dd7cddfSDavid du Colombier if(d < 0)
967dd7cddfSDavid du Colombier d = -d;
977dd7cddfSDavid du Colombier if(d <= vga->d[0]){
987dd7cddfSDavid du Colombier vga->m[0] = m;
997dd7cddfSDavid du Colombier vga->n[0] = n;
1007dd7cddfSDavid du Colombier vga->d[0] = d;
1017dd7cddfSDavid du Colombier }
1027dd7cddfSDavid du Colombier }
1037dd7cddfSDavid du Colombier
1047dd7cddfSDavid du Colombier trouble = vga->f[0]*1.005;
1057dd7cddfSDavid du Colombier fmax = trouble;
1067dd7cddfSDavid du Colombier trouble = vga->f[0]*0.995;
1077dd7cddfSDavid du Colombier fmin = trouble;
1087dd7cddfSDavid du Colombier trouble = (vga->m[0]+2)*RefFreq;
1097dd7cddfSDavid du Colombier trouble /= (vga->n[0]+2)*(1<<vga->r[0]);
1107dd7cddfSDavid du Colombier f = trouble+0.5;
1117dd7cddfSDavid du Colombier if(fmin >= f || f >= fmax)
1127dd7cddfSDavid du Colombier error("%s: pclk %lud out of range\n", ctlr->name, vga->f[0]);
1137dd7cddfSDavid du Colombier }
1147dd7cddfSDavid du Colombier
1157dd7cddfSDavid du Colombier static void
init(Vga * vga,Ctlr * ctlr)1167dd7cddfSDavid du Colombier init(Vga* vga, Ctlr* ctlr)
1177dd7cddfSDavid du Colombier {
1187dd7cddfSDavid du Colombier ulong pclk, x;
1197dd7cddfSDavid du Colombier
1207dd7cddfSDavid du Colombier s3generic.init(vga, ctlr);
1217dd7cddfSDavid du Colombier
1227dd7cddfSDavid du Colombier /*
1237dd7cddfSDavid du Colombier * Clock bits. If the desired video clock is
1247dd7cddfSDavid du Colombier * one of the two standard VGA clocks it can just be
1257dd7cddfSDavid du Colombier * set using bits <3:2> of vga->misc, otherwise we
1267dd7cddfSDavid du Colombier * need to programme the DCLK PLL.
1277dd7cddfSDavid du Colombier */
1287dd7cddfSDavid du Colombier if(vga->mode->z > 8)
1297dd7cddfSDavid du Colombier error("depth %d not supported\n", vga->mode->z);
1307dd7cddfSDavid du Colombier
1317dd7cddfSDavid du Colombier if(vga->f[0] == 0)
1327dd7cddfSDavid du Colombier vga->f[0] = vga->mode->frequency;
1337dd7cddfSDavid du Colombier vga->misc &= ~0x0C;
134*9a747e4fSDavid du Colombier if(vga->f[0] == VgaFreq0){
135*9a747e4fSDavid du Colombier /* nothing to do */;
136*9a747e4fSDavid du Colombier }
1377dd7cddfSDavid du Colombier else if(vga->f[0] == VgaFreq1)
1387dd7cddfSDavid du Colombier vga->misc |= 0x04;
1397dd7cddfSDavid du Colombier else{
1407dd7cddfSDavid du Colombier /*
1417dd7cddfSDavid du Colombier * Part comes in -135MHz speed grade. In 8-bit mode
1427dd7cddfSDavid du Colombier * the maximum DCLK is 80MHz. In 2x8-bit mode the maximum
1437dd7cddfSDavid du Colombier * DCLK is 135MHz using the internal clock doubler.
1447dd7cddfSDavid du Colombier */
1457dd7cddfSDavid du Colombier if((ctlr->flag & Hpclk2x8) && vga->mode->z == 8){
1467dd7cddfSDavid du Colombier pclk = 135000000;
1477dd7cddfSDavid du Colombier if(vga->f[0] > 80000000)
1487dd7cddfSDavid du Colombier ctlr->flag |= Upclk2x8;
1497dd7cddfSDavid du Colombier }
1507dd7cddfSDavid du Colombier else
1517dd7cddfSDavid du Colombier pclk = 80000000;
1527dd7cddfSDavid du Colombier if(vga->f[0] > pclk)
1537dd7cddfSDavid du Colombier error("%s: invalid pclk - %lud\n",
1547dd7cddfSDavid du Colombier ctlr->name, vga->f[0]);
1557dd7cddfSDavid du Colombier
1567dd7cddfSDavid du Colombier vga->f[1] = 135000000;
1577dd7cddfSDavid du Colombier vga->r[1] = 3;
15814414594SDavid du Colombier vga->n[1] = 31;
15914414594SDavid du Colombier vga->m[1] = 127;
1607dd7cddfSDavid du Colombier trio64clock(vga, ctlr);
1617dd7cddfSDavid du Colombier vga->sequencer[0x12] = (vga->r[0]<<5)|vga->n[0];
1627dd7cddfSDavid du Colombier vga->sequencer[0x13] = vga->m[0];
1637dd7cddfSDavid du Colombier vga->misc |= 0x0C;
1647dd7cddfSDavid du Colombier }
1657dd7cddfSDavid du Colombier
1667dd7cddfSDavid du Colombier /*
1677dd7cddfSDavid du Colombier * Internal clock generator.
1687dd7cddfSDavid du Colombier */
1697dd7cddfSDavid du Colombier vga->sequencer[0x15] &= ~0x31;
1707dd7cddfSDavid du Colombier vga->sequencer[0x15] |= 0x02;
1717dd7cddfSDavid du Colombier vga->sequencer[0x18] &= ~0x80;
1727dd7cddfSDavid du Colombier vga->crt[0x67] &= ~0xF2;
1737dd7cddfSDavid du Colombier if(ctlr->flag & Upclk2x8){
1747dd7cddfSDavid du Colombier vga->sequencer[0x15] |= 0x10;
1757dd7cddfSDavid du Colombier vga->sequencer[0x18] |= 0x80;
1767dd7cddfSDavid du Colombier /*
1777dd7cddfSDavid du Colombier * There's a little strip of the border
1787dd7cddfSDavid du Colombier * appears on the left in resolutions
1797dd7cddfSDavid du Colombier * 1280 and above if the 0x02 bit isn't
1807dd7cddfSDavid du Colombier * set (when it appears on the right...).
1817dd7cddfSDavid du Colombier */
1827dd7cddfSDavid du Colombier vga->crt[0x67] |= 0x10;
1837dd7cddfSDavid du Colombier }
1847dd7cddfSDavid du Colombier
1857dd7cddfSDavid du Colombier /*
1867dd7cddfSDavid du Colombier * VLB address latch delay.
1877dd7cddfSDavid du Colombier */
1887dd7cddfSDavid du Colombier if((vga->crt[0x36] & 0x03) == 0x01)
1897dd7cddfSDavid du Colombier vga->crt[0x58] &= ~0x08;
1907dd7cddfSDavid du Colombier
1917dd7cddfSDavid du Colombier /*
1927dd7cddfSDavid du Colombier * Start display FIFO fetch.
1937dd7cddfSDavid du Colombier */
1947dd7cddfSDavid du Colombier x = vga->crt[0]-5;
1957dd7cddfSDavid du Colombier vga->crt[0x3B] = x;
1967dd7cddfSDavid du Colombier if(x & 0x100)
1977dd7cddfSDavid du Colombier vga->crt[0x5D] |= 0x40;
1987dd7cddfSDavid du Colombier
1997dd7cddfSDavid du Colombier /*
2007dd7cddfSDavid du Colombier * Display memory access control.
2017dd7cddfSDavid du Colombier * Calculation of the M-parameter (Crt54) is
2027dd7cddfSDavid du Colombier * memory-system and dot-clock dependent, the
2037dd7cddfSDavid du Colombier * values below are guesses from dumping
2047dd7cddfSDavid du Colombier * registers.
2057dd7cddfSDavid du Colombier */
2067dd7cddfSDavid du Colombier vga->crt[0x60] = 0xFF;
2077dd7cddfSDavid du Colombier if(vga->mode->x <= 800)
2087dd7cddfSDavid du Colombier vga->crt[0x54] = 0xE8;
2097dd7cddfSDavid du Colombier else if(vga->mode->x <= 1024)
2107dd7cddfSDavid du Colombier vga->crt[0x54] = 0xA8;
2117dd7cddfSDavid du Colombier else
2127dd7cddfSDavid du Colombier vga->crt[0x54] = 0x00/*0x48*/;
2137dd7cddfSDavid du Colombier
2147dd7cddfSDavid du Colombier ctlr->flag |= Finit;
2157dd7cddfSDavid du Colombier }
2167dd7cddfSDavid du Colombier
2177dd7cddfSDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)2187dd7cddfSDavid du Colombier load(Vga* vga, Ctlr* ctlr)
2197dd7cddfSDavid du Colombier {
2207dd7cddfSDavid du Colombier ushort advfunc;
2217dd7cddfSDavid du Colombier
2227dd7cddfSDavid du Colombier s3generic.load(vga, ctlr);
2237dd7cddfSDavid du Colombier vgaxo(Crtx, 0x60, vga->crt[0x60]);
2247dd7cddfSDavid du Colombier vgaxo(Crtx, 0x67, vga->crt[0x67]);
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier /*
2277dd7cddfSDavid du Colombier * Load the PLL registers if necessary.
2287dd7cddfSDavid du Colombier * Not sure if the variable-delay method of setting the
2297dd7cddfSDavid du Colombier * PLL will work without a write here to vga->misc,
2307dd7cddfSDavid du Colombier * so use the immediate-load method by toggling bit 5
2317dd7cddfSDavid du Colombier * of Seq15 if necessary.
2327dd7cddfSDavid du Colombier */
2337dd7cddfSDavid du Colombier vgaxo(Seqx, 0x12, vga->sequencer[0x12]);
2347dd7cddfSDavid du Colombier vgaxo(Seqx, 0x13, vga->sequencer[0x13]);
2357dd7cddfSDavid du Colombier if((vga->misc & 0x0C) == 0x0C)
2367dd7cddfSDavid du Colombier vgaxo(Seqx, 0x15, vga->sequencer[0x15]|0x20);
2377dd7cddfSDavid du Colombier vgaxo(Seqx, 0x15, vga->sequencer[0x15]);
2387dd7cddfSDavid du Colombier vgaxo(Seqx, 0x18, vga->sequencer[0x18]);
2397dd7cddfSDavid du Colombier
2407dd7cddfSDavid du Colombier advfunc = 0x0000;
2417dd7cddfSDavid du Colombier if(ctlr->flag & Uenhanced)
2427dd7cddfSDavid du Colombier advfunc = 0x0001;
2437dd7cddfSDavid du Colombier outportw(0x4AE8, advfunc);
2447dd7cddfSDavid du Colombier }
2457dd7cddfSDavid du Colombier
2467dd7cddfSDavid du Colombier static void
dump(Vga * vga,Ctlr * ctlr)2477dd7cddfSDavid du Colombier dump(Vga* vga, Ctlr* ctlr)
2487dd7cddfSDavid du Colombier {
2497dd7cddfSDavid du Colombier int i;
2507dd7cddfSDavid du Colombier ulong dclk, m, n, r;
2517dd7cddfSDavid du Colombier
2527dd7cddfSDavid du Colombier s3generic.dump(vga, ctlr);
2537dd7cddfSDavid du Colombier
2547dd7cddfSDavid du Colombier printitem(ctlr->name, "Seq08");
2557dd7cddfSDavid du Colombier for(i = 0x08; i < 0x19; i++)
2567dd7cddfSDavid du Colombier printreg(vga->sequencer[i]);
2577dd7cddfSDavid du Colombier
2587dd7cddfSDavid du Colombier printitem(ctlr->name, "Crt2D");
2597dd7cddfSDavid du Colombier printreg(vga->crt[0x2D]);
2607dd7cddfSDavid du Colombier printreg(vga->crt[0x2E]);
2617dd7cddfSDavid du Colombier printreg(vga->crt[0x2F]);
2627dd7cddfSDavid du Colombier
2637dd7cddfSDavid du Colombier n = vga->sequencer[0x12] & 0x1F;
2647dd7cddfSDavid du Colombier r = (vga->sequencer[0x12]>>5) & 0x03;
2657dd7cddfSDavid du Colombier m = vga->sequencer[0x13] & 0x7F;
2667dd7cddfSDavid du Colombier dclk = (m+2)*RefFreq;
2677dd7cddfSDavid du Colombier dclk /= (n+2)*(1<<r);
2687dd7cddfSDavid du Colombier printitem(ctlr->name, "dclk m n r");
2697dd7cddfSDavid du Colombier Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n", dclk, m, n, r);
2707dd7cddfSDavid du Colombier }
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier Ctlr trio64 = {
2737dd7cddfSDavid du Colombier "trio64", /* name */
2747dd7cddfSDavid du Colombier snarf, /* snarf */
2757dd7cddfSDavid du Colombier options, /* options */
2767dd7cddfSDavid du Colombier init, /* init */
2777dd7cddfSDavid du Colombier load, /* load */
2787dd7cddfSDavid du Colombier dump, /* dump */
2797dd7cddfSDavid du Colombier };
280