xref: /plan9/sys/src/cmd/aux/vga/vesa.c (revision cb8c047aa49e908a428eac8b13623e1b242fa11e)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include </386/include/ureg.h>
5 typedef struct Ureg Ureg;
6 
7 #include "pci.h"
8 #include "vga.h"
9 
10 typedef struct Vbe Vbe;
11 typedef struct Vmode Vmode;
12 typedef struct Modelist Modelist;
13 typedef struct Edid Edid;
14 
15 enum
16 {
17 	MemSize = 1024*1024,
18 	PageSize = 4096,
19 	RealModeBuf = 0x9000,
20 };
21 
22 struct Vbe
23 {
24 	int	rmfd;	/* /dev/realmode */
25 	int	memfd;	/* /dev/realmem */
26 	uchar	*mem;	/* copy of memory; 1MB */
27 	uchar	*isvalid;	/* 1byte per 4kB in mem */
28 	uchar	*buf;
29 	uchar	*modebuf;
30 };
31 
32 struct Vmode
33 {
34 	char	name[32];
35 	char	chan[32];
36 	int	id;
37 	int	attr;	/* flags */
38 	int	bpl;
39 	int	dx, dy;
40 	int	depth;
41 	char	*model;
42 	int	r, g, b, x;
43 	int	ro, go, bo, xo;
44 	int	directcolor;	/* flags */
45 	ulong	paddr;
46 };
47 
48 struct Edid {
49 	char		mfr[4];		/* manufacturer */
50 	char		serialstr[16];	/* serial number as string (in extended data) */
51 	char		name[16];		/* monitor name as string (in extended data) */
52 	ushort	product;		/* product code, 0 = unused */
53 	ulong	serial;		/* serial number, 0 = unused */
54 	uchar	version;		/* major version number */
55 	uchar	revision;		/* minor version number */
56 	uchar	mfrweek;		/* week of manufacture, 0 = unused */
57 	int		mfryear;		/* year of manufacture, 0 = unused */
58 	uchar 	dxcm;		/* horizontal image size in cm. */
59 	uchar	dycm;		/* vertical image size in cm. */
60 	int		gamma;		/* gamma*100 */
61 	int		rrmin;		/* minimum vertical refresh rate */
62 	int		rrmax;		/* maximum vertical refresh rate */
63 	int		hrmin;		/* minimum horizontal refresh rate */
64 	int		hrmax;		/* maximum horizontal refresh rate */
65 	ulong	pclkmax;		/* maximum pixel clock */
66 	int		flags;
67 
68 	Modelist	*modelist;		/* list of supported modes */
69 };
70 
71 struct Modelist
72 {
73 	Mode;
74 	Modelist *next;
75 };
76 
77 enum {
78 	Fdigital = 1<<0,		/* is a digital display */
79 	Fdpmsstandby = 1<<1,	/* supports DPMS standby mode */
80 	Fdpmssuspend = 1<<2,	/* supports DPMS suspend mode */
81 	Fdpmsactiveoff = 1<<3,	/* supports DPMS active off mode */
82 	Fmonochrome = 1<<4,	/* is a monochrome display */
83 	Fgtf = 1<<5,		/* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
84 };
85 
86 #define WORD(p) ((p)[0] | ((p)[1]<<8))
87 #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
88 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
89 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
90 
91 static Vbe *vbe;
92 static Edid edid;
93 
94 Vbe *mkvbe(void);
95 int vbecheck(Vbe*);
96 uchar *vbemodes(Vbe*);
97 int vbemodeinfo(Vbe*, int, Vmode*);
98 int vbegetmode(Vbe*);
99 int vbesetmode(Vbe*, int);
100 void vbeprintinfo(Vbe*);
101 void vbeprintmodeinfo(Vbe*, int, char*);
102 int vbesnarf(Vbe*, Vga*);
103 void vesaddc(void);
104 int vbeddcedid(Vbe *vbe, Edid *e);
105 void printedid(Edid*);
106 
107 int
dbvesa(Vga * vga)108 dbvesa(Vga* vga)
109 {
110 	vbe = mkvbe();
111 	if(vbe == nil){
112 		fprint(2, "mkvbe: %r\n");
113 		return 0;
114 	}
115 	if(vbecheck(vbe) < 0){
116 		fprint(2, "dbvesa: %r\n");
117 		return 0;
118 	}
119 	vga->link = alloc(sizeof(Ctlr));
120 	*vga->link = vesa;
121 	vga->vesa = vga->link;
122 	vga->ctlr = vga->link;
123 
124 	vga->link->link = alloc(sizeof(Ctlr));
125 	*vga->link->link = softhwgc;
126 	vga->hwgc = vga->link->link;
127 
128 	return 1;
129 }
130 
131 Mode*
dbvesamode(char * mode)132 dbvesamode(char *mode)
133 {
134 	int i;
135 	uchar *p, *ep;
136 	Vmode vm;
137 	Mode *m;
138 
139 	if(vbe == nil)
140 		return nil;
141 
142 	p = vbemodes(vbe);
143 	if(p == nil)
144 		return nil;
145 	for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && p<ep; p+=2){
146 		if(vbemodeinfo(vbe, WORD(p), &vm) < 0)
147 			continue;
148 		if(strcmp(vm.name, mode) == 0)
149 			goto havemode;
150 	}
151 	if(1){
152 		fprint(2, "warning: scanning for unoffered vesa modes\n");
153 		for(i=0x100; i<0x200; i++){
154 			if(vbemodeinfo(vbe, i, &vm) < 0)
155 				continue;
156 			if(strcmp(vm.name, mode) == 0)
157 				goto havemode;
158 		}
159 	}
160 	werrstr("no such vesa mode");
161 	return nil;
162 
163 havemode:
164 	m = alloc(sizeof(Mode));
165 	strcpy(m->type, "vesa");
166 	strcpy(m->size, vm.name);
167 	strcpy(m->chan, vm.chan);
168 	m->frequency = 100;
169 	m->x = vm.dx;
170 	m->y = vm.dy;
171 	m->z = vm.depth;
172 	m->ht = m->x;
173 	m->shb = m->x;
174 	m->ehb = m->x;
175 	m->shs = m->x;
176 	m->ehs = m->x;
177 	m->vt = m->y;
178 	m->vrs = m->y;
179 	m->vre = m->y;
180 
181 	m->attr = alloc(sizeof(Attr));
182 	m->attr->attr = "id";
183 	m->attr->val = alloc(32);
184 	sprint(m->attr->val, "0x%x", vm.id);
185 	return m;
186 }
187 
188 static void
snarf(Vga * vga,Ctlr * ctlr)189 snarf(Vga* vga, Ctlr* ctlr)
190 {
191 	if(!vbe)
192 		vbe = mkvbe();
193 	if(vbe)
194 		vga->vesa = ctlr;
195 	vbesnarf(vbe, vga);
196 	vga->linear = 1;
197 	ctlr->flag |= Hlinear|Ulinear;
198 }
199 
200 static void
load(Vga * vga,Ctlr * ctlr)201 load(Vga* vga, Ctlr* ctlr)
202 {
203 	if(vbe == nil)
204 		error("no vesa bios\n");
205 	if(vbesetmode(vbe, atoi(dbattr(vga->mode->attr, "id"))) < 0){
206 		ctlr->flag |= Ferror;
207 		fprint(2, "vbesetmode: %r\n");
208 	}
209 }
210 
211 static void
dump(Vga *,Ctlr *)212 dump(Vga*, Ctlr*)
213 {
214 	int i;
215 	char did[0x200];
216 	uchar *p, *ep;
217 
218 	if(!vbe){
219 		Bprint(&stdout, "no vesa bios\n");
220 		return;
221 	}
222 
223 	memset(did, 0, sizeof did);
224 	vbeprintinfo(vbe);
225 	p = vbemodes(vbe);
226 	if(p){
227 		for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && p<ep; p+=2){
228 			vbeprintmodeinfo(vbe, WORD(p), "");
229 			if(WORD(p) < nelem(did))
230 				did[WORD(p)] = 1;
231 		}
232 	}
233 	for(i=0x100; i<0x1FF; i++)
234 		if(!did[i])
235 			vbeprintmodeinfo(vbe, i, " (unoffered)");
236 
237 
238 	if(vbeddcedid(vbe, &edid) < 0)
239 		fprint(2, "warning: reading edid: %r\n");
240 	else
241 		printedid(&edid);
242 }
243 
244 Ctlr vesa = {
245 	"vesa",			/* name */
246 	snarf,				/* snarf */
247 	0,			/* options */
248 	0,				/* init */
249 	load,				/* load */
250 	dump,				/* dump */
251 };
252 
253 Ctlr softhwgc = {
254 	"soft",
255 };
256 
257 /*
258  * VESA bios extension
259  */
260 
261 typedef struct Flag Flag;
262 struct Flag {
263 	int bit;
264 	char *desc;
265 };
266 
267 static Flag capabilityflag[] = {
268 	0x01, "8-bit-dac",
269 	0x02, "not-vga",
270 	0x04, "ramdac-needs-blank",
271 	0x08, "stereoscopic",
272 	0x10, "stereo-evc",
273 	0
274 };
275 
276 static Flag modeattributesflags[] = {
277 	1<<0, "supported",
278 	1<<2, "tty",
279 	1<<3, "color",
280 	1<<4, "graphics",
281 	1<<5, "not-vga",
282 	1<<6, "no-windowed-vga",
283 	1<<7, "linear",
284 	1<<8, "double-scan",
285 	1<<9, "interlace",
286 	1<<10, "triple-buffer",
287 	1<<11, "stereoscopic",
288 	1<<12, "dual-start-addr",
289 	0
290 };
291 
292 static Flag winattributesflags[] = {
293 	1<<0, "relocatable",
294 	1<<1, "readable",
295 	1<<2, "writeable",
296 	0
297 };
298 
299 static Flag directcolorflags[] = {
300 	1<<0, "programmable-color-ramp",
301 	1<<1, "x-usable",
302 	0
303 };
304 
305 static char *modelstr[] = {
306 	"text", "cga", "hercules", "planar", "packed", "non-chain4", "direct", "YUV"
307 };
308 
309 static void
printflags(Flag * f,int b)310 printflags(Flag *f, int b)
311 {
312 	int i;
313 
314 	for(i=0; f[i].bit; i++)
315 		if(f[i].bit & b)
316 			Bprint(&stdout, " %s", f[i].desc);
317 	Bprint(&stdout, "\n");
318 }
319 
320 Vbe*
mkvbe(void)321 mkvbe(void)
322 {
323 	Vbe *vbe;
324 
325 	vbe = alloc(sizeof(Vbe));
326 	if((vbe->rmfd = open("/dev/realmode", ORDWR)) < 0)
327 		return nil;
328 	if((vbe->memfd = open("/dev/realmodemem", ORDWR)) < 0)
329 		return nil;
330 	vbe->mem = alloc(MemSize);
331 	vbe->isvalid = alloc(MemSize/PageSize);
332 	vbe->buf = alloc(PageSize);
333 	vbe->modebuf = alloc(PageSize);
334 	return vbe;
335 }
336 
337 static void
loadpage(Vbe * vbe,int p)338 loadpage(Vbe *vbe, int p)
339 {
340 	if(p >= MemSize/PageSize || vbe->isvalid[p])
341 		return;
342 	if(pread(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize)
343 		error("read /dev/realmodemem: %r\n");
344 	vbe->isvalid[p] = 1;
345 }
346 
347 static void*
unfarptr(Vbe * vbe,uchar * p)348 unfarptr(Vbe *vbe, uchar *p)
349 {
350 	int seg, off;
351 
352 	seg = WORD(p+2);
353 	off = WORD(p);
354 	if(seg==0 && off==0)
355 		return nil;
356 	off += seg<<4;
357 	if(off >= MemSize)
358 		return nil;
359 	loadpage(vbe, off/PageSize);
360 	loadpage(vbe, off/PageSize+1);	/* just in case */
361 	return vbe->mem+off;
362 }
363 
364 uchar*
vbesetup(Vbe * vbe,Ureg * u,int ax)365 vbesetup(Vbe *vbe, Ureg *u, int ax)
366 {
367 	memset(vbe->buf, 0, PageSize);
368 	memset(u, 0, sizeof *u);
369 	u->ax = ax;
370 	u->es = (RealModeBuf>>4)&0xF000;
371 	u->di = RealModeBuf&0xFFFF;
372 	return vbe->buf;
373 }
374 
375 int
vbecall(Vbe * vbe,Ureg * u)376 vbecall(Vbe *vbe, Ureg *u)
377 {
378 	u->trap = 0x10;
379 	if(pwrite(vbe->memfd, vbe->buf, PageSize, RealModeBuf) != PageSize)
380 		error("write /dev/realmodemem: %r\n");
381 	if(pwrite(vbe->rmfd, u, sizeof *u, 0) != sizeof *u)
382 		error("write /dev/realmode: %r\n");
383 	if(pread(vbe->rmfd, u, sizeof *u, 0) != sizeof *u)
384 		error("read /dev/realmode: %r\n");
385 	if(pread(vbe->memfd, vbe->buf, PageSize, RealModeBuf) != PageSize)
386 		error("read /dev/realmodemem: %r\n");
387 	if((u->ax&0xFFFF) != 0x004F){
388 		werrstr("VBE error %#.4lux", u->ax&0xFFFF);
389 		return -1;
390 	}
391 	memset(vbe->isvalid, 0, MemSize/PageSize);
392 	return 0;
393 }
394 
395 int
vbecheck(Vbe * vbe)396 vbecheck(Vbe *vbe)
397 {
398 	uchar *p;
399 	Ureg u;
400 
401 	p = vbesetup(vbe, &u, 0x4F00);
402 	strcpy((char*)p, "VBE2");
403 	if(vbecall(vbe, &u) < 0)
404 		return -1;
405 	if(memcmp(p, "VESA", 4) != 0 || p[5] < 2){
406 		werrstr("invalid vesa signature %.4H %.4H\n", p, p+4);
407 		return -1;
408 	}
409 	return 0;
410 }
411 
412 int
vbesnarf(Vbe * vbe,Vga * vga)413 vbesnarf(Vbe *vbe, Vga *vga)
414 {
415 	uchar *p;
416 	Ureg u;
417 
418 	p = vbesetup(vbe, &u, 0x4F00);
419 	strcpy((char*)p, "VBE2");
420 	if(vbecall(vbe, &u) < 0)
421 		return -1;
422 	if(memcmp(p, "VESA", 4) != 0 || p[5] < 2)
423 		return -1;
424 	vga->apz = WORD(p+18)*0x10000UL;
425 	return 0;
426 }
427 
428 void
vbeprintinfo(Vbe * vbe)429 vbeprintinfo(Vbe *vbe)
430 {
431 	uchar *p;
432 	Ureg u;
433 
434 	p = vbesetup(vbe, &u, 0x4F00);
435 	strcpy((char*)p, "VBE2");
436 	if(vbecall(vbe, &u) < 0)
437 		return;
438 
439 	printitem("vesa", "sig");
440 	Bprint(&stdout, "%.4s %d.%d\n", (char*)p, p[5], p[4]);
441 	if(p[5] < 2)
442 		return;
443 
444 	printitem("vesa", "oem");
445 	Bprint(&stdout, "%s %d.%d\n", unfarptr(vbe, p+6), p[21], p[20]);
446 	printitem("vesa", "vendor");
447 	Bprint(&stdout, "%s\n", unfarptr(vbe, p+22));
448 	printitem("vesa", "product");
449 	Bprint(&stdout, "%s\n", unfarptr(vbe, p+26));
450 	printitem("vesa", "rev");
451 	Bprint(&stdout, "%s\n", unfarptr(vbe, p+30));
452 
453 	printitem("vesa", "cap");
454 	printflags(capabilityflag, p[10]);
455 
456 	printitem("vesa", "mem");
457 	Bprint(&stdout, "%lud\n", WORD(p+18)*0x10000UL);
458 }
459 
460 uchar*
vbemodes(Vbe * vbe)461 vbemodes(Vbe *vbe)
462 {
463 	uchar *p;
464 	Ureg u;
465 
466 	p = vbesetup(vbe, &u, 0x4F00);
467 	strcpy((char*)p, "VBE2");
468 	if(vbecall(vbe, &u) < 0)
469 		return nil;
470 	memmove(vbe->modebuf, unfarptr(vbe, p+14), 1024);
471 	return vbe->modebuf;
472 }
473 
474 int
vbemodeinfo(Vbe * vbe,int id,Vmode * m)475 vbemodeinfo(Vbe *vbe, int id, Vmode *m)
476 {
477 	int o;
478 	ulong d, c, x;
479 	uchar *p;
480 	char tmp[sizeof m->chan];
481 	Ureg u;
482 
483 	p = vbesetup(vbe, &u, 0x4F01);
484 	u.cx = id;
485 	if(vbecall(vbe, &u) < 0)
486 		return -1;
487 
488 	m->id = id;
489 	m->attr = WORD(p);
490 	m->bpl = WORD(p+16);
491 	m->dx = WORD(p+18);
492 	m->dy = WORD(p+20);
493 	m->depth = p[25];
494 	m->model = p[27] < nelem(modelstr) ? modelstr[p[27]] : "unknown";
495 	m->r = p[31];
496 	m->g = p[33];
497 	m->b = p[35];
498 	m->x = p[37];
499 	m->ro = p[32];
500 	m->go = p[34];
501 	m->bo = p[36];
502 	m->xo = p[38];
503 	m->directcolor = p[39];
504 	m->paddr = LONG(p+40);
505 	snprint(m->name, sizeof m->name, "%dx%dx%d",
506 		m->dx, m->dy, m->depth);
507 	if(m->depth <= 8) {
508 		snprint(m->chan, sizeof m->chan, "m%d", m->depth);
509 		return 0;
510 	}
511 
512 	m->xo = m->x = 0;
513 	d = 1 << (m->depth - 1);
514 	d |= d - 1;
515 	c  = ((1<<m->r)-1) << m->ro;
516 	c |= ((1<<m->g)-1) << m->go;
517 	c |= ((1<<m->b)-1) << m->bo;
518 	x = d ^ c;
519 	if(x != 0){
520 		for(; (x & 1) == 0; x >>= 1)
521 			m->xo++;
522 		for(; x & 1; x >>= 1)
523 			m->x++;
524 	}
525 
526 	m->chan[0] = o = 0;
527 	while(o < m->depth){
528 		if(m->r && m->ro == o){
529 			snprint(tmp, sizeof tmp, "r%d%s", m->r, m->chan);
530 			o += m->r;
531 		}else if(m->g && m->go == o){
532 			snprint(tmp, sizeof tmp, "g%d%s", m->g, m->chan);
533 			o += m->g;
534 		}else if(m->b && m->bo == o){
535 			snprint(tmp, sizeof tmp, "b%d%s", m->b, m->chan);
536 			o += m->b;
537 		}else if(m->x && m->xo == o){
538 			snprint(tmp, sizeof tmp, "x%d%s", m->x, m->chan);
539 			o += m->x;
540 		}else
541 			break;
542 		strncpy(m->chan, tmp, sizeof m->chan);
543 	}
544 	return 0;
545 }
546 
547 void
vbeprintmodeinfo(Vbe * vbe,int id,char * suffix)548 vbeprintmodeinfo(Vbe *vbe, int id, char *suffix)
549 {
550 	Vmode m;
551 
552 	if(vbemodeinfo(vbe, id, &m) < 0){
553 	//	Bprint(&stdout, "vesa: cannot get mode 0x%ux: %r\n", id);
554 		return;
555 	}
556 	printitem("vesa", "mode");
557 	Bprint(&stdout, "0x%ux %s %s %s%s\n",
558 		m.id, m.name, m.chan, m.model, suffix);
559 }
560 
561 int
vbegetmode(Vbe * vbe)562 vbegetmode(Vbe *vbe)
563 {
564 	Ureg u;
565 
566 	vbesetup(vbe, &u, 0x4F03);
567 	if(vbecall(vbe, &u) < 0)
568 		return 0;
569 	return u.bx;
570 }
571 
572 int
vbesetmode(Vbe * vbe,int id)573 vbesetmode(Vbe *vbe, int id)
574 {
575 	uchar *p;
576 	Ureg u;
577 
578 	p = vbesetup(vbe, &u, 0x4F02);
579 	if(id != 3)
580 		id |= 3<<14;	/* graphics: use linear, do not clear */
581 	u.bx = id;
582 	USED(p);
583 	/*
584 	 * can set mode specifics (ht hss hse vt vss vse 0 clockhz refreshhz):
585 	 *
586 		u.bx |= 1<<11;
587 		n = atoi(argv[2]); PWORD(p, n); p+=2;
588 		n = atoi(argv[3]); PWORD(p, n); p+=2;
589 		n = atoi(argv[4]); PWORD(p, n); p+=2;
590 		n = atoi(argv[5]); PWORD(p, n); p+=2;
591 		n = atoi(argv[6]); PWORD(p, n); p+=2;
592 		n = atoi(argv[7]); PWORD(p, n); p+=2;
593 		*p++ = atoi(argv[8]);
594 		n = atoi(argv[9]); PLONG(p, n); p += 4;
595 		n = atoi(argv[10]); PWORD(p, n); p += 2;
596 		if(p != vbe.buf+19){
597 			fprint(2, "prog error\n");
598 			return;
599 		}
600 	 *
601 	 */
602 	return vbecall(vbe, &u);
603 }
604 
605 void
vesatextmode(void)606 vesatextmode(void)
607 {
608 	if(vbe == nil){
609 		vbe = mkvbe();
610 		if(!vbe)
611 			error("mkvbe: %r\n");
612 	}
613 	if(vbecheck(vbe) < 0)
614 		error("vbecheck: %r\n");
615 	if(vbesetmode(vbe, 3) < 0)
616 		error("vbesetmode: %r\n");
617 }
618 
619 static Flag edidflags[] = {
620 	Fdigital, "digital",
621 	Fdpmsstandby, "standby",
622 	Fdpmssuspend, "suspend",
623 	Fdpmsactiveoff, "activeoff",
624 	Fmonochrome, "monochrome",
625 	Fgtf, "gtf",
626 	0
627 };
628 
629 int parseedid128(Edid *e, void *v);
630 
631 int
vbeddcedid(Vbe * vbe,Edid * e)632 vbeddcedid(Vbe *vbe, Edid *e)
633 {
634 	uchar *p;
635 	Ureg u;
636 
637 	p = vbesetup(vbe, &u, 0x4F15);
638 	u.bx = 0x0001;
639 	if(vbecall(vbe, &u) < 0)
640 		return -1;
641 	if(parseedid128(e, p) < 0){
642 		werrstr("parseedid128: %r");
643 		return -1;
644 	}
645 	return 0;
646 }
647 
648 void
printedid(Edid * e)649 printedid(Edid *e)
650 {
651 	Modelist *l;
652 
653 	printitem("edid", "mfr");
654 	Bprint(&stdout, "%s\n", e->mfr);
655 	printitem("edid", "serialstr");
656 	Bprint(&stdout, "%s\n", e->serialstr);
657 	printitem("edid", "name");
658 	Bprint(&stdout, "%s\n", e->name);
659 	printitem("edid", "product");
660 	Bprint(&stdout, "%d\n", e->product);
661 	printitem("edid", "serial");
662 	Bprint(&stdout, "%lud\n", e->serial);
663 	printitem("edid", "version");
664 	Bprint(&stdout, "%d.%d\n", e->version, e->revision);
665 	printitem("edid", "mfrdate");
666 	Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
667 	printitem("edid", "size (cm)");
668 	Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
669 	printitem("edid", "gamma");
670 	Bprint(&stdout, "%.2f\n", e->gamma/100.);
671 	printitem("edid", "vert (Hz)");
672 	Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
673 	printitem("edid", "horz (Hz)");
674 	Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
675 	printitem("edid", "pclkmax");
676 	Bprint(&stdout, "%lud\n", e->pclkmax);
677 	printitem("edid", "flags");
678 	printflags(edidflags, e->flags);
679 
680 	for(l=e->modelist; l; l=l->next){
681 		printitem("edid", l->name);
682 		Bprint(&stdout, "\n\t\tclock=%g\n\t\tshb=%d ehb=%d ht=%d\n\t\tvrs=%d vre=%d vt=%d\n\t\thsync=%c vsync=%c %s\n",
683 			l->frequency/1.e6, l->shb, l->ehb, l->ht, l->vrs, l->vre, l->vt, l->hsync?l->hsync:'?', l->vsync?l->vsync:'?', l->interlace?"interlace=v" : "");
684 	}
685 }
686 
687 Modelist*
addmode(Modelist * l,Mode m)688 addmode(Modelist *l, Mode m)
689 {
690 	int rr;
691 	Modelist **lp;
692 
693 //m.z = 8; // BUG
694 	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
695 	snprint(m.name, sizeof m.name, "%dx%dx%d@%dHz", m.x, m.y, m.z, rr);
696 
697 	if(m.shs == 0)
698 		m.shs = m.shb;
699 	if(m.ehs == 0)
700 		m.ehs = m.ehb;
701 	if(m.vbs == 0)
702 		m.vbs = m.vrs;
703 	if(m.vbe == 0)
704 		m.vbe = m.vbs+1;
705 
706 	for(lp=&l; *lp; lp=&(*lp)->next){
707 		if(strcmp((*lp)->name, m.name) == 0){
708 			(*lp)->Mode = m;
709 			return l;
710 		}
711 	}
712 
713 	*lp = alloc(sizeof(**lp));
714 	(*lp)->Mode = m;
715 	return l;
716 }
717 
718 /*
719  * Parse VESA EDID information.  Based on the VESA
720  * Extended Display Identification Data standard, Version 3,
721  * November 13, 1997.  See /public/doc/vesa/edidv3.pdf.
722  *
723  * This only handles 128-byte EDID blocks.  Until I find
724  * a monitor that produces 256-byte blocks, I'm not going
725  * to try to decode them.
726  */
727 
728 /*
729  * Established timings block.  There is a bitmap
730  * that says whether each mode is supported.  Most
731  * of these have VESA definitions.  Those that don't are marked
732  * as such, and we ignore them (the lookup fails).
733  */
734 static char *estabtime[] = {
735 	"720x400@70Hz",	/* non-VESA: IBM, VGA */
736 	"720x400@88Hz",	/* non-VESA: IBM, XGA2 */
737 	"640x480@60Hz",
738 	"640x480@67Hz",	/* non-VESA: Apple, Mac II */
739 	"640x480@72Hz",
740 	"640x480@75Hz",
741 	"800x600@56Hz",
742 	"800x600@60Hz",
743 
744 	"800x600@72Hz",
745 	"800x600@75Hz",
746 	"832x624@75Hz",	/* non-VESA: Apple, Mac II */
747 	"1024x768i@87Hz",	/* non-VESA: IBM */
748 	"1024x768@60Hz",
749 	"1024x768@70Hz",
750 	"1024x768@75Hz",
751 	"1280x1024@75Hz",
752 
753 	"1152x870@75Hz",	/* non-VESA: Apple, Mac II */
754 };
755 
756 /*
757  * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
758  */
759 static int
decodedtb(Mode * m,uchar * p)760 decodedtb(Mode *m, uchar *p)
761 {
762 	int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
763 	/* int dxmm, dymm, hbord, vbord; */
764 
765 	memset(m, 0, sizeof *m);
766 
767 	m->frequency = ((p[1]<<8) | p[0]) * 10000;
768 
769 	ha = ((p[4] & 0xF0)<<4) | p[2];		/* horizontal active */
770 	hb = ((p[4] & 0x0F)<<8) | p[3];		/* horizontal blanking */
771 	va = ((p[7] & 0xF0)<<4) | p[5];		/* vertical active */
772 	vb = ((p[7] & 0x0F)<<8) | p[6];		/* vertical blanking */
773 	hso = ((p[11] & 0xC0)<<2) | p[8];	/* horizontal sync offset */
774 	hspw = ((p[11] & 0x30)<<4) | p[9];	/* horizontal sync pulse width */
775 	vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4);	/* vertical sync offset */
776 	vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F);		/* vertical sync pulse width */
777 
778 	/* dxmm = (p[14] & 0xF0)<<4) | p[12]; 	/* horizontal image size (mm) */
779 	/* dymm = (p[14] & 0x0F)<<8) | p[13];	/* vertical image size (mm) */
780 	/* hbord = p[15];		/* horizontal border (pixels) */
781 	/* vbord = p[16];		/* vertical border (pixels) */
782 
783 	m->x = ha;
784 	m->y = va;
785 
786 	m->ht = ha+hb;
787 	m->shs = ha;
788 	m->shb = ha+hso;
789 	m->ehb = ha+hso+hspw;
790 	m->ehs = ha+hb;
791 
792 	m->vt = va+vb;
793 	m->vbs = va;
794 	m->vrs = va+vso;
795 	m->vre = va+vso+vspw;
796 	m->vbe = va+vb;
797 
798 	if(p[17] & 0x80)	/* interlaced */
799 		m->interlace = 'v';
800 
801 	if(p[17] & 0x60)	/* some form of stereo monitor mode; no support */
802 		return -1;
803 
804 	/*
805 	 * Sync signal description.  I have no idea how to properly handle the
806 	 * first three cases, which I think are aimed at things other than
807 	 * canonical SVGA monitors.
808 	 */
809 	switch((p[17] & 0x18)>>3) {
810 	case 0:	/* analog composite sync signal*/
811 	case 1:	/* bipolar analog composite sync signal */
812 		/* p[17] & 0x04 means serration: hsync during vsync */
813 		/* p[17] & 0x02 means sync pulse appears on RGB not just G */
814 		break;
815 
816 	case 2:	/* digital composite sync signal */
817 		/* p[17] & 0x04 means serration: hsync during vsync */
818 		/* p[17] & 0x02 means hsync positive outside vsync */
819 		break;
820 
821 	case 3:	/* digital separate sync signal; the norm */
822 		m->vsync = (p[17] & 0x04) ? '+' : '-';
823 		m->hsync = (p[17] & 0x02) ? '+' : '-';
824 		break;
825 	}
826 	/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
827 
828 	rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
829 
830 	snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
831 
832 	return 0;
833 }
834 
835 extern Mode *vesamodes[];
836 
837 int
vesalookup(Mode * m,char * name)838 vesalookup(Mode *m, char *name)
839 {
840 	Mode **p;
841 
842 	for(p=vesamodes; *p; p++)
843 		if(strcmp((*p)->name, name) == 0) {
844 			*m = **p;
845 			return 0;
846 		}
847 
848 	return -1;
849 }
850 
851 static int
decodesti(Mode * m,uchar * p)852 decodesti(Mode *m, uchar *p)
853 {
854 	int x, y, rr;
855 	char str[20];
856 
857 	x = (p[0]+31)*8;
858 	switch((p[1]>>6) & 3){
859 	default:
860 	case 0:
861 		y = x;
862 		break;
863 	case 1:
864 		y = (x*4)/3;
865 		break;
866 	case 2:
867 		y = (x*5)/4;
868 		break;
869 	case 3:
870 		y = (x*16)/9;
871 		break;
872 	}
873 	rr = (p[1] & 0x1F) + 60;
874 
875 	sprint(str, "%dx%d@%dHz", x, y, rr);
876 	return vesalookup(m, str);
877 }
878 
879 int
parseedid128(Edid * e,void * v)880 parseedid128(Edid *e, void *v)
881 {
882 	static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
883 	uchar *p, *q, sum;
884 	int dpms, estab, i, m, vid;
885 	Mode mode;
886 
887 	memset(e, 0, sizeof *e);
888 
889 	p = (uchar*)v;
890 	if(memcmp(p, magic, 8) != 0) {
891 		werrstr("bad edid header");
892 		return -1;
893 	}
894 
895 	sum = 0;
896 	for(i=0; i<128; i++)
897 		sum += p[i];
898 	if(sum != 0) {
899 		werrstr("bad edid checksum");
900 		return -1;
901 	}
902 	p += 8;
903 
904 	assert(p == (uchar*)v+8);	/* assertion offsets from pp. 12-13 of the standard */
905 	/*
906 	 * Manufacturer name is three 5-bit ascii letters, packed
907 	 * into a big endian [sic] short in big endian order.  The high bit is unused.
908 	 */
909 	i = (p[0]<<8) | p[1];
910 	p += 2;
911 	e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
912 	e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
913 	e->mfr[2] = 'A'-1 + (i & 0x1F);
914 	e->mfr[3] = '\0';
915 
916 	/*
917 	 * Product code is a little endian short.
918 	 */
919 	e->product = (p[1]<<8) | p[0];
920 	p += 2;
921 
922 	/*
923 	 * Serial number is a little endian long, 0x01010101 = unused.
924 	 */
925 	e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
926 	p += 4;
927 	if(e->serial == 0x01010101)
928 		e->serial = 0;
929 
930 	e->mfrweek = *p++;
931 	e->mfryear = 1990 + *p++;
932 
933 	assert(p == (uchar*)v+8+10);
934 	/*
935 	 * Structure version is next two bytes: major.minor.
936 	 */
937 	e->version = *p++;
938 	e->revision = *p++;
939 
940 	assert(p == (uchar*)v+8+10+2);
941 	/*
942 	 * Basic display parameters / features.
943 	 */
944 	/*
945 	 * Video input definition byte: 0x80 tells whether it is
946 	 * an analog or digital screen; we ignore the other bits.
947 	 * See p. 15 of the standard.
948 	 */
949 	vid = *p++;
950 	if(vid & 0x80)
951 		e->flags |= Fdigital;
952 
953 	e->dxcm = *p++;
954 	e->dycm = *p++;
955 	e->gamma = 100 + *p++;
956 	dpms = *p++;
957 	if(dpms & 0x80)
958 		e->flags |= Fdpmsstandby;
959 	if(dpms & 0x40)
960 		e->flags |= Fdpmssuspend;
961 	if(dpms & 0x20)
962 		e->flags |= Fdpmsactiveoff;
963 	if((dpms & 0x18) == 0x00)
964 		e->flags |= Fmonochrome;
965 	if(dpms & 0x01)
966 		e->flags |= Fgtf;
967 
968 	assert(p == (uchar*)v+8+10+2+5);
969 	/*
970 	 * Color characteristics currently ignored.
971 	 */
972 	p += 10;
973 
974 	assert(p == (uchar*)v+8+10+2+5+10);
975 	/*
976 	 * Established timings: a bitmask of 19 preset timings.
977 	 */
978 	estab = (p[0]<<16) | (p[1]<<8) | p[2];
979 	p += 3;
980 
981 	for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
982 		if(estab & m)
983 			if(vesalookup(&mode, estabtime[i]) == 0)
984 				e->modelist = addmode(e->modelist,  mode);
985 
986 	assert(p == (uchar*)v+8+10+2+5+10+3);
987 	/*
988 	 * Standard Timing Identifications: eight 2-byte selectors
989 	 * of more standard timings.
990 	 */
991 	for(i=0; i<8; i++, p+=2)
992 		if(decodesti(&mode, p+2*i) == 0)
993 			e->modelist = addmode(e->modelist, mode);
994 
995 	assert(p == (uchar*)v+8+10+2+5+10+3+16);
996 	/*
997 	 * Detailed Timings
998 	 */
999 fprint(2, "dt\n");
1000 	for(i=0; i<4; i++, p+=18) {
1001 fprint(2, "%.8H\n", p);
1002 		if(p[0] || p[1]) {	/* detailed timing block: p[0] or p[1] != 0 */
1003 			if(decodedtb(&mode, p) == 0)
1004 				e->modelist = addmode(e->modelist, mode);
1005 		} else if(p[2]==0) {	/* monitor descriptor block */
1006 			switch(p[3]) {
1007 			case 0xFF:	/* monitor serial number (13-byte ascii, 0A terminated) */
1008 				if(q = memchr(p+5, 0x0A, 13))
1009 					*q = '\0';
1010 				memset(e->serialstr, 0, sizeof(e->serialstr));
1011 				strncpy(e->serialstr, (char*)p+5, 13);
1012 				break;
1013 			case 0xFE:	/* ascii string (13-byte ascii, 0A terminated) */
1014 				break;
1015 			case 0xFD:	/* monitor range limits */
1016 				print("fd %.18H\n", p);
1017 				e->rrmin = p[5];
1018 				e->rrmax = p[6];
1019 				e->hrmin = p[7]*1000;
1020 				e->hrmax = p[8]*1000;
1021 				if(p[9] != 0xFF)
1022 					e->pclkmax = p[9]*10*1000000;
1023 				break;
1024 			case 0xFC:	/* monitor name (13-byte ascii, 0A terminated) */
1025 				if(q = memchr(p+5, 0x0A, 13))
1026 					*q = '\0';
1027 				memset(e->name, 0, sizeof(e->name));
1028 				strncpy(e->name, (char*)p+5, 13);
1029 				break;
1030 			case 0xFB:	/* extra color point data */
1031 				break;
1032 			case 0xFA:	/* extra standard timing identifications */
1033 				for(i=0; i<6; i++)
1034 					if(decodesti(&mode, p+5+2*i) == 0)
1035 						e->modelist = addmode(e->modelist, mode);
1036 				break;
1037 			}
1038 		}
1039 	}
1040 
1041 	assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
1042 	return 0;
1043 }
1044