]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/vga/3dfx.c
vga: make kernel vga drivers more stupid
[plan9front.git] / sys / src / cmd / aux / vga / 3dfx.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 #include "pci.h"
6 #include "vga.h"
7
8 /*
9  * 3Dfx.
10  */
11 enum {
12         dramInit0               = 0x018/4,
13         dramInit1               = 0x01C/4,
14         vgaInit0                = 0x028/4,
15         pllCtrl0                = 0x040/4,
16         pllCtrl1                = 0x044/4,
17         pllCtrl2                = 0x048/4,
18         dacMode                 = 0x04C/4,
19         vidProcCfg              = 0x05C/4,
20         vidScreenSize           = 0x098/4,
21         vidDesktopOverlayStride = 0x0E8/4,
22
23         Nior                    = 0x100/4,
24 };
25
26 typedef struct Tdfx {
27         ulong   io;
28         Pcidev* pci;
29
30         ulong   r[Nior];
31 } Tdfx;
32
33 static ulong
34 io32r(Tdfx* tdfx, int r)
35 {
36         return inportl(tdfx->io+(r*4));
37 }
38
39 static void
40 io32w(Tdfx* tdfx, int r, ulong l)
41 {
42         outportl(tdfx->io+(r*4), l);
43 }
44
45 static void
46 snarf(Vga* vga, Ctlr* ctlr)
47 {
48         int i;
49         ulong v;
50         Tdfx *tdfx;
51
52         if(vga->private == nil){
53                 tdfx = alloc(sizeof(Tdfx));
54                 tdfx->pci = vga->pci;
55                 if(tdfx->pci == nil)
56                         tdfx->pci = pcimatch(0, 0x121A, 0);
57                 if(tdfx->pci == nil || tdfx->pci->vid != 0x121A)
58                         error("%s: not found\n", ctlr->name);
59                 switch(tdfx->pci->did){
60                 default:
61                         error("%s: unknown chip - DID %4.4uX\n",
62                                 ctlr->name, tdfx->pci->did);
63                         break;
64                 case 0x0003:            /* Banshee */
65                         vga->f[1] = 270000000;
66                         break;
67                 case 0x0005:            /* Avenger (a.k.a. Voodoo3) */
68                         vga->f[1] = 300000000;
69                         break;
70                 case 0x0009:            /* Voodoo5 */
71                         vga->f[1] = 350000000;
72                         break;
73                 }
74                 /*
75                  * Frequency output of PLL's is given by
76                  *      fout = RefFreq*(n+2)/((m+2)*2^p)
77                  * where there are 6 bits for m, 8 bits for n
78                  * and 2 bits for p (k).
79                  */
80                 vga->m[1] = 64;
81                 vga->n[1] = 256;
82                 vga->p[1] = 4;
83
84                 if((v = (tdfx->pci->mem[2].bar & ~0x3)) == 0)
85                         error("%s: I/O not mapped\n", ctlr->name);
86                 tdfx->io = v;
87
88                 vga->private = tdfx;
89         }
90         tdfx = vga->private;
91
92         vga->crt[0x1A] = vgaxi(Crtx, 0x1A);
93         vga->crt[0x1B] = vgaxi(Crtx, 0x1B);
94         for(i = 0; i < Nior; i++)
95                 tdfx->r[i] = io32r(tdfx, i);
96
97         /*
98          * If SDRAM then there's 16MB memory else it's SGRAM
99          * and can count it based on the power-on straps -
100          * chip size can be 8Mb or 16Mb, and there can be 4 or
101          * 8 of them.
102          */
103         vga->vma = tdfx->pci->mem[1].size;
104         if(tdfx->r[dramInit1] & 0x40000000)
105                 vga->vmz = 16*1024*1024;
106         else{
107                 if(tdfx->r[dramInit0] & 0x08000000)
108                         i = 16*1024*1024/8;
109                 else
110                         i = 8*1024*1024/8;
111                 if(tdfx->r[dramInit0] & 0x04000000)
112                         i *= 8;
113                 else
114                         i *= 4;
115                 vga->vmz = i;
116         }
117
118         ctlr->flag |= Fsnarf;
119 }
120
121 static void
122 options(Vga*, Ctlr* ctlr)
123 {
124         ctlr->flag |= Hlinear|Hclk2|Foptions;
125 }
126
127 static void
128 tdfxclock(Vga* vga, Ctlr*)
129 {
130         int d, dmin;
131         uint f, m, n, p;
132
133         dmin = vga->f[0];
134         for(m = 1; m < vga->m[1]; m++){
135                 for(n = 1; n < vga->n[1]; n++){
136                         f = (RefFreq*(n+2))/(m+2);
137                         for(p = 0; p < vga->p[1]; p++){
138                                 d = vga->f[0] - (f/(1<<p));
139                                 if(d < 0)
140                                         d = -d;
141                                 if(d >= dmin)
142                                         continue;
143                                 dmin = d;
144                                 vga->m[0] = m;
145                                 vga->n[0] = n;
146                                 vga->p[0] = p;
147                         }
148                 }
149         }
150 }
151
152 static void
153 init(Vga* vga, Ctlr* ctlr)
154 {
155         int x;
156         Mode *mode;
157         Tdfx *tdfx;
158
159         mode = vga->mode;
160         tdfx = vga->private;
161
162         if(vga->linear && (ctlr->flag & Hlinear))
163                 ctlr->flag |= Ulinear;
164
165         /*
166          * Clock bits. If the desired video clock is
167          * one of the two standard VGA clocks or 50MHz it can just be
168          * set using bits <3:2> of vga->misc, otherwise we
169          * need to programme the PLL.
170          */
171         if(vga->f[0] == 0)
172                 vga->f[0] = mode->frequency;
173         vga->misc &= ~0x0C;
174         if(vga->f[0] == VgaFreq0){
175                 /* nothing to do */;
176         }
177         else if(vga->f[0] == VgaFreq1)
178                 vga->misc |= 0x04;
179         else if(vga->f[0] == 50000000)
180                 vga->misc |= 0x08;
181         else{
182                 if(vga->f[0] > vga->f[1])
183                         error("%s: invalid pclk - %lud\n",
184                                 ctlr->name, vga->f[0]);
185                 if(vga->f[0] > 135000000 && (ctlr->flag & Hclk2)){
186                         if(mode->x%16)
187                                 error("%s: f > 135MHz requires (x%%16) == 0\n",
188                                         ctlr->name);
189                         ctlr->flag |= Uclk2;
190                 }
191                 tdfxclock(vga, ctlr);
192
193                 tdfx->r[pllCtrl0] = (vga->n[0]<<8)|(vga->m[0]<<2)|vga->p[0];
194                 vga->misc |= 0x0C;
195         }
196
197         /*
198          * Pixel format and memory stride.
199          */
200         tdfx->r[vidScreenSize] = (mode->y<<12)|mode->x;
201         tdfx->r[vidProcCfg] = 0x00000081;
202         switch(mode->z){
203         default:
204                 error("%s: %d-bit mode not supported\n", ctlr->name, mode->z);
205                 break;
206         case 8:
207                 tdfx->r[vidDesktopOverlayStride] = mode->x;
208                 break;
209         case 16:
210                 tdfx->r[vidDesktopOverlayStride] = mode->x*2;
211                 tdfx->r[vidProcCfg] |= 0x00040400;
212                 break;
213         case 32:
214                 tdfx->r[vidDesktopOverlayStride] = mode->x*4;
215                 tdfx->r[vidProcCfg] |= 0x000C0400;
216                 break;
217         }
218         tdfx->r[vgaInit0] = 0x140;
219
220         /*
221          * Adjust horizontal timing if doing two screen pixels per clock.
222          */
223         tdfx->r[dacMode] = 0;
224         if(ctlr->flag & Uclk2){
225                 vga->crt[0x00] = ((mode->ht/2)>>3)-5;
226                 vga->crt[0x01] = ((mode->x/2)>>3)-1;
227                 vga->crt[0x02] = ((mode->shb/2)>>3)-1;
228         
229                 x = (mode->ehb/2)>>3;
230                 vga->crt[0x03] = 0x80|(x & 0x1F);
231                 vga->crt[0x04] = (mode->shs/2)>>3;
232                 vga->crt[0x05] = ((mode->ehs/2)>>3) & 0x1F;
233                 if(x & 0x20)
234                         vga->crt[0x05] |= 0x80;
235
236                 tdfx->r[dacMode] |= 0x01;
237                 tdfx->r[vidProcCfg] |= 0x04000000;
238         }
239
240         /*
241          * Overflow.
242          */
243         vga->crt[0x1A] = 0x00;
244         if(vga->crt[0x00] & 0x100)
245                 vga->crt[0x1A] |= 0x01;
246         if(vga->crt[0x01] & 0x100)
247                 vga->crt[0x1A] |= 0x04;
248         if(vga->crt[0x03] & 0x100)
249                 vga->crt[0x1A] |= 0x10;
250         x = mode->ehb;
251         if(ctlr->flag & Uclk2)
252                 x /= 2;
253         if((x>>3) & 0x40)
254                 vga->crt[0x1A] |= 0x20;
255         if(vga->crt[0x04] & 0x100)
256                 vga->crt[0x1A] |= 0x40;
257         x = mode->ehs;
258         if(ctlr->flag & Uclk2)
259                 x /= 2;
260         if((x>>3) & 0x20)
261                 vga->crt[0x1A] |= 0x80;
262
263         vga->crt[0x1B] = 0x00;
264         if(vga->crt[0x06] & 0x400)
265                 vga->crt[0x1B] |= 0x01;
266         if(vga->crt[0x12] & 0x400)
267                 vga->crt[0x1B] |= 0x04;
268         if(vga->crt[0x15] & 0x400)
269                 vga->crt[0x1B] |= 0x10;
270         if(vga->crt[0x10] & 0x400)
271                 vga->crt[0x1B] |= 0x40;
272
273         vga->attribute[0x11] = Pblack;
274
275         ctlr->flag |= Finit;
276 }
277
278 static void
279 load(Vga* vga, Ctlr* ctlr)
280 {
281         Tdfx *tdfx;
282
283         vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
284         vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
285
286         tdfx = vga->private;
287         io32w(tdfx, dacMode, tdfx->r[dacMode]);
288         io32w(tdfx, vidScreenSize, tdfx->r[vidScreenSize]);
289         io32w(tdfx, vidDesktopOverlayStride, tdfx->r[vidDesktopOverlayStride]);
290         io32w(tdfx, vidProcCfg, tdfx->r[vidProcCfg]);
291         io32w(tdfx, vgaInit0, tdfx->r[vgaInit0]);
292
293         if((vga->misc & 0x0C) == 0x0C)
294                 io32w(tdfx, pllCtrl0, tdfx->r[pllCtrl0]);
295
296         ctlr->flag |= Fload;
297 }
298
299 static uint
300 pllctrl(Tdfx* tdfx, int pll)
301 {
302         uint k, m, n, r;
303
304         r = tdfx->r[pllCtrl0+pll];
305         k = r & 0x03;
306         m = (r>>2) & 0x3F;
307         n = (r>>8) & 0xFF;
308
309         return (RefFreq*(n+2))/((m+2)*(1<<k));
310 }
311
312 static void
313 dump(Vga* vga, Ctlr* ctlr)
314 {
315         int i;
316         Tdfx *tdfx;
317
318         if((tdfx = vga->private) == nil)
319                 return;
320
321         printitem(ctlr->name, "Crt1A");
322         printreg(vga->crt[0x1A]);
323         printreg(vga->crt[0x1B]);
324
325         Bprint(&stdout, "\n");
326         for(i = 0; i < Nior; i++)
327                 Bprint(&stdout, "%s %2.2uX\t%.8luX\n",
328                         ctlr->name, i*4, tdfx->r[i]);
329
330         printitem(ctlr->name, "pllCtrl");
331         Bprint(&stdout, "%9ud %8ud\n", pllctrl(tdfx, 0), pllctrl(tdfx, 1));
332 }
333
334 Ctlr tdfx = {
335         "3dfx",                         /* name */
336         snarf,                          /* snarf */
337         options,                        /* options */
338         init,                           /* init */
339         load,                           /* load */
340         dump,                           /* dump */
341 };
342
343 Ctlr tdfxhwgc = {
344         "3dfxhwgc",                     /* name */
345         0,                              /* snarf */
346         0,                              /* options */
347         0,                              /* init */
348         0,                              /* load */
349         0,                              /* dump */
350 };