shithub: riscv

Download patch

ref: 21570a47195d90b1dc2a3634d8042929543599d3
parent: 20f2add592f8463ef6820afd07a342acb26da304
author: cinap_lenrek <[email protected]>
date: Mon Feb 9 14:17:04 EST 2015

igfx: displayport support for ivy bridge

--- a/sys/src/cmd/aux/vga/igfx.c
+++ b/sys/src/cmd/aux/vga/igfx.c
@@ -26,6 +26,15 @@
 	TypeIVB,		/* Ivy Bridge */
 };
 
+enum {
+	PortVGA	= 0,		/* adpa */
+	PortLCD	= 1,		/* lvds */
+	PortDPA	= 2,
+	PortDPB	= 3,
+	PortDPC	= 4,
+	PortDPD	= 5,
+};
+
 struct Reg {
 	u32int	a;		/* address or 0 when invalid */
 	u32int	v;		/* value */
@@ -54,6 +63,8 @@
 	Reg	conf;		/* pipe/trans CONF_x */
 	Reg	chicken;	/* workarround register */
 
+	Reg	dpctl;		/* TRANS_DP_CTL_x */
+
 	Dpll	*dpll;		/* this transcoders dpll */
 };
 
@@ -66,6 +77,8 @@
 	Reg	ctl;
 	Reg	auxctl;
 	Reg	auxdat[5];
+
+	uchar	dpcd[256];
 };
 
 struct Fdi {
@@ -76,8 +89,6 @@
 	Reg	rxctl;		/* FDI_RX_CTL */
 	Reg	rxmisc;		/* FDI_RX_MISC */
 	Reg	rxtu[2];	/* FDI_RX_TUSIZE */
-
-	Reg	dpctl;		/* TRANS_DP_CTL_x */
 };
 
 struct Pfit {
@@ -125,6 +136,7 @@
 	u32int	*mmio;
 
 	int	type;
+	int	cdclk;		/* core display clock in mhz */
 
 	int	npipe;
 	Pipe	pipe[4];
@@ -314,8 +326,11 @@
 	return -1;
 }
 
-static Edid* snarfedid(Igfx*, int port, int addr);
+static Edid* snarfgmedid(Igfx*, int port, int addr);
+static Edid* snarfdpedid(Igfx*, Dp *dp, int addr);
 
