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