ref: b4fcd2d68f4a843c83c5e18bac8bd6cab98875c7
parent: a4650bdf834911d0d3b06d630f960fbd0dd1c9ed
parent: 2cdadb1b2f7233a6642875e6b5fe6e0963c7171c
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jan 10 22:37:07 EST 2015
merge
--- /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;