shithub: riscv

Download patch

ref: 6f63752d84254b470322fc028dce1c79f7443e3b
parent: 3cc97ac108a3a2889efedfccef6a904baf8ed4e0
author: qwx <devnull@localhost>
date: Mon May 22 14:19:24 EDT 2017

igfx: add support for haswell graphics

add support for edp, dp and hdmi on haswell and haswell ult.
vga, dvi and specific configurations like ulx are unimplemented.

remaining issue: edp link training always fails (time out).

--- a/sys/src/9/pc/vgaigfx.c
+++ b/sys/src/9/pc/vgaigfx.c
@@ -16,6 +16,8 @@
 stolenmb(Pcidev *p)
 {
 	switch(p->did){
+	case 0x0412:	/* Haswell HD Graphics 4600 */
+	case 0x0a16:	/* Haswell HD Graphics 4400 */
 	case 0x0166:	/* Ivy Bridge */
 	case 0x0102:	/* Core-5 Sandy Bridge */
 	case 0x0152:	/* Core-i3 */
@@ -142,8 +144,8 @@
 	CURBASE,
 	CURPOS,
 
-	NPIPE = 3,
-};
+	NPIPE = 4,
+};
 
 static u32int*
 igfxcurregs(VGAscr* scr, int pipe)
@@ -152,11 +154,18 @@
 
 	if(scr->mmio == nil || scr->storage == 0)
 		return nil;
-	o = pipe*0x1000;
+	o = pipe == 3 ? 0xf000 : pipe*0x1000;
 	/* check PIPExCONF if enabled */
 	if((scr->mmio[(0x70008 | o)/4] & (1<<31)) == 0)
 		return nil;
 	switch(scr->pci->did){
+	case 0x0412:	/* Haswell HD Graphics 4600 */
+	case 0x0a16:	/* Haswell HD Graphics 4400 */
+		if(pipe > 3)
+			return nil;
+		if(pipe == 3)
+			o = 0;
+		break;
 	case 0x0166:	/* Ivy Bridge */
 	case 0x0152:	/* Core-i3 */
 		if(pipe > 2)
--- a/sys/src/cmd/aux/vga/igfx.c
+++ b/sys/src/cmd/aux/vga/igfx.c
@@ -25,6 +25,7 @@
 	TypeG45,
 	TypeIVB,		/* Ivy Bridge */
 	TypeSNB,		/* Sandy Bridge (unfinished) */
+	TypeHSW,		/* Haswell */
 };
 
 enum {
@@ -34,6 +35,7 @@
 	PortDPB	= 3,
 	PortDPC	= 4,
 	PortDPD	= 5,
+	PortDPE	= 6,
 };
 
 struct Reg {
@@ -67,6 +69,7 @@
 	Reg	dpctl;		/* TRANS_DP_CTL_x */
 
 	Dpll	*dpll;		/* this transcoders dpll */
+	Reg	clksel;		/* HSW: PIPE_CLK_SEL_x: transcoder clock select */
 };
 
 struct Hdmi {
@@ -75,7 +78,13 @@
 };
 
 struct Dp {
-	Reg	ctl;
+	/* HSW */
+	int	hdmi;
+	Reg	stat;
+	Reg	bufctl;		/* DDI_BUF_CTL_x */
+	Reg	buftrans[20];	/* DDI_BUF_TRANS */
+
+	Reg	ctl;		/* HSW: DP_TP_CTL_x */
 	Reg	auxctl;
 	Reg	auxdat[5];
 
@@ -107,6 +116,7 @@
 	Reg	stride;		/* DSPxSTRIDE */
 	Reg	surf;		/* DSPxSURF */
 	Reg	tileoff;	/* DSPxTILEOFF */
+	Reg	leftsurf;	/* HSW: PRI_LEFT_SURF_x */
 
 	Reg	pos;
 	Reg	size;
@@ -119,7 +129,7 @@
 };
 
 struct Pipe {
-	Trans;
+	Trans;			/* cpu transcoder */
 
 	Reg	src;		/* PIPExSRC */
 
@@ -144,16 +154,20 @@
 	int	npipe;
 	Pipe	pipe[4];
 
-	Dpll	dpll[2];
+	Dpll	dpll[4];
 	Pfit	pfit[3];
 
+	/* HSW */
+	int	isult;
+
+	Reg	dpllsel[5];	/* DPLL_SEL (IVB), PORT_CLK_SEL_DDIx (HSW) */
+
 	/* IVB */
-	Reg	dpllsel;	/* DPLL_SEL */
 	Reg	drefctl;	/* DREF_CTL */
 	Reg	rawclkfreq;	/* RAWCLK_FREQ */
 	Reg	ssc4params;	/* SSC4_PARAMS */
 
-	Dp	dp[4];
+	Dp	dp[5];
 	Hdmi	hdmi[4];
 
 	Reg	ppcontrol;
@@ -252,6 +266,18 @@
 		t->lm[1] = snarfreg(igfx, o + 0x48);
 		t->ln[1] = snarfreg(igfx, o + 0x4c);
 		break;
+	case TypeHSW:
+		t->dm[0] = snarfreg(igfx, o + 0x30);
+		t->dn[0] = snarfreg(igfx, o + 0x34);
+		t->lm[0] = snarfreg(igfx, o + 0x40);
+		t->ln[0] = snarfreg(igfx, o + 0x44);
+		if(t == &igfx->pipe[3]){			/* eDP pipe */
+			t->dm[1] = snarfreg(igfx, o + 0x38);
+			t->dn[1] = snarfreg(igfx, o + 0x3C);
+			t->lm[1] = snarfreg(igfx, o + 0x48);
+			t->ln[1] = snarfreg(igfx, o + 0x4C);
+		}
+		break;
 	}
 }
 
@@ -263,12 +289,21 @@
 
 	p = &igfx->pipe[x];
 
-	o = 0x60000 + x*0x1000;
+	o = x == 3 ? 0x6F000 : 0x60000 + x*0x1000;
 	snarftrans(igfx, p, o);
 
-	p->src = snarfreg(igfx, o + 0x0001C);
+	if(igfx->type != TypeHSW || x != 3)
+		p->src = snarfreg(igfx, o + 0x0001C);
 
