summaryrefslogtreecommitdiff
path: root/sys/src/cmd/aux/vga/3dfx.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/3dfx.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/aux/vga/3dfx.c')
-rwxr-xr-xsys/src/cmd/aux/vga/3dfx.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/vga/3dfx.c b/sys/src/cmd/aux/vga/3dfx.c
new file mode 100755
index 000000000..e42b7c9c0
--- /dev/null
+++ b/sys/src/cmd/aux/vga/3dfx.c
@@ -0,0 +1,348 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#include "pci.h"
+#include "vga.h"
+
+/*
+ * 3Dfx.
+ */
+enum {
+ dramInit0 = 0x018/4,
+ dramInit1 = 0x01C/4,
+ vgaInit0 = 0x028/4,
+ pllCtrl0 = 0x040/4,
+ pllCtrl1 = 0x044/4,
+ pllCtrl2 = 0x048/4,
+ dacMode = 0x04C/4,
+ vidProcCfg = 0x05C/4,
+ vidScreenSize = 0x098/4,
+ vidDesktopOverlayStride = 0x0E8/4,
+
+ Nior = 0x100/4,
+};
+
+typedef struct Tdfx {
+ ulong io;
+ Pcidev* pci;
+
+ ulong r[Nior];
+} Tdfx;
+
+static ulong
+io32r(Tdfx* tdfx, int r)
+{
+ return inportl(tdfx->io+(r*4));
+}
+
+static void
+io32w(Tdfx* tdfx, int r, ulong l)
+{
+ outportl(tdfx->io+(r*4), l);
+}
+
+static void
+snarf(Vga* vga, Ctlr* ctlr)
+{
+ int i;
+ ulong v;
+ Tdfx *tdfx;
+
+ if(vga->private == nil){
+ tdfx = alloc(sizeof(Tdfx));
+ tdfx->pci = pcimatch(0, 0x121A, 0);
+ if(tdfx->pci == nil)
+ error("%s: not found\n", ctlr->name);
+ switch(tdfx->pci->did){
+ default:
+ error("%s: unknown chip - DID %4.4uX\n",
+ ctlr->name, tdfx->pci->did);
+ break;
+ case 0x0003: /* Banshee */
+ vga->f[1] = 270000000;
+ break;
+ case 0x0005: /* Avenger (a.k.a. Voodoo3) */
+ vga->f[1] = 300000000;
+ break;
+ case 0x0009: /* Voodoo5 */
+ vga->f[1] = 350000000;
+ break;
+ }
+ /*
+ * Frequency output of PLL's is given by
+ * fout = RefFreq*(n+2)/((m+2)*2^p)
+ * where there are 6 bits for m, 8 bits for n
+ * and 2 bits for p (k).
+ */
+ vga->m[1] = 64;
+ vga->n[1] = 256;
+ vga->p[1] = 4;
+
+ if((v = (tdfx->pci->mem[2].bar & ~0x3)) == 0)
+ error("%s: I/O not mapped\n", ctlr->name);
+ tdfx->io = v;
+
+ vga->private = tdfx;
+ }
+ tdfx = vga->private;
+
+ vga->crt[0x1A] = vgaxi(Crtx, 0x1A);
+ vga->crt[0x1B] = vgaxi(Crtx, 0x1B);
+ for(i = 0; i < Nior; i++)
+ tdfx->r[i] = io32r(tdfx, i);
+
+ /*
+ * If SDRAM then there's 16MB memory else it's SGRAM
+ * and can count it based on the power-on straps -
+ * chip size can be 8Mb or 16Mb, and there can be 4 or
+ * 8 of them.
+ */
+ vga->vma = tdfx->pci->mem[1].size;
+ if(tdfx->r[dramInit1] & 0x40000000)
+ vga->vmz = 16*1024*1024;
+ else{
+ if(tdfx->r[dramInit0] & 0x08000000)
+ i = 16*1024*1024/8;
+ else
+ i = 8*1024*1024/8;
+ if(tdfx->r[dramInit0] & 0x04000000)
+ i *= 8;
+ else
+ i *= 4;
+ vga->vmz = i;
+ }
+
+ ctlr->flag |= Fsnarf;
+}
+
+static void
+options(Vga*, Ctlr* ctlr)
+{
+ ctlr->flag |= Hlinear|Hclk2|Foptions;
+}
+
+static void
+tdfxclock(Vga* vga, Ctlr*)
+{
+ int d, dmin;
+ uint f, m, n, p;
+
+ dmin = vga->f[0];
+ for(m = 1; m < vga->m[1]; m++){
+ for(n = 1; n < vga->n[1]; n++){
+ f = (RefFreq*(n+2))/(m+2);
+ for(p = 0; p < vga->p[1]; p++){
+ d = vga->f[0] - (f/(1<<p));
+ if(d < 0)
+ d = -d;
+ if(d >= dmin)
+ continue;
+ dmin = d;
+ vga->m[0] = m;
+ vga->n[0] = n;
+ vga->p[0] = p;
+ }
+ }
+ }
+}
+
+static void
+init(Vga* vga, Ctlr* ctlr)
+{
+ int x;
+ Mode *mode;
+ Tdfx *tdfx;
+
+ mode = vga->mode;
+ tdfx = vga->private;
+
+ if(vga->linear && (ctlr->flag & Hlinear))
+ ctlr->flag |= Ulinear;
+
+ /*
+ * Clock bits. If the desired video clock is
+ * one of the two standard VGA clocks or 50MHz it can just be
+ * set using bits <3:2> of vga->misc, otherwise we
+ * need to programme the PLL.
+ */
+ if(vga->f[0] == 0)
+ vga->f[0] = mode->frequency;
+ vga->misc &= ~0x0C;
+ if(vga->f[0] == VgaFreq0){
+ /* nothing to do */;
+ }
+ else if(vga->f[0] == VgaFreq1)
+ vga->misc |= 0x04;
+ else if(vga->f[0] == 50000000)
+ vga->misc |= 0x08;
+ else{
+ if(vga->f[0] > vga->f[1])
+ error("%s: invalid pclk - %lud\n",
+ ctlr->name, vga->f[0]);
+ if(vga->f[0] > 135000000 && (ctlr->flag & Hclk2)){
+ if(mode->x%16)
+ error("%s: f > 135MHz requires (x%%16) == 0\n",
+ ctlr->name);
+ ctlr->flag |= Uclk2;
+ }
+ tdfxclock(vga, ctlr);
+
+ tdfx->r[pllCtrl0] = (vga->n[0]<<8)|(vga->m[0]<<2)|vga->p[0];
+ vga->misc |= 0x0C;
+ }
+
+ /*
+ * Pixel format and memory stride.
+ */
+ tdfx->r[vidScreenSize] = (mode->y<<12)|mode->x;
+ tdfx->r[vidProcCfg] = 0x00000081;
+ switch(mode->z){
+ default:
+ error("%s: %d-bit mode not supported\n", ctlr->name, mode->z);
+ break;
+ case 8:
+ tdfx->r[vidDesktopOverlayStride] = mode->x;
+ break;
+ case 16:
+ tdfx->r[vidDesktopOverlayStride] = mode->x*2;
+ tdfx->r[vidProcCfg] |= 0x00040400;
+ break;
+ case 32:
+ tdfx->r[vidDesktopOverlayStride] = mode->x*4;
+ tdfx->r[vidProcCfg] |= 0x000C0400;
+ break;
+ }
+ tdfx->r[vgaInit0] = 0x140;
+
+ /*
+ * Adjust horizontal timing if doing two screen pixels per clock.
+ */
+ tdfx->r[dacMode] = 0;
+ if(ctlr->flag & Uclk2){
+ 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;
+
+ tdfx->r[dacMode] |= 0x01;
+ tdfx->r[vidProcCfg] |= 0x04000000;
+ }
+
+ /*
+ * Overflow.
+ */
+ vga->crt[0x1A] = 0x00;
+ if(vga->crt[0x00] & 0x100)
+ vga->crt[0x1A] |= 0x01;
+ if(vga->crt[0x01] & 0x100)
+ vga->crt[0x1A] |= 0x04;
+ if(vga->crt[0x03] & 0x100)
+ vga->crt[0x1A] |= 0x10;
+ x = mode->ehb;
+ if(ctlr->flag & Uclk2)
+ x /= 2;
+ if((x>>3) & 0x40)
+ vga->crt[0x1A] |= 0x20;
+ if(vga->crt[0x04] & 0x100)
+ vga->crt[0x1A] |= 0x40;
+ x = mode->ehs;
+ if(ctlr->flag & Uclk2)
+ x /= 2;
+ if((x>>3) & 0x20)
+ vga->crt[0x1A] |= 0x80;
+
+ vga->crt[0x1B] = 0x00;
+ if(vga->crt[0x06] & 0x400)
+ vga->crt[0x1B] |= 0x01;
+ if(vga->crt[0x12] & 0x400)
+ vga->crt[0x1B] |= 0x04;
+ if(vga->crt[0x15] & 0x400)
+ vga->crt[0x1B] |= 0x10;
+ if(vga->crt[0x10] & 0x400)
+ vga->crt[0x1B] |= 0x40;
+
+ vga->attribute[0x11] = Pblack;
+
+ ctlr->flag |= Finit;
+}
+
+static void
+load(Vga* vga, Ctlr* ctlr)
+{
+ Tdfx *tdfx;
+
+ vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
+ vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
+
+ tdfx = vga->private;
+ io32w(tdfx, dacMode, tdfx->r[dacMode]);
+ io32w(tdfx, vidScreenSize, tdfx->r[vidScreenSize]);
+ io32w(tdfx, vidDesktopOverlayStride, tdfx->r[vidDesktopOverlayStride]);
+ io32w(tdfx, vidProcCfg, tdfx->r[vidProcCfg]);
+ io32w(tdfx, vgaInit0, tdfx->r[vgaInit0]);
+
+ if((vga->misc & 0x0C) == 0x0C)
+ io32w(tdfx, pllCtrl0, tdfx->r[pllCtrl0]);
+
+ ctlr->flag |= Fload;
+}
+
+static uint
+pllctrl(Tdfx* tdfx, int pll)
+{
+ uint k, m, n, r;
+
+ r = tdfx->r[pllCtrl0+pll];
+ k = r & 0x03;
+ m = (r>>2) & 0x3F;
+ n = (r>>8) & 0xFF;
+
+ return (RefFreq*(n+2))/((m+2)*(1<<k));
+}
+
+static void
+dump(Vga* vga, Ctlr* ctlr)
+{
+ int i;
+ Tdfx *tdfx;
+
+ if((tdfx = vga->private) == nil)
+ return;
+
+ printitem(ctlr->name, "Crt1A");
+ printreg(vga->crt[0x1A]);
+ printreg(vga->crt[0x1B]);
+
+ Bprint(&stdout, "\n");
+ for(i = 0; i < Nior; i++)
+ Bprint(&stdout, "%s %2.2uX\t%.8luX\n",
+ ctlr->name, i*4, tdfx->r[i]);
+
+ printitem(ctlr->name, "pllCtrl");
+ Bprint(&stdout, "%9ud %8ud\n", pllctrl(tdfx, 0), pllctrl(tdfx, 1));
+}
+
+Ctlr tdfx = {
+ "3dfx", /* name */
+ snarf, /* snarf */
+ options, /* options */
+ init, /* init */
+ load, /* load */
+ dump, /* dump */
+};
+
+Ctlr tdfxhwgc = {
+ "3dfxhwgc", /* name */
+ 0, /* snarf */
+ 0, /* options */
+ 0, /* init */
+ 0, /* load */
+ 0, /* dump */
+};