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