xref: /plan9-contrib/sys/src/cmd/aux/vga/main.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 Biobuf stdout;
9 
10 static int iflag, lflag, pflag, rflag;
11 static char *usage =
12 	"usage: aux/vga [ -BcdilpvV ] [ -b bios-id ] [ -m monitor ] [ -x db ] [ mode [ virtualsize ] ]\n";
13 
14 static char *dbname = "/lib/vgadb";
15 static char monitordb[128];
16 
17 static void
18 dump(Vga* vga)
19 {
20 	Ctlr *ctlr;
21 	Attr *attr;
22 
23 	if(vga->mode)
24 		dbdumpmode(vga->mode);
25 
26 	for(attr = vga->attr; attr; attr = attr->next)
27 		Bprint(&stdout, "vga->attr: %s=%s\n", attr->attr, attr->val);
28 
29 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
30 		if(ctlr->dump == 0)
31 			continue;
32 
33 		trace("%s->dump\n", ctlr->name);
34 		if(ctlr->flag && ctlr->flag != Fsnarf){
35 			printitem(ctlr->name, "flag");
36 			printflag(ctlr->flag);
37 			Bprint(&stdout, "\n");
38 		}
39 		(*ctlr->dump)(vga, ctlr);
40 		ctlr->flag |= Fdump;
41 	}
42 	Bprint(&stdout, "\n");
43 }
44 
45 void
46 resyncinit(Vga* vga, Ctlr* ctlr, ulong on, ulong off)
47 {
48 	Ctlr *link;
49 
50 	trace("%s->resyncinit on 0x%8.8luX off 0x%8.8luX\n",
51 		ctlr->name, on, off);
52 
53 	for(link = vga->link; link; link = link->link){
54 		link->flag |= on;
55 		link->flag &= ~off;
56 		if(link == ctlr)
57 			continue;
58 
59 		if(link->init == 0 || (link->flag & Finit) == 0)
60 			continue;
61 		link->flag &= ~Finit;
62 		trace("%s->init 0x%8.8luX\n", link->name, link->flag);
63 		(*link->init)(vga, link);
64 	}
65 }
66 
67 void
68 sequencer(Vga* vga, int on)
69 {
70 	static uchar seq01;
71 	static int state = 1;
72 	char *s;
73 
74 	if(on)
75 		s = "on";
76 	else
77 		s = "off";
78 	trace("sequencer->enter %s\n", s);
79 	if(on){
80 		if(vga)
81 			seq01 = vga->sequencer[0x01];
82 		if(state == 0){
83 			seq01 |= 0x01;
84 			vgaxo(Seqx, 0x01, seq01);
85 			vgaxo(Seqx, 0x00, 0x03);
86 		}
87 	}
88 	else{
89 		vgaxo(Seqx, 0x00, 0x01);
90 		seq01 = vgaxi(Seqx, 0x01);
91 		vgaxo(Seqx, 0x01, seq01|0x20);
92 	}
93 	state = on;
94 	trace("sequencer->leave %s\n", s);
95 }
96 
97 static void
98 linear(Vga* vga)
99 {
100 	char buf[256];
101 
102 	/*
103 	 * Set up for linear addressing: try to allocate the
104 	 * kernel memory map then read the base-address back.
105 	 * vga->linear is a compatibility hack.
106 	 */
107 	if(vga->linear == 0){
108 		vga->ctlr->flag &= ~Ulinear;
109 		return;
110 	}
111 	if(vga->ctlr->flag & Ulinear){
112 		sprint(buf, "0x%lux 0x%lux", vga->apz ? vga->apz : vga->vmz, vga->vma);
113 		vgactlw("linear", buf);
114 		vgactlr("addr", buf);
115 		trace("linear->addr %s\n", buf);
116 		vga->vmb = strtoul(buf, 0, 0);
117 	}
118 	else
119 		vgactlw("linear", "0");
120 }
121 
122 char*
123 chanstr[32+1] = {
124 [1]	"k1",
125 [2]	"k2",
126 [4]	"k4",
127 [8]	"m8",
128 [16]	"r5g6b5",
129 [24]	"r8g8b8",
130 [32]	"x8r8g8b8",
131 };
132 
133 void
134 main(int argc, char** argv)
135 {
136 	char *bios, buf[256], sizeb[256], *p, *vsize, *psize, *type;
137 	int virtual, len;
138 	Ctlr *ctlr;
139 	Vga *vga;
140 
141 	Binit(&stdout, 1, OWRITE);
142 
143 	bios = getenv("vgactlr");
144 	if((type = getenv("monitor")) == 0)
145 		type = "vga";
146 	psize = vsize = "640x480x8";
147 
148 	ARGBEGIN{
149 
150 	case 'b':
151 		bios = ARGF();
152 		break;
153 
154 	case 'B':
155 		dumpbios(0x10000);
156 		exits(0);
157 
158 	case 'c':
159 		cflag = 1;
160 		break;
161 
162 	case 'd':
163 		dflag = 1;
164 		break;
165 
166 	case 'i':
167 		iflag = 1;
168 		break;
169 
170 	case 'l':
171 		lflag = 1;
172 		break;
173 
174 	case 'm':
175 		type = ARGF();
176 		break;
177 
178 	case 'p':
179 		pflag = 1;
180 		break;
181 
182 	case 'r':
183 		/*
184 		 * rflag > 1 means "leave me alone, I know what I'm doing."
185 		 */
186 		rflag++;
187 		break;
188 
189 	case 'v':
190 		vflag = 1;
191 		break;
192 
193 	case 'V':
194 		vflag = 1;
195 		Vflag = 1;
196 		break;
197 
198 	case 'x':
199 		dbname = ARGF();
200 		break;
201 
202 	default:
203 		error(usage, argv0);
204 
205 	}ARGEND
206 
207 	virtual = 0;
208 	switch(argc){
209 	case 1:
210 		vsize = psize = argv[0];
211 		break;
212 	case 2:
213 		psize = argv[0];
214 		vsize = argv[1];
215 		virtual = 1;
216 		break;
217 	case 0:
218 		break;
219 	default:
220 		error(usage, argv0);
221 	}
222 
223 	vga = alloc(sizeof(Vga));
224 	if(bios){
225 		if((vga->offset = strtol(bios, &p, 0)) == 0 || *p++ != '=')
226 			error("main: bad BIOS string format - %s\n", bios);
227 		len = strlen(p);
228 		vga->bios = alloc(len+1);
229 		strncpy(vga->bios, p, len);
230 		trace("main->BIOS %s\n", bios);
231 	}
232 
233 	/*
234 	 * Try to identify the VGA card and grab
235 	 * registers. Print them out if requested.
236 	 */
237 	if(dbctlr(dbname, vga) == 0 || vga->ctlr == 0){
238 		Bprint(&stdout, "%s: controller not in %s\n", argv0, dbname);
239 		dumpbios(256);
240 		type = "vga";
241 		vsize = psize = "640x480x1";
242 		virtual = 0;
243 		vga->ctlr = &generic;
244 		vga->link = &generic;
245 	}
246 
247 	trace("main->snarf\n");
248 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
249 		if(ctlr->snarf == 0)
250 			continue;
251 		trace("%s->snarf\n", ctlr->name);
252 		(*ctlr->snarf)(vga, ctlr);
253 	}
254 
255 	if(pflag)
256 		dump(vga);
257 
258 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link)
259 		if(ctlr->flag & Ferror)
260 			error("%r");
261 
262 	if(iflag || lflag){
263 		if(getenv(type))
264 			sprint(monitordb, "/env/%s", type);
265 		else
266 			strcpy(monitordb, dbname);
267 
268 		if((vga->mode = dbmode(monitordb, type, psize)) == 0)
269 			error("main: %s@%s not in %s\n", type, psize, monitordb);
270 		if(virtual){
271 			if((p = strchr(vsize, 'x')) == nil)
272 				error("bad virtual size %s\n", vsize);
273 			vga->virtx = atoi(vsize);
274 			vga->virty = atoi(p+1);
275 			if(vga->virtx < vga->mode->x || vga->virty < vga->mode->y)
276 				error("virtual size smaller than physical size\n");
277 			vga->panning = 1;
278 		}
279 		else{
280 			vga->virtx = vga->mode->x;
281 			vga->virty = vga->mode->y;
282 			vga->panning = 0;
283 		}
284 
285 		trace("vmf %d vmdf %d vf1 %lud vbw %lud\n",
286 			vga->mode->frequency, vga->mode->deffrequency,
287 			vga->f[1], vga->mode->videobw);
288 		if(vga->mode->frequency == 0 && vga->mode->videobw != 0 && vga->f[1] != 0){
289 			/*
290 			 * boost clock as much as possible subject
291 			 * to video and memory bandwidth constraints
292 			 */
293 			ulong bytes, freq, membw;
294 			double rr;
295 
296 			freq = vga->mode->videobw;
297 			if(freq > vga->f[1])
298 				freq = vga->f[1];
299 
300 			rr = (double)freq/(vga->mode->ht*vga->mode->vt);
301 			if(rr > 85.0)		/* >85Hz is ridiculous */
302 				rr = 85.0;
303 
304 			bytes = (vga->mode->x*vga->mode->y*vga->mode->z)/8;
305 			membw = rr*bytes;
306 			if(vga->membw != 0 && membw > vga->membw){
307 				membw = vga->membw;
308 				rr = (double)membw/bytes;
309 			}
310 
311 			freq = rr*(vga->mode->ht*vga->mode->vt);
312 			vga->mode->frequency = freq;
313 
314 			trace("using frequency %lud rr %.2f membw %lud\n",
315 				freq, rr, membw);
316 		}
317 		else if(vga->mode->frequency == 0)
318 			vga->mode->frequency = vga->mode->deffrequency;
319 
320 		for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
321 			if(ctlr->options == 0)
322 				continue;
323 			trace("%s->options\n", ctlr->name);
324 			(*ctlr->options)(vga, ctlr);
325 		}
326 
327 		for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
328 			if(ctlr->init == 0)
329 				continue;
330 			trace("%s->init\n", ctlr->name);
331 			(*ctlr->init)(vga, ctlr);
332 		}
333 
334 		if(strcmp(vga->mode->chan, "") == 0){
335 			if(vga->mode->z < nelem(chanstr) && chanstr[vga->mode->z])
336 				strcpy(vga->mode->chan, chanstr[vga->mode->z]);
337 			else
338 				error("%s: unknown channel type to use for depth %d", vga->ctlr->name, vga->mode->z);
339 		}
340 
341 		if(iflag || pflag)
342 			dump(vga);
343 
344 		if(lflag){
345 			trace("main->load\n");
346 			if(vga->vmz && (vga->virtx*vga->virty*vga->mode->z)/8 > vga->vmz)
347 				error("%s: not enough video memory - %lud\n",
348 					vga->ctlr->name, vga->vmz);
349 
350 			if(vga->ctlr->type)
351 				vgactlw("type", vga->ctlr->type);
352 			else if(p = strchr(vga->ctlr->name, '-')){
353 				strncpy(buf, vga->ctlr->name, p - vga->ctlr->name);
354 				buf[p - vga->ctlr->name] = 0;
355 				vgactlw("type", buf);
356 			}
357 			else
358 				vgactlw("type", vga->ctlr->name);
359 
360 			/*
361 			 * The new draw device needs linear mode set
362 			 * before size.
363 			 */
364 			linear(vga);
365 
366 			sprint(buf, "%ludx%ludx%d %s",
367 				vga->virtx, vga->virty,
368 				vga->mode->z, vga->mode->chan);
369 			if(rflag){
370 				vgactlr("size", sizeb);
371 				if(rflag < 2 && strcmp(buf, sizeb) != 0)
372 					error("bad refresh: %s != %s\n",
373 						buf, sizeb);
374 			}
375 			else
376 				vgactlw("size", buf);
377 
378 			/*
379 			 * Turn off the display during the load.
380 			 */
381 			sequencer(vga, 0);
382 
383 			for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
384 				if(ctlr->load == 0)
385 					continue;
386 				trace("%s->load\n", ctlr->name);
387 				(*ctlr->load)(vga, ctlr);
388 			}
389 
390 			sequencer(vga, 1);
391 
392 			vgactlw("drawinit", "");
393 
394 			if(vga->hwgc == 0 || cflag)
395 				vgactlw("hwgc", "off");
396 			else
397 				vgactlw("hwgc", vga->hwgc->name);
398 
399 			if(vga->virtx != vga->mode->x || vga->virty != vga->mode->y){
400 				sprint(buf, "%dx%d", vga->mode->x, vga->mode->y);
401 				vgactlw("actualsize", buf);
402 				if(vga->panning)
403 					vgactlw("panning", "on");
404 			}
405 
406 			if(pflag)
407 				dump(vga);
408 		}
409 	}
410 
411 	trace("main->exits\n");
412 	exits(0);
413 }
414