Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_posix/sys/src/cmd/aux/vga/vesa.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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*
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
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
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
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
 
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
 
290
};
291
 
292
static Flag winattributesflags[] = {
293
	1<<0, "relocatable",
294
	1<<1, "readable",
295
	1<<2, "writeable",
296
 
297
};
298
 
299
static Flag directcolorflags[] = {
300
	1<<0, "programmable-color-ramp",
301
	1<<1, "x-usable",
302
 
303
};
304
 
305
static char *modelstr[] = {
306
	"text", "cga", "hercules", "planar", "packed", "non-chain4", "direct", "YUV"
307
};
308
 
309
static void
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*
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
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*
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*
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
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
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
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
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*
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
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
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
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
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
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
 
627
};
628
 
629
int parseedid128(Edid *e, void *v);
630
 
631
int
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
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*
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
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
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
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
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
}