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