shithub: riscv

Download patch

ref: 2cdadb1b2f7233a6642875e6b5fe6e0963c7171c
parent: 6e67b04a1fa9da8591abccb3c03859b50e3679d8
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jan 10 22:35:30 EST 2015

igfx: get edid information from lvds

--- /dev/null
+++ b/sys/src/cmd/aux/vga/edid.c
@@ -1,0 +1,414 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ndb.h>
+
+#include "pci.h"
+#include "vga.h"
+#include "edid.h"
+
+static Modelist*
+addmode(Modelist *l, Mode m)
+{
+	int rr;
+	Modelist **lp;
+
+	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
+	snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
+
+	for(lp=&l; *lp; lp=&(*lp)->next){
+		if(strcmp((*lp)->name, m.name) == 0){
+			(*lp)->Mode = m;
+			return l;
+		}
+	}
+
+	*lp = alloc(sizeof(**lp));
+	(*lp)->Mode = m;
+	return l;
+}
+
+/*
+ * Parse VESA EDID information.  Based on the VESA
+ * Extended Display Identification Data standard, Version 3,
+ * November 13, 1997.  See /public/doc/vesa/edidv3.pdf.
+ *
+ * This only handles 128-byte EDID blocks.  Until I find
+ * a monitor that produces 256-byte blocks, I'm not going
+ * to try to decode them.
+ */
+
+/*
+ * Established timings block.  There is a bitmap
+ * that says whether each mode is supported.  Most
+ * of these have VESA definitions.  Those that don't are marked
+ * as such, and we ignore them (the lookup fails).
+ */
+static char *estabtime[] = {
+	"720x400@70Hz",	/* non-VESA: IBM, VGA */
+	"720x400@88Hz",	/* non-VESA: IBM, XGA2 */
+	"640x480@60Hz",
+	"640x480@67Hz",	/* non-VESA: Apple, Mac II */
+	"640x480@72Hz",
+	"640x480@75Hz",
+	"800x600@56Hz",
+	"800x600@60Hz",
+
+	"800x600@72Hz",
+	"800x600@75Hz",
+	"832x624@75Hz",	/* non-VESA: Apple, Mac II */
+	"1024x768i@87Hz",	/* non-VESA: IBM */
+	"1024x768@60Hz",
+	"1024x768@70Hz",
+	"1024x768@75Hz",
+	"1280x1024@75Hz",
+
+	"1152x870@75Hz",	/* non-VESA: Apple, Mac II */
+};
+
+/*
+ * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
+ */
+static int
+decodedtb(Mode *m, uchar *p)
+{
+	int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
+	/* int hbord, vbord, dxmm, dymm, hbord, vbord; */
+
+	memset(m, 0, sizeof *m);
+
+	m->frequency = ((p[1]<<8) | p[0]) * 10000;
+
+	ha = ((p[4] & 0xF0)<<4) | p[2];		/* horizontal active */
+	hb = ((p[4] & 0x0F)<<8) | p[3];		/* horizontal blanking */
+	va = ((p[7] & 0xF0)<<4) | p[5];		/* vertical active */
+	vb = ((p[7] & 0x0F)<<8) | p[6];		/* vertical blanking */
+	hso = ((p[11] & 0xC0)<<2) | p[8];	/* horizontal sync offset */
+	hspw = ((p[11] & 0x30)<<4) | p[9];	/* horizontal sync pulse width */
+	vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4);	/* vertical sync offset */
+	vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F);		/* vertical sync pulse width */
+
+	/* dxmm = (p[14] & 0xF0)<<4) | p[12]; 	/* horizontal image size (mm) */
+	/* dymm = (p[14] & 0x0F)<<8) | p[13];	/* vertical image size (mm) */
+	/* hbord = p[15];		/* horizontal border (pixels) */
+	/* vbord = p[16];		/* vertical border (pixels) */
+
+	m->x = ha;
+	m->y = va;
+
+	m->ht = ha+hb;
+	m->shb = ha+hso;
+	m->ehb = ha+hso+hspw;
+
+	m->vt = va+vb;
+	m->vrs = va+vso;
+	m->vre = va+vso+vspw;
+
+	if(p[17] & 0x80)	/* interlaced */
+		m->interlace = 'v';
+
+	if(p[17] & 0x60)	/* some form of stereo monitor mode; no support */
+		return -1;
+
+	/*
+	 * Sync signal description.  I have no idea how to properly handle the 
+	 * first three cases, which I think are aimed at things other than
+	 * canonical SVGA monitors.
+	 */
+	switch((p[17] & 0x18)>>3) {
+	case 0:	/* analog composite sync signal*/
+	case 1:	/* bipolar analog composite sync signal */
+		/* p[17] & 0x04 means serration: hsync during vsync */
+		/* p[17] & 0x02 means sync pulse appears on RGB not just G */
+		break;
+
+	case 2:	/* digital composite sync signal */
+		/* p[17] & 0x04 means serration: hsync during vsync */
+		/* p[17] & 0x02 means hsync positive outside vsync */
+		break;
+
+	case 3:	/* digital separate sync signal; the norm */
+		m->vsync = (p[17] & 0x04) ? '+' : '-';
+		m->hsync = (p[17] & 0x02) ? '+' : '-';
+		break;
+	}
+	/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
+
+	rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
+
+	snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
+
+	return 0;
+}
+
+static int
+vesalookup(Mode *m, char *name)
+{
+	Mode **p;
+
+	for(p=vesamodes; *p; p++)
+		if(strcmp((*p)->name, name) == 0) {
+			*m = **p;
+			return 0;
+		}
+	return -1;
+}
+
+static int
+decodesti(Mode *m, uchar *p)
+{
+	int x, y, rr;
+	char str[20];
+
+	x = (p[0]+31)*8;
+	switch((p[1]>>6) & 3){
+	default:
+	case 0:
+		y = x;
+		break;
+	case 1:
+		y = (x*4)/3;
+		break;
+	case 2:
+		y = (x*5)/4;
+		break;
+	case 3:
+		y = (x*16)/9;
+		break;
+	}
+	rr = (p[1] & 0x1F) + 60;
+
+	sprint(str, "%dx%d@%dHz", x, y, rr);
+	return vesalookup(m, str);
+}
+
+int
+parseedid128(Edid *e, void *v)
+{
+	static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
+	uchar *p, *q, sum;
+	int dpms, estab, i, m, vid;
+	Mode mode;
+
+	memset(e, 0, sizeof *e);
+
+	p = (uchar*)v;
+	if(memcmp(p, magic, 8) != 0) {
+		werrstr("bad edid header");
+		return -1;
+	}
+
+	sum = 0;
+	for(i=0; i<128; i++) 
+		sum += p[i];
+	if(sum != 0) {
+		werrstr("bad edid checksum");
+		return -1;
+	}
+	p += 8;
+
+	assert(p == (uchar*)v+8);	/* assertion offsets from pp. 12-13 of the standard */
+	/*
+	 * Manufacturer name is three 5-bit ascii letters, packed
+	 * into a big endian [sic] short in big endian order.  The high bit is unused.
+	 */
+	i = (p[0]<<8) | p[1];
+	p += 2;
+	e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
+	e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
+	e->mfr[2] = 'A'-1 + (i & 0x1F);
+	e->mfr[3] = '\0';
+
+	/*
+	 * Product code is a little endian short.
+	 */
+	e->product = (p[1]<<8) | p[0];
+	p += 2;
+
+	/*
+	 * Serial number is a little endian long, 0x01010101 = unused.
+	 */
+	e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+	p += 4;
+	if(e->serial == 0x01010101)
+		e->serial = 0;
+
+	e->mfrweek = *p++;
+	e->mfryear = 1990 + *p++;
+
+	assert(p == (uchar*)v+8+10);
+	/*
+	 * Structure version is next two bytes: major.minor.
+	 */
+	e->version = *p++;
+	e->revision = *p++;
+
+	assert(p == (uchar*)v+8+10+2);
+	/*
+	 * Basic display parameters / features.
+	 */
+	/*
+	 * Video input definition byte: 0x80 tells whether it is
+	 * an analog or digital screen; we ignore the other bits.
+	 * See p. 15 of the standard.
+	 */
+	vid = *p++;
+	if(vid & 0x80)
+		e->flags |= Fdigital;
+
+	e->dxcm = *p++;
+	e->dycm = *p++;
+	e->gamma = 100 + *p++;
+	dpms = *p++;
+	if(dpms & 0x80)
+		e->flags |= Fdpmsstandby;
+	if(dpms & 0x40)
+		e->flags |= Fdpmssuspend;
+	if(dpms & 0x20)
+		e->flags |= Fdpmsactiveoff;
+	if((dpms & 0x18) == 0x00)
+		e->flags |= Fmonochrome;
+	if(dpms & 0x01)
+		e->flags |= Fgtf;
+
+	assert(p == (uchar*)v+8+10+2+5);
+	/*
+	 * Color characteristics currently ignored.
+	 */
+	p += 10;
+
+	assert(p == (uchar*)v+8+10+2+5+10);
+	/*
+	 * Established timings: a bitmask of 19 preset timings.
+	 */
+	estab = (p[0]<<16) | (p[1]<<8) | p[2];
+	p += 3;
+
+	for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
+		if(estab & m)
+			if(vesalookup(&mode, estabtime[i]) == 0)
+				e->modelist = addmode(e->modelist,  mode);
+
+	assert(p == (uchar*)v+8+10+2+5+10+3);
+	/*
+	 * Standard Timing Identifications: eight 2-byte selectors
+	 * of more standard timings.
+	 */
+	for(i=0; i<8; i++, p+=2)
+		if(decodesti(&mode, p+2*i) == 0)
+			e->modelist = addmode(e->modelist, mode);
+
+	assert(p == (uchar*)v+8+10+2+5+10+3+16);
+	/*
+	 * Detailed Timings
+	 */
+	for(i=0; i<4; i++, p+=18) {
+		if(p[0] || p[1]) {	/* detailed timing block: p[0] or p[1] != 0 */
+			if(decodedtb(&mode, p) == 0)
+				e->modelist = addmode(e->modelist, mode);
+		} else if(p[2]==0) {	/* monitor descriptor block */
+			switch(p[3]) {
+			case 0xFF:	/* monitor serial number (13-byte ascii, 0A terminated) */
+				if(q = memchr(p+5, 0x0A, 13))
+					*q = '\0';
+				memset(e->serialstr, 0, sizeof(e->serialstr));
+				strncpy(e->serialstr, (char*)p+5, 13);
+				break;
+			case 0xFE:	/* ascii string (13-byte ascii, 0A terminated) */
+				break;
+			case 0xFD:	/* monitor range limits */
+				e->rrmin = p[5];
+				e->rrmax = p[6];
+				e->hrmin = p[7]*1000;
+				e->hrmax = p[8]*1000;
+				if(p[9] != 0xFF)
+					e->pclkmax = p[9]*10*1000000;
+				break;
+			case 0xFC:	/* monitor name (13-byte ascii, 0A terminated) */
+				if(q = memchr(p+5, 0x0A, 13))
+					*q = '\0';
+				memset(e->name, 0, sizeof(e->name));
+				strncpy(e->name, (char*)p+5, 13);
+				break;
+			case 0xFB:	/* extra color point data */
+				break;
+			case 0xFA:	/* extra standard timing identifications */
+				for(i=0; i<6; i++)
+					if(decodesti(&mode, p+5+2*i) == 0)
+						e->modelist = addmode(e->modelist, mode);
+				break;
+			}
+		}
+	}
+
+	assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
+	return 0;
+}
+
+Flag edidflags[] = {
+	Fdigital, "digital",
+	Fdpmsstandby, "standby",
+	Fdpmssuspend, "suspend",
+	Fdpmsactiveoff, "activeoff",
+	Fmonochrome, "monochrome",
+	Fgtf, "gtf",
+	0
+};
+
+void
+printflags(Flag *f, int b)
+{
+	int i;
+
+	for(i=0; f[i].bit; i++)
+		if(f[i].bit & b)
+			Bprint(&stdout, " %s", f[i].desc);
+	Bprint(&stdout, "\n");
+}
+
+void
+printedid(Edid *e)
+{
+	Modelist *l;
+
+	printitem("edid", "mfr");
+	Bprint(&stdout, "%s\n", e->mfr);
+	printitem("edid", "serialstr");
+	Bprint(&stdout, "%s\n", e->serialstr);
+	printitem("edid", "name");
+	Bprint(&stdout, "%s\n", e->name);
+	printitem("edid", "product");
+	Bprint(&stdout, "%d\n", e->product);
+	printitem("edid", "serial");
+	Bprint(&stdout, "%lud\n", e->serial);
+	printitem("edid", "version");
+	Bprint(&stdout, "%d.%d\n", e->version, e->revision);
+	printitem("edid", "mfrdate");
+	Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
+	printitem("edid", "size (cm)");
+	Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
+	printitem("edid", "gamma");
+	Bprint(&stdout, "%.2f\n", e->gamma/100.);
+	printitem("edid", "vert (Hz)");
+	Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
+	printitem("edid", "horz (Hz)");
+	Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
+	printitem("edid", "pclkmax");
+	Bprint(&stdout, "%lud\n", e->pclkmax);
+	printitem("edid", "flags");
+	printflags(edidflags, e->flags);
+
+	for(l=e->modelist; l; l=l->next){
+		printitem("edid", l->name);
+		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",
+			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" : "");
+	}
+}
--- /dev/null
+++ b/sys/src/cmd/aux/vga/edid.h
@@ -1,0 +1,50 @@
+typedef struct Modelist Modelist;
+typedef struct Edid Edid;
+typedef struct Flag Flag;
+
+struct Edid {
+	char		mfr[4];		/* manufacturer */
+	char		serialstr[16];	/* serial number as string (in extended data) */
+	char		name[16];	/* monitor name as string (in extended data) */
+	ushort		product;	/* product code, 0 = unused */
+	ulong		serial;		/* serial number, 0 = unused */
+	uchar		version;	/* major version number */
+	uchar		revision;	/* minor version number */
+	uchar		mfrweek;	/* week of manufacture, 0 = unused */
+	int		mfryear;	/* year of manufacture, 0 = unused */
+	uchar 		dxcm;		/* horizontal image size in cm. */
+	uchar		dycm;		/* vertical image size in cm. */
+	int		gamma;		/* gamma*100 */
+	int		rrmin;		/* minimum vertical refresh rate */
+	int		rrmax;		/* maximum vertical refresh rate */
+	int		hrmin;		/* minimum horizontal refresh rate */
+	int		hrmax;		/* maximum horizontal refresh rate */
+	ulong		pclkmax;	/* maximum pixel clock */
+	int		flags;
+	Modelist	*modelist;	/* list of supported modes */
+};
+
+struct Modelist
+{
+	Mode;
+	Modelist *next;
+};
+
+struct Flag {
+	int bit;
+	char *desc;
+};
+
+enum {
+	Fdigital	= 1<<0,	/* is a digital display */
+	Fdpmsstandby	= 1<<1,	/* supports DPMS standby mode */
+	Fdpmssuspend	= 1<<2,	/* supports DPMS suspend mode */
+	Fdpmsactiveoff	= 1<<3,	/* supports DPMS active off mode */
+	Fmonochrome	= 1<<4,	/* is a monochrome display */
+	Fgtf		= 1<<5,	/* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
+};
+Flag	edidflags[];
+void	printflags(Flag *f, int b);
+
+int	parseedid128(Edid *e, void *v);
+void	printedid(Edid *e);
--- a/sys/src/cmd/aux/vga/igfx.c
+++ b/sys/src/cmd/aux/vga/igfx.c
@@ -4,6 +4,7 @@
 
 #include "pci.h"
 #include "vga.h"
+#include "edid.h"
 
 typedef struct Reg Reg;
 typedef struct Dpll Dpll;
@@ -142,6 +143,8 @@
 	Reg	ppstatus;
 
 	/* G45 */
+	Reg	gmbus[6];	/* GMBUSx */
+
 	Reg	sdvoc;
 	Reg	sdvob;
 
@@ -148,6 +151,7 @@
 	/* common */
 	Reg	adpa;
 	Reg	lvds;
+	Edid	*lvdsedid;
 
 	Reg	vgacntrl;
 };
