1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 #include "pci.h"
6 #include "vga.h"
7
8 /*
9 * Cirrus Logic True Color VGA Family - CL-GD542X.
10 * Also works for Alpine VGA Family - CL-GD543X.
11 * Just the basics. BUGS:
12 * the added capabilities of the 543X aren't used.
13 */
14
15 typedef struct {
16 uchar id; /* Id */
17 ulong vclk; /* Maximum dot clock */
18 } Gd542x;
19
20 static Gd542x family[] = {
21 { 0x88, 75000000, }, /* CL-GD5420 */
22 { 0x8C, 80000000, }, /* CL-GD5422 */
23 { 0x94, 80000000, }, /* CL-GD5424 */
24 { 0x90, 80000000, }, /* CL-GD5426 */
25 { 0x98, 80000000, }, /* CL-GD5428 */
26 { 0x9C, 86000000, }, /* CL-GD5429 */
27 { 0xA0, 86000000, }, /* CL-GD5430 */
28 { 0xA8, 86000000, }, /* CL-GD5434 */
29
30 { 0xAC, 135000000, }, /* CL-GD5436 */
31 { 0xB8, 135000000, }, /* CL-GD5446 */
32 { 0xBC, 135000000, }, /* CL-GD5480 */
33
34 { 0x30, 80000000, }, /* CL-GD7543 */
35
36 { 0x00, },
37 };
38
39 static Gd542x*
identify(Vga * vga,Ctlr * ctlr)40 identify(Vga* vga, Ctlr* ctlr)
41 {
42 Gd542x *gd542x;
43 uchar id;
44
45 id = vga->crt[0x27] & ~0x03;
46 for(gd542x = &family[0]; gd542x->id; gd542x++){
47 if(gd542x->id == id)
48 return gd542x;
49 }
50
51 error("%s: unknown chip id - 0x%2.2X\n", ctlr->name, vga->crt[0x27]);
52 return 0;
53 }
54
55 static void
snarf(Vga * vga,Ctlr * ctlr)56 snarf(Vga* vga, Ctlr* ctlr)
57 {
58 int i;
59 Gd542x *gd542x;
60
61 /*
62 * Unlock extended registers.
63 */
64 vgaxo(Seqx, 0x06, 0x12);
65
66 /*
67 * Save all the registers, even though we'll only
68 * change a handful.
69 */
70 for(i = 0x06; i < 0x20; i++)
71 vga->sequencer[i] = vgaxi(Seqx, i);
72
73 for(i = 0x09; i < 0x3A; i++)
74 vga->graphics[i] = vgaxi(Grx, i);
75
76 for(i = 0x19; i < 0x1E; i++)
77 vga->crt[i] = vgaxi(Crtx, i);
78
79 vga->crt[0x27] = vgaxi(Crtx, 0x27);
80
81 /*
82 * Hack for Hidden DAC Register. Do 4 dummy reads
83 * of Pixmask first.
84 */
85 for(i = 0; i < 4; i++)
86 vgai(Pixmask);
87 vga->crt[0x28] = vgai(Pixmask);
88
89 i = 0;
90 switch(vga->crt[0x27] & ~0x03){
91
92 case 0x88: /* CL-GD5420 */
93 case 0x8C: /* CL-GD5422 */
94 case 0x94: /* CL-GD5424 */
95 case 0x80: /* CL-GD5425 */
96 case 0x90: /* CL-GD5426 */
97 case 0x98: /* CL-GD5427 */
98 case 0x9C: /* CL-GD5429 */
99 /*
100 * The BIOS leaves the memory size in Seq0A, bits 4 and 3.
101 * See Technical Reference Manual Appendix E1, Section 1.3.2.
102 *
103 * The storage area for the 64x64 cursors is the last 16Kb of
104 * display memory.
105 */
106 i = (vga->sequencer[0x0A]>>3) & 0x03;
107 break;
108
109 case 0xA0: /* CL-GD5430 */
110 case 0xA8: /* CL-GD5434 */
111 case 0xAC: /* CL-GD5436 */
112 case 0xB8: /* CL-GD5446 */
113 case 0x30: /* CL-GD7543 */
114 /*
115 * Attempt to intuit the memory size from the DRAM control
116 * register. Minimum is 512KB.
117 * If DRAM bank switching is on then there's double.
118 */
119 i = (vga->sequencer[0x0F]>>3) & 0x03;
120 if(vga->sequencer[0x0F] & 0x80)
121 i++;
122
123 /*
124 * If it's a PCI card, can do linear.
125 * Most of the Cirrus chips can do linear addressing with
126 * all the different buses, but it can get messy. It's easy
127 * to cut PCI on the CLGD543x chips out of the pack.
128 */
129 if(((vga->sequencer[0x17]>>3) & 0x07) == 0x04)
130 ctlr->flag |= Hlinear;
131 break;
132 case 0xBC: /* CL-GD5480 */
133 i = 2; /* 1024 = 256<<2 */
134 if((vga->sequencer[0x0F] & 0x18) == 0x18){
135 i <<= 1; /* 2048 = 256<<3 */
136 if(vga->sequencer[0x0F] & 0x80)
137 i <<= 2; /* 2048 = 256<<4 */
138 }
139 if(vga->sequencer[0x17] & 0x80)
140 i <<= 1;
141 ctlr->flag |= Hlinear;
142 break;
143
144 default: /* uh, ah dunno */
145 break;
146 }
147
148 if(vga->linear && (ctlr->flag & Hlinear)){
149 vga->vmz = 16*1024*1024;
150 vga->vma = 16*1024*1024;
151 ctlr->flag |= Ulinear;
152 }
153 else
154 vga->vmz = (256<<i)*1024;
155
156 gd542x = identify(vga, ctlr);
157 if(vga->f[1] == 0 || vga->f[1] > gd542x->vclk)
158 vga->f[1] = gd542x->vclk;
159 ctlr->flag |= Fsnarf;
160 }
161
162 void
clgd54xxclock(Vga * vga,Ctlr * ctlr)163 clgd54xxclock(Vga* vga, Ctlr* ctlr)
164 {
165 int f;
166 ulong d, dmin, fmin, n, nmin, p;
167
168 trace("%s->init->clgd54xxclock\n", ctlr->name);
169
170 /*
171 * Athough the Technical Reference Manual says only a handful
172 * of frequencies are tested, it also gives the following formula
173 * which can be used to generate any frequency within spec.,
174 * including the tested ones.
175 *
176 * Numerator is 7-bits, denominator 5-bits.
177 * Guess from the Technical Reference Manual that
178 * The post divisor is 1 for vclk<40MHz.
179 *
180 * Look for values of n and d and p that give
181 * the least error for
182 * vclk = (RefFreq*n)/(d*(1+p));
183 *
184 * There's nothing like brute force and ignorance.
185 */
186 fmin = vga->f[0];
187 nmin = 69;
188 dmin = 24;
189 if(vga->f[0] >= 40000000)
190 p = 0;
191 else
192 p = 1;
193 for(n = 1; n < 128; n++){
194 for(d = 1; d < 32; d++){
195 f = vga->f[0] - (RefFreq*n)/(d*(1+p));
196 if(f < 0)
197 f = -f;
198 if(f <= fmin){
199 fmin = f;
200 nmin = n;
201 dmin = d;
202 }
203 }
204 }
205
206 vga->f[0] = (RefFreq*nmin)/(dmin*(1+p));
207 vga->d[0] = dmin;
208 vga->n[0] = nmin;
209 vga->p[0] = p;
210 }
211
212 void
init(Vga * vga,Ctlr * ctlr)213 init(Vga* vga, Ctlr* ctlr)
214 {
215 Mode *mode;
216 Gd542x *gd542x;
217 ushort x;
218
219 mode = vga->mode;
220 gd542x = identify(vga, ctlr);
221
222 if(vga->f[0] == 0)
223 vga->f[0] = vga->mode->frequency;
224 if(vga->f[0] > gd542x->vclk)
225 error("%s: pclk %lud too high (> %lud)\n",
226 ctlr->name, vga->f[0], gd542x->vclk);
227
228 if(mode->z > 8)
229 error("%s: depth %d not supported\n", ctlr->name, mode->z);
230
231 /*
232 * VCLK3
233 */
234 clgd54xxclock(vga, ctlr);
235 vga->misc |= 0x0C;
236 vga->sequencer[0x0E] = vga->n[0];
237 vga->sequencer[0x1E] = (vga->d[0]<<1)|vga->p[0];
238
239 vga->sequencer[0x07] = 0x00;
240 if(mode->z == 8)
241 vga->sequencer[0x07] |= 0x01;
242
243 if(vga->f[0] >= 42000000)
244 vga->sequencer[0x0F] |= 0x20;
245 else
246 vga->sequencer[0x0F] &= ~0x20;
247
248 vga->sequencer[0x16] = (vga->sequencer[0x16] & 0xF0)|0x08;
249
250 /*
251 * Overflow bits.
252 */
253 vga->crt[0x1A] = 0x00;
254 x = mode->ehb>>3;
255 if(x & 0x40)
256 vga->crt[0x1A] |= 0x10;
257 if(x & 0x80)
258 vga->crt[0x1A] |= 0x20;
259 if(vga->crt[0x16] & 0x100)
260 vga->crt[0x1A] |= 0x40;
261 if(vga->crt[0x16] & 0x200)
262 vga->crt[0x1A] |= 0x80;
263 vga->crt[0x1B] = 0x22;
264 if(vga->crt[0x13] & 0x100)
265 vga->crt[0x1B] |= 0x10;
266
267 vga->graphics[0x0B] = 0x00;
268 if(vga->vmz > 1024*1024)
269 vga->graphics[0x0B] |= 0x20;
270
271 if(mode->interlace == 'v'){
272 vga->crt[0x19] = vga->crt[0x00]/2;
273 vga->crt[0x1A] |= 0x01;
274 }
275 }
276
277 static void
load(Vga * vga,Ctlr * ctlr)278 load(Vga* vga, Ctlr* ctlr)
279 {
280 vgaxo(Seqx, 0x0E, vga->sequencer[0x0E]);
281 vgaxo(Seqx, 0x1E, vga->sequencer[0x1E]);
282 if(ctlr->flag & Ulinear)
283 vga->sequencer[0x07] |= 0xE0;
284 vgaxo(Seqx, 0x07, vga->sequencer[0x07]);
285 vgaxo(Seqx, 0x0F, vga->sequencer[0x0F]);
286 vgaxo(Seqx, 0x16, vga->sequencer[0x16]);
287
288 if(vga->mode->interlace == 'v')
289 vgaxo(Crtx, 0x19, vga->crt[0x19]);
290 vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
291 vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
292
293 vgaxo(Grx, 0x0B, vga->graphics[0x0B]);
294 }
295
296 static void
dump(Vga * vga,Ctlr * ctlr)297 dump(Vga* vga, Ctlr* ctlr)
298 {
299 int i;
300 char *name;
301
302 name = ctlr->name;
303
304 printitem(name, "Seq06");
305 for(i = 0x06; i < 0x20; i++)
306 printreg(vga->sequencer[i]);
307
308 printitem(name, "Crt19");
309 for(i = 0x19; i < 0x1E; i++)
310 printreg(vga->crt[i]);
311
312 printitem(name, "Gr09");
313 for(i = 0x09; i < 0x3A; i++)
314 printreg(vga->graphics[i]);
315
316 printitem(name, "Id Hdr");
317 printreg(vga->crt[0x27]);
318 printreg(vga->crt[0x28]);
319 }
320
321 Ctlr clgd542x = {
322 "clgd542x", /* name */
323 snarf, /* snarf */
324 0, /* options */
325 init, /* init */
326 load, /* load */
327 dump, /* dump */
328 };
329
330 Ctlr clgd542xhwgc = {
331 "clgd542xhwgc", /* name */
332 0, /* snarf */
333 0, /* options */
334 0, /* init */
335 0, /* load */
336 0, /* dump */
337 };
338