xref: /plan9/sys/src/cmd/aux/vga/mach64.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * ATI Mach64. Some hope. Kind of like a Mach32.
10  * No support for accelerator so can only do up to 1024x768.
11  *
12  * All ATI Extended Registers are addressed using the modified index
13  *	index = (0x02<<6)|(index & 0x3F);
14  * so registers 0x00->0x3F map to 0x80->0xBF, but we will only ever
15  * look at a few in the range 0xA0->0xBF. In this way we can stash
16  * them in the vga->crt[] array.
17  */
18 enum {
19 	Configcntl	= 0x6AEC,	/* Configuration control */
20 	Configstat	= 0x72EC,	/* Configuration status */
21 	Memcntl		= 0x52EC,	/* Memory control */
22 	Scratch1	= 0x46EC,	/* Scratch Register (BIOS info) */
23 };
24 
25 typedef struct {
26 	ulong	configcntl;
27 	ulong	configstat;
28 	ulong	memcntl;
29 	ulong	scratch1;
30 } Mach64;
31 
32 /*
33  * There are a number of possible clock generator chips for these
34  * boards. We can divide any frequency by 2 (bit<6> of b8).
35  */
36 typedef struct {
37 	ulong	frequency;
38 	uchar	be;			/* <4> - bit<3> of frequency index */
39 	uchar	b9;			/* <1> - bit<2> of frequency index */
40 	uchar	genmo;			/* <3:2> - bits <1:0> of frequency index */
41 } Pclk;
42 
43 enum {
44 	Npclkx		= 16,		/* number of clock entries per table */
45 };
46 
47 /*
48  * ATI18811-0
49  */
50 static Pclk ati188110[Npclkx] = {
51 	{  42950000, 0x00, 0x00, 0x00 },
52 	{  48770000, 0x00, 0x00, 0x04 },
53 	{  92400000, 0x00, 0x00, 0x08 },
54 	{  36000000, 0x00, 0x00, 0x0C },
55 	{  50350000, 0x00, 0x02, 0x00 },
56 	{  56640000, 0x00, 0x02, 0x04 },
57 	{ 	  0, 0x00, 0x02, 0x08 },
58 	{  44900000, 0x00, 0x02, 0x0C },
59 	{  30240000, 0x10, 0x00, 0x00 },
60 	{  32000000, 0x10, 0x00, 0x04 },
61 	{ 110000000, 0x10, 0x00, 0x08 },
62 	{  80000000, 0x10, 0x00, 0x0C },
63 	{  39910000, 0x10, 0x02, 0x00 },
64 	{  44900000, 0x10, 0x02, 0x04 },
65 	{  75000000, 0x10, 0x02, 0x08 },
66 	{  65000000, 0x10, 0x02, 0x0C },
67 };
68 
69 /*
70  * ATI18811-1, ATI18811-2
71  * PCLK_TABLE = 0 in Mach64 speak.
72  */
73 static Pclk ati188111[Npclkx] = {
74 	{ 100000000, 0x00, 0x00, 0x00 },
75 	{ 126000000, 0x00, 0x00, 0x04 },
76 	{  92400000, 0x00, 0x00, 0x08 },
77 	{  36000000, 0x00, 0x00, 0x0C },
78 	{  50350000, 0x00, 0x02, 0x00 },
79 	{  56640000, 0x00, 0x02, 0x04 },
80 	{ 	  0, 0x00, 0x02, 0x08 },
81 	{  44900000, 0x00, 0x02, 0x0C },
82 	{ 135000000, 0x10, 0x00, 0x00 },
83 	{  32000000, 0x10, 0x00, 0x04 },
84 	{ 110000000, 0x10, 0x00, 0x08 },
85 	{  80000000, 0x10, 0x00, 0x0C },
86 	{  39910000, 0x10, 0x02, 0x00 },
87 	{  44900000, 0x10, 0x02, 0x04 },
88 	{  75000000, 0x10, 0x02, 0x08 },
89 	{  65000000, 0x10, 0x02, 0x0C },
90 };
91 
92 /*
93  * ATI18818
94  * The first four entries are programmable and the default
95  * settings are either those below or those below divided by 2
96  * (PCLK_TABLE = 1 and PCLK_TABLE = 2 respectively in Mach64
97  * speak).
98  */
99 static Pclk ati18818[Npclkx] = {
100 	{  50350000, 0x00, 0x00, 0x00 },
101 	{  56640000, 0x00, 0x00, 0x04 },
102 	{  63000000, 0x00, 0x00, 0x08 },
103 	{  72000000, 0x00, 0x00, 0x0C },
104 	{  40000000, 0x00, 0x02, 0x00 },
105 	{  44900000, 0x00, 0x02, 0x04 },
106 	{  49500000, 0x00, 0x02, 0x08 },
107 	{  50000000, 0x00, 0x02, 0x0C },
108 	{ 	  0, 0x10, 0x00, 0x00 },
109 	{ 110000000, 0x10, 0x00, 0x04 },
110 	{ 126000000, 0x10, 0x00, 0x08 },
111 	{ 135000000, 0x10, 0x00, 0x0C },
112 	{ 	  0, 0x10, 0x02, 0x00 },
113 	{  80000000, 0x10, 0x02, 0x04 },
114 	{  75000000, 0x10, 0x02, 0x08 },
115 	{  65000000, 0x10, 0x02, 0x0C },
116 };
117 
118 static Pclk *pclkp;			/* which clock chip we are using */
119 static ulong atix;			/* index to extended regsiters */
120 
121 static uchar
atixi(uchar index)122 atixi(uchar index)
123 {
124 	outportb(atix, index);
125 	return inportb(atix+1);
126 }
127 
128 static void
atixo(uchar index,uchar data)129 atixo(uchar index, uchar data)
130 {
131 	outportw(atix, (data<<8)|index);
132 }
133 
134 static void
atixinit(Vga * vga,Ctlr *)135 atixinit(Vga* vga, Ctlr*)
136 {
137 	uchar b;
138 
139 	/*
140 	 * Set the I/O address and offset for the ATI
141 	 * extended registers to something we know about.
142 	 */
143 	if(atix == 0){
144 		outportw(Grx, (0xCE<<8)|0x50);
145 		outportw(Grx, (0x81<<8)|0x51);
146 		atix = 0x1CE;
147 	}
148 
149 	/*
150 	 * Unlock the ATI Extended Registers.
151 	 * We leave them unlocked from now on.
152 	 * Why does this chip have so many
153 	 * lock bits?
154 	 */
155 	if((b = atixi(0xB8)) & 0x3F)
156 		atixo(0xB8, b & 0xC0);
157 	b = atixi(0xAB);
158 	atixo(0xAB, b & ~0x18);
159 	atixo(0xB4, 0x00);
160 	b = atixi(0xB9);
161 	atixo(0xB9, b & ~0x80);
162 	b = atixi(0xBE);
163 	atixo(0xBE, b|0x09);
164 
165 	if(vga->private == 0)
166 		vga->private = alloc(sizeof(Mach64));
167 }
168 
169 static void
snarf(Vga * vga,Ctlr * ctlr)170 snarf(Vga* vga, Ctlr* ctlr)
171 {
172 	int i;
173 	Mach64 *mach64;
174 
175 	atixinit(vga, ctlr);
176 	for(i = 0xA0; i < 0xC0; i++)
177 		vga->crt[i] = atixi(i);
178 
179 	mach64 = vga->private;
180 	mach64->configcntl = inportl(Configcntl);
181 	mach64->configstat = inportl(Configstat);
182 	mach64->memcntl = inportl(Memcntl);
183 	mach64->scratch1 = inportl(Scratch1);
184 
185 	/*
186 	 * Memory size.
187 	 */
188 	switch(mach64->memcntl & 0x07){
189 
190 	case 0:
191 		vga->vmz = 512*1024;
192 		break;
193 
194 	case 1:
195 		vga->vmz = 1024*1024;
196 		break;
197 
198 	case 2:
199 		vga->vmz = 2*1024*1024;
200 		break;
201 
202 	case 3:
203 		vga->vmz = 4*1024*1024;
204 		break;
205 
206 	case 4:
207 		vga->vmz = 6*1024*1024;
208 		break;
209 
210 	case 5:
211 		vga->vmz = 8*1024*1024;
212 		break;
213 	}
214 
215 	ctlr->flag |= Fsnarf;
216 }
217 
218 static void
init(Vga * vga,Ctlr * ctlr)219 init(Vga* vga, Ctlr* ctlr)
220 {
221 	Mode *mode;
222 	int f, divisor, index;
223 
224 	mode = vga->mode;
225 
226 	/*
227 	 * Must somehow determine which clock chip to use here.
228 	 * For now, punt and assume ATI18818.
229 	 */
230 	pclkp = ati18818;
231 	if(pclkp == 0)
232 		error("%s: can't determine clock chip\n", ctlr->name);
233 
234 	if(vga->f[0] == 0)
235 		vga->f[0] = vga->mode->frequency;
236 
237 	/*
238 	 * Find a clock frequency close to what we want.
239 	 * 'Close' is within 1MHz.
240 	 */
241 	for(divisor = 0, index = 0; index < Npclkx; index++, divisor = 0){
242 		divisor = 1;
243 		f = pclkp[index].frequency/divisor;
244 		if(f < vga->f[0]+1000000 && f >= vga->f[0]-1000000)
245 			break;
246 
247 		divisor = 2;
248 		f /= divisor;
249 		if(f < vga->f[0]+1000000 && f >= vga->f[0]-1000000)
250 			break;
251 	}
252 	if(divisor == 0)
253 		error("%s: no suitable clock for %lud\n",
254 			ctlr->name, vga->f[0]);
255 
256 	vga->d[0] = divisor;
257 	vga->i[0] = index;
258 
259 	vga->crt[0xB0] &= 0xDA;
260 	vga->crt[0xB1] &= 0x87;
261 	vga->crt[0xB5] &= 0x7E;
262 	vga->crt[0xB6] &= 0xE2;
263 	vga->crt[0xB3] &= 0xAF;
264 	vga->crt[0xA6] &= 0xFE;
265 	vga->crt[0xA7] &= 0xF4;
266 
267 	/*
268 	 * 256-colour linear addressing.
269 	 */
270 	if(mode->z == 8){
271 		vga->graphics[0x05] = 0x00;
272 		vga->attribute[0x10] &= ~0x40;
273 		vga->crt[0x13] = (mode->x/8)/2;
274 		vga->crt[0x14] = 0x00;
275 		vga->crt[0x17] = 0xE3;
276 
277 		vga->crt[0xB0] |= 0x20;
278 		vga->crt[0xB6] |= 0x04;
279 	}
280 	vga->attribute[0x11] = 0x00;
281 	vga->crt[0xB6] |= 0x01;
282 	vga->crt[0xBE] &= ~0x04;
283 
284 	/*
285 	 * Do the clock index bits.
286 	 */
287 	vga->crt[0xB8] &= 0x3F;
288 	vga->crt[0xB9] &= 0xFD;
289 	vga->crt[0xBE] &= 0xE5;
290 
291 	if(vga->d[0] == 2)
292 		vga->crt[0xB8] |= 0x40;
293 	vga->crt[0xB9] |= pclkp[vga->i[0]].b9;
294 	vga->crt[0xBE] |= pclkp[vga->i[0]].be;
295 	vga->misc |= pclkp[vga->i[0]].genmo;
296 
297 	if(vga->mode->interlace == 'v')
298 		vga->crt[0xBE] |= 0x02;
299 
300 	/*
301 	 * Turn off 128Kb CPU address bit so
302 	 * we only have a 64Kb aperture at 0xA0000.
303 	 */
304 	vga->crt[0xBD] &= ~0x04;
305 
306 	ctlr->type = mach32.name;
307 
308 	/*
309 	 * The Mach64 can only address 1Mb in VGA mode
310 	 */
311 	vga->vmz = 1*1024*1024;
312 
313 	ctlr->flag |= Finit;
314 }
315 
316 static void
load(Vga * vga,Ctlr * ctlr)317 load(Vga* vga, Ctlr* ctlr)
318 {
319 	/*
320 	 * We should probably do something here to make sure we that we
321 	 * have access to all the video memory through the 64Kb VGA aperture
322 	 * by disabling and linear aperture and memory boundary and then
323 	 * enabling the VGA controller.
324 	 * But for now, let's just assume it's ok, the Mach64 documentation
325 	 * is just as clear as the Mach32 documentation.
326 	 */
327 	atixo(0xB0, vga->crt[0xB0]);
328 	atixo(0xB1, vga->crt[0xB1]);
329 	atixo(0xB5, vga->crt[0xB5]);
330 	atixo(0xB6, vga->crt[0xB6]);
331 	atixo(0xB3, vga->crt[0xB3]);
332 	atixo(0xA6, vga->crt[0xA6]);
333 	atixo(0xA7, vga->crt[0xA7]);
334 	atixo(0xB8, vga->crt[0xB8]);
335 	atixo(0xB9, vga->crt[0xB9]);
336 	atixo(0xBE, vga->crt[0xBE]);
337 	vgao(MiscW, vga->misc);
338 
339 	ctlr->flag |= Fload;
340 }
341 
342 static void
dump(Vga * vga,Ctlr * ctlr)343 dump(Vga* vga, Ctlr* ctlr)
344 {
345 	int i;
346 	Mach64 *mach64;
347 
348 	printitem(ctlr->name, "ATIX");
349 	for(i = 0xA0; i < 0xC0; i++)
350 		printreg(vga->crt[i]);
351 
352 	if((mach64 = vga->private) == 0)
353 		return;
354 
355 	printitem(ctlr->name, "CONFIGCNTL");
356 	Bprint(&stdout, "%.8lux\n", mach64->configcntl);
357 	printitem(ctlr->name, "CONFIGSTAT");
358 	Bprint(&stdout, "%.8lux\n", mach64->configstat);
359 	printitem(ctlr->name, "MEMCNTL");
360 	Bprint(&stdout, "%.8lux\n", mach64->memcntl);
361 	printitem(ctlr->name, "SCRATCH1");
362 	Bprint(&stdout, "%.8lux\n", mach64->scratch1);
363 }
364 
365 Ctlr mach64 = {
366 	"mach64",			/* name */
367 	snarf,				/* snarf */
368 	0,				/* options */
369 	init,				/* init */
370 	load,				/* load */
371 	dump,				/* dump */
372 };
373