@@ -305,6 +309,8 @@
 	return -1;
 }
 
+static void snarfedid(Igfx*);
+
 static void
 snarf(Vga* vga, Ctlr* ctlr)
 {
@@ -326,16 +332,14 @@
 			return;
 		}
 		vgactlpci(igfx->pci);
+		if((igfx->pci->mem[4].bar & 1) == 0)
+			error("%s: no pio bar\n", ctlr->name);
+		igfx->pio = igfx->pci->mem[4].bar & ~1;
 		if(1){
 			vgactlw("type", ctlr->name);
 			igfx->mmio = segattach(0, "igfxmmio", 0, igfx->pci->mem[0].size);
 			if(igfx->mmio == (u32int*)-1)
-				error("%s: segattach mmio failed: %r\n", ctlr->name);
-		} else {
-			if((igfx->pci->mem[4].bar & 1) == 0)
-				error("%s: no pio bar\n", ctlr->name);
-			igfx->pio = igfx->pci->mem[4].bar & ~1;
-			igfx->mmio = nil;
+				igfx->mmio = nil;	/* use pio */
 		}
 		vga->private = igfx;
 	}
@@ -356,6 +360,10 @@
 		igfx->sdvob		= snarfreg(igfx, 0x061140);
 		igfx->sdvoc		= snarfreg(igfx, 0x061160);
 
