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