diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-01-09 02:58:14 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-01-09 02:58:14 +0100 |
commit | ce8db466ba402ddb877495f66a7bddf156569fdf (patch) | |
tree | 9c82e32cf248f5a6c8d0eb18a06e963bd5150d20 | |
parent | 6fe180657f3a7a17801cb9aa36c8fe9abda34ac7 (diff) | |
download | plan9front-ce8db466ba402ddb877495f66a7bddf156569fdf.tar.xz |
igfx: just kidding, heres the code :)
-rw-r--r-- | sys/src/cmd/aux/vga/igfx.c | 1155 |
1 files changed, 1155 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/vga/igfx.c b/sys/src/cmd/aux/vga/igfx.c new file mode 100644 index 000000000..baebdddfc --- /dev/null +++ b/sys/src/cmd/aux/vga/igfx.c @@ -0,0 +1,1155 @@ +#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 Plane Plane; +typedef struct Trans Trans; +typedef struct Pipe Pipe; + +enum { + MHz = 1000000, +}; + +enum { + TypeG45, + TypeIVB, /* Ivy Bdige */ +}; + +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 */ + + Dpll *dpll; /* this transcoders dpll */ +}; + +struct Hdmi { + Reg ctl; + Reg bufctl[4]; +}; + +struct Dp { + Reg ctl; + Reg auxctl; + Reg auxdat[5]; +}; + +struct Fdi { + Trans; + + Reg txctl; /* FDI_TX_CTL */ + + Reg rxctl; /* FDI_RX_CTL */ + Reg rxmisc; /* FDI_RX_MISC */ + Reg rxtu[2]; /* FDI_RX_TUSIZE */ + + Reg dpctl; /* TRANS_DP_CTL_x */ +}; + +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 */ +}; + +struct Pipe { + Trans; + + Reg src; /* PIPExSRC */ + + Fdi fdi[1]; /* fdi/dp transcoder */ + + Plane dsp[1]; /* display plane */ + Plane cur[1]; /* cursor plane */ + + Pfit *pfit; /* selected pfit */ +}; + +typedef struct Igfx Igfx; +struct Igfx { + Ctlr *ctlr; + Pcidev *pci; + + u32int pio; + + int type; + + int npipe; + Pipe pipe[4]; + + Dpll dpll[2]; + Pfit pfit[3]; + + /* IVB */ + Reg dpllsel; /* DPLL_SEL */ + Reg drefctl; /* DREF_CTL */ + Reg rawclkfreq; /* RAWCLK_FREQ */ + Reg ssc4params; /* SSC4_PARAMS */ + + Dp dp[4]; + Hdmi hdmi[4]; + + Reg ppcontrol; + Reg ppstatus; + + /* G45 */ + 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); + 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); + 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: + 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; + } +} + +static void +snarfpipe(Igfx *igfx, int x) +{ + u32int o; + Pipe *p; + + p = &igfx->pipe[x]; + + o = 0x60000 | x*0x1000; + snarftrans(igfx, p, o); + + p->src = snarfreg(igfx, o + 0x0001C); + + if(igfx->type == TypeIVB) { + 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->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.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); + + /* cursor plane */ + switch(igfx->type){ + case TypeIVB: + p->cur->cntr = snarfreg(igfx, 0x70080 | x*0x1000); + p->cur->surf = snarfreg(igfx, 0x70084 | x*0x1000); + break; + case TypeG45: + p->cur->cntr = snarfreg(igfx, 0x70080 | x*0x40); + p->cur->surf = snarfreg(igfx, 0x70084 | x*0x40); + break; + } +} + +static int +devtype(Igfx *igfx) +{ + if(igfx->pci->vid != 0x8086) + return -1; + switch(igfx->pci->did){ + case 0x0166: /* X230 */ + return TypeIVB; + + case 0x2a42: /* X200s */ + return TypeG45; + } + return -1; +} + +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; + } + if((igfx->pci->mem[4].bar & 1) == 0){ + error("%s: no pio bar\n", ctlr->name); + return; + } + igfx->pio = igfx->pci->mem[4].bar & ~1; + vga->private = igfx; + } + + switch(igfx->type){ + case TypeG45: + igfx->npipe = 2; /* A,B */ + + 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); + + 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->vgacntrl = snarfreg(igfx, 0x071400); + break; + + case TypeIVB: + igfx->npipe = 3; /* A,B,C */ + + 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 = 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); + 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].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); + 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<3; x++){ + 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); + + 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->adpa = snarfreg(igfx, 0x0E1100); /* DAC_CTL */ + igfx->lvds = snarfreg(igfx, 0x0E1180); /* LVDS_CTL */ + + igfx->vgacntrl = snarfreg(igfx, 0x041000); + break; + } + + for(x=0; x<igfx->npipe; x++) + snarfpipe(igfx, x); + + ctlr->flag |= Fsnarf; +} + +static void +options(Vga* vga, Ctlr* ctlr) +{ + USED(vga); + ctlr->flag |= Hlinear|Ulinear|Foptions; +} + +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++){ + M = 5*(M1+2) + (M2+2); + if(M < 70 || M > 120) + continue; + for(P1=1; P1<=8; P1++){ + P = P1 * P2; + 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 +initdpll(Igfx *igfx, int x, int freq, int islvds, int ishdmi) +{ + int cref, m1, m2, n, p1, p2; + Dpll *dpll; + + switch(igfx->type){ + case TypeG45: + /* PLL Reference Input Select */ + dpll = igfx->pipe[x].dpll; + dpll->ctrl.v &= ~(3<<13); + dpll->ctrl.v |= (islvds ? 2 : 0) << 13; + cref = islvds ? 100*MHz : 96*MHz; + break; + case TypeIVB: + /* transcoder dpll enable */ + igfx->dpllsel.v |= 8<<(x*4); + /* program rawclock to 125MHz */ + igfx->rawclkfreq.v = 125; + if(islvds){ + /* 120MHz SSC integrated source enable */ + igfx->drefctl.v &= ~(3<<11); + igfx->drefctl.v |= 2<<11; + + /* 120MHz SSC4 modulation en */ + igfx->drefctl.v |= 2; + } + /* + * 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 |= (islvds ? 3 : 0) << 13; + cref = 120*MHz; + break; + default: + return -1; + } + + /* Dpll Mode select */ + dpll->ctrl.v &= ~(3<<26); + dpll->ctrl.v |= (islvds ? 2 : 1)<<26; + + /* P2 Clock Divide */ + dpll->ctrl.v &= ~(3<<24); + if(islvds){ + p2 = 14; + if(genpll(freq, cref, p2, &m1, &m2, &n, &p1) < 0) + return -1; + } else { + 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(ishdmi) + dpll->ctrl.v |= (1<<30); + else + dpll->ctrl.v &= ~(1<<30); + + /* VGA Mode Disable */ + dpll->ctrl.v |= (1<<28); + + /* P1 Post Divisor */ + dpll->ctrl.v &= ~0xFF00FF; + dpll->ctrl.v |= 0x10001<<(p1-1); + + 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; + + dpll->fp1.v = dpll->fp0.v; + + return 0; +} + +static void +initdatalinkmn(Trans *t, int freq, int lsclk, int lanes, int tu, int bpp) +{ + uvlong m, n; + + n = 0x800000; + m = (n * ((freq * bpp)/8)) / (lsclk * lanes); + t->dm[0].v = (tu-1)<<25 | m; + t->dn[0].v = n; + + n = 0x80000; + m = (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) +{ + /* tans/pipe enable */ + t->conf.v = 1<<31; + + /* trans/pipe timing */ + t->ht.v = (m->ht - 1)<<16 | (m->x - 1); + t->hb.v = t->ht.v; + t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1); + t->vt.v = (m->vt - 1)<<16 | (m->y - 1); + t->vb.v = t->vt.v; + t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1); + t->vss.v = 0; +} + +static void +initpipe(Pipe *p, Mode *m) +{ + static uchar bpctab[4] = { 8, 10, 6, 12 }; + int i, tu, bpc, lanes; + Fdi *fdi; + + /* source image size */ + p->src.v = (m->x - 1)<<16 | (m->y - 1); + + if(p->pfit != nil){ + /* panel fitter enable, hardcoded coefficients */ + p->pfit->ctrl.v = 1<<31 | 1<<23; + p->pfit->winpos.v = 0; + p->pfit->winsize.v = (m->x << 16) | m->y; + } + + /* enable and set monitor timings for cpu pipe */ + inittrans(p, m); + + /* default for displayport */ + tu = 64; + bpc = 8; + lanes = 1; + + fdi = p->fdi; + if(fdi->rxctl.a != 0){ + /* enable and set monitor timings for transcoder */ + inittrans(fdi, m); + + /* + * hack: + * we do not program fdi in load(), so use + * snarfed bios initialized values for now. + */ + if(fdi->rxctl.v & (1<<31)){ + tu = 1+(fdi->rxtu[0].v >> 25); + bpc = bpctab[(fdi->rxctl.v >> 16) & 3]; + lanes = 1+((fdi->rxctl.v >> 19) & 7); + } + + /* fdi tx enable */ + fdi->txctl.v |= (1<<31); + /* tx port width selection */ + fdi->txctl.v &= ~(7<<19); + fdi->txctl.v |= (lanes-1)<<19; + /* tx fdi pll enable */ + fdi->txctl.v |= (1<<14); + /* clear auto training bits */ + fdi->txctl.v &= ~(7<<8 | 1); + + /* fdi rx enable */ + fdi->rxctl.v |= (1<<31); + /* 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; + } + } + /* rx fdi pll enable */ + fdi->rxctl.v |= (1<<13); + /* rx fdi rawclk to pcdclk selection */ + fdi->rxctl.v |= (1<<4); + + /* 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); + } + + /* 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; + break; + } + } + initdatalinkmn(p, m->frequency, 270*MHz, lanes, tu, 3*bpc); +} + +static void +init(Vga* vga, Ctlr* ctlr) +{ + int x, islvds; + char *val; + Igfx *igfx; + Pipe *p; + Mode *m; + + m = vga->mode; + if(m->z != 32) + error("%s: unsupported color depth %d\n", ctlr->name, m->z); + + igfx = vga->private; + + /* disable vga */ + igfx->vgacntrl.v |= (1<<31); + + x = 0; + islvds = 0; + if((val = dbattr(m->attr, "lcd")) != nil && atoi(val) != 0){ + islvds = 1; + + if(igfx->npipe > 2) + x = (igfx->lvds.v >> 29) & 3; + else + x = (igfx->lvds.v >> 30) & 1; + igfx->lvds.v |= (1<<31); + + igfx->ppcontrol.v &= ~0xFFFF0000; + igfx->ppcontrol.v |= 5; + } + p = &igfx->pipe[x]; + + /* plane enable, 32bpp and assign pipe */ + p->dsp->cntr.v = (1<<31) | (6<<26) | (x<<24); + + /* 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); + + p->dsp->surf.v = 0; + p->dsp->linoff.v = 0; + p->dsp->tileoff.v = 0; + + /* cursor plane off */ + p->cur->cntr.v = 0; + p->cur->surf.v = 0; + + if(initdpll(igfx, x, m->frequency, islvds, 0) < 0) + error("%s: frequency %d out of range\n", ctlr->name, m->frequency); + + initpipe(p, m); + + /* + * undocumented magic that makes the flickering + * top bar go away on x230 on lcd. found by + * comparing registers set by vesa bios. + */ + if(igfx->type == TypeIVB && islvds) + p->conf.v |= 3<<27; + + ctlr->flag |= Finit; +} + +static void +loadtrans(Igfx *igfx, Trans *t) +{ + int i; + + /* 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){ + /* 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 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) +{ + Pipe *p; + + p = &igfx->pipe[x]; + if((p->conf.v & (1<<31)) == 0) + return; /* pipe is disabled, done */ + + if(0){ + p->fdi->rxctl.v &= ~(1<<31); + loadreg(igfx, p->fdi->rxctl); + sleep(5); + p->fdi->txctl.v &= ~(1<<31); + 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 */ + } + + /* 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->surf); /* arm */ + + loadreg(igfx, p->cur->cntr); + loadreg(igfx, p->cur->surf); /* arm */ + + if(0){ + /* enable fdi */ + loadreg(igfx, p->fdi->rxtu[1]); + loadreg(igfx, p->fdi->rxtu[0]); + loadreg(igfx, p->fdi->rxmisc); + p->fdi->rxctl.v &= ~(3<<8 | 1<<10 | 3); + p->fdi->rxctl.v |= (1<<31); + loadreg(igfx, p->fdi->rxctl); + + p->fdi->txctl.v &= ~(3<<8 | 1<<10 | 2); + p->fdi->txctl.v |= (1<<31); + loadreg(igfx, p->fdi->txctl); + } + + /* enable the transcoder */ + loadtrans(igfx, p->fdi); +} + +static void +disabletrans(Igfx *igfx, Trans *t) +{ + int i; + + /* the front fell off on x230 */ + if(igfx->type == TypeIVB && t == &igfx->pipe[0]) + goto skippipe; + + /* 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); + +skippipe: + /* disable dpll */ + if(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); + csr(igfx, p->dsp->surf.a, ~0, 0); /* arm */ + csr(igfx, p->cur->cntr.a, 1<<31, 0); + csr(igfx, p->cur->surf.a, ~0, 0); /* arm */ + + /* disable cpu pipe */ + disabletrans(igfx, p); + + /* disable panel fitter */ + if(p->pfit != nil) + csr(igfx, p->pfit->ctrl.a, 1<<31, 0); + + if(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 displayport transcoder */ + csr(igfx, p->fdi->dpctl.a, 1<<31, 3<<29); + + /* disable pch transcoder */ + disabletrans(igfx, p->fdi); + + /* disable pch dpll enable bit */ + csr(igfx, igfx->dpllsel.a, 8<<(x*4), 0); +} + +static void +load(Vga* vga, Ctlr* ctlr) +{ + Igfx *igfx; + int x; + + 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); + 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); + + /* program new clock sources */ + loadreg(igfx, igfx->rawclkfreq); + loadreg(igfx, igfx->drefctl); + sleep(10); + + /* set lvds before enabling dpll */ + loadreg(igfx, igfx->lvds); + + /* new dpll setting */ + loadreg(igfx, igfx->dpllsel); + + /* 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++) + 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); + } + + /* turn on lcd pfit */ + 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 +dumptiming(char *name, Trans *t) +{ + int tu, m, n; + + if(t->dm[0].a != 0 && t->dm[0].v != 0){ + tu = (t->dm[0].v >> 25)+1; + 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); +} + +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]); + dumpreg(name, "dpctl", p->fdi->dpctl); + + 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); + + snprint(name, sizeof(name), "%s cur %c", igfx->ctlr->name, 'a'+x); + dumpreg(name, "cntr", p->cur->cntr); + dumpreg(name, "surf", p->cur->surf); +} + +static void +dump(Vga* vga, Ctlr* ctlr) +{ + char name[32]; + Igfx *igfx; + int x; + + if((igfx = vga->private) == nil) + return; + + for(x=0; x<igfx->npipe; x++) + dumppipe(igfx, x); + + for(x=0; x<nelem(igfx->dpll); x++){ + snprint(name, sizeof(name), "%s dpll %c", ctlr->name, 'a'+x); + dumpreg(name, "ctrl", igfx->dpll[x].ctrl); + dumpreg(name, "fp0", igfx->dpll[x].fp0); + dumpreg(name, "fp1", igfx->dpll[x].fp1); + } + + dumpreg(ctlr->name, "dpllsel", igfx->dpllsel); + + 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++){ + snprint(name, sizeof(name), "dp %c ctl", 'a'+x); + dumpreg(ctlr->name, name, igfx->dp[x].ctl); + } + for(x=0; x<nelem(igfx->hdmi); x++){ + snprint(name, sizeof(name), "hdmi %c ctl ", 'a'+x); + dumpreg(ctlr->name, name, igfx->hdmi[x].ctl); + } + + for(x=0; x<nelem(igfx->pfit); x++){ + snprint(name, sizeof(name), "pfit %c ctrl", 'a'+x); + dumpreg(ctlr->name, name, igfx->pfit[x].ctrl); + snprint(name, sizeof(name), "pfit %c winpos", 'a'+x); + dumpreg(ctlr->name, name, igfx->pfit[x].winpos); + snprint(name, sizeof(name), "pfit %c winsize", 'a'+x); + dumpreg(ctlr->name, name, igfx->pfit[x].winsize); + snprint(name, sizeof(name), "pfit %c pwrgate", 'a'+x); + dumpreg(ctlr->name, name, igfx->pfit[x].pwrgate); + } + + 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); +} + +Ctlr igfx = { + "igfx", /* name */ + snarf, /* snarf */ + options, /* options */ + init, /* init */ + load, /* load */ + dump, /* dump */ +}; |