NV50: Remove some questionable guesses.
[nouveau] / src / nv50_display.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  * Copyright (c) 2008 Maarten Maathuis
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <float.h>
26 #include <math.h>
27 #include <strings.h>
28 #include <unistd.h>
29
30 #include "nv_include.h"
31 #include "nv50_type.h"
32 #include "nv50_cursor.h"
33 #include "nv50_display.h"
34 #include "nv50_output.h"
35
36 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
37
38 void NV50CrtcSetPClk(xf86CrtcPtr crtc)
39 {
40         ScrnInfoPtr pScrn = crtc->scrn;
41         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetPClk is called.\n");
42
43         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
44         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
45         NVPtr pNv = NVPTR(pScrn);
46         int i;
47
48         /* I don't know why exactly, but these were in my table. */
49         uint32_t pll_reg = nv_crtc->head ? NV50_CRTC1_CLK_CTRL1 : NV50_CRTC0_CLK_CTRL1;
50
51         int NM1 = 0xbeef, NM2 = 0xdead, log2P;
52         struct pll_lims pll_lim;
53         get_pll_limits(pScrn, pll_reg, &pll_lim);
54         /* NV5x hardware doesn't seem to support a single vco mode, otherwise the blob is hiding it well. */
55         getMNP_double(pScrn, &pll_lim, nv_crtc->pclk, &NM1, &NM2, &log2P);
56
57         uint32_t reg1 = NVRead(pNv, pll_reg + 4);
58         uint32_t reg2 = NVRead(pNv, pll_reg + 8);
59
60         /* bit0: The blob (and bios) seem to have this on (almost) always.
61          *          I'm hoping this (experiment) will fix my image stability issues.
62          */
63         NVWrite(pNv, NV50_CRTC0_CLK_CTRL1 + nv_crtc->head * 0x800, NV50_CRTC_CLK_CTRL1_CONNECTED | 0x10000011);
64
65         /* Eventually we should learn ourselves what all the bits should be. */
66         reg1 &= 0xff00ff00;
67         reg2 &= 0x8000ff00;
68
69         uint8_t N1 = (NM1 >> 8) & 0xFF;
70         uint8_t M1 = NM1 & 0xFF;
71         uint8_t N2 = (NM2 >> 8) & 0xFF;
72         uint8_t M2 = NM2 & 0xFF;
73
74         reg1 |= (M1 << 16) | N1;
75         reg2 |= (log2P << 28) | (M2 << 16) | N2;
76
77         NVWrite(pNv, pll_reg + 4, reg1);
78         NVWrite(pNv, pll_reg + 8, reg2);
79
80         /* There seem to be a few indicator bits, which are similar to the SOR_CTRL bits. */
81         NVWrite(pNv, NV50_CRTC0_CLK_CTRL2 + nv_crtc->head * 0x800, 0);
82
83         for(i = 0; i < xf86_config->num_output; i++) {
84                 xf86OutputPtr output = xf86_config->output[i];
85
86                 if(output->crtc != crtc)
87                         continue;
88                 NV50OutputSetPClk(output, nv_crtc->pclk);
89         }
90 }
91
92 Head
93 NV50CrtcGetHead(xf86CrtcPtr crtc)
94 {
95         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
96         return nv_crtc->head;
97 }
98
99 Bool
100 NV50DispPreInit(ScrnInfoPtr pScrn)
101 {
102         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispPreInit is called.\n");
103
104         NVPtr pNv = NVPTR(pScrn);
105         /* These labels are guesswork based on symmetry (2 SOR's and 3 DAC's exist)*/
106         NVWrite(pNv, 0x00610184, NVRead(pNv, 0x00614004));
107         NVWrite(pNv, 0x00610190 + 0 * 0x10, NVRead(pNv, 0x00616100 + 0 * 0x800));
108         NVWrite(pNv, 0x00610190 + 1 * 0x10, NVRead(pNv, 0x00616100 + 1 * 0x800));
109         NVWrite(pNv, 0x00610194 + 0 * 0x10, NVRead(pNv, 0x00616104 + 0 * 0x800));
110         NVWrite(pNv, 0x00610194 + 1 * 0x10, NVRead(pNv, 0x00616104 + 1 * 0x800));
111         NVWrite(pNv, 0x00610198 + 0 * 0x10, NVRead(pNv, 0x00616108 + 0 * 0x800));
112         NVWrite(pNv, 0x00610198 + 1 * 0x10, NVRead(pNv, 0x00616108 + 1 * 0x800));
113         NVWrite(pNv, 0x0061019c + 0 * 0x10, NVRead(pNv, 0x0061610c + 0 * 0x800));
114         NVWrite(pNv, 0x0061019c + 1 * 0x10, NVRead(pNv, 0x0061610c + 1 * 0x800));
115         NVWrite(pNv, 0x006101d0 + DAC0 * 0x4, NVRead(pNv, 0x0061a000 + DAC0 * 0x800));
116         NVWrite(pNv, 0x006101d0 + DAC1 * 0x4, NVRead(pNv, 0x0061a000 + DAC1 * 0x800));
117         NVWrite(pNv, 0x006101d0 + DAC2 * 0x4, NVRead(pNv, 0x0061a000 + DAC2 * 0x800));
118         NVWrite(pNv, 0x006101e0 + SOR0 * 0x4, NVRead(pNv, 0x0061c000 + SOR0 * 0x800));
119         NVWrite(pNv, 0x006101e0 + SOR1 * 0x4, NVRead(pNv, 0x0061c000 + SOR1 * 0x800));
120         NVWrite(pNv, NV50_DAC0_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
121         NVWrite(pNv, NV50_DAC0_CLK_CTRL2, 0x00000001);
122         NVWrite(pNv, NV50_DAC1_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
123         NVWrite(pNv, NV50_DAC1_CLK_CTRL2, 0x00000001);
124         NVWrite(pNv, NV50_DAC2_DPMS_CTRL, 0x00550000 | NV50_DAC_DPMS_CTRL_PENDING);
125         NVWrite(pNv, NV50_DAC2_CLK_CTRL2, 0x00000001);
126
127         return TRUE;
128 }
129
130 Bool
131 NV50DispInit(ScrnInfoPtr pScrn)
132 {
133         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispInit is called.\n");
134
135         NVPtr pNv = NVPTR(pScrn);
136         uint32_t val;
137         if (NVRead(pNv, NV50_DISPLAY_SUPERVISOR) & 0x100) {
138                 NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, 0x100);
139                 NVWrite(pNv, 0x006194e8, NVRead(pNv, 0x006194e8) & ~1);
140                 while (NVRead(pNv, 0x006194e8) & 2);
141         }
142
143         NVWrite(pNv, NV50_DISPLAY_UNK200_CTRL, 0x2b00);
144         /* A bugfix (#12637) from the nv driver, to unlock the driver if it's left in a poor state */
145         do {
146                 val = NVRead(pNv, NV50_DISPLAY_UNK200_CTRL);
147                 if ((val & 0x9f0000) == 0x20000)
148                         NVWrite(pNv, NV50_DISPLAY_UNK200_CTRL, val | 0x800000);
149
150                 if ((val & 0x3f0000) == 0x30000)
151                         NVWrite(pNv, NV50_DISPLAY_UNK200_CTRL, val | 0x200000);
152         } while ((val & 0x1e0000) != 0);
153         NVWrite(pNv, NV50_DISPLAY_CTRL_STATE, NV50_DISPLAY_CTRL_STATE_ENABLE);
154         NVWrite(pNv, NV50_DISPLAY_UNK200_CTRL, 0x1000b03);
155         while (!(NVRead(pNv, NV50_DISPLAY_UNK200_CTRL) & 0x40000000));
156
157         NV50DisplayCommand(pScrn, 0x84, 0);
158         NV50DisplayCommand(pScrn, 0x88, 0);
159         /* The GetLVDSNativeMode() function is proof that more than crtc0 is used by the bios. */
160         NV50DisplayCommand(pScrn, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
161         NV50DisplayCommand(pScrn, 0x800, 0);
162         NV50DisplayCommand(pScrn, NV50_CRTC0_DISPLAY_START, 0);
163         NV50DisplayCommand(pScrn, 0x82c, 0);
164
165         return TRUE;
166 }
167
168 void
169 NV50DispShutdown(ScrnInfoPtr pScrn)
170 {
171         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50DispShutdown is called.\n");
172         NVPtr pNv = NVPTR(pScrn);
173         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
174         int i;
175
176         for(i = 0; i < xf86_config->num_crtc; i++) {
177                 xf86CrtcPtr crtc = xf86_config->crtc[i];
178
179                 NV50CrtcBlankScreen(crtc, TRUE);
180         }
181
182         NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
183
184         for(i = 0; i < xf86_config->num_crtc; i++) {
185                 xf86CrtcPtr crtc = xf86_config->crtc[i];
186                 NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
187
188                 /* I think this is some kind of trigger to reinitialise the supervisor. */
189                 /* Without anything active (this is shutdown) it's likely to disable itself. */
190                 /* The blob doesn't do it quite this way., it seems to do 0x30C as init and end. */
191                 /* It doesn't wait for a non-zero value either. */
192                 if (crtc->enabled) {
193                         uint32_t mask = 0;
194                         if (nv_crtc->head == 1)
195                                 mask = NV50_DISPLAY_SUPERVISOR_CRTC1;
196                         else 
197                                 mask = NV50_DISPLAY_SUPERVISOR_CRTC0;
198
199                         NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, mask);
200                         while(!(NVRead(pNv, NV50_DISPLAY_SUPERVISOR) & mask));
201                 }
202         }
203
204         NVWrite(pNv, NV50_DISPLAY_UNK200_CTRL, 0x0);
205         NVWrite(pNv, NV50_DISPLAY_CTRL_STATE, NV50_DISPLAY_CTRL_STATE_DISABLE);
206         while ((NVRead(pNv, NV50_DISPLAY_UNK200_CTRL) & 0x1e0000) != 0);
207         while ((NVRead(pNv, 0x0061c030 + SOR0 * 0x800) & 0x10000000));
208         while ((NVRead(pNv, 0x0061c030 + SOR1 * 0x800) & 0x10000000));
209 }
210
211 void
212 NV50CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y)
213 {
214         ScrnInfoPtr pScrn = crtc->scrn;
215         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcModeSet is called with position (%d, %d).\n", x, y);
216
217         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
218
219         nv_crtc->pclk = adjusted_mode->Clock;
220
221         uint32_t hsync_dur = adjusted_mode->CrtcHSyncEnd - adjusted_mode->CrtcHSyncStart;
222         uint32_t vsync_dur = adjusted_mode->CrtcVSyncEnd - adjusted_mode->CrtcVSyncStart;
223         uint32_t hsync_start_to_end = adjusted_mode->CrtcHBlankEnd - adjusted_mode->CrtcHSyncStart;
224         uint32_t vsync_start_to_end = adjusted_mode->CrtcVBlankEnd - adjusted_mode->CrtcVSyncStart;
225         /* I can't give this a proper name, anyone else can? */
226         uint32_t hunk1 = adjusted_mode->CrtcHTotal - adjusted_mode->CrtcHSyncStart + adjusted_mode->CrtcHBlankStart;
227         uint32_t vunk1 = adjusted_mode->CrtcVTotal - adjusted_mode->CrtcVSyncStart + adjusted_mode->CrtcVBlankStart;
228         /* Another strange value, this time only for interlaced modes. */
229         uint32_t vunk2a = 2*adjusted_mode->CrtcVTotal - adjusted_mode->CrtcVSyncStart + adjusted_mode->CrtcVBlankStart;
230         uint32_t vunk2b = adjusted_mode->CrtcVTotal - adjusted_mode->CrtcVSyncStart + adjusted_mode->CrtcVBlankEnd;
231
232         if (adjusted_mode->Flags & V_INTERLACE) {
233                 vsync_dur /= 2;
234                 vsync_start_to_end  /= 2;
235                 vunk1 /= 2;
236                 vunk2a /= 2;
237                 vunk2b /= 2;
238                 /* magic */
239                 if (adjusted_mode->Flags & V_DBLSCAN) {
240                         vsync_start_to_end -= 1;
241                         vunk1 -= 1;
242                         vunk2a -= 1;
243                         vunk2b -= 1;
244                 }
245         }
246
247         /* NV50CrtcCommand includes head offset */
248         /* This is the native mode when DFP && !SCALE_PANEL */
249         NV50CrtcCommand(crtc, NV50_CRTC0_CLOCK, adjusted_mode->Clock | 0x800000);
250         NV50CrtcCommand(crtc, NV50_CRTC0_INTERLACE, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0);
251         NV50CrtcCommand(crtc, NV50_CRTC0_DISPLAY_START, 0);
252         NV50CrtcCommand(crtc, 0x82c, 0);
253         NV50CrtcCommand(crtc, NV50_CRTC0_DISPLAY_TOTAL, adjusted_mode->CrtcVTotal << 16 | adjusted_mode->CrtcHTotal);
254         NV50CrtcCommand(crtc, NV50_CRTC0_SYNC_DURATION, (vsync_dur - 1) << 16 | (hsync_dur - 1));
255         NV50CrtcCommand(crtc, NV50_CRTC0_SYNC_START_TO_BLANK_END, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1));
256         NV50CrtcCommand(crtc, NV50_CRTC0_MODE_UNK1, (vunk1 - 1) << 16 | (hunk1 - 1));
257         if (adjusted_mode->Flags & V_INTERLACE) {
258                 NV50CrtcCommand(crtc, NV50_CRTC0_MODE_UNK2, (vunk2b - 1) << 16 | (vunk2a - 1));
259         }
260         NV50CrtcCommand(crtc, NV50_CRTC0_FB_SIZE, pScrn->virtualY << 16 | pScrn->virtualX);
261         NV50CrtcCommand(crtc, NV50_CRTC0_PITCH, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
262         switch(pScrn->depth) {
263                 case 8:
264                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_8BPP); 
265                         break;
266                 case 15:
267                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_15BPP);
268                         break;
269                 case 16:
270                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_16BPP);
271                         break;
272                 case 24:
273                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_24BPP); 
274                         break;
275         }
276         NV50CrtcSetDither(crtc, FALSE);
277         NV50CrtcCommand(crtc, NV50_CRTC0_UNK_8A8, 0x40000);
278         NV50CrtcCommand(crtc, NV50_CRTC0_FB_POS, y << 16 | x);
279         /* This is the actual resolution of the mode. */
280         NV50CrtcCommand(crtc, NV50_CRTC0_SCRN_SIZE, (mode->VDisplay << 16) | mode->HDisplay);
281         NV50CrtcCommand(crtc, 0x8d4, 0);
282
283         NV50CrtcBlankScreen(crtc, FALSE);
284 }
285
286 void
287 NV50CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
288 {
289         ScrnInfoPtr pScrn = crtc->scrn;
290         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcBlankScreen is called (%s).\n", blank ? "blanked" : "unblanked");
291
292         NVPtr pNv = NVPTR(pScrn);
293         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
294
295         if(blank) {
296                 NV50CrtcShowHideCursor(crtc, FALSE, FALSE);
297
298                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, NV50_CRTC0_CLUT_MODE_BLANK);
299                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, 0);
300                 if(pNv->NVArch != 0x50)
301                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_BLANK);
302                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
303                 if(pNv->NVArch != 0x50)
304                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_BLANK);
305         } else {
306                 NV50CrtcCommand(crtc, NV50_CRTC0_FB_OFFSET, pNv->FB->offset >> 8);
307                 NV50CrtcCommand(crtc, 0x864, 0);
308                 NVWrite(pNv, 0x00610380, 0);
309                 /* RAM is clamped to 256 MiB. */
310                 NVWrite(pNv, NV50_CRTC0_RAM_AMOUNT, pNv->RamAmountKBytes * 1024 - 1);
311                 NVWrite(pNv, 0x00610388, 0x150000);
312                 NVWrite(pNv, 0x0061038C, 0);
313                 if (nv_crtc->head == 1)
314                         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_OFFSET, pNv->Cursor2->offset >> 8);
315                 else
316                         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_OFFSET, pNv->Cursor->offset >> 8);
317                 if(pNv->NVArch != 0x50)
318                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_UNBLANK);
319                 if(nv_crtc->cursorVisible)
320                         NV50CrtcShowHideCursor(crtc, TRUE, FALSE);
321                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, 
322                         pScrn->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON);
323                 /* Each CRTC has it's own CLUT. */
324                 if (nv_crtc->head == 1)
325                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT1->offset >> 8);
326                 else
327                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT0->offset >> 8);
328                 if(pNv->NVArch != 0x50)
329                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_UNBLANK);
330                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_UNBLANK);
331      }
332 }
333
334 /******************************** Cursor stuff ********************************/
335 static void NV50CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
336 {
337         ScrnInfoPtr pScrn = crtc->scrn;
338         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcShowHideCursor is called (%s, %s).\n", show ? "show" : "hide", update ? "update" : "no update");
339
340         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
341
342         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR, 
343                 show ? NV50_CRTC0_CURSOR_SHOW : NV50_CRTC0_CURSOR_HIDE);
344         if (update) {
345                 nv_crtc->cursorVisible = show;
346                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
347         }
348 }
349
350 void NV50CrtcShowCursor(xf86CrtcPtr crtc)
351 {
352         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
353
354         /* Calling NV50_UPDATE_DISPLAY during modeset will lock up everything. */
355         if (nv_crtc->modeset_lock)
356                 return;
357
358         NV50CrtcShowHideCursor(crtc, TRUE, TRUE);
359 }
360
361 void NV50CrtcHideCursor(xf86CrtcPtr crtc)
362 {
363         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
364
365         /* Calling NV50_UPDATE_DISPLAY during modeset will lock up everything. */
366         if (nv_crtc->modeset_lock)
367                 return;
368
369         NV50CrtcShowHideCursor(crtc, FALSE, TRUE);
370 }
371
372 /******************************** CRTC stuff ********************************/
373
374 void
375 NV50CrtcPrepare(xf86CrtcPtr crtc)
376 {
377         ScrnInfoPtr pScrn = crtc->scrn;
378         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcPrepare is called.\n");
379
380         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
381         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
382         int i;
383
384         nv_crtc->modeset_lock = TRUE;
385
386         for(i = 0; i < xf86_config->num_output; i++) {
387                 xf86OutputPtr output = xf86_config->output[i];
388
389                 if (!output->crtc)
390                         output->funcs->mode_set(output, NULL, NULL);
391         }
392 }
393
394 void
395 NV50CrtcSetDither(xf86CrtcPtr crtc, Bool update)
396 {
397         ScrnInfoPtr pScrn = crtc->scrn;
398         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetDither is called (%s).\n", update ? "update" : "no update");
399
400         xf86OutputPtr output = NULL;
401         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
402         int i;
403
404         for (i = 0; i < xf86_config->num_output; i++) {
405                 if (xf86_config->output[i]->crtc == crtc) {
406                         output = xf86_config->output[i];
407                         break;
408                 }
409         }
410
411         if (!output)
412                 return;
413
414         NVOutputPrivatePtr nv_output = output->driver_private;
415
416         NV50CrtcCommand(crtc, NV50_CRTC0_DITHERING_CTRL, nv_output->dithering ? 
417                         NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
418         if (update) 
419                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
420 }
421
422 static void ComputeAspectScale(DisplayModePtr mode, DisplayModePtr adjusted_mode, int *outX, int *outY)
423 {
424         float scaleX, scaleY, scale;
425
426         scaleX = adjusted_mode->HDisplay / (float)mode->HDisplay;
427         scaleY = adjusted_mode->VDisplay / (float)mode->VDisplay;
428
429         if(scaleX > scaleY)
430                 scale = scaleY;
431         else
432                 scale = scaleX;
433
434         *outX = mode->HDisplay * scale;
435         *outY = mode->VDisplay * scale;
436 }
437
438 void NV50CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, enum scaling_modes scale)
439 {
440         ScrnInfoPtr pScrn = crtc->scrn;
441         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetScale is called.\n");
442
443         int outX = 0, outY = 0;
444
445         switch(scale) {
446                 case SCALE_ASPECT:
447                         ComputeAspectScale(mode, adjusted_mode, &outX, &outY);
448                         break;
449                 case SCALE_PANEL:
450                 case SCALE_FULLSCREEN:
451                         outX = adjusted_mode->HDisplay;
452                         outY = adjusted_mode->VDisplay;
453                         break;
454                 case SCALE_NOSCALE:
455                 default:
456                         outX = mode->HDisplay;
457                         outY = mode->VDisplay;
458                         break;
459         }
460
461         /* What kind of mode is this precisely? */
462         if ((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) ||
463                 mode->HDisplay != outX || mode->VDisplay != outY) {
464                 NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_CTRL, 9);
465         } else {
466                 NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_CTRL, 0);
467         }
468         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_REG1, outY << 16 | outX);
469         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_REG2, outY << 16 | outX);
470 }
471
472 void
473 NV50CrtcCommit(xf86CrtcPtr crtc)
474 {
475         ScrnInfoPtr pScrn = crtc->scrn;
476         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcCommit is called.\n");
477
478         NVCrtcPrivatePtr nv_crtc = crtc->driver_private;
479         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
480         int i, crtc_mask = 0;
481
482         /* If any heads are unused, blank them */
483         for(i = 0; i < xf86_config->num_output; i++) {
484                 xf86OutputPtr output = xf86_config->output[i];
485
486                 if (output->crtc) {
487                         /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */
488                         crtc_mask |= 1 << NV50CrtcGetHead(output->crtc);
489                 }
490         }
491
492         for(i = 0; i < xf86_config->num_crtc; i++) {
493                 if(!((1 << i) & crtc_mask)) {
494                         NV50CrtcBlankScreen(xf86_config->crtc[i], TRUE);
495                 }
496         }
497
498         xf86_reload_cursors (pScrn->pScreen);
499
500         NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
501
502         nv_crtc->modeset_lock = FALSE;
503 }
504