-	if(igfx->type == TypeIVB || igfx->type == TypeSNB) {
+	if(igfx->type == TypeHSW) {
+		p->dpctl = snarfreg(igfx, o + 0x400);	/* PIPE_DDI_FUNC_CTL_x */
+		p->dpll = &igfx->dpll[0];
+		if(x == 3){
+			x = 0;	/* use plane/cursor a */
+			p->src = snarfreg(igfx, 0x6001C);
+		}else
+			p->clksel = snarfreg(igfx, 0x46140 + x*4);
+	} else if(igfx->type == TypeIVB || igfx->type == TypeSNB) {
 		p->fdi->txctl = snarfreg(igfx, o + 0x100);
 
 		o = 0xE0000 | x*0x1000;
@@ -287,7 +322,7 @@
 
 		p->fdi->chicken = snarfreg(igfx, o + 0x10064);
 
-		p->fdi->dpll = &igfx->dpll[(igfx->dpllsel.v>>(x*4)) & 1];
+		p->fdi->dpll = &igfx->dpll[(igfx->dpllsel[0].v>>(x*4)) & 1];
 		p->dpll = nil;
 	} else {
 		p->dpll = &igfx->dpll[x & 1];
@@ -299,10 +334,13 @@
 	p->dsp->stride		= snarfreg(igfx, 0x70188 + x*0x1000);
 	p->dsp->tileoff		= snarfreg(igfx, 0x701A4 + x*0x1000);
 	p->dsp->surf		= snarfreg(igfx, 0x7019C + x*0x1000);
+	if(igfx->type == TypeHSW)
+		p->dsp->leftsurf = snarfreg(igfx, 0x701B0 + x*0x1000);
 
 	/* cursor plane */
 	switch(igfx->type){
 	case TypeIVB:
+	case TypeHSW:
 		p->cur->cntr	= snarfreg(igfx, 0x70080 + x*0x1000);
 		p->cur->base	= snarfreg(igfx, 0x70084 + x*0x1000);
 		p->cur->pos	= snarfreg(igfx, 0x70088 + x*0x1000);
@@ -325,6 +363,11 @@
 	if(igfx->pci->vid != 0x8086)
 		return -1;
 	switch(igfx->pci->did){
+	case 0x0a16:	/* HD 4400 - 4th Gen Core (ULT) */
+		igfx->isult = 1;
+		/* wet floor */
+	case 0x0412:	/* HD 4600 - 4th Gen Core */
+		return TypeHSW;
 	case 0x0166:	/* 3rd Gen Core - ThinkPad X230 */
 	case 0x0152:	/* 2nd/3rd Gen Core - Core-i3 */
 		return TypeIVB;
@@ -414,13 +457,14 @@
 	case TypeSNB:
 		igfx->npipe = 2;	/* A,B */
 		igfx->cdclk = 300;	/* MHz */
-		goto PCHcommon;
+		goto IVBcommon;
 
 	case TypeIVB:
 		igfx->npipe = 3;	/* A,B,C */
 		igfx->cdclk = 400;	/* MHz */
+		goto IVBcommon;
 
-	PCHcommon:
+	IVBcommon:
 		igfx->dpll[0].ctrl	= snarfreg(igfx, 0xC6014);
 		igfx->dpll[0].fp0	= snarfreg(igfx, 0xC6040);
 		igfx->dpll[0].fp1	= snarfreg(igfx, 0xC6044);
@@ -428,14 +472,63 @@
 		igfx->dpll[1].fp0	= snarfreg(igfx, 0xC6048);
 		igfx->dpll[1].fp1	= snarfreg(igfx, 0xC604c);
 
-		igfx->dpllsel		= snarfreg(igfx, 0xC7000);
+		igfx->dpllsel[0]	= snarfreg(igfx, 0xC7000);
 
 		igfx->drefctl		= snarfreg(igfx, 0xC6200);
-		igfx->rawclkfreq	= snarfreg(igfx, 0xC6204);
 		igfx->ssc4params	= snarfreg(igfx, 0xC6210);
 
-		/* cpu displayport A */
 		igfx->dp[0].ctl		= snarfreg(igfx, 0x64000);
+		for(x=1; x<4; x++)
+			igfx->dp[x].ctl	= snarfreg(igfx, 0xE4000 + 0x100*x);
+
+		igfx->hdmi[1].ctl	= snarfreg(igfx, 0x0E1140);	/* HDMI_CTL_B */
+		igfx->hdmi[1].bufctl[0]	= snarfreg(igfx, 0x0FC810);	/* HTMI_BUF_CTL_0 */
+		igfx->hdmi[1].bufctl[1]	= snarfreg(igfx, 0x0FC81C);	/* HTMI_BUF_CTL_1 */
+		igfx->hdmi[1].bufctl[2]	= snarfreg(igfx, 0x0FC828);	/* HTMI_BUF_CTL_2 */
+		igfx->hdmi[1].bufctl[3]	= snarfreg(igfx, 0x0FC834);	/* HTMI_BUF_CTL_3 */
+
+		igfx->hdmi[2].ctl	= snarfreg(igfx, 0x0E1150);	/* HDMI_CTL_C */
+		igfx->hdmi[2].bufctl[0]	= snarfreg(igfx, 0x0FCC00);	/* HTMI_BUF_CTL_4 */
+		igfx->hdmi[2].bufctl[1]	= snarfreg(igfx, 0x0FCC0C);	/* HTMI_BUF_CTL_5 */
+		igfx->hdmi[2].bufctl[2]	= snarfreg(igfx, 0x0FCC18);	/* HTMI_BUF_CTL_6 */
+		igfx->hdmi[2].bufctl[3]	= snarfreg(igfx, 0x0FCC24);	/* HTMI_BUF_CTL_7 */
+
+		igfx->hdmi[3].ctl	= snarfreg(igfx, 0x0E1160);	/* HDMI_CTL_D */
+		igfx->hdmi[3].bufctl[0]	= snarfreg(igfx, 0x0FD000);	/* HTMI_BUF_CTL_8 */
+		igfx->hdmi[3].bufctl[1]	= snarfreg(igfx, 0x0FD00C);	/* HTMI_BUF_CTL_9 */
+		igfx->hdmi[3].bufctl[2]	= snarfreg(igfx, 0x0FD018);	/* HTMI_BUF_CTL_10 */
+		igfx->hdmi[3].bufctl[3]	= snarfreg(igfx, 0x0FD024);	/* HTMI_BUF_CTL_11 */
+
+		igfx->lvds		= snarfreg(igfx, 0x0E1180);	/* LVDS_CTL */
+		goto PCHcommon;
+
+	case TypeHSW:
+		igfx->npipe = 4;	/* A,B,C,eDP */
+		igfx->cdclk = igfx->isult ? 450 : 540;	/* MHz */
+
+		igfx->dpll[0].ctrl	= snarfreg(igfx, 0x130040);	/* LCPLL_CTL */
+		igfx->dpll[1].ctrl	= snarfreg(igfx, 0x46040);	/* WRPLL_CTL1 */
+		igfx->dpll[2].ctrl	= snarfreg(igfx, 0x46060);	/* WRPLL_CTL2 */
+		igfx->dpll[3].ctrl	= snarfreg(igfx, 0x46020);	/* SPLL_CTL */
+
+		for(x=0; x<nelem(igfx->dp); x++){
+			if(x == 3 && igfx->isult)	/* no DDI D */
+				continue;
+			igfx->dp[x].bufctl 	= snarfreg(igfx, 0x64000 + 0x100*x);
+			igfx->dp[x].ctl 	= snarfreg(igfx, 0x64040 + 0x100*x);
+			if(x > 0)
+				igfx->dp[x].stat	= snarfreg(igfx, 0x64044 + 0x100*x);
+			for(y=0; y<nelem(igfx->dp[x].buftrans); y++)
+				igfx->dp[x].buftrans[y]	= snarfreg(igfx, 0x64E00 + 0x60*x + 4*y);
+			igfx->dpllsel[x]	= snarfreg(igfx, 0x46100 + 4*x);
+		}
+
+		goto PCHcommon;
+
+	PCHcommon:
+		igfx->rawclkfreq	= snarfreg(igfx, 0xC6204);
+
+		/* cpu displayport A */
 		igfx->dp[0].auxctl	= snarfreg(igfx, 0x64010);
 		igfx->dp[0].auxdat[0]	= snarfreg(igfx, 0x64014);
 		igfx->dp[0].auxdat[1]	= snarfreg(igfx, 0x64018);
@@ -445,7 +538,6 @@
 
 		/* pch displayport B,C,D */
 		for(x=1; x<4; x++){
-			igfx->dp[x].ctl		= snarfreg(igfx, 0xE4000 + 0x100*x);
 			igfx->dp[x].auxctl	= snarfreg(igfx, 0xE4010 + 0x100*x);
 			igfx->dp[x].auxdat[0]	= snarfreg(igfx, 0xE4014 + 0x100*x);
 			igfx->dp[x].auxdat[1]	= snarfreg(igfx, 0xE4018 + 0x100*x);
@@ -455,6 +547,10 @@
 		}
 
 		for(x=0; x<igfx->npipe; x++){
+			if(igfx->type == TypeHSW && x == 3){
+				igfx->pipe[x].pfit = &igfx->pfit[0];
+				continue;
+			}
 			igfx->pfit[x].pwrgate	= snarfreg(igfx, 0x68060 + 0x800*x);
 			igfx->pfit[x].winpos	= snarfreg(igfx, 0x68070 + 0x800*x);
 			igfx->pfit[x].winsize	= snarfreg(igfx, 0x68074 + 0x800*x);
@@ -464,34 +560,15 @@
 			if(igfx->pipe[y].pfit == nil)
 				igfx->pipe[y].pfit = &igfx->pfit[x];
 		}
+
 		igfx->ppstatus		= snarfreg(igfx, 0xC7200);
 		igfx->ppcontrol		= snarfreg(igfx, 0xC7204);
 
-		igfx->hdmi[1].ctl	= snarfreg(igfx, 0x0E1140);	/* HDMI_CTL_B */
-		igfx->hdmi[1].bufctl[0]	= snarfreg(igfx, 0x0FC810);	/* HTMI_BUF_CTL_0 */
-		igfx->hdmi[1].bufctl[1]	= snarfreg(igfx, 0x0FC81C);	/* HTMI_BUF_CTL_1 */
-		igfx->hdmi[1].bufctl[2]	= snarfreg(igfx, 0x0FC828);	/* HTMI_BUF_CTL_2 */
-		igfx->hdmi[1].bufctl[3]	= snarfreg(igfx, 0x0FC834);	/* HTMI_BUF_CTL_3 */
-
-		igfx->hdmi[2].ctl	= snarfreg(igfx, 0x0E1150);	/* HDMI_CTL_C */
-		igfx->hdmi[2].bufctl[0]	= snarfreg(igfx, 0x0FCC00);	/* HTMI_BUF_CTL_4 */
-		igfx->hdmi[2].bufctl[1]	= snarfreg(igfx, 0x0FCC0C);	/* HTMI_BUF_CTL_5 */
-		igfx->hdmi[2].bufctl[2]	= snarfreg(igfx, 0x0FCC18);	/* HTMI_BUF_CTL_6 */
-		igfx->hdmi[2].bufctl[3]	= snarfreg(igfx, 0x0FCC24);	/* HTMI_BUF_CTL_7 */
-
-		igfx->hdmi[3].ctl	= snarfreg(igfx, 0x0E1160);	/* HDMI_CTL_D */
-		igfx->hdmi[3].bufctl[0]	= snarfreg(igfx, 0x0FD000);	/* HTMI_BUF_CTL_8 */
-		igfx->hdmi[3].bufctl[1]	= snarfreg(igfx, 0x0FD00C);	/* HTMI_BUF_CTL_9 */
-		igfx->hdmi[3].bufctl[2]	= snarfreg(igfx, 0x0FD018);	/* HTMI_BUF_CTL_10 */
-		igfx->hdmi[3].bufctl[3]	= snarfreg(igfx, 0x0FD024);	/* HTMI_BUF_CTL_11 */
-
 		for(x=0; x<5; x++)
 			igfx->gmbus[x]	= snarfreg(igfx, 0xC5100 + x*4);
 		igfx->gmbus[x]	= snarfreg(igfx, 0xC5120);
 
 		igfx->adpa		= snarfreg(igfx, 0x0E1100);	/* DAC_CTL */
-		igfx->lvds		= snarfreg(igfx, 0x0E1180);	/* LVDS_CTL */
-
 		igfx->vgacntrl		= snarfreg(igfx, 0x041000);
 		break;
 	}
@@ -513,15 +590,26 @@
 			for(l = vga->edid[x]->modelist; l != nil; l = l->next)
 				l->attr = mkattr(l->attr, "lcd", "1");
 			break;
+		case PortDPD:
+			if(igfx->type == TypeHSW && igfx->isult)
+				continue;
 		case PortDPA:
 		case PortDPB:
 		case PortDPC:
-		case PortDPD:
 			vga->edid[x] = snarfdpedid(igfx, &igfx->dp[x-PortDPA], 0x50);
 			break;
-		}
-		if(vga->edid[x] == nil)
+		default:
 			continue;
+		}
+
+		if(vga->edid[x] == nil){
+			if(x < PortDPB)
+				continue;
+			vga->edid[x] = snarfgmedid(igfx, x + 1 & ~1 | x >> 1 & 1, 0x50);
+			if(vga->edid[x] == nil)
+				continue;
+			igfx->dp[x-PortDPA].hdmi = 1;
+		}
 		for(l = vga->edid[x]->modelist; l != nil; l = l->next)
 			l->attr = mkattr(l->attr, "display", "%d", x+1);
 	}
