ref: 930efe67e81635e632f1e6f93e1c56499c79a55f
dir: /sys/src/cmd/aux/vga/igfx.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "pci.h" #include "vga.h" typedef struct Reg Reg; typedef struct Dpll Dpll; typedef struct Hdmi Hdmi; typedef struct Dp Dp; typedef struct Fdi Fdi; typedef struct Pfit Pfit; typedef struct Curs Curs; typedef struct Plane Plane; typedef struct Trans Trans; typedef struct Pipe Pipe; typedef struct Igfx Igfx; enum { MHz = 1000000, }; enum { TypeG45, TypeIVB, /* Ivy Bridge */ TypeSNB, /* Sandy Bridge (unfinished) */ TypeHSW, /* Haswell */ }; enum { PortVGA = 0, /* adpa */ PortLCD = 1, /* lvds */ PortDPA = 2, PortDPB = 3, PortDPC = 4, PortDPD = 5, PortDPE = 6, }; struct Reg { u32int a; /* address or 0 when invalid */ u32int v; /* value */ }; struct Dpll { Reg ctrl; /* DPLLx_CTRL */ Reg fp0; /* FPx0 */ Reg fp1; /* FPx1 */ }; struct Trans { Reg dm[2]; /* pipe/trans DATAM */ Reg dn[2]; /* pipe/trans DATAN */ Reg lm[2]; /* pipe/trans LINKM */ Reg ln[2]; /* pipe/trans LINKN */ Reg ht; /* pipe/trans HTOTAL_x */ Reg hb; /* pipe/trans HBLANK_x */ Reg hs; /* pipe/trans HSYNC_x */ Reg vt; /* pipe/trans VTOTAL_x */ Reg vb; /* pipe/trans VBLANK_x */ Reg vs; /* pipe/trans VSYNC_x */ Reg vss; /* pipe/trans VSYNCSHIFT_x */ Reg conf; /* pipe/trans CONF_x */ Reg chicken; /* workarround register */ Reg dpctl; /* TRANS_DP_CTL_x */ Dpll *dpll; /* this transcoders dpll */ Reg clksel; /* HSW: PIPE_CLK_SEL_x: transcoder clock select */ }; struct Hdmi { Reg ctl; Reg bufctl[4]; }; struct Dp { /* 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]; uchar dpcd[256]; }; struct Fdi { Trans; Reg txctl; /* FDI_TX_CTL */ Reg rxctl; /* FDI_RX_CTL */ Reg rxmisc; /* FDI_RX_MISC */ Reg rxiir; /* FDI_RX_IIR */ Reg rximr; /* FDI_RX_IMR */ Reg rxtu[2]; /* FDI_RX_TUSIZE */ }; struct Pfit { Reg ctrl; Reg winpos; Reg winsize; Reg pwrgate; }; struct Plane { Reg cntr; /* DSPxCNTR */ Reg linoff; /* DSPxLINOFF */ Reg stride; /* DSPxSTRIDE */ Reg surf; /* DSPxSURF */ Reg tileoff; /* DSPxTILEOFF */ Reg leftsurf; /* HSW: PRI_LEFT_SURF_x */ Reg pos; Reg size; }; struct Curs { Reg cntr; Reg base; Reg pos; }; struct Pipe { Trans; /* cpu transcoder */ Reg src; /* PIPExSRC */ Fdi fdi[1]; /* fdi/dp transcoder */ Plane dsp[1]; /* display plane */ Curs cur[1]; /* hardware cursor */ Pfit *pfit; /* selected panel fitter */ }; struct Igfx { Ctlr *ctlr; Pcidev *pci; u32int pio; u32int *mmio; int type; int cdclk; /* core display clock in mhz */ int npipe; Pipe pipe[4]; Dpll dpll[4]; Pfit pfit[3]; /* HSW */ int isult; Reg dpllsel[5]; /* DPLL_SEL (IVB), PORT_CLK_SEL_DDIx (HSW) */ /* IVB */ Reg drefctl; /* DREF_CTL */ Reg rawclkfreq; /* RAWCLK_FREQ */ Reg ssc4params; /* SSC4_PARAMS */ Dp dp[5]; Hdmi hdmi[4]; Reg ppcontrol; Reg ppstatus; /* G45 */ Reg gmbus[6]; /* GMBUSx */ Reg sdvoc; Reg sdvob; /* common */ Reg adpa; Reg lvds; Reg vgacntrl; }; static u32int rr(Igfx *igfx, u32int a) { if(a == 0) return 0; assert((a & 3) == 0); if(igfx->mmio != nil) return igfx->mmio[a/4]; outportl(igfx->pio, a); return inportl(igfx->pio + 4); } static void wr(Igfx *igfx, u32int a, u32int v) { if(a == 0) /* invalid */ return; assert((a & 3) == 0); if(igfx->mmio != nil){ igfx->mmio[a/4] = v; return; } outportl(igfx->pio, a); outportl(igfx->pio + 4, v); } static void csr(Igfx *igfx, u32int reg, u32int clr, u32int set) { wr(igfx, reg, (rr(igfx, reg) & ~clr) | set); } static void loadreg(Igfx *igfx, Reg r) { wr(igfx, r.a, r.v); } static Reg snarfreg(Igfx *igfx, u32int a) { Reg r; r.a = a; r.v = rr(igfx, a); return r; } static void snarftrans(Igfx *igfx, Trans *t, u32int o) { /* pipe timing */ t->ht = snarfreg(igfx, o + 0x00000); t->hb = snarfreg(igfx, o + 0x00004); t->hs = snarfreg(igfx, o + 0x00008); t->vt = snarfreg(igfx, o + 0x0000C); t->vb = snarfreg(igfx, o + 0x00010); t->vs = snarfreg(igfx, o + 0x00014); t->vss = snarfreg(igfx, o + 0x00028); t->conf = snarfreg(igfx, o + 0x10008); switch(igfx->type){ case TypeG45: if(t == &igfx->pipe[0]){ /* PIPEA */ t->dm[0] = snarfreg(igfx, 0x70050); /* GMCHDataM */ t->dn[0] = snarfreg(igfx, 0x70054); /* GMCHDataN */ t->lm[0] = snarfreg(igfx, 0x70060); /* DPLinkM */ t->ln[0] = snarfreg(igfx, 0x70064); /* DPLinkN */ } break; case TypeIVB: case TypeSNB: t->dm[0] = snarfreg(igfx, o + 0x30); t->dn[0] = snarfreg(igfx, o + 0x34); t->dm[1] = snarfreg(igfx, o + 0x38); t->dn[1] = snarfreg(igfx, o + 0x3c); t->lm[0] = snarfreg(igfx, o + 0x40); t->ln[0] = snarfreg(igfx, o + 0x44); 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; } } static void snarfpipe(Igfx *igfx, int x) { u32int o; Pipe *p; p = &igfx->pipe[x]; o = x == 3 ? 0x6F000 : 0x60000 + x*0x1000; snarftrans(igfx, p, o); if(igfx->type != TypeHSW || x != 3) p->src = snarfreg(igfx, o + 0x0001C); 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; snarftrans(igfx, p->fdi, o); p->fdi->dpctl = snarfreg(igfx, o + 0x300); p->fdi->rxctl = snarfreg(igfx, o + 0x1000c); p->fdi->rxmisc = snarfreg(igfx, o + 0x10010); p->fdi->rxiir = snarfreg(igfx, o + 0x10014); p->fdi->rximr = snarfreg(igfx, o + 0x10018); p->fdi->rxtu[0] = snarfreg(igfx, o + 0x10030); p->fdi->rxtu[1] = snarfreg(igfx, o + 0x10038); p->fdi->chicken = snarfreg(igfx, o + 0x10064); p->fdi->dpll = &igfx->dpll[(igfx->dpllsel[0].v>>(x*4)) & 1]; p->dpll = nil; } else { p->dpll = &igfx->dpll[x & 1]; } /* display plane */ p->dsp->cntr = snarfreg(igfx, 0x70180 + x*0x1000); p->dsp->linoff = snarfreg(igfx, 0x70184 + x*0x1000); 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); break; case TypeG45: p->dsp->pos = snarfreg(igfx, 0x7018C + x*0x1000); p->dsp->size = snarfreg(igfx, 0x70190 + x*0x1000); /* wet floor */ case TypeSNB: p->cur->cntr = snarfreg(igfx, 0x70080 + x*0x40); p->cur->base = snarfreg(igfx, 0x70084 + x*0x40); p->cur->pos = snarfreg(igfx, 0x70088 + x*0x40); break; } } static int devtype(Igfx *igfx) { 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; case 0x0046: /* Thinkpad T510 */ case 0x0102: /* Dell Optiplex 790 */ case 0x0126: /* Thinkpad X220 */ return TypeSNB; case 0x27a2: /* GM945/82940GML - ThinkPad X60 Tablet */ case 0x29a2: /* 82P965/G965 HECI desktop */ case 0x2a02: /* GM965/GL960/X3100 - ThinkPad X61 Tablet */ case 0x2a42: /* 4 Series Mobile - ThinkPad X200 */ return TypeG45; } return -1; } 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) { Igfx *igfx; int x, y; igfx = vga->private; if(igfx == nil) { igfx = alloc(sizeof(Igfx)); igfx->ctlr = ctlr; igfx->pci = vga->pci; if(igfx->pci == nil){ error("%s: no pci device\n", ctlr->name); return; } igfx->type = devtype(igfx); if(igfx->type < 0){ error("%s: unrecognized device\n", ctlr->name); return; } vgactlpci(igfx->pci); if(1){ vgactlw("type", ctlr->name); igfx->mmio = segattach(0, "igfxmmio", 0, igfx->pci->mem[0].size); if(igfx->mmio == (u32int*)-1) error("%s: attaching mmio: %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; } vga->private = igfx; } 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); igfx->dpll[0].fp1 = snarfreg(igfx, 0x06044); igfx->dpll[1].ctrl = snarfreg(igfx, 0x06018); igfx->dpll[1].fp0 = snarfreg(igfx, 0x06048); igfx->dpll[1].fp1 = snarfreg(igfx, 0x0604c); igfx->adpa = snarfreg(igfx, 0x061100); igfx->lvds = snarfreg(igfx, 0x061180); 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[x] = snarfreg(igfx, 0x5120); igfx->pfit[0].ctrl = snarfreg(igfx, 0x061230); y = (igfx->pfit[0].ctrl.v >> 29) & 3; if(igfx->pipe[y].pfit == nil) igfx->pipe[y].pfit = &igfx->pfit[0]; igfx->ppstatus = snarfreg(igfx, 0x61200); igfx->ppcontrol = snarfreg(igfx, 0x61204); igfx->vgacntrl = snarfreg(igfx, 0x071400); break; case TypeSNB: igfx->npipe = 2; /* A,B */ igfx->cdclk = 400; /* MHz */ goto IVBcommon; case TypeIVB: igfx->npipe = 3; /* A,B,C */ igfx->cdclk = 400; /* MHz */ goto IVBcommon; IVBcommon: igfx->dpll[0].ctrl = snarfreg(igfx, 0xC6014); igfx->dpll[0].fp0 = snarfreg(igfx, 0xC6040); igfx->dpll[0].fp1 = snarfreg(igfx, 0xC6044); igfx->dpll[1].ctrl = snarfreg(igfx, 0xC6018); igfx->dpll[1].fp0 = snarfreg(igfx, 0xC6048); igfx->dpll[1].fp1 = snarfreg(igfx, 0xC604c); igfx->dpllsel[0] = snarfreg(igfx, 0xC7000); igfx->drefctl = snarfreg(igfx, 0xC6200); igfx->ssc4params = snarfreg(igfx, 0xC6210); 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); igfx->dp[0].auxdat[2] = snarfreg(igfx, 0x6401C); igfx->dp[0].auxdat[3] = snarfreg(igfx, 0x64020); igfx->dp[0].auxdat[4] = snarfreg(igfx, 0x64024); /* pch displayport B,C,D */ for(x=1; x<4; 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); igfx->dp[x].auxdat[2] = snarfreg(igfx, 0xE401C + 0x100*x); igfx->dp[x].auxdat[3] = snarfreg(igfx, 0xE4020 + 0x100*x); igfx->dp[x].auxdat[4] = snarfreg(igfx, 0xE4024 + 0x100*x); } 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); igfx->pfit[x].ctrl = snarfreg(igfx, 0x68080 + 0x800*x); y = (igfx->pfit[x].ctrl.v >> 29) & 3; if(igfx->pipe[y].pfit == nil) igfx->pipe[y].pfit = &igfx->pfit[x]; } igfx->ppstatus = snarfreg(igfx, 0xC7200); igfx->ppcontrol = snarfreg(igfx, 0xC7204); 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->vgacntrl = snarfreg(igfx, 0x041000); break; } for(x=0; x<igfx->npipe; x++) snarfpipe(igfx, x); for(x=0; x<nelem(vga->edid); x++){ Modelist *l; 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 PortDPD: if(igfx->type == TypeHSW && igfx->isult) continue; case PortDPA: case PortDPB: case PortDPC: vga->edid[x] = snarfdpedid(igfx, &igfx->dp[x-PortDPA], 0x50); break; 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); } ctlr->flag |= Fsnarf; } static void options(Vga* vga, Ctlr* ctlr) { USED(vga); 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; int best, error; vlong a; best = -1; for(N=3; N<=8; N++) for(M2=5; M2<=9; M2++) // for(M1=10; M1<=20; M1++){ for(M1=12; M1<=22; M1++){ M = 5*(M1+2) + (M2+2); if(M < 79 || M > 127) // if(M < 70 || M > 120) continue; for(P1=1; P1<=8; P1++){ P = P1 * P2; if(P < 5 || P > 98) // if(P < 4 || P > 98) continue; a = cref; a *= M; a /= N+2; a /= P; if(a < 20*MHz || a > 400*MHz) continue; error = a; error -= freq; if(error < 0) error = -error; if(best < 0 || error < best){ best = error; *m1 = M1; *m2 = M2; *n = N; *p1 = P1; } } } return best; } static int getcref(Igfx *igfx, int x) { Dpll *dpll; dpll = &igfx->dpll[x]; if(igfx->type == TypeG45){ if(((dpll->ctrl.v >> 13) & 3) == 3) return 100*MHz; return 96*MHz; } return 120*MHz; } static int initdpll(Igfx *igfx, int x, int freq, int port) { int cref, m1, m2, n, n2, p1, p2, r2; Dpll *dpll; switch(igfx->type){ case TypeG45: /* PLL Reference Input Select */ dpll = igfx->pipe[x].dpll; dpll->ctrl.v &= ~(3<<13); dpll->ctrl.v |= (port == PortLCD ? 3 : 0) << 13; break; case TypeSNB: case TypeIVB: /* transcoder dpll enable */ igfx->dpllsel[0].v |= 8<<(x*4); /* program rawclock to 125MHz */ igfx->rawclkfreq.v = 125; igfx->drefctl.v &= ~(3<<13); igfx->drefctl.v &= ~(3<<11); igfx->drefctl.v &= ~(3<<9); igfx->drefctl.v &= ~(3<<7); igfx->drefctl.v &= ~3; if(port == PortLCD){ igfx->drefctl.v |= 2<<11; igfx->drefctl.v |= 1; } else { igfx->drefctl.v |= 2<<9; } /* * PLL Reference Input Select: * 000 DREFCLK (default is 120 MHz) for DAC/HDMI/DVI/DP * 001 Super SSC 120MHz super-spread clock * 011 SSC Spread spectrum input clock (120MHz default) for LVDS/DP */ dpll = igfx->pipe[x].fdi->dpll; 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; } cref = getcref(igfx, x); /* Dpll Mode select */ dpll->ctrl.v &= ~(3<<26); dpll->ctrl.v |= (port == PortLCD ? 2 : 1)<<26; /* P2 Clock Divide */ dpll->ctrl.v &= ~(3<<24); if(port == PortLCD){ p2 = 14; if(freq > 112*MHz){ p2 >>= 1; dpll->ctrl.v |= (1<<24); } 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; dpll->ctrl.v |= (1<<24); } if(genpll(freq, cref, p2, &m1, &m2, &n, &p1) < 0) return -1; } /* Dpll VCO Enable */ dpll->ctrl.v |= (1<<31); /* Dpll Serial DVO High Speed IO clock Enable */ if(port >= PortDPA) dpll->ctrl.v |= (1<<30); else dpll->ctrl.v &= ~(1<<30); /* VGA Mode Disable */ if(igfx->type == TypeG45) dpll->ctrl.v |= (1<<28); dpll->fp0.v &= ~(0x3f<<16); dpll->fp0.v |= n << 16; dpll->fp0.v &= ~(0x3f<<8); dpll->fp0.v |= m1 << 8; dpll->fp0.v &= ~(0x3f<<0); dpll->fp0.v |= m2 << 0; /* FP0 P1 Post Divisor */ dpll->ctrl.v &= ~0xFF0000; dpll->ctrl.v |= 0x010000<<(p1-1); /* FP1 P1 Post divisor */ if(igfx->pci->did != 0x27a2){ dpll->ctrl.v &= ~0xFF; dpll->ctrl.v |= 0x01<<(p1-1); dpll->fp1.v = dpll->fp0.v; } return 0; } static int needlanes(int freq, int lsclk, int bpp) { vlong v; int n; v = ((vlong)freq * bpp) / 8; for(n=1; n<4 && v>lsclk; n<<=1, v>>=1) ; return n; } static void initdatalinkmn(Trans *t, int freq, int lsclk, int lanes, int tu, int bpp) { u32int m, n; n = 0x800000; m = (n * (((uvlong)freq * bpp)/8)) / ((uvlong)lsclk * lanes); t->dm[0].v = (tu-1)<<25 | m; t->dn[0].v = n; n = 0x80000; m = ((uvlong)n * freq) / lsclk; t->lm[0].v = m; t->ln[0].v = n; t->dm[1].v = t->dm[0].v; t->dn[1].v = t->dn[0].v; t->lm[1].v = t->lm[0].v; t->ln[1].v = t->ln[0].v; } static void inittrans(Trans *t, Mode *m) { /* clear all but 27:28 frame start delay (initialized by bios) */ t->conf.v &= 3<<27; /* tans/pipe enable */ t->conf.v |= 1<<31; /* trans/pipe timing */ t->ht.v = (m->ht - 1)<<16 | (m->x - 1); t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1); t->vt.v = (m->vt - 1)<<16 | (m->y - 1); t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1); t->hb.v = t->ht.v; t->vb.v = t->vt.v; t->vss.v = 0; } static void initpipe(Igfx *igfx, Pipe *p, Mode *m, int bpc, int port) { static uchar bpctab[4] = { 8, 10, 6, 12 }; int i, tu, lanes; Fdi *fdi; /* source image size */ p->src.v = (m->x - 1)<<16 | (m->y - 1); if(p->pfit != nil){ /* panel fitter off */ p->pfit->ctrl.v &= ~(1<<31); p->pfit->winpos.v = 0; p->pfit->winsize.v = 0; } /* enable and set monitor timings for cpu pipe */ inittrans(p, m); /* default for displayport */ lanes = needlanes(m->frequency, 270*MHz, 3*bpc); tu = 64; fdi = p->fdi; if(fdi->rxctl.a != 0){ /* enable and set monitor timings for transcoder */ inittrans(fdi, m); /* tx port width selection */ fdi->txctl.v &= ~(7<<19); fdi->txctl.v |= (lanes-1)<<19; /* rx port width selection */ fdi->rxctl.v &= ~(7<<19); fdi->rxctl.v |= (lanes-1)<<19; /* bits per color for transcoder */ for(i=0; i<nelem(bpctab); i++){ if(bpctab[i] == bpc){ fdi->rxctl.v &= ~(7<<16); fdi->rxctl.v |= i<<16; fdi->dpctl.v &= ~(7<<9); fdi->dpctl.v |= i<<9; break; } } /* enhanced framing on */ fdi->rxctl.v |= (1<<6); fdi->txctl.v |= (1<<18); /* tusize 1 and 2 */ 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){ 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; } } initdatalinkmn(p, m->frequency, 270*MHz, lanes, tu, 3*bpc); } static void init(Vga* vga, Ctlr* ctlr) { int x, nl, port, bpc; char *val; Igfx *igfx; Pipe *p; Mode *m; Reg *r; m = vga->mode; if(m->z != 32) error("%s: unsupported color depth %d\n", ctlr->name, m->z); bpc = 8; /* bits per color channel */ igfx = vga->private; /* disable vga */ igfx->vgacntrl.v |= (1<<31); /* disable all pipes and ports */ 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 */ 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].fdi->dpctl.v &= ~(1<<31); 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){ port = atoi(val)-1; if(igfx->type == TypeHSW && !igfx->dp[port-PortDPA].hdmi) bpc = 6; }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->type == TypeHSW) /* unimplemented */ goto Badport; if(igfx->type == TypeG45) x = (igfx->adpa.v >> 30) & 1; else x = (igfx->adpa.v >> 29) & 3; 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->type == TypeHSW) goto Badport; if(igfx->type == TypeG45) x = (igfx->lvds.v >> 30) & 1; else x = (igfx->lvds.v >> 29) & 3; igfx->lvds.v |= (1<<31); igfx->ppcontrol.v |= 5; if(igfx->type == TypeG45){ igfx->lvds.v &= ~(1<<24); /* data format select 18/24bpc */ igfx->lvds.v &= ~(3<<20); if(m->hsync == '-') igfx->lvds.v ^= 1<<20; if(m->vsync == '-') igfx->lvds.v ^= 1<<21; igfx->lvds.v |= (1<<15); /* border enable */ } break; case PortDPA: 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; /* port reversal: off */ r->v &= ~(1<<15); /* reserved MBZ */ r->v &= ~(15<<11); /* displayport transcoder */ if(port == PortDPA){ /* reserved MBZ */ r->v &= ~(15<<10); /* 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){ /* reserved MBZ */ r->v &= ~(15<<11); /* 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]; /* plane enable, 32bpp */ 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); p->dsp->stride.v += 63; p->dsp->stride.v &= ~63; /* virtual width in pixels */ vga->virtx = p->dsp->stride.v / (m->z / 8); /* plane position and size */ p->dsp->pos.v = 0; p->dsp->size.v = (m->y - 1)<<16 | (m->x - 1); /* sic */ 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; if(igfx->type == TypeG45) p->cur->cntr.v |= x<<28; /* pipe assign */ p->cur->pos.v = 0; p->cur->base.v = 0; if(initdpll(igfx, x, m->frequency, port) < 0) error("%s: frequency %d out of range\n", ctlr->name, m->frequency); initpipe(igfx, p, m, bpc, port); ctlr->flag |= Finit; } static void loadtrans(Igfx *igfx, Trans *t) { int i; if(t->conf.a == 0) return; /* program trans/pipe timings */ loadreg(igfx, t->ht); loadreg(igfx, t->hb); loadreg(igfx, t->hs); loadreg(igfx, t->vt); loadreg(igfx, t->vb); loadreg(igfx, t->vs); loadreg(igfx, t->vss); loadreg(igfx, t->dm[0]); loadreg(igfx, t->dn[0]); loadreg(igfx, t->lm[0]); loadreg(igfx, t->ln[0]); loadreg(igfx, t->dm[1]); loadreg(igfx, t->dn[1]); loadreg(igfx, t->lm[1]); loadreg(igfx, t->ln[1]); if(t->dpll != nil && igfx->type != TypeHSW){ /* program dpll */ t->dpll->ctrl.v &= ~(1<<31); loadreg(igfx, t->dpll->ctrl); loadreg(igfx, t->dpll->fp0); loadreg(igfx, t->dpll->fp1); /* enable dpll */ t->dpll->ctrl.v |= (1<<31); loadreg(igfx, t->dpll->ctrl); sleep(10); } /* 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); loadreg(igfx, t->conf); for(i=0; i<100; i++){ sleep(10); if(rr(igfx, t->conf.a) & (1<<30)) break; } } static void enablepipe(Igfx *igfx, int x) { int i; Pipe *p; p = &igfx->pipe[x]; 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 */ p->fdi->rxctl.v |= (1<<13); /* enable pll */ loadreg(igfx, p->fdi->rxctl); sleep(5); p->fdi->rxctl.v |= (1<<4); /* pcdclk */ loadreg(igfx, p->fdi->rxctl); sleep(5); /* clear auto training bits */ if(igfx->type == TypeSNB) p->fdi->txctl.v &= ~(3<<28 | 1<<10 | 1); else p->fdi->txctl.v &= ~(7<<8 | 1); p->fdi->txctl.v &= ~(1<<31); /* disable */ p->fdi->txctl.v |= (1<<14); /* enable pll */ loadreg(igfx, p->fdi->txctl); sleep(5); } /* image size (vga needs to be off) */ loadreg(igfx, p->src); /* set panel fitter as needed */ if(p->pfit != nil){ loadreg(igfx, p->pfit->ctrl); loadreg(igfx, p->pfit->winpos); loadreg(igfx, p->pfit->winsize); /* arm */ } /* keep planes disabled while pipe comes up */ if(igfx->type == TypeG45) p->conf.v |= 3<<18; /* enable cpu pipe */ loadtrans(igfx, p); /* program plane */ loadreg(igfx, p->dsp->cntr); loadreg(igfx, p->dsp->linoff); loadreg(igfx, p->dsp->stride); loadreg(igfx, p->dsp->tileoff); 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); loadreg(igfx, p->cur->pos); loadreg(igfx, p->cur->base); /* arm */ /* enable planes */ if(igfx->type == TypeG45) { p->conf.v &= ~(3<<18); loadreg(igfx, p->conf); } if(p->fdi->rxctl.a != 0){ /* enable fdi */ loadreg(igfx, p->fdi->rxtu[1]); loadreg(igfx, p->fdi->rxtu[0]); loadreg(igfx, p->fdi->rxmisc); if(igfx->type == TypeSNB){ /* unmask bit lock and symbol lock bits */ csr(igfx, p->fdi->rximr.a, 3<<8, 0); p->fdi->txctl.v &= ~(3<<28); /* link train pattern1 */ p->fdi->txctl.v |= 1<<31; /* enable */ loadreg(igfx, p->fdi->txctl); p->fdi->rxctl.v &= ~(3<<8); /* link train pattern1 */ p->fdi->rxctl.v |= 1<<31; /* enable */ loadreg(igfx, p->fdi->rxctl); /* wait for bit lock */ for(i=0; i<10; i++){ sleep(1); if(rr(igfx, p->fdi->rxiir.a) & (1<<8)) break; } csr(igfx, p->fdi->rxiir.a, 0, 1<<8); /* switch to link train pattern2 */ csr(igfx, p->fdi->txctl.a, 3<<28, 1<<28); csr(igfx, p->fdi->rxctl.a, 3<<8, 1<<8); /* wait for symbol lock */ for(i=0; i<10; i++){ sleep(1); if(rr(igfx, p->fdi->rxiir.a) & (1<<9)) break; } csr(igfx, p->fdi->rxiir.a, 0, 1<<9); /* switch to link train normal */ csr(igfx, p->fdi->txctl.a, 0, 3<<28); csr(igfx, p->fdi->rxctl.a, 0, 3<<8); /* wait idle pattern time */ sleep(5); } else { p->fdi->rxctl.v &= ~(3<<8); /* link train pattern 00 */ p->fdi->rxctl.v |= 1<<10; /* auto train enable */ p->fdi->rxctl.v |= 1<<31; /* enable */ loadreg(igfx, p->fdi->rxctl); p->fdi->txctl.v &= ~(3<<8); /* link train pattern 00 */ p->fdi->txctl.v |= 1<<10; /* auto train enable */ p->fdi->txctl.v |= 1<<31; /* enable */ loadreg(igfx, p->fdi->txctl); /* wait for link training done */ for(i=0; i<200; i++){ sleep(5); if(rr(igfx, p->fdi->txctl.a) & 2) break; } } } /* enable the transcoder */ loadtrans(igfx, p->fdi); } static void disabletrans(Igfx *igfx, Trans *t) { int i; /* deselect pipe clock */ csr(igfx, t->clksel.a, 7<<29, 0); /* disable displayport transcoder */ 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); for(i=0; i<100; i++){ sleep(10); if((rr(igfx, t->conf.a) & (1<<30)) == 0) break; } /* workarround: clear timing override bit */ csr(igfx, t->chicken.a, 1<<31, 0); /* disable dpll */ if(igfx->type != TypeHSW && t->dpll != nil) csr(igfx, t->dpll->ctrl.a, 1<<31, 0); } static void disablepipe(Igfx *igfx, int x) { Pipe *p; p = &igfx->pipe[x]; /* planes off */ csr(igfx, p->dsp->cntr.a, 1<<31, 0); wr(igfx, p->dsp->surf.a, 0); /* arm */ /* cursor off */ 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); /* disable panel fitter */ if(p->pfit != nil) csr(igfx, p->pfit->ctrl.a, 1<<31, 0); /* disable fdi transmitter and receiver */ csr(igfx, p->fdi->txctl.a, 1<<31 | 1<<10, 0); csr(igfx, p->fdi->rxctl.a, 1<<31 | 1<<10, 0); /* disable pch transcoder */ disabletrans(igfx, p->fdi); /* disable pch dpll enable bit */ if(igfx->type != TypeHSW) csr(igfx, igfx->dpllsel[0].a, 8<<(x*4), 0); } void checkgtt(Igfx *igfx, Mode *m) { int fd, c; ulong n; char buf[64], *fl[5]; u32int i, j, pa, nilpte, *gtt; if(igfx->mmio == nil) return; gtt = (u32int*)((uchar*)igfx->mmio + igfx->pci->mem[0].size/2); pa = (gtt[0] & ~((1<<12)-1)) + (1<<12) | 1; for(i=1; i<64*1024/4-4; i++, pa+=1<<12) if((gtt[i] & ~((1<<11)-1<<1)) != pa) break; n = m->x * m->y * m->z / 8; if(i<<12 >= n) return; /* unmap pages past stolen memory */ nilpte = gtt[64*1024/4-5]; wr(igfx, 0x2170, 0); /* flush write buffers */ for(j=i; j<64*1024/4-5; j++){ if((gtt[j] & 1) == 0 || (gtt[j] & ~((1<<11)-1<<1)) == pa) break; pa = gtt[j]; gtt[j] = nilpte; } wr(igfx, 0x2170, 0); /* flush write buffers */ trace("%s: mapping %lud additional bytes for requested mode\n", igfx->ctlr->name, n - (i<<12)); snprint(buf, sizeof buf, "#g/igfxtra"); if((fd = open(buf, OREAD)) >= 0){ close(fd); if(remove(buf) < 0) goto err; } if((fd = create(buf, OREAD, DMDIR|0777)) < 0) goto err; close(fd); strncat(buf, "/ctl", sizeof(buf)-strlen("/ctl")); if((fd = open(buf, ORDWR|OTRUNC)) < 0) goto err; snprint(buf, sizeof buf, "va 0x10000000 %#lux fixed", n - (i<<12)); if(write(fd, buf, strlen(buf)) < 0){ close(fd); goto err; } seek(fd, 0, 0); if((c = read(fd, buf, sizeof buf)) <= 0){ close(fd); goto err; } close(fd); buf[c-1] = 0; if(getfields(buf, fl, nelem(fl), 0, " ") != nelem(fl) || (pa = strtoul(fl[4], nil, 16)) == 0){ werrstr("invalid physical base address"); goto err; } n >>= 12; wr(igfx, 0x2170, 0); /* flush write buffers */ for(; i<n; i++, pa+=1<<12) gtt[i] = pa | 1; wr(igfx, 0x2170, 0); /* flush write buffers */ return; err: trace("%s: checkgtt: %r\n", igfx->ctlr->name); } static void load(Vga* vga, Ctlr* ctlr) { Igfx *igfx; int x, y; igfx = vga->private; /* power lcd off */ if(igfx->ppcontrol.a != 0){ csr(igfx, igfx->ppcontrol.a, 0xFFFF0005, 0xABCD0000); for(x=0; x<5000; x++){ sleep(10); if((rr(igfx, igfx->ppstatus.a) & (1<<31)) == 0) break; } } /* disable ports */ csr(igfx, igfx->sdvob.a, (1<<29) | (1<<31), 0); 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++){ 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); /* disable vga plane */ csr(igfx, igfx->vgacntrl.a, 0, 1<<31); /* turn off all pipes */ for(x = 0; x < igfx->npipe; x++) disablepipe(igfx, x); /* check if enough memory has been mapped for requested mode */ checkgtt(igfx, vga->mode); if(igfx->type == TypeG45){ /* toggle dsp a on and off (from enable sequence) */ csr(igfx, igfx->pipe[0].conf.a, 3<<18, 0); csr(igfx, igfx->pipe[0].dsp->cntr.a, 0, 1<<31); wr(igfx, igfx->pipe[0].dsp->surf.a, 0); /* arm */ csr(igfx, igfx->pipe[0].dsp->cntr.a, 1<<31, 0); wr(igfx, igfx->pipe[0].dsp->surf.a, 0); /* arm */ 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 */ loadreg(igfx, igfx->lvds); /* new dpll setting */ for(x=0; x<nelem(igfx->dpllsel); x++) loadreg(igfx, igfx->dpllsel[x]); /* program all pipes */ for(x = 0; x < igfx->npipe; x++) enablepipe(igfx, x); /* program vga plane */ loadreg(igfx, igfx->vgacntrl); /* program ports */ loadreg(igfx, igfx->adpa); 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; } /* program lcd power */ loadreg(igfx, igfx->ppcontrol); ctlr->flag |= Fload; } static void dumpreg(char *name, char *item, Reg r) { if(r.a == 0) return; printitem(name, item); Bprint(&stdout, " [%.8ux] = %.8ux\n", r.a, r.v); } 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; if(t->dm[0].a != 0 && t->dm[0].v != 0){ tu = 1+((t->dm[0].v >> 25) & 0x3f); printitem(name, "dm1 tu"); Bprint(&stdout, " %d\n", tu); m = t->dm[0].v & 0xffffff; n = t->dn[0].v; if(n > 0){ printitem(name, "dm1/dn1"); Bprint(&stdout, " %f\n", (double)m / (double)n); } m = t->lm[0].v; n = t->ln[0].v; if(n > 0){ printitem(name, "lm1/ln1"); Bprint(&stdout, " %f\n", (double)m / (double)n); } } } static void dumptrans(char *name, Trans *t) { dumpreg(name, "conf", t->conf); dumpreg(name, "dm1", t->dm[0]); dumpreg(name, "dn1", t->dn[0]); dumpreg(name, "lm1", t->lm[0]); dumpreg(name, "ln1", t->ln[0]); dumpreg(name, "dm2", t->dm[1]); dumpreg(name, "dn2", t->dn[1]); dumpreg(name, "lm2", t->lm[1]); dumpreg(name, "ln2", t->ln[1]); dumptiming(name, t); dumpreg(name, "ht", t->ht); dumpreg(name, "hb", t->hb); dumpreg(name, "hs", t->hs); dumpreg(name, "vt", t->vt); dumpreg(name, "vb", t->vb); dumpreg(name, "vs", t->vs); dumpreg(name, "vss", t->vss); dumpreg(name, "dpctl", t->dpctl); dumpreg(name, "clksel", t->clksel); } static void dumppipe(Igfx *igfx, int x) { char name[32]; Pipe *p; p = &igfx->pipe[x]; snprint(name, sizeof(name), "%s pipe %c", igfx->ctlr->name, 'a'+x); dumpreg(name, "src", p->src); dumptrans(name, p); snprint(name, sizeof(name), "%s fdi %c", igfx->ctlr->name, 'a'+x); dumptrans(name, p->fdi); dumpreg(name, "txctl", p->fdi->txctl); dumpreg(name, "rxctl", p->fdi->rxctl); dumpreg(name, "rxmisc", p->fdi->rxmisc); dumpreg(name, "rxtu1", p->fdi->rxtu[0]); dumpreg(name, "rxtu2", p->fdi->rxtu[1]); snprint(name, sizeof(name), "%s dsp %c", igfx->ctlr->name, 'a'+x); dumpreg(name, "cntr", p->dsp->cntr); dumpreg(name, "linoff", p->dsp->linoff); 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); snprint(name, sizeof(name), "%s cur %c", igfx->ctlr->name, 'a'+x); dumpreg(name, "cntr", p->cur->cntr); dumpreg(name, "base", p->cur->base); dumpreg(name, "pos", p->cur->pos); } static void dumpdpll(Igfx *igfx, int x) { int cref, m1, m2, n, p1, p2; uvlong freq; char name[32]; Dpll *dpll; u32int m; dpll = &igfx->dpll[x]; snprint(name, sizeof(name), "%s dpll %c", igfx->ctlr->name, 'a'+x); dumpreg(name, "ctrl", dpll->ctrl); 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; m = (dpll->ctrl.v >> 16) & 0xFF; for(p1 = 1; p1 <= 8; p1++) if(m & (1<<(p1-1))) break; printitem(name, "ctrl p1"); Bprint(&stdout, " %d\n", p1); printitem(name, "ctrl p2"); Bprint(&stdout, " %d\n", p2); n = (dpll->fp0.v >> 16) & 0x3f; m1 = (dpll->fp0.v >> 8) & 0x3f; m2 = (dpll->fp0.v >> 0) & 0x3f; cref = getcref(igfx, x); freq = ((uvlong)cref * (5*(m1+2) + (m2+2)) / (n+2)) / (p1 * p2); printitem(name, "fp0 m1"); Bprint(&stdout, " %d\n", m1); printitem(name, "fp0 m2"); Bprint(&stdout, " %d\n", m2); printitem(name, "fp0 n"); Bprint(&stdout, " %d\n", n); printitem(name, "cref"); Bprint(&stdout, " %d\n", cref); printitem(name, "fp0 freq"); Bprint(&stdout, " %lld\n", freq); } static void dump(Vga* vga, Ctlr* ctlr) { char name[32]; Igfx *igfx; int x, y; if((igfx = vga->private) == nil) return; for(x=0; x<igfx->npipe; x++) dumppipe(igfx, x); for(x=0; x<nelem(igfx->dpll); x++) dumpdpll(igfx, x); 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); 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); 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); dumpreg(name, "ctl", igfx->hdmi[x].ctl); } for(x=0; x<nelem(igfx->pfit); x++){ snprint(name, sizeof(name), "%s pfit %c", ctlr->name, 'a'+x); dumpreg(name, "ctrl", igfx->pfit[x].ctrl); dumpreg(name, "winpos", igfx->pfit[x].winpos); dumpreg(name, "winsize", igfx->pfit[x].winsize); dumpreg(name, "pwrgate", igfx->pfit[x].pwrgate); } dumpreg(ctlr->name, "ppcontrol", igfx->ppcontrol); dumpreg(ctlr->name, "ppstatus", igfx->ppstatus); dumpreg(ctlr->name, "adpa", igfx->adpa); dumpreg(ctlr->name, "lvds", igfx->lvds); dumpreg(ctlr->name, "sdvob", igfx->sdvob); dumpreg(ctlr->name, "sdvoc", igfx->sdvoc); 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; 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", 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; u32int w; if(dp->ctl.a == 0) return 0; 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); w = dp->ctl.v >> (igfx->type == TypeHSW ? 1 : 19) & 7; wdpaux(igfx, dp, 0x101, w+1); 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); 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; 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; Edid *e; 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); 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 { 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 port, 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, port); 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 Edid* snarfgmedid(Igfx *igfx, int port, int addr) { uchar buf[256]; /* read twice */ if(gmbusread(igfx, port, addr, buf, 128) != 128) return nil; if(gmbusread(igfx, port, addr, buf + 128, 128) != 128) return nil; return parseedid128(edidshift(buf)); } Ctlr igfx = { "igfx", /* name */ snarf, /* snarf */ options, /* options */ init, /* init */ load, /* load */ dump, /* dump */ }; Ctlr igfxhwgc = { "igfxhwgc", };