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