added NV10 EXA functions to proto header
[nouveau] / src / nv50_display.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef ENABLE_RANDR12
30
31 #include <float.h>
32 #include <math.h>
33 #include <strings.h>
34 #include <unistd.h>
35
36 #include "nv_include.h"
37 #include "nv50_type.h"
38 #include "nv50_cursor.h"
39 #include "nv50_display.h"
40 #include "nv50_output.h"
41
42 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
43
44 /*
45  * PLL calculation.  pclk is in kHz.
46  */
47 static void
48 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
49 {
50     const float refclk = 27000.0f;
51     const float minVcoA = 100000;
52     const float maxVcoA = 400000;
53     const float minVcoB = 600000;
54     float maxVcoB = 1400000;
55     const float minUA = 2000;
56     const float maxUA = 400000;
57     const float minUB = 50000;
58     const float maxUB = 200000;
59     const int minNA = 1, maxNA = 255;
60     const int minNB = 1, maxNB = 31;
61     const int minMA = 1, maxMA = 255;
62     const int minMB = 1, maxMB = 31;
63     const int minP = 0, maxP = 6;
64     int lowP, highP;
65     float vcoB;
66
67     int na, ma, nb, mb, p;
68     float bestError = FLT_MAX;
69
70     *pNA = *pMA = *pNB = *pMB = *pP = 0;
71
72     if(maxVcoB < pclk + pclk / 200)
73         maxVcoB = pclk + pclk / 200;
74     if(minVcoB / (1 << maxP) > pclk)
75         pclk = minVcoB / (1 << maxP);
76
77     vcoB = maxVcoB - maxVcoB / 200;
78     lowP = minP;
79     vcoB /= 1 << (lowP + 1);
80
81     while(pclk <= vcoB && lowP < maxP)
82     {
83         vcoB /= 2;
84         lowP++;
85     }
86
87     vcoB = maxVcoB + maxVcoB / 200;
88     highP = lowP;
89     vcoB /= 1 << (highP + 1);
90
91     while(pclk <= vcoB && highP < maxP)
92     {
93         vcoB /= 2;
94         highP++;
95     }
96
97     for(p = lowP; p <= highP; p++)
98     {
99         for(ma = minMA; ma <= maxMA; ma++)
100         {
101             if(refclk / ma < minUA)
102                 break;
103             else if(refclk / ma > maxUA)
104                 continue;
105
106             for(na = minNA; na <= maxNA; na++)
107             {
108                 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
109                     continue;
110
111                 for(mb = minMB; mb <= maxMB; mb++)
112                 {
113                     if(refclk * na / ma / mb < minUB)
114                         break;
115                     else if(refclk * na / ma / mb > maxUB)
116                         continue;
117
118                     nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
119
120                     if(nb > maxNB)
121                         break;
122                     else if(nb < minNB)
123                         continue;
124                     else
125                     {
126                         float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
127                         float error = fabsf(pclk - freq);
128                         if(error < bestError) {
129                             *pNA = na;
130                             *pMA = ma;
131                             *pNB = nb;
132                             *pMB = mb;
133                             *pP = p;
134                             bestError = error;
135                         }
136                     }
137                 }
138             }
139         }
140     }
141 }
142
143 void NV50CrtcSetPClk(xf86CrtcPtr crtc)
144 {
145     NVPtr pNv = NVPTR(crtc->scrn);
146     NV50CrtcPrivPtr pPriv = crtc->driver_private;
147     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
148     const int headOff = 0x800 * pPriv->head;
149     int lo_n, lo_m, hi_n, hi_m, p, i;
150     CARD32 lo = pNv->REGS[(0x00614104+headOff)/4];
151     CARD32 hi = pNv->REGS[(0x00614108+headOff)/4];
152
153     pNv->REGS[(0x00614100+headOff)/4] = 0x10000610;
154     lo &= 0xff00ff00;
155     hi &= 0x8000ff00;
156
157     NV50CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
158
159     lo |= (lo_m << 16) | lo_n;
160     hi |= (p << 28) | (hi_m << 16) | hi_n;
161     pNv->REGS[(0x00614104+headOff)/4] = lo;
162     pNv->REGS[(0x00614108+headOff)/4] = hi;
163     pNv->REGS[(0x00614200+headOff)/4] = 0;
164
165     for(i = 0; i < xf86_config->num_output; i++) {
166         xf86OutputPtr output = xf86_config->output[i];
167
168         if(output->crtc != crtc)
169             continue;
170         NV50OutputSetPClk(output, pPriv->pclk);
171     }
172 }
173
174 Head
175 NV50CrtcGetHead(xf86CrtcPtr crtc)
176 {
177     NV50CrtcPrivPtr pPriv = crtc->driver_private;
178     return pPriv->head;
179 }
180
181 Bool
182 NV50DispPreInit(ScrnInfoPtr pScrn)
183 {
184     NVPtr pNv = NVPTR(pScrn);
185
186     pNv->REGS[0x00610184/4] = pNv->REGS[0x00614004/4];
187     pNv->REGS[0x00610190/4] = pNv->REGS[0x00616100/4];
188     pNv->REGS[0x006101a0/4] = pNv->REGS[0x00616900/4];
189     pNv->REGS[0x00610194/4] = pNv->REGS[0x00616104/4];
190     pNv->REGS[0x006101a4/4] = pNv->REGS[0x00616904/4];
191     pNv->REGS[0x00610198/4] = pNv->REGS[0x00616108/4];
192     pNv->REGS[0x006101a8/4] = pNv->REGS[0x00616908/4];
193     pNv->REGS[0x0061019C/4] = pNv->REGS[0x0061610C/4];
194     pNv->REGS[0x006101ac/4] = pNv->REGS[0x0061690c/4];
195     pNv->REGS[0x006101D0/4] = pNv->REGS[0x0061A000/4];
196     pNv->REGS[0x006101D4/4] = pNv->REGS[0x0061A800/4];
197     pNv->REGS[0x006101D8/4] = pNv->REGS[0x0061B000/4];
198     pNv->REGS[0x006101E0/4] = pNv->REGS[0x0061C000/4];
199     pNv->REGS[0x006101E4/4] = pNv->REGS[0x0061C800/4];
200     pNv->REGS[0x0061A004/4] = 0x80550000;
201     pNv->REGS[0x0061A010/4] = 0x00000001;
202     pNv->REGS[0x0061A804/4] = 0x80550000;
203     pNv->REGS[0x0061A810/4] = 0x00000001;
204     pNv->REGS[0x0061B004/4] = 0x80550000;
205     pNv->REGS[0x0061B010/4] = 0x00000001;
206
207     return TRUE;
208 }
209
210 Bool
211 NV50DispInit(ScrnInfoPtr pScrn)
212 {
213     NVPtr pNv = NVPTR(pScrn);
214
215     if(pNv->REGS[0x00610024/4] & 0x100) {
216         pNv->REGS[0x00610024/4] = 0x100;
217         pNv->REGS[0x006194E8/4] &= ~1;
218         while(pNv->REGS[0x006194E8/4] & 2);
219     }
220
221     pNv->REGS[0x00610200/4] = 0x2b00;
222     while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
223     pNv->REGS[0x00610300/4] = 1;
224     pNv->REGS[0x00610200/4] = 0x1000b03;
225     while(!(pNv->REGS[0x00610200/4] & 0x40000000));
226
227         NV50DisplayCommand(pScrn, 0x84, 0);
228         NV50DisplayCommand(pScrn, 0x88, 0);
229         NV50DisplayCommand(pScrn, 0x874, 0);
230         NV50DisplayCommand(pScrn, 0x800, 0);
231         NV50DisplayCommand(pScrn, 0x810, 0);
232         NV50DisplayCommand(pScrn, 0x82c, 0);
233
234     return TRUE;
235 }
236
237 void
238 NV50DispShutdown(ScrnInfoPtr pScrn)
239 {
240     NVPtr pNv = NVPTR(pScrn);
241     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
242     int i;
243
244     for(i = 0; i < xf86_config->num_crtc; i++) {
245         xf86CrtcPtr crtc = xf86_config->crtc[i];
246
247         NV50CrtcBlankScreen(crtc, TRUE);
248     }
249
250         NV50DisplayCommand(pScrn, 0x80, 0);
251
252     for(i = 0; i < xf86_config->num_crtc; i++) {
253         xf86CrtcPtr crtc = xf86_config->crtc[i];
254
255         if(crtc->enabled) {
256             const CARD32 mask = 4 << NV50CrtcGetHead(crtc);
257
258             pNv->REGS[0x00610024/4] = mask;
259             while(!(pNv->REGS[0x00610024/4] & mask));
260         }
261     }
262
263     pNv->REGS[0x00610200/4] = 0;
264     pNv->REGS[0x00610300/4] = 0;
265     while ((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
266     while ((pNv->REGS[0x0061C030/4] & 0x10000000));
267     while ((pNv->REGS[0x0061C830/4] & 0x10000000));
268 }
269
270 void
271 NV50CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src)
272 {
273     /* Magic mode timing fudge factor */
274     const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1;
275     const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1;
276
277     /* Stash the src timings in the Crtc fields in dst */
278     dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal;
279     dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
280         (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1);
281     dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
282         (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1);
283     dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
284         (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1);
285     dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 |
286         ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2);
287 }
288
289 Bool
290 NV50CrtcModeFixup(xf86CrtcPtr crtc,
291                  DisplayModePtr mode, DisplayModePtr adjusted_mode)
292 {
293     NV50CrtcPrivPtr pPriv = crtc->driver_private;
294
295     if (pPriv->skipModeFixup)
296         return TRUE;
297
298     NV50CrtcDoModeFixup(adjusted_mode, mode);
299     return TRUE;
300 }
301
302 void
303 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
304                DisplayModePtr adjusted_mode, int x, int y)
305 {
306     ScrnInfoPtr pScrn = crtc->scrn;
307     NV50CrtcPrivPtr pPriv = crtc->driver_private;
308     const int HDisplay = adjusted_mode->HDisplay;
309     const int VDisplay = adjusted_mode->VDisplay;
310
311     pPriv->pclk = adjusted_mode->Clock;
312
313         /* DoCrtc includes head offset */
314         NV50CrtcCommand(crtc, 0x804, adjusted_mode->Clock | 0x800000);
315         NV50CrtcCommand(crtc, 0x808, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
316         NV50CrtcCommand(crtc, 0x810, 0);
317         NV50CrtcCommand(crtc, 0x82c, 0);
318         NV50CrtcCommand(crtc, 0x814, adjusted_mode->CrtcHBlankStart);
319         NV50CrtcCommand(crtc, 0x818, adjusted_mode->CrtcHSyncEnd);
320         NV50CrtcCommand(crtc, 0x81c, adjusted_mode->CrtcHBlankEnd);
321         NV50CrtcCommand(crtc, 0x820, adjusted_mode->CrtcHTotal);
322         if(adjusted_mode->Flags & V_INTERLACE) {
323                 NV50CrtcCommand(crtc, 0x824, adjusted_mode->CrtcHSkew);
324         }
325         NV50CrtcCommand(crtc, 0x868, pScrn->virtualY << 16 | pScrn->virtualX);
326         NV50CrtcCommand(crtc, 0x86c, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
327         switch(pScrn->depth) {
328                 case 8: NV50CrtcCommand(crtc, 0x870, 0x1e00); break;
329                 case 15: NV50CrtcCommand(crtc, 0x870, 0xe900); break;
330                 case 16: NV50CrtcCommand(crtc, 0x870, 0xe800); break;
331                 case 24: NV50CrtcCommand(crtc, 0x870, 0xcf00); break;
332         }
333         NV50CrtcSetDither(crtc, pPriv->dither, FALSE);
334         NV50CrtcCommand(crtc, 0x8a8, 0x40000);
335         NV50CrtcCommand(crtc, 0x8c0, y << 16 | x);
336         NV50CrtcCommand(crtc, 0x8c8, VDisplay << 16 | HDisplay);
337         NV50CrtcCommand(crtc, 0x8d4, 0);
338
339         NV50CrtcBlankScreen(crtc, FALSE);
340 }
341
342 void
343 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
344 {
345         ScrnInfoPtr pScrn = crtc->scrn;
346         NVPtr pNv = NVPTR(pScrn);
347         NV50CrtcPrivPtr pPriv = crtc->driver_private;
348
349         if(blank) {
350                 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
351
352                 NV50CrtcCommand(crtc, 0x840, 0);
353                 NV50CrtcCommand(crtc, 0x844, 0);
354                 if(pNv->NVArch != 0x50)
355                         NV50CrtcCommand(crtc, 0x85c, 0);
356                 NV50CrtcCommand(crtc, 0x874, 0);
357                 if(pNv->NVArch != 0x50)
358                         NV50CrtcCommand(crtc, 0x89c, 0);
359         } else {
360                 NV50CrtcCommand(crtc, 0x860, pNv->FB->offset >> 8);
361                 NV50CrtcCommand(crtc, 0x864, 0);
362                 pNv->REGS[0x00610380/4] = 0;
363                 /*XXX: in "nv" this is total vram size.  our RamAmountKBytes is clamped
364                 *     to 256MiB.
365                 */
366                 pNv->REGS[0x00610384/4] = pNv->RamAmountKBytes * 1024 - 1;
367                 pNv->REGS[0x00610388/4] = 0x150000;
368                 pNv->REGS[0x0061038C/4] = 0;
369                 NV50CrtcCommand(crtc, 0x884, pNv->Cursor->offset >> 8);
370                 if(pNv->NVArch != 0x50)
371                         NV50CrtcCommand(crtc, 0x89c, 1);
372                 if(pPriv->cursorVisible)
373                         NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
374                 NV50CrtcCommand(crtc, 0x840, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
375                 NV50CrtcCommand(crtc, 0x844, pNv->CLUT->offset >> 8);
376                 if(pNv->NVArch != 0x50)
377                         NV50CrtcCommand(crtc, 0x85c, 1);
378                 NV50CrtcCommand(crtc, 0x874, 1);
379      }
380 }
381
382 /******************************** Cursor stuff ********************************/
383 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
384 {
385         ScrnInfoPtr pScrn = crtc->scrn;
386         NV50CrtcPrivPtr pPriv = crtc->driver_private;
387
388         NV50CrtcCommand(crtc, 0x880, show ? 0x85000000 : 0x5000000);
389         if(update) {
390                 pPriv->cursorVisible = show;
391                 NV50CrtcCommand(crtc, 0x80, 0);
392         }
393 }
394
395 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
396 {
397     NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
398 }
399
400 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
401 {
402     NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
403 }
404
405 /******************************** CRTC stuff ********************************/
406
407 void
408 NV50CrtcPrepare(xf86CrtcPtr crtc)
409 {
410     ScrnInfoPtr pScrn = crtc->scrn;
411     NV50CrtcPrivPtr pPriv = crtc->driver_private;
412     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
413     int i;
414
415     for(i = 0; i < xf86_config->num_output; i++) {
416         xf86OutputPtr output = xf86_config->output[i];
417
418         if(!output->crtc)
419             output->funcs->mode_set(output, NULL, NULL);
420     }
421
422     pPriv->skipModeFixup = FALSE;
423 }
424
425 void
426 NV50CrtcSkipModeFixup(xf86CrtcPtr crtc)
427 {
428     NV50CrtcPrivPtr pPriv = crtc->driver_private;
429     pPriv->skipModeFixup = TRUE;
430 }
431
432 void
433 NV50CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update)
434 {
435         ScrnInfoPtr pScrn = crtc->scrn;
436         NV50CrtcPrivPtr pPriv = crtc->driver_private;
437
438         pPriv->dither = dither;
439
440         NV50CrtcCommand(crtc, 0x8a0, dither ? 0x11 : 0);
441         if(update) 
442                 NV50CrtcCommand(crtc, 0x80, 0);
443 }
444
445 static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY)
446 {
447     float scaleX, scaleY, scale;
448
449     scaleX = mode->CrtcHDisplay / (float)mode->HDisplay;
450     scaleY = mode->CrtcVDisplay / (float)mode->VDisplay;
451
452     if(scaleX > scaleY)
453         scale = scaleY;
454     else
455         scale = scaleX;
456
457     *outX = mode->HDisplay * scale;
458     *outY = mode->VDisplay * scale;
459 }
460
461 void NV50CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode,
462                      enum NV50ScaleMode scale)
463 {
464     ScrnInfoPtr pScrn = crtc->scrn;
465     NV50CrtcPrivPtr pPriv = crtc->driver_private;
466     int outX, outY;
467
468     switch(scale) {
469         case NV50_SCALE_ASPECT:
470             ComputeAspectScale(mode, &outX, &outY);
471             break;
472
473         case NV50_SCALE_OFF:
474         case NV50_SCALE_FILL:
475             outX = mode->CrtcHDisplay;
476             outY = mode->CrtcVDisplay;
477             break;
478
479         case NV50_SCALE_CENTER:
480             outX = mode->HDisplay;
481             outY = mode->VDisplay;
482             break;
483     }
484
485         if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
486                 mode->HDisplay != outX || mode->VDisplay != outY) {
487                 NV50CrtcCommand(crtc, 0x8a4, 9);
488         } else {
489                 NV50CrtcCommand(crtc, 0x8a4, 0);
490         }
491         NV50CrtcCommand(crtc, 0x8d8, outY << 16 | outX);
492         NV50CrtcCommand(crtc, 0x8dc, outY << 16 | outX);
493 }
494
495 void
496 NV50CrtcCommit(xf86CrtcPtr crtc)
497 {
498     ScrnInfoPtr pScrn = crtc->scrn;
499     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
500     int i, crtc_mask = 0;
501
502     /* If any heads are unused, blank them */
503     for(i = 0; i < xf86_config->num_output; i++) {
504         xf86OutputPtr output = xf86_config->output[i];
505
506         if(output->crtc)
507             /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
508             crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
509     }
510
511     for(i = 0; i < xf86_config->num_crtc; i++)
512         if(!((1 << i) & crtc_mask))
513             NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
514
515         NV50CrtcCommand(crtc, 0x80, 0);
516 }
517
518 #endif /* ENABLE_RANDR12 */