g80_display.c revision bd2f6fc9
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 "g80_type.h"
35#include "g80_cursor.h"
36#include "g80_display.h"
37#include "g80_output.h"
38
39typedef struct G80CrtcPrivRec {
40    Head head;
41    int pclk; /* Target pixel clock in kHz */
42    Bool cursorVisible;
43    Bool skipModeFixup;
44    Bool dither;
45} G80CrtcPrivRec, *G80CrtcPrivPtr;
46
47static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
48
49/*
50 * PLL calculation.  pclk is in kHz.
51 */
52static void
53G80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
54{
55    const float refclk = 27000.0f;
56    const float minVcoA = 100000;
57    const float maxVcoA = 400000;
58    const float minVcoB = 600000;
59    float maxVcoB = 1400000;
60    const float minUA = 2000;
61    const float maxUA = 400000;
62    const float minUB = 50000;
63    const float maxUB = 200000;
64    const int minNA = 1, maxNA = 255;
65    const int minNB = 1, maxNB = 31;
66    const int minMA = 1, maxMA = 255;
67    const int minMB = 1, maxMB = 31;
68    const int minP = 0, maxP = 6;
69    int lowP, highP;
70    float vcoB;
71
72    int na, ma, nb, mb, p;
73    float bestError = FLT_MAX;
74
75    *pNA = *pMA = *pNB = *pMB = *pP = 0;
76
77    if(maxVcoB < pclk + pclk / 200)
78        maxVcoB = pclk + pclk / 200;
79    if(minVcoB / (1 << maxP) > pclk)
80        pclk = minVcoB / (1 << maxP);
81
82    vcoB = maxVcoB - maxVcoB / 200;
83    lowP = minP;
84    vcoB /= 1 << (lowP + 1);
85
86    while(pclk <= vcoB && lowP < maxP)
87    {
88        vcoB /= 2;
89        lowP++;
90    }
91
92    vcoB = maxVcoB + maxVcoB / 200;
93    highP = lowP;
94    vcoB /= 1 << (highP + 1);
95
96    while(pclk <= vcoB && highP < maxP)
97    {
98        vcoB /= 2;
99        highP++;
100    }
101
102    for(p = lowP; p <= highP; p++)
103    {
104        for(ma = minMA; ma <= maxMA; ma++)
105        {
106            if(refclk / ma < minUA)
107                break;
108            else if(refclk / ma > maxUA)
109                continue;
110
111            for(na = minNA; na <= maxNA; na++)
112            {
113                if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
114                    continue;
115
116                for(mb = minMB; mb <= maxMB; mb++)
117                {
118                    if(refclk * na / ma / mb < minUB)
119                        break;
120                    else if(refclk * na / ma / mb > maxUB)
121                        continue;
122
123                    nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
124
125                    if(nb > maxNB)
126                        break;
127                    else if(nb < minNB)
128                        continue;
129                    else
130                    {
131                        float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
132                        float error = fabsf(pclk - freq);
133                        if(error < bestError) {
134                            *pNA = na;
135                            *pMA = ma;
136                            *pNB = nb;
137                            *pMB = mb;
138                            *pP = p;
139                            bestError = error;
140                        }
141                    }
142                }
143            }
144        }
145    }
146}
147
148static void
149G80CalcPLL2(float pclk, int *pN, int *pM, int *pPL)
150{
151    const float refclk = 27000.0f;
152    const int minN = 8, maxN = 255;
153    const int minM = 1, maxM = 255;
154    const int minPL = 1, maxPL = 63;
155    const int minU = 25000, maxU = 50000;
156    const int minVco = 500000;
157    int maxVco = 1000000;
158    int lowPL, highPL, pl;
159    float vco, bestError = FLT_MAX;
160
161    vco = pclk + pclk / 50;
162
163    if(maxVco < vco) maxVco = vco;
164
165    highPL = (maxVco + vco - 1) / pclk;
166    if(highPL > maxPL) highPL = maxPL;
167    if(highPL < minPL) highPL = minPL;
168
169    lowPL = minVco / vco;
170    if(lowPL > maxPL) lowPL = maxPL;
171    if(lowPL < minPL) lowPL = minPL;
172
173    for(pl = highPL; pl >= lowPL; pl--) {
174        int m;
175
176        for(m = minM; m <= maxM; m++) {
177            int n;
178            float freq, error;
179
180            if(refclk / m < minU) break;
181            if(refclk / m > maxU) continue;
182
183            n = rint(pclk * pl * m / refclk);
184            if(n > maxN) break;
185            if(n < minN) continue;
186
187            freq = refclk * (n / (float)m) / pl;
188            error = fabsf(pclk - freq);
189            if(error < bestError) {
190                *pN = n;
191                *pM = m;
192                *pPL = pl;
193                bestError = error;
194            }
195        }
196    }
197}
198
199static void
200G80CrtcSetPClk(xf86CrtcPtr crtc)
201{
202    G80Ptr pNv = G80PTR(crtc->scrn);
203    G80CrtcPrivPtr pPriv = crtc->driver_private;
204    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
205    const int headOff = 0x800 * pPriv->head;
206    int i;
207
208    if(pPriv->pclk == 0)
209        return;
210
211    if(pNv->architecture <= 0xa0 ||
212       pNv->architecture == 0xaa ||
213       pNv->architecture == 0xac) {
214        int lo_n, lo_m, hi_n, hi_m, p, i;
215        CARD32 lo = pNv->reg[(0x00614104+headOff)/4];
216        CARD32 hi = pNv->reg[(0x00614108+headOff)/4];
217
218        pNv->reg[(0x00614100+headOff)/4] = 0x10000610;
219        lo &= 0xff00ff00;
220        hi &= 0x8000ff00;
221
222        G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
223
224        lo |= (lo_m << 16) | lo_n;
225        hi |= (p << 28) | (hi_m << 16) | hi_n;
226        pNv->reg[(0x00614104+headOff)/4] = lo;
227        pNv->reg[(0x00614108+headOff)/4] = hi;
228    } else {
229        int n, m, pl;
230        CARD32 r = pNv->reg[(0x00614104+headOff)/4];
231
232        pNv->reg[(0x00614100+headOff)/4] = 0x50000610;
233        r &= 0xffc00000;
234
235        G80CalcPLL2(pPriv->pclk, &n, &m, &pl);
236        r |= pl << 16 | m << 8 | n;
237
238        pNv->reg[(0x00614104+headOff)/4] = r;
239    }
240    pNv->reg[(0x00614200+headOff)/4] = 0;
241
242    for(i = 0; i < xf86_config->num_output; i++) {
243        xf86OutputPtr output = xf86_config->output[i];
244
245        if(output->crtc != crtc)
246            continue;
247        G80OutputSetPClk(output, pPriv->pclk);
248    }
249}
250
251void
252G80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data)
253{
254    G80Ptr pNv = G80PTR(pScrn);
255
256    pNv->reg[0x00610304/4] = data;
257    pNv->reg[0x00610300/4] = addr | 0x80010001;
258
259    while(pNv->reg[0x00610300/4] & 0x80000000) {
260        const int super = ffs((pNv->reg[0x00610024/4] >> 4) & 7);
261
262        if(super) {
263            if(super == 2) {
264                xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
265                int i;
266
267                for(i = 0; i < xf86_config->num_crtc; i++)
268                {
269                    xf86CrtcPtr crtc = xf86_config->crtc[i];
270                    const int headOff = 0x800 * G80CrtcGetHead(crtc);
271
272                    if((pNv->reg[(0x00614200+headOff)/4] & 0xc0) == 0x80)
273                        G80CrtcSetPClk(crtc);
274                }
275            }
276
277            pNv->reg[0x00610024/4] = 8 << super;
278            pNv->reg[0x00610030/4] = 0x80000000;
279        }
280    }
281}
282
283Head
284G80CrtcGetHead(xf86CrtcPtr crtc)
285{
286    G80CrtcPrivPtr pPriv = crtc->driver_private;
287    return pPriv->head;
288}
289
290Bool
291G80DispPreInit(ScrnInfoPtr pScrn)
292{
293    G80Ptr pNv = G80PTR(pScrn);
294
295    pNv->reg[0x00610184/4] = pNv->reg[0x00614004/4];
296    pNv->reg[0x00610190/4] = pNv->reg[0x00616100/4];
297    pNv->reg[0x006101a0/4] = pNv->reg[0x00616900/4];
298    pNv->reg[0x00610194/4] = pNv->reg[0x00616104/4];
299    pNv->reg[0x006101a4/4] = pNv->reg[0x00616904/4];
300    pNv->reg[0x00610198/4] = pNv->reg[0x00616108/4];
301    pNv->reg[0x006101a8/4] = pNv->reg[0x00616908/4];
302    pNv->reg[0x0061019C/4] = pNv->reg[0x0061610C/4];
303    pNv->reg[0x006101ac/4] = pNv->reg[0x0061690c/4];
304    pNv->reg[0x006101D0/4] = pNv->reg[0x0061A000/4];
305    pNv->reg[0x006101D4/4] = pNv->reg[0x0061A800/4];
306    pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4];
307    pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4];
308    pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4];
309    pNv->reg[0x006101E8/4] = pNv->reg[0x0061D000/4];
310    pNv->reg[0x006101EC/4] = pNv->reg[0x0061D800/4];
311    pNv->reg[0x0061A004/4] = 0x80550000;
312    pNv->reg[0x0061A010/4] = 0x00000001;
313    pNv->reg[0x0061A804/4] = 0x80550000;
314    pNv->reg[0x0061A810/4] = 0x00000001;
315    pNv->reg[0x0061B004/4] = 0x80550000;
316    pNv->reg[0x0061B010/4] = 0x00000001;
317
318    return TRUE;
319}
320
321Bool
322G80DispInit(ScrnInfoPtr pScrn)
323{
324    G80Ptr pNv = G80PTR(pScrn);
325    CARD32 val;
326
327    if(pNv->reg[0x00610024/4] & 0x100) {
328        pNv->reg[0x00610024/4] = 0x100;
329        pNv->reg[0x006194E8/4] &= ~1;
330        while(pNv->reg[0x006194E8/4] & 2);
331    }
332
333    pNv->reg[0x00610200/4] = 0x2b00;
334    do {
335        val = pNv->reg[0x00610200/4];
336
337        if ((val & 0x9f0000) == 0x20000)
338            pNv->reg[0x00610200/4] = val | 0x800000;
339
340        if ((val & 0x3f0000) == 0x30000)
341            pNv->reg[0x00610200/4] = val | 0x200000;
342    } while ((val & 0x1e0000) != 0);
343    pNv->reg[0x00610300/4] = 1;
344    pNv->reg[0x00610200/4] = 0x1000b03;
345    while(!(pNv->reg[0x00610200/4] & 0x40000000));
346
347    C(0x00000084, 0);
348    C(0x00000088, 0);
349    C(0x00000874, 0);
350    C(0x00000800, 0);
351    C(0x00000810, 0);
352    C(0x0000082C, 0);
353
354    return TRUE;
355}
356
357void
358G80DispShutdown(ScrnInfoPtr pScrn)
359{
360    G80Ptr pNv = G80PTR(pScrn);
361    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
362    int i;
363
364    for(i = 0; i < xf86_config->num_crtc; i++) {
365        xf86CrtcPtr crtc = xf86_config->crtc[i];
366
367        G80CrtcBlankScreen(crtc, TRUE);
368    }
369
370    C(0x00000080, 0);
371
372    for(i = 0; i < xf86_config->num_crtc; i++) {
373        xf86CrtcPtr crtc = xf86_config->crtc[i];
374
375        if(crtc->enabled) {
376            const CARD32 mask = 4 << G80CrtcGetHead(crtc);
377
378            pNv->reg[0x00610024/4] = mask;
379            while(!(pNv->reg[0x00610024/4] & mask));
380        }
381    }
382
383    pNv->reg[0x00610200/4] = 0;
384    pNv->reg[0x00610300/4] = 0;
385    while((pNv->reg[0x00610200/4] & 0x1e0000) != 0);
386    while((pNv->reg[0x61C030/4] & 0x10000000));
387    while((pNv->reg[0x61C830/4] & 0x10000000));
388}
389
390void
391G80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src)
392{
393    /* Magic mode timing fudge factor */
394    const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1;
395    const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1;
396
397    /* Stash the src timings in the Crtc fields in dst */
398    dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal;
399    dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
400        (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1);
401    dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
402        (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1);
403    dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
404        (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1);
405    dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 |
406        ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2);
407}
408
409static Bool
410G80CrtcModeFixup(xf86CrtcPtr crtc,
411                 DisplayModePtr mode, DisplayModePtr adjusted_mode)
412{
413    G80CrtcPrivPtr pPriv = crtc->driver_private;
414
415    if(pPriv->skipModeFixup)
416        return TRUE;
417
418    G80CrtcDoModeFixup(adjusted_mode, mode);
419    return TRUE;
420}
421
422static void
423G80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
424               DisplayModePtr adjusted_mode, int x, int y)
425{
426    ScrnInfoPtr pScrn = crtc->scrn;
427    G80CrtcPrivPtr pPriv = crtc->driver_private;
428    const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay;
429    const int headOff = 0x400 * G80CrtcGetHead(crtc);
430
431    pPriv->pclk = adjusted_mode->Clock;
432
433    C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000);
434    C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
435    C(0x00000810 + headOff, 0);
436    C(0x0000082C + headOff, 0);
437    C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart);
438    C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd);
439    C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd);
440    C(0x00000820 + headOff, adjusted_mode->CrtcHTotal);
441    if(adjusted_mode->Flags & V_INTERLACE)
442        C(0x00000824 + headOff, adjusted_mode->CrtcHSkew);
443    C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX);
444    C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
445    switch(pScrn->depth) {
446        case  8: C(0x00000870 + headOff, 0x1E00); break;
447        case 15: C(0x00000870 + headOff, 0xE900); break;
448        case 16: C(0x00000870 + headOff, 0xE800); break;
449        case 24: C(0x00000870 + headOff, 0xCF00); break;
450    }
451    G80CrtcSetDither(crtc, pPriv->dither, FALSE);
452    C(0x000008A8 + headOff, 0x40000);
453    C(0x000008C0 + headOff, y << 16 | x);
454    C(0x000008C8 + headOff, VDisplay << 16 | HDisplay);
455    C(0x000008D4 + headOff, 0);
456
457    G80CrtcBlankScreen(crtc, FALSE);
458}
459
460void
461G80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
462{
463    ScrnInfoPtr pScrn = crtc->scrn;
464    G80Ptr pNv = G80PTR(pScrn);
465    G80CrtcPrivPtr pPriv = crtc->driver_private;
466    const int headOff = 0x400 * pPriv->head;
467
468    if(blank) {
469        G80CrtcShowHideCursor(crtc, FALSE, FALSE);
470
471        C(0x00000840 + headOff, 0);
472        C(0x00000844 + headOff, 0);
473        if(pNv->architecture != 0x50)
474            C(0x0000085C + headOff, 0);
475        C(0x00000874 + headOff, 0);
476        if(pNv->architecture != 0x50)
477            C(0x0000089C + headOff, 0);
478    } else {
479        C(0x00000860 + headOff, 0);
480        C(0x00000864 + headOff, 0);
481        pNv->reg[0x00610380/4] = 0;
482        pNv->reg[0x00610384/4] = pNv->videoRam * 1024 - 1;
483        pNv->reg[0x00610388/4] = 0x150000;
484        pNv->reg[0x0061038C/4] = 0;
485        C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40);
486        if(pNv->architecture != 0x50)
487            C(0x0000089C + headOff, 1);
488        if(pPriv->cursorVisible)
489            G80CrtcShowHideCursor(crtc, TRUE, FALSE);
490        C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
491        C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000) >> 8);
492        if(pNv->architecture != 0x50)
493            C(0x0000085C + headOff, 1);
494        C(0x00000874 + headOff, 1);
495    }
496}
497
498static void
499G80CrtcDPMSSet(xf86CrtcPtr crtc, int mode)
500{
501}
502
503/******************************** Cursor stuff ********************************/
504static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
505{
506    ScrnInfoPtr pScrn = crtc->scrn;
507    G80CrtcPrivPtr pPriv = crtc->driver_private;
508    const int headOff = 0x400 * G80CrtcGetHead(crtc);
509
510    C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000);
511    if(update) {
512        pPriv->cursorVisible = show;
513        C(0x00000080, 0);
514    }
515}
516
517static void G80CrtcShowCursor(xf86CrtcPtr crtc)
518{
519    G80CrtcShowHideCursor(crtc, TRUE, TRUE);
520}
521
522static void G80CrtcHideCursor(xf86CrtcPtr crtc)
523{
524    G80CrtcShowHideCursor(crtc, FALSE, TRUE);
525}
526
527/******************************** CRTC stuff ********************************/
528
529static Bool
530G80CrtcLock(xf86CrtcPtr crtc)
531{
532    return FALSE;
533}
534
535static void
536G80CrtcPrepare(xf86CrtcPtr crtc)
537{
538    ScrnInfoPtr pScrn = crtc->scrn;
539    G80CrtcPrivPtr pPriv = crtc->driver_private;
540    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
541    int i;
542
543    for(i = 0; i < xf86_config->num_output; i++) {
544        xf86OutputPtr output = xf86_config->output[i];
545
546        if(!output->crtc)
547            output->funcs->mode_set(output, NULL, NULL);
548    }
549
550    pPriv->skipModeFixup = FALSE;
551}
552
553void
554G80CrtcSkipModeFixup(xf86CrtcPtr crtc)
555{
556    G80CrtcPrivPtr pPriv = crtc->driver_private;
557    pPriv->skipModeFixup = TRUE;
558}
559
560void
561G80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update)
562{
563    ScrnInfoPtr pScrn = crtc->scrn;
564    G80CrtcPrivPtr pPriv = crtc->driver_private;
565    const int headOff = 0x400 * G80CrtcGetHead(crtc);
566
567    pPriv->dither = dither;
568
569    C(0x000008A0 + headOff, dither ? 0x11 : 0);
570    if(update) C(0x00000080, 0);
571}
572
573static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY)
574{
575    float scaleX, scaleY, scale;
576
577    scaleX = mode->CrtcHDisplay / (float)mode->HDisplay;
578    scaleY = mode->CrtcVDisplay / (float)mode->VDisplay;
579
580    if(scaleX > scaleY)
581        scale = scaleY;
582    else
583        scale = scaleX;
584
585    *outX = mode->HDisplay * scale;
586    *outY = mode->VDisplay * scale;
587}
588
589void G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode,
590                     enum G80ScaleMode scale)
591{
592    ScrnInfoPtr pScrn = crtc->scrn;
593    G80CrtcPrivPtr pPriv = crtc->driver_private;
594    const int headOff = 0x400 * pPriv->head;
595    int outX, outY;
596
597    switch(scale) {
598        default:
599        case G80_SCALE_ASPECT:
600            ComputeAspectScale(mode, &outX, &outY);
601            break;
602
603        case G80_SCALE_OFF:
604        case G80_SCALE_FILL:
605            outX = mode->CrtcHDisplay;
606            outY = mode->CrtcVDisplay;
607            break;
608
609        case G80_SCALE_CENTER:
610            outX = mode->HDisplay;
611            outY = mode->VDisplay;
612            break;
613    }
614
615    if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
616       mode->HDisplay != outX || mode->VDisplay != outY) {
617        C(0x000008A4 + headOff, 9);
618    } else {
619        C(0x000008A4 + headOff, 0);
620    }
621    C(0x000008D8 + headOff, outY << 16 | outX);
622    C(0x000008DC + headOff, outY << 16 | outX);
623}
624
625static void
626G80CrtcCommit(xf86CrtcPtr crtc)
627{
628    ScrnInfoPtr pScrn = crtc->scrn;
629    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
630    int i, crtc_mask = 0;
631
632    /* If any heads are unused, blank them */
633    for(i = 0; i < xf86_config->num_output; i++) {
634        xf86OutputPtr output = xf86_config->output[i];
635
636        if(output->crtc)
637            /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
638            crtc_mask |= 1 << G80CrtcGetHead(output->crtc);
639    }
640
641    for(i = 0; i < xf86_config->num_crtc; i++)
642        if(!((1 << i) & crtc_mask))
643            G80CrtcBlankScreen(xf86_config->crtc[i], TRUE);
644
645    C(0x00000080, 0);
646}
647
648static const xf86CrtcFuncsRec g80_crtc_funcs = {
649    .dpms = G80CrtcDPMSSet,
650    .save = NULL,
651    .restore = NULL,
652    .lock = G80CrtcLock,
653    .unlock = NULL,
654    .mode_fixup = G80CrtcModeFixup,
655    .prepare = G80CrtcPrepare,
656    .mode_set = G80CrtcModeSet,
657    // .gamma_set = G80DispGammaSet,
658    .commit = G80CrtcCommit,
659    .shadow_create = NULL,
660    .shadow_destroy = NULL,
661    .set_cursor_position = G80SetCursorPosition,
662    .show_cursor = G80CrtcShowCursor,
663    .hide_cursor = G80CrtcHideCursor,
664    .load_cursor_argb = G80LoadCursorARGB,
665    .destroy = NULL,
666};
667
668void
669G80DispCreateCrtcs(ScrnInfoPtr pScrn)
670{
671    G80Ptr pNv = G80PTR(pScrn);
672    Head head;
673    xf86CrtcPtr crtc;
674    G80CrtcPrivPtr g80_crtc;
675
676    /* Create a "crtc" object for each head */
677    for(head = HEAD0; head <= HEAD1; head++) {
678        crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs);
679        if(!crtc) return;
680
681        g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1);
682        g80_crtc->head = head;
683        g80_crtc->dither = pNv->Dither;
684        crtc->driver_private = g80_crtc;
685    }
686}
687