xref: /plan9/sys/src/9/pc/devvga.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1 /*
2  * VGA controller
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "../port/error.h"
11 
12 #define	Image	IMAGE
13 #include <draw.h>
14 #include <memdraw.h>
15 #include <cursor.h>
16 #include "screen.h"
17 
18 enum {
19 	Qdir,
20 	Qvgabios,
21 	Qvgactl,
22 	Qvgaovl,
23 	Qvgaovlctl,
24 };
25 
26 static Dirtab vgadir[] = {
27 	".",	{ Qdir, 0, QTDIR },		0,	0550,
28 	"vgabios",	{ Qvgabios, 0 },	0x100000, 0440,
29 	"vgactl",		{ Qvgactl, 0 },		0,	0660,
30 	"vgaovl",		{ Qvgaovl, 0 },		0,	0660,
31 	"vgaovlctl",	{ Qvgaovlctl, 0 },	0, 	0660,
32 };
33 
34 enum {
35 	CMactualsize,
36 	CMblank,
37 	CMblanktime,
38 	CMdrawinit,
39 	CMhwaccel,
40 	CMhwblank,
41 	CMhwgc,
42 	CMlinear,
43 	CMpalettedepth,
44 	CMpanning,
45 	CMsize,
46 	CMtextmode,
47 	CMtype,
48 	CMunblank,
49 };
50 
51 static Cmdtab vgactlmsg[] = {
52 	CMactualsize,	"actualsize",	2,
53 	CMblank,	"blank",	1,
54 	CMblanktime,	"blanktime",	2,
55 	CMdrawinit,	"drawinit",	1,
56 	CMhwaccel,	"hwaccel",	2,
57 	CMhwblank,	"hwblank",	2,
58 	CMhwgc,		"hwgc",		2,
59 	CMlinear,	"linear",	0,
60 	CMpalettedepth,	"palettedepth",	2,
61 	CMpanning,	"panning",	2,
62 	CMsize,		"size",		3,
63 	CMtextmode,	"textmode",	1,
64 	CMtype,		"type",		2,
65 	CMunblank,	"unblank",	1,
66 };
67 
68 static void
vgareset(void)69 vgareset(void)
70 {
71 	/* reserve the 'standard' vga registers */
72 	if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
73 		panic("vga ports already allocated");
74 	if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
75 		panic("vga ports already allocated");
76 	conf.monitor = 1;
77 }
78 
79 static Chan*
vgaattach(char * spec)80 vgaattach(char* spec)
81 {
82 	if(*spec && strcmp(spec, "0"))
83 		error(Eio);
84 	return devattach('v', spec);
85 }
86 
87 Walkqid*
vgawalk(Chan * c,Chan * nc,char ** name,int nname)88 vgawalk(Chan* c, Chan *nc, char** name, int nname)
89 {
90 	return devwalk(c, nc, name, nname, vgadir, nelem(vgadir), devgen);
91 }
92 
93 static int
vgastat(Chan * c,uchar * dp,int n)94 vgastat(Chan* c, uchar* dp, int n)
95 {
96 	return devstat(c, dp, n, vgadir, nelem(vgadir), devgen);
97 }
98 
99 static Chan*
vgaopen(Chan * c,int omode)100 vgaopen(Chan* c, int omode)
101 {
102 	VGAscr *scr;
103 	static char *openctl = "openctl\n";
104 
105 	scr = &vgascreen[0];
106 	if ((ulong)c->qid.path == Qvgaovlctl) {
107 		if (scr->dev && scr->dev->ovlctl)
108 			scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
109 		else
110 			error(Enonexist);
111 	}
112 	return devopen(c, omode, vgadir, nelem(vgadir), devgen);
113 }
114 
115 static void
vgaclose(Chan * c)116 vgaclose(Chan* c)
117 {
118 	VGAscr *scr;
119 	static char *closectl = "closectl\n";
120 
121 	scr = &vgascreen[0];
122 	if((ulong)c->qid.path == Qvgaovlctl)
123 		if(scr->dev && scr->dev->ovlctl){
124 			if(waserror()){
125 				print("ovlctl error: %s\n", up->errstr);
126 				return;
127 			}
128 			scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
129 			poperror();
130 		}
131 }
132 
133 static void
checkport(int start,int end)134 checkport(int start, int end)
135 {
136 	/* standard vga regs are OK */
137 	if(start >= 0x2b0 && end <= 0x2df+1)
138 		return;
139 	if(start >= 0x3c0 && end <= 0x3da+1)
140 		return;
141 
142 	if(iounused(start, end))
143 		return;
144 	error(Eperm);
145 }
146 
147 static long
vgaread(Chan * c,void * a,long n,vlong off)148 vgaread(Chan* c, void* a, long n, vlong off)
149 {
150 	int len;
151 	char *p, *s;
152 	VGAscr *scr;
153 	ulong offset = off;
154 	char chbuf[30];
155 
156 	switch((ulong)c->qid.path){
157 
158 	case Qdir:
159 		return devdirread(c, a, n, vgadir, nelem(vgadir), devgen);
160 
161 	case Qvgabios:
162 		if(offset >= 0x100000)
163 			return 0;
164 		if(offset+n >= 0x100000)
165 			n = 0x100000 - offset;
166 		memmove(a, (uchar*)kaddr(0)+offset, n);
167 		return n;
168 
169 	case Qvgactl:
170 		scr = &vgascreen[0];
171 
172 		p = malloc(READSTR);
173 		if(p == nil)
174 			error(Enomem);
175 		if(waserror()){
176 			free(p);
177 			nexterror();
178 		}
179 
180 		len = 0;
181 
182 		if(scr->dev)
183 			s = scr->dev->name;
184 		else
185 			s = "cga";
186 		len += snprint(p+len, READSTR-len, "type %s\n", s);
187 
188 		if(scr->gscreen) {
189 			len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
190 				scr->gscreen->r.max.x, scr->gscreen->r.max.y,
191 				scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
192 
193 			if(Dx(scr->gscreen->r) != Dx(physgscreenr)
194 			|| Dy(scr->gscreen->r) != Dy(physgscreenr))
195 				len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
196 					physgscreenr.max.x, physgscreenr.max.y);
197 		}
198 
199 		len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
200 			blanktime, drawidletime(), scr->isblank ? "off" : "on");
201 		len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
202 		len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
203 		len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
204 		len += snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
205 		USED(len);
206 
207 		n = readstr(offset, a, n, p);
208 		poperror();
209 		free(p);
210 
211 		return n;
212 
213 	case Qvgaovl:
214 	case Qvgaovlctl:
215 		error(Ebadusefd);
216 		break;
217 
218 	default:
219 		error(Egreg);
220 		break;
221 	}
222 
223 	return 0;
224 }
225 
226 static char Ebusy[] = "vga already configured";
227 
228 static void
vgactl(Cmdbuf * cb)229 vgactl(Cmdbuf *cb)
230 {
231 	int align, i, size, x, y, z;
232 	char *chanstr, *p;
233 	ulong chan;
234 	Cmdtab *ct;
235 	VGAscr *scr;
236 	extern VGAdev *vgadev[];
237 	extern VGAcur *vgacur[];
238 
239 	scr = &vgascreen[0];
240 	ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
241 	switch(ct->index){
242 	case CMhwgc:
243 		if(strcmp(cb->f[1], "off") == 0){
244 			lock(&cursor);
245 			if(scr->cur){
246 				if(scr->cur->disable)
247 					scr->cur->disable(scr);
248 				scr->cur = nil;
249 			}
250 			unlock(&cursor);
251 			return;
252 		}
253 		if(strcmp(cb->f[1], "soft") == 0){
254 			lock(&cursor);
255 			swcursorinit();
256 			if(scr->cur && scr->cur->disable)
257 				scr->cur->disable(scr);
258 			scr->cur = &swcursor;
259 			if(scr->cur->enable)
260 				scr->cur->enable(scr);
261 			unlock(&cursor);
262 			return;
263 		}
264 		for(i = 0; vgacur[i]; i++){
265 			if(strcmp(cb->f[1], vgacur[i]->name))
266 				continue;
267 			lock(&cursor);
268 			if(scr->cur && scr->cur->disable)
269 				scr->cur->disable(scr);
270 			scr->cur = vgacur[i];
271 			if(scr->cur->enable)
272 				scr->cur->enable(scr);
273 			unlock(&cursor);
274 			return;
275 		}
276 		break;
277 
278 	case CMtype:
279 		for(i = 0; vgadev[i]; i++){
280 			if(strcmp(cb->f[1], vgadev[i]->name))
281 				continue;
282 			if(scr->dev && scr->dev->disable)
283 				scr->dev->disable(scr);
284 			scr->dev = vgadev[i];
285 			if(scr->dev->enable)
286 				scr->dev->enable(scr);
287 			return;
288 		}
289 		break;
290 
291 	case CMtextmode:
292 		screeninit();
293 		return;
294 
295 	case CMsize:
296 		x = strtoul(cb->f[1], &p, 0);
297 		if(x == 0 || x > 10240)
298 			error(Ebadarg);
299 		if(*p)
300 			p++;
301 
302 		y = strtoul(p, &p, 0);
303 		if(y == 0 || y > 10240)
304 			error(Ebadarg);
305 		if(*p)
306 			p++;
307 
308 		z = strtoul(p, &p, 0);
309 
310 		chanstr = cb->f[2];
311 		if((chan = strtochan(chanstr)) == 0)
312 			error("bad channel");
313 
314 		if(chantodepth(chan) != z)
315 			error("depth, channel do not match");
316 
317 		cursoroff(1);
318 		deletescreenimage();
319 		if(screensize(x, y, z, chan))
320 			error(Egreg);
321 		vgascreenwin(scr);
322 		resetscreenimage();
323 		cursoron(1);
324 		return;
325 
326 	case CMactualsize:
327 		if(scr->gscreen == nil)
328 			error("set the screen size first");
329 
330 		x = strtoul(cb->f[1], &p, 0);
331 		if(x == 0 || x > 2048)
332 			error(Ebadarg);
333 		if(*p)
334 			p++;
335 
336 		y = strtoul(p, nil, 0);
337 		if(y == 0 || y > 2048)
338 			error(Ebadarg);
339 
340 		if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
341 			error("physical screen bigger than virtual");
342 
343 		physgscreenr = Rect(0,0,x,y);
344 		scr->gscreen->clipr = physgscreenr;
345 		return;
346 
347 	case CMpalettedepth:
348 		x = strtoul(cb->f[1], &p, 0);
349 		if(x != 8 && x != 6)
350 			error(Ebadarg);
351 
352 		scr->palettedepth = x;
353 		return;
354 
355 	case CMdrawinit:
356 		if(scr->gscreen == nil)
357 			error("drawinit: no gscreen");
358 		if(scr->dev && scr->dev->drawinit)
359 			scr->dev->drawinit(scr);
360 		return;
361 
362 	case CMlinear:
363 		if(cb->nf!=2 && cb->nf!=3)
364 			error(Ebadarg);
365 		size = strtoul(cb->f[1], 0, 0);
366 		if(cb->nf == 2)
367 			align = 0;
368 		else
369 			align = strtoul(cb->f[2], 0, 0);
370 		if(screenaperture(size, align) < 0)
371 			error("not enough free address space");
372 		return;
373 /*
374 	case CMmemset:
375 		memset((void*)strtoul(cb->f[1], 0, 0), atoi(cb->f[2]), atoi(cb->f[3]));
376 		return;
377 */
378 
379 	case CMblank:
380 		drawblankscreen(1);
381 		return;
382 
383 	case CMunblank:
384 		drawblankscreen(0);
385 		return;
386 
387 	case CMblanktime:
388 		blanktime = strtoul(cb->f[1], 0, 0);
389 		return;
390 
391 	case CMpanning:
392 		if(strcmp(cb->f[1], "on") == 0){
393 			if(scr == nil || scr->cur == nil)
394 				error("set screen first");
395 			if(!scr->cur->doespanning)
396 				error("panning not supported");
397 			scr->gscreen->clipr = scr->gscreen->r;
398 			panning = 1;
399 		}
400 		else if(strcmp(cb->f[1], "off") == 0){
401 			scr->gscreen->clipr = physgscreenr;
402 			panning = 0;
403 		}else
404 			break;
405 		return;
406 
407 	case CMhwaccel:
408 		if(strcmp(cb->f[1], "on") == 0)
409 			hwaccel = 1;
410 		else if(strcmp(cb->f[1], "off") == 0)
411 			hwaccel = 0;
412 		else
413 			break;
414 		return;
415 
416 	case CMhwblank:
417 		if(strcmp(cb->f[1], "on") == 0)
418 			hwblank = 1;
419 		else if(strcmp(cb->f[1], "off") == 0)
420 			hwblank = 0;
421 		else
422 			break;
423 		return;
424 	}
425 
426 	cmderror(cb, "bad VGA control message");
427 }
428 
429 char Enooverlay[] = "No overlay support";
430 
431 static long
vgawrite(Chan * c,void * a,long n,vlong off)432 vgawrite(Chan* c, void* a, long n, vlong off)
433 {
434 	ulong offset = off;
435 	Cmdbuf *cb;
436 	VGAscr *scr;
437 
438 	switch((ulong)c->qid.path){
439 
440 	case Qdir:
441 		error(Eperm);
442 
443 	case Qvgactl:
444 		if(offset || n >= READSTR)
445 			error(Ebadarg);
446 		cb = parsecmd(a, n);
447 		if(waserror()){
448 			free(cb);
449 			nexterror();
450 		}
451 		vgactl(cb);
452 		poperror();
453 		free(cb);
454 		return n;
455 
456 	case Qvgaovl:
457 		scr = &vgascreen[0];
458 		if (scr->dev == nil || scr->dev->ovlwrite == nil) {
459 			error(Enooverlay);
460 			break;
461 		}
462 		return scr->dev->ovlwrite(scr, a, n, off);
463 
464 	case Qvgaovlctl:
465 		scr = &vgascreen[0];
466 		if (scr->dev == nil || scr->dev->ovlctl == nil) {
467 			error(Enooverlay);
468 			break;
469 		}
470 		scr->dev->ovlctl(scr, c, a, n);
471 		return n;
472 
473 	default:
474 		error(Egreg);
475 		break;
476 	}
477 
478 	return 0;
479 }
480 
481 Dev vgadevtab = {
482 	'v',
483 	"vga",
484 
485 	vgareset,
486 	devinit,
487 	devshutdown,
488 	vgaattach,
489 	vgawalk,
490 	vgastat,
491 	vgaopen,
492 	devcreate,
493 	vgaclose,
494 	vgaread,
495 	devbread,
496 	vgawrite,
497 	devbwrite,
498 	devremove,
499 	devwstat,
500 };
501