+		for(x=0; x<5; x++)
+			igfx->gmbus[x]	= snarfreg(igfx, 0x5100 + x*4);
+		igfx->gmbus[5]	= snarfreg(igfx, 0x5120);
+
 		igfx->pfit[0].ctrl	= snarfreg(igfx, 0x061230);
 		y = (igfx->pfit[0].ctrl.v >> 29) & 3;
 		if(igfx->pipe[y].pfit == nil)
@@ -441,6 +449,8 @@
 	for(x=0; x<igfx->npipe; x++)
 		snarfpipe(igfx, x);
 
+	snarfedid(igfx);
+
 	ctlr->flag |= Fsnarf;
 }
 
@@ -614,7 +624,7 @@
 	/* trans/pipe timing */
 	t->ht.v = (m->ht - 1)<<16 | (m->x - 1);
 	t->hb.v = t->ht.v;
-	t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1);
+	t->hs.v = (m->ehb - 1)<<16 | (m->shb - 1);
 	t->vt.v = (m->vt - 1)<<16 | (m->y - 1);
 	t->vb.v = t->vt.v;
 	t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1);
@@ -1165,6 +1175,81 @@
 	dumpreg(ctlr->name, "sdvoc", igfx->sdvoc);
 
 	dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
+
+	if(igfx->lvdsedid != nil)
+		printedid(igfx->lvdsedid);
+}
+
+enum {
+	GMBUSCP = 0,	/* Clock/Port selection */
+	GMBUSCS = 1,	/* Command/Status */
+	GMBUSST = 2,	/* Status Register */
+	GMBUSDB	= 3,	/* Data Buffer Register */
+	GMBUSIM = 4,	/* Interrupt Mask */
+	GMBUSIX = 5,	/* Index Register */
+};
+	
+static int
+gmbusread(Igfx *igfx, int portsel, int addr, uchar *data, int len)
+{
+	u32int x, y;
+	int n, t;
+
+	if(igfx->gmbus[GMBUSCP].a == 0)
+		return -1;
+
+	wr(igfx, igfx->gmbus[GMBUSCP].a, portsel);
+	wr(igfx, igfx->gmbus[GMBUSIX].a, 0);
+
+	/* bus cycle without index and stop, byte count, slave address, read */
+	wr(igfx, igfx->gmbus[GMBUSCS].a, 1<<30 | 5<<25 | len<<16 | addr<<1 | 1);
+
+	n = 0;
+	while(len > 0){
+		x = 0;
+		for(t=0; t<100; t++){
+			x = rr(igfx, igfx->gmbus[GMBUSST].a);
+			if(x & (1<<11))
+				break;
+			sleep(5);
+		}
+		if((x & (1<<11)) == 0)
+			return -1;
+
+		t = 4 - (x & 3);
+		if(t > len)
+			t = len;
+		len -= t;
+
+		y = rr(igfx, igfx->gmbus[GMBUSDB].a);
+		switch(t){
+		case 4:
+			data[n++] = y & 0xff, y >>= 8;
+		case 3:
+			data[n++] = y & 0xff, y >>= 8;
+		case 2:
+			data[n++] = y & 0xff, y >>= 8;
+		case 1:
+			data[n++] = y & 0xff;
+		}
+	}
+	return n;
+}
+
+static void
+snarfedid(Igfx *igfx)
+{
+	uchar buf[128];
+
+	if(igfx->type != TypeG45)
+		return;
+	if(gmbusread(igfx, 3, 0x50, buf, sizeof(buf)) != sizeof(buf))
+		return;
+	igfx->lvdsedid = malloc(sizeof(Edid));
+	if(parseedid128(igfx->lvdsedid, buf) != 0){
+		free(igfx->lvdsedid);
+		igfx->lvdsedid = nil;
+	}
 }
 
 Ctlr igfx = {
--- a/sys/src/cmd/aux/vga/mkfile
+++ b/sys/src/cmd/aux/vga/mkfile
@@ -15,6 +15,7 @@
 	cyber938x.$O\
 	data.$O\
 	db.$O\
+	edid.$O\
 	error.$O\
 	et4000.$O\
 	et4000hwgc.$O\
@@ -66,7 +67,8 @@
 
 HFILES=\
 	pci.h\
-	vga.h
+	vga.h\
+	edid.h\
 
 UPDATE=\
 	mkfile\
@@ -74,7 +76,6 @@
 	${OFILES:%.$O=%.c}\
 	/lib/vgadb\
 	riva_tbl.h\
-
 
 </sys/src/cmd/mkone
 
--- a/sys/src/cmd/aux/vga/vesa.c
+++ b/sys/src/cmd/aux/vga/vesa.c
@@ -6,11 +6,10 @@
 
 #include "pci.h"
 #include "vga.h"
+#include "edid.h"
 
 typedef struct Vbe Vbe;
 typedef struct Vmode Vmode;
-typedef struct Modelist Modelist;
-typedef struct Edid Edid;
 
 enum
 {
@@ -47,44 +46,6 @@
 	ulong	paddr;
 };
 
-struct Edid {
-	char		mfr[4];		/* manufacturer */
-	char		serialstr[16];	/* serial number as string (in extended data) */
-	char		name[16];		/* monitor name as string (in extended data) */
-	ushort	product;		/* product code, 0 = unused */
-	ulong	serial;		/* serial number, 0 = unused */
-	uchar	version;		/* major version number */
-	uchar	revision;		/* minor version number */
-	uchar	mfrweek;		/* week of manufacture, 0 = unused */
-	int		mfryear;		/* year of manufacture, 0 = unused */
-	uchar 	dxcm;		/* horizontal image size in cm. */
-	uchar	dycm;		/* vertical image size in cm. */
-	int		gamma;		/* gamma*100 */
-	int		rrmin;		/* minimum vertical refresh rate */
-	int		rrmax;		/* maximum vertical refresh rate */
-	int		hrmin;		/* minimum horizontal refresh rate */
-	int		hrmax;		/* maximum horizontal refresh rate */
-	ulong	pclkmax;		/* maximum pixel clock */
-	int		flags;
-
-	Modelist	*modelist;		/* list of supported modes */
-};
-
-struct Modelist
-{
-	Mode;
-	Modelist *next;
-};
-
-enum {
-	Fdigital = 1<<0,		/* is a digital display */
-	Fdpmsstandby = 1<<1,	/* supports DPMS standby mode */
-	Fdpmssuspend = 1<<2,	/* supports DPMS suspend mode */
-	Fdpmsactiveoff = 1<<3,	/* supports DPMS active off mode */
-	Fmonochrome = 1<<4,	/* is a monochrome display */
-	Fgtf = 1<<5,		/* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
-};
-
 #define WORD(p) ((p)[0] | ((p)[1]<<8))
 #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
@@ -93,8 +54,6 @@
 static Vbe *vbe;
 static Edid *edid;
 
-extern Mode *vesamodes[];
-
 Vbe *mkvbe(void);
 int vbecheck(Vbe*);
 uchar *vbemodes(Vbe*);
@@ -106,12 +65,11 @@
 int vbesnarf(Vbe*, Vga*);
 void vesaddc(void);
 int vbeddcedid(Vbe *vbe, Edid *e);
-void printedid(Edid*);
-void fixbios(Vbe*);
 uchar* vbesetup(Vbe*, Ureg*, int);
 int vbecall(Vbe*, Ureg*);
 int setdisplay(Vbe *vbe, int display);
 int getdisplay(Vbe *vbe);
+void fixbios(Vbe*);
 
 int
 dbvesa(Vga* vga)
@@ -463,12 +421,6 @@
  * VESA bios extension
  */
 
-typedef struct Flag Flag;
-struct Flag {
-	int bit;
-	char *desc;
-};
-
 static Flag capabilityflag[] = {
 	0x01, "8-bit-dac",
 	0x02, "not-vga",
@@ -544,18 +496,6 @@
 	[ModYUV]	"YUV",
 };
 
-
-static void
-printflags(Flag *f, int b)
-{
-	int i;
-
-	for(i=0; f[i].bit; i++)
-		if(f[i].bit & b)
-			Bprint(&stdout, " %s", f[i].desc);
-	Bprint(&stdout, "\n");
-}
-
 Vbe*
 mkvbe(void)
 {
@@ -928,16 +868,6 @@
 		error("vbesetmode: %r\n");
 }
 
-static Flag edidflags[] = {
-	Fdigital, "digital",
-	Fdpmsstandby, "standby",
-	Fdpmssuspend, "suspend",
-	Fdpmsactiveoff, "activeoff",
-	Fmonochrome, "monochrome",
-	Fgtf, "gtf",
-	0
-};
-
 int parseedid128(Edid *e, void *v);
 
 int
@@ -1001,405 +931,6 @@
 }
 
 void
-printedid(Edid *e)
-{
-	Modelist *l;
-
-	printitem("edid", "mfr");
-	Bprint(&stdout, "%s\n", e->mfr);
-	printitem("edid", "serialstr");
-	Bprint(&stdout, "%s\n", e->serialstr);
-	printitem("edid", "name");
-	Bprint(&stdout, "%s\n", e->name);
-	printitem("edid", "product");
-	Bprint(&stdout, "%d\n", e->product);
-	printitem("edid", "serial");
-	Bprint(&stdout, "%lud\n", e->serial);
-	printitem("edid", "version");
-	Bprint(&stdout, "%d.%d\n", e->version, e->revision);
-	printitem("edid", "mfrdate");
-	Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
-	printitem("edid", "size (cm)");
-	Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
-	printitem("edid", "gamma");
-	Bprint(&stdout, "%.2f\n", e->gamma/100.);
-	printitem("edid", "vert (Hz)");
-	Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
-	printitem("edid", "horz (Hz)");
-	Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
-	printitem("edid", "pclkmax");
-	Bprint(&stdout, "%lud\n", e->pclkmax);
-	printitem("edid", "flags");
-	printflags(edidflags, e->flags);
-
-	for(l=e->modelist; l; l=l->next){
-		printitem("edid", l->name);
-		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",
-			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" : "");
-	}
-}
-
-Modelist*
-addmode(Modelist *l, Mode m)
-{
-	int rr;
-	Modelist **lp;
-
-	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
-	snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
-
-	if(m.shs == 0)
-		m.shs = m.shb;
-	if(m.ehs == 0)
-		m.ehs = m.ehb;
-	if(m.vbs == 0)
-		m.vbs = m.vrs;
-	if(m.vbe == 0)
-		m.vbe = m.vbs+1;
-
-	for(lp=&l; *lp; lp=&(*lp)->next){
-		if(strcmp((*lp)->name, m.name) == 0){
-			(*lp)->Mode = m;
-			return l;
-		}
-	}
-
-	*lp = alloc(sizeof(**lp));
-	(*lp)->Mode = m;
-	return l;
-}
-
-/*
- * Parse VESA EDID information.  Based on the VESA
- * Extended Display Identification Data standard, Version 3,
- * November 13, 1997.  See /public/doc/vesa/edidv3.pdf.
- *
- * This only handles 128-byte EDID blocks.  Until I find
- * a monitor that produces 256-byte blocks, I'm not going
- * to try to decode them.
- */
-
-/*
- * Established timings block.  There is a bitmap
- * that says whether each mode is supported.  Most
- * of these have VESA definitions.  Those that don't are marked
- * as such, and we ignore them (the lookup fails).
- */
-static char *estabtime[] = {
-	"720x400@70Hz",	/* non-VESA: IBM, VGA */
-	"720x400@88Hz",	/* non-VESA: IBM, XGA2 */
-	"640x480@60Hz",
-	"640x480@67Hz",	/* non-VESA: Apple, Mac II */
-	"640x480@72Hz",
-	"640x480@75Hz",
-	"800x600@56Hz",
-	"800x600@60Hz",
-
-	"800x600@72Hz",
-	"800x600@75Hz",
-	"832x624@75Hz",	/* non-VESA: Apple, Mac II */
-	"1024x768i@87Hz",	/* non-VESA: IBM */
-	"1024x768@60Hz",
-	"1024x768@70Hz",
-	"1024x768@75Hz",
-	"1280x1024@75Hz",
-
-	"1152x870@75Hz",	/* non-VESA: Apple, Mac II */
-};
-
-/*
- * Decode the EDID detailed timing block.  See pp. 20-21 of the standard.
- */
-static int
-decodedtb(Mode *m, uchar *p)
-{
-	int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
-	/* int dxmm, dymm, hbord, vbord; */
-
-	memset(m, 0, sizeof *m);
-
-	m->frequency = ((p[1]<<8) | p[0]) * 10000;
-
-	ha = ((p[4] & 0xF0)<<4) | p[2];		/* horizontal active */
-	hb = ((p[4] & 0x0F)<<8) | p[3];		/* horizontal blanking */
-	va = ((p[7] & 0xF0)<<4) | p[5];		/* vertical active */
-	vb = ((p[7] & 0x0F)<<8) | p[6];		/* vertical blanking */
-	hso = ((p[11] & 0xC0)<<2) | p[8];	/* horizontal sync offset */
-	hspw = ((p[11] & 0x30)<<4) | p[9];	/* horizontal sync pulse width */
-	vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4);	/* vertical sync offset */
-	vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F);		/* vertical sync pulse width */
-
-	/* dxmm = (p[14] & 0xF0)<<4) | p[12]; 	/* horizontal image size (mm) */
-	/* dymm = (p[14] & 0x0F)<<8) | p[13];	/* vertical image size (mm) */
-	/* hbord = p[15];		/* horizontal border (pixels) */
-	/* vbord = p[16];		/* vertical border (pixels) */
-
-	m->x = ha;
-	m->y = va;
-
-	m->ht = ha+hb;
-	m->shs = ha;
-	m->shb = ha+hso;
-	m->ehb = ha+hso+hspw;
-	m->ehs = ha+hb;
-
-	m->vt = va+vb;
-	m->vbs = va;	
-	m->vrs = va+vso;
-	m->vre = va+vso+vspw;
-	m->vbe = va+vb;
-
-	if(p[17] & 0x80)	/* interlaced */
-		m->interlace = 'v';
-
-	if(p[17] & 0x60)	/* some form of stereo monitor mode; no support */
-		return -1;
-
-	/*
-	 * Sync signal description.  I have no idea how to properly handle the 
-	 * first three cases, which I think are aimed at things other than
-	 * canonical SVGA monitors.
-	 */
-	switch((p[17] & 0x18)>>3) {
-	case 0:	/* analog composite sync signal*/
-	case 1:	/* bipolar analog composite sync signal */
-		/* p[17] & 0x04 means serration: hsync during vsync */
-		/* p[17] & 0x02 means sync pulse appears on RGB not just G */
-		break;
-
-	case 2:	/* digital composite sync signal */
-		/* p[17] & 0x04 means serration: hsync during vsync */
-		/* p[17] & 0x02 means hsync positive outside vsync */
-		break;
-
-	case 3:	/* digital separate sync signal; the norm */
-		m->vsync = (p[17] & 0x04) ? '+' : '-';
-		m->hsync = (p[17] & 0x02) ? '+' : '-';
-		break;
-	}
-	/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
-
-	rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
-
-	snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
-
-	return 0;
-}
-
-int
-vesalookup(Mode *m, char *name)
-{
-	Mode **p;
-
-	for(p=vesamodes; *p; p++)
-		if(strcmp((*p)->name, name) == 0) {
-			*m = **p;
-			return 0;
-		}
-
-	return -1;
-}
-
-static int
-decodesti(Mode *m, uchar *p)
-{
-	int x, y, rr;
-	char str[20];
-
-	x = (p[0]+31)*8;
-	switch((p[1]>>6) & 3){
-	default:
-	case 0:
-		y = x;
-		break;
-	case 1:
-		y = (x*4)/3;
-		break;
-	case 2:
-		y = (x*5)/4;
-		break;
-	case 3:
-		y = (x*16)/9;
-		break;
-	}
-	rr = (p[1] & 0x1F) + 60;
-
-	sprint(str, "%dx%d@%dHz", x, y, rr);
-	return vesalookup(m, str);
-}
-
-int
-parseedid128(Edid *e, void *v)
-{
-	static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
-	uchar *p, *q, sum;
-	int dpms, estab, i, m, vid;
-	Mode mode;
-
-	memset(e, 0, sizeof *e);
-
-	p = (uchar*)v;
-	if(memcmp(p, magic, 8) != 0) {
-		werrstr("bad edid header");
-		return -1;
-	}
-
-	sum = 0;
-	for(i=0; i<128; i++) 
-		sum += p[i];
-	if(sum != 0) {
-		werrstr("bad edid checksum");
-		return -1;
-	}
-	p += 8;
-
-	assert(p == (uchar*)v+8);	/* assertion offsets from pp. 12-13 of the standard */
-	/*
-	 * Manufacturer name is three 5-bit ascii letters, packed
-	 * into a big endian [sic] short in big endian order.  The high bit is unused.
-	 */
-	i = (p[0]<<8) | p[1];
-	p += 2;
-	e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
-	e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
-	e->mfr[2] = 'A'-1 + (i & 0x1F);
-	e->mfr[3] = '\0';
-
-	/*
-	 * Product code is a little endian short.
-	 */
-	e->product = (p[1]<<8) | p[0];
-	p += 2;
-
-	/*
-	 * Serial number is a little endian long, 0x01010101 = unused.
-	 */
-	e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
-	p += 4;
-	if(e->serial == 0x01010101)
-		e->serial = 0;
-
-	e->mfrweek = *p++;
-	e->mfryear = 1990 + *p++;
-
-	assert(p == (uchar*)v+8+10);
-	/*
-	 * Structure version is next two bytes: major.minor.
-	 */
-	e->version = *p++;
-	e->revision = *p++;
-
-	assert(p == (uchar*)v+8+10+2);
-	/*
-	 * Basic display parameters / features.
-	 */
-	/*
-	 * Video input definition byte: 0x80 tells whether it is
-	 * an analog or digital screen; we ignore the other bits.
-	 * See p. 15 of the standard.
-	 */
-	vid = *p++;
-	if(vid & 0x80)
-		e->flags |= Fdigital;
-
-	e->dxcm = *p++;
-	e->dycm = *p++;
-	e->gamma = 100 + *p++;
-	dpms = *p++;
-	if(dpms & 0x80)
-		e->flags |= Fdpmsstandby;
-	if(dpms & 0x40)
-		e->flags |= Fdpmssuspend;
-	if(dpms & 0x20)
-		e->flags |= Fdpmsactiveoff;
-	if((dpms & 0x18) == 0x00)
-		e->flags |= Fmonochrome;
-	if(dpms & 0x01)
-		e->flags |= Fgtf;
-
-	assert(p == (uchar*)v+8+10+2+5);
-	/*
-	 * Color characteristics currently ignored.
-	 */
-	p += 10;
-
-	assert(p == (uchar*)v+8+10+2+5+10);
-	/*
-	 * Established timings: a bitmask of 19 preset timings.
-	 */
-	estab = (p[0]<<16) | (p[1]<<8) | p[2];
-	p += 3;
-
-	for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
-		if(estab & m)
-			if(vesalookup(&mode, estabtime[i]) == 0)
-				e->modelist = addmode(e->modelist,  mode);
-
-	assert(p == (uchar*)v+8+10+2+5+10+3);
-	/*
-	 * Standard Timing Identifications: eight 2-byte selectors
-	 * of more standard timings.
-	 */
-	for(i=0; i<8; i++, p+=2)
-		if(decodesti(&mode, p+2*i) == 0)
-			e->modelist = addmode(e->modelist, mode);
-
-	assert(p == (uchar*)v+8+10+2+5+10+3+16);
-	/*
-	 * Detailed Timings
-	 */
-	for(i=0; i<4; i++, p+=18) {
-		if(p[0] || p[1]) {	/* detailed timing block: p[0] or p[1] != 0 */
-			if(decodedtb(&mode, p) == 0)
-				e->modelist = addmode(e->modelist, mode);
-		} else if(p[2]==0) {	/* monitor descriptor block */
-			switch(p[3]) {
-			case 0xFF:	/* monitor serial number (13-byte ascii, 0A terminated) */
-				if(q = memchr(p+5, 0x0A, 13))
-					*q = '\0';
-				memset(e->serialstr, 0, sizeof(e->serialstr));
-				strncpy(e->serialstr, (char*)p+5, 13);
-				break;
-			case 0xFE:	/* ascii string (13-byte ascii, 0A terminated) */
-				break;
-			case 0xFD:	/* monitor range limits */
-				e->rrmin = p[5];
-				e->rrmax = p[6];
-				e->hrmin = p[7]*1000;
-				e->hrmax = p[8]*1000;
-				if(p[9] != 0xFF)
-					e->pclkmax = p[9]*10*1000000;
-				break;
-			case 0xFC:	/* monitor name (13-byte ascii, 0A terminated) */
-				if(q = memchr(p+5, 0x0A, 13))
-					*q = '\0';
-				memset(e->name, 0, sizeof(e->name));
-				strncpy(e->name, (char*)p+5, 13);
-				break;
-			case 0xFB:	/* extra color point data */
-				break;
-			case 0xFA:	/* extra standard timing identifications */
-				for(i=0; i<6; i++)
-					if(decodesti(&mode, p+5+2*i) == 0)
-						e->modelist = addmode(e->modelist, mode);
-				break;
-			}
-		}
-	}
-
-	assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
-	return 0;
-}
-
-void
 fixbios(Vbe *vbe)
 {
 	uchar *p;
@@ -1424,3 +955,4 @@
 		}
 	}
 }
+
--- a/sys/src/cmd/aux/vga/vga.h
+++ b/sys/src/cmd/aux/vga/vga.h
@@ -112,8 +112,8 @@
 	int	vrs;			/* Vertical Retrace Start (Crt10) */
 	int	vre;			/* Vertical Retrace End (Crt11) */
 
-	int		vbs;		/* optional Vertical Blank Start */
-	int		vbe;		/* optional Vertical Blank End */
+	int	vbs;			/* optional Vertical Blank Start */
+	int	vbe;			/* optional Vertical Blank End */
 	
 	ulong	videobw;
 
@@ -252,6 +252,9 @@
 extern Mode* dbmode(char*, char*, char*);
 extern void dbdumpmode(Mode*);
 
+/* edid.c */
+extern Mode* edidmode(uchar *, int);
+
 /* error.c */
 extern void error(char*, ...);
 extern void trace(char*, ...);
@@ -437,6 +440,9 @@
 extern int dbvesa(Vga*);
 extern Mode *dbvesamode(char*);
 extern void vesatextmode(void);
+
+/* vesadb.c */
+extern Mode *vesamodes[];
 
 /* virge.c */
 extern Ctlr virge;