xref: /plan9/sys/src/cmd/aux/vga/mach32.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 Mach32. Some hope.
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 	Advfunc		= 0x4AE8,	/* Advanced Function Control Register */
20 	Clocksel	= 0x4AEE,	/* Clock Select Register */
21 	Misc		= 0x36EE,	/* Miscellaneous Register */
22 	Membndry	= 0x42EE,	/* Memory Boundary Register */
23 	Memcfg		= 0x5EEE,	/* Memory Control Register */
24 };
25 
26 typedef struct {
27 	ushort	advfunc;
28 	ushort	clocksel;
29 	ushort	misc;
30 	ushort	membndry;
31 	ushort	memcfg;
32 } Mach32;
33 
34 /*
35  * There are a number of possible clock generator chips for these
36  * boards, and I don't know how to find out which is installed, other
37  * than by looking at the board. So, pick a subset that will work for
38  * all.
39  */
40 typedef struct {
41 	ulong	frequency;
42 	uchar	b8;			/* <6> - divide by 2 */
43 	uchar	b9;			/* <1> - bit <3> of frequency index */
44 	uchar	be;			/* <4> - bit <2> of frequency index */
45 	uchar	misc;			/* <3:2> - bits <1:0> of frequency index */
46 } Clock;
47 
48 static Clock clocks[] = {
49 	{  VgaFreq0,	0x40, 0x02, 0x00, 0x00, },
50 	{  32000000,	0x00, 0x00, 0x10, 0x04, },
51 	{  40000000,	0x00, 0x02, 0x10, 0x00, },
52 	{  44900000,	0x00, 0x02, 0x00, 0x0C, },
53 	{  65000000,	0x00, 0x02, 0x10, 0x0C, },
54 	{  75000000,	0x00, 0x02, 0x10, 0x08, },
55 	{	  0, },
56 };
57 
58 static ulong atix;
59 
60 static uchar
atixi(uchar index)61 atixi(uchar index)
62 {
63 	outportb(atix, index);
64 	return inportb(atix+1);
65 }
66 
67 static void
atixo(uchar index,uchar data)68 atixo(uchar index, uchar data)
69 {
70 	outportw(atix, (data<<8)|index);
71 }
72 
73 static void
atixinit(Vga * vga,Ctlr *)74 atixinit(Vga* vga, Ctlr*)
75 {
76 	uchar b;
77 	Mach32 *mach32;
78 
79 	/*
80 	 * We could try to read in a part of the BIOS and try to determine
81 	 * the extended register address, but since we can't determine the offset value,
82 	 * we'll just have to assume the defaults all round.
83 	 */
84 	atix = 0x1CE;
85 
86 	/*
87 	 * Unlock the ATI Extended Registers.
88 	 * We leave them unlocked from now on.
89 	 * Why does this chip have so many
90 	 * lock bits?
91 	 */
92 	if((b = atixi(0xB8)) & 0x3F)
93 		atixo(0xB8, b & 0xC0);
94 	b = atixi(0xAB);
95 	atixo(0xAB, b & ~0x18);
96 	atixo(0xB4, 0x00);
97 	b = atixi(0xB9);
98 	atixo(0xB9, b & ~0x80);
99 	b = atixi(0xBE);
100 	atixo(0xBE, b|0x09);
101 
102 	if(vga->private == 0)
103 		vga->private = alloc(sizeof(mach32));
104 }
105 
106 static void
snarf(Vga * vga,Ctlr * ctlr)107 snarf(Vga* vga, Ctlr* ctlr)
108 {
109 	int i;
110 	Mach32 *mach32;
111 
112 	atixinit(vga, ctlr);
113 	for(i = 0xA0; i < 0xC0; i++)
114 		vga->crt[i] = atixi(i);
115 
116 	mach32 = vga->private;
117 	mach32->advfunc = inportw(Advfunc);
118 	mach32->clocksel = inportw(Clocksel);
119 	mach32->misc = inportw(Misc);
120 	mach32->membndry = inportw(Membndry);
121 	mach32->memcfg = inportw(Memcfg);
122 
123 	/*
124 	 * Memory size.
125 	 */
126 	switch((mach32->misc>>2) & 0x03){
127 
128 	case 0:
129 		vga->vmz = 512*1024;
130 		break;
131 
132 	case 1:
133 		vga->vmz = 1024*1024;
134 		break;
135 
136 	case 2:
137 		vga->vmz = 2*1024*1024;
138 		break;
139 
140 	case 3:
141 		vga->vmz = 4*1024*1024;
142 		break;
143 	}
144 
145 	ctlr->flag |= Fsnarf;
146 }
147 
148 static void
options(Vga *,Ctlr * ctlr)149 options(Vga*, Ctlr* ctlr)
150 {
151 	ctlr->flag |= Foptions;
152 }
153 
154 static void
init(Vga * vga,Ctlr * ctlr)155 init(Vga* vga, Ctlr* ctlr)
156 {
157 	Clock *clockp;
158 	Mode *mode;
159 
160 	mode = vga->mode;
161 
162 	if(vga->f[0] == 0)
163 		vga->f[0] = vga->mode->frequency;
164 	for(clockp = clocks; clockp->frequency; clockp++){
165 		if(clockp->frequency > vga->f[0]+100000)
166 			continue;
167 		if(clockp->frequency > vga->f[0]-100000)
168 			break;
169 	}
170 	if(clockp->frequency == 0)
171 		error("%s: no suitable clock for %lud\n",
172 			ctlr->name, vga->f[0]);
173 
174 	vga->crt[0xB0] &= 0xDA;
175 	vga->crt[0xB1] &= 0x87;
176 	vga->crt[0xB5] &= 0x7E;
177 	vga->crt[0xB6] &= 0xE2;
178 	vga->crt[0xB3] &= 0xAF;
179 	vga->crt[0xA6] &= 0xFE;
180 	vga->crt[0xA7] &= 0xF4;
181 
182 	/*
183 	 * 256-colour linear addressing.
184 	 */
185 	if(mode->z == 8){
186 		vga->graphics[0x05] = 0x00;
187 		vga->attribute[0x10] &= ~0x40;
188 		vga->crt[0x13] = (mode->x/8)/2;
189 		vga->crt[0x14] = 0x00;
190 		vga->crt[0x17] = 0xE3;
191 
192 		vga->crt[0xB0] |= 0x20;
193 		vga->crt[0xB6] |= 0x04;
194 	}
195 	vga->attribute[0x11] = 0x00;
196 	vga->crt[0xB6] |= 0x01;
197 	vga->crt[0xBE] &= ~0x04;
198 
199 	/*
200 	 * Do the clock index bits.
201 	 */
202 	vga->crt[0xB9] &= 0xFD;
203 	vga->crt[0xB8] &= 0x3F;
204 	vga->crt[0xBE] &= 0xE5;
205 
206 	vga->crt[0xB8] |= clockp->b8;
207 	vga->crt[0xB9] |= clockp->b9;
208 	vga->crt[0xBE] |= clockp->be;
209 	vga->misc |= clockp->misc;
210 
211 	if(vga->mode->interlace == 'v')
212 		vga->crt[0xBE] |= 0x02;
213 
214 	/*
215 	 * Turn off 128Kb CPU address bit so
216 	 * we only have a 64Kb aperture at 0xA0000.
217 	 */
218 	vga->crt[0xBD] &= ~0x04;
219 
220 	ctlr->flag |= Finit;
221 }
222 
223 static void
load(Vga * vga,Ctlr * ctlr)224 load(Vga* vga, Ctlr* ctlr)
225 {
226 	ushort x;
227 
228 	/*
229 	 * Make sure we are in VGA mode,
230 	 * and that we have access to all the video memory through
231 	 * the 64Kb VGA aperture by disabling and linear aperture
232 	 * and memory boundary.
233 	 */
234 	outportw(Clocksel, 0x0000);
235 	x = inportw(Memcfg) & ~0x0003;
236 	outportw(Memcfg, x);
237 	outportw(Membndry, 0x0000);
238 
239 	atixo(0xB0, vga->crt[0xB0]);
240 	atixo(0xB1, vga->crt[0xB1]);
241 	atixo(0xB5, vga->crt[0xB5]);
242 	atixo(0xB6, vga->crt[0xB6]);
243 	atixo(0xB3, vga->crt[0xB3]);
244 	atixo(0xA6, vga->crt[0xA6]);
245 	atixo(0xA7, vga->crt[0xA7]);
246 	atixo(0xB8, vga->crt[0xB8]);
247 	atixo(0xB9, vga->crt[0xB9]);
248 	atixo(0xBE, vga->crt[0xBE]);
249 	vgao(MiscW, vga->misc);
250 
251 	ctlr->flag |= Fload;
252 }
253 
254 static void
dump(Vga * vga,Ctlr * ctlr)255 dump(Vga* vga, Ctlr* ctlr)
256 {
257 	int i;
258 	Mach32 *mach32;
259 
260 	printitem(ctlr->name, "ATIX");
261 	for(i = 0xA0; i < 0xC0; i++)
262 		printreg(vga->crt[i]);
263 
264 	if((mach32 = vga->private) == 0)
265 		return;
266 
267 	printitem(ctlr->name, "ADVFUNC");
268 	Bprint(&stdout, "%.4ux\n", mach32->advfunc);
269 	printitem(ctlr->name, "CLOCKSEL");
270 	Bprint(&stdout, "%.4ux\n", mach32->clocksel);
271 	printitem(ctlr->name, "MISC");
272 	Bprint(&stdout, "%.4ux\n", mach32->misc);
273 	printitem(ctlr->name, "MEMBNDRY");
274 	Bprint(&stdout, "%.4ux\n", mach32->membndry);
275 	printitem(ctlr->name, "MEMCFG");
276 	Bprint(&stdout, "%.4ux\n", mach32->memcfg);
277 }
278 
279 Ctlr mach32 = {
280 	"mach32",			/* name */
281 	snarf,				/* snarf */
282 	options,			/* options */
283 	init,				/* init */
284 	load,				/* load */
285 	dump,				/* dump */
286 };
287