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