@@ -536,7 +624,86 @@
 	ctlr->flag |= Hlinear|Ulinear|Foptions;
 }
 
+enum {
+	Lcpll = 2700,
+	Lcpll2k = Lcpll * 2000,
+};
+
 static int
+wrbudget(int freq)
+{
+	static int b[] = {
+		25175000,0, 25200000,0, 27000000,0, 27027000,0,
+		37762500,0, 37800000,0, 40500000,0, 40541000,0,
+		54000000,0, 54054000,0, 59341000,0, 59400000,0,
+		72000000,0, 74176000,0, 74250000,0, 81000000,0,
+		81081000,0, 89012000,0, 89100000,0, 108000000,0,
+		108108000,0, 111264000,0, 111375000,0, 148352000,0,
+		148500000,0, 162000000,0, 162162000,0, 222525000,0,
+		222750000,0, 296703000,0, 297000000,0, 233500000,1500,
+		245250000,1500, 247750000,1500, 253250000,1500, 298000000,1500,
+		169128000,2000, 169500000,2000, 179500000,2000, 202000000,2000,
+		256250000,4000, 262500000,4000, 270000000,4000, 272500000,4000,
+		273750000,4000, 280750000,4000, 281250000,4000, 286000000,4000,
+		291750000,4000, 267250000,5000, 268500000,5000
+	};
+	int *i;
+
+	for(i=b; i<b+nelem(b); i+=2)
+		if(i[0] == freq)
+			return i[1];
+	return 1000;
+}
+
+static void
+genwrpll(int freq, int *n2, int *p, int *r2)
+{
+	int budget, N2, P, R2;
+	vlong f2k, a, b, c, d, Δ, bestΔ;
+
+	f2k = freq / 100;
+	if(f2k == Lcpll2k){	/* bypass wrpll entirely and use lcpll */
+		*n2 = 2;
+		*p = 1;
+		*r2 = 2;
+		return;
+	}
+	budget = wrbudget(freq);
+	*p = 0;
+	for(R2=Lcpll*2/400+1; R2<=Lcpll*2/48; R2++)
+	for(N2=2400*R2/Lcpll+1; N2<=4800*R2/Lcpll; N2++)
+		for(P=2; P<=64; P+=2){
+			if(*p == 0){
+				*n2 = N2;
+				*p = P;
+				*r2 = R2;
+				continue;
+			}
+			Δ = f2k * P * R2;
+			Δ -= N2 * Lcpll2k;
+			if(Δ < 0)
+				Δ = -Δ;
+			bestΔ = f2k * *p * *r2;
+			bestΔ -= *n2 * Lcpll2k;
+			if(bestΔ < 0)
+				bestΔ = -bestΔ;
+			a = f2k * budget;
+			b = a;
+			a *= P * R2;
+			b *= *p * *r2;
+			c = Δ * MHz;
+			d = bestΔ * MHz;
+			if(a < c && b < d && *p * *r2 * Δ < P * R2 * bestΔ
+			|| a >= c && b < d
+			|| a >= c && b >= d && N2 * *r2 * *r2 > *n2 * R2 * R2){
+				*n2 = N2;
+				*p = P;
+				*r2 = R2;
+			}
+		}
+}
+
+static int
 genpll(int freq, int cref, int P2, int *m1, int *m2, int *n, int *p1)
 {
 	int M1, M2, M, N, P, P1;
@@ -596,7 +763,7 @@
 static int
 initdpll(Igfx *igfx, int x, int freq, int port)
 {
-	int cref, m1, m2, n, p1, p2;
+	int cref, m1, m2, n, n2, p1, p2, r2;
 	Dpll *dpll;
 
 	switch(igfx->type){
@@ -609,7 +776,7 @@
 	case TypeSNB:
 	case TypeIVB:
 		/* transcoder dpll enable */
-		igfx->dpllsel.v |= 8<<(x*4);
+		igfx->dpllsel[0].v |= 8<<(x*4);
 		/* program rawclock to 125MHz */
 		igfx->rawclkfreq.v = 125;
 
@@ -636,6 +803,33 @@
 		dpll->ctrl.v &= ~(7<<13);
 		dpll->ctrl.v |= (port == PortLCD ? 3 : 0) << 13;
 		break;
+	case TypeHSW:
+		/* select port clock to pipe */
+		igfx->pipe[x].clksel.v = port+1-PortDPA<<29;
+
+		if(igfx->dp[port-PortDPA].hdmi){
+			/* select port clock */
+			igfx->dpllsel[port-PortDPA].v = 1<<31;	/* WRPLL1 */
+			igfx->pipe[x].dpll = &igfx->dpll[1];
+
+			dpll = igfx->pipe[x].dpll;
+			/* enable pll */
+			dpll->ctrl.v = 1<<31;
+			/* LCPLL 2700 (non scc) reference */
+			dpll->ctrl.v |= 3<<28;
+
+			genwrpll(freq, &n2, &p1, &r2);
+			dpll->ctrl.v |= n2 << 16;
+			dpll->ctrl.v |= p1 << 8;
+			dpll->ctrl.v |= r2;
+		}else{
+			/* select port clock */
+			igfx->dpllsel[port-PortDPA].v = 1<<29;	/* LCPLL 1350 */
+			/* keep preconfigured frequency settings, keep cdclk */
+			dpll = igfx->pipe[x].dpll;
+			dpll->ctrl.v &= ~(1<<31) & ~(1<<28) & ~(1<<26);
+		}
+		return 0;
 	default:
 		return -1;
 	}
@@ -755,7 +949,7 @@
 }
 
 static void
-initpipe(Pipe *p, Mode *m, int bpc)
+initpipe(Igfx *igfx, Pipe *p, Mode *m, int bpc, int port)
 {
 	static uchar bpctab[4] = { 8, 10, 6, 12 };
 	int i, tu, lanes;
@@ -809,13 +1003,44 @@
 		fdi->rxtu[0].v = (tu-1)<<25;
 		fdi->rxtu[1].v = (tu-1)<<25;
 		initdatalinkmn(fdi, m->frequency, 270*MHz, lanes, tu, 3*bpc);
+	}else if(igfx->type == TypeHSW){
+		p->dpctl.v &= 0xf773733e;	/* mbz */
+		/* transcoder enable */
+		p->dpctl.v |= 1<<31;
+		/* DDI select (ignored by eDP) */
+		p->dpctl.v &= ~(7<<28);
+		p->dpctl.v |= (port-PortDPA)<<28;
+		/* displayport SST or hdmi mode */
+		p->dpctl.v &= ~(7<<24);
+		if(!igfx->dp[port-PortDPA].hdmi)
+			p->dpctl.v |= 2<<24;
+		/* sync polarity */
+		p->dpctl.v |= 3<<16;
+		if(m->hsync == '-')
+			p->dpctl.v ^= 1<<16;
+		if(m->vsync == '-')
+			p->dpctl.v ^= 1<<17;
+		/* eDP input select: always on power well */
+		p->dpctl.v &= ~(7<<12);
+		/* dp port width */
+		if(igfx->dp[port-PortDPA].hdmi)
+			lanes = 4;
+		else{
+			p->dpctl.v &= ~(7<<1);
+			p->dpctl.v |= lanes-1 << 1;
+		}
 	}
 
 	/* bits per color for cpu pipe */
 	for(i=0; i<nelem(bpctab); i++){
 		if(bpctab[i] == bpc){
-			p->conf.v &= ~(7<<5);
-			p->conf.v |= i<<5;
+			if(igfx->type == TypeHSW){
+				p->dpctl.v &= ~(7<<20);
+				p->dpctl.v |= i<<20;
+			}else{
+				p->conf.v &= ~(7<<5);
+				p->conf.v |= i<<5;
+			}
 			break;
 		}
 	}
@@ -825,7 +1050,7 @@
 static void
 init(Vga* vga, Ctlr* ctlr)
 {
-	int x, port, bpc;
+	int x, nl, port, bpc;
 	char *val;
 	Igfx *igfx;
 	Pipe *p;
@@ -844,29 +1069,44 @@
 	igfx->vgacntrl.v |= (1<<31);
 
 	/* disable all pipes and ports */
-	igfx->ppcontrol.v &= 0xFFFF;
-	igfx->ppcontrol.v &= ~5;
+	igfx->ppcontrol.v &= 10;	/* 15:4 mbz; unset 5<<0 */
 	igfx->lvds.v &= ~(1<<31);
 	igfx->adpa.v &= ~(1<<31);
 	if(igfx->type == TypeG45)
 		igfx->adpa.v |= (3<<10);	/* Monitor DPMS: off */
-	for(x=0; x<nelem(igfx->dp); x++)
+	if(igfx->type == TypeHSW){
+		for(x=1; x<nelem(igfx->dpll); x++)
+			igfx->dpll[x].ctrl.v &= ~(1<<31);
+		for(x=0; x<nelem(igfx->dpllsel); x++)
+			igfx->dpllsel[x].v = 7<<29;
+	}
+	for(x=0; x<nelem(igfx->dp); x++){
 		igfx->dp[x].ctl.v &= ~(1<<31);
+		igfx->dp[x].bufctl.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);
+		if(igfx->type == TypeHSW){
+			igfx->pipe[x].dpctl.v &= ~(7<<28);
+			igfx->pipe[x].fdi->dpctl.v &= ~(7<<28);
+		}else{
+			igfx->pipe[x].dpctl.v |= (3<<29);
+			igfx->pipe[x].fdi->dpctl.v |= (3<<29);
+		}
 		/* disable transcoder/pipe */
 		igfx->pipe[x].conf.v &= ~(1<<31);
+		igfx->pipe[x].fdi->conf.v &= ~(1<<31);
 	}
 
-	if((val = dbattr(m->attr, "display")) != nil)
+	if((val = dbattr(m->attr, "display")) != nil){
 		port = atoi(val)-1;
-	else if(dbattr(m->attr, "lcd") != nil)
+		if(igfx->type == TypeHSW && !igfx->dp[port-PortDPA].hdmi)
+			bpc = 6;
+	}else if(dbattr(m->attr, "lcd") != nil)
 		port = PortLCD;
 	else
 		port = PortVGA;
@@ -880,6 +1120,8 @@
 		break;
 
 	case PortVGA:
+		if(igfx->type == TypeHSW)	/* unimplemented */
+			goto Badport;
 		if(igfx->type == TypeG45)
 			x = (igfx->adpa.v >> 30) & 1;
 		else
@@ -898,6 +1140,8 @@
 		break;
 
 	case PortLCD:
+		if(igfx->type == TypeHSW)
+			goto Badport;
 		if(igfx->type == TypeG45)
 			x = (igfx->lvds.v >> 30) & 1;
 		else
@@ -922,11 +1166,75 @@
 	case PortDPB:
 	case PortDPC:
 	case PortDPD:
+	case PortDPE:
 		r = &igfx->dp[port - PortDPA].ctl;
 		if(r->a == 0)
 			goto Badport;
 		/* port enable */
 		r->v |= 1<<31;
+		/* use PIPE_A for displayport */
+		x = 0;
+
+		if(igfx->type == TypeHSW){
+			if(port == PortDPA){
+				/* only enable panel for eDP */
+				igfx->ppcontrol.v |= 5;
+				/* use eDP pipe */
+				x = 3;
+			}
+
+			/* reserved MBZ */
+			r->v &= ~(7<<28) & ~(1<<26) & ~(63<<19) & ~(3<<16) & ~(15<<11) & ~(1<<7) & ~31;
+			/* displayport SST mode */
+			r->v &= ~(1<<27);
+			/* link not in training, send normal pixels */
+			r->v |= 3<<8;
+			if(igfx->dp[port-PortDPA].hdmi){
+				/* hdmi: do not configure displayport */
+				r->a = 0;
+				r->v &= ~(1<<31);
+			}
+
+			r = &igfx->dp[port - PortDPA].bufctl;
+			/* buffer enable */
+			r->v |= 1<<31;
+			/* reserved MBZ */
+			r->v &= ~(7<<28) & ~(127<<17) & ~(255<<8) & ~(3<<5);
+			/* grab lanes shared with port e when using port a */
+			if(port == PortDPA)
+				r->v |= 1<<4;
+			else if(port == PortDPE)
+				igfx->dp[0].bufctl.v &= ~(1<<4);
+			/* dp port width */
+			r->v &= ~(15<<1);
+			if(!igfx->dp[port-PortDPA].hdmi){
+				nl = needlanes(m->frequency, 270*MHz, 3*bpc);
+				/* x4 unsupported on port e */
+				if(nl > 2 && port == PortDPE)
+					goto Badport;
+				r->v |= nl-1 << 1;
+			}
+			/* port reversal: off */
+			r->v &= ~(1<<16);
+
+			/* buffer translation (vsl/pel) */
+			r = igfx->dp[port - PortDPA].buftrans;
+			r[1].v  = 0x0006000E;	r[0].v  = 0x00FFFFFF;
+			r[3].v  = 0x0005000A;	r[2].v  = 0x00D75FFF;
+			r[5].v  = 0x00040006;	r[4].v  = 0x00C30FFF;
+			r[7].v  = 0x000B0000;	r[6].v  = 0x80AAAFFF;
+			r[9].v  = 0x0005000A;	r[8].v  = 0x00FFFFFF;
+			r[11].v = 0x000C0004;	r[10].v = 0x00D75FFF;
+			r[13].v = 0x000B0000;	r[12].v = 0x80C30FFF;
+			r[15].v = 0x00040006;	r[14].v = 0x00FFFFFF;
+			r[17].v = 0x000B0000;	r[16].v = 0x80D75FFF;
+			r[19].v = 0x00040006;	r[18].v = 0x00FFFFFF;
+			break;
+		}
+
+		if(port == PortDPE)
+			goto Badport;
+
 		/* port width selection */
 		r->v &= ~(7<<19);
 		r->v |= needlanes(m->frequency, 270*MHz, 3*bpc)-1 << 19;
@@ -935,10 +1243,10 @@
 		r->v &= ~(1<<15);
 		/* reserved MBZ */
 		r->v &= ~(15<<11);
-		/* use PIPE_A for displayport */
-		x = 0;
 		/* displayport transcoder */
 		if(port == PortDPA){
+			/* reserved MBZ */
+			r->v &= ~(15<<10);
 			/* pll frequency: 270mhz */
 			r->v &= ~(3<<16);
 			/* pll enable */
@@ -947,6 +1255,8 @@
 			r->v &= ~(3<<29);
 			r->v |= x<<29;
 		} else if(igfx->pipe[x].fdi->dpctl.a != 0){
+			/* reserved MBZ */
+			r->v &= ~(15<<11);
 			/* audio output: disable */
 			r->v &= ~(1<<6);
 			/* transcoder displayport configuration */
@@ -971,6 +1281,8 @@
 	p->dsp->cntr.v = (1<<31) | (6<<26);
 	if(igfx->type == TypeG45)
 		p->dsp->cntr.v |= x<<24;	/* pipe assign */
+	else
+		p->dsp->cntr.v &= ~511;	/* mbz */
 
 	/* stride must be 64 byte aligned */
 	p->dsp->stride.v = m->x * (m->z / 8);
@@ -987,6 +1299,7 @@
 	p->dsp->surf.v = 0;
 	p->dsp->linoff.v = 0;
 	p->dsp->tileoff.v = 0;
+	p->dsp->leftsurf.v = 0;
 
 	/* cursor plane off */
 	p->cur->cntr.v = 0;
@@ -998,7 +1311,7 @@
 	if(initdpll(igfx, x, m->frequency, port) < 0)
 		error("%s: frequency %d out of range\n", ctlr->name, m->frequency);
 
-	initpipe(p, m, bpc);
+	initpipe(igfx, p, m, bpc, port);
 
 	ctlr->flag |= Finit;
 }
@@ -1008,6 +1321,9 @@
 {
 	int i;
 
+	if(t->conf.a == 0)
+		return;
+
 	/* program trans/pipe timings */
 	loadreg(igfx, t->ht);
 	loadreg(igfx, t->hb);
@@ -1026,7 +1342,7 @@
 	loadreg(igfx, t->lm[1]);
 	loadreg(igfx, t->ln[1]);
 
-	if(t->dpll != nil){
+	if(t->dpll != nil && igfx->type != TypeHSW){
 		/* program dpll */
 		t->dpll->ctrl.v &= ~(1<<31);
 		loadreg(igfx, t->dpll->ctrl);
@@ -1066,6 +1382,9 @@
 	if((p->conf.v & (1<<31)) == 0)
 		return;	/* pipe is disabled, done */
 
+	/* select pipe clock */
+	loadreg(igfx, p->clksel);
+
 	if(p->fdi->rxctl.a != 0){
 		p->fdi->rxctl.v &= ~(1<<31);
 		p->fdi->rxctl.v &= ~(1<<4);	/* rawclk */
@@ -1107,6 +1426,7 @@
 	loadreg(igfx, p->dsp->size);
 	loadreg(igfx, p->dsp->pos);
 	loadreg(igfx, p->dsp->surf);	/* arm */
+	loadreg(igfx, p->dsp->leftsurf);
 
 	/* program cursor */
 	loadreg(igfx, p->cur->cntr);
@@ -1192,8 +1512,21 @@
 {
 	int i;
 
+	/* deselect pipe clock */
+	csr(igfx, t->clksel.a, 7<<29, 0);
+
 	/* disable displayport transcoder */
-	csr(igfx, t->dpctl.a, 1<<31, 3<<29);
+	if(igfx->type == TypeHSW){
+		csr(igfx, t->dpctl.a, 15<<28, 0);
+		csr(igfx, t->ht.a, ~0, 0);
+		csr(igfx, t->hb.a, ~0, 0);
+		csr(igfx, t->hs.a, ~0, 0);
+		csr(igfx, t->vt.a, ~0, 0);
+		csr(igfx, t->vb.a, ~0, 0);
+		csr(igfx, t->vs.a, ~0, 0);
+		csr(igfx, t->vss.a, ~0, 0);
+	}else
+		csr(igfx, t->dpctl.a, 1<<31, 3<<29);
 
 	/* disable transcoder / pipe */
 	csr(igfx, t->conf.a, 1<<31, 0);
@@ -1206,7 +1539,7 @@
 	csr(igfx, t->chicken.a, 1<<31, 0);
 
 	/* disable dpll  */
-	if(t->dpll != nil)
+	if(igfx->type != TypeHSW && t->dpll != nil)
 		csr(igfx, t->dpll->ctrl.a, 1<<31, 0);
 }
 
@@ -1221,12 +1554,16 @@
 	csr(igfx, p->dsp->cntr.a, 1<<31, 0);
 	wr(igfx, p->dsp->surf.a, 0);	/* arm */
 	/* cursor off */
-	csr(igfx, p->cur->cntr.a, 1<<5 | 7, 0);
+	if(igfx->type == TypeHSW)
+		csr(igfx, p->cur->cntr.a, 0x3F, 0);
+	else
+		csr(igfx, p->cur->cntr.a, 1<<5 | 7, 0);
 	wr(igfx, p->cur->base.a, 0);	/* arm */
 
 	/* display/overlay/cursor planes off */
 	if(igfx->type == TypeG45)
 		csr(igfx, p->conf.a, 0, 3<<18);
+	csr(igfx, p->src.a, ~0, 0);
 
 	/* disable cpu pipe */
 	disabletrans(igfx, p);
@@ -1243,7 +1580,8 @@
 	disabletrans(igfx, p->fdi);
 
 	/* disable pch dpll enable bit */
-	csr(igfx, igfx->dpllsel.a, 8<<(x*4), 0);
+	if(igfx->type != TypeHSW)
+		csr(igfx, igfx->dpllsel[0].a, 8<<(x*4), 0);
 }
 
 static void
@@ -1250,7 +1588,7 @@
 load(Vga* vga, Ctlr* ctlr)
 {
 	Igfx *igfx;
-	int x;
+	int x, y;
 
 	igfx = vga->private;
 
@@ -1269,8 +1607,18 @@
 	csr(igfx, igfx->sdvoc.a, (1<<29) | (1<<31), 0);
 	csr(igfx, igfx->adpa.a, 1<<31, 0);
 	csr(igfx, igfx->lvds.a, 1<<31, 0);
-	for(x = 0; x < nelem(igfx->dp); x++)
+	for(x = 0; x < nelem(igfx->dp); x++){
 		csr(igfx, igfx->dp[x].ctl.a, 1<<31, 0);
+		if(igfx->dp[x].bufctl.a != 0){
+			csr(igfx, igfx->dp[x].bufctl.a, 1<<31, 0);
+			/* wait for buffers to return to idle */
+			for(y=0; y<5; y++){
+				sleep(10);
+				if(rr(igfx, igfx->dp[x].bufctl.a) & 1<<7)
+					break;
+			}
+		}
+	}
 	for(x = 0; x < nelem(igfx->hdmi); x++)
 		csr(igfx, igfx->hdmi[x].ctl.a, 1<<31, 0);
 
@@ -1291,9 +1639,23 @@
 		csr(igfx, igfx->pipe[0].conf.a, 0, 3<<18);
 	}
 
+	if(igfx->type == TypeHSW){
+		/* deselect port clock */
+		for(x=0; x<nelem(igfx->dpllsel); x++)
+			csr(igfx, igfx->dpllsel[x].a, 0, 7<<29);
+
+		/* disable dpll's other than LCPLL */
+		for(x=1; x<nelem(igfx->dpll); x++)
+			csr(igfx, igfx->dpll[x].ctrl.a, 1<<31, 0);
+	}
+
 	/* program new clock sources */
 	loadreg(igfx, igfx->rawclkfreq);
 	loadreg(igfx, igfx->drefctl);
+	/* program cpu pll */
+	if(igfx->type == TypeHSW)
+		for(x=0; x<nelem(igfx->dpll); x++)
+			loadreg(igfx, igfx->dpll[x].ctrl);
 	sleep(10);
 
 	/* set lvds before enabling dpll */
@@ -1300,7 +1662,8 @@
 	loadreg(igfx, igfx->lvds);
 
 	/* new dpll setting */
-	loadreg(igfx, igfx->dpllsel);
+	for(x=0; x<nelem(igfx->dpllsel); x++)
+		loadreg(igfx, igfx->dpllsel[x]);
 
 	/* program all pipes */
 	for(x = 0; x < igfx->npipe; x++)
@@ -1314,6 +1677,9 @@
 	loadreg(igfx, igfx->sdvob);
 	loadreg(igfx, igfx->sdvoc);
 	for(x = 0; x < nelem(igfx->dp); x++){
+		for(y=0; y<nelem(igfx->dp[x].buftrans); y++)
+			loadreg(igfx, igfx->dp[x].buftrans[y]);
+		loadreg(igfx, igfx->dp[x].bufctl);
 		if(enabledp(igfx, &igfx->dp[x]) < 0)
 			ctlr->flag |= Ferror;
 	}
@@ -1403,6 +1769,7 @@
 	dumpreg(name, "vss", t->vss);
 
 	dumpreg(name, "dpctl", t->dpctl);
+	dumpreg(name, "clksel", t->clksel);
 }
 
 static void
@@ -1431,6 +1798,7 @@
 	dumpreg(name, "stride", p->dsp->stride);
 	dumpreg(name, "surf", p->dsp->surf);
 	dumpreg(name, "tileoff", p->dsp->tileoff);
+	dumpreg(name, "leftsurf", p->dsp->leftsurf);
 	dumpreg(name, "pos", p->dsp->pos);
 	dumpreg(name, "size", p->dsp->size);
 
@@ -1456,6 +1824,9 @@
 	dumpreg(name, "fp0", dpll->fp0);
 	dumpreg(name, "fp1", dpll->fp1);
 
+	if(igfx->type == TypeHSW)
+		return;
+
 	p2 = ((dpll->ctrl.v >> 13) & 3) == 3 ? 14 : 10;
 	if(((dpll->ctrl.v >> 24) & 3) == 1)
 		p2 >>= 1;
@@ -1493,7 +1864,7 @@
 {
 	char name[32];
 	Igfx *igfx;
-	int x;
+	int x, y;
 
 	if((igfx = vga->private) == nil)
 		return;
@@ -1504,7 +1875,10 @@
 	for(x=0; x<nelem(igfx->dpll); x++)
 		dumpdpll(igfx, x);
 
-	dumpreg(ctlr->name, "dpllsel", igfx->dpllsel);
+	for(x=0; x<nelem(igfx->dpllsel); x++){
+		snprint(name, sizeof(name), "%s dpllsel %c", ctlr->name, 'a'+x);
+		dumpreg(name, "dpllsel", igfx->dpllsel[x]);
+	}
 
 	dumpreg(ctlr->name, "drefctl", igfx->drefctl);
 	dumpreg(ctlr->name, "rawclkfreq", igfx->rawclkfreq);
@@ -1515,7 +1889,13 @@
 			continue;
 		snprint(name, sizeof(name), "%s dp %c", ctlr->name, 'a'+x);
 		dumpreg(name, "ctl", igfx->dp[x].ctl);
+		dumpreg(name, "bufctl", igfx->dp[x].bufctl);
+		dumpreg(name, "stat", igfx->dp[x].stat);
 		dumphex(name, "dpcd", igfx->dp[x].dpcd, sizeof(igfx->dp[x].dpcd));
+		for(y=0; y<nelem(igfx->dp[x].buftrans); y++){
+			snprint(name, sizeof(name), "%s buftrans %c %d", ctlr->name, 'a'+x, y);
+			dumpreg(name, "ctl", igfx->dp[x].buftrans[y]);
+		}
 	}
 	for(x=0; x<nelem(igfx->hdmi); x++){
 		snprint(name, sizeof(name), "%s hdmi %c", ctlr->name, 'a'+x);
@@ -1645,7 +2025,9 @@
 	if(data != nil && len > 0){
 		if((cmd & CmdRead) == 0)
 			memmove(buf+4, data, len);
-		r = 4 + len;
+		r = 4;
+		if((cmd & CmdRead) == 0)
+			r += len;
 	}
 	if((r = dpauxio(igfx, dp, buf, r)) < 0){
 		trace("%s: dpauxio: dp %c, cmd %x, addr %x, len %d: %r\n",
@@ -1682,6 +2064,7 @@
 enabledp(Igfx *igfx, Dp *dp)
 {
 	int try, r;
+	u32int w;
 
 	if(dp->ctl.a == 0)
 		return 0;
@@ -1688,9 +2071,14 @@
 	if((dp->ctl.v & (1<<31)) == 0)
 		return 0;
 
+	/* FIXME: always times out */
+	if(igfx->type == TypeHSW && dp == &igfx->dp[0])
+		goto Skip;
+
 	/* Link configuration */
 	wdpaux(igfx, dp, 0x100, (270*MHz) / 27000000);
-	wdpaux(igfx, dp, 0x101, ((dp->ctl.v>>19)&7)+1);
+	w = dp->ctl.v >> (igfx->type == TypeHSW ? 1 : 19) & 7;
+	wdpaux(igfx, dp, 0x101, w+1);
 
 	r = 0;
 
@@ -1727,6 +2115,19 @@
 	}
 	trace("pattern2 finished: %x\n", r);
 
+	if(igfx->type == TypeHSW){
+		/* set link training to idle pattern and wait for 5 idle
+		 * patterns */
+		dp->ctl.v &= ~(7<<8);
+		dp->ctl.v |= 2<<8;
+		loadreg(igfx, dp->ctl);
+		for(try=0; try<10; try++){
+			sleep(10);
+			if(rr(igfx, dp->stat.a) & (1<<25))
+				break;
+		}
+	}
+Skip:
 	/* stop training */
 	dp->ctl.v &= ~(7<<8);
 	dp->ctl.v |= 3<<8;
@@ -1768,6 +2169,7 @@
 {
 	uchar buf[256];
 	int i;
+	Edid *e;
 
 	for(i=0; i<sizeof(dp->dpcd); i+=16)
 		if(dpauxtra(igfx, dp, CmdNative|CmdRead, i, dp->dpcd+i, 16) != 16)
@@ -1795,7 +2197,9 @@
 
 	dpauxtra(igfx, dp, CmdRead, addr, nil, 0);
 
-	return parseedid128(edidshift(buf));
+	if((e = parseedid128(edidshift(buf))) == nil)
+		trace("%s: snarfdpedid: dp %c: %r\n", igfx->ctlr->name, 'a'+(int)(dp - &igfx->dp[0]));
+	return e;
 }
 
 enum {