]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/vga/igfx.c
igfx: just kidding, heres the code :)
[plan9front.git] / sys / src / cmd / aux / vga / igfx.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 #include "pci.h"
6 #include "vga.h"
7
8 typedef struct Reg Reg;
9 typedef struct Dpll Dpll;
10 typedef struct Hdmi Hdmi;
11 typedef struct Dp Dp;
12 typedef struct Fdi Fdi;
13 typedef struct Pfit Pfit;
14 typedef struct Plane Plane;
15 typedef struct Trans Trans;
16 typedef struct Pipe Pipe;
17
18 enum {
19         MHz = 1000000,
20 };
21
22 enum {
23         TypeG45,
24         TypeIVB,                /* Ivy Bdige */
25 };
26
27 struct Reg {
28         u32int  a;              /* address or 0 when invalid */
29         u32int  v;              /* value */
30 };
31
32 struct Dpll {
33         Reg     ctrl;           /* DPLLx_CTRL */
34         Reg     fp0;            /* FPx0 */
35         Reg     fp1;            /* FPx1 */
36 };
37
38 struct Trans {
39         Reg     dm[2];          /* pipe/trans DATAM */
40         Reg     dn[2];          /* pipe/trans DATAN */
41         Reg     lm[2];          /* pipe/trans LINKM */
42         Reg     ln[2];          /* pipe/trans LINKN */
43
44         Reg     ht;             /* pipe/trans HTOTAL_x */
45         Reg     hb;             /* pipe/trans HBLANK_x */
46         Reg     hs;             /* pipe/trans HSYNC_x */
47         Reg     vt;             /* pipe/trans VTOTAL_x */
48         Reg     vb;             /* pipe/trans VBLANK_x */
49         Reg     vs;             /* pipe/trans VSYNC_x */
50         Reg     vss;            /* pipe/trans VSYNCSHIFT_x */
51
52         Reg     conf;           /* pipe/trans CONF_x */
53         Reg     chicken;        /* workarround register */
54
55         Dpll    *dpll;          /* this transcoders dpll */
56 };
57
58 struct Hdmi {
59         Reg     ctl;
60         Reg     bufctl[4];
61 };
62
63 struct Dp {
64         Reg     ctl;
65         Reg     auxctl;
66         Reg     auxdat[5];
67 };
68
69 struct Fdi {
70         Trans;
71
72         Reg     txctl;          /* FDI_TX_CTL */
73
74         Reg     rxctl;          /* FDI_RX_CTL */
75         Reg     rxmisc;         /* FDI_RX_MISC */
76         Reg     rxtu[2];        /* FDI_RX_TUSIZE */
77
78         Reg     dpctl;          /* TRANS_DP_CTL_x */
79 };
80
81 struct Pfit {
82         Reg     ctrl;
83         Reg     winpos;
84         Reg     winsize;
85         Reg     pwrgate;
86 };
87
88 struct Plane {
89         Reg     cntr;           /* DSPxCNTR */
90         Reg     linoff;         /* DSPxLINOFF */
91         Reg     stride;         /* DSPxSTRIDE */
92         Reg     surf;           /* DSPxSURF */
93         Reg     tileoff;        /* DSPxTILEOFF */
94 };
95
96 struct Pipe {
97         Trans;
98
99         Reg     src;            /* PIPExSRC */
100
101         Fdi     fdi[1];         /* fdi/dp transcoder */
102
103         Plane   dsp[1];         /* display plane */
104         Plane   cur[1];         /* cursor plane */
105
106         Pfit    *pfit;          /* selected pfit */
107 };
108
109 typedef struct Igfx Igfx;
110 struct Igfx {
111         Ctlr    *ctlr;
112         Pcidev  *pci;
113
114         u32int  pio;
115
116         int     type;
117
118         int     npipe;
119         Pipe    pipe[4];
120
121         Dpll    dpll[2];
122         Pfit    pfit[3];
123
124         /* IVB */
125         Reg     dpllsel;        /* DPLL_SEL */
126         Reg     drefctl;        /* DREF_CTL */
127         Reg     rawclkfreq;     /* RAWCLK_FREQ */
128         Reg     ssc4params;     /* SSC4_PARAMS */
129
130         Dp      dp[4];
131         Hdmi    hdmi[4];
132
133         Reg     ppcontrol;
134         Reg     ppstatus;
135
136         /* G45 */
137         Reg     sdvoc;
138         Reg     sdvob;
139
140         /* common */
141         Reg     adpa;
142         Reg     lvds;
143
144         Reg     vgacntrl;
145 };
146
147 static u32int
148 rr(Igfx *igfx, u32int a)
149 {
150         if(a == 0)
151                 return 0;
152         assert((a & 3) == 0);
153         outportl(igfx->pio, a);
154         return inportl(igfx->pio + 4);
155 }
156 static void
157 wr(Igfx *igfx, u32int a, u32int v)
158 {
159         if(a == 0)      /* invalid */
160                 return;
161
162         assert((a & 3) == 0);
163         outportl(igfx->pio, a);
164         outportl(igfx->pio + 4, v);
165 }
166 static void
167 csr(Igfx *igfx, u32int reg, u32int clr, u32int set)
168 {
169         wr(igfx, reg, (rr(igfx, reg) & ~clr) | set);
170 }
171
172 static void
173 loadreg(Igfx *igfx, Reg r)
174 {
175         wr(igfx, r.a, r.v);
176 }
177
178 static Reg
179 snarfreg(Igfx *igfx, u32int a)
180 {
181         Reg r;
182
183         r.a = a;
184         r.v = rr(igfx, a);
185         return r;
186 }
187
188 static void
189 snarftrans(Igfx *igfx, Trans *t, u32int o)
190 {
191         /* pipe timing */
192         t->ht   = snarfreg(igfx, o + 0x00000);
193         t->hb   = snarfreg(igfx, o + 0x00004);
194         t->hs   = snarfreg(igfx, o + 0x00008);
195         t->vt   = snarfreg(igfx, o + 0x0000C);
196         t->vb   = snarfreg(igfx, o + 0x00010);
197         t->vs   = snarfreg(igfx, o + 0x00014);
198         t->vss  = snarfreg(igfx, o + 0x00028);
199
200         t->conf = snarfreg(igfx, o + 0x10008);
201
202         switch(igfx->type){
203         case TypeG45:
204                 if(t == &igfx->pipe[0]){                        /* PIPEA */
205                         t->dm[0] = snarfreg(igfx, 0x70050);     /* GMCHDataM */
206                         t->dn[0] = snarfreg(igfx, 0x70054);     /* GMCHDataN */
207                         t->lm[0] = snarfreg(igfx, 0x70060);     /* DPLinkM */
208                         t->ln[0] = snarfreg(igfx, 0x70064);     /* DPLinkN */
209                 }
210                 break;
211         case TypeIVB:
212                 t->dm[0] = snarfreg(igfx, o + 0x30);
213                 t->dn[0] = snarfreg(igfx, o + 0x34);
214                 t->dm[1] = snarfreg(igfx, o + 0x38);
215                 t->dn[1] = snarfreg(igfx, o + 0x3c);
216                 t->lm[0] = snarfreg(igfx, o + 0x40);
217                 t->ln[0] = snarfreg(igfx, o + 0x44);
218                 t->lm[1] = snarfreg(igfx, o + 0x48);
219                 t->ln[1] = snarfreg(igfx, o + 0x4c);
220                 break;
221         }
222 }
223
224 static void
225 snarfpipe(Igfx *igfx, int x)
226 {
227         u32int o;
228         Pipe *p;
229
230         p = &igfx->pipe[x];
231
232         o = 0x60000 | x*0x1000;
233         snarftrans(igfx, p, o);
234
235         p->src = snarfreg(igfx, o + 0x0001C);
236
237         if(igfx->type == TypeIVB) {
238                 p->fdi->txctl = snarfreg(igfx, o + 0x100);
239
240                 o = 0xE0000 | x*0x1000;
241                 snarftrans(igfx, p->fdi, o);
242
243                 p->fdi->dpctl = snarfreg(igfx, o + 0x300);
244
245                 p->fdi->rxctl = snarfreg(igfx, o + 0x1000c);
246                 p->fdi->rxmisc = snarfreg(igfx, o + 0x10010);
247                 p->fdi->rxtu[0] = snarfreg(igfx, o + 0x10030);
248                 p->fdi->rxtu[1] = snarfreg(igfx, o + 0x10038);
249
250                 p->fdi->chicken = snarfreg(igfx, o + 0x10064);
251
252                 p->fdi->dpll = &igfx->dpll[(igfx->dpllsel.v>>(x*4)) & 1];
253                 p->dpll = nil;
254         } else {
255                 p->dpll = &igfx->dpll[x & 1];
256         }
257
258         /* display plane */
259         p->dsp->cntr            = snarfreg(igfx, 0x70180 | x*0x1000);
260         p->dsp->linoff          = snarfreg(igfx, 0x70184 | x*0x1000);
261         p->dsp->stride          = snarfreg(igfx, 0x70188 | x*0x1000);
262         p->dsp->tileoff         = snarfreg(igfx, 0x701A4 | x*0x1000);
263         p->dsp->surf            = snarfreg(igfx, 0x7019C | x*0x1000);
264
265         /* cursor plane */
266         switch(igfx->type){
267         case TypeIVB:
268                 p->cur->cntr    = snarfreg(igfx, 0x70080 | x*0x1000);
269                 p->cur->surf    = snarfreg(igfx, 0x70084 | x*0x1000);
270                 break;
271         case TypeG45:
272                 p->cur->cntr    = snarfreg(igfx, 0x70080 | x*0x40);
273                 p->cur->surf    = snarfreg(igfx, 0x70084 | x*0x40);
274                 break;
275         }
276 }
277
278 static int
279 devtype(Igfx *igfx)
280 {
281         if(igfx->pci->vid != 0x8086)
282                 return -1;
283         switch(igfx->pci->did){
284         case 0x0166:    /* X230 */
285                 return TypeIVB;
286
287         case 0x2a42:    /* X200s */
288                 return TypeG45;
289         }
290         return -1;
291 }
292
293 static void
294 snarf(Vga* vga, Ctlr* ctlr)
295 {
296         Igfx *igfx;
297         int x, y;
298
299         igfx = vga->private;
300         if(igfx == nil) {
301                 igfx = alloc(sizeof(Igfx));
302                 igfx->ctlr = ctlr;
303                 igfx->pci = vga->pci;
304                 if(igfx->pci == nil){
305                         error("%s: no pci device\n", ctlr->name);
306                         return;
307                 }
308                 igfx->type = devtype(igfx);
309                 if(igfx->type < 0){
310                         error("%s: unrecognized device\n", ctlr->name);
311                         return;
312                 }
313                 if((igfx->pci->mem[4].bar & 1) == 0){
314                         error("%s: no pio bar\n", ctlr->name);
315                         return;
316                 }
317                 igfx->pio = igfx->pci->mem[4].bar & ~1;
318                 vga->private = igfx;
319         }
320
321         switch(igfx->type){
322         case TypeG45:
323                 igfx->npipe = 2;        /* A,B */
324
325                 igfx->dpll[0].ctrl      = snarfreg(igfx, 0x06014);
326                 igfx->dpll[0].fp0       = snarfreg(igfx, 0x06040);
327                 igfx->dpll[0].fp1       = snarfreg(igfx, 0x06044);
328                 igfx->dpll[1].ctrl      = snarfreg(igfx, 0x06018);
329                 igfx->dpll[1].fp0       = snarfreg(igfx, 0x06048);
330                 igfx->dpll[1].fp1       = snarfreg(igfx, 0x0604c);
331
332                 igfx->adpa              = snarfreg(igfx, 0x061100);
333                 igfx->lvds              = snarfreg(igfx, 0x061180);
334                 igfx->sdvob             = snarfreg(igfx, 0x061140);
335                 igfx->sdvoc             = snarfreg(igfx, 0x061160);
336
337                 igfx->pfit[0].ctrl      = snarfreg(igfx, 0x061230);
338                 y = (igfx->pfit[0].ctrl.v >> 29) & 3;
339                 if(igfx->pipe[y].pfit == nil)
340                         igfx->pipe[y].pfit = &igfx->pfit[0];
341
342                 igfx->vgacntrl          = snarfreg(igfx, 0x071400);
343                 break;
344
345         case TypeIVB:
346                 igfx->npipe = 3;        /* A,B,C */
347
348                 igfx->dpll[0].ctrl      = snarfreg(igfx, 0xC6014);
349                 igfx->dpll[0].fp0       = snarfreg(igfx, 0xC6040);
350                 igfx->dpll[0].fp1       = snarfreg(igfx, 0xC6044);
351                 igfx->dpll[1].ctrl      = snarfreg(igfx, 0xC6018);
352                 igfx->dpll[1].fp0       = snarfreg(igfx, 0xC6048);
353                 igfx->dpll[1].fp1       = snarfreg(igfx, 0xC604c);
354
355                 igfx->dpllsel           = snarfreg(igfx, 0xC7000);
356
357                 igfx->drefctl           = snarfreg(igfx, 0xC6200);
358                 igfx->rawclkfreq        = snarfreg(igfx, 0xC6204);
359                 igfx->ssc4params        = snarfreg(igfx, 0xC6210);
360
361                 /* cpu displayport A*/
362                 igfx->dp[0].ctl         = snarfreg(igfx, 0x64000);
363                 igfx->dp[0].auxctl      = snarfreg(igfx, 0x64010);
364                 igfx->dp[0].auxdat[0]   = snarfreg(igfx, 0x64014);
365                 igfx->dp[0].auxdat[1]   = snarfreg(igfx, 0x64018);
366                 igfx->dp[0].auxdat[2]   = snarfreg(igfx, 0x6401C);
367                 igfx->dp[0].auxdat[3]   = snarfreg(igfx, 0x64020);
368                 igfx->dp[0].auxdat[4]   = snarfreg(igfx, 0x64024);
369
370                 /* pch displayport B,C,D */
371                 for(x=1; x<4; x++){
372                         igfx->dp[x].ctl         = snarfreg(igfx, 0xE4000 + 0x100*x);
373                         igfx->dp[x].auxctl      = snarfreg(igfx, 0xE4010 + 0x100*x);
374                         igfx->dp[x].auxdat[0]   = snarfreg(igfx, 0xE4014 + 0x100*x);
375                         igfx->dp[x].auxdat[1]   = snarfreg(igfx, 0xE4018 + 0x100*x);
376                         igfx->dp[x].auxdat[2]   = snarfreg(igfx, 0xE401C + 0x100*x);
377                         igfx->dp[x].auxdat[3]   = snarfreg(igfx, 0xE4020 + 0x100*x);
378                         igfx->dp[x].auxdat[4]   = snarfreg(igfx, 0xE4024 + 0x100*x);
379                 }
380
381                 for(x=0; x<3; x++){
382                         igfx->pfit[x].pwrgate   = snarfreg(igfx, 0x68060 + 0x800*x);
383                         igfx->pfit[x].winpos    = snarfreg(igfx, 0x68070 + 0x800*x);
384                         igfx->pfit[x].winsize   = snarfreg(igfx, 0x68074 + 0x800*x);
385                         igfx->pfit[x].ctrl      = snarfreg(igfx, 0x68080 + 0x800*x);
386
387                         y = (igfx->pfit[x].ctrl.v >> 29) & 3;
388                         if(igfx->pipe[y].pfit == nil)
389                                 igfx->pipe[y].pfit = &igfx->pfit[x];
390                 }
391                 igfx->ppstatus          = snarfreg(igfx, 0xC7200);
392                 igfx->ppcontrol         = snarfreg(igfx, 0xC7204);
393
394                 igfx->hdmi[1].ctl       = snarfreg(igfx, 0x0E1140);     /* HDMI_CTL_B */
395                 igfx->hdmi[1].bufctl[0] = snarfreg(igfx, 0x0FC810);     /* HTMI_BUF_CTL_0 */
396                 igfx->hdmi[1].bufctl[1] = snarfreg(igfx, 0x0FC81C);     /* HTMI_BUF_CTL_1 */
397                 igfx->hdmi[1].bufctl[2] = snarfreg(igfx, 0x0FC828);     /* HTMI_BUF_CTL_2 */
398                 igfx->hdmi[1].bufctl[3] = snarfreg(igfx, 0x0FC834);     /* HTMI_BUF_CTL_3 */
399
400                 igfx->hdmi[2].ctl       = snarfreg(igfx, 0x0E1150);     /* HDMI_CTL_C */
401                 igfx->hdmi[2].bufctl[0] = snarfreg(igfx, 0x0FCC00);     /* HTMI_BUF_CTL_4 */
402                 igfx->hdmi[2].bufctl[1] = snarfreg(igfx, 0x0FCC0C);     /* HTMI_BUF_CTL_5 */
403                 igfx->hdmi[2].bufctl[2] = snarfreg(igfx, 0x0FCC18);     /* HTMI_BUF_CTL_6 */
404                 igfx->hdmi[2].bufctl[3] = snarfreg(igfx, 0x0FCC24);     /* HTMI_BUF_CTL_7 */
405
406                 igfx->hdmi[3].ctl       = snarfreg(igfx, 0x0E1160);     /* HDMI_CTL_D */
407                 igfx->hdmi[3].bufctl[0] = snarfreg(igfx, 0x0FD000);     /* HTMI_BUF_CTL_8 */
408                 igfx->hdmi[3].bufctl[1] = snarfreg(igfx, 0x0FD00C);     /* HTMI_BUF_CTL_9 */
409                 igfx->hdmi[3].bufctl[2] = snarfreg(igfx, 0x0FD018);     /* HTMI_BUF_CTL_10 */
410                 igfx->hdmi[3].bufctl[3] = snarfreg(igfx, 0x0FD024);     /* HTMI_BUF_CTL_11 */
411
412                 igfx->adpa              = snarfreg(igfx, 0x0E1100);     /* DAC_CTL */
413                 igfx->lvds              = snarfreg(igfx, 0x0E1180);     /* LVDS_CTL */
414
415                 igfx->vgacntrl          = snarfreg(igfx, 0x041000);
416                 break;
417         }
418
419         for(x=0; x<igfx->npipe; x++)
420                 snarfpipe(igfx, x);
421
422         ctlr->flag |= Fsnarf;
423 }
424
425 static void
426 options(Vga* vga, Ctlr* ctlr)
427 {
428         USED(vga);
429         ctlr->flag |= Hlinear|Ulinear|Foptions;
430 }
431
432 static int
433 genpll(int freq, int cref, int P2, int *m1, int *m2, int *n, int *p1)
434 {
435         int M1, M2, M, N, P, P1;
436         int best, error;
437         vlong a;
438
439         best = -1;
440         for(N=3; N<=8; N++)
441         for(M2=5; M2<=9; M2++)
442         for(M1=10; M1<=20; M1++){
443                 M = 5*(M1+2) + (M2+2);
444                 if(M < 70 || M > 120)
445                         continue;
446                 for(P1=1; P1<=8; P1++){
447                         P = P1 * P2;
448                         if(P < 4 || P > 98)
449                                 continue;
450                         a = cref;
451                         a *= M;
452                         a /= N+2;
453                         a /= P;
454                         if(a < 20*MHz || a > 400*MHz)
455                                 continue;
456                         error = a;
457                         error -= freq;
458                         if(error < 0)
459                                 error = -error;
460                         if(best < 0 || error < best){
461                                 best = error;
462                                 *m1 = M1;
463                                 *m2 = M2;
464                                 *n = N;
465                                 *p1 = P1;
466                         }
467                 }
468         }
469         return best;
470 }
471
472 static int
473 initdpll(Igfx *igfx, int x, int freq, int islvds, int ishdmi)
474 {
475         int cref, m1, m2, n, p1, p2;
476         Dpll *dpll;
477
478         switch(igfx->type){
479         case TypeG45:
480                 /* PLL Reference Input Select */
481                 dpll = igfx->pipe[x].dpll;
482                 dpll->ctrl.v &= ~(3<<13);
483                 dpll->ctrl.v |= (islvds ? 2 : 0) << 13;
484                 cref = islvds ? 100*MHz : 96*MHz;
485                 break;
486         case TypeIVB:
487                 /* transcoder dpll enable */
488                 igfx->dpllsel.v |= 8<<(x*4);
489                 /* program rawclock to 125MHz */
490                 igfx->rawclkfreq.v = 125;
491                 if(islvds){
492                         /* 120MHz SSC integrated source enable */
493                         igfx->drefctl.v &= ~(3<<11);
494                         igfx->drefctl.v |= 2<<11;
495
496                         /* 120MHz SSC4 modulation en */ 
497                         igfx->drefctl.v |= 2;
498                 }
499                 /*
500                  * PLL Reference Input Select:
501                  * 000  DREFCLK         (default is 120 MHz) for DAC/HDMI/DVI/DP
502                  * 001  Super SSC       120MHz super-spread clock
503                  * 011  SSC             Spread spectrum input clock (120MHz default) for LVDS/DP
504                  */
505                 dpll = igfx->pipe[x].fdi->dpll;
506                 dpll->ctrl.v &= ~(7<<13);
507                 dpll->ctrl.v |= (islvds ? 3 : 0) << 13;
508                 cref = 120*MHz;
509                 break;
510         default:
511                 return -1;
512         }
513
514         /* Dpll Mode select */
515         dpll->ctrl.v &= ~(3<<26);
516         dpll->ctrl.v |= (islvds ? 2 : 1)<<26;
517
518         /* P2 Clock Divide */
519         dpll->ctrl.v &= ~(3<<24);       
520         if(islvds){
521                 p2 = 14;
522                 if(genpll(freq, cref, p2, &m1, &m2, &n, &p1) < 0)
523                         return -1;
524         } else {
525                 p2 = 10;
526                 if(freq > 270*MHz){
527                         p2 >>= 1;
528                         dpll->ctrl.v |= (1<<24);
529                 }
530                 if(genpll(freq, cref, p2, &m1, &m2, &n, &p1) < 0)
531                         return -1;
532         }
533
534         /* Dpll VCO Enable */
535         dpll->ctrl.v |= (1<<31);
536
537         /* Dpll Serial DVO High Speed IO clock Enable */
538         if(ishdmi)
539                 dpll->ctrl.v |= (1<<30);
540         else
541                 dpll->ctrl.v &= ~(1<<30);
542
543         /* VGA Mode Disable */
544         dpll->ctrl.v |= (1<<28);
545
546         /* P1 Post Divisor */
547         dpll->ctrl.v &= ~0xFF00FF;
548         dpll->ctrl.v |= 0x10001<<(p1-1);
549
550         dpll->fp0.v &= ~(0x3f<<16);
551         dpll->fp0.v |= n << 16;
552         dpll->fp0.v &= ~(0x3f<<8);
553         dpll->fp0.v |= m1 << 8;
554         dpll->fp0.v &= ~(0x3f<<0);
555         dpll->fp0.v |= m2 << 0;
556
557         dpll->fp1.v = dpll->fp0.v;
558
559         return 0;
560 }
561
562 static void
563 initdatalinkmn(Trans *t, int freq, int lsclk, int lanes, int tu, int bpp)
564 {
565         uvlong m, n;
566
567         n = 0x800000;
568         m = (n * ((freq * bpp)/8)) / (lsclk * lanes);
569         t->dm[0].v = (tu-1)<<25 | m;
570         t->dn[0].v = n;
571
572         n = 0x80000;
573         m = (n * freq) / lsclk;
574         t->lm[0].v = m;
575         t->ln[0].v = n;
576
577         t->dm[1].v = t->dm[0].v;
578         t->dn[1].v = t->dn[0].v;
579         t->lm[1].v = t->lm[0].v;
580         t->ln[1].v = t->ln[0].v;
581 }
582
583 static void
584 inittrans(Trans *t, Mode *m)
585 {
586         /* tans/pipe enable */
587         t->conf.v = 1<<31;
588
589         /* trans/pipe timing */
590         t->ht.v = (m->ht - 1)<<16 | (m->x - 1);
591         t->hb.v = t->ht.v;
592         t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1);
593         t->vt.v = (m->vt - 1)<<16 | (m->y - 1);
594         t->vb.v = t->vt.v;
595         t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1);
596         t->vss.v = 0;
597 }
598
599 static void
600 initpipe(Pipe *p, Mode *m)
601 {
602         static uchar bpctab[4] = { 8, 10, 6, 12 };
603         int i, tu, bpc, lanes;
604         Fdi *fdi;
605
606         /* source image size */
607         p->src.v = (m->x - 1)<<16 | (m->y - 1);
608
609         if(p->pfit != nil){
610                 /* panel fitter enable, hardcoded coefficients */
611                 p->pfit->ctrl.v = 1<<31 | 1<<23;
612                 p->pfit->winpos.v = 0;
613                 p->pfit->winsize.v = (m->x << 16) | m->y;
614         }
615
616         /* enable and set monitor timings for cpu pipe */
617         inittrans(p, m);
618
619         /* default for displayport */
620         tu = 64;
621         bpc = 8;
622         lanes = 1;
623
624         fdi = p->fdi;
625         if(fdi->rxctl.a != 0){
626                 /* enable and set monitor timings for transcoder */
627                 inittrans(fdi, m);
628
629                 /*
630                  * hack:
631                  * we do not program fdi in load(), so use
632                  * snarfed bios initialized values for now.
633                  */
634                 if(fdi->rxctl.v & (1<<31)){
635                         tu = 1+(fdi->rxtu[0].v >> 25);
636                         bpc = bpctab[(fdi->rxctl.v >> 16) & 3];
637                         lanes = 1+((fdi->rxctl.v >> 19) & 7);
638                 }
639
640                 /* fdi tx enable */
641                 fdi->txctl.v |= (1<<31);
642                 /* tx port width selection */
643                 fdi->txctl.v &= ~(7<<19);
644                 fdi->txctl.v |= (lanes-1)<<19;
645                 /* tx fdi pll enable */
646                 fdi->txctl.v |= (1<<14);
647                 /* clear auto training bits */
648                 fdi->txctl.v &= ~(7<<8 | 1);
649
650                 /* fdi rx enable */
651                 fdi->rxctl.v |= (1<<31);
652                 /* rx port width selection */
653                 fdi->rxctl.v &= ~(7<<19);
654                 fdi->rxctl.v |= (lanes-1)<<19;
655                 /* bits per color for transcoder */
656                 for(i=0; i<nelem(bpctab); i++){
657                         if(bpctab[i] == bpc){
658                                 fdi->rxctl.v &= ~(7<<16);
659                                 fdi->rxctl.v |= i<<16;
660                                 fdi->dpctl.v &= ~(7<<9);
661                                 fdi->dpctl.v |= i<<9;
662                                 break;
663                         }
664                 }
665                 /* rx fdi pll enable */
666                 fdi->rxctl.v |= (1<<13);
667                 /* rx fdi rawclk to pcdclk selection */
668                 fdi->rxctl.v |= (1<<4);
669
670                 /* tusize 1 and 2 */
671                 fdi->rxtu[0].v = (tu-1)<<25;
672                 fdi->rxtu[1].v = (tu-1)<<25;
673                 initdatalinkmn(fdi, m->frequency, 270*MHz, lanes, tu, 3*bpc);
674         }
675
676         /* bits per color for cpu pipe */
677         for(i=0; i<nelem(bpctab); i++){
678                 if(bpctab[i] == bpc){
679                         p->conf.v &= ~(7<<5);
680                         p->conf.v |= i<<5;
681                         break;
682                 }
683         }
684         initdatalinkmn(p, m->frequency, 270*MHz, lanes, tu, 3*bpc);
685 }
686
687 static void
688 init(Vga* vga, Ctlr* ctlr)
689 {
690         int x, islvds;
691         char *val;
692         Igfx *igfx;
693         Pipe *p;
694         Mode *m;
695
696         m = vga->mode;
697         if(m->z != 32)
698                 error("%s: unsupported color depth %d\n", ctlr->name, m->z);
699
700         igfx = vga->private;
701
702         /* disable vga */
703         igfx->vgacntrl.v |= (1<<31);
704
705         x = 0;
706         islvds = 0;
707         if((val = dbattr(m->attr, "lcd")) != nil && atoi(val) != 0){
708                 islvds = 1;
709
710                 if(igfx->npipe > 2)
711                         x = (igfx->lvds.v >> 29) & 3;
712                 else
713                         x = (igfx->lvds.v >> 30) & 1;
714                 igfx->lvds.v |= (1<<31);
715
716                 igfx->ppcontrol.v &= ~0xFFFF0000;
717                 igfx->ppcontrol.v |= 5;
718         }
719         p = &igfx->pipe[x];
720
721         /* plane enable, 32bpp and assign pipe */
722         p->dsp->cntr.v = (1<<31) | (6<<26) | (x<<24);
723
724         /* stride must be 64 byte aligned */
725         p->dsp->stride.v = m->x * (m->z / 8);
726         p->dsp->stride.v += 63;
727         p->dsp->stride.v &= ~63;
728
729         /* virtual width in pixels */
730         vga->virtx = p->dsp->stride.v / (m->z / 8);
731
732         p->dsp->surf.v = 0;
733         p->dsp->linoff.v = 0;
734         p->dsp->tileoff.v = 0;
735
736         /* cursor plane off */
737         p->cur->cntr.v = 0;
738         p->cur->surf.v = 0;
739
740         if(initdpll(igfx, x, m->frequency, islvds, 0) < 0)
741                 error("%s: frequency %d out of range\n", ctlr->name, m->frequency);
742
743         initpipe(p, m);
744
745         /*
746          * undocumented magic that makes the flickering
747          * top bar go away on x230 on lcd. found by
748          * comparing registers set by vesa bios.
749          */
750         if(igfx->type == TypeIVB && islvds)
751                 p->conf.v |= 3<<27;
752
753         ctlr->flag |= Finit;
754 }
755
756 static void
757 loadtrans(Igfx *igfx, Trans *t)
758 {
759         int i;
760
761         /* program trans/pipe timings */
762         loadreg(igfx, t->ht);
763         loadreg(igfx, t->hb);
764         loadreg(igfx, t->hs);
765         loadreg(igfx, t->vt);
766         loadreg(igfx, t->vb);
767         loadreg(igfx, t->vs);
768         loadreg(igfx, t->vss);
769
770         loadreg(igfx, t->dm[0]);
771         loadreg(igfx, t->dn[0]);
772         loadreg(igfx, t->lm[0]);
773         loadreg(igfx, t->ln[0]);
774         loadreg(igfx, t->dm[1]);
775         loadreg(igfx, t->dn[1]);
776         loadreg(igfx, t->lm[1]);
777         loadreg(igfx, t->ln[1]);
778
779         if(t->dpll != nil){
780                 /* program dpll */
781                 t->dpll->ctrl.v &= ~(1<<31);
782                 loadreg(igfx, t->dpll->ctrl);
783                 loadreg(igfx, t->dpll->fp0);
784                 loadreg(igfx, t->dpll->fp1);
785
786                 /* enable dpll */
787                 t->dpll->ctrl.v |= (1<<31);
788                 loadreg(igfx, t->dpll->ctrl);
789                 sleep(10);
790         }
791
792         /* workarround: set timing override bit */
793         csr(igfx, t->chicken.a, 0, 1<<31);
794
795         /* enable trans/pipe */
796         t->conf.v |= (1<<31);
797         t->conf.v &= ~(1<<30);
798         loadreg(igfx, t->conf);
799         for(i=0; i<100; i++){
800                 sleep(10);
801                 if(rr(igfx, t->conf.a) & (1<<30))
802                         break;
803         }
804 }
805
806 static void
807 enablepipe(Igfx *igfx, int x)
808 {
809         Pipe *p;
810
811         p = &igfx->pipe[x];
812         if((p->conf.v & (1<<31)) == 0)
813                 return; /* pipe is disabled, done */
814
815         if(0){
816                 p->fdi->rxctl.v &= ~(1<<31);
817                 loadreg(igfx, p->fdi->rxctl);
818                 sleep(5);
819                 p->fdi->txctl.v &= ~(1<<31);
820                 loadreg(igfx, p->fdi->txctl);
821                 sleep(5);
822         }
823
824         /* image size (vga needs to be off) */
825         loadreg(igfx, p->src);
826
827         /* set panel fitter as needed */
828         if(p->pfit != nil){
829                 loadreg(igfx, p->pfit->ctrl);
830                 loadreg(igfx, p->pfit->winpos);
831                 loadreg(igfx, p->pfit->winsize);        /* arm */
832         }
833
834         /* enable cpu pipe */
835         loadtrans(igfx, p);
836
837         /* program plane */
838         loadreg(igfx, p->dsp->cntr);
839         loadreg(igfx, p->dsp->linoff);
840         loadreg(igfx, p->dsp->stride);
841         loadreg(igfx, p->dsp->tileoff);
842         loadreg(igfx, p->dsp->surf);    /* arm */
843
844         loadreg(igfx, p->cur->cntr);
845         loadreg(igfx, p->cur->surf);    /* arm */
846
847         if(0){
848                 /* enable fdi */
849                 loadreg(igfx, p->fdi->rxtu[1]);
850                 loadreg(igfx, p->fdi->rxtu[0]);
851                 loadreg(igfx, p->fdi->rxmisc);
852                 p->fdi->rxctl.v &= ~(3<<8 | 1<<10 | 3);
853                 p->fdi->rxctl.v |= (1<<31);
854                 loadreg(igfx, p->fdi->rxctl);
855
856                 p->fdi->txctl.v &= ~(3<<8 | 1<<10 | 2);
857                 p->fdi->txctl.v |= (1<<31);
858                 loadreg(igfx, p->fdi->txctl);
859         }
860
861         /* enable the transcoder */
862         loadtrans(igfx, p->fdi);
863 }
864
865 static void
866 disabletrans(Igfx *igfx, Trans *t)
867 {
868         int i;
869
870         /* the front fell off on x230 */
871         if(igfx->type == TypeIVB && t == &igfx->pipe[0])
872                 goto skippipe;
873
874         /* disable transcoder / pipe */
875         csr(igfx, t->conf.a, 1<<31, 0);
876         for(i=0; i<100; i++){
877                 sleep(10);
878                 if((rr(igfx, t->conf.a) & (1<<30)) == 0)
879                         break;
880         }
881         /* workarround: clear timing override bit */
882         csr(igfx, t->chicken.a, 1<<31, 0);
883
884 skippipe:
885         /* disable dpll  */
886         if(t->dpll != nil)
887                 csr(igfx, t->dpll->ctrl.a, 1<<31, 0);
888 }
889
890 static void
891 disablepipe(Igfx *igfx, int x)
892 {
893         Pipe *p;
894
895         p = &igfx->pipe[x];
896
897         /* planes off */
898         csr(igfx, p->dsp->cntr.a, 1<<31, 0);
899         csr(igfx, p->dsp->surf.a, ~0, 0);       /* arm */
900         csr(igfx, p->cur->cntr.a, 1<<31, 0);
901         csr(igfx, p->cur->surf.a, ~0, 0);       /* arm */
902
903         /* disable cpu pipe */
904         disabletrans(igfx, p);
905
906         /* disable panel fitter */
907         if(p->pfit != nil)
908                 csr(igfx, p->pfit->ctrl.a, 1<<31, 0);
909
910         if(0){
911                 /* disable fdi transmitter and receiver */
912                 csr(igfx, p->fdi->txctl.a, 1<<31 | 1<<10, 0);
913                 csr(igfx, p->fdi->rxctl.a, 1<<31 | 1<<10, 0);
914         }
915
916         /* disable displayport transcoder */
917         csr(igfx, p->fdi->dpctl.a, 1<<31, 3<<29);
918
919         /* disable pch transcoder */
920         disabletrans(igfx, p->fdi);
921
922         /* disable pch dpll enable bit */
923         csr(igfx, igfx->dpllsel.a, 8<<(x*4), 0);
924 }
925
926 static void
927 load(Vga* vga, Ctlr* ctlr)
928 {
929         Igfx *igfx;
930         int x;
931
932         igfx = vga->private;
933
934         /* power lcd off */
935         if(igfx->ppcontrol.a != 0){
936                 csr(igfx, igfx->ppcontrol.a, 0xFFFF0005, 0xABCD0000);
937                 for(x=0; x<5000; x++){
938                         sleep(10);
939                         if((rr(igfx, igfx->ppstatus.a) & (1<<31)) == 0)
940                                 break;
941                 }
942         }
943
944         /* disable ports */
945         csr(igfx, igfx->sdvob.a, (1<<29) | (1<<31), 0);
946         csr(igfx, igfx->sdvoc.a, (1<<29) | (1<<31), 0);
947         csr(igfx, igfx->adpa.a, 1<<31, 0);
948         csr(igfx, igfx->lvds.a, 1<<31, 0);
949         for(x = 0; x < nelem(igfx->dp); x++)
950                 csr(igfx, igfx->dp[x].ctl.a, 1<<31, 0);
951         for(x = 0; x < nelem(igfx->hdmi); x++)
952                 csr(igfx, igfx->hdmi[x].ctl.a, 1<<31, 0);
953
954         /* disable vga plane */
955         csr(igfx, igfx->vgacntrl.a, 0, 1<<31);
956
957         /* turn off all pipes */
958         for(x = 0; x < igfx->npipe; x++)
959                 disablepipe(igfx, x);
960
961         /* program new clock sources */
962         loadreg(igfx, igfx->rawclkfreq);
963         loadreg(igfx, igfx->drefctl);
964         sleep(10);
965
966         /* set lvds before enabling dpll */
967         loadreg(igfx, igfx->lvds);
968
969         /* new dpll setting */
970         loadreg(igfx, igfx->dpllsel);
971
972         /* program all pipes */
973         for(x = 0; x < igfx->npipe; x++)
974                 enablepipe(igfx, x);
975
976         /* program vga plane */
977         loadreg(igfx, igfx->vgacntrl);
978
979         /* program ports */
980         loadreg(igfx, igfx->adpa);
981         loadreg(igfx, igfx->sdvob);
982         loadreg(igfx, igfx->sdvoc);
983         for(x = 0; x < nelem(igfx->dp); x++)
984                 loadreg(igfx, igfx->dp[x].ctl);
985         for(x=0; x<nelem(igfx->hdmi); x++){
986                 loadreg(igfx, igfx->hdmi[x].bufctl[0]);
987                 loadreg(igfx, igfx->hdmi[x].bufctl[1]);
988                 loadreg(igfx, igfx->hdmi[x].bufctl[2]);
989                 loadreg(igfx, igfx->hdmi[x].bufctl[3]);
990                 loadreg(igfx, igfx->hdmi[x].ctl);
991         }
992
993         /* turn on lcd pfit */
994         loadreg(igfx, igfx->ppcontrol);
995
996         ctlr->flag |= Fload;
997 }
998
999 static void
1000 dumpreg(char *name, char *item, Reg r)
1001 {
1002         if(r.a == 0)
1003                 return;
1004
1005         printitem(name, item);
1006         Bprint(&stdout, " [%.8ux] = %.8ux\n", r.a, r.v);
1007 }
1008
1009 static void
1010 dumptiming(char *name, Trans *t)
1011 {
1012         int tu, m, n;
1013
1014         if(t->dm[0].a != 0 && t->dm[0].v != 0){
1015                 tu = (t->dm[0].v >> 25)+1;
1016                 printitem(name, "dm1 tu");
1017                 Bprint(&stdout, " %d\n", tu);
1018
1019                 m = t->dm[0].v & 0xffffff;
1020                 n = t->dn[0].v;
1021                 if(n > 0){
1022                         printitem(name, "dm1/dn1");
1023                         Bprint(&stdout, " %f\n", (double)m / (double)n);
1024                 }
1025
1026                 m = t->lm[0].v;
1027                 n = t->ln[0].v;
1028                 if(n > 0){
1029                         printitem(name, "lm1/ln1");
1030                         Bprint(&stdout, " %f\n", (double)m / (double)n);
1031                 }
1032         }
1033 }
1034
1035 static void
1036 dumptrans(char *name, Trans *t)
1037 {
1038         dumpreg(name, "conf", t->conf);
1039
1040         dumpreg(name, "dm1", t->dm[0]);
1041         dumpreg(name, "dn1", t->dn[0]);
1042         dumpreg(name, "lm1", t->lm[0]);
1043         dumpreg(name, "ln1", t->ln[0]);
1044         dumpreg(name, "dm2", t->dm[1]);
1045         dumpreg(name, "dn2", t->dn[1]);
1046         dumpreg(name, "lm2", t->lm[1]);
1047         dumpreg(name, "ln2", t->ln[1]);
1048
1049         dumptiming(name, t);
1050
1051         dumpreg(name, "ht", t->ht);
1052         dumpreg(name, "hb", t->hb);
1053         dumpreg(name, "hs", t->hs);
1054
1055         dumpreg(name, "vt", t->vt);
1056         dumpreg(name, "vb", t->vb);
1057         dumpreg(name, "vs", t->vs);
1058         dumpreg(name, "vss", t->vss);
1059 }
1060
1061 static void
1062 dumppipe(Igfx *igfx, int x)
1063 {
1064         char name[32];
1065         Pipe *p;
1066
1067         p = &igfx->pipe[x];
1068
1069         snprint(name, sizeof(name), "%s pipe %c", igfx->ctlr->name, 'a'+x);
1070         dumpreg(name, "src", p->src);
1071         dumptrans(name, p);
1072
1073         snprint(name, sizeof(name), "%s fdi %c", igfx->ctlr->name, 'a'+x);
1074         dumptrans(name, p->fdi);
1075         dumpreg(name, "txctl", p->fdi->txctl);
1076         dumpreg(name, "rxctl", p->fdi->rxctl);
1077         dumpreg(name, "rxmisc", p->fdi->rxmisc);
1078         dumpreg(name, "rxtu1", p->fdi->rxtu[0]);
1079         dumpreg(name, "rxtu2", p->fdi->rxtu[1]);
1080         dumpreg(name, "dpctl", p->fdi->dpctl);
1081
1082         snprint(name, sizeof(name), "%s dsp %c", igfx->ctlr->name, 'a'+x);
1083         dumpreg(name, "cntr", p->dsp->cntr);
1084         dumpreg(name, "linoff", p->dsp->linoff);
1085         dumpreg(name, "stride", p->dsp->stride);
1086         dumpreg(name, "surf", p->dsp->surf);
1087         dumpreg(name, "tileoff", p->dsp->tileoff);
1088
1089         snprint(name, sizeof(name), "%s cur %c", igfx->ctlr->name, 'a'+x);
1090         dumpreg(name, "cntr", p->cur->cntr);
1091         dumpreg(name, "surf", p->cur->surf);
1092 }
1093
1094 static void
1095 dump(Vga* vga, Ctlr* ctlr)
1096 {
1097         char name[32];
1098         Igfx *igfx;
1099         int x;
1100
1101         if((igfx = vga->private) == nil)
1102                 return;
1103
1104         for(x=0; x<igfx->npipe; x++)
1105                 dumppipe(igfx, x);
1106
1107         for(x=0; x<nelem(igfx->dpll); x++){
1108                 snprint(name, sizeof(name), "%s dpll %c", ctlr->name, 'a'+x);
1109                 dumpreg(name, "ctrl", igfx->dpll[x].ctrl);
1110                 dumpreg(name, "fp0", igfx->dpll[x].fp0);
1111                 dumpreg(name, "fp1", igfx->dpll[x].fp1);
1112         }
1113
1114         dumpreg(ctlr->name, "dpllsel", igfx->dpllsel);
1115
1116         dumpreg(ctlr->name, "drefctl", igfx->drefctl);
1117         dumpreg(ctlr->name, "rawclkfreq", igfx->rawclkfreq);
1118         dumpreg(ctlr->name, "ssc4params", igfx->ssc4params);
1119
1120         for(x=0; x<nelem(igfx->dp); x++){
1121                 snprint(name, sizeof(name), "dp %c ctl", 'a'+x);
1122                 dumpreg(ctlr->name, name, igfx->dp[x].ctl);
1123         }
1124         for(x=0; x<nelem(igfx->hdmi); x++){
1125                 snprint(name, sizeof(name), "hdmi %c ctl ", 'a'+x);
1126                 dumpreg(ctlr->name, name, igfx->hdmi[x].ctl);
1127         }
1128
1129         for(x=0; x<nelem(igfx->pfit); x++){
1130                 snprint(name, sizeof(name), "pfit %c ctrl", 'a'+x);
1131                 dumpreg(ctlr->name, name, igfx->pfit[x].ctrl);
1132                 snprint(name, sizeof(name), "pfit %c winpos", 'a'+x);
1133                 dumpreg(ctlr->name, name, igfx->pfit[x].winpos);
1134                 snprint(name, sizeof(name), "pfit %c winsize", 'a'+x);
1135                 dumpreg(ctlr->name, name, igfx->pfit[x].winsize);
1136                 snprint(name, sizeof(name), "pfit %c pwrgate", 'a'+x);
1137                 dumpreg(ctlr->name, name, igfx->pfit[x].pwrgate);
1138         }
1139
1140         dumpreg(ctlr->name, "adpa", igfx->adpa);
1141         dumpreg(ctlr->name, "lvds", igfx->lvds);
1142         dumpreg(ctlr->name, "sdvob", igfx->sdvob);
1143         dumpreg(ctlr->name, "sdvoc", igfx->sdvoc);
1144
1145         dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
1146 }
1147
1148 Ctlr igfx = {
1149         "igfx",                 /* name */
1150         snarf,                  /* snarf */
1151         options,                /* options */
1152         init,                   /* init */
1153         load,                   /* load */
1154         dump,                   /* dump */
1155 };