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