+static int enabledp(Igfx*, Dp*);
+
 static void
 snarf(Vga* vga, Ctlr* ctlr)
 {
@@ -353,6 +368,7 @@
 	switch(igfx->type){
 	case TypeG45:
 		igfx->npipe = 2;	/* A,B */
+		igfx->cdclk = 200;	/* MHz */
 
 		igfx->dpll[0].ctrl	= snarfreg(igfx, 0x06014);
 		igfx->dpll[0].fp0	= snarfreg(igfx, 0x06040);
@@ -383,6 +399,7 @@
 
 	case TypeIVB:
 		igfx->npipe = 3;	/* A,B,C */
+		igfx->cdclk = 400;	/* MHz */
 
 		igfx->dpll[0].ctrl	= snarfreg(igfx, 0xC6014);
 		igfx->dpll[0].fp0	= snarfreg(igfx, 0xC6040);
@@ -462,12 +479,31 @@
 	for(x=0; x<igfx->npipe; x++)
 		snarfpipe(igfx, x);
 
-	vga->edid[0] = snarfedid(igfx, 2, 0x50);
-	vga->edid[1] = snarfedid(igfx, 3, 0x50);
-	if(vga->edid[1] != nil){
+	for(x=0; x<nelem(vga->edid); x++){
 		Modelist *l;
-		for(l = vga->edid[1]->modelist; l != nil; l = l->next)
-			l->attr = mkattr(l->attr, "lcd", "1");
+
+		switch(x){
+		case PortVGA:
+			vga->edid[x] = snarfgmedid(igfx, 2, 0x50);
+			break;
+		case PortLCD:
+			vga->edid[x] = snarfgmedid(igfx, 3, 0x50);
+			if(vga->edid[x] == nil)
+				continue;
+			for(l = vga->edid[x]->modelist; l != nil; l = l->next)
+				l->attr = mkattr(l->attr, "lcd", "1");
+			break;
+		case PortDPA:
+		case PortDPB:
+		case PortDPC:
+		case PortDPD:
+			vga->edid[x] = snarfdpedid(igfx, &igfx->dp[x-PortDPA], 0x50);
+			break;
+		}
+		if(vga->edid[x] == nil)
+			continue;
+		for(l = vga->edid[x]->modelist; l != nil; l = l->next)
+			l->attr = mkattr(l->attr, "display", "%d", x+1);
 	}
 
 	ctlr->flag |= Fsnarf;
@@ -538,7 +574,7 @@
 }
 
 static int
-initdpll(Igfx *igfx, int x, int freq, int islvds, int ishdmi)
+initdpll(Igfx *igfx, int x, int freq, int port)
 {
 	int cref, m1, m2, n, p1, p2;
 	Dpll *dpll;
@@ -548,7 +584,7 @@
 		/* PLL Reference Input Select */
 		dpll = igfx->pipe[x].dpll;
 		dpll->ctrl.v &= ~(3<<13);
-		dpll->ctrl.v |= (islvds ? 3 : 0) << 13;
+		dpll->ctrl.v |= (port == PortLCD ? 3 : 0) << 13;
 		break;
 	case TypeIVB:
 		/* transcoder dpll enable */
@@ -562,7 +598,7 @@
 		igfx->drefctl.v &= ~(3<<7);
 		igfx->drefctl.v &= ~3;
 
-		if(islvds){
+		if(port == PortLCD){
 			igfx->drefctl.v |= 2<<11;
 			igfx->drefctl.v |= 1;
 		} else {
@@ -577,7 +613,7 @@
 		 */
 		dpll = igfx->pipe[x].fdi->dpll;
 		dpll->ctrl.v &= ~(7<<13);
-		dpll->ctrl.v |= (islvds ? 3 : 0) << 13;
+		dpll->ctrl.v |= (port == PortLCD ? 3 : 0) << 13;
 		break;
 	default:
 		return -1;
@@ -586,15 +622,19 @@
 
 	/* Dpll Mode select */
 	dpll->ctrl.v &= ~(3<<26);
-	dpll->ctrl.v |= (islvds ? 2 : 1)<<26;
+	dpll->ctrl.v |= (port == PortLCD ? 2 : 1)<<26;
 
 	/* P2 Clock Divide */
-	dpll->ctrl.v &= ~(3<<24);	
-	if(islvds){
+	dpll->ctrl.v &= ~(3<<24);
+	if(port == PortLCD){
 		p2 = 14;
 		if(genpll(freq, cref, p2, &m1, &m2, &n, &p1) < 0)
 			return -1;
 	} else {
+		/* generate 270MHz clock for displayport */
+		if(port >= PortDPA)
+			freq = 270*MHz;
+
 		p2 = 10;
 		if(freq > 270*MHz){
 			p2 >>= 1;
@@ -608,7 +648,7 @@
 	dpll->ctrl.v |= (1<<31);
 
 	/* Dpll Serial DVO High Speed IO clock Enable */
-	if(ishdmi)
+	if(port >= PortDPA)
 		dpll->ctrl.v |= (1<<30);
 	else
 		dpll->ctrl.v &= ~(1<<30);
@@ -753,11 +793,12 @@
 static void
 init(Vga* vga, Ctlr* ctlr)
 {
-	int x, islvds;
+	int x, port;
 	char *val;
 	Igfx *igfx;
 	Pipe *p;
 	Mode *m;
+	Reg *r;
 
 	m = vga->mode;
 	if(m->z != 32)
@@ -768,7 +809,7 @@
 	/* disable vga */
 	igfx->vgacntrl.v |= (1<<31);
 
-	/* disable all pipes adpa and lvds */
+	/* disable all pipes and ports */
 	igfx->ppcontrol.v &= 0xFFFF;
 	igfx->ppcontrol.v &= ~5;
 	igfx->lvds.v &= ~(1<<31);
@@ -775,15 +816,55 @@
 	igfx->adpa.v &= ~(1<<31);
 	if(igfx->type == TypeG45)
 		igfx->adpa.v |= (3<<10);	/* Monitor DPMS: off */
-
-	for(x=0; x<igfx->npipe; x++)
+	for(x=0; x<nelem(igfx->dp); x++)
+		igfx->dp[x].ctl.v &= ~(1<<31);
+	for(x=0; x<nelem(igfx->hdmi); x++)
+		igfx->hdmi[x].ctl.v &= ~(1<<31);
+	for(x=0; x<igfx->npipe; x++){
+		/* disable displayport transcoders */
+		igfx->pipe[x].dpctl.v &= ~(1<<31);
+		igfx->pipe[x].dpctl.v |= (3<<29);
+		igfx->pipe[x].fdi->dpctl.v &= ~(1<<31);
+		igfx->pipe[x].fdi->dpctl.v |= (3<<29);
+		/* disable transcoder/pipe */
 		igfx->pipe[x].conf.v &= ~(1<<31);
+	}
 
-	islvds = 0;
-	if((val = dbattr(m->attr, "lcd")) != nil && atoi(val) != 0){
-		islvds = 1;
+	if((val = dbattr(m->attr, "display")) != nil)
+		port = atoi(val)-1;
+	else if(dbattr(m->attr, "lcd") != nil)
+		port = PortLCD;
+	else
+		port = PortVGA;
 
+	trace("%s: display #%d\n", ctlr->name, port+1);
+
+	switch(port){
+	default:
+	Badport:
+		error("%s: display #%d not supported\n", ctlr->name, port+1);
+		break;
+
+	case PortVGA:
 		if(igfx->npipe > 2)
+			x = (igfx->adpa.v >> 29) & 3;
+		else
+			x = (igfx->adpa.v >> 30) & 1;
+		igfx->adpa.v |= (1<<31);
+		if(igfx->type == TypeG45){
+			igfx->adpa.v &= ~(3<<10);	/* Monitor DPMS: on */
+
+			igfx->adpa.v &= ~(1<<15);	/* ADPA Polarity Select */
+			igfx->adpa.v |= 3<<3;
+			if(m->hsync == '-')
+				igfx->adpa.v ^= 1<<3;
+			if(m->vsync == '-')
+				igfx->adpa.v ^= 1<<4;
+		}
+		break;
+
+	case PortLCD:
+		if(igfx->npipe > 2)
 			x = (igfx->lvds.v >> 29) & 3;
 		else
 			x = (igfx->lvds.v >> 30) & 1;
@@ -801,23 +882,53 @@
 
 			igfx->lvds.v |= (1<<15);	/* border enable */
 		}
+		break;
 
-	} else {
-		if(igfx->npipe > 2)
-			x = (igfx->adpa.v >> 29) & 3;
-		else
-			x = (igfx->adpa.v >> 30) & 1;
-		igfx->adpa.v |= (1<<31);
-		if(igfx->type == TypeG45){
-			igfx->adpa.v &= ~(3<<10);	/* Monitor DPMS: on */
+	case PortDPA:
+	case PortDPB:
+	case PortDPC:
+	case PortDPD:
+		r = &igfx->dp[port - PortDPA].ctl;
+		if(r->a == 0)
+			goto Badport;
+		/* port enable */
+		r->v |= 1<<31;
+		/* port width selection: x1 Mode */
+		r->v &= ~(7<<19);
 
-			igfx->adpa.v &= ~(1<<15);	/* ADPA Polarity Select */
-			igfx->adpa.v |= 3<<3;
-			if(m->hsync == '-')
-				igfx->adpa.v ^= 1<<3;
-			if(m->vsync == '-')
-				igfx->adpa.v ^= 1<<4;
+		/* port reversal: off */
+		r->v &= ~(1<<15);
+		/* reserved MBZ */
+		r->v &= ~(15<<11);
+		/* use PIPE_A for displayport */
+		x = 0;
+		/* displayport transcoder */
+		if(port == PortDPA){
+			/* pll frequency: 270mhz */
+			r->v &= ~(3<<16);
+			/* pll enable */
+			r->v |= 1<<14;
+			/* pipe select */
+			r->v &= ~(3<<29);
+			r->v |= x<<29;
+		} else if(igfx->pipe[x].fdi->dpctl.a != 0){
+			/* audio output: disable */
+			r->v &= ~(1<<6);
+			/* transcoder displayport configuration */
+			r = &igfx->pipe[x].fdi->dpctl;
+			/* transcoder enable */
+			r->v |= 1<<31;
+			/* port select: B,C,D */
+			r->v &= ~(3<<29);
+			r->v |= (port-PortDPB)<<29;
 		}
+		/* sync polarity */
+		r->v |= 3<<3;
+		if(m->hsync == '-')
+			r->v ^= 1<<3;
+		if(m->vsync == '-')
+			r->v ^= 1<<4;
+		break;
 	}
 	p = &igfx->pipe[x];
 
@@ -849,7 +960,7 @@
 	p->cur->pos.v = 0;
 	p->cur->base.v = 0;
 
-	if(initdpll(igfx, x, m->frequency, islvds, 0) < 0)
+	if(initdpll(igfx, x, m->frequency, port) < 0)
 		error("%s: frequency %d out of range\n", ctlr->name, m->frequency);
 
 	initpipe(p, m);
@@ -896,6 +1007,9 @@
 	/* workarround: set timing override bit */
 	csr(igfx, t->chicken.a, 0, 1<<31);
 
+	/* enable displayport transcoder */
+	loadreg(igfx, t->dpctl);
+
 	/* enable trans/pipe */
 	t->conf.v |= (1<<31);
 	t->conf.v &= ~(1<<30);
@@ -1003,6 +1117,9 @@
 {
 	int i;
 
+	/* disable displayport transcoder */
+	csr(igfx, t->dpctl.a, 1<<31, 3<<29);
+
 	/* disable transcoder / pipe */
 	csr(igfx, t->conf.a, 1<<31, 0);
 	for(i=0; i<100; i++){
@@ -1047,9 +1164,6 @@
 	csr(igfx, p->fdi->txctl.a, 1<<31 | 1<<10, 0);
 	csr(igfx, p->fdi->rxctl.a, 1<<31 | 1<<10, 0);
 
-	/* disable displayport transcoder */
-	csr(igfx, p->fdi->dpctl.a, 1<<31, 3<<29);
-
 	/* disable pch transcoder */
 	disabletrans(igfx, p->fdi);
 
@@ -1124,14 +1238,9 @@
 	loadreg(igfx, igfx->adpa);
 	loadreg(igfx, igfx->sdvob);
 	loadreg(igfx, igfx->sdvoc);
-	for(x = 0; x < nelem(igfx->dp); x++)
-		loadreg(igfx, igfx->dp[x].ctl);
-	for(x=0; x<nelem(igfx->hdmi); x++){
-		loadreg(igfx, igfx->hdmi[x].bufctl[0]);
-		loadreg(igfx, igfx->hdmi[x].bufctl[1]);
-		loadreg(igfx, igfx->hdmi[x].bufctl[2]);
-		loadreg(igfx, igfx->hdmi[x].bufctl[3]);
-		loadreg(igfx, igfx->hdmi[x].ctl);
+	for(x = 0; x < nelem(igfx->dp); x++){
+		if(enabledp(igfx, &igfx->dp[x]) < 0)
+			ctlr->flag |= Ferror;
 	}
 
 	/* program lcd power */
@@ -1151,6 +1260,23 @@
 }
 
 static void
+dumphex(char *name, char *item, uchar *data, int len)
+{
+	int i;
+
+	for(i=0; i<len; i++){
+		if((i & 15) == 0){
+			if(i > 0)
+				Bprint(&stdout, "\n");
+			printitem(name, item);
+			Bprint(&stdout, " [%.2x] =", i);
+		}
+		Bprint(&stdout, " %.2X", data[i]);
+	}
+	Bprint(&stdout, "\n");
+}
+
+static void
 dumptiming(char *name, Trans *t)
 {
 	int tu, m, n;
@@ -1200,6 +1326,8 @@
 	dumpreg(name, "vb", t->vb);
 	dumpreg(name, "vs", t->vs);
 	dumpreg(name, "vss", t->vss);
+
+	dumpreg(name, "dpctl", t->dpctl);
 }
 
 static void
@@ -1221,7 +1349,6 @@
 	dumpreg(name, "rxmisc", p->fdi->rxmisc);
 	dumpreg(name, "rxtu1", p->fdi->rxtu[0]);
 	dumpreg(name, "rxtu2", p->fdi->rxtu[1]);
-	dumpreg(name, "dpctl", p->fdi->dpctl);
 
 	snprint(name, sizeof(name), "%s dsp %c", igfx->ctlr->name, 'a'+x);
 	dumpreg(name, "cntr", p->dsp->cntr);
@@ -1309,8 +1436,11 @@
 	dumpreg(ctlr->name, "ssc4params", igfx->ssc4params);
 
 	for(x=0; x<nelem(igfx->dp); x++){
+		if(igfx->dp[x].ctl.a == 0)
+			continue;
 		snprint(name, sizeof(name), "%s dp %c", ctlr->name, 'a'+x);
 		dumpreg(name, "ctl", igfx->dp[x].ctl);
+		dumphex(name, "dpcd", igfx->dp[x].dpcd, sizeof(igfx->dp[x].dpcd));
 	}
 	for(x=0; x<nelem(igfx->hdmi); x++){
 		snprint(name, sizeof(name), "%s hdmi %c", ctlr->name, 'a'+x);
@@ -1336,7 +1466,264 @@
 	dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
 }
 
+static int
+dpauxio(Igfx *igfx, Dp *dp, uchar buf[20], int len)
+{
+	int t, i;
+	u32int w;
+
+	if(dp->auxctl.a == 0){
+		werrstr("not present");
+		return -1;
+	}
+
+	t = 0;
+	while(rr(igfx, dp->auxctl.a) & (1<<31)){
+		if(++t >= 10){
+			werrstr("busy");
+			return -1;
+		}
+		sleep(5);
+	}
+
+	/* clear sticky bits */
+	wr(igfx, dp->auxctl.a, (1<<28) | (1<25) | (1<<30));
+
+	for(i=0; i<nelem(dp->auxdat); i++){
+		w  = buf[i*4+0]<<24;
+		w |= buf[i*4+1]<<16;
+		w |= buf[i*4+2]<<8;
+		w |= buf[i*4+3];
+		wr(igfx, dp->auxdat[i].a, w);
+	}
+
+	/* 2X Bit Clock divider */
+	w = ((dp == &igfx->dp[0]) ? igfx->cdclk : (igfx->rawclkfreq.v & 0x3ff)) >> 1;
+	if(w < 1 || w > 0x3fd){
+		werrstr("bad clock");
+		return -1;
+	}
+
+	/* hack: slow down a bit */
+	w += 2;
+
+	w |= 1<<31;	/* SendBusy */
+	w |= 1<<29;	/* interrupt disabled */
+	w |= 3<<26;	/* timeout 1600µs */
+	w |= len<<20;	/* send bytes */
+	w |= 5<<16;	/* precharge time (5*2 = 10µs) */
+	wr(igfx, dp->auxctl.a, w);
+
+	t = 0;
+	for(;;){
+		w = rr(igfx, dp->auxctl.a);
+		if((w & (1<<30)) != 0)
+			break;
+		if(++t >= 10){
+			werrstr("busy");
+			return -1;
+		}
+		sleep(5);
+	}
+	if(w & (1<28)){
+		werrstr("receive timeout");
+		return -1;
+	}
+	if(w & (1<<25)){
+		werrstr("receive error");
+		return -1;
+	}
+
+	len = (w >> 20) & 0x1f;
+	for(i=0; i<nelem(dp->auxdat); i++){
+		w = rr(igfx, dp->auxdat[i].a);
+		buf[i*4+0] = w>>24;
+		buf[i*4+1] = w>>16;
+		buf[i*4+2] = w>>8;
+		buf[i*4+3] = w;
+	}
+
+	return len;
+}
+
 enum {
+	CmdNative	= 8,
+	CmdMot		= 4,
+	CmdRead		= 1,
+	CmdWrite	= 0,
+};
+
+static int
+dpauxtra(Igfx *igfx, Dp *dp, int cmd, int addr, uchar *data, int len)
+{
+	uchar buf[20];
+	int r;
+
+	assert(len <= 16);
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = (cmd << 4) | ((addr >> 16) & 0xF);
+	buf[1] = addr >> 8;
+	buf[2] = addr;
+	buf[3] = len-1;
+	r = 3;	
+	if(data != nil && len > 0){
+		if((cmd & CmdRead) == 0)
+			memmove(buf+4, data, len);
+		r = 4 + len;
+	}
+	if((r = dpauxio(igfx, dp, buf, r)) < 0){
+		trace("%s: dpauxio: dp %c, cmd %x, addr %x, len %d: %r\n",
+			igfx->ctlr->name, 'a'+(int)(dp - &igfx->dp[0]), cmd, addr, len);
+		return -1;
+	}
+	if(r == 0 || data == nil || len == 0)
+		return 0;
+	if((cmd & CmdRead) != 0){
+		if(--r < len)
+			len = r;
+		memmove(data, buf+1, len);
+	}
+	return len;
+}
+
+static int
+rdpaux(Igfx *igfx, Dp *dp, int addr)
+{
+	uchar buf[1];
+	if(dpauxtra(igfx, dp, CmdNative|CmdRead, addr, buf, 1) != 1)
+		return -1;
+	return buf[0];
+}
+static int
+wdpaux(Igfx *igfx, Dp *dp, int addr, uchar val)
+{
+	if(dpauxtra(igfx, dp, CmdNative|CmdWrite, addr, &val, 1) != 1)
+		return -1;
+	return 0;
+}
+
+static int
+enabledp(Igfx *igfx, Dp *dp)
+{
+	int try, r;
+
+	if(dp->ctl.a == 0)
+		return 0;
+	if((dp->ctl.v & (1<<31)) == 0)
+		return 0;
+
+	/* Link configuration */
+	wdpaux(igfx, dp, 0x100, 0x0A);	/* 270Mhz */
+	wdpaux(igfx, dp, 0x101, 0x01);	/* one lane */
+
+	r = 0;
+
+	/* Link training pattern 1 */
+	dp->ctl.v &= ~(7<<8);
+	loadreg(igfx, dp->ctl);
+	for(try = 0;;try++){
+		if(try > 5)
+			goto Fail;
+		/* Link training pattern 1 */
+		wdpaux(igfx, dp, 0x102, 0x01);
+		sleep(100);
+		if((r = rdpaux(igfx, dp, 0x202)) < 0)
+			goto Fail;
+		if(r & 1)	/* LANE0_CR_DONE */
+			break;
+	}
+	trace("pattern1 finished: %x\n", r);
+
+	/* Link training pattern 2 */
+	dp->ctl.v &= ~(7<<8);
+	dp->ctl.v |= 1<<8;
+	loadreg(igfx, dp->ctl);
+	for(try = 0;;try++){
+		if(try > 5)
+			goto Fail;
+		/* Link training pattern 2 */
+		wdpaux(igfx, dp, 0x102, 0x02);
+		sleep(100);
+		if((r = rdpaux(igfx, dp, 0x202)) < 0)
+			goto Fail;
+		if((r & 7) == 7)
+			break;
+	}
+	trace("pattern2 finished: %x\n", r);
+
+	/* stop training */
+	dp->ctl.v &= ~(7<<8);
+	dp->ctl.v |= 3<<8;
+	loadreg(igfx, dp->ctl);
+	wdpaux(igfx, dp, 0x102, 0x00);
+	return 1;
+
+Fail:
+	trace("training failed: %x\n", r);
+
+	/* disable port */
+	dp->ctl.v &= ~(1<<31);
+	loadreg(igfx, dp->ctl);
+	wdpaux(igfx, dp, 0x102, 0x00);
+	return -1;
+}
+
+static uchar*
+edidshift(uchar buf[256])
+{
+	uchar tmp[256];
+	int i;
+
+	/* shift if neccesary so edid block is at the start */
+	for(i=0; i<256-8; i++){
+		if(buf[i+0] == 0x00 && buf[i+1] == 0xFF && buf[i+2] == 0xFF && buf[i+3] == 0xFF
+		&& buf[i+4] == 0xFF && buf[i+5] == 0xFF && buf[i+6] == 0xFF && buf[i+7] == 0x00){
+			memmove(tmp, buf, i);
+			memmove(buf, buf + i, 256 - i);
+			memmove(buf + (256 - i), tmp, i);
+			break;
+		}
+	}
+	return buf;
+}
+
+static Edid*
+snarfdpedid(Igfx *igfx, Dp *dp, int addr)
+{
+	uchar buf[256];
+	int i;
+
+	for(i=0; i<sizeof(dp->dpcd); i+=16)
+		if(dpauxtra(igfx, dp, CmdNative|CmdRead, i, dp->dpcd+i, 16) != 16)
+			return nil;
+
+	if(dp->dpcd[0] == 0)	/* nothing there, dont try to get edid */
+		return nil;
+
+	if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, nil, 0) < 0)
+		return nil;
+
+	for(i=0; i<sizeof(buf); i+=16){
+		if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, buf+i, 16) == 16)
+			continue;
+		if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, buf+i, 16) == 16)
+			continue;
+		if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, buf+i, 16) == 16)
+			continue;
+		if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, buf+i, 16) == 16)
+			continue;
+		if(dpauxtra(igfx, dp, CmdMot|CmdRead, addr, buf+i, 16) == 16)
+			continue;
+		return nil;
+	}
+
+	dpauxtra(igfx, dp, CmdRead, addr, nil, 0);
+
+	return parseedid128(edidshift(buf));
+}
+
+enum {
 	GMBUSCP = 0,	/* Clock/Port selection */
 	GMBUSCS = 1,	/* Command/Status */
 	GMBUSST = 2,	/* Status Register */
@@ -1394,10 +1781,9 @@
 }
 
 static Edid*
-snarfedid(Igfx *igfx, int port, int addr)
+snarfgmedid(Igfx *igfx, int port, int addr)
 {
-	uchar buf[256], tmp[256];
-	int i;
+	uchar buf[256];
 
 	/* read twice */
 	if(gmbusread(igfx, port, addr, buf, 128) != 128)
@@ -1405,18 +1791,7 @@
 	if(gmbusread(igfx, port, addr, buf + 128, 128) != 128)
 		return nil;
 
-	/* shift if neccesary so edid block is at the start */
-	for(i=0; i<256-8; i++){
-		if(buf[i+0] == 0x00 && buf[i+1] == 0xFF && buf[i+2] == 0xFF && buf[i+3] == 0xFF
-		&& buf[i+4] == 0xFF && buf[i+5] == 0xFF && buf[i+6] == 0xFF && buf[i+7] == 0x00){
-			memmove(tmp, buf, i);
-			memmove(buf, buf + i, 256 - i);
-			memmove(buf + (256 - i), tmp, i);
-			break;
-		}
-	}
-
-	return parseedid128(buf);
+	return parseedid128(edidshift(buf));
 }
 
 Ctlr igfx = {