changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > plan9front / changeset: igfx: get edid information from lvds

changeset 4192: deca0df1450c
parent 4191: 6c111860e9b6
child 4196: 8a341092873b
author: cinap_lenrek@felloff.net
date: Sun, 11 Jan 2015 03:35:30 +0100
files: sys/src/cmd/aux/vga/edid.c sys/src/cmd/aux/vga/edid.h sys/src/cmd/aux/vga/igfx.c sys/src/cmd/aux/vga/mkfile sys/src/cmd/aux/vga/vesa.c sys/src/cmd/aux/vga/vga.h
description: igfx: get edid information from lvds
     1.1new file mode 100644
     1.2--- /dev/null
     1.3+++ b/sys/src/cmd/aux/vga/edid.c
     1.4@@ -0,0 +1,414 @@
     1.5+#include <u.h>
     1.6+#include <libc.h>
     1.7+#include <bio.h>
     1.8+#include <ndb.h>
     1.9+
    1.10+#include "pci.h"
    1.11+#include "vga.h"
    1.12+#include "edid.h"
    1.13+
    1.14+static Modelist*
    1.15+addmode(Modelist *l, Mode m)
    1.16+{
    1.17+	int rr;
    1.18+	Modelist **lp;
    1.19+
    1.20+	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
    1.21+	snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
    1.22+
    1.23+	for(lp=&l; *lp; lp=&(*lp)->next){
    1.24+		if(strcmp((*lp)->name, m.name) == 0){
    1.25+			(*lp)->Mode = m;
    1.26+			return l;
    1.27+		}
    1.28+	}
    1.29+
    1.30+	*lp = alloc(sizeof(**lp));
    1.31+	(*lp)->Mode = m;
    1.32+	return l;
    1.33+}
    1.34+
    1.35+/*
    1.36+ * Parse VESA EDID information.  Based on the VESA
    1.37+ * Extended Display Identification Data standard, Version 3,
    1.38+ * November 13, 1997.  See /public/doc/vesa/edidv3.pdf.
    1.39+ *
    1.40+ * This only handles 128-byte EDID blocks.  Until I find
    1.41+ * a monitor that produces 256-byte blocks, I'm not going
    1.42+ * to try to decode them.
    1.43+ */
    1.44+
    1.45+/*
    1.46+ * Established timings block.  There is a bitmap
    1.47+ * that says whether each mode is supported.  Most
    1.48+ * of these have VESA definitions.  Those that don't are marked
    1.49+ * as such, and we ignore them (the lookup fails).
    1.50+ */
    1.51+static char *estabtime[] = {
    1.52+	"720x400@70Hz",	/* non-VESA: IBM, VGA */
    1.53+	"720x400@88Hz",	/* non-VESA: IBM, XGA2 */
    1.54+	"640x480@60Hz",
    1.55+	"640x480@67Hz",	/* non-VESA: Apple, Mac II */
    1.56+	"640x480@72Hz",
    1.57+	"640x480@75Hz",
    1.58+	"800x600@56Hz",
    1.59+	"800x600@60Hz",
    1.60+
    1.61+	"800x600@72Hz",
    1.62+	"800x600@75Hz",
    1.63+	"832x624@75Hz",	/* non-VESA: Apple, Mac II */
    1.64+	"1024x768i@87Hz",	/* non-VESA: IBM */
    1.65+	"1024x768@60Hz",
    1.66+	"1024x768@70Hz",
    1.67+	"1024x768@75Hz",
    1.68+	"1280x1024@75Hz",
    1.69+
    1.70+	"1152x870@75Hz",	/* non-VESA: Apple, Mac II */
    1.71+};
    1.72+
    1.73+/*
    1.74+ * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
    1.75+ */
    1.76+static int
    1.77+decodedtb(Mode *m, uchar *p)
    1.78+{
    1.79+	int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
    1.80+	/* int hbord, vbord, dxmm, dymm, hbord, vbord; */
    1.81+
    1.82+	memset(m, 0, sizeof *m);
    1.83+
    1.84+	m->frequency = ((p[1]<<8) | p[0]) * 10000;
    1.85+
    1.86+	ha = ((p[4] & 0xF0)<<4) | p[2];		/* horizontal active */
    1.87+	hb = ((p[4] & 0x0F)<<8) | p[3];		/* horizontal blanking */
    1.88+	va = ((p[7] & 0xF0)<<4) | p[5];		/* vertical active */
    1.89+	vb = ((p[7] & 0x0F)<<8) | p[6];		/* vertical blanking */
    1.90+	hso = ((p[11] & 0xC0)<<2) | p[8];	/* horizontal sync offset */
    1.91+	hspw = ((p[11] & 0x30)<<4) | p[9];	/* horizontal sync pulse width */
    1.92+	vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4);	/* vertical sync offset */
    1.93+	vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F);		/* vertical sync pulse width */
    1.94+
    1.95+	/* dxmm = (p[14] & 0xF0)<<4) | p[12]; 	/* horizontal image size (mm) */
    1.96+	/* dymm = (p[14] & 0x0F)<<8) | p[13];	/* vertical image size (mm) */
    1.97+	/* hbord = p[15];		/* horizontal border (pixels) */
    1.98+	/* vbord = p[16];		/* vertical border (pixels) */
    1.99+
   1.100+	m->x = ha;
   1.101+	m->y = va;
   1.102+
   1.103+	m->ht = ha+hb;
   1.104+	m->shb = ha+hso;
   1.105+	m->ehb = ha+hso+hspw;
   1.106+
   1.107+	m->vt = va+vb;
   1.108+	m->vrs = va+vso;
   1.109+	m->vre = va+vso+vspw;
   1.110+
   1.111+	if(p[17] & 0x80)	/* interlaced */
   1.112+		m->interlace = 'v';
   1.113+
   1.114+	if(p[17] & 0x60)	/* some form of stereo monitor mode; no support */
   1.115+		return -1;
   1.116+
   1.117+	/*
   1.118+	 * Sync signal description.  I have no idea how to properly handle the 
   1.119+	 * first three cases, which I think are aimed at things other than
   1.120+	 * canonical SVGA monitors.
   1.121+	 */
   1.122+	switch((p[17] & 0x18)>>3) {
   1.123+	case 0:	/* analog composite sync signal*/
   1.124+	case 1:	/* bipolar analog composite sync signal */
   1.125+		/* p[17] & 0x04 means serration: hsync during vsync */
   1.126+		/* p[17] & 0x02 means sync pulse appears on RGB not just G */
   1.127+		break;
   1.128+
   1.129+	case 2:	/* digital composite sync signal */
   1.130+		/* p[17] & 0x04 means serration: hsync during vsync */
   1.131+		/* p[17] & 0x02 means hsync positive outside vsync */
   1.132+		break;
   1.133+
   1.134+	case 3:	/* digital separate sync signal; the norm */
   1.135+		m->vsync = (p[17] & 0x04) ? '+' : '-';
   1.136+		m->hsync = (p[17] & 0x02) ? '+' : '-';
   1.137+		break;
   1.138+	}
   1.139+	/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
   1.140+
   1.141+	rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
   1.142+
   1.143+	snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
   1.144+
   1.145+	return 0;
   1.146+}
   1.147+
   1.148+static int
   1.149+vesalookup(Mode *m, char *name)
   1.150+{
   1.151+	Mode **p;
   1.152+
   1.153+	for(p=vesamodes; *p; p++)
   1.154+		if(strcmp((*p)->name, name) == 0) {
   1.155+			*m = **p;
   1.156+			return 0;
   1.157+		}
   1.158+	return -1;
   1.159+}
   1.160+
   1.161+static int
   1.162+decodesti(Mode *m, uchar *p)
   1.163+{
   1.164+	int x, y, rr;
   1.165+	char str[20];
   1.166+
   1.167+	x = (p[0]+31)*8;
   1.168+	switch((p[1]>>6) & 3){
   1.169+	default:
   1.170+	case 0:
   1.171+		y = x;
   1.172+		break;
   1.173+	case 1:
   1.174+		y = (x*4)/3;
   1.175+		break;
   1.176+	case 2:
   1.177+		y = (x*5)/4;
   1.178+		break;
   1.179+	case 3:
   1.180+		y = (x*16)/9;
   1.181+		break;
   1.182+	}
   1.183+	rr = (p[1] & 0x1F) + 60;
   1.184+
   1.185+	sprint(str, "%dx%d@%dHz", x, y, rr);
   1.186+	return vesalookup(m, str);
   1.187+}
   1.188+
   1.189+int
   1.190+parseedid128(Edid *e, void *v)
   1.191+{
   1.192+	static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
   1.193+	uchar *p, *q, sum;
   1.194+	int dpms, estab, i, m, vid;
   1.195+	Mode mode;
   1.196+
   1.197+	memset(e, 0, sizeof *e);
   1.198+
   1.199+	p = (uchar*)v;
   1.200+	if(memcmp(p, magic, 8) != 0) {
   1.201+		werrstr("bad edid header");
   1.202+		return -1;
   1.203+	}
   1.204+
   1.205+	sum = 0;
   1.206+	for(i=0; i<128; i++) 
   1.207+		sum += p[i];
   1.208+	if(sum != 0) {
   1.209+		werrstr("bad edid checksum");
   1.210+		return -1;
   1.211+	}
   1.212+	p += 8;
   1.213+
   1.214+	assert(p == (uchar*)v+8);	/* assertion offsets from pp. 12-13 of the standard */
   1.215+	/*
   1.216+	 * Manufacturer name is three 5-bit ascii letters, packed
   1.217+	 * into a big endian [sic] short in big endian order.  The high bit is unused.
   1.218+	 */
   1.219+	i = (p[0]<<8) | p[1];
   1.220+	p += 2;
   1.221+	e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
   1.222+	e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
   1.223+	e->mfr[2] = 'A'-1 + (i & 0x1F);
   1.224+	e->mfr[3] = '\0';
   1.225+
   1.226+	/*
   1.227+	 * Product code is a little endian short.
   1.228+	 */
   1.229+	e->product = (p[1]<<8) | p[0];
   1.230+	p += 2;
   1.231+
   1.232+	/*
   1.233+	 * Serial number is a little endian long, 0x01010101 = unused.
   1.234+	 */
   1.235+	e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
   1.236+	p += 4;
   1.237+	if(e->serial == 0x01010101)
   1.238+		e->serial = 0;
   1.239+
   1.240+	e->mfrweek = *p++;
   1.241+	e->mfryear = 1990 + *p++;
   1.242+
   1.243+	assert(p == (uchar*)v+8+10);
   1.244+	/*
   1.245+	 * Structure version is next two bytes: major.minor.
   1.246+	 */
   1.247+	e->version = *p++;
   1.248+	e->revision = *p++;
   1.249+
   1.250+	assert(p == (uchar*)v+8+10+2);
   1.251+	/*
   1.252+	 * Basic display parameters / features.
   1.253+	 */
   1.254+	/*
   1.255+	 * Video input definition byte: 0x80 tells whether it is
   1.256+	 * an analog or digital screen; we ignore the other bits.
   1.257+	 * See p. 15 of the standard.
   1.258+	 */
   1.259+	vid = *p++;
   1.260+	if(vid & 0x80)
   1.261+		e->flags |= Fdigital;
   1.262+
   1.263+	e->dxcm = *p++;
   1.264+	e->dycm = *p++;
   1.265+	e->gamma = 100 + *p++;
   1.266+	dpms = *p++;
   1.267+	if(dpms & 0x80)
   1.268+		e->flags |= Fdpmsstandby;
   1.269+	if(dpms & 0x40)
   1.270+		e->flags |= Fdpmssuspend;
   1.271+	if(dpms & 0x20)
   1.272+		e->flags |= Fdpmsactiveoff;
   1.273+	if((dpms & 0x18) == 0x00)
   1.274+		e->flags |= Fmonochrome;
   1.275+	if(dpms & 0x01)
   1.276+		e->flags |= Fgtf;
   1.277+
   1.278+	assert(p == (uchar*)v+8+10+2+5);
   1.279+	/*
   1.280+	 * Color characteristics currently ignored.
   1.281+	 */
   1.282+	p += 10;
   1.283+
   1.284+	assert(p == (uchar*)v+8+10+2+5+10);
   1.285+	/*
   1.286+	 * Established timings: a bitmask of 19 preset timings.
   1.287+	 */
   1.288+	estab = (p[0]<<16) | (p[1]<<8) | p[2];
   1.289+	p += 3;
   1.290+
   1.291+	for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
   1.292+		if(estab & m)
   1.293+			if(vesalookup(&mode, estabtime[i]) == 0)
   1.294+				e->modelist = addmode(e->modelist,  mode);
   1.295+
   1.296+	assert(p == (uchar*)v+8+10+2+5+10+3);
   1.297+	/*
   1.298+	 * Standard Timing Identifications: eight 2-byte selectors
   1.299+	 * of more standard timings.
   1.300+	 */
   1.301+	for(i=0; i<8; i++, p+=2)
   1.302+		if(decodesti(&mode, p+2*i) == 0)
   1.303+			e->modelist = addmode(e->modelist, mode);
   1.304+
   1.305+	assert(p == (uchar*)v+8+10+2+5+10+3+16);
   1.306+	/*
   1.307+	 * Detailed Timings
   1.308+	 */
   1.309+	for(i=0; i<4; i++, p+=18) {
   1.310+		if(p[0] || p[1]) {	/* detailed timing block: p[0] or p[1] != 0 */
   1.311+			if(decodedtb(&mode, p) == 0)
   1.312+				e->modelist = addmode(e->modelist, mode);
   1.313+		} else if(p[2]==0) {	/* monitor descriptor block */
   1.314+			switch(p[3]) {
   1.315+			case 0xFF:	/* monitor serial number (13-byte ascii, 0A terminated) */
   1.316+				if(q = memchr(p+5, 0x0A, 13))
   1.317+					*q = '\0';
   1.318+				memset(e->serialstr, 0, sizeof(e->serialstr));
   1.319+				strncpy(e->serialstr, (char*)p+5, 13);
   1.320+				break;
   1.321+			case 0xFE:	/* ascii string (13-byte ascii, 0A terminated) */
   1.322+				break;
   1.323+			case 0xFD:	/* monitor range limits */
   1.324+				e->rrmin = p[5];
   1.325+				e->rrmax = p[6];
   1.326+				e->hrmin = p[7]*1000;
   1.327+				e->hrmax = p[8]*1000;
   1.328+				if(p[9] != 0xFF)
   1.329+					e->pclkmax = p[9]*10*1000000;
   1.330+				break;
   1.331+			case 0xFC:	/* monitor name (13-byte ascii, 0A terminated) */
   1.332+				if(q = memchr(p+5, 0x0A, 13))
   1.333+					*q = '\0';
   1.334+				memset(e->name, 0, sizeof(e->name));
   1.335+				strncpy(e->name, (char*)p+5, 13);
   1.336+				break;
   1.337+			case 0xFB:	/* extra color point data */
   1.338+				break;
   1.339+			case 0xFA:	/* extra standard timing identifications */
   1.340+				for(i=0; i<6; i++)
   1.341+					if(decodesti(&mode, p+5+2*i) == 0)
   1.342+						e->modelist = addmode(e->modelist, mode);
   1.343+				break;
   1.344+			}
   1.345+		}
   1.346+	}
   1.347+
   1.348+	assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
   1.349+	return 0;
   1.350+}
   1.351+
   1.352+Flag edidflags[] = {
   1.353+	Fdigital, "digital",
   1.354+	Fdpmsstandby, "standby",
   1.355+	Fdpmssuspend, "suspend",
   1.356+	Fdpmsactiveoff, "activeoff",
   1.357+	Fmonochrome, "monochrome",
   1.358+	Fgtf, "gtf",
   1.359+	0
   1.360+};
   1.361+
   1.362+void
   1.363+printflags(Flag *f, int b)
   1.364+{
   1.365+	int i;
   1.366+
   1.367+	for(i=0; f[i].bit; i++)
   1.368+		if(f[i].bit & b)
   1.369+			Bprint(&stdout, " %s", f[i].desc);
   1.370+	Bprint(&stdout, "\n");
   1.371+}
   1.372+
   1.373+void
   1.374+printedid(Edid *e)
   1.375+{
   1.376+	Modelist *l;
   1.377+
   1.378+	printitem("edid", "mfr");
   1.379+	Bprint(&stdout, "%s\n", e->mfr);
   1.380+	printitem("edid", "serialstr");
   1.381+	Bprint(&stdout, "%s\n", e->serialstr);
   1.382+	printitem("edid", "name");
   1.383+	Bprint(&stdout, "%s\n", e->name);
   1.384+	printitem("edid", "product");
   1.385+	Bprint(&stdout, "%d\n", e->product);
   1.386+	printitem("edid", "serial");
   1.387+	Bprint(&stdout, "%lud\n", e->serial);
   1.388+	printitem("edid", "version");
   1.389+	Bprint(&stdout, "%d.%d\n", e->version, e->revision);
   1.390+	printitem("edid", "mfrdate");
   1.391+	Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
   1.392+	printitem("edid", "size (cm)");
   1.393+	Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
   1.394+	printitem("edid", "gamma");
   1.395+	Bprint(&stdout, "%.2f\n", e->gamma/100.);
   1.396+	printitem("edid", "vert (Hz)");
   1.397+	Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
   1.398+	printitem("edid", "horz (Hz)");
   1.399+	Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
   1.400+	printitem("edid", "pclkmax");
   1.401+	Bprint(&stdout, "%lud\n", e->pclkmax);
   1.402+	printitem("edid", "flags");
   1.403+	printflags(edidflags, e->flags);
   1.404+
   1.405+	for(l=e->modelist; l; l=l->next){
   1.406+		printitem("edid", l->name);
   1.407+		Bprint(&stdout, "\n\t\tclock=%g\n"
   1.408+			"\t\tshb=%d ehb=%d ht=%d\n"
   1.409+			"\t\tvrs=%d vre=%d vt=%d\n"
   1.410+			"\t\thsync=%c vsync=%c %s\n",
   1.411+			l->frequency/1.e6, 
   1.412+			l->shb, l->ehb, l->ht,
   1.413+			l->vrs, l->vre, l->vt,
   1.414+			l->hsync?l->hsync:'?',
   1.415+			l->vsync?l->vsync:'?',
   1.416+			l->interlace?"interlace=v" : "");
   1.417+	}
   1.418+}
     2.1new file mode 100644
     2.2--- /dev/null
     2.3+++ b/sys/src/cmd/aux/vga/edid.h
     2.4@@ -0,0 +1,50 @@
     2.5+typedef struct Modelist Modelist;
     2.6+typedef struct Edid Edid;
     2.7+typedef struct Flag Flag;
     2.8+
     2.9+struct Edid {
    2.10+	char		mfr[4];		/* manufacturer */
    2.11+	char		serialstr[16];	/* serial number as string (in extended data) */
    2.12+	char		name[16];	/* monitor name as string (in extended data) */
    2.13+	ushort		product;	/* product code, 0 = unused */
    2.14+	ulong		serial;		/* serial number, 0 = unused */
    2.15+	uchar		version;	/* major version number */
    2.16+	uchar		revision;	/* minor version number */
    2.17+	uchar		mfrweek;	/* week of manufacture, 0 = unused */
    2.18+	int		mfryear;	/* year of manufacture, 0 = unused */
    2.19+	uchar 		dxcm;		/* horizontal image size in cm. */
    2.20+	uchar		dycm;		/* vertical image size in cm. */
    2.21+	int		gamma;		/* gamma*100 */
    2.22+	int		rrmin;		/* minimum vertical refresh rate */
    2.23+	int		rrmax;		/* maximum vertical refresh rate */
    2.24+	int		hrmin;		/* minimum horizontal refresh rate */
    2.25+	int		hrmax;		/* maximum horizontal refresh rate */
    2.26+	ulong		pclkmax;	/* maximum pixel clock */
    2.27+	int		flags;
    2.28+	Modelist	*modelist;	/* list of supported modes */
    2.29+};
    2.30+
    2.31+struct Modelist
    2.32+{
    2.33+	Mode;
    2.34+	Modelist *next;
    2.35+};
    2.36+
    2.37+struct Flag {
    2.38+	int bit;
    2.39+	char *desc;
    2.40+};
    2.41+
    2.42+enum {
    2.43+	Fdigital	= 1<<0,	/* is a digital display */
    2.44+	Fdpmsstandby	= 1<<1,	/* supports DPMS standby mode */
    2.45+	Fdpmssuspend	= 1<<2,	/* supports DPMS suspend mode */
    2.46+	Fdpmsactiveoff	= 1<<3,	/* supports DPMS active off mode */
    2.47+	Fmonochrome	= 1<<4,	/* is a monochrome display */
    2.48+	Fgtf		= 1<<5,	/* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
    2.49+};
    2.50+Flag	edidflags[];
    2.51+void	printflags(Flag *f, int b);
    2.52+
    2.53+int	parseedid128(Edid *e, void *v);
    2.54+void	printedid(Edid *e);
     3.1--- a/sys/src/cmd/aux/vga/igfx.c
     3.2+++ b/sys/src/cmd/aux/vga/igfx.c
     3.3@@ -4,6 +4,7 @@
     3.4 
     3.5 #include "pci.h"
     3.6 #include "vga.h"
     3.7+#include "edid.h"
     3.8 
     3.9 typedef struct Reg Reg;
    3.10 typedef struct Dpll Dpll;
    3.11@@ -142,12 +143,15 @@ struct Igfx {
    3.12 	Reg	ppstatus;
    3.13 
    3.14 	/* G45 */
    3.15+	Reg	gmbus[6];	/* GMBUSx */
    3.16+
    3.17 	Reg	sdvoc;
    3.18 	Reg	sdvob;
    3.19 
    3.20 	/* common */
    3.21 	Reg	adpa;
    3.22 	Reg	lvds;
    3.23+	Edid	*lvdsedid;
    3.24 
    3.25 	Reg	vgacntrl;
    3.26 };
    3.27@@ -305,6 +309,8 @@ devtype(Igfx *igfx)
    3.28 	return -1;
    3.29 }
    3.30 
    3.31+static void snarfedid(Igfx*);
    3.32+
    3.33 static void
    3.34 snarf(Vga* vga, Ctlr* ctlr)
    3.35 {
    3.36@@ -326,16 +332,14 @@ snarf(Vga* vga, Ctlr* ctlr)
    3.37 			return;
    3.38 		}
    3.39 		vgactlpci(igfx->pci);
    3.40+		if((igfx->pci->mem[4].bar & 1) == 0)
    3.41+			error("%s: no pio bar\n", ctlr->name);
    3.42+		igfx->pio = igfx->pci->mem[4].bar & ~1;
    3.43 		if(1){
    3.44 			vgactlw("type", ctlr->name);
    3.45 			igfx->mmio = segattach(0, "igfxmmio", 0, igfx->pci->mem[0].size);
    3.46 			if(igfx->mmio == (u32int*)-1)
    3.47-				error("%s: segattach mmio failed: %r\n", ctlr->name);
    3.48-		} else {
    3.49-			if((igfx->pci->mem[4].bar & 1) == 0)
    3.50-				error("%s: no pio bar\n", ctlr->name);
    3.51-			igfx->pio = igfx->pci->mem[4].bar & ~1;
    3.52-			igfx->mmio = nil;
    3.53+				igfx->mmio = nil;	/* use pio */
    3.54 		}
    3.55 		vga->private = igfx;
    3.56 	}
    3.57@@ -356,6 +360,10 @@ snarf(Vga* vga, Ctlr* ctlr)
    3.58 		igfx->sdvob		= snarfreg(igfx, 0x061140);
    3.59 		igfx->sdvoc		= snarfreg(igfx, 0x061160);
    3.60 
    3.61+		for(x=0; x<5; x++)
    3.62+			igfx->gmbus[x]	= snarfreg(igfx, 0x5100 + x*4);
    3.63+		igfx->gmbus[5]	= snarfreg(igfx, 0x5120);
    3.64+
    3.65 		igfx->pfit[0].ctrl	= snarfreg(igfx, 0x061230);
    3.66 		y = (igfx->pfit[0].ctrl.v >> 29) & 3;
    3.67 		if(igfx->pipe[y].pfit == nil)
    3.68@@ -441,6 +449,8 @@ snarf(Vga* vga, Ctlr* ctlr)
    3.69 	for(x=0; x<igfx->npipe; x++)
    3.70 		snarfpipe(igfx, x);
    3.71 
    3.72+	snarfedid(igfx);
    3.73+
    3.74 	ctlr->flag |= Fsnarf;
    3.75 }
    3.76 
    3.77@@ -614,7 +624,7 @@ inittrans(Trans *t, Mode *m)
    3.78 	/* trans/pipe timing */
    3.79 	t->ht.v = (m->ht - 1)<<16 | (m->x - 1);
    3.80 	t->hb.v = t->ht.v;
    3.81-	t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1);
    3.82+	t->hs.v = (m->ehb - 1)<<16 | (m->shb - 1);
    3.83 	t->vt.v = (m->vt - 1)<<16 | (m->y - 1);
    3.84 	t->vb.v = t->vt.v;
    3.85 	t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1);
    3.86@@ -1165,6 +1175,81 @@ dump(Vga* vga, Ctlr* ctlr)
    3.87 	dumpreg(ctlr->name, "sdvoc", igfx->sdvoc);
    3.88 
    3.89 	dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
    3.90+
    3.91+	if(igfx->lvdsedid != nil)
    3.92+		printedid(igfx->lvdsedid);
    3.93+}
    3.94+
    3.95+enum {
    3.96+	GMBUSCP = 0,	/* Clock/Port selection */
    3.97+	GMBUSCS = 1,	/* Command/Status */
    3.98+	GMBUSST = 2,	/* Status Register */
    3.99+	GMBUSDB	= 3,	/* Data Buffer Register */
   3.100+	GMBUSIM = 4,	/* Interrupt Mask */
   3.101+	GMBUSIX = 5,	/* Index Register */
   3.102+};
   3.103+	
   3.104+static int
   3.105+gmbusread(Igfx *igfx, int portsel, int addr, uchar *data, int len)
   3.106+{
   3.107+	u32int x, y;
   3.108+	int n, t;
   3.109+
   3.110+	if(igfx->gmbus[GMBUSCP].a == 0)
   3.111+		return -1;
   3.112+
   3.113+	wr(igfx, igfx->gmbus[GMBUSCP].a, portsel);
   3.114+	wr(igfx, igfx->gmbus[GMBUSIX].a, 0);
   3.115+
   3.116+	/* bus cycle without index and stop, byte count, slave address, read */
   3.117+	wr(igfx, igfx->gmbus[GMBUSCS].a, 1<<30 | 5<<25 | len<<16 | addr<<1 | 1);
   3.118+
   3.119+	n = 0;
   3.120+	while(len > 0){
   3.121+		x = 0;
   3.122+		for(t=0; t<100; t++){
   3.123+			x = rr(igfx, igfx->gmbus[GMBUSST].a);
   3.124+			if(x & (1<<11))
   3.125+				break;
   3.126+			sleep(5);
   3.127+		}
   3.128+		if((x & (1<<11)) == 0)
   3.129+			return -1;
   3.130+
   3.131+		t = 4 - (x & 3);
   3.132+		if(t > len)
   3.133+			t = len;
   3.134+		len -= t;
   3.135+
   3.136+		y = rr(igfx, igfx->gmbus[GMBUSDB].a);
   3.137+		switch(t){
   3.138+		case 4:
   3.139+			data[n++] = y & 0xff, y >>= 8;
   3.140+		case 3:
   3.141+			data[n++] = y & 0xff, y >>= 8;
   3.142+		case 2:
   3.143+			data[n++] = y & 0xff, y >>= 8;
   3.144+		case 1:
   3.145+			data[n++] = y & 0xff;
   3.146+		}
   3.147+	}
   3.148+	return n;
   3.149+}
   3.150+
   3.151+static void
   3.152+snarfedid(Igfx *igfx)
   3.153+{
   3.154+	uchar buf[128];
   3.155+
   3.156+	if(igfx->type != TypeG45)
   3.157+		return;
   3.158+	if(gmbusread(igfx, 3, 0x50, buf, sizeof(buf)) != sizeof(buf))
   3.159+		return;
   3.160+	igfx->lvdsedid = malloc(sizeof(Edid));
   3.161+	if(parseedid128(igfx->lvdsedid, buf) != 0){
   3.162+		free(igfx->lvdsedid);
   3.163+		igfx->lvdsedid = nil;
   3.164+	}
   3.165 }
   3.166 
   3.167 Ctlr igfx = {
     4.1--- a/sys/src/cmd/aux/vga/mkfile
     4.2+++ b/sys/src/cmd/aux/vga/mkfile
     4.3@@ -15,6 +15,7 @@ OFILES=\
     4.4 	cyber938x.$O\
     4.5 	data.$O\
     4.6 	db.$O\
     4.7+	edid.$O\
     4.8 	error.$O\
     4.9 	et4000.$O\
    4.10 	et4000hwgc.$O\
    4.11@@ -66,7 +67,8 @@ OFILES=\
    4.12 
    4.13 HFILES=\
    4.14 	pci.h\
    4.15-	vga.h
    4.16+	vga.h\
    4.17+	edid.h\
    4.18 
    4.19 UPDATE=\
    4.20 	mkfile\
    4.21@@ -75,7 +77,6 @@ UPDATE=\
    4.22 	/lib/vgadb\
    4.23 	riva_tbl.h\
    4.24 
    4.25-
    4.26 </sys/src/cmd/mkone
    4.27 
    4.28 geode.$O:	geode_modes.h
     5.1--- a/sys/src/cmd/aux/vga/vesa.c
     5.2+++ b/sys/src/cmd/aux/vga/vesa.c
     5.3@@ -6,11 +6,10 @@ typedef struct Ureg Ureg;
     5.4 
     5.5 #include "pci.h"
     5.6 #include "vga.h"
     5.7+#include "edid.h"
     5.8 
     5.9 typedef struct Vbe Vbe;
    5.10 typedef struct Vmode Vmode;
    5.11-typedef struct Modelist Modelist;
    5.12-typedef struct Edid Edid;
    5.13 
    5.14 enum
    5.15 {
    5.16@@ -47,44 +46,6 @@ struct Vmode
    5.17 	ulong	paddr;
    5.18 };
    5.19 
    5.20-struct Edid {
    5.21-	char		mfr[4];		/* manufacturer */
    5.22-	char		serialstr[16];	/* serial number as string (in extended data) */
    5.23-	char		name[16];		/* monitor name as string (in extended data) */
    5.24-	ushort	product;		/* product code, 0 = unused */
    5.25-	ulong	serial;		/* serial number, 0 = unused */
    5.26-	uchar	version;		/* major version number */
    5.27-	uchar	revision;		/* minor version number */
    5.28-	uchar	mfrweek;		/* week of manufacture, 0 = unused */
    5.29-	int		mfryear;		/* year of manufacture, 0 = unused */
    5.30-	uchar 	dxcm;		/* horizontal image size in cm. */
    5.31-	uchar	dycm;		/* vertical image size in cm. */
    5.32-	int		gamma;		/* gamma*100 */
    5.33-	int		rrmin;		/* minimum vertical refresh rate */
    5.34-	int		rrmax;		/* maximum vertical refresh rate */
    5.35-	int		hrmin;		/* minimum horizontal refresh rate */
    5.36-	int		hrmax;		/* maximum horizontal refresh rate */
    5.37-	ulong	pclkmax;		/* maximum pixel clock */
    5.38-	int		flags;
    5.39-
    5.40-	Modelist	*modelist;		/* list of supported modes */
    5.41-};
    5.42-
    5.43-struct Modelist
    5.44-{
    5.45-	Mode;
    5.46-	Modelist *next;
    5.47-};
    5.48-
    5.49-enum {
    5.50-	Fdigital = 1<<0,		/* is a digital display */
    5.51-	Fdpmsstandby = 1<<1,	/* supports DPMS standby mode */
    5.52-	Fdpmssuspend = 1<<2,	/* supports DPMS suspend mode */
    5.53-	Fdpmsactiveoff = 1<<3,	/* supports DPMS active off mode */
    5.54-	Fmonochrome = 1<<4,	/* is a monochrome display */
    5.55-	Fgtf = 1<<5,		/* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
    5.56-};
    5.57-
    5.58 #define WORD(p) ((p)[0] | ((p)[1]<<8))
    5.59 #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
    5.60 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
    5.61@@ -93,8 +54,6 @@ enum {
    5.62 static Vbe *vbe;
    5.63 static Edid *edid;
    5.64 
    5.65-extern Mode *vesamodes[];
    5.66-
    5.67 Vbe *mkvbe(void);
    5.68 int vbecheck(Vbe*);
    5.69 uchar *vbemodes(Vbe*);
    5.70@@ -106,12 +65,11 @@ void vbeprintmodeinfo(Vbe*, int, char*);
    5.71 int vbesnarf(Vbe*, Vga*);
    5.72 void vesaddc(void);
    5.73 int vbeddcedid(Vbe *vbe, Edid *e);
    5.74-void printedid(Edid*);
    5.75-void fixbios(Vbe*);
    5.76 uchar* vbesetup(Vbe*, Ureg*, int);
    5.77 int vbecall(Vbe*, Ureg*);
    5.78 int setdisplay(Vbe *vbe, int display);
    5.79 int getdisplay(Vbe *vbe);
    5.80+void fixbios(Vbe*);
    5.81 
    5.82 int
    5.83 dbvesa(Vga* vga)
    5.84@@ -463,12 +421,6 @@ Ctlr softhwgc = {
    5.85  * VESA bios extension
    5.86  */
    5.87 
    5.88-typedef struct Flag Flag;
    5.89-struct Flag {
    5.90-	int bit;
    5.91-	char *desc;
    5.92-};
    5.93-
    5.94 static Flag capabilityflag[] = {
    5.95 	0x01, "8-bit-dac",
    5.96 	0x02, "not-vga",
    5.97@@ -544,18 +496,6 @@ static char *modelstr[] = {
    5.98 	[ModYUV]	"YUV",
    5.99 };
   5.100 
   5.101-
   5.102-static void
   5.103-printflags(Flag *f, int b)
   5.104-{
   5.105-	int i;
   5.106-
   5.107-	for(i=0; f[i].bit; i++)
   5.108-		if(f[i].bit & b)
   5.109-			Bprint(&stdout, " %s", f[i].desc);
   5.110-	Bprint(&stdout, "\n");
   5.111-}
   5.112-
   5.113 Vbe*
   5.114 mkvbe(void)
   5.115 {
   5.116@@ -928,16 +868,6 @@ vesatextmode(void)
   5.117 		error("vbesetmode: %r\n");
   5.118 }
   5.119 
   5.120-static Flag edidflags[] = {
   5.121-	Fdigital, "digital",
   5.122-	Fdpmsstandby, "standby",
   5.123-	Fdpmssuspend, "suspend",
   5.124-	Fdpmsactiveoff, "activeoff",
   5.125-	Fmonochrome, "monochrome",
   5.126-	Fgtf, "gtf",
   5.127-	0
   5.128-};
   5.129-
   5.130 int parseedid128(Edid *e, void *v);
   5.131 
   5.132 int
   5.133@@ -1001,405 +931,6 @@ setdisplay(Vbe *vbe, int display)
   5.134 }
   5.135 
   5.136 void
   5.137-printedid(Edid *e)
   5.138-{
   5.139-	Modelist *l;
   5.140-
   5.141-	printitem("edid", "mfr");
   5.142-	Bprint(&stdout, "%s\n", e->mfr);
   5.143-	printitem("edid", "serialstr");
   5.144-	Bprint(&stdout, "%s\n", e->serialstr);
   5.145-	printitem("edid", "name");
   5.146-	Bprint(&stdout, "%s\n", e->name);
   5.147-	printitem("edid", "product");
   5.148-	Bprint(&stdout, "%d\n", e->product);
   5.149-	printitem("edid", "serial");
   5.150-	Bprint(&stdout, "%lud\n", e->serial);
   5.151-	printitem("edid", "version");
   5.152-	Bprint(&stdout, "%d.%d\n", e->version, e->revision);
   5.153-	printitem("edid", "mfrdate");
   5.154-	Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
   5.155-	printitem("edid", "size (cm)");
   5.156-	Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
   5.157-	printitem("edid", "gamma");
   5.158-	Bprint(&stdout, "%.2f\n", e->gamma/100.);
   5.159-	printitem("edid", "vert (Hz)");
   5.160-	Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
   5.161-	printitem("edid", "horz (Hz)");
   5.162-	Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
   5.163-	printitem("edid", "pclkmax");
   5.164-	Bprint(&stdout, "%lud\n", e->pclkmax);
   5.165-	printitem("edid", "flags");
   5.166-	printflags(edidflags, e->flags);
   5.167-
   5.168-	for(l=e->modelist; l; l=l->next){
   5.169-		printitem("edid", l->name);
   5.170-		Bprint(&stdout, "\n\t\tclock=%g\n"
   5.171-			"\t\tshb=%d ehb=%d ht=%d\n"
   5.172-			"\t\tvrs=%d vre=%d vt=%d\n"
   5.173-			"\t\thsync=%c vsync=%c %s\n",
   5.174-			l->frequency/1.e6, 
   5.175-			l->shb, l->ehb, l->ht,
   5.176-			l->vrs, l->vre, l->vt,
   5.177-			l->hsync?l->hsync:'?',
   5.178-			l->vsync?l->vsync:'?',
   5.179-			l->interlace?"interlace=v" : "");
   5.180-	}
   5.181-}
   5.182-
   5.183-Modelist*
   5.184-addmode(Modelist *l, Mode m)
   5.185-{
   5.186-	int rr;
   5.187-	Modelist **lp;
   5.188-
   5.189-	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
   5.190-	snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
   5.191-
   5.192-	if(m.shs == 0)
   5.193-		m.shs = m.shb;
   5.194-	if(m.ehs == 0)
   5.195-		m.ehs = m.ehb;
   5.196-	if(m.vbs == 0)
   5.197-		m.vbs = m.vrs;
   5.198-	if(m.vbe == 0)
   5.199-		m.vbe = m.vbs+1;
   5.200-
   5.201-	for(lp=&l; *lp; lp=&(*lp)->next){
   5.202-		if(strcmp((*lp)->name, m.name) == 0){
   5.203-			(*lp)->Mode = m;
   5.204-			return l;
   5.205-		}
   5.206-	}
   5.207-
   5.208-	*lp = alloc(sizeof(**lp));
   5.209-	(*lp)->Mode = m;
   5.210-	return l;
   5.211-}
   5.212-
   5.213-/*
   5.214- * Parse VESA EDID information.  Based on the VESA
   5.215- * Extended Display Identification Data standard, Version 3,
   5.216- * November 13, 1997.  See /public/doc/vesa/edidv3.pdf.
   5.217- *
   5.218- * This only handles 128-byte EDID blocks.  Until I find
   5.219- * a monitor that produces 256-byte blocks, I'm not going
   5.220- * to try to decode them.
   5.221- */
   5.222-
   5.223-/*
   5.224- * Established timings block.  There is a bitmap
   5.225- * that says whether each mode is supported.  Most
   5.226- * of these have VESA definitions.  Those that don't are marked
   5.227- * as such, and we ignore them (the lookup fails).
   5.228- */
   5.229-static char *estabtime[] = {
   5.230-	"720x400@70Hz",	/* non-VESA: IBM, VGA */
   5.231-	"720x400@88Hz",	/* non-VESA: IBM, XGA2 */
   5.232-	"640x480@60Hz",
   5.233-	"640x480@67Hz",	/* non-VESA: Apple, Mac II */
   5.234-	"640x480@72Hz",
   5.235-	"640x480@75Hz",
   5.236-	"800x600@56Hz",
   5.237-	"800x600@60Hz",
   5.238-
   5.239-	"800x600@72Hz",
   5.240-	"800x600@75Hz",
   5.241-	"832x624@75Hz",	/* non-VESA: Apple, Mac II */
   5.242-	"1024x768i@87Hz",	/* non-VESA: IBM */
   5.243-	"1024x768@60Hz",
   5.244-	"1024x768@70Hz",
   5.245-	"1024x768@75Hz",
   5.246-	"1280x1024@75Hz",
   5.247-
   5.248-	"1152x870@75Hz",	/* non-VESA: Apple, Mac II */
   5.249-};
   5.250-
   5.251-/*
   5.252- * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
   5.253- */
   5.254-static int
   5.255-decodedtb(Mode *m, uchar *p)
   5.256-{
   5.257-	int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
   5.258-	/* int dxmm, dymm, hbord, vbord; */
   5.259-
   5.260-	memset(m, 0, sizeof *m);
   5.261-
   5.262-	m->frequency = ((p[1]<<8) | p[0]) * 10000;
   5.263-
   5.264-	ha = ((p[4] & 0xF0)<<4) | p[2];		/* horizontal active */
   5.265-	hb = ((p[4] & 0x0F)<<8) | p[3];		/* horizontal blanking */
   5.266-	va = ((p[7] & 0xF0)<<4) | p[5];		/* vertical active */
   5.267-	vb = ((p[7] & 0x0F)<<8) | p[6];		/* vertical blanking */
   5.268-	hso = ((p[11] & 0xC0)<<2) | p[8];	/* horizontal sync offset */
   5.269-	hspw = ((p[11] & 0x30)<<4) | p[9];	/* horizontal sync pulse width */
   5.270-	vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4);	/* vertical sync offset */
   5.271-	vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F);		/* vertical sync pulse width */
   5.272-
   5.273-	/* dxmm = (p[14] & 0xF0)<<4) | p[12]; 	/* horizontal image size (mm) */
   5.274-	/* dymm = (p[14] & 0x0F)<<8) | p[13];	/* vertical image size (mm) */
   5.275-	/* hbord = p[15];		/* horizontal border (pixels) */
   5.276-	/* vbord = p[16];		/* vertical border (pixels) */
   5.277-
   5.278-	m->x = ha;
   5.279-	m->y = va;
   5.280-
   5.281-	m->ht = ha+hb;
   5.282-	m->shs = ha;
   5.283-	m->shb = ha+hso;
   5.284-	m->ehb = ha+hso+hspw;
   5.285-	m->ehs = ha+hb;
   5.286-
   5.287-	m->vt = va+vb;
   5.288-	m->vbs = va;	
   5.289-	m->vrs = va+vso;
   5.290-	m->vre = va+vso+vspw;
   5.291-	m->vbe = va+vb;
   5.292-
   5.293-	if(p[17] & 0x80)	/* interlaced */
   5.294-		m->interlace = 'v';
   5.295-
   5.296-	if(p[17] & 0x60)	/* some form of stereo monitor mode; no support */
   5.297-		return -1;
   5.298-
   5.299-	/*
   5.300-	 * Sync signal description.  I have no idea how to properly handle the 
   5.301-	 * first three cases, which I think are aimed at things other than
   5.302-	 * canonical SVGA monitors.
   5.303-	 */
   5.304-	switch((p[17] & 0x18)>>3) {
   5.305-	case 0:	/* analog composite sync signal*/
   5.306-	case 1:	/* bipolar analog composite sync signal */
   5.307-		/* p[17] & 0x04 means serration: hsync during vsync */
   5.308-		/* p[17] & 0x02 means sync pulse appears on RGB not just G */
   5.309-		break;
   5.310-
   5.311-	case 2:	/* digital composite sync signal */
   5.312-		/* p[17] & 0x04 means serration: hsync during vsync */
   5.313-		/* p[17] & 0x02 means hsync positive outside vsync */
   5.314-		break;
   5.315-
   5.316-	case 3:	/* digital separate sync signal; the norm */
   5.317-		m->vsync = (p[17] & 0x04) ? '+' : '-';
   5.318-		m->hsync = (p[17] & 0x02) ? '+' : '-';
   5.319-		break;
   5.320-	}
   5.321-	/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
   5.322-
   5.323-	rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
   5.324-
   5.325-	snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
   5.326-
   5.327-	return 0;
   5.328-}
   5.329-
   5.330-int
   5.331-vesalookup(Mode *m, char *name)
   5.332-{
   5.333-	Mode **p;
   5.334-
   5.335-	for(p=vesamodes; *p; p++)
   5.336-		if(strcmp((*p)->name, name) == 0) {
   5.337-			*m = **p;
   5.338-			return 0;
   5.339-		}
   5.340-
   5.341-	return -1;
   5.342-}
   5.343-
   5.344-static int
   5.345-decodesti(Mode *m, uchar *p)
   5.346-{
   5.347-	int x, y, rr;
   5.348-	char str[20];
   5.349-
   5.350-	x = (p[0]+31)*8;
   5.351-	switch((p[1]>>6) & 3){
   5.352-	default:
   5.353-	case 0:
   5.354-		y = x;
   5.355-		break;
   5.356-	case 1:
   5.357-		y = (x*4)/3;
   5.358-		break;
   5.359-	case 2:
   5.360-		y = (x*5)/4;
   5.361-		break;
   5.362-	case 3:
   5.363-		y = (x*16)/9;
   5.364-		break;
   5.365-	}
   5.366-	rr = (p[1] & 0x1F) + 60;
   5.367-
   5.368-	sprint(str, "%dx%d@%dHz", x, y, rr);
   5.369-	return vesalookup(m, str);
   5.370-}
   5.371-
   5.372-int
   5.373-parseedid128(Edid *e, void *v)
   5.374-{
   5.375-	static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
   5.376-	uchar *p, *q, sum;
   5.377-	int dpms, estab, i, m, vid;
   5.378-	Mode mode;
   5.379-
   5.380-	memset(e, 0, sizeof *e);
   5.381-
   5.382-	p = (uchar*)v;
   5.383-	if(memcmp(p, magic, 8) != 0) {
   5.384-		werrstr("bad edid header");
   5.385-		return -1;
   5.386-	}
   5.387-
   5.388-	sum = 0;
   5.389-	for(i=0; i<128; i++) 
   5.390-		sum += p[i];
   5.391-	if(sum != 0) {
   5.392-		werrstr("bad edid checksum");
   5.393-		return -1;
   5.394-	}
   5.395-	p += 8;
   5.396-
   5.397-	assert(p == (uchar*)v+8);	/* assertion offsets from pp. 12-13 of the standard */
   5.398-	/*
   5.399-	 * Manufacturer name is three 5-bit ascii letters, packed
   5.400-	 * into a big endian [sic] short in big endian order.  The high bit is unused.
   5.401-	 */
   5.402-	i = (p[0]<<8) | p[1];
   5.403-	p += 2;
   5.404-	e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
   5.405-	e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
   5.406-	e->mfr[2] = 'A'-1 + (i & 0x1F);
   5.407-	e->mfr[3] = '\0';
   5.408-
   5.409-	/*
   5.410-	 * Product code is a little endian short.
   5.411-	 */
   5.412-	e->product = (p[1]<<8) | p[0];
   5.413-	p += 2;
   5.414-
   5.415-	/*
   5.416-	 * Serial number is a little endian long, 0x01010101 = unused.
   5.417-	 */
   5.418-	e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
   5.419-	p += 4;
   5.420-	if(e->serial == 0x01010101)
   5.421-		e->serial = 0;
   5.422-
   5.423-	e->mfrweek = *p++;
   5.424-	e->mfryear = 1990 + *p++;
   5.425-
   5.426-	assert(p == (uchar*)v+8+10);
   5.427-	/*
   5.428-	 * Structure version is next two bytes: major.minor.
   5.429-	 */
   5.430-	e->version = *p++;
   5.431-	e->revision = *p++;
   5.432-
   5.433-	assert(p == (uchar*)v+8+10+2);
   5.434-	/*
   5.435-	 * Basic display parameters / features.
   5.436-	 */
   5.437-	/*
   5.438-	 * Video input definition byte: 0x80 tells whether it is
   5.439-	 * an analog or digital screen; we ignore the other bits.
   5.440-	 * See p. 15 of the standard.
   5.441-	 */
   5.442-	vid = *p++;
   5.443-	if(vid & 0x80)
   5.444-		e->flags |= Fdigital;
   5.445-
   5.446-	e->dxcm = *p++;
   5.447-	e->dycm = *p++;
   5.448-	e->gamma = 100 + *p++;
   5.449-	dpms = *p++;
   5.450-	if(dpms & 0x80)
   5.451-		e->flags |= Fdpmsstandby;
   5.452-	if(dpms & 0x40)
   5.453-		e->flags |= Fdpmssuspend;
   5.454-	if(dpms & 0x20)
   5.455-		e->flags |= Fdpmsactiveoff;
   5.456-	if((dpms & 0x18) == 0x00)
   5.457-		e->flags |= Fmonochrome;
   5.458-	if(dpms & 0x01)
   5.459-		e->flags |= Fgtf;
   5.460-
   5.461-	assert(p == (uchar*)v+8+10+2+5);
   5.462-	/*
   5.463-	 * Color characteristics currently ignored.
   5.464-	 */
   5.465-	p += 10;
   5.466-
   5.467-	assert(p == (uchar*)v+8+10+2+5+10);
   5.468-	/*
   5.469-	 * Established timings: a bitmask of 19 preset timings.
   5.470-	 */
   5.471-	estab = (p[0]<<16) | (p[1]<<8) | p[2];
   5.472-	p += 3;
   5.473-
   5.474-	for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
   5.475-		if(estab & m)
   5.476-			if(vesalookup(&mode, estabtime[i]) == 0)
   5.477-				e->modelist = addmode(e->modelist,  mode);
   5.478-
   5.479-	assert(p == (uchar*)v+8+10+2+5+10+3);
   5.480-	/*
   5.481-	 * Standard Timing Identifications: eight 2-byte selectors
   5.482-	 * of more standard timings.
   5.483-	 */
   5.484-	for(i=0; i<8; i++, p+=2)
   5.485-		if(decodesti(&mode, p+2*i) == 0)
   5.486-			e->modelist = addmode(e->modelist, mode);
   5.487-
   5.488-	assert(p == (uchar*)v+8+10+2+5+10+3+16);
   5.489-	/*
   5.490-	 * Detailed Timings
   5.491-	 */
   5.492-	for(i=0; i<4; i++, p+=18) {
   5.493-		if(p[0] || p[1]) {	/* detailed timing block: p[0] or p[1] != 0 */
   5.494-			if(decodedtb(&mode, p) == 0)
   5.495-				e->modelist = addmode(e->modelist, mode);
   5.496-		} else if(p[2]==0) {	/* monitor descriptor block */
   5.497-			switch(p[3]) {
   5.498-			case 0xFF:	/* monitor serial number (13-byte ascii, 0A terminated) */
   5.499-				if(q = memchr(p+5, 0x0A, 13))
   5.500-					*q = '\0';
   5.501-				memset(e->serialstr, 0, sizeof(e->serialstr));
   5.502-				strncpy(e->serialstr, (char*)p+5, 13);
   5.503-				break;
   5.504-			case 0xFE:	/* ascii string (13-byte ascii, 0A terminated) */
   5.505-				break;
   5.506-			case 0xFD:	/* monitor range limits */
   5.507-				e->rrmin = p[5];
   5.508-				e->rrmax = p[6];
   5.509-				e->hrmin = p[7]*1000;
   5.510-				e->hrmax = p[8]*1000;
   5.511-				if(p[9] != 0xFF)
   5.512-					e->pclkmax = p[9]*10*1000000;
   5.513-				break;
   5.514-			case 0xFC:	/* monitor name (13-byte ascii, 0A terminated) */
   5.515-				if(q = memchr(p+5, 0x0A, 13))
   5.516-					*q = '\0';
   5.517-				memset(e->name, 0, sizeof(e->name));
   5.518-				strncpy(e->name, (char*)p+5, 13);
   5.519-				break;
   5.520-			case 0xFB:	/* extra color point data */
   5.521-				break;
   5.522-			case 0xFA:	/* extra standard timing identifications */
   5.523-				for(i=0; i<6; i++)
   5.524-					if(decodesti(&mode, p+5+2*i) == 0)
   5.525-						e->modelist = addmode(e->modelist, mode);
   5.526-				break;
   5.527-			}
   5.528-		}
   5.529-	}
   5.530-
   5.531-	assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
   5.532-	return 0;
   5.533-}
   5.534-
   5.535-void
   5.536 fixbios(Vbe *vbe)
   5.537 {
   5.538 	uchar *p;
   5.539@@ -1424,3 +955,4 @@ fixbios(Vbe *vbe)
   5.540 		}
   5.541 	}
   5.542 }
   5.543+
     6.1--- a/sys/src/cmd/aux/vga/vga.h
     6.2+++ b/sys/src/cmd/aux/vga/vga.h
     6.3@@ -112,8 +112,8 @@ typedef struct Mode {
     6.4 	int	vrs;			/* Vertical Retrace Start (Crt10) */
     6.5 	int	vre;			/* Vertical Retrace End (Crt11) */
     6.6 
     6.7-	int		vbs;		/* optional Vertical Blank Start */
     6.8-	int		vbe;		/* optional Vertical Blank End */
     6.9+	int	vbs;			/* optional Vertical Blank Start */
    6.10+	int	vbe;			/* optional Vertical Blank End */
    6.11 	
    6.12 	ulong	videobw;
    6.13 
    6.14@@ -252,6 +252,9 @@ extern int dbctlr(char*, Vga*);
    6.15 extern Mode* dbmode(char*, char*, char*);
    6.16 extern void dbdumpmode(Mode*);
    6.17 
    6.18+/* edid.c */
    6.19+extern Mode* edidmode(uchar *, int);
    6.20+
    6.21 /* error.c */
    6.22 extern void error(char*, ...);
    6.23 extern void trace(char*, ...);
    6.24@@ -438,6 +441,9 @@ extern int dbvesa(Vga*);
    6.25 extern Mode *dbvesamode(char*);
    6.26 extern void vesatextmode(void);
    6.27 
    6.28+/* vesadb.c */
    6.29+extern Mode *vesamodes[];
    6.30+
    6.31 /* virge.c */
    6.32 extern Ctlr virge;
    6.33