diff --git a/sys/src/cmd/aux/vga/edid.c b/sys/src/cmd/aux/vga/edid.c new file mode 100644 index 000000000..ba95d2611 --- /dev/null +++ b/sys/src/cmd/aux/vga/edid.c @@ -0,0 +1,414 @@ +#include +#include +#include +#include + +#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>=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" : ""); + } +} diff --git a/sys/src/cmd/aux/vga/edid.h b/sys/src/cmd/aux/vga/edid.h new file mode 100644 index 000000000..84321dbba --- /dev/null +++ b/sys/src/cmd/aux/vga/edid.h @@ -0,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); diff --git a/sys/src/cmd/aux/vga/igfx.c b/sys/src/cmd/aux/vga/igfx.c index c5c7e0713..e3e8740dc 100644 --- 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,12 +143,15 @@ struct Igfx { Reg ppstatus; /* G45 */ + Reg gmbus[6]; /* GMBUSx */ + Reg sdvoc; Reg sdvob; /* common */ Reg adpa; Reg lvds; + Edid *lvdsedid; Reg vgacntrl; }; @@ -305,6 +309,8 @@ devtype(Igfx *igfx) return -1; } +static void snarfedid(Igfx*); + static void snarf(Vga* vga, Ctlr* ctlr) { @@ -326,16 +332,14 @@ snarf(Vga* vga, Ctlr* ctlr) 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 @@ snarf(Vga* vga, Ctlr* ctlr) 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 @@ snarf(Vga* vga, Ctlr* ctlr) for(x=0; xnpipe; x++) snarfpipe(igfx, x); + snarfedid(igfx); + ctlr->flag |= Fsnarf; } @@ -614,7 +624,7 @@ inittrans(Trans *t, Mode *m) /* 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 @@ dump(Vga* vga, Ctlr* ctlr) 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 = { diff --git a/sys/src/cmd/aux/vga/mkfile b/sys/src/cmd/aux/vga/mkfile index cd57744b1..6d821110d 100644 --- a/sys/src/cmd/aux/vga/mkfile +++ b/sys/src/cmd/aux/vga/mkfile @@ -15,6 +15,7 @@ OFILES=\ cyber938x.$O\ data.$O\ db.$O\ + edid.$O\ error.$O\ et4000.$O\ et4000hwgc.$O\ @@ -66,7 +67,8 @@ OFILES=\ HFILES=\ pci.h\ - vga.h + vga.h\ + edid.h\ UPDATE=\ mkfile\ @@ -75,7 +77,6 @@ UPDATE=\ /lib/vgadb\ riva_tbl.h\ - >8 @@ -93,8 +54,6 @@ enum { static Vbe *vbe; static Edid *edid; -extern Mode *vesamodes[]; - Vbe *mkvbe(void); int vbecheck(Vbe*); uchar *vbemodes(Vbe*); @@ -106,12 +65,11 @@ void vbeprintmodeinfo(Vbe*, int, char*); 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 @@ Ctlr softhwgc = { * 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 @@ static char *modelstr[] = { [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 @@ vesatextmode(void) 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 @@ -1000,405 +930,6 @@ setdisplay(Vbe *vbe, int display) return -1; } -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>=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) { @@ -1424,3 +955,4 @@ fixbios(Vbe *vbe) } } } + diff --git a/sys/src/cmd/aux/vga/vga.h b/sys/src/cmd/aux/vga/vga.h index f479edf30..b9f778ae5 100644 --- a/sys/src/cmd/aux/vga/vga.h +++ b/sys/src/cmd/aux/vga/vga.h @@ -112,8 +112,8 @@ typedef struct Mode { 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 int dbctlr(char*, Vga*); 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*, ...); @@ -438,6 +441,9 @@ extern int dbvesa(Vga*); extern Mode *dbvesamode(char*); extern void vesatextmode(void); +/* vesadb.c */ +extern Mode *vesamodes[]; + /* virge.c */ extern Ctlr virge;