summaryrefslogtreecommitdiff
path: root/sys/src/cmd/aux/vga/mach32.c
blob: e183a2da2b5cfbcd24af07f8c8663d79f1eb59b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#include <u.h>
#include <libc.h>
#include <bio.h>

#include "pci.h"
#include "vga.h"

/*
 * ATI Mach32. Some hope.
 * No support for accelerator so can only do up to 1024x768.
 *
 * All ATI Extended Registers are addressed using the modified index
 *	index = (0x02<<6)|(index & 0x3F);
 * so registers 0x00->0x3F map to 0x80->0xBF, but we will only ever
 * look at a few in the range 0xA0->0xBF. In this way we can stash
 * them in the vga->crt[] array.
 */
enum {
	Advfunc		= 0x4AE8,	/* Advanced Function Control Register */
	Clocksel	= 0x4AEE,	/* Clock Select Register */
	Misc		= 0x36EE,	/* Miscellaneous Register */
	Membndry	= 0x42EE,	/* Memory Boundary Register */
	Memcfg		= 0x5EEE,	/* Memory Control Register */
};

typedef struct {
	ushort	advfunc;
	ushort	clocksel;
	ushort	misc;
	ushort	membndry;
	ushort	memcfg;
} Mach32;

/*
 * There are a number of possible clock generator chips for these
 * boards, and I don't know how to find out which is installed, other
 * than by looking at the board. So, pick a subset that will work for
 * all.
 */
typedef struct {
	ulong	frequency;
	uchar	b8;			/* <6> - divide by 2 */
	uchar	b9;			/* <1> - bit <3> of frequency index */
	uchar	be;			/* <4> - bit <2> of frequency index */
	uchar	misc;			/* <3:2> - bits <1:0> of frequency index */
} Clock;

static Clock clocks[] = {
	{  VgaFreq0,	0x40, 0x02, 0x00, 0x00, },
	{  32000000,	0x00, 0x00, 0x10, 0x04, },
	{  40000000,	0x00, 0x02, 0x10, 0x00, },
	{  44900000,	0x00, 0x02, 0x00, 0x0C, },
	{  65000000,	0x00, 0x02, 0x10, 0x0C, },
	{  75000000,	0x00, 0x02, 0x10, 0x08, },
	{	  0, },
};
	
static ulong atix;

static uchar
atixi(uchar index)
{
	outportb(atix, index);
	return inportb(atix+1);
}

static void
atixo(uchar index, uchar data)
{
	outportw(atix, (data<<8)|index);
}

static void
atixinit(Vga* vga, Ctlr*)
{
	uchar b;
	Mach32 *mach32;

	/*
	 * We could try to read in a part of the BIOS and try to determine
	 * the extended register address, but since we can't determine the offset value,
	 * we'll just have to assume the defaults all round.
	 */
	atix = 0x1CE;

	/*
	 * Unlock the ATI Extended Registers.
	 * We leave them unlocked from now on.
	 * Why does this chip have so many
	 * lock bits?
	 */
	if((b = atixi(0xB8)) & 0x3F)
		atixo(0xB8, b & 0xC0);
	b = atixi(0xAB);
	atixo(0xAB, b & ~0x18);
	atixo(0xB4, 0x00);
	b = atixi(0xB9);
	atixo(0xB9, b & ~0x80);
	b = atixi(0xBE);
	atixo(0xBE, b|0x09);

	if(vga->private == 0)
		vga->private = alloc(sizeof(mach32));
}

static void
snarf(Vga* vga, Ctlr* ctlr)
{
	int i;
	Mach32 *mach32;

	atixinit(vga, ctlr);
	for(i = 0xA0; i < 0xC0; i++)
		vga->crt[i] = atixi(i);

	mach32 = vga->private;
	mach32->advfunc = inportw(Advfunc);
	mach32->clocksel = inportw(Clocksel);
	mach32->misc = inportw(Misc);
	mach32->membndry = inportw(Membndry);
	mach32->memcfg = inportw(Memcfg);

	/*
	 * Memory size.
	 */
	switch((mach32->misc>>2) & 0x03){

	case 0:
		vga->vmz = 512*1024;
		break;

	case 1:
		vga->vmz = 1024*1024;
		break;

	case 2:
		vga->vmz = 2*1024*1024;
		break;

	case 3:
		vga->vmz = 4*1024*1024;
		break;
	}

	ctlr->flag |= Fsnarf;
}

static void
options(Vga*, Ctlr* ctlr)
{
	ctlr->flag |= Foptions;
}

