NV50: Implement per CRTC gamma correction.
[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 #include <float.h>
25 #include <math.h>
26 #include <strings.h>
27 #include <unistd.h>
28
29 #include "nv_include.h"
30 #include "nv50_type.h"
31 #include "nv50_cursor.h"
32 #include "nv50_display.h"
33 #include "nv50_output.h"
34
35 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
36
37 /*
38  * PLL calculation.  pclk is in kHz.
39  */
40 static void
41 NV50CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
42 {
43     const float refclk = 27000.0f;
44     const float minVcoA = 100000;
45     const float maxVcoA = 400000;
46     const float minVcoB = 600000;
47     float maxVcoB = 1400000;
48     const float minUA = 2000;
49     const float maxUA = 400000;
50     const float minUB = 50000;
51     const float maxUB = 200000;
52     const int minNA = 1, maxNA = 255;
53     const int minNB = 1, maxNB = 31;
54     const int minMA = 1, maxMA = 255;
55     const int minMB = 1, maxMB = 31;
56     const int minP = 0, maxP = 6;
57     int lowP, highP;
58     float vcoB;
59
60     int na, ma, nb, mb, p;
61     float bestError = FLT_MAX;
62
63     *pNA = *pMA = *pNB = *pMB = *pP = 0;
64
65     if(maxVcoB < pclk + pclk / 200)
66         maxVcoB = pclk + pclk / 200;
67     if(minVcoB / (1 << maxP) > pclk)
68         pclk = minVcoB / (1 << maxP);
69
70     vcoB = maxVcoB - maxVcoB / 200;
71     lowP = minP;
72     vcoB /= 1 << (lowP + 1);
73
74     while(pclk <= vcoB && lowP < maxP)
75     {
76         vcoB /= 2;
77         lowP++;
78     }
79
80     vcoB = maxVcoB + maxVcoB / 200;
81     highP = lowP;
82     vcoB /= 1 << (highP + 1);
83
84     while(pclk <= vcoB && highP < maxP)
85     {
86         vcoB /= 2;
87         highP++;
88     }
89
90     for(p = lowP; p <= highP; p++)
91     {
92         for(ma = minMA; ma <= maxMA; ma++)
93         {
94             if(refclk / ma < minUA)
95                 break;
96             else if(refclk / ma > maxUA)
97                 continue;
98
99             for(na = minNA; na <= maxNA; na++)
100             {
101                 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
102                     continue;
103
104                 for(mb = minMB; mb <= maxMB; mb++)
105                 {
106                     if(refclk * na / ma / mb < minUB)
107                         break;
108                     else if(refclk * na / ma / mb > maxUB)
109                         continue;
110
111                     nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
112
113                     if(nb > maxNB)
114                         break;
115                     else if(nb < minNB)
116                         continue;
117                     else
118                     {
119                         float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
120                         float error = fabsf(pclk - freq);
121                         if(error < bestError) {
122                             *pNA = na;
123                             *pMA = ma;
124                             *pNB = nb;
125                             *pMB = mb;
126                             *pP = p;
127                             bestError = error;
128                         }
129                     }
130                 }
131             }
132         }
133     }
134 }
135
136 void NV50CrtcSetPClk(xf86CrtcPtr crtc)
137 {
138         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
139         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
140         ScrnInfoPtr pScrn = crtc->scrn;
141         NVPtr pNv = NVPTR(pScrn);
142         int lo_n, lo_m, hi_n, hi_m, p, i;
143         /* These clocks are probably rerouted from the 0x4000 range to the 0x610000 range */
144         CARD32 lo = NVRead(pNv, nv_crtc->head ? NV50_CRTC_VPLL2_A : NV50_CRTC_VPLL1_A);
145         CARD32 hi = NVRead(pNv, nv_crtc->head ? NV50_CRTC_VPLL2_B : NV50_CRTC_VPLL1_B);
146
147         NVWrite(pNv, 0x00614100 + nv_crtc->head * 0x800, 0x10000610);
148         lo &= 0xff00ff00;
149         hi &= 0x8000ff00;
150
151         NV50CalcPLL(nv_crtc->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p);
152
153         lo |= (lo_m << 16) | lo_n;
154         hi |= (p << 28) | (hi_m << 16) | hi_n;
155         NVWrite(pNv, nv_crtc->head ? NV50_CRTC_VPLL2_A : NV50_CRTC_VPLL1_A, lo);
156         NVWrite(pNv, nv_crtc->head ? NV50_CRTC_VPLL2_B : NV50_CRTC_VPLL1_B, hi);
157         NVWrite(pNv, 0x00614200 + nv_crtc->head * 0x800, 0);
158
159         for(i = 0; i < xf86_config->num_output; i++) {
160                 xf86OutputPtr output = xf86_config->output[i];
161
162                 if(output->crtc != crtc)
163                         continue;
164                 NV50OutputSetPClk(output, nv_crtc->pclk);
165         }
166 }
167
168 Head
169 NV50CrtcGetHead(xf86CrtcPtr crtc)
170 {
171         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
172         return nv_crtc->head;
173 }
174
175 Bool
176 NV50DispPreInit(ScrnInfoPtr pScrn)
177 {
178         NVPtr pNv = NVPTR(pScrn);
179         /* These labels are guesswork based on symmetry (2 SOR's and 3 DAC's exist)*/
180         NVWrite(pNv, 0x00610184, NVRead(pNv, 0x00614004));
181         NVWrite(pNv, 0x00610190 + SOR0 * 0x10, NVRead(pNv, 0x00616100 + SOR0 * 0x800));
182         NVWrite(pNv, 0x00610190 + SOR1 * 0x10, NVRead(pNv, 0x00616100 + SOR1 * 0x800));
183         NVWrite(pNv, 0x00610194 + SOR0 * 0x10, NVRead(pNv, 0x00616104 + SOR0 * 0x800));
184         NVWrite(pNv, 0x00610194 + SOR1 * 0x10, NVRead(pNv, 0x00616104 + SOR1 * 0x800));
185         NVWrite(pNv, 0x00610198 + SOR0 * 0x10, NVRead(pNv, 0x00616108 + SOR0 * 0x800));
186         NVWrite(pNv, 0x00610198 + SOR1 * 0x10, NVRead(pNv, 0x00616108 + SOR1 * 0x800));
187         NVWrite(pNv, 0x0061019c + SOR0 * 0x10, NVRead(pNv, 0x0061610c + SOR0 * 0x800));
188         NVWrite(pNv, 0x0061019c + SOR1 * 0x10, NVRead(pNv, 0x0061610c + SOR1 * 0x800));
189         NVWrite(pNv, 0x006101d0 + DAC0 * 0x4, NVRead(pNv, 0x0061a000 + DAC0 * 0x800));
190         NVWrite(pNv, 0x006101d0 + DAC1 * 0x4, NVRead(pNv, 0x0061a000 + DAC1 * 0x800));
191         NVWrite(pNv, 0x006101d0 + DAC2 * 0x4, NVRead(pNv, 0x0061a000 + DAC2 * 0x800));
192         NVWrite(pNv, 0x006101e0 + SOR0 * 0x4, NVRead(pNv, 0x0061c000 + SOR0 * 0x800));
193         NVWrite(pNv, 0x006101e0 + SOR1 * 0x4, NVRead(pNv, 0x0061c000 + SOR1 * 0x800));
194         NVWrite(pNv, NV50_DAC0_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
195         NVWrite(pNv, 0x0061a010 + DAC0 * 0x800, 0x00000001);
196         NVWrite(pNv, NV50_DAC1_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
197         NVWrite(pNv, 0x0061a010 + DAC1 * 0x800, 0x00000001);
198         NVWrite(pNv, NV50_DAC2_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
199         NVWrite(pNv, 0x0061a010 + DAC2 * 0x800, 0x00000001);
200
201         return TRUE;
202 }
203
204 Bool
205 NV50DispInit(ScrnInfoPtr pScrn)
206 {
207         NVPtr pNv = NVPTR(pScrn);
208         uint32_t val;
209         if (NVRead(pNv, NV50_DISPLAY_SUPERVISOR) & 0x100) {
210                 NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, 0x100);
211                 NVWrite(pNv, 0x006194e8, NVRead(pNv, 0x006194e8) & ~1);
212                 while (NVRead(pNv, 0x006194e8) & 2);
213         }
214
215         NVWrite(pNv, 0x00610200, 0x2b00);
216         /* A bugfix (#12637) from the nv driver, to unlock the driver if it's left in a poor state */
217         do {
218                 val = NVRead(pNv, 0x00610200);
219                 if ((val & 0x9f0000) == 0x20000)
220                         NVWrite(pNv, 0x00610200, val | 0x800000);
221
222                 if ((val & 0x3f0000) == 0x30000)
223                         NVWrite(pNv, 0x00610200, val | 0x200000);
224         } while ((val & 0x1e0000) != 0);
225         NVWrite(pNv, NV50_DISPLAY_CTRL_STATE, NV50_DISPLAY_CTRL_STATE_ENABLE);
226         NVWrite(pNv, 0x00610200, 0x1000b03);
227         while (!(NVRead(pNv, 0x00610200) & 0x40000000));
228
229         NV50DisplayCommand(pScrn, 0x84, 0);
230         NV50DisplayCommand(pScrn, 0x88, 0);
231         /* Does the bios always use crtc0? */
232         NV50DisplayCommand(pScrn, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
233         NV50DisplayCommand(pScrn, 0x800, 0);
234         NV50DisplayCommand(pScrn, 0x810, 0);
235         NV50DisplayCommand(pScrn, 0x82c, 0);
236
237         return TRUE;
238 }
239
240 void
241 NV50DispShutdown(ScrnInfoPtr pScrn)
242 {
243         NVPtr pNv = NVPTR(pScrn);
244         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
245         int i;
246
247         for(i = 0; i < xf86_config->num_crtc; i++) {
248                 xf86CrtcPtr crtc = xf86_config->crtc[i];
249
250                 NV50CrtcBlankScreen(crtc, TRUE);
251         }
252
253         NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
254
255         for(i = 0; i < xf86_config->num_crtc; i++) {
256                 xf86CrtcPtr crtc = xf86_config->crtc[i];
257                 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
258
259                 if(crtc->enabled) {
260                         uint32_t mask = 0;
261                         if (nv_crtc->head == 1)
262                                 mask = NV50_DISPLAY_SUPERVISOR_DISABLE_CRTC1;
263                         else 
264                                 mask = NV50_DISPLAY_SUPERVISOR_DISABLE_CRTC0;
265
266                         NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, mask);
267                         while(!(NVRead(pNv, NV50_DISPLAY_SUPERVISOR) & mask));
268                 }
269         }
270
271         NVWrite(pNv, 0x00610200, 0x0);
272         NVWrite(pNv, NV50_DISPLAY_CTRL_STATE, NV50_DISPLAY_CTRL_STATE_DISABLE);
273         while ((NVRead(pNv, 0x00610200) & 0x1e0000) != 0);
274         while ((NVRead(pNv, 0x0061c030 + SOR0 * 0x800) & 0x10000000));
275         while ((NVRead(pNv, 0x0061c030 + SOR1 * 0x800) & 0x10000000));
276 }
277
278 void
279 NV50CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src)
280 {
281         /* Magic mode timing fudge factor */
282         const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1;
283         const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1;
284
285         /* Stash the src timings in the Crtc fields in dst */
286         dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal;
287         dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 |
288                 (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1);
289         dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 |
290                 (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1);
291         dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 |
292                 (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1);
293         dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 |
294                 ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2);
295 }
296
297 Bool
298 NV50CrtcModeFixup(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode)
299 {
300         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
301
302         if (nv_crtc->skipModeFixup)
303                 return TRUE;
304
305         NV50CrtcDoModeFixup(adjusted_mode, mode);
306         return TRUE;
307 }
308
309 void
310 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y)
311 {
312         ScrnInfoPtr pScrn = crtc->scrn;
313         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
314         const int HDisplay = adjusted_mode->HDisplay;
315         const int VDisplay = adjusted_mode->VDisplay;
316
317         nv_crtc->pclk = adjusted_mode->Clock;
318
319         /* NV50CrtcCommand includes head offset */
320         NV50CrtcCommand(crtc, NV50_CRTC0_CLOCK, adjusted_mode->Clock | 0x800000);
321         NV50CrtcCommand(crtc, NV50_CRTC0_INTERLACE, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
322         NV50CrtcCommand(crtc, 0x810, 0);
323         NV50CrtcCommand(crtc, 0x82c, 0);
324         /* This confirms my suspicion that recent nvidia hardware does no vertical programming */
325         /* NV40 still has it as a legacy mode, and i don't know how to do the "new" way, but it definately exists */
326         NV50CrtcCommand(crtc, NV50_CRTC0_HBLANK_START, adjusted_mode->CrtcHBlankStart);
327         NV50CrtcCommand(crtc, NV50_CRTC0_HSYNC_END, adjusted_mode->CrtcHSyncEnd);
328         NV50CrtcCommand(crtc, NV50_CRTC0_HBLANK_END, adjusted_mode->CrtcHBlankEnd);
329         NV50CrtcCommand(crtc, NV50_CRTC0_HTOTAL, adjusted_mode->CrtcHTotal);
330         if(adjusted_mode->Flags & V_INTERLACE) {
331                 NV50CrtcCommand(crtc, 0x824, adjusted_mode->CrtcHSkew);
332         }
333         NV50CrtcCommand(crtc, NV50_CRTC0_FB_SIZE, pScrn->virtualY << 16 | pScrn->virtualX);
334         NV50CrtcCommand(crtc, NV50_CRTC0_PITCH, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
335         switch(pScrn->depth) {
336                 case 8:
337                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_8BPP); 
338                         break;
339                 case 15:
340                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_15BPP);
341                         break;
342                 case 16:
343                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_16BPP);
344                         break;
345                 case 24:
346                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_24BPP); 
347                         break;
348         }
349         NV50CrtcSetDither(crtc, FALSE);
350         NV50CrtcCommand(crtc, 0x8a8, 0x40000);
351         NV50CrtcCommand(crtc, NV50_CRTC0_FB_POS, y << 16 | x);
352         NV50CrtcCommand(crtc, NV50_CRTC0_SCRN_SIZE, VDisplay << 16 | HDisplay);
353         NV50CrtcCommand(crtc, 0x8d4, 0);
354
355         NV50CrtcBlankScreen(crtc, FALSE);
356 }
357
358 void
359 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
360 {
361         ScrnInfoPtr pScrn = crtc->scrn;
362         NVPtr pNv = NVPTR(pScrn);
363         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
364
365         if(blank) {
366                 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
367
368                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, NV50_CRTC0_CLUT_MODE_BLANK);
369                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, 0);
370                 if(pNv->NVArch != 0x50)
371                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_BLANK);
372                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
373                 if(pNv->NVArch != 0x50)
374                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_BLANK);
375         } else {
376                 NV50CrtcCommand(crtc, NV50_CRTC0_FB_OFFSET, pNv->FB->offset >> 8);
377                 NV50CrtcCommand(crtc, 0x864, 0);
378                 NVWrite(pNv, 0x00610380, 0);
379                 /* RAM is clamped to 256 MiB. */
380                 NVWrite(pNv, NV50_CRTC0_RAM_AMOUNT, pNv->RamAmountKBytes * 1024 - 1);
381                 NVWrite(pNv, 0x00610388, 0x150000);
382                 NVWrite(pNv, 0x0061038C, 0);
383                 NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_OFFSET, pNv->Cursor->offset >> 8);
384                 if(pNv->NVArch != 0x50)
385                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_UNBLANK);
386                 if(nv_crtc->cursorVisible)
387                         NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
388                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, 
389                         pScrn->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON);
390                 /* Each CRTC has it's own CLUT. */
391                 if (nv_crtc->head == 1)
392                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT1->offset >> 8);
393                 else
394                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT0->offset >> 8);
395                 if(pNv->NVArch != 0x50)
396                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_UNBLANK);
397                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_UNBLANK);
398      }
399 }
400
401 /******************************** Cursor stuff ********************************/
402 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
403 {
404         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
405         ScrnInfoPtr pScrn = crtc->scrn;
406
407         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR, 
408                 show ? NV50_CRTC0_CURSOR_SHOW : NV50_CRTC0_CURSOR_HIDE);
409         if (update) {
410                 nv_crtc->cursorVisible = show;
411                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
412         }
413 }
414
415 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
416 {
417         NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
418 }
419
420 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
421 {
422         NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
423 }
424
425 /******************************** CRTC stuff ********************************/
426
427 void
428 NV50CrtcPrepare(xf86CrtcPtr crtc)
429 {
430         ScrnInfoPtr pScrn = crtc->scrn;
431         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
432         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
433         int i;
434
435         for(i = 0; i < xf86_config->num_output; i++) {
436                 xf86OutputPtr output = xf86_config->output[i];
437
438                 if(!output->crtc)
439                         output->funcs->mode_set(output, NULL, NULL);
440         }
441
442         nv_crtc->skipModeFixup = FALSE;
443 }
444
445 void
446 NV50CrtcSkipModeFixup(xf86CrtcPtr crtc)
447 {
448         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
449         nv_crtc->skipModeFixup = TRUE;
450 }
451
452 void
453 NV50CrtcSetDither(xf86CrtcPtr crtc, Bool update)
454 {
455         xf86OutputPtr output = NULL;
456         ScrnInfoPtr pScrn = crtc->scrn;
457         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
458         int i;
459
460         for (i = 0; i < xf86_config->num_output; i++) {
461                 if (xf86_config->output[i]->crtc == crtc) {
462                         output = xf86_config->output[i];
463                         break;
464                 }
465         }
466
467         if (!output)
468                 return;
469
470         NVOutputPrivatePtr nv_output = output->driver_private;
471
472         NV50CrtcCommand(crtc, NV50_CRTC0_DITHERING_CTRL, nv_output->dithering ? 
473                         NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
474         if (update) 
475                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
476 }
477
478 static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY)
479 {
480         float scaleX, scaleY, scale;
481
482         scaleX = mode->CrtcHDisplay / (float)mode->HDisplay;
483         scaleY = mode->CrtcVDisplay / (float)mode->VDisplay;
484
485         if(scaleX > scaleY)
486                 scale = scaleY;
487         else
488                 scale = scaleX;
489
490         *outX = mode->HDisplay * scale;
491         *outY = mode->VDisplay * scale;
492 }
493
494 void NV50CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, enum scaling_modes scale)
495 {
496         int outX = 0, outY = 0;
497
498         switch(scale) {
499                 case SCALE_ASPECT:
500                         ComputeAspectScale(mode, &outX, &outY);
501                         break;
502                 case SCALE_PANEL:
503                 case SCALE_FULLSCREEN:
504                         outX = mode->CrtcHDisplay;
505                         outY = mode->CrtcVDisplay;
506                         break;
507                 case SCALE_NOSCALE:
508                 default:
509                         outX = mode->HDisplay;
510                         outY = mode->VDisplay;
511                         break;
512         }
513
514         if ((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
515                 mode->HDisplay != outX || mode->VDisplay != outY) {
516                 NV50CrtcCommand(crtc, 0x8a4, 9);
517         } else {
518                 NV50CrtcCommand(crtc, 0x8a4, 0);
519         }
520         NV50CrtcCommand(crtc, 0x8d8, outY << 16 | outX);
521         NV50CrtcCommand(crtc, 0x8dc, outY << 16 | outX);
522 }
523
524 void
525 NV50CrtcCommit(xf86CrtcPtr crtc)
526 {
527         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
528         ScrnInfoPtr pScrn = crtc->scrn;
529         int i, crtc_mask = 0;
530
531         /* If any heads are unused, blank them */
532         for(i = 0; i < xf86_config->num_output; i++) {
533                 xf86OutputPtr output = xf86_config->output[i];
534
535                 if (output->crtc) {
536                         /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
537                         crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
538                 }
539         }
540
541         for(i = 0; i < xf86_config->num_crtc; i++) {
542                 if(!((1 << i) & crtc_mask)) {
543                         NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
544                 }
545         }
546
547         NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
548 }
549