1fc5a983dSmrg/*
271ba42d0Smrg * Copyright (c) 2007,2010 NVIDIA Corporation
3fc5a983dSmrg *
4fc5a983dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5fc5a983dSmrg * copy of this software and associated documentation files (the
6fc5a983dSmrg * "Software"), to deal in the Software without restriction, including
7fc5a983dSmrg * without limitation the rights to use, copy, modify, merge, publish,
8fc5a983dSmrg * distribute, sublicense, and/or sell copies of the Software, and to
9fc5a983dSmrg * permit persons to whom the Software is furnished to do so, subject to
10fc5a983dSmrg * the following conditions:
11fc5a983dSmrg *
12fc5a983dSmrg * The above copyright notice and this permission notice shall be included
13fc5a983dSmrg * in all copies or substantial portions of the Software.
14fc5a983dSmrg *
15fc5a983dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16fc5a983dSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17fc5a983dSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18fc5a983dSmrg * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19fc5a983dSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20fc5a983dSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21fc5a983dSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22fc5a983dSmrg */
23fc5a983dSmrg
24fc5a983dSmrg
25fc5a983dSmrg#ifdef HAVE_CONFIG_H
26fc5a983dSmrg#include "config.h"
27fc5a983dSmrg#endif
28fc5a983dSmrg
29fc5a983dSmrg#include <float.h>
30fc5a983dSmrg#include <math.h>
31fc5a983dSmrg#include <strings.h>
32fc5a983dSmrg#include <unistd.h>
33fc5a983dSmrg
34fc5a983dSmrg#include "g80_type.h"
35fc5a983dSmrg#include "g80_cursor.h"
36fc5a983dSmrg#include "g80_display.h"
37fc5a983dSmrg#include "g80_output.h"
38fc5a983dSmrg
39fc5a983dSmrgtypedef struct G80CrtcPrivRec {
40fc5a983dSmrg    Head head;
41fc5a983dSmrg    int pclk; /* Target pixel clock in kHz */
42fc5a983dSmrg    Bool cursorVisible;
43fc5a983dSmrg    Bool skipModeFixup;
44fc5a983dSmrg    Bool dither;
4571ba42d0Smrg    /* Look-up table values to be set when the CRTC is enabled */
4671ba42d0Smrg    uint16_t lut_r[256], lut_g[256], lut_b[256];
47fc5a983dSmrg} G80CrtcPrivRec, *G80CrtcPrivPtr;
48fc5a983dSmrg
49fc5a983dSmrgstatic void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
50fc5a983dSmrg
51fc5a983dSmrg/*
52fc5a983dSmrg * PLL calculation.  pclk is in kHz.
53fc5a983dSmrg */
54fc5a983dSmrgstatic void
55fc5a983dSmrgG80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
56fc5a983dSmrg{
57fc5a983dSmrg    const float refclk = 27000.0f;
58fc5a983dSmrg    const float minVcoA = 100000;
59fc5a983dSmrg    const float maxVcoA = 400000;
60fc5a983dSmrg    const float minVcoB = 600000;
61fc5a983dSmrg    float maxVcoB = 1400000;
62fc5a983dSmrg    const float minUA = 2000;
63fc5a983dSmrg    const float maxUA = 400000;
64fc5a983dSmrg    const float minUB = 50000;
65fc5a983dSmrg    const float maxUB = 200000;
66fc5a983dSmrg    const int minNA = 1, maxNA = 255;
67fc5a983dSmrg    const int minNB = 1, maxNB = 31;
68fc5a983dSmrg    const int minMA = 1, maxMA = 255;
69fc5a983dSmrg    const int minMB = 1, maxMB = 31;
70fc5a983dSmrg    const int minP = 0, maxP = 6;
71fc5a983dSmrg    int lowP, highP;
72fc5a983dSmrg    float vcoB;
73fc5a983dSmrg
74fc5a983dSmrg    int na, ma, nb, mb, p;
75fc5a983dSmrg    float bestError = FLT_MAX;
76fc5a983dSmrg
77fc5a983dSmrg    *pNA = *pMA = *pNB = *pMB = *pP = 0;
78fc5a983dSmrg
79fc5a983dSmrg    if(maxVcoB < pclk + pclk / 200)
80fc5a983dSmrg        maxVcoB = pclk + pclk / 200;
81fc5a983dSmrg    if(minVcoB / (1 << maxP) > pclk)
82fc5a983dSmrg        pclk = minVcoB / (1 << maxP);
83fc5a983dSmrg
84fc5a983dSmrg    vcoB = maxVcoB - maxVcoB / 200;
85fc5a983dSmrg    lowP = minP;
86fc5a983dSmrg    vcoB /= 1 << (lowP + 1);
87fc5a983dSmrg
88fc5a983dSmrg    while(pclk <= vcoB && lowP < maxP)
89fc5a983dSmrg    {
90fc5a983dSmrg        vcoB /= 2;
91fc5a983dSmrg        lowP++;
92fc5a983dSmrg    }
93fc5a983dSmrg
94fc5a983dSmrg    vcoB = maxVcoB + maxVcoB / 200;
95fc5a983dSmrg    highP = lowP;
96fc5a983dSmrg    vcoB /= 1 << (highP + 1);
97fc5a983dSmrg
98fc5a983dSmrg    while(pclk <= vcoB && highP < maxP)
99fc5a983dSmrg    {
100fc5a983dSmrg        vcoB /= 2;
101fc5a983dSmrg        highP++;
102fc5a983dSmrg    }
103fc5a983dSmrg
104fc5a983dSmrg    for(p = lowP; p <= highP; p++)
105fc5a983dSmrg    {
106fc5a983dSmrg        for(ma = minMA; ma <= maxMA; ma++)
107fc5a983dSmrg        {
108fc5a983dSmrg            if(refclk / ma < minUA)
109fc5a983dSmrg                break;
110fc5a983dSmrg            else if(refclk / ma > maxUA)
111fc5a983dSmrg                continue;
112fc5a983dSmrg
113fc5a983dSmrg            for(na = minNA; na <= maxNA; na++)
114fc5a983dSmrg            {
115fc5a983dSmrg                if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
116fc5a983dSmrg                    continue;
117fc5a983dSmrg
118fc5a983dSmrg                for(mb = minMB; mb <= maxMB; mb++)
119fc5a983dSmrg                {
120fc5a983dSmrg                    if(refclk * na / ma / mb < minUB)
121fc5a983dSmrg                        break;
122fc5a983dSmrg                    else if(refclk * na / ma / mb > maxUB)
123fc5a983dSmrg                        continue;
124fc5a983dSmrg
125fc5a983dSmrg                    nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
126fc5a983dSmrg
127fc5a983dSmrg                    if(nb > maxNB)
128fc5a983dSmrg                        break;
129fc5a983dSmrg                    else if(nb < minNB)
130fc5a983dSmrg                        continue;
131fc5a983dSmrg                    else
132fc5a983dSmrg                    {
133fc5a983dSmrg                        float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
134fc5a983dSmrg                        float error = fabsf(pclk - freq);
135fc5a983dSmrg                        if(error < bestError) {
136fc5a983dSmrg                            *pNA = na;
137fc5a983dSmrg                            *pMA = ma;
138fc5a983dSmrg                            *pNB = nb;
139fc5a983dSmrg                            *pMB = mb;
140fc5a983dSmrg                            *pP = p;
141fc5a983dSmrg                            bestError = error;
142fc5a983dSmrg                        }
143fc5a983dSmrg                    }
144fc5a983dSmrg                }
145fc5a983dSmrg            }
146fc5a983dSmrg        }
147fc5a983dSmrg    }
148fc5a983dSmrg}
149fc5a983dSmrg
150bd2f6fc9Smrgstatic void
151bd2f6fc9SmrgG80CalcPLL2(float pclk, int *pN, int *pM, int *pPL)
152bd2f6fc9Smrg{
153bd2f6fc9Smrg    const float refclk = 27000.0f;
154bd2f6fc9Smrg    const int minN = 8, maxN = 255;
155bd2f6fc9Smrg    const int minM = 1, maxM = 255;
156bd2f6fc9Smrg    const int minPL = 1, maxPL = 63;
157bd2f6fc9Smrg    const int minU = 25000, maxU = 50000;
158bd2f6fc9Smrg    const int minVco = 500000;
159bd2f6fc9Smrg    int maxVco = 1000000;
160bd2f6fc9Smrg    int lowPL, highPL, pl;
161bd2f6fc9Smrg    float vco, bestError = FLT_MAX;
162bd2f6fc9Smrg
163bd2f6fc9Smrg    vco = pclk + pclk / 50;
164bd2f6fc9Smrg
165bd2f6fc9Smrg    if(maxVco < vco) maxVco = vco;
166bd2f6fc9Smrg
167bd2f6fc9Smrg    highPL = (maxVco + vco - 1) / pclk;
168bd2f6fc9Smrg    if(highPL > maxPL) highPL = maxPL;
169bd2f6fc9Smrg    if(highPL < minPL) highPL = minPL;
170bd2f6fc9Smrg
171bd2f6fc9Smrg    lowPL = minVco / vco;
172bd2f6fc9Smrg    if(lowPL > maxPL) lowPL = maxPL;
173bd2f6fc9Smrg    if(lowPL < minPL) lowPL = minPL;
174bd2f6fc9Smrg
175bd2f6fc9Smrg    for(pl = highPL; pl >= lowPL; pl--) {
176bd2f6fc9Smrg        int m;
177bd2f6fc9Smrg
178bd2f6fc9Smrg        for(m = minM; m <= maxM; m++) {
179bd2f6fc9Smrg            int n;
180bd2f6fc9Smrg            float freq, error;
181bd2f6fc9Smrg
182bd2f6fc9Smrg            if(refclk / m < minU) break;
183bd2f6fc9Smrg            if(refclk / m > maxU) continue;
184bd2f6fc9Smrg
185bd2f6fc9Smrg            n = rint(pclk * pl * m / refclk);
186bd2f6fc9Smrg            if(n > maxN) break;
187bd2f6fc9Smrg            if(n < minN) continue;
188bd2f6fc9Smrg
189bd2f6fc9Smrg            freq = refclk * (n / (float)m) / pl;
190bd2f6fc9Smrg            error = fabsf(pclk - freq);
191bd2f6fc9Smrg            if(error < bestError) {
192bd2f6fc9Smrg                *pN = n;
193bd2f6fc9Smrg                *pM = m;
194bd2f6fc9Smrg                *pPL = pl;
195bd2f6fc9Smrg                bestError = error;
196bd2f6fc9Smrg            }
197bd2f6fc9Smrg        }
198bd2f6fc9Smrg    }
199bd2f6fc9Smrg}
200bd2f6fc9Smrg
201fc5a983dSmrgstatic void
202fc5a983dSmrgG80CrtcSetPClk(xf86CrtcPtr crtc)
203fc5a983dSmrg{
204fc5a983dSmrg    G80Ptr pNv = G80PTR(crtc->scrn);
205fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
206fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
207fc5a983dSmrg    const int headOff = 0x800 * pPriv->head;
208bd2f6fc9Smrg    int i;
209bd2f6fc9Smrg
210bd2f6fc9Smrg    if(pPriv->pclk == 0)
211bd2f6fc9Smrg        return;
212bd2f6fc9Smrg
213bd2f6fc9Smrg    if(pNv->architecture <= 0xa0 ||
214bd2f6fc9Smrg       pNv->architecture == 0xaa ||
215bd2f6fc9Smrg       pNv->architecture == 0xac) {
216bd304fc0Smrg        int lo_n, lo_m, hi_n, hi_m, p;
217bd2f6fc9Smrg        CARD32 lo = pNv->reg[(0x00614104+headOff)/4];
218bd2f6fc9Smrg        CARD32 hi = pNv->reg[(0x00614108+headOff)/4];
219bd2f6fc9Smrg
220bd2f6fc9Smrg        pNv->reg[(0x00614100+headOff)/4] = 0x10000610;
221bd2f6fc9Smrg        lo &= 0xff00ff00;
222bd2f6fc9Smrg        hi &= 0x8000ff00;
223fc5a983dSmrg
224bd2f6fc9Smrg        G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
225fc5a983dSmrg
226bd2f6fc9Smrg        lo |= (lo_m << 16) | lo_n;
227bd2f6fc9Smrg        hi |= (p << 28) | (hi_m << 16) | hi_n;
228bd2f6fc9Smrg        pNv->reg[(0x00614104+headOff)/4] = lo;
229bd2f6fc9Smrg        pNv->reg[(0x00614108+headOff)/4] = hi;
230bd2f6fc9Smrg    } else {
231bd2f6fc9Smrg        int n, m, pl;
232bd2f6fc9Smrg        CARD32 r = pNv->reg[(0x00614104+headOff)/4];
233fc5a983dSmrg
234bd2f6fc9Smrg        pNv->reg[(0x00614100+headOff)/4] = 0x50000610;
235bd2f6fc9Smrg        r &= 0xffc00000;
236bd2f6fc9Smrg
237bd2f6fc9Smrg        G80CalcPLL2(pPriv->pclk, &n, &m, &pl);
238bd2f6fc9Smrg        r |= pl << 16 | m << 8 | n;
239bd2f6fc9Smrg
240bd2f6fc9Smrg        pNv->reg[(0x00614104+headOff)/4] = r;
241bd2f6fc9Smrg    }
242fc5a983dSmrg    pNv->reg[(0x00614200+headOff)/4] = 0;
243fc5a983dSmrg
244fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
245fc5a983dSmrg        xf86OutputPtr output = xf86_config->output[i];
246fc5a983dSmrg
247fc5a983dSmrg        if(output->crtc != crtc)
248fc5a983dSmrg            continue;
249fc5a983dSmrg        G80OutputSetPClk(output, pPriv->pclk);
250fc5a983dSmrg    }
251fc5a983dSmrg}
252fc5a983dSmrg
253fc5a983dSmrgvoid
254fc5a983dSmrgG80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data)
255fc5a983dSmrg{
256fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
257fc5a983dSmrg
258fc5a983dSmrg    pNv->reg[0x00610304/4] = data;
259fc5a983dSmrg    pNv->reg[0x00610300/4] = addr | 0x80010001;
260fc5a983dSmrg
261fc5a983dSmrg    while(pNv->reg[0x00610300/4] & 0x80000000) {
262fc5a983dSmrg        const int super = ffs((pNv->reg[0x00610024/4] >> 4) & 7);
263fc5a983dSmrg
264fc5a983dSmrg        if(super) {
265fc5a983dSmrg            if(super == 2) {
266fc5a983dSmrg                xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
267fc5a983dSmrg                int i;
268fc5a983dSmrg
269fc5a983dSmrg                for(i = 0; i < xf86_config->num_crtc; i++)
270fc5a983dSmrg                {
271fc5a983dSmrg                    xf86CrtcPtr crtc = xf86_config->crtc[i];
272f3561b8bSmrg                    const int headOff = 0x800 * G80CrtcGetHead(crtc);
273fc5a983dSmrg
274f3561b8bSmrg                    if((pNv->reg[(0x00614200+headOff)/4] & 0xc0) == 0x80)
275fc5a983dSmrg                        G80CrtcSetPClk(crtc);
276fc5a983dSmrg                }
277fc5a983dSmrg            }
278fc5a983dSmrg
279fc5a983dSmrg            pNv->reg[0x00610024/4] = 8 << super;
280fc5a983dSmrg            pNv->reg[0x00610030/4] = 0x80000000;
281fc5a983dSmrg        }
282fc5a983dSmrg    }
283fc5a983dSmrg}
284fc5a983dSmrg
285fc5a983dSmrgHead
286fc5a983dSmrgG80CrtcGetHead(xf86CrtcPtr crtc)
287fc5a983dSmrg{
288fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
289fc5a983dSmrg    return pPriv->head;
290fc5a983dSmrg}
291fc5a983dSmrg
292fc5a983dSmrgBool
293fc5a983dSmrgG80DispPreInit(ScrnInfoPtr pScrn)
294fc5a983dSmrg{
295fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
296fc5a983dSmrg
297fc5a983dSmrg    pNv->reg[0x00610184/4] = pNv->reg[0x00614004/4];
298fc5a983dSmrg    pNv->reg[0x00610190/4] = pNv->reg[0x00616100/4];
299fc5a983dSmrg    pNv->reg[0x006101a0/4] = pNv->reg[0x00616900/4];
300fc5a983dSmrg    pNv->reg[0x00610194/4] = pNv->reg[0x00616104/4];
301fc5a983dSmrg    pNv->reg[0x006101a4/4] = pNv->reg[0x00616904/4];
302fc5a983dSmrg    pNv->reg[0x00610198/4] = pNv->reg[0x00616108/4];
303fc5a983dSmrg    pNv->reg[0x006101a8/4] = pNv->reg[0x00616908/4];
304fc5a983dSmrg    pNv->reg[0x0061019C/4] = pNv->reg[0x0061610C/4];
305fc5a983dSmrg    pNv->reg[0x006101ac/4] = pNv->reg[0x0061690c/4];
306fc5a983dSmrg    pNv->reg[0x006101D0/4] = pNv->reg[0x0061A000/4];
307fc5a983dSmrg    pNv->reg[0x006101D4/4] = pNv->reg[0x0061A800/4];
308fc5a983dSmrg    pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4];
309fc5a983dSmrg    pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4];
310fc5a983dSmrg    pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4];
311bd2f6fc9Smrg    pNv->reg[0x006101E8/4] = pNv->reg[0x0061D000/4];
312bd2f6fc9Smrg    pNv->reg[0x006101EC/4] = pNv->reg[0x0061D800/4];
313fc5a983dSmrg    pNv->reg[0x0061A004/4] = 0x80550000;
314fc5a983dSmrg    pNv->reg[0x0061A010/4] = 0x00000001;
315fc5a983dSmrg    pNv->reg[0x0061A804/4] = 0x80550000;
316fc5a983dSmrg    pNv->reg[0x0061A810/4] = 0x00000001;
317fc5a983dSmrg    pNv->reg[0x0061B004/4] = 0x80550000;
318fc5a983dSmrg    pNv->reg[0x0061B010/4] = 0x00000001;
319fc5a983dSmrg
320fc5a983dSmrg    return TRUE;
321fc5a983dSmrg}
322fc5a983dSmrg
323fc5a983dSmrgBool
324fc5a983dSmrgG80DispInit(ScrnInfoPtr pScrn)
325fc5a983dSmrg{
326fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
327fc5a983dSmrg    CARD32 val;
328fc5a983dSmrg
329fc5a983dSmrg    if(pNv->reg[0x00610024/4] & 0x100) {
330fc5a983dSmrg        pNv->reg[0x00610024/4] = 0x100;
331fc5a983dSmrg        pNv->reg[0x006194E8/4] &= ~1;
332fc5a983dSmrg        while(pNv->reg[0x006194E8/4] & 2);
333fc5a983dSmrg    }
334fc5a983dSmrg
335fc5a983dSmrg    pNv->reg[0x00610200/4] = 0x2b00;
336fc5a983dSmrg    do {
337fc5a983dSmrg        val = pNv->reg[0x00610200/4];
338fc5a983dSmrg
339fc5a983dSmrg        if ((val & 0x9f0000) == 0x20000)
340fc5a983dSmrg            pNv->reg[0x00610200/4] = val | 0x800000;
341fc5a983dSmrg
342fc5a983dSmrg        if ((val & 0x3f0000) == 0x30000)
343fc5a983dSmrg            pNv->reg[0x00610200/4] = val | 0x200000;
344fc5a983dSmrg    } while ((val & 0x1e0000) != 0);
345fc5a983dSmrg    pNv->reg[0x00610300/4] = 1;
346fc5a983dSmrg    pNv->reg[0x00610200/4] = 0x1000b03;
347fc5a983dSmrg    while(!(pNv->reg[0x00610200/4] & 0x40000000));
348fc5a983dSmrg
349fc5a983dSmrg    C(0x00000084, 0);
350fc5a983dSmrg    C(0x00000088, 0);
351fc5a983dSmrg    C(0x00000874, 0);
352fc5a983dSmrg    C(0x00000800, 0);
353fc5a983dSmrg    C(0x00000810, 0);
354fc5a983dSmrg    C(0x0000082C, 0);
355fc5a983dSmrg
356fc5a983dSmrg    return TRUE;
357fc5a983dSmrg}
358fc5a983dSmrg
359fc5a983dSmrgvoid
360fc5a983dSmrgG80DispShutdown(ScrnInfoPtr pScrn)
361fc5a983dSmrg{
362fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
363fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
364fc5a983dSmrg    int i;
365fc5a983dSmrg
366fc5a983dSmrg    for(i = 0; i < xf86_config->num_crtc; i++) {
367fc5a983dSmrg        xf86CrtcPtr crtc = xf86_config->crtc[i];
368fc5a983dSmrg
369fc5a983dSmrg        G80CrtcBlankScreen(crtc, TRUE);
370fc5a983dSmrg    }
371fc5a983dSmrg
372fc5a983dSmrg    C(0x00000080, 0);
373fc5a983dSmrg
374fc5a983dSmrg    for(i = 0; i < xf86_config->num_crtc; i++) {
375fc5a983dSmrg        xf86CrtcPtr crtc = xf86_config->crtc[i];
376fc5a983dSmrg
377fc5a983dSmrg        if(crtc->enabled) {
378fc5a983dSmrg            const CARD32 mask = 4 << G80CrtcGetHead(crtc);
379fc5a983dSmrg
380fc5a983dSmrg            pNv->reg[0x00610024/4] = mask;
381fc5a983dSmrg            while(!(pNv->reg[0x00610024/4] & mask));
382fc5a983dSmrg        }
383fc5a983dSmrg    }
384fc5a983dSmrg
385fc5a983dSmrg    pNv->reg[0x00610200/4] = 0;
386fc5a983dSmrg    pNv->reg[0x00610300/4] = 0;
387fc5a983dSmrg    while((pNv->reg[0x00610200/4] & 0x1e0000) != 0);
388fc5a983dSmrg    while((pNv->reg[0x61C030/4] & 0x10000000));
389fc5a983dSmrg    while((pNv->reg[0x61C830/4] & 0x10000000));
390fc5a983dSmrg}
391fc5a983dSmrg
392fc5a983dSmrgvoid
393fc5a983dSmrgG80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src)
394fc5a983dSmrg{
395fc5a983dSmrg    /* Magic mode timing fudge factor */
396fc5a983dSmrg    const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1;
397fc5a983dSmrg    const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1;
398fc5a983dSmrg
399fc5a983dSmrg    /* Stash the src timings in the Crtc fields in dst */
400fc5a983dSmrg    dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal;
401fc5a983dSmrg    dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
402fc5a983dSmrg        (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1);
403fc5a983dSmrg    dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
404fc5a983dSmrg        (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1);
405fc5a983dSmrg    dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
406fc5a983dSmrg        (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1);
407fc5a983dSmrg    dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 |
408fc5a983dSmrg        ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2);
409fc5a983dSmrg}
410fc5a983dSmrg
411fc5a983dSmrgstatic Bool
412fc5a983dSmrgG80CrtcModeFixup(xf86CrtcPtr crtc,
413fc5a983dSmrg                 DisplayModePtr mode, DisplayModePtr adjusted_mode)
414fc5a983dSmrg{
415fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
416fc5a983dSmrg
417fc5a983dSmrg    if(pPriv->skipModeFixup)
418fc5a983dSmrg        return TRUE;
419fc5a983dSmrg
420fc5a983dSmrg    G80CrtcDoModeFixup(adjusted_mode, mode);
421fc5a983dSmrg    return TRUE;
422fc5a983dSmrg}
423fc5a983dSmrg
424fc5a983dSmrgstatic void
425fc5a983dSmrgG80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
426fc5a983dSmrg               DisplayModePtr adjusted_mode, int x, int y)
427fc5a983dSmrg{
428fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
429fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
430fc5a983dSmrg    const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay;
431fc5a983dSmrg    const int headOff = 0x400 * G80CrtcGetHead(crtc);
432fc5a983dSmrg
433fc5a983dSmrg    pPriv->pclk = adjusted_mode->Clock;
434fc5a983dSmrg
435fc5a983dSmrg    C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000);
436fc5a983dSmrg    C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
437fc5a983dSmrg    C(0x00000810 + headOff, 0);
438fc5a983dSmrg    C(0x0000082C + headOff, 0);
439fc5a983dSmrg    C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart);
440fc5a983dSmrg    C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd);
441fc5a983dSmrg    C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd);
442fc5a983dSmrg    C(0x00000820 + headOff, adjusted_mode->CrtcHTotal);
443fc5a983dSmrg    if(adjusted_mode->Flags & V_INTERLACE)
444fc5a983dSmrg        C(0x00000824 + headOff, adjusted_mode->CrtcHSkew);
445fc5a983dSmrg    C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX);
446fc5a983dSmrg    C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
447fc5a983dSmrg    switch(pScrn->depth) {
448fc5a983dSmrg        case  8: C(0x00000870 + headOff, 0x1E00); break;
449fc5a983dSmrg        case 15: C(0x00000870 + headOff, 0xE900); break;
450fc5a983dSmrg        case 16: C(0x00000870 + headOff, 0xE800); break;
451fc5a983dSmrg        case 24: C(0x00000870 + headOff, 0xCF00); break;
452fc5a983dSmrg    }
453fc5a983dSmrg    G80CrtcSetDither(crtc, pPriv->dither, FALSE);
454fc5a983dSmrg    C(0x000008A8 + headOff, 0x40000);
455fc5a983dSmrg    C(0x000008C0 + headOff, y << 16 | x);
456fc5a983dSmrg    C(0x000008C8 + headOff, VDisplay << 16 | HDisplay);
457fc5a983dSmrg    C(0x000008D4 + headOff, 0);
458fc5a983dSmrg
459fc5a983dSmrg    G80CrtcBlankScreen(crtc, FALSE);
460fc5a983dSmrg}
461fc5a983dSmrg
462fc5a983dSmrgvoid
463fc5a983dSmrgG80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
464fc5a983dSmrg{
465fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
466fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
467fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
468fc5a983dSmrg    const int headOff = 0x400 * pPriv->head;
469fc5a983dSmrg
470fc5a983dSmrg    if(blank) {
471fc5a983dSmrg        G80CrtcShowHideCursor(crtc, FALSE, FALSE);
472fc5a983dSmrg
473fc5a983dSmrg        C(0x00000840 + headOff, 0);
474fc5a983dSmrg        C(0x00000844 + headOff, 0);
475fc5a983dSmrg        if(pNv->architecture != 0x50)
476fc5a983dSmrg            C(0x0000085C + headOff, 0);
477fc5a983dSmrg        C(0x00000874 + headOff, 0);
478fc5a983dSmrg        if(pNv->architecture != 0x50)
479fc5a983dSmrg            C(0x0000089C + headOff, 0);
480fc5a983dSmrg    } else {
481fc5a983dSmrg        C(0x00000860 + headOff, 0);
482fc5a983dSmrg        C(0x00000864 + headOff, 0);
483fc5a983dSmrg        pNv->reg[0x00610380/4] = 0;
484fc5a983dSmrg        pNv->reg[0x00610384/4] = pNv->videoRam * 1024 - 1;
485fc5a983dSmrg        pNv->reg[0x00610388/4] = 0x150000;
486fc5a983dSmrg        pNv->reg[0x0061038C/4] = 0;
487fc5a983dSmrg        C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40);
488fc5a983dSmrg        if(pNv->architecture != 0x50)
489fc5a983dSmrg            C(0x0000089C + headOff, 1);
490fc5a983dSmrg        if(pPriv->cursorVisible)
491fc5a983dSmrg            G80CrtcShowHideCursor(crtc, TRUE, FALSE);
492fc5a983dSmrg        C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
49371ba42d0Smrg        C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head) >> 8);
494fc5a983dSmrg        if(pNv->architecture != 0x50)
495fc5a983dSmrg            C(0x0000085C + headOff, 1);
496fc5a983dSmrg        C(0x00000874 + headOff, 1);
497fc5a983dSmrg    }
498fc5a983dSmrg}
499fc5a983dSmrg
500fc5a983dSmrgstatic void
501fc5a983dSmrgG80CrtcDPMSSet(xf86CrtcPtr crtc, int mode)
502fc5a983dSmrg{
503fc5a983dSmrg}
504fc5a983dSmrg
505fc5a983dSmrg/******************************** Cursor stuff ********************************/
506fc5a983dSmrgstatic void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
507fc5a983dSmrg{
508fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
509fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
510fc5a983dSmrg    const int headOff = 0x400 * G80CrtcGetHead(crtc);
511fc5a983dSmrg
512fc5a983dSmrg    C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000);
513fc5a983dSmrg    if(update) {
514fc5a983dSmrg        pPriv->cursorVisible = show;
515fc5a983dSmrg        C(0x00000080, 0);
516fc5a983dSmrg    }
517fc5a983dSmrg}
518fc5a983dSmrg
519fc5a983dSmrgstatic void G80CrtcShowCursor(xf86CrtcPtr crtc)
520fc5a983dSmrg{
521fc5a983dSmrg    G80CrtcShowHideCursor(crtc, TRUE, TRUE);
522fc5a983dSmrg}
523fc5a983dSmrg
524fc5a983dSmrgstatic void G80CrtcHideCursor(xf86CrtcPtr crtc)
525fc5a983dSmrg{
526fc5a983dSmrg    G80CrtcShowHideCursor(crtc, FALSE, TRUE);
527fc5a983dSmrg}
528fc5a983dSmrg
529fc5a983dSmrg/******************************** CRTC stuff ********************************/
530fc5a983dSmrg
531fc5a983dSmrgstatic Bool
532fc5a983dSmrgG80CrtcLock(xf86CrtcPtr crtc)
533fc5a983dSmrg{
534fc5a983dSmrg    return FALSE;
535fc5a983dSmrg}
536fc5a983dSmrg
537fc5a983dSmrgstatic void
538fc5a983dSmrgG80CrtcPrepare(xf86CrtcPtr crtc)
539fc5a983dSmrg{
540fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
541fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
542fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
543fc5a983dSmrg    int i;
544fc5a983dSmrg
545fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
546fc5a983dSmrg        xf86OutputPtr output = xf86_config->output[i];
547fc5a983dSmrg
548fc5a983dSmrg        if(!output->crtc)
549fc5a983dSmrg            output->funcs->mode_set(output, NULL, NULL);
550fc5a983dSmrg    }
551fc5a983dSmrg
552fc5a983dSmrg    pPriv->skipModeFixup = FALSE;
553fc5a983dSmrg}
554fc5a983dSmrg
555fc5a983dSmrgvoid
556fc5a983dSmrgG80CrtcSkipModeFixup(xf86CrtcPtr crtc)
557fc5a983dSmrg{
558fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
559fc5a983dSmrg    pPriv->skipModeFixup = TRUE;
560fc5a983dSmrg}
561fc5a983dSmrg
562fc5a983dSmrgvoid
563fc5a983dSmrgG80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update)
564fc5a983dSmrg{
565fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
566fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
567fc5a983dSmrg    const int headOff = 0x400 * G80CrtcGetHead(crtc);
568fc5a983dSmrg
569fc5a983dSmrg    pPriv->dither = dither;
570fc5a983dSmrg
571fc5a983dSmrg    C(0x000008A0 + headOff, dither ? 0x11 : 0);
572fc5a983dSmrg    if(update) C(0x00000080, 0);
573fc5a983dSmrg}
574fc5a983dSmrg
575fc5a983dSmrgstatic void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY)
576fc5a983dSmrg{
577fc5a983dSmrg    float scaleX, scaleY, scale;
578fc5a983dSmrg
579fc5a983dSmrg    scaleX = mode->CrtcHDisplay / (float)mode->HDisplay;
580fc5a983dSmrg    scaleY = mode->CrtcVDisplay / (float)mode->VDisplay;
581fc5a983dSmrg
582fc5a983dSmrg    if(scaleX > scaleY)
583fc5a983dSmrg        scale = scaleY;
584fc5a983dSmrg    else
585fc5a983dSmrg        scale = scaleX;
586fc5a983dSmrg
587fc5a983dSmrg    *outX = mode->HDisplay * scale;
588fc5a983dSmrg    *outY = mode->VDisplay * scale;
589fc5a983dSmrg}
590fc5a983dSmrg
591fc5a983dSmrgvoid G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode,
592fc5a983dSmrg                     enum G80ScaleMode scale)
593fc5a983dSmrg{
594fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
595fc5a983dSmrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
596fc5a983dSmrg    const int headOff = 0x400 * pPriv->head;
597fc5a983dSmrg    int outX, outY;
598fc5a983dSmrg
599fc5a983dSmrg    switch(scale) {
600fc5a983dSmrg        default:
601fc5a983dSmrg        case G80_SCALE_ASPECT:
602fc5a983dSmrg            ComputeAspectScale(mode, &outX, &outY);
603fc5a983dSmrg            break;
604fc5a983dSmrg
605fc5a983dSmrg        case G80_SCALE_OFF:
606fc5a983dSmrg        case G80_SCALE_FILL:
607fc5a983dSmrg            outX = mode->CrtcHDisplay;
608fc5a983dSmrg            outY = mode->CrtcVDisplay;
609fc5a983dSmrg            break;
610fc5a983dSmrg
611fc5a983dSmrg        case G80_SCALE_CENTER:
612fc5a983dSmrg            outX = mode->HDisplay;
613fc5a983dSmrg            outY = mode->VDisplay;
614fc5a983dSmrg            break;
615fc5a983dSmrg    }
616fc5a983dSmrg
617fc5a983dSmrg    if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
618fc5a983dSmrg       mode->HDisplay != outX || mode->VDisplay != outY) {
619fc5a983dSmrg        C(0x000008A4 + headOff, 9);
620fc5a983dSmrg    } else {
621fc5a983dSmrg        C(0x000008A4 + headOff, 0);
622fc5a983dSmrg    }
623fc5a983dSmrg    C(0x000008D8 + headOff, outY << 16 | outX);
624fc5a983dSmrg    C(0x000008DC + headOff, outY << 16 | outX);
625fc5a983dSmrg}
626fc5a983dSmrg
627fc5a983dSmrgstatic void
628fc5a983dSmrgG80CrtcCommit(xf86CrtcPtr crtc)
629fc5a983dSmrg{
630fc5a983dSmrg    ScrnInfoPtr pScrn = crtc->scrn;
631fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
632fc5a983dSmrg    int i, crtc_mask = 0;
633fc5a983dSmrg
634fc5a983dSmrg    /* If any heads are unused, blank them */
635fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
636fc5a983dSmrg        xf86OutputPtr output = xf86_config->output[i];
637fc5a983dSmrg
638fc5a983dSmrg        if(output->crtc)
639fc5a983dSmrg            /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
640fc5a983dSmrg            crtc_mask |= 1 << G80CrtcGetHead(output->crtc);
641fc5a983dSmrg    }
642fc5a983dSmrg
643fc5a983dSmrg    for(i = 0; i < xf86_config->num_crtc; i++)
644fc5a983dSmrg        if(!((1 << i) & crtc_mask))
645fc5a983dSmrg            G80CrtcBlankScreen(xf86_config->crtc[i], TRUE);
646fc5a983dSmrg
647fc5a983dSmrg    C(0x00000080, 0);
648fc5a983dSmrg}
649fc5a983dSmrg
65071ba42d0Smrgstatic void
65171ba42d0SmrgG80CrtcGammaSet(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue,
65271ba42d0Smrg                int size)
65371ba42d0Smrg{
65471ba42d0Smrg    ScrnInfoPtr pScrn = crtc->scrn;
65571ba42d0Smrg    G80Ptr pNv = G80PTR(pScrn);
65671ba42d0Smrg    G80CrtcPrivPtr pPriv = crtc->driver_private;
65771ba42d0Smrg    int i;
65871ba42d0Smrg    volatile struct {
65971ba42d0Smrg        uint16_t red, green, blue, unused;
66071ba42d0Smrg    } *lut = (void*)&pNv->mem[pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head];
66171ba42d0Smrg
66271ba42d0Smrg    assert(size == 256);
66371ba42d0Smrg
66471ba42d0Smrg    for(i = 0; i < size; i++) {
66571ba42d0Smrg        pPriv->lut_r[i] = lut[i].red   = red[i] >> 2;
66671ba42d0Smrg        pPriv->lut_g[i] = lut[i].green = green[i] >> 2;
66771ba42d0Smrg        pPriv->lut_b[i] = lut[i].blue  = blue[i] >> 2;
66871ba42d0Smrg    }
66971ba42d0Smrg
67071ba42d0Smrg    lut[256] = lut[255];
67171ba42d0Smrg}
67271ba42d0Smrg
67371ba42d0Smrgvoid
67471ba42d0SmrgG80LoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
67571ba42d0Smrg               VisualPtr pVisual)
67671ba42d0Smrg{
67771ba42d0Smrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
67871ba42d0Smrg    int i, j, index;
67971ba42d0Smrg    int p;
68071ba42d0Smrg    uint16_t lut_r[256], lut_g[256], lut_b[256];
68171ba42d0Smrg
68271ba42d0Smrg    for(p = 0; p < xf86_config->num_crtc; p++) {
68371ba42d0Smrg        xf86CrtcPtr crtc = xf86_config->crtc[p];
68471ba42d0Smrg        G80CrtcPrivPtr pPriv = crtc->driver_private;
68571ba42d0Smrg
68671ba42d0Smrg        /* Initialize to the old lookup table values. */
68771ba42d0Smrg        for(i = 0; i < 256; i++) {
68871ba42d0Smrg            lut_r[i] = pPriv->lut_r[i] << 2;
68971ba42d0Smrg            lut_g[i] = pPriv->lut_g[i] << 2;
69071ba42d0Smrg            lut_b[i] = pPriv->lut_b[i] << 2;
69171ba42d0Smrg        }
69271ba42d0Smrg
69371ba42d0Smrg        switch(pScrn->depth) {
69471ba42d0Smrg            case 15:
69571ba42d0Smrg                for(i = 0; i < numColors; i++) {
69671ba42d0Smrg                    index = indices[i];
69771ba42d0Smrg                    for(j = 0; j < 8; j++) {
69871ba42d0Smrg                        lut_r[index * 8 + j] =
69971ba42d0Smrg                            colors[index].red << 8;
70071ba42d0Smrg                        lut_g[index * 8 + j] =
70171ba42d0Smrg                            colors[index].green << 8;
70271ba42d0Smrg                        lut_b[index * 8 + j] =
70371ba42d0Smrg                            colors[index].blue << 8;
70471ba42d0Smrg                    }
70571ba42d0Smrg                }
70671ba42d0Smrg                break;
70771ba42d0Smrg            case 16:
70871ba42d0Smrg                for(i = 0; i < numColors; i++) {
70971ba42d0Smrg                    index = indices[i];
71071ba42d0Smrg
71171ba42d0Smrg                    if(index <= 31) {
71271ba42d0Smrg                        for(j = 0; j < 8; j++) {
71371ba42d0Smrg                            lut_r[index * 8 + j] =
71471ba42d0Smrg                                colors[index].red << 8;
71571ba42d0Smrg                            lut_b[index * 8 + j] =
71671ba42d0Smrg                                colors[index].blue << 8;
71771ba42d0Smrg                        }
71871ba42d0Smrg                    }
71971ba42d0Smrg
72071ba42d0Smrg                    for(j = 0; j < 4; j++) {
72171ba42d0Smrg                        lut_g[index * 4 + j] =
72271ba42d0Smrg                            colors[index].green << 8;
72371ba42d0Smrg                    }
72471ba42d0Smrg                }
72571ba42d0Smrg                break;
72671ba42d0Smrg            default:
72771ba42d0Smrg                for(i = 0; i < numColors; i++) {
72871ba42d0Smrg                    index = indices[i];
72971ba42d0Smrg                    lut_r[index] = colors[index].red << 8;
73071ba42d0Smrg                    lut_g[index] = colors[index].green << 8;
73171ba42d0Smrg                    lut_b[index] = colors[index].blue << 8;
73271ba42d0Smrg                }
73371ba42d0Smrg                break;
73471ba42d0Smrg        }
73571ba42d0Smrg
73671ba42d0Smrg        /* Make the change through RandR */
73771ba42d0Smrg        RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
73871ba42d0Smrg    }
73971ba42d0Smrg}
74071ba42d0Smrg
741fc5a983dSmrgstatic const xf86CrtcFuncsRec g80_crtc_funcs = {
742fc5a983dSmrg    .dpms = G80CrtcDPMSSet,
743fc5a983dSmrg    .save = NULL,
744fc5a983dSmrg    .restore = NULL,
745fc5a983dSmrg    .lock = G80CrtcLock,
746fc5a983dSmrg    .unlock = NULL,
747fc5a983dSmrg    .mode_fixup = G80CrtcModeFixup,
748fc5a983dSmrg    .prepare = G80CrtcPrepare,
749fc5a983dSmrg    .mode_set = G80CrtcModeSet,
75071ba42d0Smrg    .gamma_set = G80CrtcGammaSet,
751fc5a983dSmrg    .commit = G80CrtcCommit,
752fc5a983dSmrg    .shadow_create = NULL,
753fc5a983dSmrg    .shadow_destroy = NULL,
754fc5a983dSmrg    .set_cursor_position = G80SetCursorPosition,
755fc5a983dSmrg    .show_cursor = G80CrtcShowCursor,
756fc5a983dSmrg    .hide_cursor = G80CrtcHideCursor,
757fc5a983dSmrg    .load_cursor_argb = G80LoadCursorARGB,
758fc5a983dSmrg    .destroy = NULL,
759fc5a983dSmrg};
760fc5a983dSmrg
761fc5a983dSmrgvoid
762fc5a983dSmrgG80DispCreateCrtcs(ScrnInfoPtr pScrn)
763fc5a983dSmrg{
764fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
765fc5a983dSmrg    Head head;
766fc5a983dSmrg    xf86CrtcPtr crtc;
767fc5a983dSmrg    G80CrtcPrivPtr g80_crtc;
768fc5a983dSmrg
769fc5a983dSmrg    /* Create a "crtc" object for each head */
770fc5a983dSmrg    for(head = HEAD0; head <= HEAD1; head++) {
771fc5a983dSmrg        crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs);
772fc5a983dSmrg        if(!crtc) return;
773fc5a983dSmrg
774fc5a983dSmrg        g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1);
775fc5a983dSmrg        g80_crtc->head = head;
776fc5a983dSmrg        g80_crtc->dither = pNv->Dither;
777fc5a983dSmrg        crtc->driver_private = g80_crtc;
778fc5a983dSmrg    }
779fc5a983dSmrg}
780