xref: /plan9/sys/src/cmd/aux/vga/hiqvideo.c (revision fb7f0c934c48abaed6040d054ef636408c3c522d)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * Chips and Technologies HiQVideo.
10  * Extensions in the 69000 over the 65550 are not fully taken into account.
11  * Depths other than 8 are too slow.
12  */
13 enum {
14 	Frx		= 0x3D0,	/* Flat Panel Extensions Index */
15 	Mrx		= 0x3D2,	/* Multimedia Extensions Index */
16 	Xrx		= 0x3D6,	/* Configuration Extensions Index */
17 };
18 
19 typedef struct {
20 	Pcidev* pci;
21 
22 	uchar	fr[256];
23 	uchar	mr[256];
24 	uchar	xr[256];
25 } HiQVideo;
26 
27 static uchar
hiqvideoxi(long port,uchar index)28 hiqvideoxi(long port, uchar index)
29 {
30 	uchar data;
31 
32 	outportb(port, index);
33 	data = inportb(port+1);
34 
35 	return data;
36 }
37 
38 static void
hiqvideoxo(long port,uchar index,uchar data)39 hiqvideoxo(long port, uchar index, uchar data)
40 {
41 	outportb(port, index);
42 	outportb(port+1, data);
43 }
44 
45 static void
snarf(Vga * vga,Ctlr * ctlr)46 snarf(Vga* vga, Ctlr* ctlr)
47 {
48 	int i;
49 	Pcidev *p;
50 	HiQVideo *hqv;
51 
52 	if(vga->private == nil){
53 		vga->private = alloc(sizeof(HiQVideo));
54 		hqv = vga->private;
55 		if((p = pcimatch(nil, 0x102C, 0)) == nil)
56 			error("%s: not found\n", ctlr->name);
57 		switch(p->did){
58 		case 0x00C0:		/* 69000 HiQVideo */
59 			vga->f[1] = 135000000;
60 			vga->vmz = 2048*1024;
61 			break;
62 		case 0x00E0:		/* 65550 HiQV32 */
63 		case 0x00E4:		/* 65554 HiQV32 */
64 		case 0x00E5:		/* 65555 HiQV32 */
65 			vga->f[1] = 90000000;
66 			vga->vmz = 2048*1024;
67 			break;
68 		default:
69 			error("%s: DID %4.4uX unsupported\n",
70 				ctlr->name, p->did);
71 		}
72 		hqv->pci = p;
73 	}
74 	hqv = vga->private;
75 
76 	for(i = 0; i < 0x50; i++)
77 		hqv->fr[i] = hiqvideoxi(Frx, i);
78 	for(i = 0; i < 0x60; i++)
79 		hqv->mr[i] = hiqvideoxi(Mrx, i);
80 	for(i = 0x30; i < 0x80; i++)
81 		vga->crt[i] = vgaxi(Crtx, i);
82 	for(i = 0; i < 0x100; i++)
83 		hqv->xr[i] = hiqvideoxi(Xrx, i);
84 
85 	switch(hqv->pci->did){
86 	case 0x00C0:			/* 69000 HiQVideo */
87 		vga->f[1] = 135000000;
88 		vga->vmz = 2048*1024;
89 		break;
90 	case 0x00E0:			/* 65550 HiQV32 */
91 	case 0x00E4:			/* 65554 HiQV32 */
92 	case 0x00E5:			/* 65555 HiQV32 */
93 		/*
94 		 * Check VCC to determine max clock.
95 		 * 5V allows a higher rate.
96 		 */
97 		if(hqv->fr[0x0A] & 0x02)
98 			vga->f[1] = 110000000;
99 		else
100 			vga->f[1] = 80000000;
101 		switch((hqv->xr[0x43]>>1) & 0x03){
102 		case 0:
103 			vga->vmz = 1*1024*1024;
104 			break;
105 		case 1:
106 			vga->vmz = 2*1024*1024;
107 			break;
108 		default:
109 			vga->vmz = 16*1024*1024;
110 			break;
111 		}
112 		break;
113 	}
114 
115 	ctlr->flag |= Fsnarf;
116 }
117 
118 static void
options(Vga *,Ctlr * ctlr)119 options(Vga*, Ctlr* ctlr)
120 {
121 	ctlr->flag |= Hlinear|Foptions;
122 }
123 
124 static void
clock(Vga * vga,Ctlr * ctlr)125 clock(Vga* vga, Ctlr* ctlr)
126 {
127 	double f, fmin, fvco, m;
128 	ulong n, nmin, nmax, pd, rd;
129 
130 	/*
131 	 * Constraints:
132 	 *  1.	1MHz <= Fref <= 60MHz
133 	 *  2.	150KHz <= Fref/(RD*N) <= 2MHz
134 	 *  3.	48MHz < Fvco <= 220MHz
135 	 *  4.	3 <= M <= 1023
136 	 *  5.	3 <= N <= 1023
137 	 */
138 	f = vga->f[0];
139 
140 	/*
141 	 * Although pd = 0 is valid, things seems more stable
142 	 * starting at 1.
143 	 */
144 	for(pd = 1; pd < 6; pd++){
145 		f = vga->f[0]*(1<<pd);
146 		if(f > 48000000 && f <= 220000000)
147 			break;
148 	}
149 	if(pd >= 6)
150 		error("%s: dclk %lud out of range\n", ctlr->name, vga->f[0]);
151 	vga->p[0] = pd;
152 
153 	/*
154 	 * The reference divisor has only two possible values, 1 and 4.
155 	 */
156 	fmin = f;
157 	for(rd = 1; rd <= 4; rd += 3){
158 		/*
159 		 * Find the range for n (constraint 2).
160 		 */
161 		for(nmin = 3; nmin <= 1023; nmin++){
162 			if(RefFreq/(rd*nmin) <= 2000000)
163 				break;
164 		}
165 		for(nmax = 1023; nmax >= 3; nmax--){
166 			if(RefFreq/(rd*nmax) >= 150000)
167 				break;
168 		}
169 
170 		/*
171 		 * Now find the closest match for M and N.
172 		 */
173 		for(n = nmin; n < nmax; n++){
174 			for(m = 3; m <= vga->m[1]; m++){
175 				fvco = (RefFreq*4*m)/(rd*n);
176 				if(fvco < 48000000 || fvco > 220000000)
177 					continue;
178 				fvco -= f;
179 				if(fvco < 0)
180 					fvco = -fvco;
181 				if(fvco < fmin){
182 					fmin = fvco;
183 					vga->m[0] = m;
184 					vga->n[0] = n;
185 					vga->p[0] = pd;
186 					vga->r[0] = rd;
187 				}
188 			}
189 		}
190 	}
191 
192 }
193 
194 static void
init(Vga * vga,Ctlr * ctlr)195 init(Vga* vga, Ctlr* ctlr)
196 {
197 	HiQVideo *hqv;
198 
199 	hqv = vga->private;
200 
201 	if(vga->f[0] == 0)
202 		vga->f[0] = vga->mode->frequency;
203 	vga->misc &= ~0x0C;
204 	hqv->fr[0x03] &= ~0x0C;
205 
206 	switch(vga->mode->z){
207 	case 8:
208 	case 16:
209 	case 32:
210 		break;
211 	default:
212 		error("depth %d not supported\n", vga->mode->z);
213 	}
214 
215 	/*
216 	 * FR[0x01] == 1 for CRT, 2 for LCD.
217 	 * Don't programme the clock if it's an LCD.
218 	 */
219 	if((hqv->fr[0x01] & 0x03) == 0x02){
220 		vga->misc |= 0x08;
221 		hqv->fr[0x03] |= 0x08;
222 	}
223 	else{
224 		/*
225 		 * Clock bits. If the desired video clock is
226 		 * one of the two standard VGA clocks it can just be
227 		 * set using bits <3:2> of vga->misc or Fr03,
228 		 * otherwise the DCLK PLL needs to be programmed.
229 		 */
230 		if(vga->f[0] == VgaFreq0){
231 			/* nothing to do */;
232 		}
233 		else if(vga->f[0] == VgaFreq1){
234 			vga->misc |= 0x04;
235 			hqv->fr[0x03] |= 0x04;
236 		}
237 		else{
238 			if(vga->f[0] > vga->f[1])
239 				error("%s: invalid dclk - %lud\n",
240 					ctlr->name, vga->f[0]);
241 
242 			if((hqv->fr[0x01] & 0x03) != 0x02){
243 				vga->m[1] = 1023;
244 				clock(vga, ctlr);
245 				hqv->xr[0xC8] = (vga->m[0]-2) & 0xFF;
246 				hqv->xr[0xCA] = ((vga->m[0]-2) & 0x300)>>8;
247 				hqv->xr[0xC9] = (vga->n[0]-2) & 0xFF;
248 				hqv->xr[0xCA] |= ((vga->n[0]-2) & 0x300)>>4;
249 				hqv->xr[0xCB] = (vga->p[0]<<4)|(vga->r[0] == 1);
250 			}
251 			vga->misc |= 0x08;
252 			hqv->fr[0x03] |= 0x08;
253 		}
254 	}
255 
256 	if(vga->mode->y >= 480)
257 		vga->misc &= ~0xC0;
258 
259 	/*
260 	 * 10 bits should be enough, but set the extended mode anyway.
261 	 */
262 	hqv->xr[0x09] |= 0x01;
263 	vga->crt[0x30] = ((vga->mode->vt-2)>>8) & 0x0F;
264 	vga->crt[0x31] = ((vga->mode->y-1)>>8) & 0x0F;
265 	vga->crt[0x32] = (vga->mode->vrs>>8) & 0x0F;
266 	vga->crt[0x33] = (vga->mode->vrs>>8) & 0x0F;
267 	vga->crt[0x38] = (((vga->mode->ht>>3)-5)>>8) & 0x01;
268 	vga->crt[0x3C] = (vga->mode->ehb>>3) & 0xC0;
269 	vga->crt[0x41] = (vga->crt[0x13]>>8) & 0x0F;
270 	vga->crt[0x40] = 0x80;
271 
272 	hqv->xr[0x40] |= 0x03;
273 	hqv->xr[0x80] |= 0x10;
274 	hqv->xr[0x81] &= ~0x0F;
275 	switch(vga->mode->z){
276 	case 8:
277 		hqv->xr[0x81] |= 0x12;
278 		break;
279 	case 16:
280 		hqv->xr[0x81] |= 0x15;
281 		break;
282 	case 32:
283 		hqv->xr[0x81] |= 0x16;
284 		break;
285 	}
286 
287 	hqv->xr[0x0A] = 0x01;
288 	if(vga->linear && (ctlr->flag & Hlinear))
289 		ctlr->flag |= Ulinear;
290 
291 	vga->attribute[0x11] = 0;
292 
293 	ctlr->flag |= Finit;
294 }
295 
296 static void
load(Vga * vga,Ctlr * ctlr)297 load(Vga* vga, Ctlr* ctlr)
298 {
299 	HiQVideo *hqv;
300 
301 	hqv = vga->private;
302 
303 	hiqvideoxo(Xrx, 0x0E, 0x00);
304 	while((vgai(Status1) & 0x08) == 0x08)
305 		;
306 	while((vgai(Status1) & 0x08) == 0)
307 		;
308 	vgaxo(Seqx, 0x07, 0x00);
309 
310 	/*
311 	 * Set the clock if necessary.
312 	 */
313 	if((vga->misc & 0x0C) == 0x08 && (hqv->fr[0x01] & 0x03) != 0x02){
314 		vgao(MiscW, vga->misc & ~0x0C);
315 		hiqvideoxo(Frx, 0x03, hqv->fr[0x03] & ~0x0C);
316 
317 		hiqvideoxo(Xrx, 0xC8, hqv->xr[0xC8]);
318 		hiqvideoxo(Xrx, 0xC9, hqv->xr[0xC9]);
319 		hiqvideoxo(Xrx, 0xCA, hqv->xr[0xCA]);
320 		hiqvideoxo(Xrx, 0xCB, hqv->xr[0xCB]);
321 	}
322 	hiqvideoxo(Frx, 0x03, hqv->fr[0x03]);
323 	vgao(MiscW, vga->misc);
324 
325 	hiqvideoxo(Xrx, 0x09, hqv->xr[0x09]);
326 	vgaxo(Crtx, 0x30, vga->crt[0x30]);
327 	vgaxo(Crtx, 0x31, vga->crt[0x31]);
328 	vgaxo(Crtx, 0x32, vga->crt[0x32]);
329 	vgaxo(Crtx, 0x33, vga->crt[0x33]);
330 	vgaxo(Crtx, 0x38, vga->crt[0x38]);
331 	vgaxo(Crtx, 0x3C, vga->crt[0x3C]);
332 	vgaxo(Crtx, 0x41, vga->crt[0x41]);
333 	vgaxo(Crtx, 0x40, vga->crt[0x40]);
334 
335 	hiqvideoxo(Xrx, 0x40, hqv->xr[0x40]);
336 	hiqvideoxo(Xrx, 0x80, hqv->xr[0x80]);
337 	hiqvideoxo(Xrx, 0x81, hqv->xr[0x81]);
338 	if(ctlr->flag & Ulinear){
339 		hqv->xr[0x05] = vga->vmb>>16;
340 		hqv->xr[0x06] = vga->vmb>>24;
341 		hiqvideoxo(Xrx, 0x05, hqv->xr[0x05]);
342 		hiqvideoxo(Xrx, 0x06, hqv->xr[0x06]);
343 		hqv->xr[0x0A] = 0x02;
344 	}
345 	hiqvideoxo(Xrx, 0x0A, hqv->xr[0x0A]);
346 
347 	ctlr->flag |= Fload;
348 }
349 
350 static ulong
dumpmclk(uchar data[4])351 dumpmclk(uchar data[4])
352 {
353 	double f, m, n;
354 	int pds, rds;
355 
356 	m = data[0] & 0x7F;
357 	n = data[1] & 0x7F;
358 	pds = 1<<((data[2] & 0x70)>>4);
359 	if(data[2] & 0x01)
360 		rds = 1;
361 	else
362 		rds = 4;
363 	f = (RefFreq*4*(m+2))/(rds*(n+2));
364 	f /= pds;
365 
366 	return f;
367 }
368 
369 static ulong
dumpvclk(uchar data[4])370 dumpvclk(uchar data[4])
371 {
372 	double f, m, n;
373 	int pds, rds;
374 
375 	m = ((data[2] & 0x03)<<8)|data[0];
376 	n = (((data[2] & 0x30)>>4)<<8)|data[1];
377 	pds = 1<<((data[3] & 0x70)>>4);
378 	if(data[3] & 0x01)
379 		rds = 1;
380 	else
381 		rds = 4;
382 	f = (RefFreq*4*(m+2))/(rds*(n+2));
383 	f /= pds;
384 
385 	return f;
386 }
387 
388 static void
dump(Vga * vga,Ctlr * ctlr)389 dump(Vga* vga, Ctlr* ctlr)
390 {
391 	int i;
392 	char *name;
393 	HiQVideo *hqv;
394 
395 	name = ctlr->name;
396 	hqv = vga->private;
397 
398 	printitem(name, "Fr00");
399 	for(i = 0; i < 0x50; i++)
400 		printreg(hqv->fr[i]);
401 	printitem(name, "Mr00");
402 	for(i = 0; i < 0x60; i++)
403 		printreg(hqv->mr[i]);
404 	printitem(name, "Crt30");
405 	for(i = 0x30; i < 0x80; i++)
406 		printreg(vga->crt[i]);
407 	printitem(name, "Xr00");
408 	for(i = 0; i < 0x100; i++)
409 		printreg(hqv->xr[i]);
410 
411 	printitem(ctlr->name, "CLK0");
412 	for(i = 0; i < 4; i++)
413 		printreg(hqv->xr[0xC0+i]);
414 	Bprint(&stdout, "%23ld", dumpvclk(&hqv->xr[0xC0]));
415 	printitem(ctlr->name, "CLK1");
416 	for(i = 0; i < 4; i++)
417 		printreg(hqv->xr[0xC4+i]);
418 	Bprint(&stdout, "%23ld", dumpvclk(&hqv->xr[0xC4]));
419 	printitem(ctlr->name, "CLK2");
420 	for(i = 0; i < 4; i++)
421 		printreg(hqv->xr[0xC8+i]);
422 	Bprint(&stdout, "%23ld", dumpvclk(&hqv->xr[0xC8]));
423 	printitem(ctlr->name, "MCLK");
424 	for(i = 0; i < 4; i++)
425 		printreg(hqv->xr[0xCC+i]);
426 	Bprint(&stdout, "%23ld", dumpmclk(&hqv->xr[0xCC]));
427 
428 	printitem(ctlr->name, "m n pd rd");
429 	Bprint(&stdout, "%9ld %8ld       - %8ld %8ld\n",
430 		vga->m[0], vga->n[0], vga->p[0], vga->r[0]);
431 }
432 
433 Ctlr hiqvideo = {
434 	"hiqvideo",			/* name */
435 	snarf,				/* snarf */
436 	options,			/* options */
437 	init,				/* init */
438 	load,				/* load */
439 	dump,				/* dump */
440 };
441 
442 Ctlr hiqvideohwgc = {
443 	"hiqvideohwgc",			/* name */
444 	0,				/* snarf */
445 	0,				/* options */
446 	0,				/* init */
447 	0,				/* load */
448 	0,				/* dump */
449 };
450