]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/vga/edid.c
exec(2): fix prototypes
[plan9front.git] / sys / src / cmd / aux / vga / edid.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ndb.h>
5
6 #include "pci.h"
7 #include "vga.h"
8
9 static Modelist*
10 addmode(Modelist *l, Mode m)
11 {
12         Modelist *ll;
13         int rr;
14
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);
17
18         for(ll = l; ll != nil; ll = ll->next){
19                 if(strcmp(ll->name, m.name) == 0){
20                         ll->Mode = m;
21                         return l;
22                 }
23         }
24
25         ll = alloc(sizeof(Modelist));
26         ll->Mode = m;
27         ll->next = l;
28         return ll;
29 }
30
31 /*
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.
35  *
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.
39  */
40
41 /*
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).
46  */
47 static char *estabtime[] = {
48         "720x400@70Hz", /* non-VESA: IBM, VGA */
49         "720x400@88Hz", /* non-VESA: IBM, XGA2 */
50         "640x480@60Hz",
51         "640x480@67Hz", /* non-VESA: Apple, Mac II */
52         "640x480@72Hz",
53         "640x480@75Hz",
54         "800x600@56Hz",
55         "800x600@60Hz",
56
57         "800x600@72Hz",
58         "800x600@75Hz",
59         "832x624@75Hz", /* non-VESA: Apple, Mac II */
60         "1024x768i@87Hz",       /* non-VESA: IBM */
61         "1024x768@60Hz",
62         "1024x768@70Hz",
63         "1024x768@75Hz",
64         "1280x1024@75Hz",
65
66         "1152x870@75Hz",        /* non-VESA: Apple, Mac II */
67 };
68
69 /*
70  * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
71  */
72 static int
73 decodedtb(Mode *m, uchar *p)
74 {
75         int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
76         /* int hbord, vbord, dxmm, dymm, hbord, vbord; */
77
78         memset(m, 0, sizeof *m);
79
80         m->frequency = ((p[1]<<8) | p[0]) * 10000;
81
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 */
90
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) */
95
96         m->x = ha;
97         m->y = va;
98
99         m->ht = ha+hb;
100         m->shb = ha+hso;
101         m->ehb = ha+hso+hspw;
102
103         m->vt = va+vb;
104         m->vrs = va+vso;
105         m->vre = va+vso+vspw;
106
107         if(p[17] & 0x80)        /* interlaced */
108                 m->interlace = 'v';
109
110         if(p[17] & 0x60)        /* some form of stereo monitor mode; no support */
111                 return -1;
112
113         /*
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.
117          */
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 */
123                 break;
124
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 */
128                 break;
129
130         case 3: /* digital separate sync signal; the norm */
131                 m->vsync = (p[17] & 0x04) ? '+' : '-';
132                 m->hsync = (p[17] & 0x02) ? '+' : '-';
133                 break;
134         }
135         /* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
136
137         rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
138
139         snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
140
141         return 0;
142 }
143
144 static int
145 vesalookup(Mode *m, char *name)
146 {
147         Mode **p;
148
149         for(p=vesamodes; *p; p++)
150                 if(strcmp((*p)->name, name) == 0) {
151                         *m = **p;
152                         return 0;
153                 }
154         return -1;
155 }
156
157 static int
158 decodesti(Mode *m, uchar *p)
159 {
160         int x, y, rr;
161         char str[20];
162
163         x = (p[0]+31)*8;
164         switch((p[1]>>6) & 3){
165         default:
166         case 0:
167                 y = x;
168                 break;
169         case 1:
170                 y = (x*4)/3;
171                 break;
172         case 2:
173                 y = (x*5)/4;
174                 break;
175         case 3:
176                 y = (x*16)/9;
177                 break;
178         }
179         rr = (p[1] & 0x1F) + 60;
180
181         sprint(str, "%dx%d@%dHz", x, y, rr);
182         return vesalookup(m, str);
183 }
184
185 Edid*
186 parseedid128(void *v)
187 {
188         static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
189         uchar *p, *q, sum;
190         int dpms, estab, i, m, vid;
191         Mode mode;
192         Edid *e;
193
194         e = alloc(sizeof(Edid));
195
196         p = (uchar*)v;
197         if(memcmp(p, magic, 8) != 0) {
198                 free(e);
199                 werrstr("bad edid header");
200                 return nil;
201         }
202
203         sum = 0;
204         for(i=0; i<128; i++) 
205                 sum += p[i];
206         if(sum != 0) {
207                 free(e);
208                 werrstr("bad edid checksum");
209                 return nil;
210         }
211         p += 8;
212
213         assert(p == (uchar*)v+8);       /* assertion offsets from pp. 12-13 of the standard */
214         /*
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.
217          */
218         i = (p[0]<<8) | p[1];
219         p += 2;
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);
223         e->mfr[3] = '\0';
224
225         /*
226          * Product code is a little endian short.
227          */
228         e->product = (p[1]<<8) | p[0];
229         p += 2;
230
231         /*
232          * Serial number is a little endian long, 0x01010101 = unused.
233          */
234         e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
235         p += 4;
236         if(e->serial == 0x01010101)
237                 e->serial = 0;
238
239         e->mfrweek = *p++;
240         e->mfryear = 1990 + *p++;
241
242         assert(p == (uchar*)v+8+10);
243         /*
244          * Structure version is next two bytes: major.minor.
245          */
246         e->version = *p++;
247         e->revision = *p++;
248
249         assert(p == (uchar*)v+8+10+2);
250         /*
251          * Basic display parameters / features.
252          */
253         /*
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.
257          */
258         vid = *p++;
259         if(vid & 0x80)
260                 e->flags |= Fdigital;
261
262         e->dxcm = *p++;
263         e->dycm = *p++;
264         e->gamma = 100 + *p++;
265         dpms = *p++;
266         if(dpms & 0x80)
267                 e->flags |= Fdpmsstandby;
268         if(dpms & 0x40)
269                 e->flags |= Fdpmssuspend;
270         if(dpms & 0x20)
271                 e->flags |= Fdpmsactiveoff;
272         if((dpms & 0x18) == 0x00)
273                 e->flags |= Fmonochrome;
274         if(dpms & 0x01)
275                 e->flags |= Fgtf;
276
277         assert(p == (uchar*)v+8+10+2+5);
278         /*
279          * Color characteristics currently ignored.
280          */
281         p += 10;
282
283         assert(p == (uchar*)v+8+10+2+5+10);
284         /*
285          * Established timings: a bitmask of 19 preset timings.
286          */
287         estab = (p[0]<<16) | (p[1]<<8) | p[2];
288         p += 3;
289
290         for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
291                 if(estab & m)
292                         if(vesalookup(&mode, estabtime[i]) == 0)
293                                 e->modelist = addmode(e->modelist,  mode);
294
295         assert(p == (uchar*)v+8+10+2+5+10+3);
296         /*
297          * Standard Timing Identifications: eight 2-byte selectors
298          * of more standard timings.
299          */
300
301         for(i=0; i<8; i++, p+=2)
302                 if(decodesti(&mode, p+2*i) == 0)
303                         e->modelist = addmode(e->modelist, mode);
304
305         assert(p == (uchar*)v+8+10+2+5+10+3+16);
306         /*
307          * Detailed Timings
308          */
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 */
314                         switch(p[3]) {
315                         case 0xFF:      /* monitor serial number (13-byte ascii, 0A terminated) */
316                                 if(q = memchr(p+5, 0x0A, 13))
317                                         *q = '\0';
318                                 memset(e->serialstr, 0, sizeof(e->serialstr));
319                                 strncpy(e->serialstr, (char*)p+5, 13);
320                                 break;
321                         case 0xFE:      /* ascii string (13-byte ascii, 0A terminated) */
322                                 break;
323                         case 0xFD:      /* monitor range limits */
324                                 e->rrmin = p[5];
325                                 e->rrmax = p[6];
326                                 e->hrmin = p[7]*1000;
327                                 e->hrmax = p[8]*1000;
328                                 if(p[9] != 0xFF)
329                                         e->pclkmax = p[9]*10*1000000;
330                                 break;
331                         case 0xFC:      /* monitor name (13-byte ascii, 0A terminated) */
332                                 if(q = memchr(p+5, 0x0A, 13))
333                                         *q = '\0';
334                                 memset(e->name, 0, sizeof(e->name));
335                                 strncpy(e->name, (char*)p+5, 13);
336                                 break;
337                         case 0xFB:      /* extra color point data */
338                                 break;
339                         case 0xFA:      /* extra standard timing identifications */
340                                 for(i=0; i<6; i++)
341                                         if(decodesti(&mode, p+5+2*i) == 0)
342                                                 e->modelist = addmode(e->modelist, mode);
343                                 break;
344                         }
345                 }
346         }
347
348         assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
349         return e;
350 }
351
352 Flag edidflags[] = {
353         Fdigital, "digital",
354         Fdpmsstandby, "standby",
355         Fdpmssuspend, "suspend",
356         Fdpmsactiveoff, "activeoff",
357         Fmonochrome, "monochrome",
358         Fgtf, "gtf",
359         0
360 };
361
362 void
363 printflags(Flag *f, int b)
364 {
365         int i;
366
367         for(i=0; f[i].bit; i++)
368                 if(f[i].bit & b)
369                         Bprint(&stdout, " %s", f[i].desc);
370         Bprint(&stdout, "\n");
371 }
372
373 void
374 printedid(Edid *e)
375 {
376         Modelist *l;
377
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);
404
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" : "");
414         }
415 }