make randr 1.2 disableable for xorg server < 1.3
[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 typedef struct NV50CrtcPrivRec {
43     Head head;
44     int pclk; /* Target pixel clock in kHz */
45     Bool cursorVisible;
46 } NV50CrtcPrivRec, *NV50CrtcPrivPtr;
47
48 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
49
50 /*
51  * PLL calculation.  pclk is in kHz.
52  */
53 static void
54 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
55 {
56     const float refclk = 27000.0f;
57     const float minVcoA = 100000;
58     const float maxVcoA = 400000;
59     const float minVcoB = 600000;
60     float maxVcoB = 1400000;
61     const float minUA = 2000;
62     const float maxUA = 400000;
63     const float minUB = 50000;
64     const float maxUB = 200000;
65     const int minNA = 1, maxNA = 255;
66     const int minNB = 1, maxNB = 31;
67     const int minMA = 1, maxMA = 255;
68     const int minMB = 1, maxMB = 31;
69     const int minP = 0, maxP = 6;
70     int lowP, highP;
71     float vcoB;
72
73     int na, ma, nb, mb, p;
74     float bestError = FLT_MAX;
75
76     *pNA = *pMA = *pNB = *pMB = *pP = 0;
77
78     if(maxVcoB < pclk + pclk / 200)
79         maxVcoB = pclk + pclk / 200;
80     if(minVcoB / (1 << maxP) > pclk)
81         pclk = minVcoB / (1 << maxP);
82
83     vcoB = maxVcoB - maxVcoB / 200;
84     lowP = minP;
85     vcoB /= 1 << (lowP + 1);
86
87     while(pclk <= vcoB && lowP < maxP)
88     {
89         vcoB /= 2;
90         lowP++;
91     }
92
93     vcoB = maxVcoB + maxVcoB / 200;
94     highP = lowP;
95     vcoB /= 1 << (highP + 1);
96
97     while(pclk <= vcoB && highP < maxP)
98     {
99         vcoB /= 2;
100         highP++;
101     }
102
103     for(p = lowP; p <= highP; p++)
104     {
105         for(ma = minMA; ma <= maxMA; ma++)
106         {
107             if(refclk / ma < minUA)
108                 break;
109             else if(refclk / ma > maxUA)
110                 continue;
111
112             for(na = minNA; na <= maxNA; na++)
113             {
114                 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
115                     continue;
116
117                 for(mb = minMB; mb <= maxMB; mb++)
118                 {
119                     if(refclk * na / ma / mb < minUB)
120                         break;
121                     else if(refclk * na / ma / mb > maxUB)
122                         continue;
123
124                     nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
125
126                     if(nb > maxNB)
127                         break;
128                     else if(nb < minNB)
129                         continue;
130                     else
131                     {
132                         float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
133                         float error = fabsf(pclk - freq);
134                         if(error < bestError) {
135                             *pNA = na;
136                             *pMA = ma;
137                             *pNB = nb;
138                             *pMB = mb;
139                             *pP = p;
140                             bestError = error;
141                         }
142                     }
143                 }
144             }
145         }
146     }
147 }
148
149 static void
150 NV50CrtcSetPClk(xf86CrtcPtr crtc)
151 {
152     NVPtr pNv = NVPTR(crtc->scrn);
153     NV50CrtcPrivPtr pPriv = crtc->driver_private;
154     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
155     const int headOff = 0x800 * pPriv->head;
156     int lo_n, lo_m, hi_n, hi_m, p, i;
157     CARD32 lo = pNv->REGS[(0x00614104+headOff)/4];
158     CARD32 hi = pNv->REGS[(0x00614108+headOff)/4];
159
160     pNv->REGS[(0x00614100+headOff)/4] = 0x10000610;
161     lo &= 0xff00ff00;
162     hi &= 0x8000ff00;
163
164     NV50CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
165
166     lo |= (lo_m << 16) | lo_n;
167     hi |= (p << 28) | (hi_m << 16) | hi_n;
168     pNv->REGS[(0x00614104+headOff)/4] = lo;
169     pNv->REGS[(0x00614108+headOff)/4] = hi;
170     pNv->REGS[(0x00614200+headOff)/4] = 0;
171
172     for(i = 0; i < xf86_config->num_output; i++) {
173         xf86OutputPtr output = xf86_config->output[i];
174
175         if(output->crtc != crtc)
176             continue;
177         NV50OutputSetPClk(output, pPriv->pclk);
178     }
179 }
180
181 void
182 NV50DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data)
183 {
184     NVPtr pNv = NVPTR(pScrn);
185
186     pNv->REGS[0x00610304/4] = data;
187     pNv->REGS[0x00610300/4] = addr | 0x80010001;
188
189     while(pNv->REGS[0x00610300/4] & 0x80000000) {
190         const int super = ffs((pNv->REGS[0x00610024/4] >> 4) & 7);
191
192         if(super) {
193             if(super == 2) {
194                 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
195                 const CARD32 r = pNv->REGS[0x00610030/4];
196                 int i;
197
198                 for(i = 0; i < xf86_config->num_crtc; i++)
199                 {
200                     xf86CrtcPtr crtc = xf86_config->crtc[i];
201                     NV50CrtcPrivPtr pPriv = crtc->driver_private;
202
203                     if(r & (0x200 << pPriv->head))
204                         NV50CrtcSetPClk(crtc);
205                 }
206             }
207
208             pNv->REGS[0x00610024/4] = 8 << super;
209             pNv->REGS[0x00610030/4] = 0x80000000;
210         }
211     }
212 }
213
214 Head
215 NV50CrtcGetHead(xf86CrtcPtr crtc)
216 {
217     NV50CrtcPrivPtr pPriv = crtc->driver_private;
218     return pPriv->head;
219 }
220
221 Bool
222 NV50DispPreInit(ScrnInfoPtr pScrn)
223 {
224     NVPtr pNv = NVPTR(pScrn);
225
226     pNv->REGS[0x00610184/4] = pNv->REGS[0x00614004/4];
227     pNv->REGS[0x00610190/4] = pNv->REGS[0x00616100/4];
228     pNv->REGS[0x006101a0/4] = pNv->REGS[0x00616900/4];
229     pNv->REGS[0x00610194/4] = pNv->REGS[0x00616104/4];
230     pNv->REGS[0x006101a4/4] = pNv->REGS[0x00616904/4];
231     pNv->REGS[0x00610198/4] = pNv->REGS[0x00616108/4];
232     pNv->REGS[0x006101a8/4] = pNv->REGS[0x00616908/4];
233     pNv->REGS[0x0061019C/4] = pNv->REGS[0x0061610C/4];
234     pNv->REGS[0x006101ac/4] = pNv->REGS[0x0061690c/4];
235     pNv->REGS[0x006101D0/4] = pNv->REGS[0x0061A000/4];
236     pNv->REGS[0x006101D4/4] = pNv->REGS[0x0061A800/4];
237     pNv->REGS[0x006101D8/4] = pNv->REGS[0x0061B000/4];
238     pNv->REGS[0x006101E0/4] = pNv->REGS[0x0061C000/4];
239     pNv->REGS[0x006101E4/4] = pNv->REGS[0x0061C800/4];
240     pNv->REGS[0x0061c00c/4] = 0x03010700;
241     pNv->REGS[0x0061c010/4] = 0x0000152f;
242     pNv->REGS[0x0061c014/4] = 0x00000000;
243     pNv->REGS[0x0061c018/4] = 0x00245af8;
244     pNv->REGS[0x0061c80c/4] = 0x03010700;
245     pNv->REGS[0x0061c810/4] = 0x0000152f;
246     pNv->REGS[0x0061c814/4] = 0x00000000;
247     pNv->REGS[0x0061c818/4] = 0x00245af8;
248     pNv->REGS[0x0061A004/4] = 0x80550000;
249     pNv->REGS[0x0061A010/4] = 0x00000001;
250     pNv->REGS[0x0061A804/4] = 0x80550000;
251     pNv->REGS[0x0061A810/4] = 0x00000001;
252     pNv->REGS[0x0061B004/4] = 0x80550000;
253     pNv->REGS[0x0061B010/4] = 0x00000001;
254
255     return TRUE;
256 }
257
258 Bool
259 NV50DispInit(ScrnInfoPtr pScrn)
260 {
261     NVPtr pNv = NVPTR(pScrn);
262
263     if(pNv->REGS[0x00610024/4] & 0x100) {
264         pNv->REGS[0x00610024/4] = 0x100;
265         pNv->REGS[0x006194E8/4] &= ~1;
266         while(pNv->REGS[0x006194E8/4] & 2);
267     }
268
269     pNv->REGS[0x00610200/4] = 0x2b00;
270     while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
271     pNv->REGS[0x00610300/4] = 1;
272     pNv->REGS[0x00610200/4] = 0x1000b03;
273     while(!(pNv->REGS[0x00610200/4] & 0x40000000));
274
275     C(0x00000084, 0);
276     C(0x00000088, 0);
277     C(0x00000874, 0);
278     C(0x00000800, 0);
279     C(0x00000810, 0);
280     C(0x0000082C, 0);
281
282     return TRUE;
283 }
284
285 void
286 NV50DispShutdown(ScrnInfoPtr pScrn)
287 {
288     NVPtr pNv = NVPTR(pScrn);
289     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
290     int i;
291
292     for(i = 0; i < xf86_config->num_crtc; i++) {
293         xf86CrtcPtr crtc = xf86_config->crtc[i];
294
295         NV50CrtcBlankScreen(crtc, TRUE);
296     }
297
298     C(0x00000080, 0);
299
300     for(i = 0; i < xf86_config->num_crtc; i++) {
301         xf86CrtcPtr crtc = xf86_config->crtc[i];
302
303         if(crtc->enabled) {
304             const CARD32 mask = 4 << NV50CrtcGetHead(crtc);
305
306             pNv->REGS[0x00610024/4] = mask;
307             while(!(pNv->REGS[0x00610024/4] & mask));
308         }
309     }
310
311     pNv->REGS[0x00610200/4] = 0;
312     pNv->REGS[0x00610300/4] = 0;
313     while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
314 }
315
316 static Bool
317 NV50CrtcModeFixup(xf86CrtcPtr crtc,
318                  DisplayModePtr mode, DisplayModePtr adjusted_mode)
319 {
320     // TODO: Fix up the mode here
321     return TRUE;
322 }
323
324 static void
325 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
326                DisplayModePtr adjusted_mode, int x, int y)
327 {
328     ScrnInfoPtr pScrn = crtc->scrn;
329     NV50CrtcPrivPtr pPriv = crtc->driver_private;
330     const int HDisplay = mode->HDisplay, VDisplay = mode->VDisplay;
331     const int headOff = 0x400 * NV50CrtcGetHead(crtc);
332     int interlaceDiv, fudge;
333
334     // TODO: Use adjusted_mode and fix it up in NV50CrtcModeFixup
335     pPriv->pclk = mode->Clock;
336
337     /* Magic mode timing fudge factor */
338     fudge = ((mode->Flags & V_INTERLACE) && (mode->Flags & V_DBLSCAN)) ? 2 : 1;
339     interlaceDiv = (mode->Flags & V_INTERLACE) ? 2 : 1;
340
341     C(0x00000804 + headOff, mode->Clock | 0x800000);
342     C(0x00000808 + headOff, (mode->Flags & V_INTERLACE) ? 2 : 0);
343     C(0x00000810 + headOff, 0);
344     C(0x0000082C + headOff, 0);
345     C(0x00000814 + headOff, mode->CrtcVTotal << 16 | mode->CrtcHTotal);
346     C(0x00000818 + headOff,
347         ((mode->CrtcVSyncEnd - mode->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
348         (mode->CrtcHSyncEnd - mode->CrtcHSyncStart - 1));
349     C(0x0000081C + headOff,
350         ((mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
351         (mode->CrtcHBlankEnd - mode->CrtcHSyncStart - 1));
352     C(0x00000820 + headOff,
353         ((mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
354         (mode->CrtcHTotal - mode->CrtcHSyncStart + mode->CrtcHBlankStart - 1));
355     if(mode->Flags & V_INTERLACE) {
356         C(0x00000824 + headOff,
357             ((mode->CrtcVTotal + mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / 2 - 2) << 16 |
358             ((2*mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / 2 - 2));
359     }
360     C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX);
361     C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
362     switch(pScrn->depth) {
363         case  8: C(0x00000870 + headOff, 0x1E00); break;
364         case 15: C(0x00000870 + headOff, 0xE900); break;
365         case 16: C(0x00000870 + headOff, 0xE800); break;
366         case 24: C(0x00000870 + headOff, 0xCF00); break;
367     }
368     C(0x000008A0 + headOff, 0);
369     if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
370        mode->CrtcHDisplay != HDisplay || mode->CrtcVDisplay != VDisplay) {
371         C(0x000008A4 + headOff, 9);
372     } else {
373         C(0x000008A4 + headOff, 0);
374     }
375     C(0x000008A8 + headOff, 0x40000);
376     C(0x000008C0 + headOff, y << 16 | x);
377     C(0x000008C8 + headOff, VDisplay << 16 | HDisplay);
378     C(0x000008D4 + headOff, 0);
379     C(0x000008D8 + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay);
380     C(0x000008DC + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay);
381
382     NV50CrtcBlankScreen(crtc, FALSE);
383 }
384
385 void
386 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
387 {
388     ScrnInfoPtr pScrn = crtc->scrn;
389     NVPtr pNv = NVPTR(pScrn);
390     NV50CrtcPrivPtr pPriv = crtc->driver_private;
391     const int headOff = 0x400 * pPriv->head;
392
393     if(blank) {
394         NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
395
396         C(0x00000840 + headOff, 0);
397         C(0x00000844 + headOff, 0);
398         if(pNv->NVArch != 0x50)
399             C(0x0000085C + headOff, 0);
400         C(0x00000874 + headOff, 0);
401         if(pNv->NVArch != 0x50)
402             C(0x0000089C + headOff, 0);
403     } else {
404         C(0x00000860 + headOff, pNv->FB->offset >> 8);
405         C(0x00000864 + headOff, 0);
406         pNv->REGS[0x00610380/4] = 0;
407         /*XXX: in "nv" this is total vram size.  our RamAmountKBytes is clamped
408          *     to 256MiB.
409          */
410         pNv->REGS[0x00610384/4] = pNv->RamAmountKBytes * 1024 - 1;
411         pNv->REGS[0x00610388/4] = 0x150000;
412         pNv->REGS[0x0061038C/4] = 0;
413         C(0x00000884 + headOff, pNv->Cursor->offset >> 8);
414         if(pNv->NVArch != 0x50)
415             C(0x0000089C + headOff, 1);
416         if(pPriv->cursorVisible)
417             NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
418         C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
419         C(0x00000844 + headOff, pNv->CLUT->offset >> 8);
420         if(pNv->NVArch != 0x50)
421             C(0x0000085C + headOff, 1);
422         C(0x00000874 + headOff, 1);
423     }
424 }
425
426 void
427 NV50CrtcDPMSSet(xf86CrtcPtr crtc, int mode)
428 {
429 }
430
431 /******************************** Cursor stuff ********************************/
432 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
433 {
434     ScrnInfoPtr pScrn = crtc->scrn;
435     NV50CrtcPrivPtr pPriv = crtc->driver_private;
436     const int headOff = 0x400 * NV50CrtcGetHead(crtc);
437
438     C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000);
439     if(update) {
440         pPriv->cursorVisible = show;
441         C(0x00000080, 0);
442     }
443 }
444
445 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
446 {
447     NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
448 }
449
450 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
451 {
452     NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
453 }
454
455 /******************************** CRTC stuff ********************************/
456
457 static Bool
458 NV50CrtcLock(xf86CrtcPtr crtc)
459 {
460     return FALSE;
461 }
462
463 static void
464 NV50CrtcPrepare(xf86CrtcPtr crtc)
465 {
466     ScrnInfoPtr pScrn = crtc->scrn;
467     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
468     int i;
469
470     for(i = 0; i < xf86_config->num_output; i++) {
471         xf86OutputPtr output = xf86_config->output[i];
472
473         if(!output->crtc)
474             output->funcs->mode_set(output, NULL, NULL);
475     }
476 }
477
478 static void
479 NV50CrtcCommit(xf86CrtcPtr crtc)
480 {
481     ScrnInfoPtr pScrn = crtc->scrn;
482     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
483     int i, crtc_mask = 0;
484
485     /* If any heads are unused, blank them */
486     for(i = 0; i < xf86_config->num_output; i++) {
487         xf86OutputPtr output = xf86_config->output[i];
488
489         if(output->crtc)
490             /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
491             crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
492     }
493
494     for(i = 0; i < xf86_config->num_crtc; i++)
495         if(!((1 << i) & crtc_mask))
496             NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
497
498     C(0x00000080, 0);
499 }
500
501 static const xf86CrtcFuncsRec nv50_crtc_funcs = {
502     .dpms = NV50CrtcDPMSSet,
503     .save = NULL,
504     .restore = NULL,
505     .lock = NV50CrtcLock,
506     .unlock = NULL,
507     .mode_fixup = NV50CrtcModeFixup,
508     .prepare = NV50CrtcPrepare,
509     .mode_set = NV50CrtcModeSet,
510     // .gamma_set = NV50DispGammaSet,
511     .commit = NV50CrtcCommit,
512     .shadow_create = NULL,
513     .shadow_destroy = NULL,
514     .set_cursor_position = NV50SetCursorPosition,
515     .show_cursor = NV50CrtcShowCursor,
516     .hide_cursor = NV50CrtcHideCursor,
517     .load_cursor_argb = NV50LoadCursorARGB,
518     .destroy = NULL,
519 };
520
521 void
522 NV50DispCreateCrtcs(ScrnInfoPtr pScrn)
523 {
524     Head head;
525     xf86CrtcPtr crtc;
526     NV50CrtcPrivPtr nv50_crtc;
527
528     /* Create a "crtc" object for each head */
529     for(head = HEAD0; head <= HEAD1; head++) {
530         crtc = xf86CrtcCreate(pScrn, &nv50_crtc_funcs);
531         if(!crtc) return;
532
533         nv50_crtc = xnfcalloc(sizeof(*nv50_crtc), 1);
534         nv50_crtc->head = head;
535         crtc->driver_private = nv50_crtc;
536     }
537 }
538
539 #endif /* ENABLE_RANDR12 */