10 addmode(Modelist *l, Mode m)
15 rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
16 snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
18 for(ll = l; ll != nil; ll = ll->next){
19 if(strcmp(ll->name, m.name) == 0){
25 ll = alloc(sizeof(Modelist));
32 * Parse VESA EDID information. Based on the VESA
33 * Extended Display Identification Data standard, Version 3,
34 * November 13, 1997. See /public/doc/vesa/edidv3.pdf.
36 * This only handles 128-byte EDID blocks. Until I find
37 * a monitor that produces 256-byte blocks, I'm not going
38 * to try to decode them.
42 * Established timings block. There is a bitmap
43 * that says whether each mode is supported. Most
44 * of these have VESA definitions. Those that don't are marked
45 * as such, and we ignore them (the lookup fails).
47 static char *estabtime[] = {
48 "720x400@70Hz", /* non-VESA: IBM, VGA */
49 "720x400@88Hz", /* non-VESA: IBM, XGA2 */
51 "640x480@67Hz", /* non-VESA: Apple, Mac II */
59 "832x624@75Hz", /* non-VESA: Apple, Mac II */
60 "1024x768i@87Hz", /* non-VESA: IBM */
66 "1152x870@75Hz", /* non-VESA: Apple, Mac II */
70 * Decode the EDID detailed timing block. See pp. 20-21 of the standard.
73 decodedtb(Mode *m, uchar *p)
75 int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
76 /* int hbord, vbord, dxmm, dymm, hbord, vbord; */
78 memset(m, 0, sizeof *m);
80 m->frequency = ((p[1]<<8) | p[0]) * 10000;
82 ha = ((p[4] & 0xF0)<<4) | p[2]; /* horizontal active */
83 hb = ((p[4] & 0x0F)<<8) | p[3]; /* horizontal blanking */
84 va = ((p[7] & 0xF0)<<4) | p[5]; /* vertical active */
85 vb = ((p[7] & 0x0F)<<8) | p[6]; /* vertical blanking */
86 hso = ((p[11] & 0xC0)<<2) | p[8]; /* horizontal sync offset */
87 hspw = ((p[11] & 0x30)<<4) | p[9]; /* horizontal sync pulse width */
88 vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4); /* vertical sync offset */
89 vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F); /* vertical sync pulse width */
91 /* dxmm = (p[14] & 0xF0)<<4) | p[12]; /* horizontal image size (mm) */
92 /* dymm = (p[14] & 0x0F)<<8) | p[13]; /* vertical image size (mm) */
93 /* hbord = p[15]; /* horizontal border (pixels) */
94 /* vbord = p[16]; /* vertical border (pixels) */
101 m->ehb = ha+hso+hspw;
105 m->vre = va+vso+vspw;
107 if(p[17] & 0x80) /* interlaced */
110 if(p[17] & 0x60) /* some form of stereo monitor mode; no support */
114 * Sync signal description. I have no idea how to properly handle the
115 * first three cases, which I think are aimed at things other than
116 * canonical SVGA monitors.
118 switch((p[17] & 0x18)>>3) {
119 case 0: /* analog composite sync signal*/
120 case 1: /* bipolar analog composite sync signal */
121 /* p[17] & 0x04 means serration: hsync during vsync */
122 /* p[17] & 0x02 means sync pulse appears on RGB not just G */
125 case 2: /* digital composite sync signal */
126 /* p[17] & 0x04 means serration: hsync during vsync */
127 /* p[17] & 0x02 means hsync positive outside vsync */
130 case 3: /* digital separate sync signal; the norm */
131 m->vsync = (p[17] & 0x04) ? '+' : '-';
132 m->hsync = (p[17] & 0x02) ? '+' : '-';
135 /* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
137 rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
139 snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
145 vesalookup(Mode *m, char *name)
149 for(p=vesamodes; *p; p++)
150 if(strcmp((*p)->name, name) == 0) {
158 decodesti(Mode *m, uchar *p)
164 switch((p[1]>>6) & 3){
179 rr = (p[1] & 0x1F) + 60;
181 sprint(str, "%dx%d@%dHz", x, y, rr);
182 return vesalookup(m, str);
186 parseedid128(void *v)
188 static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
190 int dpms, estab, i, m, vid;
194 e = alloc(sizeof(Edid));
197 if(memcmp(p, magic, 8) != 0) {
199 werrstr("bad edid header");
208 werrstr("bad edid checksum");
213 assert(p == (uchar*)v+8); /* assertion offsets from pp. 12-13 of the standard */
215 * Manufacturer name is three 5-bit ascii letters, packed
216 * into a big endian [sic] short in big endian order. The high bit is unused.
218 i = (p[0]<<8) | p[1];
220 e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
221 e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
222 e->mfr[2] = 'A'-1 + (i & 0x1F);
226 * Product code is a little endian short.
228 e->product = (p[1]<<8) | p[0];
232 * Serial number is a little endian long, 0x01010101 = unused.
234 e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
236 if(e->serial == 0x01010101)
240 e->mfryear = 1990 + *p++;
242 assert(p == (uchar*)v+8+10);
244 * Structure version is next two bytes: major.minor.
249 assert(p == (uchar*)v+8+10+2);
251 * Basic display parameters / features.
254 * Video input definition byte: 0x80 tells whether it is
255 * an analog or digital screen; we ignore the other bits.
256 * See p. 15 of the standard.
260 e->flags |= Fdigital;
264 e->gamma = 100 + *p++;
267 e->flags |= Fdpmsstandby;
269 e->flags |= Fdpmssuspend;
271 e->flags |= Fdpmsactiveoff;
272 if((dpms & 0x18) == 0x00)
273 e->flags |= Fmonochrome;
277 assert(p == (uchar*)v+8+10+2+5);
279 * Color characteristics currently ignored.
283 assert(p == (uchar*)v+8+10+2+5+10);
285 * Established timings: a bitmask of 19 preset timings.
287 estab = (p[0]<<16) | (p[1]<<8) | p[2];
290 for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
292 if(vesalookup(&mode, estabtime[i]) == 0)
293 e->modelist = addmode(e->modelist, mode);
295 assert(p == (uchar*)v+8+10+2+5+10+3);
297 * Standard Timing Identifications: eight 2-byte selectors
298 * of more standard timings.
301 for(i=0; i<8; i++, p+=2)
302 if(decodesti(&mode, p+2*i) == 0)
303 e->modelist = addmode(e->modelist, mode);
305 assert(p == (uchar*)v+8+10+2+5+10+3+16);
309 for(i=0; i<4; i++, p+=18) {
310 if(p[0] || p[1]) { /* detailed timing block: p[0] or p[1] != 0 */
311 if(decodedtb(&mode, p) == 0)
312 e->modelist = addmode(e->modelist, mode);
313 } else if(p[2]==0) { /* monitor descriptor block */
315 case 0xFF: /* monitor serial number (13-byte ascii, 0A terminated) */
316 if(q = memchr(p+5, 0x0A, 13))
318 memset(e->serialstr, 0, sizeof(e->serialstr));
319 strncpy(e->serialstr, (char*)p+5, 13);
321 case 0xFE: /* ascii string (13-byte ascii, 0A terminated) */
323 case 0xFD: /* monitor range limits */
326 e->hrmin = p[7]*1000;
327 e->hrmax = p[8]*1000;
329 e->pclkmax = p[9]*10*1000000;
331 case 0xFC: /* monitor name (13-byte ascii, 0A terminated) */
332 if(q = memchr(p+5, 0x0A, 13))
334 memset(e->name, 0, sizeof(e->name));
335 strncpy(e->name, (char*)p+5, 13);
337 case 0xFB: /* extra color point data */
339 case 0xFA: /* extra standard timing identifications */
341 if(decodesti(&mode, p+5+2*i) == 0)
342 e->modelist = addmode(e->modelist, mode);
348 assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
354 Fdpmsstandby, "standby",
355 Fdpmssuspend, "suspend",
356 Fdpmsactiveoff, "activeoff",
357 Fmonochrome, "monochrome",
363 printflags(Flag *f, int b)
367 for(i=0; f[i].bit; i++)
369 Bprint(&stdout, " %s", f[i].desc);
370 Bprint(&stdout, "\n");
378 printitem("edid", "mfr");
379 Bprint(&stdout, "%s\n", e->mfr);
380 printitem("edid", "serialstr");
381 Bprint(&stdout, "%s\n", e->serialstr);
382 printitem("edid", "name");
383 Bprint(&stdout, "%s\n", e->name);
384 printitem("edid", "product");
385 Bprint(&stdout, "%d\n", e->product);
386 printitem("edid", "serial");
387 Bprint(&stdout, "%lud\n", e->serial);
388 printitem("edid", "version");
389 Bprint(&stdout, "%d.%d\n", e->version, e->revision);
390 printitem("edid", "mfrdate");
391 Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
392 printitem("edid", "size (cm)");
393 Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
394 printitem("edid", "gamma");
395 Bprint(&stdout, "%.2f\n", e->gamma/100.);
396 printitem("edid", "vert (Hz)");
397 Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
398 printitem("edid", "horz (Hz)");
399 Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
400 printitem("edid", "pclkmax");
401 Bprint(&stdout, "%lud\n", e->pclkmax);
402 printitem("edid", "flags");
403 printflags(edidflags, e->flags);
405 for(l=e->modelist; l; l=l->next){
406 printitem("edid", l->name);
407 Bprint(&stdout, "\n\t\tclock=%g\n", l->frequency/1.e6);
408 Bprint(&stdout, "\t\tshb=%d ehb=%d ht=%d\n", l->shb, l->ehb, l->ht);
409 Bprint(&stdout, "\t\tvrs=%d vre=%d vt=%d\n", l->vrs, l->vre, l->vt);
410 Bprint(&stdout, "\t\thsync=%c vsync=%c %s\n",
411 l->hsync?l->hsync:'?',
412 l->vsync?l->vsync:'?',
413 l->interlace?"interlace=v" : "");