static void
init(Vga* vga, Ctlr* ctlr)
{
	Clock *clockp;
	Mode *mode;

	mode = vga->mode;

	if(vga->f[0] == 0)
		vga->f[0] = vga->mode->frequency;
	for(clockp = clocks; clockp->frequency; clockp++){
		if(clockp->frequency > vga->f[0]+100000)
			continue;
		if(clockp->frequency > vga->f[0]-100000)
			break;
	}
	if(clockp->frequency == 0)
		error("%s: no suitable clock for %lud\n",
			ctlr->name, vga->f[0]);

	vga->crt[0xB0] &= 0xDA;
	vga->crt[0xB1] &= 0x87;
	vga->crt[0xB5] &= 0x7E;
	vga->crt[0xB6] &= 0xE2;
	vga->crt[0xB3] &= 0xAF;
	vga->crt[0xA6] &= 0xFE;
	vga->crt[0xA7] &= 0xF4;

	/*
	 * 256-colour linear addressing.
	 */
	if(mode->z == 8){
		vga->graphics[0x05] = 0x00;
		vga->attribute[0x10] &= ~0x40;
		vga->crt[0x13] = (mode->x/8)/2;
		vga->crt[0x14] = 0x00;
		vga->crt[0x17] = 0xE3;

		vga->crt[0xB0] |= 0x20;
		vga->crt[0xB6] |= 0x04;
	}
	vga->attribute[0x11] = 0x00;
	vga->crt[0xB6] |= 0x01;
	vga->crt[0xBE] &= ~0x04;

	/*
	 * Do the clock index bits.
	 */
	vga->crt[0xB9] &= 0xFD;
	vga->crt[0xB8] &= 0x3F;
	vga->crt[0xBE] &= 0xE5;

	vga->crt[0xB8] |= clockp->b8;
	vga->crt[0xB9] |= clockp->b9;
	vga->crt[0xBE] |= clockp->be;
	vga->misc |= clockp->misc;

	if(vga->mode->interlace == 'v')
		vga->crt[0xBE] |= 0x02;

	/*
	 * Turn off 128Kb CPU address bit so
	 * we only have a 64Kb aperture at 0xA0000.
	 */
	vga->crt[0xBD] &= ~0x04;

	ctlr->flag |= Finit;
}

static void
load(Vga* vga, Ctlr* ctlr)
{
	ushort x;

	/*
	 * Make sure we are in VGA mode,
	 * and that we have access to all the video memory through
	 * the 64Kb VGA aperture by disabling and linear aperture
	 * and memory boundary.
	 */
	outportw(Clocksel, 0x0000);
	x = inportw(Memcfg) & ~0x0003;
	outportw(Memcfg, x);
	outportw(Membndry, 0x0000);

	atixo(0xB0, vga->crt[0xB0]);
	atixo(0xB1, vga->crt[0xB1]);
	atixo(0xB5, vga->crt[0xB5]);
	atixo(0xB6, vga->crt[0xB6]);
	atixo(0xB3, vga->crt[0xB3]);
	atixo(0xA6, vga->crt[0xA6]);
	atixo(0xA7, vga->crt[0xA7]);
	atixo(0xB8, vga->crt[0xB8]);
	atixo(0xB9, vga->crt[0xB9]);
	atixo(0xBE, vga->crt[0xBE]);
	vgao(MiscW, vga->misc);

	ctlr->flag |= Fload;
}

static void
dump(Vga* vga, Ctlr* ctlr)
{
	int i;
	Mach32 *mach32;

	printitem(ctlr->name, "ATIX");
	for(i = 0xA0; i < 0xC0; i++)
		printreg(vga->crt[i]);

	if((mach32 = vga->private) == 0)
		return;

	printitem(ctlr->name, "ADVFUNC");
	Bprint(&stdout, "%.4ux\n", mach32->advfunc);
	printitem(ctlr->name, "CLOCKSEL");
	Bprint(&stdout, "%.4ux\n", mach32->clocksel);
	printitem(ctlr->name, "MISC");
	Bprint(&stdout, "%.4ux\n", mach32->misc);
	printitem(ctlr->name, "MEMBNDRY");
	Bprint(&stdout, "%.4ux\n", mach32->membndry);
	printitem(ctlr->name, "MEMCFG");
	Bprint(&stdout, "%.4ux\n", mach32->memcfg);
}

Ctlr mach32 = {
	"mach32",			/* name */
	snarf,				/* snarf */
	options,			/* options */
	init,				/* init */
	load,				/* load */
	dump,				/* dump */
};